Browse Source

Implement slider

pull/1/head
Emil Ernerfeldt 6 years ago
parent
commit
2ec24af92a
  1. 11
      src/app.rs
  2. 65
      src/gui.rs
  3. 7
      src/lib.rs
  4. 90
      src/math.rs
  5. 63
      src/style.rs
  6. 36
      src/types.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

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

7
src/lib.rs

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

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

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

@ -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…
Cancel
Save