From e7b098ac56c060f01d971c97437ee8a81b14062f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 1 Sep 2020 20:40:54 +0200 Subject: [PATCH] [style] make the default style a lot more compact and sleek --- TODO.md | 2 +- egui/src/containers/collapsing_header.rs | 15 +++--- egui/src/containers/resize.rs | 2 +- egui/src/context.rs | 9 +++- egui/src/style.rs | 60 ++++++++++++++---------- egui/src/widgets.rs | 59 +++++++++++++---------- egui/src/widgets/slider.rs | 4 +- egui/src/widgets/text_edit.rs | 2 +- 8 files changed, 89 insertions(+), 64 deletions(-) diff --git a/TODO.md b/TODO.md index 88b19acc..fffd75e2 100644 --- a/TODO.md +++ b/TODO.md @@ -45,11 +45,11 @@ TODO-list for the Egui project. If you looking for something to do, look here. * [x] Pixel-perfect painting (round positions to nearest pixel). * [x] Fix `aa_size`: should be 1, currently fudged at 1.5 * [x] Fix thin rounded corners rendering bug (too bright) + * [x] Smoother animation (e.g. ease-out)? NO: animation are too brief for subtelty * [ ] Veriy alpha and sRGB correctness * [x] sRGBA decode in fragment shader * [ ] Thin circles look bad * [ ] Color picker - * [ ] Smoother animation (e.g. ease-out)? * Math * [ ] Change `width.min(max_width)` to `width.at_most(max_width)` diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs index 75cb077d..422703aa 100644 --- a/egui/src/containers/collapsing_header.rs +++ b/egui/src/containers/collapsing_header.rs @@ -2,7 +2,7 @@ use std::hash::Hash; use crate::{ layout::Direction, - paint::{LineStyle, PaintCmd, TextStyle}, + paint::{PaintCmd, TextStyle}, widgets::Label, *, }; @@ -105,8 +105,7 @@ impl State { /// Paint the arrow icon that indicated if the region is open or not pub fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) { - let stroke_color = ui.style().interact(response).stroke_color; - let stroke_width = ui.style().interact(response).stroke_width; + let line_style = ui.style().interact(response).line_style(); let rect = response.rect; @@ -124,7 +123,7 @@ pub fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) { points, closed: true, fill: Default::default(), - outline: LineStyle::new(stroke_width, stroke_color), + outline: line_style, }); } @@ -183,17 +182,19 @@ impl CollapsingHeader { let available = ui.available_finite(); let text_pos = available.min + vec2(ui.style().spacing.indent, 0.0); - let galley = label.layout_width(ui, available.width() - ui.style().spacing.indent); + let galley = label.layout_width(ui, available.right() - text_pos.x); let text_max_x = text_pos.x + galley.size.x; let desired_width = text_max_x - available.left(); let desired_width = desired_width.max(available.width()); - let size = vec2( + let mut desired_size = vec2( desired_width, galley.size.y + 2.0 * ui.style().spacing.button_padding.y, ); + desired_size.y = desired_size.y.max(ui.style().spacing.clickable_diameter); + let rect = ui.allocate_space(desired_size); + let rect = rect.expand2(ui.style().spacing.button_expand); - let rect = ui.allocate_space(size); let response = ui.interact(rect, id, Sense::click()); let text_pos = pos2(text_pos.x, response.rect.center().y - galley.size.y / 2.0); diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs index f0fdd227..77a9d83d 100644 --- a/egui/src/containers/resize.rs +++ b/egui/src/containers/resize.rs @@ -280,7 +280,7 @@ pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, style: LineStyle let corner = painter.round_pos_to_pixels(rect.right_bottom()); let mut w = 2.0; - while w < 12.0 { + while w <= rect.width() && w <= rect.height() { painter.line_segment( [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)], style, diff --git a/egui/src/context.rs b/egui/src/context.rs index 3a30c836..2699289b 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -504,9 +504,8 @@ impl Context { use crate::containers::*; CollapsingHeader::new("Style") - .default_open(false) + .default_open(true) .show(ui, |ui| { - self.paint_options.lock().ui(ui); self.style_ui(ui); }); @@ -518,6 +517,12 @@ impl Context { self.fonts().texture().ui(ui); self.set_fonts(font_definitions); }); + + CollapsingHeader::new("Painting") + .default_open(true) + .show(ui, |ui| { + self.paint_options.lock().ui(ui); + }); } pub fn inspection_ui(&self, ui: &mut Ui) { diff --git a/egui/src/style.rs b/egui/src/style.rs index bf2889a0..5c40c674 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -34,6 +34,11 @@ pub struct Spacing { /// Button size is text size plus this on each side pub button_padding: Vec2, + /// Expand buttons by this much *after* allocating them. + /// This is then mostly a visual change (but also makes them easier to hit with the mouse). + /// This allows for compact layout where buttons actually eat into item_spacing a bit + pub button_expand: Vec2, + /// Indent collapsing regions etc by this much. pub indent: f32, @@ -99,8 +104,8 @@ pub struct Visuals { pub resize_corner_size: f32, - /// Blink text cursor by this frequency. If None, always show the cursor. - pub cursor_blink_hz: Option, + /// Blink text cursor by this frequency. If 0, always show the cursor. + pub cursor_blink_hz: f32, pub text_cursor_width: f32, /// Allow child widgets to be just on the border and still have an outline with some thickness @@ -152,7 +157,7 @@ pub struct WidgetVisuals { /// When you need a fill. pub main_fill: Srgba, - /// Stroke and text color of the interactive part of a component (button, slider grab, checkbox, ...) + /// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, ...) pub stroke_color: Srgba, /// For lines etc @@ -181,11 +186,12 @@ impl Default for Style { impl Default for Spacing { fn default() -> Self { Self { - item_spacing: vec2(8.0, 4.0), + item_spacing: vec2(8.0, 6.0), window_padding: vec2(6.0, 6.0), - button_padding: vec2(4.0, 1.0), + button_padding: vec2(2.0, 0.0), + button_expand: vec2(1.0, 1.0), indent: 21.0, - clickable_diameter: 22.0, + clickable_diameter: 14.0, // TODO: automatically higher on touch screens slider_width: 140.0, icon_width: 14.0, menu_bar_height: 16.0, @@ -207,13 +213,13 @@ impl Default for Visuals { Self { interacted: Default::default(), text_color: Srgba::gray(160), - line_width: 1.0, + line_width: 0.5, thin_outline: LineStyle::new(0.5, GRAY), background_fill: Rgba::luminance_alpha(0.013, 0.95).into(), dark_bg_color: Srgba::black_alpha(140), window_corner_radius: 10.0, - resize_corner_size: 16.0, - cursor_blink_hz: None, // Some(1.0) + resize_corner_size: 12.0, + cursor_blink_hz: 0.0, // 1.0 looks good text_cursor_width: 2.0, clip_rect_margin: 3.0, debug_widget_rects: false, @@ -228,30 +234,30 @@ impl Default for Interacted { active: WidgetVisuals { bg_fill: Srgba::black_alpha(128), bg_outline: LineStyle::new(2.0, WHITE), - corner_radius: 0.0, + corner_radius: 4.0, main_fill: srgba(120, 120, 200, 255), stroke_color: WHITE, stroke_width: 2.0, }, hovered: WidgetVisuals { - bg_fill: TRANSPARENT, - bg_outline: LineStyle::new(1.0, WHITE), - corner_radius: 2.0, + bg_fill: Rgba::luminance_alpha(0.06, 0.5).into(), + bg_outline: LineStyle::new(1.0, Rgba::white_alpha(0.5)), + corner_radius: 4.0, main_fill: srgba(100, 100, 150, 255), stroke_color: Srgba::gray(240), stroke_width: 1.5, }, inactive: WidgetVisuals { - bg_fill: TRANSPARENT, - bg_outline: LineStyle::new(1.0, Srgba::gray(128)), + bg_fill: Rgba::luminance_alpha(0.04, 0.5).into(), + bg_outline: LineStyle::new(1.0, Rgba::white_alpha(0.04)), corner_radius: 4.0, main_fill: srgba(60, 60, 80, 255), - stroke_color: Srgba::gray(200), // Mustn't look grayed out! - stroke_width: 1.0, + stroke_color: Srgba::gray(200), // Should NOT look grayed out! + stroke_width: 0.5, }, disabled: WidgetVisuals { bg_fill: TRANSPARENT, - bg_outline: LineStyle::new(0.5, Srgba::gray(128)), + bg_outline: LineStyle::new(0.5, Srgba::gray(70)), corner_radius: 4.0, main_fill: srgba(50, 50, 50, 255), stroke_color: Srgba::gray(128), // Should look grayed out @@ -294,6 +300,7 @@ impl Spacing { item_spacing, window_padding, button_padding, + button_expand, indent, clickable_diameter, slider_width, @@ -301,9 +308,10 @@ impl Spacing { menu_bar_height, } = self; - ui_slider_vec2(ui, item_spacing, 0.0..=20.0, "item_spacing"); - ui_slider_vec2(ui, window_padding, 0.0..=20.0, "window_padding"); - ui_slider_vec2(ui, button_padding, 0.0..=20.0, "button_padding"); + ui_slider_vec2(ui, item_spacing, 0.0..=10.0, "item_spacing"); + ui_slider_vec2(ui, window_padding, 0.0..=10.0, "window_padding"); + ui_slider_vec2(ui, button_padding, 0.0..=10.0, "button_padding"); + ui_slider_vec2(ui, button_expand, 0.0..=10.0, "button_expand"); ui.add(Slider::f32(indent, 0.0..=100.0).text("indent")); ui.add(Slider::f32(clickable_diameter, 0.0..=40.0).text("clickable_diameter")); ui.add(Slider::f32(slider_width, 0.0..=1000.0).text("slider_width")); @@ -400,7 +408,7 @@ impl Visuals { 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")); - let _ = cursor_blink_hz; // TODO + ui.add(Slider::f32(cursor_blink_hz, 0.0..=4.0).text("cursor_blink_hz")); ui.add(Slider::f32(text_cursor_width, 0.0..=2.0).text("text_cursor_width")); ui.add(Slider::f32(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin")); @@ -416,8 +424,9 @@ impl LineStyle { pub fn ui(&mut self, ui: &mut crate::Ui, text: &str) { let Self { width, color } = self; ui.horizontal_centered(|ui| { + ui.style_mut().spacing.slider_width /= 2.0; ui.label(format!("{}: ", text)); - ui.add(Slider::f32(width, 0.0..=10.0).text("width")); + ui.add(Slider::f32(width, 0.0..=5.0).text("width")); ui_color(ui, color, "color"); }); } @@ -427,9 +436,8 @@ impl LineStyle { fn ui_slider_vec2(ui: &mut Ui, value: &mut Vec2, range: std::ops::RangeInclusive, text: &str) { ui.horizontal_centered(|ui| { ui.label(format!("{}: ", text)); - ui.add(Slider::f32(&mut value.x, range.clone())) - .tooltip_text("x"); - ui.add(Slider::f32(&mut value.y, range)).tooltip_text("y"); + ui.add(Slider::f32(&mut value.x, range.clone()).text("w")); + ui.add(Slider::f32(&mut value.y, range.clone()).text("h")); }); } diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index 5f762849..6786082c 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -283,12 +283,13 @@ impl Widget for Button { let id = ui.make_position_id(); let font = &ui.fonts()[text_style]; let galley = font.layout_multiline(text, ui.available().width()); - let padding = ui.style().spacing.button_padding; - let mut size = galley.size + 2.0 * padding; - size.y = size.y.max(ui.style().spacing.clickable_diameter); - let rect = ui.allocate_space(size); + let mut desired_size = galley.size + 2.0 * ui.style().spacing.button_padding; + desired_size.y = desired_size.y.max(ui.style().spacing.clickable_diameter); + let rect = ui.allocate_space(desired_size); + let rect = rect.expand2(ui.style().spacing.button_expand); + let response = ui.interact(rect, id, sense); - let text_cursor = response.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y); + let text_cursor = response.rect.center() - 0.5 * galley.size; let fill = fill.unwrap_or(ui.style().interact(&response).bg_fill); ui.painter().add(PaintCmd::Rect { rect: response.rect, @@ -342,18 +343,23 @@ impl<'a> Widget for Checkbox<'a> { let text_style = TextStyle::Button; let font = &ui.fonts()[text_style]; let galley = font.layout_single_line(text); - let size = ui.style().spacing.button_padding - + vec2(ui.style().spacing.icon_width, 0.0) - + galley.size - + ui.style().spacing.button_padding; - let rect = ui.allocate_space(size); + + let icon_width = ui.style().spacing.icon_width; + let button_padding = ui.style().spacing.button_padding; + let mut desired_size = + button_padding + vec2(icon_width, 0.0) + galley.size + button_padding; + desired_size.y = desired_size.y.max(ui.style().spacing.clickable_diameter); + let rect = ui.allocate_space(desired_size); + let response = ui.interact(rect, id, Sense::click()); - let text_cursor = response.rect.min - + ui.style().spacing.button_padding - + vec2(ui.style().spacing.icon_width, 0.0); if response.clicked { *checked = !*checked; } + + let text_cursor = pos2( + response.rect.min.x + button_padding.x + icon_width, + response.rect.center().y - 0.5 * galley.size.y, + ); let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(response.rect); ui.painter().add(PaintCmd::Rect { rect: big_icon_rect, @@ -362,7 +368,7 @@ impl<'a> Widget for Checkbox<'a> { outline: ui.style().interact(&response).bg_outline, }); - let stroke_color = ui.style().interact(&response).stroke_color; + let line_style = ui.style().interact(&response).line_style(); if *checked { ui.painter().add(PaintCmd::Path { @@ -372,12 +378,12 @@ impl<'a> Widget for Checkbox<'a> { pos2(small_icon_rect.right(), small_icon_rect.top()), ], closed: false, - outline: LineStyle::new(ui.style().visuals.line_width, stroke_color), + outline: line_style, fill: Default::default(), }); } - let text_color = text_color.unwrap_or(stroke_color); + let text_color = text_color.unwrap_or(line_style.color); ui.painter() .galley(text_cursor, galley, text_style, text_color); response @@ -420,15 +426,20 @@ impl Widget for RadioButton { let text_style = TextStyle::Button; let font = &ui.fonts()[text_style]; let galley = font.layout_multiline(text, ui.available().width()); - let size = ui.style().spacing.button_padding - + vec2(ui.style().spacing.icon_width, 0.0) - + galley.size - + ui.style().spacing.button_padding; - let rect = ui.allocate_space(size); + + let icon_width = ui.style().spacing.icon_width; + let button_padding = ui.style().spacing.button_padding; + let mut desired_size = + button_padding + vec2(icon_width, 0.0) + galley.size + button_padding; + desired_size.y = desired_size.y.max(ui.style().spacing.clickable_diameter); + let rect = ui.allocate_space(desired_size); + let response = ui.interact(rect, id, Sense::click()); - let text_cursor = response.rect.min - + ui.style().spacing.button_padding - + vec2(ui.style().spacing.icon_width, 0.0); + + let text_cursor = pos2( + response.rect.min.x + button_padding.x + icon_width, + response.rect.center().y - 0.5 * galley.size.y, + ); let bg_fill = ui.style().interact(&response).bg_fill; let stroke_color = ui.style().interact(&response).stroke_color; diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index ea2e0a50..c911f34c 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -164,8 +164,8 @@ impl<'a> Slider<'a> { ui.painter().add(PaintCmd::Rect { rect: rail_rect, corner_radius: rail_radius, - fill: ui.style().visuals.background_fill, - outline: LineStyle::new(1.0, Srgba::gray(200)), // TODO + fill: ui.style().visuals.interacted.inactive.bg_fill, + outline: ui.style().visuals.interacted.inactive.bg_outline, }); ui.painter().add(PaintCmd::Circle { diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 9f080d47..bfe1041e 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -190,7 +190,7 @@ impl<'t> Widget for TextEdit<'t> { if ui.memory().has_kb_focus(id) { let cursor_blink_hz = ui.style().visuals.cursor_blink_hz; - let show_cursor = if let Some(cursor_blink_hz) = cursor_blink_hz { + let show_cursor = if 0.0 < cursor_blink_hz { ui.ctx().request_repaint(); // TODO: only when cursor blinks on or off (ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0 } else {