Skip to content

Commit

Permalink
Per-corner rounding of rectangles (#1206)
Browse files Browse the repository at this point in the history
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
  • Loading branch information
4JX and emilk authored Feb 5, 2022
1 parent 0fa4bb9 commit 7e7b9e1
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 78 deletions.
10 changes: 5 additions & 5 deletions egui/src/containers/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use epaint::*;
pub struct Frame {
/// On each side
pub margin: Vec2,
pub corner_radius: f32,
pub corner_radius: Rounding,
pub shadow: Shadow,
pub fill: Color32,
pub stroke: Stroke,
Expand All @@ -33,7 +33,7 @@ impl Frame {
pub(crate) fn side_top_panel(style: &Style) -> Self {
Self {
margin: Vec2::new(8.0, 2.0),
corner_radius: 0.0,
corner_radius: Rounding::none(),
fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(),
..Default::default()
Expand All @@ -43,7 +43,7 @@ impl Frame {
pub(crate) fn central_panel(style: &Style) -> Self {
Self {
margin: Vec2::new(8.0, 8.0),
corner_radius: 0.0,
corner_radius: Rounding::none(),
fill: style.visuals.window_fill(),
stroke: Default::default(),
..Default::default()
Expand Down Expand Up @@ -103,8 +103,8 @@ impl Frame {
self
}

pub fn corner_radius(mut self, corner_radius: f32) -> Self {
self.corner_radius = corner_radius;
pub fn corner_radius(mut self, corner_radius: impl Into<Rounding>) -> Self {
self.corner_radius = corner_radius.into();
self
}

Expand Down
32 changes: 16 additions & 16 deletions egui/src/containers/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,36 +705,36 @@ fn paint_frame_interaction(
let mut points = Vec::new();

if interaction.right && !interaction.bottom && !interaction.top {
points.push(pos2(max.x, min.y + cr));
points.push(pos2(max.x, max.y - cr));
points.push(pos2(max.x, min.y + cr.ne));
points.push(pos2(max.x, max.y - cr.se));
}
if interaction.right && interaction.bottom {
points.push(pos2(max.x, min.y + cr));
points.push(pos2(max.x, max.y - cr));
add_circle_quadrant(&mut points, pos2(max.x - cr, max.y - cr), cr, 0.0);
points.push(pos2(max.x, min.y + cr.ne));
points.push(pos2(max.x, max.y - cr.se));
add_circle_quadrant(&mut points, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0);
}
if interaction.bottom {
points.push(pos2(max.x - cr, max.y));
points.push(pos2(min.x + cr, max.y));
points.push(pos2(max.x - cr.se, max.y));
points.push(pos2(min.x + cr.sw, max.y));
}
if interaction.left && interaction.bottom {
add_circle_quadrant(&mut points, pos2(min.x + cr, max.y - cr), cr, 1.0);
add_circle_quadrant(&mut points, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0);
}
if interaction.left {
points.push(pos2(min.x, max.y - cr));
points.push(pos2(min.x, min.y + cr));
points.push(pos2(min.x, max.y - cr.sw));
points.push(pos2(min.x, min.y + cr.nw));
}
if interaction.left && interaction.top {
add_circle_quadrant(&mut points, pos2(min.x + cr, min.y + cr), cr, 2.0);
add_circle_quadrant(&mut points, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0);
}
if interaction.top {
points.push(pos2(min.x + cr, min.y));
points.push(pos2(max.x - cr, min.y));
points.push(pos2(min.x + cr.nw, min.y));
points.push(pos2(max.x - cr.ne, min.y));
}
if interaction.right && interaction.top {
add_circle_quadrant(&mut points, pos2(max.x - cr, min.y + cr), cr, 3.0);
points.push(pos2(max.x, min.y + cr));
points.push(pos2(max.x, max.y - cr));
add_circle_quadrant(&mut points, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0);
points.push(pos2(max.x, min.y + cr.ne));
points.push(pos2(max.x, max.y - cr.se));
}
ui.painter().add(Shape::line(points, visuals.bg_stroke));
}
Expand Down
4 changes: 2 additions & 2 deletions egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ pub use epaint::{
color, mutex,
text::{FontData, FontDefinitions, FontFamily, FontId},
textures::TexturesDelta,
AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Shape, Stroke, TextureHandle,
TextureId,
AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Rounding, Shape, Stroke,
TextureHandle, TextureId,
};

pub mod text {
Expand Down
24 changes: 17 additions & 7 deletions egui/src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
use epaint::{
mutex::{Arc, RwLockReadGuard, RwLockWriteGuard},
text::{Fonts, Galley},
CircleShape, RectShape, Shape, Stroke, TextShape,
CircleShape, RectShape, Rounding, Shape, Stroke, TextShape,
};

/// Helper to paint shapes and text to a specific region on a specific layer.
Expand Down Expand Up @@ -278,31 +278,41 @@ impl Painter {
pub fn rect(
&self,
rect: Rect,
corner_radius: f32,
corner_radius: impl Into<Rounding>,
fill_color: impl Into<Color32>,
stroke: impl Into<Stroke>,
) {
self.add(RectShape {
rect,
corner_radius,
corner_radius: corner_radius.into(),
fill: fill_color.into(),
stroke: stroke.into(),
});
}

pub fn rect_filled(&self, rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) {
pub fn rect_filled(
&self,
rect: Rect,
corner_radius: impl Into<Rounding>,
fill_color: impl Into<Color32>,
) {
self.add(RectShape {
rect,
corner_radius,
corner_radius: corner_radius.into(),
fill: fill_color.into(),
stroke: Default::default(),
});
}

pub fn rect_stroke(&self, rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) {
pub fn rect_stroke(
&self,
rect: Rect,
corner_radius: impl Into<Rounding>,
stroke: impl Into<Stroke>,
) {
self.add(RectShape {
rect,
corner_radius,
corner_radius: corner_radius.into(),
fill: Default::default(),
stroke: stroke.into(),
});
Expand Down
43 changes: 27 additions & 16 deletions egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![allow(clippy::if_same_then_else)]

use crate::{color::*, emath::*, FontFamily, FontId, Response, RichText, WidgetText};
use epaint::{mutex::Arc, Shadow, Stroke};
use epaint::{mutex::Arc, Rounding, Shadow, Stroke};
use std::collections::BTreeMap;

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -344,7 +344,7 @@ pub struct Visuals {
/// Background color behind code-styled monospaced labels.
pub code_bg_color: Color32,

pub window_corner_radius: f32,
pub window_corner_radius: Rounding,
pub window_shadow: Shadow,

pub popup_shadow: Shadow,
Expand Down Expand Up @@ -453,7 +453,7 @@ pub struct WidgetVisuals {
pub bg_stroke: Stroke,

/// Button frames etc.
pub corner_radius: f32,
pub corner_radius: Rounding,

/// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, …).
pub fg_stroke: Stroke,
Expand Down Expand Up @@ -566,7 +566,7 @@ impl Visuals {
faint_bg_color: Color32::from_gray(24),
extreme_bg_color: Color32::from_gray(10),
code_bg_color: Color32::from_gray(64),
window_corner_radius: 6.0,
window_corner_radius: Rounding::same(6.0),
window_shadow: Shadow::big_dark(),
popup_shadow: Shadow::small_dark(),
resize_corner_size: 12.0,
Expand Down Expand Up @@ -629,35 +629,35 @@ impl Widgets {
bg_fill: Color32::from_gray(27), // window background
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines, windows outlines
fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
inactive: WidgetVisuals {
bg_fill: Color32::from_gray(60), // button background
bg_stroke: Default::default(),
fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
hovered: WidgetVisuals {
bg_fill: Color32::from_gray(70),
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
corner_radius: 3.0,
corner_radius: Rounding::same(3.0),
expansion: 1.0,
},
active: WidgetVisuals {
bg_fill: Color32::from_gray(55),
bg_stroke: Stroke::new(1.0, Color32::WHITE),
fg_stroke: Stroke::new(2.0, Color32::WHITE),
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 1.0,
},
open: WidgetVisuals {
bg_fill: Color32::from_gray(27),
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),
fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
}
Expand All @@ -669,35 +669,35 @@ impl Widgets {
bg_fill: Color32::from_gray(235), // window background
bg_stroke: Stroke::new(1.0, Color32::from_gray(190)), // separators, indentation lines, windows outlines
fg_stroke: Stroke::new(1.0, Color32::from_gray(100)), // normal text color
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
inactive: WidgetVisuals {
bg_fill: Color32::from_gray(215), // button background
bg_stroke: Default::default(),
fg_stroke: Stroke::new(1.0, Color32::from_gray(80)), // button text
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
hovered: WidgetVisuals {
bg_fill: Color32::from_gray(210),
bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button
fg_stroke: Stroke::new(1.5, Color32::BLACK),
corner_radius: 3.0,
corner_radius: Rounding::same(3.0),
expansion: 1.0,
},
active: WidgetVisuals {
bg_fill: Color32::from_gray(165),
bg_stroke: Stroke::new(1.0, Color32::BLACK),
fg_stroke: Stroke::new(2.0, Color32::BLACK),
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 1.0,
},
open: WidgetVisuals {
bg_fill: Color32::from_gray(220),
bg_stroke: Stroke::new(1.0, Color32::from_gray(160)),
fg_stroke: Stroke::new(1.0, Color32::BLACK),
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
expansion: 0.0,
},
}
Expand Down Expand Up @@ -949,7 +949,12 @@ impl WidgetVisuals {
} = self;
ui_color(ui, bg_fill, "bg_fill");
stroke_ui(ui, bg_stroke, "bg_stroke");
ui.add(Slider::new(corner_radius, 0.0..=10.0).text("corner_radius"));

ui.add(Slider::new(&mut corner_radius.nw, 0.0..=10.0).text("corner_radius_nw"));
ui.add(Slider::new(&mut corner_radius.ne, 0.0..=10.0).text("corner_radius_ne"));
ui.add(Slider::new(&mut corner_radius.sw, 0.0..=10.0).text("corner_radius_sw"));
ui.add(Slider::new(&mut corner_radius.se, 0.0..=10.0).text("corner_radius_se"));

stroke_ui(ui, fg_stroke, "fg_stroke (text)");
ui.add(Slider::new(expansion, -5.0..=5.0).text("expansion"))
.on_hover_text("make shapes this much larger");
Expand Down Expand Up @@ -1024,7 +1029,13 @@ impl Visuals {
// Common shortcuts
ui_color(ui, &mut widgets.noninteractive.bg_fill, "Fill");
stroke_ui(ui, &mut widgets.noninteractive.bg_stroke, "Outline");
ui.add(Slider::new(window_corner_radius, 0.0..=20.0).text("Rounding"));

ui.label("Rounding");
ui.add(Slider::new(&mut window_corner_radius.nw, 0.0..=20.0).text("Top Left"));
ui.add(Slider::new(&mut window_corner_radius.ne, 0.0..=20.0).text("Top Right"));
ui.add(Slider::new(&mut window_corner_radius.sw, 0.0..=20.0).text("Bottom Left"));
ui.add(Slider::new(&mut window_corner_radius.se, 0.0..=20.0).text("Bottom Right"));

shadow_ui(ui, window_shadow, "Shadow");
shadow_ui(ui, popup_shadow, "Shadow (small menus and popups)");
});
Expand Down
7 changes: 6 additions & 1 deletion egui/src/widgets/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,12 @@ impl Widget for ImageButton {
if ui.is_rect_visible(rect) {
let (expansion, corner_radius, fill, stroke) = if selected {
let selection = ui.visuals().selection;
(-padding, 0.0, selection.bg_fill, selection.stroke)
(
-padding,
Rounding::none(),
selection.bg_fill,
selection.stroke,
)
} else if frame {
let visuals = ui.style().interact(&response);
let expansion = if response.hovered {
Expand Down
10 changes: 8 additions & 2 deletions egui/src/widgets/color_picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fn show_hsva(ui: &mut Ui, color: Hsva, desired_size: Vec2) -> Response {
} else {
ui.painter().add(RectShape {
rect,
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
fill: color.into(),
stroke: Stroke::new(3.0, color.to_opaque()),
});
Expand Down Expand Up @@ -90,7 +90,13 @@ fn color_button(ui: &mut Ui, color: Color32, open: bool) -> Response {
ui.painter().rect_filled(left_half, 0.0, color);
ui.painter().rect_filled(right_half, 0.0, color.to_opaque());

let corner_radius = visuals.corner_radius.at_most(2.0);
let corner_radius = Rounding {
nw: visuals.corner_radius.nw.at_most(2.0),
ne: visuals.corner_radius.ne.at_most(2.0),
sw: visuals.corner_radius.sw.at_most(2.0),
se: visuals.corner_radius.se.at_most(2.0),
};

ui.painter()
.rect_stroke(rect, corner_radius, (2.0, visuals.bg_fill)); // fill is intentional, because default style has no border
}
Expand Down
4 changes: 2 additions & 2 deletions egui/src/widgets/plot/items/bar.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::emath::NumExt;
use crate::epaint::{Color32, RectShape, Shape, Stroke};
use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke};

use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
use crate::plot::{BarChart, ScreenTransform, Value};
Expand Down Expand Up @@ -129,7 +129,7 @@ impl Bar {
let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max());
let rect = Shape::Rect(RectShape {
rect,
corner_radius: 0.0,
corner_radius: Rounding::none(),
fill,
stroke,
});
Expand Down
4 changes: 2 additions & 2 deletions egui/src/widgets/plot/items/box_elem.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::emath::NumExt;
use crate::epaint::{Color32, RectShape, Shape, Stroke};
use crate::epaint::{Color32, RectShape, Rounding, Shape, Stroke};

use super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
use crate::plot::{BoxPlot, ScreenTransform, Value};
Expand Down Expand Up @@ -152,7 +152,7 @@ impl BoxElem {
);
let rect = Shape::Rect(RectShape {
rect,
corner_radius: 0.0,
corner_radius: Rounding::none(),
fill,
stroke,
});
Expand Down
2 changes: 1 addition & 1 deletion egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ impl Plot {
if show_background {
ui.painter().sub_region(rect).add(epaint::RectShape {
rect,
corner_radius: 2.0,
corner_radius: Rounding::same(2.0),
fill: ui.visuals().extreme_bg_color,
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
});
Expand Down
2 changes: 1 addition & 1 deletion epaint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub use {
image::{AlphaImage, ColorImage, ImageData, ImageDelta},
mesh::{Mesh, Mesh16, Vertex},
shadow::Shadow,
shape::{CircleShape, PathShape, RectShape, Shape, TextShape},
shape::{CircleShape, PathShape, RectShape, Rounding, Shape, TextShape},
stats::PaintStats,
stroke::Stroke,
tessellator::{tessellate_shapes, TessellationOptions, Tessellator},
Expand Down
Loading

0 comments on commit 7e7b9e1

Please sign in to comment.