persitent interaction with buttons
This commit is contained in:
parent
094f8216c5
commit
7c3aa61c02
6 changed files with 105 additions and 53 deletions
3
lint.sh
3
lint.sh
|
@ -3,3 +3,6 @@ set -eu
|
||||||
|
|
||||||
echo "Lint and clean up typescript:"
|
echo "Lint and clean up typescript:"
|
||||||
tslint --fix docs/*.ts
|
tslint --fix docs/*.ts
|
||||||
|
|
||||||
|
echo "Cargo clippy"
|
||||||
|
cargo clippy
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use crate::gui::Gui;
|
use crate::gui::Gui;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
count: i32,
|
count: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> Self {
|
|
||||||
App { count: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show_gui(&mut self, gui: &mut Gui) {
|
pub fn show_gui(&mut self, gui: &mut Gui) {
|
||||||
if gui.button("Click me").clicked {
|
if gui.button("Click me").clicked {
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
|
@ -22,7 +19,7 @@ impl App {
|
||||||
self.count
|
self.count
|
||||||
));
|
));
|
||||||
|
|
||||||
let commands_json = serde_json::to_string_pretty(&gui.paint_commands()).unwrap();
|
let commands_json = format!("{:#?}", gui.gui_commands());
|
||||||
gui.label(format!("All paint commands:\n{}", commands_json));
|
gui.label(format!("All gui commands: {}", commands_json));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
92
src/gui.rs
92
src/gui.rs
|
@ -3,63 +3,58 @@ use crate::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 {}
|
||||||
|
|
||||||
pub struct Gui {
|
type Id = u64;
|
||||||
commands: Vec<GuiCmd>,
|
|
||||||
input: GuiInput,
|
|
||||||
|
|
||||||
cursor: Vec2,
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct GuiState {
|
||||||
|
/// The widget being interacted with (e.g. dragged, in case of a slider).
|
||||||
|
pub active_id: Option<Id>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gui {
|
||||||
|
pub commands: Vec<GuiCmd>,
|
||||||
|
pub cursor: Vec2,
|
||||||
|
pub input: GuiInput,
|
||||||
|
pub state: GuiState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gui {
|
impl Gui {
|
||||||
pub fn new(input: GuiInput) -> Self {
|
|
||||||
Gui {
|
|
||||||
commands: vec![],
|
|
||||||
input,
|
|
||||||
cursor: Vec2 { x: 32.0, y: 32.0 },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn input(&self) -> &GuiInput {
|
pub fn input(&self) -> &GuiInput {
|
||||||
&self.input
|
&self.input
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_commands(self) -> Vec<GuiCmd> {
|
pub fn gui_commands(&self) -> &[GuiCmd] {
|
||||||
self.commands
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint_commands(&self) -> &[GuiCmd] {
|
|
||||||
&self.commands
|
&self.commands
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect(&mut self, rect: Rect, style: RectStyle) -> InteractInfo {
|
|
||||||
let hovered = rect.contains(self.input.mouse_pos);
|
|
||||||
let clicked = hovered && self.input.mouse_clicked;
|
|
||||||
let interact = InteractInfo { hovered, clicked };
|
|
||||||
self.commands.push(GuiCmd::Rect {
|
|
||||||
interact,
|
|
||||||
rect,
|
|
||||||
style,
|
|
||||||
});
|
|
||||||
interact
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text<S: Into<String>>(&mut self, pos: Vec2, style: TextStyle, text: S) {
|
|
||||||
self.commands.push(GuiCmd::Text {
|
|
||||||
pos,
|
|
||||||
style,
|
|
||||||
text: text.into(),
|
|
||||||
text_align: TextAlign::Start,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn button<S: Into<String>>(&mut self, text: S) -> InteractInfo {
|
pub fn button<S: Into<String>>(&mut self, text: S) -> InteractInfo {
|
||||||
|
let text: String = text.into();
|
||||||
|
let id = self.get_id(&text);
|
||||||
let rect = Rect {
|
let rect = Rect {
|
||||||
pos: self.cursor,
|
pos: self.cursor,
|
||||||
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 interact = self.rect(rect, RectStyle::Button);
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.commands.push(GuiCmd::Rect {
|
||||||
|
interact,
|
||||||
|
rect,
|
||||||
|
style: RectStyle::Button,
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: clip-rect of text
|
// TODO: clip-rect of text
|
||||||
self.text(
|
self.text(
|
||||||
|
@ -75,7 +70,8 @@ impl Gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label<S: Into<String>>(&mut self, text: S) {
|
pub fn label<S: Into<String>>(&mut self, text: S) {
|
||||||
for line in text.into().split("\n") {
|
let text: String = text.into();
|
||||||
|
for line in text.split('\n') {
|
||||||
self.text(self.cursor, TextStyle::Label, line);
|
self.text(self.cursor, TextStyle::Label, line);
|
||||||
self.cursor.y += 16.0;
|
self.cursor.y += 16.0;
|
||||||
}
|
}
|
||||||
|
@ -83,4 +79,20 @@ impl Gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn get_id(&self, id_str: &str) -> Id {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
hasher.write(id_str.as_bytes());
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text<S: Into<String>>(&mut self, pos: Vec2, style: TextStyle, text: S) {
|
||||||
|
self.commands.push(GuiCmd::Text {
|
||||||
|
pos,
|
||||||
|
style,
|
||||||
|
text: text.into(),
|
||||||
|
text_align: TextAlign::Start,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -39,16 +39,27 @@ pub fn show_gui(raw_input_json: &str) -> String {
|
||||||
let raw_input: RawInput = serde_json::from_str(raw_input_json).unwrap();
|
let raw_input: RawInput = serde_json::from_str(raw_input_json).unwrap();
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref APP: Mutex<app::App> = Mutex::new(app::App::new());
|
static ref APP: Mutex<app::App> = Default::default();
|
||||||
static ref LAST_INPUT: Mutex<RawInput> = Default::default();
|
static ref LAST_INPUT: Mutex<RawInput> = Default::default();
|
||||||
|
static ref GUI_STATE: Mutex<gui::GuiState> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let gui_input = GuiInput::from_last_and_new(&LAST_INPUT.lock().unwrap(), &raw_input);
|
let gui_input = GuiInput::from_last_and_new(&LAST_INPUT.lock().unwrap(), &raw_input);
|
||||||
*LAST_INPUT.lock().unwrap() = raw_input;
|
*LAST_INPUT.lock().unwrap() = raw_input;
|
||||||
|
|
||||||
let mut gui = gui::Gui::new(gui_input);
|
let mut gui = gui::Gui {
|
||||||
|
commands: Vec::new(),
|
||||||
|
cursor: Vec2 { x: 32.0, y: 32.0 },
|
||||||
|
input: gui_input,
|
||||||
|
state: *GUI_STATE.lock().unwrap(),
|
||||||
|
};
|
||||||
|
if !gui_input.mouse_down {
|
||||||
|
gui.state.active_id = None;
|
||||||
|
}
|
||||||
APP.lock().unwrap().show_gui(&mut gui);
|
APP.lock().unwrap().show_gui(&mut gui);
|
||||||
let commands = gui.into_commands();
|
|
||||||
let commands = style::into_paint_commands(commands);
|
*GUI_STATE.lock().unwrap() = gui.state;
|
||||||
|
|
||||||
|
let commands = style::into_paint_commands(gui.gui_commands());
|
||||||
serde_json::to_string(&commands).unwrap()
|
serde_json::to_string(&commands).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
||||||
interact,
|
interact,
|
||||||
} => match style {
|
} => match style {
|
||||||
RectStyle::Button => {
|
RectStyle::Button => {
|
||||||
let fill_style = if interact.hovered {
|
let fill_style = if interact.active {
|
||||||
|
"#888888ff".to_string()
|
||||||
|
} else if interact.hovered {
|
||||||
"#444444ff".to_string()
|
"#444444ff".to_string()
|
||||||
} else {
|
} else {
|
||||||
"#222222ff".to_string()
|
"#222222ff".to_string()
|
||||||
|
@ -43,6 +45,6 @@ fn translate_cmd(cmd: GuiCmd) -> PaintCmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_paint_commands(gui_commands: Vec<GuiCmd>) -> Vec<PaintCmd> {
|
pub fn into_paint_commands(gui_commands: &[GuiCmd]) -> Vec<PaintCmd> {
|
||||||
gui_commands.into_iter().map(translate_cmd).collect()
|
gui_commands.iter().cloned().map(translate_cmd).collect()
|
||||||
}
|
}
|
||||||
|
|
27
src/types.rs
27
src/types.rs
|
@ -74,10 +74,37 @@ impl GuiInput {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Names taken from Dear ImGui
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Serialize)]
|
||||||
|
pub struct LayoutOptions {
|
||||||
|
// Horizontal and vertical spacing between widgets
|
||||||
|
item_spacing: Vec2,
|
||||||
|
|
||||||
|
/// Padding within a framed rectangle (used by most widgets)
|
||||||
|
frame_padding: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutOptions {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// Values taken from Dear ImGui
|
||||||
|
LayoutOptions {
|
||||||
|
item_spacing: Vec2 { x: 8.0, y: 4.0 },
|
||||||
|
frame_padding: Vec2 { x: 4.0, y: 3.0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Serialize)]
|
||||||
pub struct InteractInfo {
|
pub struct InteractInfo {
|
||||||
pub hovered: bool,
|
pub hovered: bool,
|
||||||
|
|
||||||
|
/// The mouse went got pressed on this thing this frame
|
||||||
pub clicked: bool,
|
pub clicked: bool,
|
||||||
|
|
||||||
|
/// The mouse is interacting with this thing (e.g. dragging it)
|
||||||
|
pub active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize)]
|
#[derive(Clone, Copy, Debug, Serialize)]
|
||||||
|
|
Loading…
Reference in a new issue