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)]
|
||||
pub struct App {
|
||||
count: i32,
|
||||
slider_value: f32,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -10,14 +11,10 @@ impl App {
|
|||
if gui.button("Click me").clicked {
|
||||
self.count += 1;
|
||||
}
|
||||
if gui.button("Or click me instead!").clicked {
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
gui.label(format!(
|
||||
"The buttons have been clicked {} times",
|
||||
self.count
|
||||
));
|
||||
gui.label(format!("The button 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());
|
||||
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
|
||||
// pub struct Widget {}
|
||||
|
@ -37,18 +37,7 @@ impl Gui {
|
|||
size: Vec2 { x: 200.0, y: 32.0 }, // TODO: get from some settings
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
let interact = InteractInfo {
|
||||
hovered,
|
||||
clicked,
|
||||
active,
|
||||
};
|
||||
let interact = self.interactive_rect(id, &rect);
|
||||
|
||||
self.commands.push(GuiCmd::Rect {
|
||||
interact,
|
||||
|
@ -78,8 +67,58 @@ impl Gui {
|
|||
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 {
|
||||
use std::hash::Hasher;
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
#![deny(warnings)]
|
||||
|
||||
extern crate lazy_static;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_bindgen;
|
||||
extern crate web_sys;
|
||||
|
||||
#[macro_use]
|
||||
#[macro_use] // TODO: get rid of this
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::types::*;
|
||||
use crate::{math::Vec2, types::*};
|
||||
|
||||
pub mod app;
|
||||
pub mod gui;
|
||||
pub mod math;
|
||||
pub mod style;
|
||||
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
|
||||
fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
||||
fn translate_cmd(out_commands: &mut Vec<PaintCmd>, cmd: GuiCmd) {
|
||||
match cmd {
|
||||
GuiCmd::Rect {
|
||||
rect,
|
||||
|
@ -16,12 +16,12 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
|||
} else {
|
||||
"#222222ff".to_string()
|
||||
};
|
||||
PaintCmd::RoundedRect {
|
||||
out_commands.push(PaintCmd::RoundedRect {
|
||||
corner_radius: 5.0,
|
||||
fill_style,
|
||||
pos: rect.pos,
|
||||
size: rect.size,
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
GuiCmd::Text {
|
||||
|
@ -34,17 +34,66 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
|||
TextStyle::Button => "#ffffffbb".to_string(),
|
||||
TextStyle::Label => "#ffffffbb".to_string(),
|
||||
};
|
||||
PaintCmd::Text {
|
||||
out_commands.push(PaintCmd::Text {
|
||||
fill_style,
|
||||
font: "14px Palatino".to_string(),
|
||||
pos,
|
||||
text,
|
||||
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> {
|
||||
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)]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::math::{Rect, Vec2};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -141,6 +115,14 @@ pub enum GuiCmd {
|
|||
text_align: TextAlign,
|
||||
style: TextStyle,
|
||||
},
|
||||
Slider {
|
||||
interact: InteractInfo,
|
||||
label: String,
|
||||
max: f32,
|
||||
min: f32,
|
||||
rect: Rect,
|
||||
value: f32,
|
||||
},
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue