Implement slider

This commit is contained in:
Emil Ernerfeldt 2018-12-26 17:01:46 +01:00
parent 7c3aa61c02
commit 2ec24af92a
6 changed files with 216 additions and 56 deletions

View file

@ -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));

View file

@ -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();

View file

@ -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
View 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)
}

View file

@ -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
} }

View file

@ -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,
},
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------