Implement slider
This commit is contained in:
parent
7c3aa61c02
commit
2ec24af92a
6 changed files with 216 additions and 56 deletions
11
src/app.rs
11
src/app.rs
|
@ -3,6 +3,7 @@ use crate::gui::Gui;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
count: i32,
|
count: i32,
|
||||||
|
slider_value: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -10,14 +11,10 @@ impl App {
|
||||||
if gui.button("Click me").clicked {
|
if gui.button("Click me").clicked {
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
}
|
}
|
||||||
if gui.button("Or click me instead!").clicked {
|
|
||||||
self.count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.label(format!(
|
gui.label(format!("The button have been clicked {} times", self.count));
|
||||||
"The buttons have been clicked {} times",
|
|
||||||
self.count
|
gui.slider_f32("Slider", &mut self.slider_value, 0.0, 10.0);
|
||||||
));
|
|
||||||
|
|
||||||
let commands_json = format!("{:#?}", gui.gui_commands());
|
let commands_json = format!("{:#?}", gui.gui_commands());
|
||||||
gui.label(format!("All gui commands: {}", commands_json));
|
gui.label(format!("All gui commands: {}", commands_json));
|
||||||
|
|
65
src/gui.rs
65
src/gui.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::types::*;
|
use crate::{math::*, types::*};
|
||||||
|
|
||||||
// TODO: implement Gui on this so we can add children to a widget
|
// TODO: implement Gui on this so we can add children to a widget
|
||||||
// pub struct Widget {}
|
// pub struct Widget {}
|
||||||
|
@ -37,18 +37,7 @@ impl Gui {
|
||||||
size: Vec2 { x: 200.0, y: 32.0 }, // TODO: get from some settings
|
size: Vec2 { x: 200.0, y: 32.0 }, // TODO: get from some settings
|
||||||
};
|
};
|
||||||
|
|
||||||
let hovered = rect.contains(self.input.mouse_pos);
|
let interact = self.interactive_rect(id, &rect);
|
||||||
let clicked = hovered && self.input.mouse_clicked;
|
|
||||||
if clicked {
|
|
||||||
self.state.active_id = Some(id);
|
|
||||||
}
|
|
||||||
let active = self.state.active_id == Some(id);
|
|
||||||
|
|
||||||
let interact = InteractInfo {
|
|
||||||
hovered,
|
|
||||||
clicked,
|
|
||||||
active,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.commands.push(GuiCmd::Rect {
|
self.commands.push(GuiCmd::Rect {
|
||||||
interact,
|
interact,
|
||||||
|
@ -78,8 +67,58 @@ impl Gui {
|
||||||
self.cursor.y += 16.0; // Padding
|
self.cursor.y += 16.0; // Padding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn slider_f32<S: Into<String>>(
|
||||||
|
&mut self,
|
||||||
|
label: S,
|
||||||
|
value: &mut f32,
|
||||||
|
min: f32,
|
||||||
|
max: f32,
|
||||||
|
) -> InteractInfo {
|
||||||
|
let label: String = label.into();
|
||||||
|
let id = self.get_id(&label);
|
||||||
|
let rect = Rect {
|
||||||
|
pos: self.cursor,
|
||||||
|
size: Vec2 { x: 200.0, y: 24.0 }, // TODO: get from some settings
|
||||||
|
};
|
||||||
|
let interact = self.interactive_rect(id, &rect);
|
||||||
|
|
||||||
|
debug_assert!(min <= max);
|
||||||
|
|
||||||
|
if interact.active {
|
||||||
|
*value = remap_clamp(self.input.mouse_pos.x, rect.min().x, rect.max().x, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.commands.push(GuiCmd::Slider {
|
||||||
|
interact,
|
||||||
|
label,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
rect,
|
||||||
|
value: *value,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.cursor.y += rect.size.y + 16.0;
|
||||||
|
|
||||||
|
interact
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn interactive_rect(&mut self, id: Id, rect: &Rect) -> InteractInfo {
|
||||||
|
let hovered = rect.contains(self.input.mouse_pos);
|
||||||
|
let clicked = hovered && self.input.mouse_clicked;
|
||||||
|
if clicked {
|
||||||
|
self.state.active_id = Some(id);
|
||||||
|
}
|
||||||
|
let active = self.state.active_id == Some(id);
|
||||||
|
|
||||||
|
InteractInfo {
|
||||||
|
hovered,
|
||||||
|
clicked,
|
||||||
|
active,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_id(&self, id_str: &str) -> Id {
|
fn get_id(&self, id_str: &str) -> Id {
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
|
#![deny(warnings)]
|
||||||
|
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate wasm_bindgen;
|
extern crate wasm_bindgen;
|
||||||
extern crate web_sys;
|
extern crate web_sys;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] // TODO: get rid of this
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::types::*;
|
use crate::{math::Vec2, types::*};
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod gui;
|
pub mod gui;
|
||||||
|
pub mod math;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
|
90
src/math.rs
Normal file
90
src/math.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Vec2 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Vec2 {
|
||||||
|
type Output = Vec2;
|
||||||
|
fn add(self, rhs: Vec2) -> Vec2 {
|
||||||
|
Vec2 {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub for Vec2 {
|
||||||
|
type Output = Vec2;
|
||||||
|
fn sub(self, rhs: Vec2) -> Vec2 {
|
||||||
|
Vec2 {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<f32> for Vec2 {
|
||||||
|
type Output = Vec2;
|
||||||
|
fn mul(self, factor: f32) -> Vec2 {
|
||||||
|
Vec2 {
|
||||||
|
x: self.x * factor,
|
||||||
|
y: self.y * factor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vec2(x: f32, y: f32) -> Vec2 {
|
||||||
|
Vec2 { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct Rect {
|
||||||
|
pub pos: Vec2,
|
||||||
|
pub size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rect {
|
||||||
|
pub fn from_center_size(center: Vec2, size: Vec2) -> Self {
|
||||||
|
Rect {
|
||||||
|
pos: center - size * 0.5,
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, p: Vec2) -> bool {
|
||||||
|
self.pos.x <= p.x
|
||||||
|
&& p.x <= self.pos.x + self.size.x
|
||||||
|
&& self.pos.y <= p.y
|
||||||
|
&& p.y <= self.pos.y + self.size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center(&self) -> Vec2 {
|
||||||
|
Vec2 {
|
||||||
|
x: self.pos.x + self.size.x / 2.0,
|
||||||
|
y: self.pos.y + self.size.y / 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min(&self) -> Vec2 {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
pub fn max(&self) -> Vec2 {
|
||||||
|
self.pos + self.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lerp(t: f32, min: f32, max: f32) -> f32 {
|
||||||
|
(1.0 - t) * min + t * max
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remap_clamp(from: f32, from_min: f32, from_max: f32, to_min: f32, to_max: f32) -> f32 {
|
||||||
|
let t = if from <= from_min {
|
||||||
|
0.0
|
||||||
|
} else if from >= from_max {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
(from - from_min) / (from_max - from_min)
|
||||||
|
};
|
||||||
|
lerp(t, to_min, to_max)
|
||||||
|
}
|
63
src/style.rs
63
src/style.rs
|
@ -1,7 +1,7 @@
|
||||||
use crate::types::*;
|
use crate::{math::*, types::*};
|
||||||
|
|
||||||
/// TODO: a Style struct which defines colors etc
|
/// TODO: a Style struct which defines colors etc
|
||||||
fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
fn translate_cmd(out_commands: &mut Vec<PaintCmd>, cmd: GuiCmd) {
|
||||||
match cmd {
|
match cmd {
|
||||||
GuiCmd::Rect {
|
GuiCmd::Rect {
|
||||||
rect,
|
rect,
|
||||||
|
@ -16,12 +16,12 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
||||||
} else {
|
} else {
|
||||||
"#222222ff".to_string()
|
"#222222ff".to_string()
|
||||||
};
|
};
|
||||||
PaintCmd::RoundedRect {
|
out_commands.push(PaintCmd::RoundedRect {
|
||||||
corner_radius: 5.0,
|
corner_radius: 5.0,
|
||||||
fill_style,
|
fill_style,
|
||||||
pos: rect.pos,
|
pos: rect.pos,
|
||||||
size: rect.size,
|
size: rect.size,
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GuiCmd::Text {
|
GuiCmd::Text {
|
||||||
|
@ -34,17 +34,66 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
||||||
TextStyle::Button => "#ffffffbb".to_string(),
|
TextStyle::Button => "#ffffffbb".to_string(),
|
||||||
TextStyle::Label => "#ffffffbb".to_string(),
|
TextStyle::Label => "#ffffffbb".to_string(),
|
||||||
};
|
};
|
||||||
PaintCmd::Text {
|
out_commands.push(PaintCmd::Text {
|
||||||
fill_style,
|
fill_style,
|
||||||
font: "14px Palatino".to_string(),
|
font: "14px Palatino".to_string(),
|
||||||
pos,
|
pos,
|
||||||
text,
|
text,
|
||||||
text_align,
|
text_align,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
GuiCmd::Slider {
|
||||||
|
interact,
|
||||||
|
label,
|
||||||
|
max,
|
||||||
|
min,
|
||||||
|
rect,
|
||||||
|
value,
|
||||||
|
} => {
|
||||||
|
let thin_rect = Rect::from_center_size(rect.center(), vec2(rect.size.x, 8.0));
|
||||||
|
|
||||||
|
let marker_center_x = remap_clamp(value, min, max, rect.min().x, rect.max().x);
|
||||||
|
|
||||||
|
let marker_rect =
|
||||||
|
Rect::from_center_size(vec2(marker_center_x, rect.center().y), vec2(16.0, 16.0));
|
||||||
|
|
||||||
|
let marker_fill_style = if interact.active {
|
||||||
|
"#888888ff".to_string()
|
||||||
|
} else if interact.hovered {
|
||||||
|
"#444444ff".to_string()
|
||||||
|
} else {
|
||||||
|
"#222222ff".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
out_commands.push(PaintCmd::RoundedRect {
|
||||||
|
corner_radius: 2.0,
|
||||||
|
fill_style: "#111111ff".to_string(),
|
||||||
|
pos: thin_rect.pos,
|
||||||
|
size: thin_rect.size,
|
||||||
|
});
|
||||||
|
|
||||||
|
out_commands.push(PaintCmd::RoundedRect {
|
||||||
|
corner_radius: 3.0,
|
||||||
|
fill_style: marker_fill_style,
|
||||||
|
pos: marker_rect.pos,
|
||||||
|
size: marker_rect.size,
|
||||||
|
});
|
||||||
|
|
||||||
|
out_commands.push(PaintCmd::Text {
|
||||||
|
fill_style: "#ffffffbb".to_string(),
|
||||||
|
font: "14px Palatino".to_string(),
|
||||||
|
pos: rect.center(),
|
||||||
|
text: format!("{}: {:.3}", label, value),
|
||||||
|
text_align: TextAlign::Center,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_paint_commands(gui_commands: &[GuiCmd]) -> Vec<PaintCmd> {
|
pub fn into_paint_commands(gui_commands: &[GuiCmd]) -> Vec<PaintCmd> {
|
||||||
gui_commands.iter().cloned().map(translate_cmd).collect()
|
let mut paint_commands = vec![];
|
||||||
|
for gui_cmd in gui_commands {
|
||||||
|
translate_cmd(&mut paint_commands, gui_cmd.clone())
|
||||||
|
}
|
||||||
|
paint_commands
|
||||||
}
|
}
|
||||||
|
|
36
src/types.rs
36
src/types.rs
|
@ -1,30 +1,4 @@
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
use crate::math::{Rect, Vec2};
|
||||||
pub struct Vec2 {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
|
||||||
pub struct Rect {
|
|
||||||
pub pos: Vec2,
|
|
||||||
pub size: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rect {
|
|
||||||
pub fn contains(&self, p: Vec2) -> bool {
|
|
||||||
self.pos.x <= p.x
|
|
||||||
&& p.x <= self.pos.x + self.size.x
|
|
||||||
&& self.pos.y <= p.y
|
|
||||||
&& p.y <= self.pos.y + self.size.y
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn center(&self) -> Vec2 {
|
|
||||||
Vec2 {
|
|
||||||
x: self.pos.x + self.size.x / 2.0,
|
|
||||||
y: self.pos.y + self.size.y / 2.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -141,6 +115,14 @@ pub enum GuiCmd {
|
||||||
text_align: TextAlign,
|
text_align: TextAlign,
|
||||||
style: TextStyle,
|
style: TextStyle,
|
||||||
},
|
},
|
||||||
|
Slider {
|
||||||
|
interact: InteractInfo,
|
||||||
|
label: String,
|
||||||
|
max: f32,
|
||||||
|
min: f32,
|
||||||
|
rect: Rect,
|
||||||
|
value: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue