[style] Slightly expand buttons when hovering and interacting

This commit is contained in:
Emil Ernerfeldt 2021-01-12 20:50:54 +01:00
parent 1b40a5dda5
commit 6d8a766614
10 changed files with 45 additions and 20 deletions

View file

@ -106,12 +106,14 @@ impl State {
/// Paint the arrow icon that indicated if the region is open or not /// Paint the arrow icon that indicated if the region is open or not
pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) { pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
let stroke = ui.style().interact(response).fg_stroke; let visuals = ui.style().interact(response);
let stroke = visuals.fg_stroke;
let rect = response.rect; let rect = response.rect;
// Draw a pointy triangle arrow: // Draw a pointy triangle arrow:
let rect = Rect::from_center_size(rect.center(), vec2(rect.width(), rect.height()) * 0.75); let rect = Rect::from_center_size(rect.center(), vec2(rect.width(), rect.height()) * 0.75);
let rect = rect.expand(visuals.expansion);
let mut points = vec![rect.left_top(), rect.right_top(), rect.center_bottom()]; let mut points = vec![rect.left_top(), rect.right_top(), rect.center_bottom()];
use std::f32::consts::TAU; use std::f32::consts::TAU;
let rotation = math::Rot2::from_angle(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0)); let rotation = math::Rot2::from_angle(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0));
@ -203,10 +205,12 @@ impl CollapsingHeader {
state.toggle(ui); state.toggle(ui);
} }
let visuals = ui.style().interact(&header_response);
let text_color = visuals.text_color();
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect: header_response.rect, rect: header_response.rect.expand(visuals.expansion),
corner_radius: ui.style().interact(&header_response).corner_radius, corner_radius: visuals.corner_radius,
fill: ui.style().interact(&header_response).bg_fill, fill: visuals.bg_fill,
stroke: Default::default(), stroke: Default::default(),
}); });
@ -228,7 +232,7 @@ impl CollapsingHeader {
text_pos, text_pos,
galley, galley,
label.text_style_or_default(ui.style()), label.text_style_or_default(ui.style()),
ui.style().interact(&header_response).text_color(), text_color,
); );
Prepared { Prepared {

View file

@ -79,7 +79,7 @@ pub fn combo_box(
let icon_rect = Align2::RIGHT_CENTER.align_size_within_rect(icon_size, rect); let icon_rect = Align2::RIGHT_CENTER.align_size_within_rect(icon_size, rect);
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
paint_icon(ui.painter(), icon_rect, visuals); paint_icon(ui.painter(), icon_rect.expand(visuals.expansion), visuals);
let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size, rect); let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size, rect);
ui.painter() ui.painter()
@ -145,7 +145,7 @@ fn button_frame(
ui.painter().set( ui.painter().set(
where_to_put_background, where_to_put_background,
Shape::Rect { Shape::Rect {
rect: outer_rect, rect: outer_rect.expand(visuals.expansion),
corner_radius: visuals.corner_radius, corner_radius: visuals.corner_radius,
fill: visuals.bg_fill, fill: visuals.bg_fill,
stroke: visuals.bg_stroke, stroke: visuals.bg_stroke,

View file

@ -767,9 +767,9 @@ fn close_button(ui: &mut Ui, rect: Rect) -> Response {
let response = ui.interact(rect, close_id, Sense::click()); let response = ui.interact(rect, close_id, Sense::click());
ui.expand_to_include_rect(response.rect); ui.expand_to_include_rect(response.rect);
let rect = rect.shrink(2.0); let visuals = ui.style().interact(&response);
let rect = rect.shrink(2.0).expand(visuals.expansion);
let stroke = ui.style().interact(&response).fg_stroke; let stroke = visuals.fg_stroke;
ui.painter() ui.painter()
.line_segment([rect.left_top(), rect.right_bottom()], stroke); .line_segment([rect.left_top(), rect.right_bottom()], stroke);
ui.painter() ui.painter()

View file

@ -227,6 +227,9 @@ pub struct WidgetVisuals {
/// 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,
/// Make the frame this much larger
pub expansion: f32,
} }
impl WidgetVisuals { impl WidgetVisuals {
@ -287,7 +290,7 @@ impl Default for Visuals {
window_shadow: Shadow::big(), window_shadow: Shadow::big(),
resize_corner_size: 12.0, resize_corner_size: 12.0,
text_cursor_width: 2.0, text_cursor_width: 2.0,
clip_rect_margin: 1.0, // should be half the size of the widest frame stroke clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
debug_expand_width: false, debug_expand_width: false,
debug_expand_height: false, debug_expand_height: false,
debug_resize: false, debug_resize: false,
@ -316,6 +319,7 @@ impl Default for Widgets {
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(120, 120, 200), fg_fill: Color32::from_rgb(120, 120, 200),
fg_stroke: Stroke::new(2.0, Color32::WHITE), fg_stroke: Stroke::new(2.0, Color32::WHITE),
expansion: 2.0,
}, },
hovered: WidgetVisuals { hovered: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.06, 0.5).into(), bg_fill: Rgba::from_luminance_alpha(0.06, 0.5).into(),
@ -323,6 +327,7 @@ impl Default for Widgets {
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(100, 100, 150), fg_fill: Color32::from_rgb(100, 100, 150),
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)), fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
expansion: 1.0,
}, },
inactive: WidgetVisuals { inactive: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.04, 0.5).into(), bg_fill: Rgba::from_luminance_alpha(0.04, 0.5).into(),
@ -330,6 +335,7 @@ impl Default for Widgets {
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(60, 60, 80), fg_fill: Color32::from_rgb(60, 60, 80),
fg_stroke: Stroke::new(1.0, Color32::from_gray(200)), // Should NOT look grayed out! fg_stroke: Stroke::new(1.0, Color32::from_gray(200)), // Should NOT look grayed out!
expansion: 0.0,
}, },
disabled: WidgetVisuals { disabled: WidgetVisuals {
bg_fill: Rgba::from_luminance_alpha(0.02, 0.5).into(), bg_fill: Rgba::from_luminance_alpha(0.02, 0.5).into(),
@ -337,6 +343,7 @@ impl Default for Widgets {
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Color32::from_rgb(50, 50, 50), 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,
}, },
noninteractive: WidgetVisuals { noninteractive: WidgetVisuals {
bg_stroke: Stroke::new(1.0, Rgba::from_white_alpha(0.06)), bg_stroke: Stroke::new(1.0, Rgba::from_white_alpha(0.06)),
@ -344,6 +351,7 @@ impl Default for Widgets {
corner_radius: 4.0, corner_radius: 4.0,
fg_fill: Default::default(), 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(160)), // text color
expansion: 0.0,
}, },
} }
} }
@ -460,6 +468,7 @@ impl WidgetVisuals {
corner_radius, corner_radius,
fg_fill, fg_fill,
fg_stroke, fg_stroke,
expansion,
} = self; } = self;
ui_color(ui, bg_fill, "bg_fill"); ui_color(ui, bg_fill, "bg_fill");
@ -467,6 +476,7 @@ impl WidgetVisuals {
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"); 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"));
} }
} }

View file

@ -117,8 +117,12 @@ impl Widget for Button {
if frame { if frame {
let fill = fill.unwrap_or(visuals.bg_fill); let fill = fill.unwrap_or(visuals.bg_fill);
ui.painter() ui.painter().rect(
.rect(rect, visuals.corner_radius, fill, visuals.bg_stroke); rect.expand(visuals.expansion),
visuals.corner_radius,
fill,
visuals.bg_stroke,
);
} }
let text_color = text_color let text_color = text_color
@ -198,7 +202,7 @@ impl<'a> Widget for Checkbox<'a> {
); );
let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(rect); let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(rect);
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect: big_icon_rect, rect: big_icon_rect.expand(visuals.expansion),
corner_radius: visuals.corner_radius, corner_radius: visuals.corner_radius,
fill: visuals.bg_fill, fill: visuals.bg_fill,
stroke: visuals.bg_stroke, stroke: visuals.bg_stroke,
@ -292,7 +296,7 @@ impl Widget for RadioButton {
painter.add(Shape::Circle { painter.add(Shape::Circle {
center: big_icon_rect.center(), center: big_icon_rect.center(),
radius: big_icon_rect.width() / 2.0, radius: big_icon_rect.width() / 2.0 + visuals.expansion,
fill: visuals.bg_fill, fill: visuals.bg_fill,
stroke: visuals.bg_stroke, stroke: visuals.bg_stroke,
}); });
@ -392,7 +396,7 @@ impl Widget for ImageButton {
.rect(rect, 0.0, selection.bg_fill, selection.stroke); .rect(rect, 0.0, selection.bg_fill, selection.stroke);
} else if frame { } else if frame {
ui.painter().rect( ui.painter().rect(
rect, rect.expand(visuals.expansion),
visuals.corner_radius, visuals.corner_radius,
visuals.bg_fill, visuals.bg_fill,
visuals.bg_stroke, visuals.bg_stroke,

View file

@ -60,6 +60,7 @@ fn color_button(ui: &mut Ui, color: Color32) -> Response {
let desired_size = ui.style().spacing.interact_size; let desired_size = ui.style().spacing.interact_size;
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click()); let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
let rect = rect.expand(visuals.expansion);
background_checkers(ui.painter(), rect); background_checkers(ui.painter(), rect);
ui.painter().add(Shape::Rect { ui.painter().add(Shape::Rect {
rect, rect,

View file

@ -43,6 +43,7 @@ impl Widget for SelectableLabel {
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
if selected || response.hovered { if selected || response.hovered {
let rect = rect.expand(visuals.expansion);
let bg_fill = if selected { let bg_fill = if selected {
ui.style().visuals.selection.bg_fill ui.style().visuals.selection.bg_fill
} else { } else {

View file

@ -278,18 +278,21 @@ impl<'a> Slider<'a> {
); );
let marker_center_x = self.x_from_value(value, x_range); let marker_center_x = self.x_from_value(value, x_range);
let visuals = ui.style().interact(response);
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,
// 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), radius: handle_radius(rect) + visuals.expansion,
fill: ui.style().interact(response).fg_fill, fill: visuals.fg_fill,
stroke: ui.style().interact(response).fg_stroke, stroke: visuals.fg_stroke,
}); });
} }
} }

View file

@ -228,7 +228,7 @@ impl<'t> Widget for TextEdit<'t> {
let response = response | ui.allocate_response(frame_rect.size(), Sense::click()); let response = response | ui.allocate_response(frame_rect.size(), Sense::click());
let visuals = ui.style().interact(&response); let visuals = ui.style().interact(&response);
let frame_rect = response.rect; let frame_rect = response.rect.expand(visuals.expansion);
ui.painter().set( ui.painter().set(
where_to_put_background, where_to_put_background,
Shape::Rect { Shape::Rect {

View file

@ -44,6 +44,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
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.25);
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 radius = 0.5 * rect.height(); let radius = 0.5 * rect.height();
ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke); ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke);
// Paint the circle, animating it from left to right with `how_on`: // Paint the circle, animating it from left to right with `how_on`:
@ -69,6 +70,7 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
let off_bg_fill = egui::Rgba::TRANSPARENT; let off_bg_fill = egui::Rgba::TRANSPARENT;
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.25);
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 radius = 0.5 * rect.height(); let radius = 0.5 * rect.height();
ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke); ui.painter().rect(rect, radius, bg_fill, visuals.bg_stroke);
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);