From 79f64d20666200f269d4bb6f89240cd0091a03c4 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 28 Dec 2018 23:29:24 +0100 Subject: [PATCH] Hovering tooltip window, bit a bad API --- src/app.rs | 34 +++++++++++++---------- src/emgui.rs | 2 +- src/layout.rs | 77 +++++++++++++++++++++++++++++++++++++++++---------- src/style.rs | 20 ++++++++++++- src/types.rs | 4 +++ 5 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9adf6450..f1580a67 100644 --- a/src/app.rs +++ b/src/app.rs @@ -37,7 +37,12 @@ impl GuiSettings for App { gui.input().screen_size.y, )); - gui.checkbox("checkbox", &mut self.checked); + // TODO: add tooltip text with: gui.button("click me").tooltip_text("tooltip") + if gui.checkbox("checkbox", &mut self.checked).hovered { + gui.tooltip_text( + "This is a multiline tooltip that explains the checkbox you are hovering.\nThis is the second line.\nThis is the third.", + ); + } gui.horizontal(|gui| { if gui.radio("First", self.selected_alternative == 0).clicked { @@ -64,22 +69,21 @@ impl GuiSettings for App { gui.slider_f32("stroke_width", &mut self.stroke_width, 0.0, 10.0); }); - gui.commands - .push(GuiCmd::PaintCommands(vec![PaintCmd::Rect { - corner_radius: self.corner_radius, - fill_color: Some(srgba(136, 136, 136, 255)), - pos: vec2(300.0, 100.0), - size: vec2(self.width, self.height), - outline: Some(Outline { - width: self.stroke_width, - color: srgba(255, 255, 255, 255), - }), - }])); + gui.add_paint_command(GuiCmd::PaintCommands(vec![PaintCmd::Rect { + corner_radius: self.corner_radius, + fill_color: Some(srgba(136, 136, 136, 255)), + pos: vec2(300.0, 100.0), + size: vec2(self.width, self.height), + outline: Some(Outline { + width: self.stroke_width, + color: srgba(255, 255, 255, 255), + }), + }])); gui.foldable("LayoutOptions", |gui| { - let mut options = gui.options; + let mut options = gui.options().clone(); options.show_gui(gui); - gui.options = options; + gui.set_options(options); }); } } @@ -93,6 +97,8 @@ impl GuiSettings for crate::layout::LayoutOptions { gui.slider_f32("char_size.y", &mut self.char_size.y, 0.0, 20.0); gui.slider_f32("item_spacing.x", &mut self.item_spacing.x, 0.0, 10.0); gui.slider_f32("item_spacing.y", &mut self.item_spacing.y, 0.0, 10.0); + gui.slider_f32("window_padding.x", &mut self.window_padding.x, 0.0, 10.0); + gui.slider_f32("window_padding.y", &mut self.window_padding.y, 0.0, 10.0); gui.slider_f32("indent", &mut self.indent, 0.0, 100.0); gui.slider_f32("width", &mut self.width, 0.0, 1000.0); gui.slider_f32("button_padding.x", &mut self.button_padding.x, 0.0, 20.0); diff --git a/src/emgui.rs b/src/emgui.rs index 3ffe1292..1ab04aa4 100644 --- a/src/emgui.rs +++ b/src/emgui.rs @@ -12,7 +12,7 @@ impl Emgui { pub fn new_frame(&mut self, new_input: RawInput) { let gui_input = GuiInput::from_last_and_new(&self.last_input, &new_input); self.last_input = new_input; - self.layout.new_frae(gui_input); + self.layout.new_frame(gui_input); } pub fn paint(&mut self) -> Vec { diff --git a/src/layout.rs b/src/layout.rs index eda477aa..4689881e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -10,7 +10,10 @@ pub struct LayoutOptions { /// All text is monospace! pub char_size: Vec2, - // Horizontal and vertical spacing between widgets + /// Horizontal and vertical padding within a window frame. + pub window_padding: Vec2, + + /// Horizontal and vertical spacing between widgets pub item_spacing: Vec2, /// Indent foldable regions etc by this much. @@ -32,6 +35,7 @@ impl Default for LayoutOptions { LayoutOptions { char_size: vec2(7.2, 14.0), item_spacing: vec2(8.0, 4.0), + window_padding: vec2(6.0, 6.0), indent: 21.0, width: 250.0, button_padding: vec2(5.0, 3.0), @@ -108,15 +112,15 @@ impl Layouter { type Id = u64; -/// TODO: non-pub #[derive(Clone, Debug, Default)] pub struct Layout { - pub options: LayoutOptions, // TODO: remove pub + options: LayoutOptions, input: GuiInput, memory: Memory, id: Id, layouter: Layouter, - pub commands: Vec, // TODO: remove pub + graphics: Vec, + hovering_graphics: Vec, } impl Layout { @@ -124,13 +128,22 @@ impl Layout { &self.input } - pub fn gui_commands(&self) -> &[GuiCmd] { - &self.commands + pub fn gui_commands(&self) -> impl Iterator { + self.graphics.iter().chain(self.hovering_graphics.iter()) + } + + pub fn options(&self) -> &LayoutOptions { + &self.options + } + + pub fn set_options(&mut self, options: LayoutOptions) { + self.options = options; } // TODO: move - pub fn new_frae(&mut self, gui_input: GuiInput) { - self.commands.clear(); + pub fn new_frame(&mut self, gui_input: GuiInput) { + self.graphics.clear(); + self.hovering_graphics.clear(); self.layouter = Default::default(); self.input = gui_input; if !gui_input.mouse_down { @@ -147,7 +160,7 @@ impl Layout { let text_cursor = self.layouter.cursor + self.options.button_padding; let (rect, interact) = self.reserve_interactive_space(id, text_size + 2.0 * self.options.button_padding); - self.commands.push(GuiCmd::Button { interact, rect }); + self.graphics.push(GuiCmd::Button { interact, rect }); self.add_text(text_cursor, text); interact } @@ -169,7 +182,7 @@ impl Layout { if interact.clicked { *checked = !*checked; } - self.commands.push(GuiCmd::Checkbox { + self.graphics.push(GuiCmd::Checkbox { checked: *checked, interact, rect, @@ -200,7 +213,7 @@ impl Layout { + text_size + self.options.button_padding, ); - self.commands.push(GuiCmd::RadioButton { + self.graphics.push(GuiCmd::RadioButton { checked, interact, rect, @@ -240,7 +253,7 @@ impl Layout { ); } - self.commands.push(GuiCmd::Slider { + self.graphics.push(GuiCmd::Slider { interact, max, min, @@ -284,7 +297,7 @@ impl Layout { } let open = self.memory.open_foldables.contains(&id); - self.commands.push(GuiCmd::FoldableHeader { + self.graphics.push(GuiCmd::FoldableHeader { interact, rect, open, @@ -320,6 +333,42 @@ impl Layout { self.layouter.reserve_space(horizontal_layouter.size); } + // ------------------------------------------------------------------------ + // Free painting. It is up to the caller to make sure there is room for these. + pub fn add_paint_command(&mut self, cmd: GuiCmd) { + self.graphics.push(cmd); + } + + // ------------------------------------------------------------------------ + + /// Show some text in a window under mouse position. + pub fn tooltip_text>(&mut self, text: S) { + let window_pos = self.input.mouse_pos + vec2(16.0, 16.0); + + // TODO: less copying + let mut popup_layout = Layout { + options: self.options, + input: self.input, + memory: self.memory.clone(), // TODO: Arc + id: self.id, + layouter: Default::default(), + graphics: vec![], + hovering_graphics: vec![], + }; + popup_layout.layouter.cursor = window_pos + self.options.window_padding; + + popup_layout.label(text); + + // TODO: handle the last item_spacing in a nicer way + let inner_size = popup_layout.layouter.size - self.options.item_spacing; + let outer_size = inner_size + 2.0 * self.options.window_padding; + + let rect = Rect::from_min_size(window_pos, outer_size); + self.hovering_graphics.push(GuiCmd::Window { rect }); + self.hovering_graphics + .extend(popup_layout.gui_commands().cloned()); + } + // ------------------------------------------------------------------------ fn reserve_space_default_spacing(&mut self, size: Vec2) -> Rect { @@ -380,7 +429,7 @@ impl Layout { fn add_text(&mut self, pos: Vec2, text: Vec) { for fragment in text { - self.commands.push(GuiCmd::Text { + self.graphics.push(GuiCmd::Text { pos: pos + vec2(fragment.rect.pos.x, fragment.rect.center().y), style: TextStyle::Label, text: fragment.text, diff --git a/src/style.rs b/src/style.rs index 633da9d3..4c530ea5 100644 --- a/src/style.rs +++ b/src/style.rs @@ -255,10 +255,28 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { text, }); } + GuiCmd::Window { rect } => { + out_commands.push(PaintCmd::Rect { + corner_radius: 5.0, + fill_color: Some(style.background_fill_color()), + outline: Some(Outline { + color: srgba(255, 255, 255, 255), // TODO + width: 1.0, + }), + pos: rect.pos, + size: rect.size, + }); + } } } -pub fn into_paint_commands(gui_commands: &[GuiCmd], style: &Style) -> Vec { +pub fn into_paint_commands<'a, GuiCmdIterator>( + gui_commands: GuiCmdIterator, + style: &Style, +) -> Vec +where + GuiCmdIterator: Iterator, +{ let mut paint_commands = vec![]; for gui_cmd in gui_commands { translate_cmd(&mut paint_commands, style, gui_cmd.clone()) diff --git a/src/types.rs b/src/types.rs index 89aa7698..56837003 100644 --- a/src/types.rs +++ b/src/types.rs @@ -119,6 +119,10 @@ pub enum GuiCmd { style: TextStyle, text: String, }, + /// Background of e.g. a popup + Window { + rect: Rect, + }, } // ----------------------------------------------------------------------------