New sleeker visual style

Remove a lot of borders, remove transparency, simplify and unify.
This commit is contained in:
Emil Ernerfeldt 2021-01-15 22:20:26 +01:00
parent 6d5eaeeafa
commit 620e43d483
13 changed files with 108 additions and 78 deletions

View file

@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed 🔧 ### Changed 🔧
* New simpler and sleeker look!
* Center window titles. * Center window titles.
* Tweak size and alignment of some emojis to match other text. * Tweak size and alignment of some emojis to match other text.
* Rename `PaintCmd` to `Shape`. * Rename `PaintCmd` to `Shape`.

View file

@ -209,7 +209,8 @@ impl CollapsingHeader {
rect: header_response.rect.expand(visuals.expansion), rect: header_response.rect.expand(visuals.expansion),
corner_radius: visuals.corner_radius, corner_radius: visuals.corner_radius,
fill: visuals.bg_fill, fill: visuals.bg_fill,
stroke: Default::default(), stroke: visuals.bg_stroke,
// stroke: Default::default(),
}); });
{ {

View file

@ -44,7 +44,7 @@ impl Frame {
corner_radius: style.visuals.window_corner_radius, corner_radius: style.visuals.window_corner_radius,
shadow: style.visuals.window_shadow, shadow: style.visuals.window_shadow,
fill: style.visuals.widgets.noninteractive.bg_fill, fill: style.visuals.widgets.noninteractive.bg_fill,
stroke: style.visuals.widgets.inactive.bg_stroke, // because we can resize windows stroke: style.visuals.widgets.noninteractive.bg_stroke,
} }
} }

View file

@ -337,8 +337,8 @@ impl Prepared {
ui.painter().add(paint::Shape::Rect { ui.painter().add(paint::Shape::Rect {
rect: handle_rect.expand(-2.0), rect: handle_rect.expand(-2.0),
corner_radius, corner_radius,
fill: visuals.fg_fill, fill: visuals.bg_fill,
stroke: visuals.fg_stroke, stroke: visuals.bg_stroke,
}); });
} }

View file

@ -744,7 +744,7 @@ impl TitleBar {
// let y = lerp(self.rect.bottom()..=content_response.rect.top(), 0.5); // let y = lerp(self.rect.bottom()..=content_response.rect.top(), 0.5);
ui.painter().line_segment( ui.painter().line_segment(
[pos2(left, y), pos2(right, y)], [pos2(left, y), pos2(right, y)],
ui.style().visuals.widgets.inactive.bg_stroke, ui.style().visuals.widgets.noninteractive.bg_stroke,
); );
} }

View file

@ -79,7 +79,7 @@ fn menu_impl<'c>(
let mut button = Button::new(title); let mut button = Button::new(title);
if bar_state.open_menu == Some(menu_id) { if bar_state.open_menu == Some(menu_id) {
button = button.fill(Some(ui.style().visuals.widgets.active.fg_fill)); button = button.fill(Some(ui.style().visuals.selection.bg_fill));
} }
let button_response = ui.add(button); let button_response = ui.add(button);

View file

@ -56,7 +56,7 @@ pub struct Spacing {
/// Indent collapsing regions etc by this much. /// Indent collapsing regions etc by this much.
pub indent: f32, pub indent: f32,
/// Minimum size of e.g. a button. /// Minimum size of e.g. a button (including padding).
/// `interact_size.y` is the default height of button, slider, etc. /// `interact_size.y` is the default height of button, slider, etc.
/// Anything clickable should be (at least) this size. /// Anything clickable should be (at least) this size.
pub interact_size: Vec2, // TODO: rename min_interact_size ? pub interact_size: Vec2, // TODO: rename min_interact_size ?
@ -181,16 +181,16 @@ pub struct Selection {
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
pub struct Widgets { 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 /// For a non-interactive widget
pub noninteractive: WidgetVisuals, pub noninteractive: WidgetVisuals,
/// For an otherwise interactive widget that has been disabled
pub disabled: WidgetVisuals,
/// For an interactive widget that is "resting"
pub inactive: WidgetVisuals,
/// For an interactive widget that is being hovered
pub hovered: WidgetVisuals,
/// For an interactive widget that is being interacted with
pub active: WidgetVisuals,
} }
impl Widgets { impl Widgets {
@ -216,15 +216,12 @@ pub struct WidgetVisuals {
/// For surrounding rectangle of things that need it, /// For surrounding rectangle of things that need it,
/// like buttons, the box of the checkbox, etc. /// like buttons, the box of the checkbox, etc.
/// Should maybe be called `frame_stroke`.
pub bg_stroke: Stroke, pub bg_stroke: Stroke,
/// Button frames etc /// Button frames etc
pub corner_radius: f32, pub corner_radius: f32,
/// Fill color of the interactive part of a component (slider grab, checkbox, ...)
/// When you need a fill.
pub fg_fill: Color32,
/// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, ...) /// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, ...)
pub fg_stroke: Stroke, pub fg_stroke: Stroke,
@ -256,14 +253,14 @@ impl Default for Spacing {
fn default() -> Self { fn default() -> Self {
Self { Self {
item_spacing: vec2(8.0, 3.0), item_spacing: vec2(8.0, 3.0),
window_padding: vec2(4.0, 4.0), window_padding: Vec2::splat(6.0),
button_padding: vec2(3.0, 1.0), button_padding: vec2(4.0, 1.0),
indent: 25.0, indent: 25.0,
interact_size: vec2(40.0, 20.0), interact_size: vec2(40.0, 20.0),
slider_width: 100.0, slider_width: 100.0,
text_edit_width: 280.0, text_edit_width: 280.0,
icon_width: 16.0, icon_width: 16.0,
icon_spacing: 1.0, icon_spacing: 0.0,
tooltip_width: 400.0, tooltip_width: 400.0,
} }
} }
@ -284,7 +281,7 @@ impl Default for Visuals {
override_text_color: None, override_text_color: None,
widgets: Default::default(), widgets: Default::default(),
selection: Default::default(), selection: Default::default(),
dark_bg_color: Color32::from_black_alpha(140), dark_bg_color: Color32::from_gray(10),
hyperlink_color: Color32::from_rgb(90, 170, 255), hyperlink_color: Color32::from_rgb(90, 170, 255),
window_corner_radius: 10.0, window_corner_radius: 10.0,
window_shadow: Shadow::big(), window_shadow: Shadow::big(),
@ -313,46 +310,46 @@ impl Default for Selection {
impl Default for Widgets { impl Default for Widgets {
fn default() -> Self { fn default() -> Self {
Self { Self {
active: WidgetVisuals { noninteractive: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.10, 0.5).into(), bg_stroke: Stroke::new(1.0, Color32::from_gray(65)), // window outline
bg_stroke: Stroke::new(2.0, Color32::WHITE), bg_fill: Color32::from_gray(30), // window background
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(120, 120, 200),
fg_stroke: Stroke::new(2.0, Color32::WHITE), fg_stroke: Stroke::new(1.0, Color32::from_gray(160)), // text color
expansion: 2.0,
},
hovered: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.06, 0.5).into(),
bg_stroke: Stroke::new(1.0, Rgba::from_white_alpha(0.5)),
corner_radius: 4.0,
fg_fill: Color32::from_rgb(100, 100, 150),
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
expansion: 1.0,
},
inactive: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.04, 0.5).into(),
bg_stroke: Stroke::new(1.0, Rgba::from_white_alpha(0.06)), // default window outline. Should be pretty readable
corner_radius: 4.0,
fg_fill: Color32::from_rgb(60, 60, 80),
fg_stroke: Stroke::new(1.0, Color32::from_gray(200)), // Should NOT look grayed out!
expansion: 0.0, expansion: 0.0,
}, },
disabled: WidgetVisuals { disabled: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.02, 0.5).into(), bg_fill: Color32::from_gray(40), // Should look grayed out
bg_stroke: Stroke::new(0.5, Color32::from_gray(70)), bg_stroke: Stroke::new(1.0, Color32::from_gray(70)),
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(50, 50, 50),
fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // Should look grayed out fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // Should look grayed out
expansion: 0.0, expansion: 0.0,
}, },
noninteractive: WidgetVisuals { inactive: WidgetVisuals {
bg_stroke: Stroke::new(1.0, Rgba::from_white_alpha(0.06)), bg_fill: Color32::from_gray(70),
bg_fill: Rgba::from_luminance_alpha(0.010, 0.975).into(), // window background bg_stroke: Default::default(),
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Default::default(),
fg_stroke: Stroke::new(1.0, Color32::from_gray(160)), // text color fg_stroke: Stroke::new(1.0, Color32::from_gray(200)), // Should NOT look grayed out!
expansion: 0.0, expansion: 0.0,
}, },
hovered: WidgetVisuals {
bg_fill: Color32::from_gray(80),
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
corner_radius: 4.0,
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
expansion: 1.0,
},
active: WidgetVisuals {
bg_fill: Color32::from_gray(90),
bg_stroke: Stroke::new(1.0, Color32::WHITE),
corner_radius: 4.0,
fg_stroke: Stroke::new(2.0, Color32::WHITE),
expansion: 2.0,
},
} }
} }
} }
@ -443,11 +440,26 @@ impl Widgets {
noninteractive, noninteractive,
} = self; } = self;
ui.collapsing("noninteractive", |ui| noninteractive.ui(ui)); ui.collapsing("noninteractive", |ui| {
ui.collapsing("interactive & disabled", |ui| disabled.ui(ui)); ui.label("The style of something that you cannot interact with.");
ui.collapsing("interactive & inactive", |ui| inactive.ui(ui)); noninteractive.ui(ui)
ui.collapsing("interactive & hovered", |ui| hovered.ui(ui)); });
ui.collapsing("interactive & active", |ui| active.ui(ui)); ui.collapsing("interactive & disabled", |ui| {
ui.label("The style of a disabled button.");
disabled.ui(ui)
});
ui.collapsing("interactive & inactive", |ui| {
ui.label("The style of a widget, such as a button, at rest.");
inactive.ui(ui)
});
ui.collapsing("interactive & hovered", |ui| {
ui.label("The style of a widget while you hover it.");
hovered.ui(ui)
});
ui.collapsing("interactive & active", |ui| {
ui.label("The style of a widget as you are clicking or dragging it.");
active.ui(ui)
});
} }
} }
@ -466,7 +478,6 @@ impl WidgetVisuals {
bg_fill, bg_fill,
bg_stroke, bg_stroke,
corner_radius, corner_radius,
fg_fill,
fg_stroke, fg_stroke,
expansion, expansion,
} = self; } = self;
@ -474,7 +485,6 @@ impl WidgetVisuals {
ui_color(ui, bg_fill, "bg_fill"); ui_color(ui, bg_fill, "bg_fill");
stroke_ui(ui, bg_stroke, "bg_stroke"); stroke_ui(ui, bg_stroke, "bg_stroke");
ui.add(Slider::f32(corner_radius, 0.0..=10.0).text("corner_radius")); ui.add(Slider::f32(corner_radius, 0.0..=10.0).text("corner_radius"));
ui_color(ui, fg_fill, "fg_fill");
stroke_ui(ui, fg_stroke, "fg_stroke (text)"); stroke_ui(ui, fg_stroke, "fg_stroke (text)");
ui.add(Slider::f32(expansion, -5.0..=5.0).text("expansion")); ui.add(Slider::f32(expansion, -5.0..=5.0).text("expansion"));
} }

View file

@ -217,6 +217,7 @@ impl<'a> Widget for Checkbox<'a> {
pos2(small_icon_rect.right(), small_icon_rect.top()), pos2(small_icon_rect.right(), small_icon_rect.top()),
], ],
visuals.fg_stroke, visuals.fg_stroke,
// ui.style().visuals.selection.stroke, // too much color
)); ));
} }
@ -306,9 +307,8 @@ impl Widget for RadioButton {
center: small_icon_rect.center(), center: small_icon_rect.center(),
radius: small_icon_rect.width() / 3.0, radius: small_icon_rect.width() / 3.0,
fill: visuals.fg_stroke.color, // Intentional to use stroke and not fill fill: visuals.fg_stroke.color, // Intentional to use stroke and not fill
// fill: ui.style().visuals.selection.stroke.color, // too much color
stroke: Default::default(), stroke: Default::default(),
// fill: visuals.fg_fill,
// stroke: visuals.fg_stroke,
}); });
} }

View file

@ -78,7 +78,7 @@ fn color_button(ui: &mut Ui, color: Color32) -> Response {
ui.painter().rect_filled(left, 0.0, color); ui.painter().rect_filled(left, 0.0, color);
ui.painter().rect_filled(right, 0.0, color.to_opaque()); ui.painter().rect_filled(right, 0.0, color.to_opaque());
ui.painter() ui.painter()
.rect_stroke(rect, corner_radius, visuals.fg_stroke); .rect_stroke(rect, corner_radius, visuals.bg_stroke);
} else { } else {
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect, rect,

View file

@ -44,12 +44,18 @@ impl Widget for SelectableLabel {
if selected || response.hovered { if selected || response.hovered {
let rect = rect.expand(visuals.expansion); let rect = rect.expand(visuals.expansion);
let bg_fill = if selected { let fill = if selected {
ui.style().visuals.selection.bg_fill ui.style().visuals.selection.bg_fill
} else { } else {
Default::default() Default::default()
}; };
ui.painter().rect(rect, 0.0, bg_fill, visuals.bg_stroke); let stroke = if selected {
ui.style().visuals.selection.stroke
} else {
visuals.bg_stroke
};
let corner_radius = 2.0;
ui.painter().rect(rect, corner_radius, fill, stroke);
} }
let text_color = ui let text_color = ui

View file

@ -282,16 +282,19 @@ impl<'a> Slider<'a> {
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect: rail_rect, rect: rail_rect,
corner_radius: rail_radius, corner_radius: rail_radius,
// fill: visuals.bg_fill,
fill: ui.style().visuals.widgets.inactive.bg_fill, fill: ui.style().visuals.widgets.inactive.bg_fill,
// fill: visuals.bg_fill,
// fill: ui.style().visuals.dark_bg_color,
stroke: Default::default(),
// stroke: visuals.bg_stroke, // stroke: visuals.bg_stroke,
stroke: ui.style().visuals.widgets.inactive.bg_stroke, // stroke: ui.style().visuals.widgets.inactive.bg_stroke,
}); });
ui.painter().add(Shape::Circle { ui.painter().add(Shape::Circle {
center: pos2(marker_center_x, rail_rect.center().y), center: pos2(marker_center_x, rail_rect.center().y),
radius: handle_radius(rect) + visuals.expansion, radius: handle_radius(rect) + visuals.expansion,
fill: visuals.fg_fill, fill: visuals.bg_fill,
stroke: visuals.fg_stroke, stroke: visuals.fg_stroke,
}); });
} }

View file

@ -240,15 +240,24 @@ impl<'t> Widget for TextEdit<'t> {
if frame { if frame {
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
let frame_rect = response.rect.expand(visuals.expansion); let frame_rect = response.rect.expand(visuals.expansion);
ui.painter().set( let shape = if response.has_kb_focus {
where_to_put_background, Shape::Rect {
rect: frame_rect,
corner_radius: visuals.corner_radius,
// fill: ui.style().visuals.selection.bg_fill,
fill: ui.style().visuals.dark_bg_color,
stroke: ui.style().visuals.selection.stroke,
}
} else {
Shape::Rect { Shape::Rect {
rect: frame_rect, rect: frame_rect,
corner_radius: visuals.corner_radius, corner_radius: visuals.corner_radius,
fill: ui.style().visuals.dark_bg_color, fill: ui.style().visuals.dark_bg_color,
stroke: visuals.bg_stroke, stroke: visuals.bg_stroke, // TODO: we want to show something here, or a text-edit field doesn't "pop".
}, }
); };
ui.painter().set(where_to_put_background, shape);
} }
response response

View file

@ -40,8 +40,8 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
// "how should something that is being interacted with be painted?". // "how should something that is being interacted with be painted?".
// This will, for instance, give us different colors when the widget is hovered or clicked. // This will, for instance, give us different colors when the widget is hovered or clicked.
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
let off_bg_fill = egui::Rgba::TRANSPARENT; let off_bg_fill = egui::Rgba::from(visuals.bg_fill);
let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.25); let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.0);
let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on); let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on);
// All coordinates are in absolute screen coordinates so we use `rect` to place the elements. // All coordinates are in absolute screen coordinates so we use `rect` to place the elements.
let rect = rect.expand(visuals.expansion); let rect = rect.expand(visuals.expansion);
@ -51,7 +51,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on); let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
let center = egui::pos2(circle_x, rect.center().y); let center = egui::pos2(circle_x, rect.center().y);
ui.painter() ui.painter()
.circle(center, 0.75 * radius, visuals.fg_fill, visuals.fg_stroke); .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);
// All done! Return the interaction response so the user can check what happened // All done! Return the interaction response so the user can check what happened
// (hovered, clicked, ...) and maybe show a tooltip: // (hovered, clicked, ...) and maybe show a tooltip:
@ -67,8 +67,8 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
let how_on = ui.ctx().animate_bool(response.id, *on); let how_on = ui.ctx().animate_bool(response.id, *on);
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
let off_bg_fill = egui::Rgba::TRANSPARENT; let off_bg_fill = egui::Rgba::from(visuals.bg_fill);
let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.25); let on_bg_fill = egui::Rgba::from_rgb(0.0, 0.5, 0.0);
let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on); let bg_fill = egui::lerp(off_bg_fill..=on_bg_fill, how_on);
let rect = rect.expand(visuals.expansion); let rect = rect.expand(visuals.expansion);
let radius = 0.5 * rect.height(); let radius = 0.5 * rect.height();
@ -76,7 +76,7 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on); let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
let center = egui::pos2(circle_x, rect.center().y); let center = egui::pos2(circle_x, rect.center().y);
ui.painter() ui.painter()
.circle(center, 0.75 * radius, visuals.fg_fill, visuals.fg_stroke); .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);
response response
} }