From 7f83876005b9cd6db07dcd87b92e3921bd37696b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 16 Jan 2019 09:28:43 -0600 Subject: [PATCH] Enable colored text (and other misc fixes) --- emigui/src/emigui.rs | 10 ++---- emigui/src/layout.rs | 24 +++++++++---- emigui/src/lib.rs | 4 +-- emigui/src/{painter.rs => mesher.rs} | 21 ++--------- emigui/src/style.rs | 9 +++-- emigui/src/texture_atlas.rs | 4 +-- emigui/src/types.rs | 10 ++---- emigui/src/widgets.rs | 52 ++++++++++++++++++++++++---- emigui_wasm/src/app.rs | 52 ++++++++++++++-------------- 9 files changed, 104 insertions(+), 82 deletions(-) rename emigui/src/{painter.rs => mesher.rs} (96%) diff --git a/emigui/src/emigui.rs b/emigui/src/emigui.rs index 1098cb56..3265dba9 100644 --- a/emigui/src/emigui.rs +++ b/emigui/src/emigui.rs @@ -6,7 +6,7 @@ use crate::{ style, types::GuiInput, widgets::*, - Frame, Painter, RawInput, + Frame, RawInput, }; #[derive(Clone, Copy, Default)] @@ -42,19 +42,15 @@ pub struct Emigui { pub last_input: RawInput, pub data: Arc, pub style: style::Style, - pub painter: Painter, stats: Stats, } impl Emigui { pub fn new() -> Emigui { - let data = Arc::new(layout::Data::new()); - let fonts = data.fonts.clone(); Emigui { last_input: Default::default(), - data, + data: Arc::new(layout::Data::new()), style: Default::default(), - painter: Painter::new(fonts), stats: Default::default(), } } @@ -90,7 +86,7 @@ impl Emigui { pub fn paint(&mut self) -> Frame { let gui_commands = self.data.graphics.lock().unwrap().drain(); let paint_commands = style::into_paint_commands(gui_commands, &self.style); - let frame = self.painter.paint(&paint_commands); + let frame = Frame::paint(&self.data.fonts, &paint_commands); self.stats.num_vertices = frame.vertices.len(); self.stats.num_triangles = frame.indices.len() / 3; frame diff --git a/emigui/src/layout.rs b/emigui/src/layout.rs index 69076f4f..84d15ddb 100644 --- a/emigui/src/layout.rs +++ b/emigui/src/layout.rs @@ -51,7 +51,7 @@ impl Default for LayoutOptions { // ---------------------------------------------------------------------------- -// TODO: rename +// TODO: rename GuiResponse pub struct GuiResponse { /// The mouse is hovering above this pub hovered: bool, @@ -181,7 +181,7 @@ impl Clone for Data { Data { options: Mutex::new(self.options()), fonts: self.fonts.clone(), - input: self.input.clone(), + input: self.input, memory: Mutex::new(self.memory.lock().unwrap().clone()), graphics: Mutex::new(self.graphics.lock().unwrap().clone()), } @@ -358,6 +358,7 @@ impl Region { text_cursor + vec2(self.options().start_icon_width, 0.0), text_style, text, + None, ); if open { @@ -475,13 +476,15 @@ impl Region { pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option) -> InteractInfo { let pos = self.reserve_space_without_padding(size + self.options().item_spacing); - let rect = Rect::from_min_size(pos, size); + let mut memory = self.data.memory.lock().unwrap(); - let hovered = rect.contains(self.input().mouse_pos); + let is_something_else_active = + memory.active_id.is_some() && memory.active_id != interaction_id; + + let hovered = !is_something_else_active && rect.contains(self.input().mouse_pos); let clicked = hovered && self.input().mouse_clicked; let active = if interaction_id.is_some() { - let mut memory = self.data.memory.lock().unwrap(); if clicked { memory.active_id = interaction_id; } @@ -543,12 +546,19 @@ impl Region { }) } - pub fn add_text(&mut self, pos: Vec2, text_style: TextStyle, text: Vec) { + pub fn add_text( + &mut self, + pos: Vec2, + text_style: TextStyle, + text: Vec, + color: Option, + ) { for fragment in text { self.add_graphic(GuiCmd::Text { + color, pos: pos + vec2(0.0, fragment.y_offset), - text_style, text: fragment.text, + text_style, x_offsets: fragment.x_offsets, }); } diff --git a/emigui/src/lib.rs b/emigui/src/lib.rs index 1e372f5c..c7404868 100644 --- a/emigui/src/lib.rs +++ b/emigui/src/lib.rs @@ -11,7 +11,7 @@ mod font; mod fonts; mod layout; pub mod math; -mod painter; +mod mesher; mod style; mod texture_atlas; pub mod types; @@ -21,7 +21,7 @@ pub use crate::{ emigui::Emigui, fonts::TextStyle, layout::{Align, LayoutOptions, Region}, - painter::{Frame, Painter, Vertex}, + mesher::{Frame, Vertex}, style::Style, types::RawInput, }; diff --git a/emigui/src/painter.rs b/emigui/src/mesher.rs similarity index 96% rename from emigui/src/painter.rs rename to emigui/src/mesher.rs index b663fa51..b6af061a 100644 --- a/emigui/src/painter.rs +++ b/emigui/src/mesher.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - const ANTI_ALIAS: bool = true; const AA_SIZE: f32 = 1.0; @@ -22,7 +20,6 @@ pub struct Vertex { #[derive(Clone, Debug, Default)] pub struct Frame { - pub clear_color: Option, /// Draw as triangles (i.e. the length is a multiple of three) pub indices: Vec, pub vertices: Vec, @@ -189,19 +186,8 @@ impl Frame { } } } -} -#[derive(Clone)] -pub struct Painter { - fonts: Arc, -} - -impl Painter { - pub fn new(fonts: Arc) -> Painter { - Painter { fonts } - } - - pub fn paint(&self, commands: &[PaintCmd]) -> Frame { + pub fn paint(fonts: &Fonts, commands: &[PaintCmd]) -> Frame { let mut path_points = Vec::new(); let mut path_normals = Vec::new(); @@ -238,9 +224,6 @@ impl Painter { ); } } - PaintCmd::Clear { fill_color } => { - frame.clear_color = Some(*fill_color); - } PaintCmd::Line { points, color, @@ -338,7 +321,7 @@ impl Painter { text_style, x_offsets, } => { - let font = &self.fonts[*text_style]; + let font = &fonts[*text_style]; for (c, x_offset) in text.chars().zip(x_offsets.iter()) { if let Some(glyph) = font.uv_rect(c) { let mut top_left = Vertex { diff --git a/emigui/src/style.rs b/emigui/src/style.rs index 44c21139..baa36475 100644 --- a/emigui/src/style.rs +++ b/emigui/src/style.rs @@ -129,8 +129,6 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { rect: interact.rect, }); - // TODO: paint a little triangle or arrow or something instead of this - let (small_icon_rect, _) = style.icon_rectangles(&interact.rect); // Draw a minus: out_commands.push(PaintCmd::Line { @@ -215,12 +213,13 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { } } GuiCmd::Text { + color, pos, - text_style, text, + text_style, x_offsets, } => { - let color = style.text_color(); + let color = color.unwrap_or_else(|| style.text_color()); out_commands.push(PaintCmd::Text { color, text_style, @@ -237,7 +236,7 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { color: srgba(255, 255, 255, 255), // TODO width: 1.0, }), - rect: rect, + rect, }); } } diff --git a/emigui/src/texture_atlas.rs b/emigui/src/texture_atlas.rs index d127263e..2d7098b0 100644 --- a/emigui/src/texture_atlas.rs +++ b/emigui/src/texture_atlas.rs @@ -13,8 +13,8 @@ pub struct TextureAtlas { impl TextureAtlas { pub fn new(width: usize, height: usize) -> Self { TextureAtlas { - width: width, - height: height, + width, + height, pixels: vec![0; width * height], ..Default::default() } diff --git a/emigui/src/types.rs b/emigui/src/types.rs index b67387cd..57f99290 100644 --- a/emigui/src/types.rs +++ b/emigui/src/types.rs @@ -120,13 +120,12 @@ pub enum GuiCmd { min: f32, value: f32, }, - /// Paint a single line of mono-space text. - /// The text should start at the given position and flow to the right. - /// The text should be vertically centered at the given position. + /// A string of text with a position for each character. Text { + color: Option, pos: Vec2, - text_style: TextStyle, text: String, + text_style: TextStyle, /// Start each character in the text, as offset from pos. x_offsets: Vec, }, @@ -153,9 +152,6 @@ pub enum PaintCmd { outline: Option, radius: f32, }, - Clear { - fill_color: Color, - }, Line { points: Vec, color: Color, diff --git a/emigui/src/widgets.rs b/emigui/src/widgets.rs index b714c8d9..7724c4ab 100644 --- a/emigui/src/widgets.rs +++ b/emigui/src/widgets.rs @@ -17,6 +17,7 @@ pub trait Widget { pub struct Label { text: String, text_style: TextStyle, + text_color: Option, } impl Label { @@ -24,6 +25,7 @@ impl Label { Label { text: text.into(), text_style: TextStyle::Body, + text_color: None, } } @@ -31,6 +33,11 @@ impl Label { self.text_style = text_style; self } + + pub fn text_color(mut self, text_color: Color) -> Self { + self.text_color = Some(text_color); + self + } } pub fn label>(text: S) -> Label { @@ -42,7 +49,7 @@ impl Widget for Label { let font = ®ion.fonts()[self.text_style]; let (text, text_size) = font.layout_multiline(&self.text, region.width()); let interact = region.reserve_space(text_size, None); - region.add_text(interact.rect.min(), self.text_style, text); + region.add_text(interact.rect.min(), self.text_style, text, self.text_color); region.response(interact) } } @@ -51,11 +58,20 @@ impl Widget for Label { pub struct Button { text: String, + text_color: Option, } impl Button { pub fn new>(text: S) -> Self { - Button { text: text.into() } + Button { + text: text.into(), + text_color: None, + } + } + + pub fn text_color(mut self, text_color: Color) -> Self { + self.text_color = Some(text_color); + self } } @@ -69,7 +85,7 @@ impl Widget for Button { region.reserve_space(text_size + 2.0 * region.options().button_padding, Some(id)); let text_cursor = interact.rect.min() + region.options().button_padding; region.add_graphic(GuiCmd::Button { interact }); - region.add_text(text_cursor, text_style, text); + region.add_text(text_cursor, text_style, text, self.text_color); region.response(interact) } } @@ -80,6 +96,7 @@ impl Widget for Button { pub struct Checkbox<'a> { checked: &'a mut bool, text: String, + text_color: Option, } impl<'a> Checkbox<'a> { @@ -87,8 +104,14 @@ impl<'a> Checkbox<'a> { Checkbox { checked, text: text.into(), + text_color: None, } } + + pub fn text_color(mut self, text_color: Color) -> Self { + self.text_color = Some(text_color); + self + } } impl<'a> Widget for Checkbox<'a> { @@ -114,7 +137,7 @@ impl<'a> Widget for Checkbox<'a> { checked: *self.checked, interact, }); - region.add_text(text_cursor, text_style, text); + region.add_text(text_cursor, text_style, text, self.text_color); region.response(interact) } } @@ -125,6 +148,7 @@ impl<'a> Widget for Checkbox<'a> { pub struct RadioButton { checked: bool, text: String, + text_color: Option, } impl RadioButton { @@ -132,8 +156,14 @@ impl RadioButton { RadioButton { checked, text: text.into(), + text_color: None, } } + + pub fn text_color(mut self, text_color: Color) -> Self { + self.text_color = Some(text_color); + self + } } pub fn radio>(checked: bool, text: S) -> RadioButton { @@ -160,7 +190,7 @@ impl Widget for RadioButton { checked: self.checked, interact, }); - region.add_text(text_cursor, text_style, text); + region.add_text(text_cursor, text_style, text, self.text_color); region.response(interact) } } @@ -174,6 +204,7 @@ pub struct Slider<'a> { max: f32, id: Option, text: Option, + text_color: Option, text_on_top: Option, } @@ -186,6 +217,7 @@ impl<'a> Slider<'a> { id: None, text: None, text_on_top: None, + text_color: None, } } @@ -198,6 +230,11 @@ impl<'a> Slider<'a> { self.text = Some(text.into()); self } + + pub fn text_color(mut self, text_color: Color) -> Self { + self.text_color = Some(text_color); + self + } } impl<'a> Widget for Slider<'a> { @@ -207,8 +244,9 @@ impl<'a> Widget for Slider<'a> { if let Some(text) = &self.text { let text_on_top = self.text_on_top.unwrap_or_default(); + let text_color = self.text_color; let full_text = format!("{}: {:.3}", text, self.value); - let id = Some(self.id.unwrap_or(make_id(text))); + let id = Some(self.id.unwrap_or_else(|| make_id(text))); let mut naked = self; naked.id = id; naked.text = None; @@ -216,7 +254,7 @@ impl<'a> Widget for Slider<'a> { if text_on_top { let (text, text_size) = font.layout_multiline(&full_text, region.width()); let pos = region.reserve_space_without_padding(text_size); - region.add_text(pos, text_style, text); + region.add_text(pos, text_style, text, text_color); naked.add_to(region) } else { region.columns(2, |columns| { diff --git a/emigui_wasm/src/app.rs b/emigui_wasm/src/app.rs index d4f98060..9c02a0d1 100644 --- a/emigui_wasm/src/app.rs +++ b/emigui_wasm/src/app.rs @@ -3,7 +3,7 @@ use emigui::{math::*, types::*, widgets::*, Align, Region, TextStyle}; pub struct App { checked: bool, count: i32, - selected_alternative: i32, + radio: i32, size: Vec2, corner_radius: f32, @@ -14,7 +14,7 @@ impl Default for App { fn default() -> App { App { checked: true, - selected_alternative: 0, + radio: 0, count: 0, size: vec2(100.0, 50.0), corner_radius: 5.0, @@ -35,6 +35,11 @@ impl App { gui.input().screen_size.y, ))); + gui.horizontal(Align::Min, |gui| { + gui.add(label("Text can have").text_color(srgba(110, 255, 110, 255))); + gui.add(label("color").text_color(srgba(128, 140, 255, 255))); + }); + gui.add(label("Hover me")).tooltip_text( "This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.", ); @@ -42,35 +47,30 @@ impl App { gui.add(Checkbox::new(&mut self.checked, "checkbox")); gui.horizontal(Align::Min, |gui| { - if gui - .add(radio(self.selected_alternative == 0, "First")) - .clicked - { - self.selected_alternative = 0; + if gui.add(radio(self.radio == 0, "First")).clicked { + self.radio = 0; } - if gui - .add(radio(self.selected_alternative == 1, "Second")) - .clicked - { - self.selected_alternative = 1; + if gui.add(radio(self.radio == 1, "Second")).clicked { + self.radio = 1; } - if gui - .add(radio(self.selected_alternative == 2, "Final")) - .clicked - { - self.selected_alternative = 2; + if gui.add(radio(self.radio == 2, "Final")).clicked { + self.radio = 2; } }); - if gui - .add(Button::new("Click me")) - .tooltip_text("This will just increase a counter.") - .clicked - { - self.count += 1; - } - - gui.add(label(format!("This is a multiline label.\nThe button have been clicked {} times.\nBelow are more options.", self.count))); + gui.horizontal(Align::Min, |gui| { + if gui + .add(Button::new("Click me")) + .tooltip_text("This will just increase a counter.") + .clicked + { + self.count += 1; + } + gui.add(label(format!( + "The button have been clicked {} times", + self.count + ))); + }); gui.foldable("Test box rendering", |gui| { gui.add(Slider::new(&mut self.size.x, 0.0, 500.0).text("width"));