From 95787736131bbc2c6c08698946be7f0d840f646f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 8 Sep 2020 09:07:08 +0200 Subject: [PATCH] [style] tweak style, and refactor struct Style --- egui/src/containers/frame.rs | 27 ++++++---- egui/src/containers/resize.rs | 2 +- egui/src/containers/scroll_area.rs | 10 ++-- egui/src/containers/window.rs | 6 +-- egui/src/menu.rs | 42 +++++++-------- egui/src/style.rs | 86 +++++++++++++++--------------- egui/src/ui.rs | 2 +- egui/src/widgets.rs | 61 +++++++-------------- egui/src/widgets/slider.rs | 23 ++++---- egui/src/widgets/text_edit.rs | 13 +++-- 10 files changed, 131 insertions(+), 141 deletions(-) diff --git a/egui/src/containers/frame.rs b/egui/src/containers/frame.rs index 72fcf386..cfd15782 100644 --- a/egui/src/containers/frame.rs +++ b/egui/src/containers/frame.rs @@ -3,7 +3,7 @@ use crate::{layers::PaintCmdIdx, paint::*, *}; /// Adds a rectangular frame and background to some `Ui`. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Frame { // On each side pub margin: Vec2, @@ -13,12 +13,21 @@ pub struct Frame { } impl Frame { + pub fn none() -> Self { + Self { + margin: Vec2::zero(), + corner_radius: 0.0, + fill: Default::default(), + stroke: Stroke::none(), + } + } + pub fn window(style: &Style) -> Self { Self { margin: style.spacing.window_padding, corner_radius: style.visuals.window_corner_radius, - fill: style.visuals.background_fill, - stroke: style.visuals.interacted.inactive.bg_stroke, // because we can resize windows + fill: style.visuals.widgets.noninteractive.bg_fill, + stroke: style.visuals.widgets.inactive.bg_stroke, // because we can resize windows } } @@ -26,8 +35,8 @@ impl Frame { Self { margin: Vec2::splat(1.0), corner_radius: 0.0, - fill: style.visuals.background_fill, - stroke: Stroke::new(0.5, Srgba::gray(128)), + fill: style.visuals.widgets.noninteractive.bg_fill, + stroke: style.visuals.widgets.noninteractive.bg_stroke, } } @@ -35,8 +44,8 @@ impl Frame { Self { margin: Vec2::splat(1.0), corner_radius: 2.0, - fill: style.visuals.background_fill, - stroke: Stroke::new(1.0, Srgba::gray(128)), + fill: style.visuals.widgets.noninteractive.bg_fill, + stroke: style.visuals.widgets.noninteractive.bg_stroke, } } @@ -44,8 +53,8 @@ impl Frame { Self { margin: style.spacing.window_padding, corner_radius: 5.0, - fill: style.visuals.background_fill, - stroke: Stroke::new(1.0, Srgba::gray(128)), + fill: style.visuals.widgets.noninteractive.bg_fill, + stroke: style.visuals.widgets.noninteractive.bg_stroke, } } diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs index de9a247f..5a11ecc7 100644 --- a/egui/src/containers/resize.rs +++ b/egui/src/containers/resize.rs @@ -238,7 +238,7 @@ impl Resize { rect, corner_radius: 3.0, fill: Default::default(), - stroke: ui.style().visuals.thin_stroke, + stroke: ui.style().visuals.widgets.noninteractive.bg_stroke, }); } diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index c29c622b..ae517090 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -259,22 +259,22 @@ impl Prepared { ); } - let style = ui.style(); - let handle_fill = style.interact(&response).fg_fill; - let handle_stroke = style.interact(&response).bg_stroke; + let visuals = ui.style().interact(&response); ui.painter().add(paint::PaintCmd::Rect { rect: outer_scroll_rect, corner_radius, fill: ui.style().visuals.dark_bg_color, stroke: Default::default(), + // fill: visuals.bg_fill, + // stroke: visuals.bg_stroke, }); ui.painter().add(paint::PaintCmd::Rect { rect: handle_rect.expand(-2.0), corner_radius, - fill: handle_fill, - stroke: handle_stroke, + fill: visuals.fg_fill, + stroke: visuals.fg_stroke, }); } diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 3189b553..42f5c7df 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -294,14 +294,14 @@ impl<'open> Window<'open> { &mut area_content_ui, outer_rect, interaction, - ctx.style().visuals.interacted.active, + ctx.style().visuals.widgets.active, ); } else if let Some(hover_interaction) = hover_interaction { paint_frame_interaction( &mut area_content_ui, outer_rect, hover_interaction, - ctx.style().visuals.interacted.hovered, + ctx.style().visuals.widgets.hovered, ); } } @@ -678,7 +678,7 @@ impl TitleBar { let y = content_rect.top() + ui.style().spacing.item_spacing.y * 0.5; ui.painter().line_segment( [pos2(left, y), pos2(right, y)], - ui.style().visuals.interacted.inactive.bg_stroke, + ui.style().visuals.widgets.inactive.bg_stroke, ); } diff --git a/egui/src/menu.rs b/egui/src/menu.rs index 55cd9d9c..c4cdec89 100644 --- a/egui/src/menu.rs +++ b/egui/src/menu.rs @@ -62,12 +62,12 @@ pub fn bar(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect) Frame::menu_bar(ui.style()).show(ui, |ui| { let mut style = ui.style().clone(); style.spacing.button_padding = vec2(2.0, 0.0); - // style.visuals.interacted.active.bg_fill = TRANSPARENT; - style.visuals.interacted.active.bg_stroke = Stroke::none(); - // style.visuals.interacted.hovered.bg_fill = TRANSPARENT; - style.visuals.interacted.hovered.bg_stroke = Stroke::none(); - style.visuals.interacted.inactive.bg_fill = TRANSPARENT; - style.visuals.interacted.inactive.bg_stroke = Stroke::none(); + // style.visuals.widgets.active.bg_fill = TRANSPARENT; + style.visuals.widgets.active.bg_stroke = Stroke::none(); + // style.visuals.widgets.hovered.bg_fill = TRANSPARENT; + style.visuals.widgets.hovered.bg_stroke = Stroke::none(); + style.visuals.widgets.inactive.bg_fill = TRANSPARENT; + style.visuals.widgets.inactive.bg_stroke = Stroke::none(); ui.set_style(style); // Take full width and fixed height: @@ -77,7 +77,7 @@ pub fn bar(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect) let ret = add_contents(ui); - let clicked_outside = !ui.hovered(ui.rect()) && ui.input().mouse.released; + let clicked_outside = !ui.hovered(ui.rect()) && ui.input().mouse.click; if clicked_outside || ui.input().key_pressed(Key::Escape) { // TODO: this prevents sub-menus in menus. We should fix that. let bar_id = ui.id(); @@ -108,7 +108,7 @@ fn menu_impl<'c>( let mut button = Button::new(title); if bar_state.open_menu == Some(menu_id) { - button = button.fill(Some(ui.style().visuals.interacted.active.fg_fill)); + button = button.fill(Some(ui.style().visuals.widgets.active.fg_fill)); } let button_response = ui.add(button); @@ -121,23 +121,19 @@ fn menu_impl<'c>( .fixed_pos(button_response.rect.left_bottom()); let frame = Frame::menu(ui.style()); - let resize = Resize::default().auto_sized().with_stroke(false); - let menu_response = area.show(ui.ctx(), |ui| { frame.show(ui, |ui| { - resize.show(ui, |ui| { - let mut style = ui.style().clone(); - style.spacing.button_padding = vec2(2.0, 0.0); - // style.visuals.interacted.active.bg_fill = TRANSPARENT; - style.visuals.interacted.active.bg_stroke = Stroke::none(); - // style.visuals.interacted.hovered.bg_fill = TRANSPARENT; - style.visuals.interacted.hovered.bg_stroke = Stroke::none(); - style.visuals.interacted.inactive.bg_fill = TRANSPARENT; - style.visuals.interacted.inactive.bg_stroke = Stroke::none(); - ui.set_style(style); - ui.set_layout(Layout::justified(Direction::Vertical)); - add_contents(ui) - }) + let mut style = ui.style().clone(); + style.spacing.button_padding = vec2(2.0, 0.0); + // style.visuals.widgets.active.bg_fill = TRANSPARENT; + style.visuals.widgets.active.bg_stroke = Stroke::none(); + // style.visuals.widgets.hovered.bg_fill = TRANSPARENT; + style.visuals.widgets.hovered.bg_stroke = Stroke::none(); + style.visuals.widgets.inactive.bg_fill = TRANSPARENT; + style.visuals.widgets.inactive.bg_stroke = Stroke::none(); + ui.set_style(style); + ui.set_layout(Layout::justified(Direction::Vertical)); + add_contents(ui) }) }); diff --git a/egui/src/style.rs b/egui/src/style.rs index 0b8827e6..ad4ffc06 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -26,7 +26,7 @@ impl Style { // TODO: rename style.interact() to maybe... `style.response_visuals` ? /// Use this style for interactive things pub fn interact(&self, response: &Response) -> &WidgetVisuals { - self.visuals.interacted.style(response) + self.visuals.widgets.style(response) } } @@ -51,6 +51,7 @@ pub struct Spacing { pub indent: f32, /// Anything clickable is (at least) this wide. + /// TODO: rename `button_height` or something? pub clickable_diameter: f32, /// Total width of a slider @@ -93,20 +94,11 @@ pub struct Interaction { #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Visuals { - pub interacted: Interacted, + pub widgets: Widgets, - pub text_color: Srgba, - - /// For stuff like check marks in check boxes. - pub line_width: f32, - - pub thin_stroke: Stroke, - - /// e.g. the background of windows - pub background_fill: Srgba, - - /// e.g. the background of the slider or text edit - pub dark_bg_color: Srgba, + /// e.g. the background of the slider or text edit, + /// needs to look different from other interactive stuff. + pub dark_bg_color: Srgba, // TODO: remove, rename, or clarify what it is for pub window_corner_radius: f32, @@ -125,16 +117,28 @@ pub struct Visuals { pub debug_resize: bool, } -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Interacted { - pub active: WidgetVisuals, - pub hovered: WidgetVisuals, - pub inactive: WidgetVisuals, - pub disabled: WidgetVisuals, +impl Visuals { + pub fn text_color(&self) -> Srgba { + self.widgets.noninteractive.text_color() + } } -impl Interacted { +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct Widgets { + /// For an interactive widget that is being interacted with + pub active: WidgetVisuals, + /// For an interactive widget that is being hovered + pub hovered: WidgetVisuals, + /// For an interactive widget that is "resting" + pub inactive: WidgetVisuals, + /// For an otherwise interactive widget that has been disabled + pub disabled: WidgetVisuals, + /// For a non-interactive widget + pub noninteractive: WidgetVisuals, +} + +impl Widgets { pub fn style(&self, response: &Response) -> &WidgetVisuals { if response.active || response.has_kb_focus { &self.active @@ -218,11 +222,7 @@ impl Default for Interaction { impl Default for Visuals { fn default() -> Self { Self { - interacted: Default::default(), - text_color: Srgba::gray(160), - line_width: 0.5, - thin_stroke: Stroke::new(0.5, GRAY), - background_fill: Rgba::luminance_alpha(0.013, 0.95).into(), + widgets: Default::default(), dark_bg_color: Srgba::black_alpha(140), window_corner_radius: 10.0, resize_corner_size: 12.0, @@ -235,7 +235,7 @@ impl Default for Visuals { } } -impl Default for Interacted { +impl Default for Widgets { fn default() -> Self { Self { active: WidgetVisuals { @@ -257,14 +257,21 @@ impl Default for Interacted { bg_stroke: Stroke::new(1.0, Rgba::white_alpha(0.04)), corner_radius: 4.0, fg_fill: srgba(60, 60, 80, 255), - fg_stroke: Stroke::new(0.5, Srgba::gray(200)), // Should NOT look grayed out! + fg_stroke: Stroke::new(1.0, Srgba::gray(200)), // Should NOT look grayed out! }, disabled: WidgetVisuals { bg_fill: TRANSPARENT, bg_stroke: Stroke::new(0.5, Srgba::gray(70)), corner_radius: 4.0, fg_fill: srgba(50, 50, 50, 255), - fg_stroke: Stroke::new(0.5, Srgba::gray(128)), // Should look grayed out + fg_stroke: Stroke::new(1.0, Srgba::gray(128)), // Should look grayed out + }, + noninteractive: WidgetVisuals { + bg_stroke: Stroke::new(1.0, Rgba::white_alpha(0.03)), + bg_fill: Rgba::luminance_alpha(0.013, 0.95).into(), + corner_radius: 4.0, + fg_fill: Default::default(), + fg_stroke: Stroke::new(1.0, Srgba::gray(160)), // text color }, } } @@ -348,7 +355,7 @@ impl Interaction { } } -impl Interacted { +impl Widgets { pub fn ui(&mut self, ui: &mut crate::Ui) { if ui.add(Button::new("Reset")).clicked { *self = Default::default(); @@ -359,8 +366,11 @@ impl Interacted { hovered, inactive, disabled, + noninteractive, } = self; + ui.collapsing("noninteractive", |ui| noninteractive.ui(ui)); + ui.heading("Interactive:"); ui.collapsing("active", |ui| active.ui(ui)); ui.collapsing("hovered", |ui| hovered.ui(ui)); ui.collapsing("inactive", |ui| inactive.ui(ui)); @@ -382,7 +392,7 @@ impl WidgetVisuals { bg_stroke.ui(ui, "bg_stroke"); ui.add(Slider::f32(corner_radius, 0.0..=10.0).text("corner_radius")); ui_color(ui, fg_fill, "fg_fill"); - fg_stroke.ui(ui, "fg_stroke"); + fg_stroke.ui(ui, "fg_stroke (text)"); } } @@ -393,11 +403,7 @@ impl Visuals { } let Self { - interacted, - text_color, - line_width, - thin_stroke, - background_fill, + widgets, dark_bg_color, window_corner_radius, resize_corner_size, @@ -408,11 +414,7 @@ impl Visuals { debug_resize, } = self; - ui.collapsing("interacted", |ui| interacted.ui(ui)); - ui_color(ui, text_color, "text_color"); - ui.add(Slider::f32(line_width, 0.0..=10.0).text("line_width")); - thin_stroke.ui(ui, "thin_stroke"); - ui_color(ui, background_fill, "background_fill"); + ui.collapsing("widgets", |ui| widgets.ui(ui)); ui_color(ui, dark_bg_color, "dark_bg_color"); ui.add(Slider::f32(window_corner_radius, 0.0..=20.0).text("window_corner_radius")); ui.add(Slider::f32(resize_corner_size, 0.0..=20.0).text("resize_corner_size")); diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 58c5bed0..8f91d449 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -556,7 +556,7 @@ impl Ui { let line_end = pos2(line_start.x, line_start.y + size.y - 2.0); self.painter.line_segment( [line_start, line_end], - (self.style().visuals.line_width, Srgba::gray(150)), + self.style().visuals.widgets.noninteractive.bg_stroke, ); (ret, self.allocate_space(indent + size)) diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index 0e4f905e..6999e293 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -116,7 +116,7 @@ impl Label { let text_style = self.text_style_or_default(ui.style()); let text_color = self .text_color - .unwrap_or_else(|| ui.style().visuals.text_color); + .unwrap_or_else(|| ui.style().visuals.text_color()); ui.painter().galley(pos, galley, text_style, text_color); } @@ -201,9 +201,11 @@ impl Widget for Hyperlink { ui.ctx().output().cursor_icon = CursorIcon::PointingHand; } if response.clicked { - ui.ctx().output().open_url = Some(url); + ui.ctx().output().open_url = Some(url.clone()); } + let style = ui.style().interact(&response); + if response.hovered { // Underline: for line in &galley.lines { @@ -214,7 +216,7 @@ impl Widget for Hyperlink { let max_x = pos.x + line.max_x(); ui.painter().line_segment( [pos2(min_x, y), pos2(max_x, y)], - (ui.style().visuals.line_width, color), + (style.fg_stroke.width, color), ); } } @@ -222,7 +224,7 @@ impl Widget for Hyperlink { ui.painter() .galley(response.rect.min, galley, text_style, color); - response + response.tooltip_text(url) } } @@ -254,6 +256,11 @@ impl Button { self } + pub fn text_color_opt(mut self, text_color: Option) -> Self { + self.text_color = text_color; + self + } + pub fn text_style(mut self, text_style: TextStyle) -> Self { self.text_style = text_style; self @@ -484,25 +491,12 @@ impl Widget for RadioButton { /// A visual separator. A horizontal or vertical line (depending on `Layout`). pub struct Separator { - line_width: Option, spacing: f32, - extra: f32, - color: Srgba, } impl Separator { pub fn new() -> Self { - Self { - line_width: None, - spacing: 6.0, - extra: 0.0, - color: Srgba::gray(128), // TODO: from style - } - } - - pub fn line_width(mut self, line_width: f32) -> Self { - self.line_width = Some(line_width); - self + Self { spacing: 6.0 } } /// How much space we take up. The line is painted in the middle of this. @@ -510,29 +504,11 @@ impl Separator { self.spacing = spacing; self } - - /// Draw this much longer on each side - pub fn extra(mut self, extra: f32) -> Self { - self.extra = extra; - self - } - - pub fn color(mut self, color: Srgba) -> Self { - self.color = color; - self - } } impl Widget for Separator { fn ui(self, ui: &mut Ui) -> Response { - let Separator { - line_width, - spacing, - extra, - color, - } = self; - - let line_width = line_width.unwrap_or_else(|| ui.style().visuals.line_width); + let Separator { spacing } = self; let available_space = ui.available_finite().size(); @@ -541,8 +517,8 @@ impl Widget for Separator { let rect = ui.allocate_space(vec2(spacing, available_space.y)); ( [ - pos2(rect.center().x, rect.top() - extra), - pos2(rect.center().x, rect.bottom() + extra), + pos2(rect.center().x, rect.top()), + pos2(rect.center().x, rect.bottom()), ], rect, ) @@ -551,14 +527,15 @@ impl Widget for Separator { let rect = ui.allocate_space(vec2(available_space.x, spacing)); ( [ - pos2(rect.left() - extra, rect.center().y), - pos2(rect.right() + extra, rect.center().y), + pos2(rect.left(), rect.center().y), + pos2(rect.right(), rect.center().y), ], rect, ) } }; - ui.painter().line_segment(points, (line_width, color)); + let stroke = ui.style().visuals.widgets.noninteractive.bg_stroke; + ui.painter().line_segment(points, stroke); ui.interact_hover(rect) } } diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index 8c4c653d..7fc55722 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -178,8 +178,8 @@ impl<'a> Slider<'a> { ui.painter().add(PaintCmd::Rect { rect: rail_rect, corner_radius: rail_radius, - fill: ui.style().visuals.interacted.inactive.bg_fill, - stroke: ui.style().visuals.interacted.inactive.bg_stroke, + fill: ui.style().visuals.widgets.inactive.bg_fill, + stroke: ui.style().visuals.widgets.inactive.bg_stroke, }); ui.painter().add(PaintCmd::Circle { @@ -193,11 +193,11 @@ impl<'a> Slider<'a> { /// Just the text label fn text_ui(&mut self, ui: &mut Ui, x_range: RangeInclusive) { - let text_color = self - .text_color - .unwrap_or_else(|| ui.style().visuals.text_color); - if let Some(label_text) = self.text.as_deref() { + let text_color = self + .text_color + .unwrap_or_else(|| ui.style().visuals.text_color()); + ui.style_mut().spacing.item_spacing.x = 0.0; ui.add( Label::new(format!("{}: ", label_text)) @@ -223,7 +223,7 @@ impl<'a> Slider<'a> { .id(kb_edit_id) .multiline(false) .desired_width(0.0) - .text_color(text_color) + .text_color_opt(self.text_color) .text_style(TextStyle::Monospace), ); if let Ok(value) = value_text.parse() { @@ -236,13 +236,12 @@ impl<'a> Slider<'a> { } } else { let response = ui.add( - Label::new(value_text) - .multiline(false) - .text_color(text_color) - .text_style(TextStyle::Monospace), + Button::new(value_text) + .text_style(TextStyle::Monospace) + .text_color_opt(self.text_color), ); let response = response.tooltip_text("Click to enter a value"); - let response = ui.interact(response.rect, kb_edit_id, Sense::click()); + // let response = ui.interact(response.rect, kb_edit_id, Sense::click()); if response.clicked { ui.memory().request_kb_focus(kb_edit_id); ui.memory().temp_edit_string = None; // Filled in next frame diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index b88ac072..90c6cddf 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -55,6 +55,11 @@ impl<'t> TextEdit<'t> { self } + pub fn text_color_opt(mut self, text_color: Option) -> Self { + self.text_color = text_color; + self + } + pub fn multiline(mut self, multiline: bool) -> Self { self.multiline = multiline; self @@ -178,14 +183,16 @@ impl<'t> Widget for TextEdit<'t> { } let painter = ui.painter(); + let visuals = ui.style().interact(&response); { let bg_rect = response.rect.expand(2.0); // breathing room for content painter.add(PaintCmd::Rect { rect: bg_rect, - corner_radius: ui.style().interact(&response).corner_radius, + corner_radius: visuals.corner_radius, fill: ui.style().visuals.dark_bg_color, - stroke: ui.style().interact(&response).bg_stroke, + // fill: visuals.bg_fill, + stroke: visuals.bg_stroke, }); } @@ -209,7 +216,7 @@ impl<'t> Widget for TextEdit<'t> { } } - let text_color = text_color.unwrap_or_else(|| ui.style().interact(&response).text_color()); + let text_color = text_color.unwrap_or_else(|| visuals.text_color()); painter.galley(response.rect.min, galley, text_style, text_color); ui.memory().text_edit.insert(id, state); response