From 52eb5bdf2c8eb6b7b35dfda23d7b0eb8ae27b92e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 27 Dec 2018 19:08:43 +0100 Subject: [PATCH] Create foldable areas --- src/app.rs | 9 ++++--- src/layout.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 5 ++-- src/style.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.rs | 7 ++++++ 5 files changed, 146 insertions(+), 7 deletions(-) diff --git a/src/app.rs b/src/app.rs index ff439ac3e..7a614b8b0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -75,10 +75,11 @@ impl GuiSettings for App { }), }])); - gui.label("LayoutOptions:"); - let mut layout_options = gui.layout_options; - layout_options.show_gui(gui); - gui.layout_options = layout_options; + gui.foldable("LayoutOptions", |gui| { + let mut layout_options = gui.layout_options; + layout_options.show_gui(gui); + gui.layout_options = layout_options; + }); } } diff --git a/src/layout.rs b/src/layout.rs index 2ee5670df..d815b161b 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use crate::{math::*, types::*}; // ---------------------------------------------------------------------------- @@ -7,6 +9,9 @@ pub struct LayoutOptions { // Horizontal and vertical spacing between widgets pub item_spacing: Vec2, + /// Indent foldable regions etc by this much. + pub indent: f32, + /// Default width of buttons, sliders etc pub width: f32, @@ -24,6 +29,7 @@ impl Default for LayoutOptions { fn default() -> Self { LayoutOptions { item_spacing: Vec2 { x: 8.0, y: 4.0 }, + indent: 21.0, width: 200.0, button_height: 24.0, checkbox_radio_height: 24.0, @@ -34,10 +40,13 @@ impl Default for LayoutOptions { // ---------------------------------------------------------------------------- -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct State { /// The widget being interacted with (e.g. dragged, in case of a slider). pub active_id: Option, + + /// Which foldable regions are open. + open_foldables: HashSet, } // ---------------------------------------------------------------------------- @@ -186,6 +195,57 @@ impl Layout { interact } + // ------------------------------------------------------------------------ + // Areas: + + pub fn foldable(&mut self, label: S, add_contents: F) -> InteractInfo + where + S: Into, + F: FnOnce(&mut Layout), + { + let label: String = label.into(); + let id = self.get_id(&label); + + let rect = Rect { + pos: self.cursor, + size: Vec2 { + x: self.layout_options.width, + y: self.layout_options.button_height, + }, + }; + + let interact = self.interactive_rect(id, &rect); + self.cursor.y += rect.size.y + self.layout_options.item_spacing.y; + + if interact.clicked { + if self.state.open_foldables.contains(&id) { + self.state.open_foldables.remove(&id); + } else { + self.state.open_foldables.insert(id); + } + } + let open = self.state.open_foldables.contains(&id); + + self.commands.push(GuiCmd::FoldableHeader { + interact, + rect, + label, + open, + }); + + if open { + // TODO: push/pop id stack + let old_x = self.cursor.x; + self.cursor.x += self.layout_options.indent; + add_contents(self); + self.cursor.x = old_x; + + // TODO: paint background? + } + + interact + } + // ------------------------------------------------------------------------ fn interactive_rect(&mut self, id: Id, rect: &Rect) -> InteractInfo { diff --git a/src/lib.rs b/src/lib.rs index cfaafd4a8..57de2a3cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,9 +52,10 @@ pub fn show_gui(raw_input_json: &str) -> String { use crate::app::GuiSettings; APP.lock().unwrap().show_gui(&mut emgui.layout); - emgui.layout.label("Style:"); let mut style = emgui.style.clone(); - style.show_gui(&mut emgui.layout); + emgui.layout.foldable("Style", |gui| { + style.show_gui(gui); + }); emgui.style = style; let commands = emgui.paint(); diff --git a/src/style.rs b/src/style.rs index 62ee63ee3..893276593 100644 --- a/src/style.rs +++ b/src/style.rs @@ -142,6 +142,76 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { out_commands.push(debug_rect(rect)); } } + GuiCmd::FoldableHeader { + interact, + label, + open, + rect, + } => { + let fill_color = if interact.active { + srgba(136, 136, 136, 255) + } else if interact.hovered { + srgba(100, 100, 100, 255) + } else { + srgba(68, 68, 68, 255) + }; + + let stroke_color = if interact.active { + srgba(255, 255, 255, 255) + } else if interact.hovered { + srgba(255, 255, 255, 200) + } else { + srgba(255, 255, 255, 170) + }; + + out_commands.push(PaintCmd::Rect { + corner_radius: 3.0, + fill_color: Some(fill_color), + outline: None, + pos: rect.pos, + size: rect.size, + }); + + // TODO: paint a little triangle or arrow or something instead of this + + let box_side = 16.0; + let box_rect = Rect::from_center_size( + vec2(rect.min().x + box_side * 0.5, rect.center().y), + vec2(box_side, box_side), + ); + // Draw a minus: + out_commands.push(PaintCmd::Line { + points: vec![ + vec2(box_rect.min().x, box_rect.center().y), + vec2(box_rect.max().x, box_rect.center().y), + ], + color: stroke_color, + width: style.line_width, + }); + if open { + // Draw it as a plus: + out_commands.push(PaintCmd::Line { + points: vec![ + vec2(box_rect.center().x, box_rect.min().y), + vec2(box_rect.center().x, box_rect.max().y), + ], + color: stroke_color, + width: style.line_width, + }); + } + + out_commands.push(PaintCmd::Text { + fill_color: stroke_color, + font_name: style.font_name.clone(), + font_size: style.font_size, + pos: Vec2 { + x: box_rect.max().x + 4.0, + y: rect.center().y - style.font_size / 2.0, + }, + text: label, + text_align: TextAlign::Start, + }); + } GuiCmd::RadioButton { checked, interact, diff --git a/src/types.rs b/src/types.rs index a2923c110..cf282f81b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -102,6 +102,13 @@ pub enum GuiCmd { rect: Rect, text: String, }, + // The header for a foldable region + FoldableHeader { + interact: InteractInfo, + open: bool, + rect: Rect, + label: String, + }, RadioButton { checked: bool, interact: InteractInfo,