diff --git a/docs/emgui_bg.wasm b/docs/emgui_bg.wasm index da741051..1707e9b2 100644 Binary files a/docs/emgui_bg.wasm and b/docs/emgui_bg.wasm differ diff --git a/docs/frontend.js b/docs/frontend.js index 306da3e0..56f66a3d 100644 --- a/docs/frontend.js +++ b/docs/frontend.js @@ -63,8 +63,7 @@ function paint_command(canvas, cmd) { case "text": ctx.fillStyle = styleFromColor(cmd.fill_color); ctx.font = cmd.font_size + "px " + cmd.font_name; - ctx.textAlign = cmd.text_align; - ctx.textBaseline = "top"; + ctx.textBaseline = "middle"; ctx.fillText(cmd.text, cmd.pos.x, cmd.pos.y); return; } diff --git a/docs/frontend.ts b/docs/frontend.ts index f03e70e0..86169fa0 100644 --- a/docs/frontend.ts +++ b/docs/frontend.ts @@ -56,7 +56,6 @@ interface Text { pos: Vec2; stroke_color: Color | null; text: string; - text_align: "start" | "center" | "end"; } type PaintCmd = Circle | Clear | Line | Rect | Text; @@ -132,8 +131,7 @@ function paint_command(canvas, cmd: PaintCmd) { case "text": ctx.fillStyle = styleFromColor(cmd.fill_color); ctx.font = `${cmd.font_size}px ${cmd.font_name}`; - ctx.textAlign = cmd.text_align; - ctx.textBaseline = "top"; + ctx.textBaseline = "middle"; ctx.fillText(cmd.text, cmd.pos.x, cmd.pos.y); return; } diff --git a/src/app.rs b/src/app.rs index 2623282a..8598697f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -56,7 +56,7 @@ impl GuiSettings for App { self.count += 1; } - gui.label(format!("The button have been clicked {} times", self.count)); + gui.label(format!("This is a multiline label.\nThe button have been clicked {} times.\nBelow are more options.", self.count)); gui.foldable("Box rendering options", |gui| { gui.slider_f32("width", &mut self.width, 0.0, 500.0); @@ -78,9 +78,9 @@ impl GuiSettings for App { }])); gui.foldable("LayoutOptions", |gui| { - let mut layout_options = gui.layout_options; - layout_options.show_gui(gui); - gui.layout_options = layout_options; + let mut options = gui.options; + options.show_gui(gui); + gui.options = options; }); } } @@ -94,16 +94,11 @@ 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("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); gui.slider_f32("button_padding.y", &mut self.button_padding.y, 0.0, 20.0); - gui.slider_f32( - "checkbox_radio_height", - &mut self.checkbox_radio_height, - 0.0, - 60.0, - ); - gui.slider_f32("slider_height", &mut self.slider_height, 0.0, 60.0); + gui.slider_f32("start_icon_width", &mut self.start_icon_width, 0.0, 60.0); } } diff --git a/src/layout.rs b/src/layout.rs index 4cdfa1a4..2fa71a42 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -22,23 +22,20 @@ pub struct LayoutOptions { /// Button size is text size plus this on each side pub button_padding: Vec2, - /// Height of a checkbox and radio button - pub checkbox_radio_height: f32, - - /// Height of a slider - pub slider_height: f32, + /// Checkboxed, radio button and foldables have an icon at the start. + /// The text starts after this many pixels. + pub start_icon_width: f32, } impl Default for LayoutOptions { fn default() -> Self { LayoutOptions { char_size: vec2(7.2, 14.0), - item_spacing: vec2(5.0, 3.0), + item_spacing: vec2(8.0, 4.0), indent: 21.0, width: 200.0, - button_padding: vec2(8.0, 8.0), - checkbox_radio_height: 24.0, - slider_height: 32.0, + button_padding: vec2(5.0, 3.0), + start_icon_width: 20.0, } } } @@ -69,7 +66,7 @@ type Id = u64; #[derive(Clone, Debug, Default)] pub struct Layout { - pub layout_options: LayoutOptions, + pub options: LayoutOptions, pub input: GuiInput, pub cursor: Vec2, id: Id, @@ -92,23 +89,26 @@ impl Layout { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.cursor + self.layout_options.button_padding; + let text_cursor = self.cursor + self.options.button_padding; let (rect, interact) = - self.reserve_space(id, text_size + 2.0 * self.layout_options.button_padding); + self.reserve_space(id, text_size + 2.0 * self.options.button_padding); self.commands.push(GuiCmd::Button { interact, rect }); self.add_text(text_cursor, text); interact } - pub fn checkbox>(&mut self, label: S, checked: &mut bool) -> InteractInfo { - let label: String = label.into(); - let id = self.get_id(&label); + pub fn checkbox>(&mut self, text: S, checked: &mut bool) -> InteractInfo { + let text: String = text.into(); + let id = self.get_id(&text); + let (text, text_size) = self.layout_text(&text); + let text_cursor = + self.cursor + self.options.button_padding + vec2(self.options.start_icon_width, 0.0); let (rect, interact) = self.reserve_space( id, - Vec2 { - x: self.layout_options.width, - y: self.layout_options.checkbox_radio_height, - }, + self.options.button_padding + + vec2(self.options.start_icon_width, 0.0) + + text_size + + self.options.button_padding, ); if interact.clicked { *checked = !*checked; @@ -117,8 +117,8 @@ impl Layout { checked: *checked, interact, rect, - text: label, }); + self.add_text(text_cursor, text); interact } @@ -127,57 +127,68 @@ impl Layout { let (text, text_size) = self.layout_text(&text); self.add_text(self.cursor, text); self.cursor.y += text_size.y; - self.cursor.y += self.layout_options.item_spacing.y; + self.cursor.y += self.options.item_spacing.y; } /// A radio button - pub fn radio>(&mut self, label: S, checked: bool) -> InteractInfo { - let label: String = label.into(); - let id = self.get_id(&label); + pub fn radio>(&mut self, text: S, checked: bool) -> InteractInfo { + let text: String = text.into(); + let id = self.get_id(&text); + let (text, text_size) = self.layout_text(&text); + let text_cursor = + self.cursor + self.options.button_padding + vec2(self.options.start_icon_width, 0.0); let (rect, interact) = self.reserve_space( id, - Vec2 { - x: self.layout_options.width, - y: self.layout_options.checkbox_radio_height, - }, + self.options.button_padding + + vec2(self.options.start_icon_width, 0.0) + + text_size + + self.options.button_padding, ); self.commands.push(GuiCmd::RadioButton { checked, interact, rect, - text: label, }); + self.add_text(text_cursor, text); interact } pub fn slider_f32>( &mut self, - label: S, + text: S, value: &mut f32, min: f32, max: f32, ) -> InteractInfo { debug_assert!(min <= max); - let label: String = label.into(); - let id = self.get_id(&label); - let (rect, interact) = self.reserve_space( + let text: String = text.into(); + let id = self.get_id(&text); + let (text, text_size) = self.layout_text(&format!("{}: {:.3}", text, value)); + self.add_text(self.cursor, text); + self.cursor.y += text_size.y; + let (slider_rect, interact) = self.reserve_space( id, Vec2 { - x: self.layout_options.width, - y: self.layout_options.slider_height, + x: self.options.width, + y: self.options.char_size.y, }, ); if interact.active { - *value = remap_clamp(self.input.mouse_pos.x, rect.min().x, rect.max().x, min, max); + *value = remap_clamp( + self.input.mouse_pos.x, + slider_rect.min().x, + slider_rect.max().x, + min, + max, + ); } self.commands.push(GuiCmd::Slider { interact, - label, max, min, - rect, + rect: slider_rect, value: *value, }); @@ -195,12 +206,12 @@ impl Layout { let text: String = text.into(); let id = self.get_id(&text); let (text, text_size) = self.layout_text(&text); - let text_cursor = self.cursor + self.layout_options.button_padding; + let text_cursor = self.cursor + self.options.button_padding; let (rect, interact) = self.reserve_space( id, vec2( - self.layout_options.width, - text_size.y + 2.0 * self.layout_options.button_padding.y, + self.options.width, + text_size.y + 2.0 * self.options.button_padding.y, ), ); @@ -218,14 +229,13 @@ impl Layout { rect, open, }); - let icon_width = 16.0; // TODO: this offset is ugly - self.add_text(text_cursor + vec2(icon_width, 0.0), text); + self.add_text(text_cursor + vec2(self.options.start_icon_width, 0.0), text); if open { let old_id = self.id; self.id = id; let old_x = self.cursor.x; - self.cursor.x += self.layout_options.indent; + self.cursor.x += self.options.indent; add_contents(self); self.cursor.x = old_x; self.id = old_id; @@ -242,7 +252,7 @@ impl Layout { size, }; let interact = self.interactive_rect(id, &rect); - self.cursor.y += rect.size.y + self.layout_options.item_spacing.y; + self.cursor.y += rect.size.y + self.options.item_spacing.y; (rect, interact) } @@ -270,7 +280,7 @@ impl Layout { } fn layout_text(&self, text: &str) -> (TextFragments, Vec2) { - let char_size = self.layout_options.char_size; + let char_size = self.options.char_size; let mut cursor_y = 0.0; let mut max_width = 0.0; let mut text_fragments = Vec::new(); @@ -293,10 +303,9 @@ impl Layout { fn add_text(&mut self, pos: Vec2, text: Vec) { for fragment in text { self.commands.push(GuiCmd::Text { - pos: pos + fragment.rect.pos, + pos: pos + vec2(fragment.rect.pos.x, fragment.rect.center().y), style: TextStyle::Label, text: fragment.text, - text_align: TextAlign::Start, }); } } diff --git a/src/style.rs b/src/style.rs index 0c8f34dd..633da9d3 100644 --- a/src/style.rs +++ b/src/style.rs @@ -59,8 +59,8 @@ impl Style { } } - /// Returns small icon rectangle, big icon rectangle, and the remaining rectangle - fn icon_rectangles(&self, rect: &Rect) -> (Rect, Rect, Rect) { + /// Returns small icon rectangle and big icon rectangle + fn icon_rectangles(&self, rect: &Rect) -> (Rect, Rect) { let box_side = 16.0; let big_icon_rect = Rect::from_center_size( vec2(rect.min().x + 4.0 + box_side * 0.5, rect.center().y), @@ -69,10 +69,7 @@ impl Style { let small_icon_rect = Rect::from_center_size(big_icon_rect.center(), vec2(10.0, 10.0)); - let rest_rect = - Rect::from_min_size(vec2(big_icon_rect.max().x + 4.0, rect.min().y), rect.size); - - (small_icon_rect, big_icon_rect, rest_rect) + (small_icon_rect, big_icon_rect) } } @@ -109,9 +106,8 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { checked, interact, rect, - text, } => { - let (small_icon_rect, big_icon_rect, rest_rect) = style.icon_rectangles(&rect); + let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect); out_commands.push(PaintCmd::Rect { corner_radius: 3.0, fill_color: Some(style.interact_fill_color(&interact)), @@ -134,18 +130,6 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { }); } - out_commands.push(PaintCmd::Text { - fill_color: stroke_color, - font_name: style.font_name.clone(), - font_size: style.font_size, - pos: Vec2 { - x: rest_rect.min().x, - y: rest_rect.center().y - 4.0, - }, - text, - text_align: TextAlign::Start, - }); - if style.debug_rects { out_commands.push(debug_rect(rect)); } @@ -168,7 +152,7 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { // TODO: paint a little triangle or arrow or something instead of this - let (small_icon_rect, _, _) = style.icon_rectangles(&rect); + let (small_icon_rect, _) = style.icon_rectangles(&rect); // Draw a minus: out_commands.push(PaintCmd::Line { points: vec![ @@ -178,7 +162,7 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { color: stroke_color, width: style.line_width, }); - if open { + if !open { // Draw it as a plus: out_commands.push(PaintCmd::Line { points: vec![ @@ -194,12 +178,11 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { checked, interact, rect, - text, } => { let fill_color = style.interact_fill_color(&interact); let stroke_color = style.interact_stroke_color(&interact); - let (small_icon_rect, big_icon_rect, rest_rect) = style.icon_rectangles(&rect); + let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect); out_commands.push(PaintCmd::Circle { center: big_icon_rect.center(), @@ -217,35 +200,18 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { }); } - out_commands.push(PaintCmd::Text { - fill_color: stroke_color, - font_name: style.font_name.clone(), - font_size: style.font_size, - pos: Vec2 { - x: rest_rect.min().x, - y: rect.center().y - 4.0, - }, - text, - text_align: TextAlign::Start, - }); - if style.debug_rects { out_commands.push(debug_rect(rect)); } } GuiCmd::Slider { interact, - label, max, min, rect, value, } => { - let thin_rect = Rect::from_min_size( - vec2(rect.min().x, lerp(rect.min().y, rect.max().y, 2.0 / 3.0)), - vec2(rect.size.x, 8.0), - ); - + let thin_rect = Rect::from_center_size(rect.center(), vec2(rect.size.x, 6.0)); let marker_center_x = remap_clamp(value, min, max, rect.min().x, rect.max().x); let marker_rect = Rect::from_center_size( @@ -269,18 +235,6 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { size: marker_rect.size, }); - out_commands.push(PaintCmd::Text { - fill_color: style.interact_stroke_color(&interact), - font_name: style.font_name.clone(), - font_size: style.font_size, - pos: vec2( - rect.min().x, - lerp(rect.min().y, rect.max().y, 1.0 / 3.0) - 5.0, - ), - text: format!("{}: {:.3}", label, value), - text_align: TextAlign::Start, - }); - if style.debug_rects { out_commands.push(debug_rect(rect)); } @@ -288,7 +242,6 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { GuiCmd::Text { pos, text, - text_align, style: text_style, } => { let fill_color = match text_style { @@ -298,9 +251,8 @@ fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { fill_color, font_name: style.font_name.clone(), font_size: style.font_size, - pos: pos + vec2(0.0, style.font_size / 2.0 - 5.0), // TODO + pos, text, - text_align, }); } } diff --git a/src/types.rs b/src/types.rs index a6313460..89aa7698 100644 --- a/src/types.rs +++ b/src/types.rs @@ -74,14 +74,6 @@ pub struct InteractInfo { pub active: bool, } -#[derive(Clone, Copy, Debug, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum TextAlign { - Start, // Test with arabic text - Center, - End, -} - #[derive(Clone, Copy, Debug, Serialize)] #[serde(rename_all = "snake_case")] pub enum TextStyle { @@ -100,7 +92,6 @@ pub enum GuiCmd { checked: bool, interact: InteractInfo, rect: Rect, - text: String, }, /// The header button background for a foldable region FoldableHeader { @@ -112,21 +103,21 @@ pub enum GuiCmd { checked: bool, interact: InteractInfo, rect: Rect, - text: String, }, Slider { interact: InteractInfo, - label: String, max: f32, min: f32, rect: Rect, 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. Text { pos: Vec2, style: TextStyle, text: String, - text_align: TextAlign, }, } @@ -162,6 +153,9 @@ pub enum PaintCmd { pos: Vec2, size: Vec2, }, + /// 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. Text { fill_color: Color, /// Name, e.g. Palatino @@ -170,6 +164,5 @@ pub enum PaintCmd { font_size: f32, pos: Vec2, text: String, - text_align: TextAlign, }, }