Per-corner rounding of rectangles (#1206)
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
0fa4bb9c64
commit
7e7b9e1919
14 changed files with 197 additions and 78 deletions
|
@ -9,7 +9,7 @@ use epaint::*;
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
/// On each side
|
/// On each side
|
||||||
pub margin: Vec2,
|
pub margin: Vec2,
|
||||||
pub corner_radius: f32,
|
pub corner_radius: Rounding,
|
||||||
pub shadow: Shadow,
|
pub shadow: Shadow,
|
||||||
pub fill: Color32,
|
pub fill: Color32,
|
||||||
pub stroke: Stroke,
|
pub stroke: Stroke,
|
||||||
|
@ -33,7 +33,7 @@ impl Frame {
|
||||||
pub(crate) fn side_top_panel(style: &Style) -> Self {
|
pub(crate) fn side_top_panel(style: &Style) -> Self {
|
||||||
Self {
|
Self {
|
||||||
margin: Vec2::new(8.0, 2.0),
|
margin: Vec2::new(8.0, 2.0),
|
||||||
corner_radius: 0.0,
|
corner_radius: Rounding::none(),
|
||||||
fill: style.visuals.window_fill(),
|
fill: style.visuals.window_fill(),
|
||||||
stroke: style.visuals.window_stroke(),
|
stroke: style.visuals.window_stroke(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -43,7 +43,7 @@ impl Frame {
|
||||||
pub(crate) fn central_panel(style: &Style) -> Self {
|
pub(crate) fn central_panel(style: &Style) -> Self {
|
||||||
Self {
|
Self {
|
||||||
margin: Vec2::new(8.0, 8.0),
|
margin: Vec2::new(8.0, 8.0),
|
||||||
corner_radius: 0.0,
|
corner_radius: Rounding::none(),
|
||||||
fill: style.visuals.window_fill(),
|
fill: style.visuals.window_fill(),
|
||||||
stroke: Default::default(),
|
stroke: Default::default(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -103,8 +103,8 @@ impl Frame {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn corner_radius(mut self, corner_radius: f32) -> Self {
|
pub fn corner_radius(mut self, corner_radius: impl Into<Rounding>) -> Self {
|
||||||
self.corner_radius = corner_radius;
|
self.corner_radius = corner_radius.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -705,36 +705,36 @@ fn paint_frame_interaction(
|
||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
|
|
||||||
if interaction.right && !interaction.bottom && !interaction.top {
|
if interaction.right && !interaction.bottom && !interaction.top {
|
||||||
points.push(pos2(max.x, min.y + cr));
|
points.push(pos2(max.x, min.y + cr.ne));
|
||||||
points.push(pos2(max.x, max.y - cr));
|
points.push(pos2(max.x, max.y - cr.se));
|
||||||
}
|
}
|
||||||
if interaction.right && interaction.bottom {
|
if interaction.right && interaction.bottom {
|
||||||
points.push(pos2(max.x, min.y + cr));
|
points.push(pos2(max.x, min.y + cr.ne));
|
||||||
points.push(pos2(max.x, max.y - cr));
|
points.push(pos2(max.x, max.y - cr.se));
|
||||||
add_circle_quadrant(&mut points, pos2(max.x - cr, max.y - cr), cr, 0.0);
|
add_circle_quadrant(&mut points, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0);
|
||||||
}
|
}
|
||||||
if interaction.bottom {
|
if interaction.bottom {
|
||||||
points.push(pos2(max.x - cr, max.y));
|
points.push(pos2(max.x - cr.se, max.y));
|
||||||
points.push(pos2(min.x + cr, max.y));
|
points.push(pos2(min.x + cr.sw, max.y));
|
||||||
}
|
}
|
||||||
if interaction.left && interaction.bottom {
|
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 {
|
if interaction.left {
|
||||||
points.push(pos2(min.x, max.y - cr));
|
points.push(pos2(min.x, max.y - cr.sw));
|
||||||
points.push(pos2(min.x, min.y + cr));
|
points.push(pos2(min.x, min.y + cr.nw));
|
||||||
}
|
}
|
||||||
if interaction.left && interaction.top {
|
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 {
|
if interaction.top {
|
||||||
points.push(pos2(min.x + cr, min.y));
|
points.push(pos2(min.x + cr.nw, min.y));
|
||||||
points.push(pos2(max.x - cr, min.y));
|
points.push(pos2(max.x - cr.ne, min.y));
|
||||||
}
|
}
|
||||||
if interaction.right && interaction.top {
|
if interaction.right && interaction.top {
|
||||||
add_circle_quadrant(&mut points, pos2(max.x - cr, min.y + cr), cr, 3.0);
|
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));
|
points.push(pos2(max.x, min.y + cr.ne));
|
||||||
points.push(pos2(max.x, max.y - cr));
|
points.push(pos2(max.x, max.y - cr.se));
|
||||||
}
|
}
|
||||||
ui.painter().add(Shape::line(points, visuals.bg_stroke));
|
ui.painter().add(Shape::line(points, visuals.bg_stroke));
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,8 +387,8 @@ pub use epaint::{
|
||||||
color, mutex,
|
color, mutex,
|
||||||
text::{FontData, FontDefinitions, FontFamily, FontId},
|
text::{FontData, FontDefinitions, FontFamily, FontId},
|
||||||
textures::TexturesDelta,
|
textures::TexturesDelta,
|
||||||
AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Shape, Stroke, TextureHandle,
|
AlphaImage, ClippedMesh, Color32, ColorImage, ImageData, Rgba, Rounding, Shape, Stroke,
|
||||||
TextureId,
|
TextureHandle, TextureId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod text {
|
pub mod text {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use epaint::{
|
use epaint::{
|
||||||
mutex::{Arc, RwLockReadGuard, RwLockWriteGuard},
|
mutex::{Arc, RwLockReadGuard, RwLockWriteGuard},
|
||||||
text::{Fonts, Galley},
|
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.
|
/// Helper to paint shapes and text to a specific region on a specific layer.
|
||||||
|
@ -278,31 +278,41 @@ impl Painter {
|
||||||
pub fn rect(
|
pub fn rect(
|
||||||
&self,
|
&self,
|
||||||
rect: Rect,
|
rect: Rect,
|
||||||
corner_radius: f32,
|
corner_radius: impl Into<Rounding>,
|
||||||
fill_color: impl Into<Color32>,
|
fill_color: impl Into<Color32>,
|
||||||
stroke: impl Into<Stroke>,
|
stroke: impl Into<Stroke>,
|
||||||
) {
|
) {
|
||||||
self.add(RectShape {
|
self.add(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius,
|
corner_radius: corner_radius.into(),
|
||||||
fill: fill_color.into(),
|
fill: fill_color.into(),
|
||||||
stroke: stroke.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 {
|
self.add(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius,
|
corner_radius: corner_radius.into(),
|
||||||
fill: fill_color.into(),
|
fill: fill_color.into(),
|
||||||
stroke: Default::default(),
|
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 {
|
self.add(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius,
|
corner_radius: corner_radius.into(),
|
||||||
fill: Default::default(),
|
fill: Default::default(),
|
||||||
stroke: stroke.into(),
|
stroke: stroke.into(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![allow(clippy::if_same_then_else)]
|
#![allow(clippy::if_same_then_else)]
|
||||||
|
|
||||||
use crate::{color::*, emath::*, FontFamily, FontId, Response, RichText, WidgetText};
|
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;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -344,7 +344,7 @@ pub struct Visuals {
|
||||||
/// Background color behind code-styled monospaced labels.
|
/// Background color behind code-styled monospaced labels.
|
||||||
pub code_bg_color: Color32,
|
pub code_bg_color: Color32,
|
||||||
|
|
||||||
pub window_corner_radius: f32,
|
pub window_corner_radius: Rounding,
|
||||||
pub window_shadow: Shadow,
|
pub window_shadow: Shadow,
|
||||||
|
|
||||||
pub popup_shadow: Shadow,
|
pub popup_shadow: Shadow,
|
||||||
|
@ -453,7 +453,7 @@ pub struct WidgetVisuals {
|
||||||
pub bg_stroke: Stroke,
|
pub bg_stroke: Stroke,
|
||||||
|
|
||||||
/// Button frames etc.
|
/// 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, …).
|
/// 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,
|
||||||
|
@ -566,7 +566,7 @@ impl Visuals {
|
||||||
faint_bg_color: Color32::from_gray(24),
|
faint_bg_color: Color32::from_gray(24),
|
||||||
extreme_bg_color: Color32::from_gray(10),
|
extreme_bg_color: Color32::from_gray(10),
|
||||||
code_bg_color: Color32::from_gray(64),
|
code_bg_color: Color32::from_gray(64),
|
||||||
window_corner_radius: 6.0,
|
window_corner_radius: Rounding::same(6.0),
|
||||||
window_shadow: Shadow::big_dark(),
|
window_shadow: Shadow::big_dark(),
|
||||||
popup_shadow: Shadow::small_dark(),
|
popup_shadow: Shadow::small_dark(),
|
||||||
resize_corner_size: 12.0,
|
resize_corner_size: 12.0,
|
||||||
|
@ -629,35 +629,35 @@ impl Widgets {
|
||||||
bg_fill: Color32::from_gray(27), // window background
|
bg_fill: Color32::from_gray(27), // window background
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines, windows outlines
|
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
|
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,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
inactive: WidgetVisuals {
|
inactive: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(60), // button background
|
bg_fill: Color32::from_gray(60), // button background
|
||||||
bg_stroke: Default::default(),
|
bg_stroke: Default::default(),
|
||||||
fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text
|
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,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
hovered: WidgetVisuals {
|
hovered: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(70),
|
bg_fill: Color32::from_gray(70),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
|
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)),
|
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
|
||||||
corner_radius: 3.0,
|
corner_radius: Rounding::same(3.0),
|
||||||
expansion: 1.0,
|
expansion: 1.0,
|
||||||
},
|
},
|
||||||
active: WidgetVisuals {
|
active: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(55),
|
bg_fill: Color32::from_gray(55),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::WHITE),
|
bg_stroke: Stroke::new(1.0, Color32::WHITE),
|
||||||
fg_stroke: Stroke::new(2.0, Color32::WHITE),
|
fg_stroke: Stroke::new(2.0, Color32::WHITE),
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
expansion: 1.0,
|
expansion: 1.0,
|
||||||
},
|
},
|
||||||
open: WidgetVisuals {
|
open: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(27),
|
bg_fill: Color32::from_gray(27),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),
|
||||||
fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),
|
fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
expansion: 0.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -669,35 +669,35 @@ impl Widgets {
|
||||||
bg_fill: Color32::from_gray(235), // window background
|
bg_fill: Color32::from_gray(235), // window background
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(190)), // separators, indentation lines, windows outlines
|
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
|
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,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
inactive: WidgetVisuals {
|
inactive: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(215), // button background
|
bg_fill: Color32::from_gray(215), // button background
|
||||||
bg_stroke: Default::default(),
|
bg_stroke: Default::default(),
|
||||||
fg_stroke: Stroke::new(1.0, Color32::from_gray(80)), // button text
|
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,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
hovered: WidgetVisuals {
|
hovered: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(210),
|
bg_fill: Color32::from_gray(210),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(105)), // e.g. hover over window edge or button
|
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),
|
fg_stroke: Stroke::new(1.5, Color32::BLACK),
|
||||||
corner_radius: 3.0,
|
corner_radius: Rounding::same(3.0),
|
||||||
expansion: 1.0,
|
expansion: 1.0,
|
||||||
},
|
},
|
||||||
active: WidgetVisuals {
|
active: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(165),
|
bg_fill: Color32::from_gray(165),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::BLACK),
|
bg_stroke: Stroke::new(1.0, Color32::BLACK),
|
||||||
fg_stroke: Stroke::new(2.0, Color32::BLACK),
|
fg_stroke: Stroke::new(2.0, Color32::BLACK),
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
expansion: 1.0,
|
expansion: 1.0,
|
||||||
},
|
},
|
||||||
open: WidgetVisuals {
|
open: WidgetVisuals {
|
||||||
bg_fill: Color32::from_gray(220),
|
bg_fill: Color32::from_gray(220),
|
||||||
bg_stroke: Stroke::new(1.0, Color32::from_gray(160)),
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(160)),
|
||||||
fg_stroke: Stroke::new(1.0, Color32::BLACK),
|
fg_stroke: Stroke::new(1.0, Color32::BLACK),
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
expansion: 0.0,
|
expansion: 0.0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -949,7 +949,12 @@ impl WidgetVisuals {
|
||||||
} = self;
|
} = self;
|
||||||
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::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)");
|
stroke_ui(ui, fg_stroke, "fg_stroke (text)");
|
||||||
ui.add(Slider::new(expansion, -5.0..=5.0).text("expansion"))
|
ui.add(Slider::new(expansion, -5.0..=5.0).text("expansion"))
|
||||||
.on_hover_text("make shapes this much larger");
|
.on_hover_text("make shapes this much larger");
|
||||||
|
@ -1024,7 +1029,13 @@ impl Visuals {
|
||||||
// Common shortcuts
|
// Common shortcuts
|
||||||
ui_color(ui, &mut widgets.noninteractive.bg_fill, "Fill");
|
ui_color(ui, &mut widgets.noninteractive.bg_fill, "Fill");
|
||||||
stroke_ui(ui, &mut widgets.noninteractive.bg_stroke, "Outline");
|
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, window_shadow, "Shadow");
|
||||||
shadow_ui(ui, popup_shadow, "Shadow (small menus and popups)");
|
shadow_ui(ui, popup_shadow, "Shadow (small menus and popups)");
|
||||||
});
|
});
|
||||||
|
|
|
@ -457,7 +457,12 @@ impl Widget for ImageButton {
|
||||||
if ui.is_rect_visible(rect) {
|
if ui.is_rect_visible(rect) {
|
||||||
let (expansion, corner_radius, fill, stroke) = if selected {
|
let (expansion, corner_radius, fill, stroke) = if selected {
|
||||||
let selection = ui.visuals().selection;
|
let selection = ui.visuals().selection;
|
||||||
(-padding, 0.0, selection.bg_fill, selection.stroke)
|
(
|
||||||
|
-padding,
|
||||||
|
Rounding::none(),
|
||||||
|
selection.bg_fill,
|
||||||
|
selection.stroke,
|
||||||
|
)
|
||||||
} else if frame {
|
} else if frame {
|
||||||
let visuals = ui.style().interact(&response);
|
let visuals = ui.style().interact(&response);
|
||||||
let expansion = if response.hovered {
|
let expansion = if response.hovered {
|
||||||
|
|
|
@ -61,7 +61,7 @@ fn show_hsva(ui: &mut Ui, color: Hsva, desired_size: Vec2) -> Response {
|
||||||
} else {
|
} else {
|
||||||
ui.painter().add(RectShape {
|
ui.painter().add(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
fill: color.into(),
|
fill: color.into(),
|
||||||
stroke: Stroke::new(3.0, color.to_opaque()),
|
stroke: Stroke::new(3.0, color.to_opaque()),
|
||||||
});
|
});
|
||||||
|
@ -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(left_half, 0.0, color);
|
||||||
ui.painter().rect_filled(right_half, 0.0, color.to_opaque());
|
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()
|
ui.painter()
|
||||||
.rect_stroke(rect, corner_radius, (2.0, visuals.bg_fill)); // fill is intentional, because default style has no border
|
.rect_stroke(rect, corner_radius, (2.0, visuals.bg_fill)); // fill is intentional, because default style has no border
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::emath::NumExt;
|
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 super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
|
||||||
use crate::plot::{BarChart, ScreenTransform, Value};
|
use crate::plot::{BarChart, ScreenTransform, Value};
|
||||||
|
@ -129,7 +129,7 @@ impl Bar {
|
||||||
let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max());
|
let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max());
|
||||||
let rect = Shape::Rect(RectShape {
|
let rect = Shape::Rect(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius: 0.0,
|
corner_radius: Rounding::none(),
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::emath::NumExt;
|
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 super::{add_rulers_and_text, highlighted_color, Orientation, PlotConfig, RectElement};
|
||||||
use crate::plot::{BoxPlot, ScreenTransform, Value};
|
use crate::plot::{BoxPlot, ScreenTransform, Value};
|
||||||
|
@ -152,7 +152,7 @@ impl BoxElem {
|
||||||
);
|
);
|
||||||
let rect = Shape::Rect(RectShape {
|
let rect = Shape::Rect(RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius: 0.0,
|
corner_radius: Rounding::none(),
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
});
|
});
|
||||||
|
|
|
@ -479,7 +479,7 @@ impl Plot {
|
||||||
if show_background {
|
if show_background {
|
||||||
ui.painter().sub_region(rect).add(epaint::RectShape {
|
ui.painter().sub_region(rect).add(epaint::RectShape {
|
||||||
rect,
|
rect,
|
||||||
corner_radius: 2.0,
|
corner_radius: Rounding::same(2.0),
|
||||||
fill: ui.visuals().extreme_bg_color,
|
fill: ui.visuals().extreme_bg_color,
|
||||||
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
|
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
|
||||||
});
|
});
|
||||||
|
|
|
@ -110,7 +110,7 @@ pub use {
|
||||||
image::{AlphaImage, ColorImage, ImageData, ImageDelta},
|
image::{AlphaImage, ColorImage, ImageData, ImageDelta},
|
||||||
mesh::{Mesh, Mesh16, Vertex},
|
mesh::{Mesh, Mesh16, Vertex},
|
||||||
shadow::Shadow,
|
shadow::Shadow,
|
||||||
shape::{CircleShape, PathShape, RectShape, Shape, TextShape},
|
shape::{CircleShape, PathShape, RectShape, Rounding, Shape, TextShape},
|
||||||
stats::PaintStats,
|
stats::PaintStats,
|
||||||
stroke::Stroke,
|
stroke::Stroke,
|
||||||
tessellator::{tessellate_shapes, TessellationOptions, Tessellator},
|
tessellator::{tessellate_shapes, TessellationOptions, Tessellator},
|
||||||
|
|
|
@ -46,17 +46,23 @@ impl Shadow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tessellate(&self, rect: emath::Rect, corner_radius: f32) -> Mesh {
|
pub fn tessellate(&self, rect: emath::Rect, corner_radius: impl Into<Rounding>) -> Mesh {
|
||||||
// tessellator.clip_rect = clip_rect; // TODO: culling
|
// tessellator.clip_rect = clip_rect; // TODO: culling
|
||||||
|
|
||||||
let Self { extrusion, color } = *self;
|
let Self { extrusion, color } = *self;
|
||||||
|
|
||||||
|
let cr: Rounding = corner_radius.into();
|
||||||
|
let half_ext = 0.5 * extrusion;
|
||||||
|
|
||||||
|
let ext_corner_radius = Rounding {
|
||||||
|
nw: cr.nw + half_ext,
|
||||||
|
ne: cr.ne + half_ext,
|
||||||
|
sw: cr.sw + half_ext,
|
||||||
|
se: cr.se + half_ext,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::tessellator::*;
|
use crate::tessellator::*;
|
||||||
let rect = RectShape::filled(
|
let rect = RectShape::filled(rect.expand(half_ext), ext_corner_radius, color);
|
||||||
rect.expand(0.5 * extrusion),
|
|
||||||
corner_radius + 0.5 * extrusion,
|
|
||||||
color,
|
|
||||||
);
|
|
||||||
let mut tessellator = Tessellator::from_options(TessellationOptions {
|
let mut tessellator = Tessellator::from_options(TessellationOptions {
|
||||||
aa_size: extrusion,
|
aa_size: extrusion,
|
||||||
anti_alias: true,
|
anti_alias: true,
|
||||||
|
|
|
@ -114,12 +114,20 @@ impl Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rect_filled(rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) -> Self {
|
pub fn rect_filled(
|
||||||
|
rect: Rect,
|
||||||
|
corner_radius: impl Into<Rounding>,
|
||||||
|
fill_color: impl Into<Color32>,
|
||||||
|
) -> Self {
|
||||||
Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
|
Self::Rect(RectShape::filled(rect, corner_radius, fill_color))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rect_stroke(rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) -> Self {
|
pub fn rect_stroke(
|
||||||
|
rect: Rect,
|
||||||
|
corner_radius: impl Into<Rounding>,
|
||||||
|
stroke: impl Into<Stroke>,
|
||||||
|
) -> Self {
|
||||||
Self::Rect(RectShape::stroke(rect, corner_radius, stroke))
|
Self::Rect(RectShape::stroke(rect, corner_radius, stroke))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,28 +329,36 @@ impl From<PathShape> for Shape {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct RectShape {
|
pub struct RectShape {
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
/// How rounded the corners are. Use `0.0` for no rounding.
|
/// How rounded the corners are. Use `Rounding::none()` for no rounding.
|
||||||
pub corner_radius: f32,
|
pub corner_radius: Rounding,
|
||||||
pub fill: Color32,
|
pub fill: Color32,
|
||||||
pub stroke: Stroke,
|
pub stroke: Stroke,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RectShape {
|
impl RectShape {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn filled(rect: Rect, corner_radius: f32, fill_color: impl Into<Color32>) -> Self {
|
pub fn filled(
|
||||||
|
rect: Rect,
|
||||||
|
corner_radius: impl Into<Rounding>,
|
||||||
|
fill_color: impl Into<Color32>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rect,
|
rect,
|
||||||
corner_radius,
|
corner_radius: corner_radius.into(),
|
||||||
fill: fill_color.into(),
|
fill: fill_color.into(),
|
||||||
stroke: Default::default(),
|
stroke: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stroke(rect: Rect, corner_radius: f32, stroke: impl Into<Stroke>) -> Self {
|
pub fn stroke(
|
||||||
|
rect: Rect,
|
||||||
|
corner_radius: impl Into<Rounding>,
|
||||||
|
stroke: impl Into<Stroke>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rect,
|
rect,
|
||||||
corner_radius,
|
corner_radius: corner_radius.into(),
|
||||||
fill: Default::default(),
|
fill: Default::default(),
|
||||||
stroke: stroke.into(),
|
stroke: stroke.into(),
|
||||||
}
|
}
|
||||||
|
@ -362,6 +378,57 @@ impl From<RectShape> for Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
/// How rounded the corners of things should be
|
||||||
|
pub struct Rounding {
|
||||||
|
pub nw: f32,
|
||||||
|
pub ne: f32,
|
||||||
|
pub sw: f32,
|
||||||
|
pub se: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Rounding {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for Rounding {
|
||||||
|
#[inline]
|
||||||
|
fn from(radius: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
nw: radius,
|
||||||
|
ne: radius,
|
||||||
|
sw: radius,
|
||||||
|
se: radius,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rounding {
|
||||||
|
#[inline]
|
||||||
|
pub fn same(radius: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
nw: radius,
|
||||||
|
ne: radius,
|
||||||
|
sw: radius,
|
||||||
|
se: radius,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
nw: 0.0,
|
||||||
|
ne: 0.0,
|
||||||
|
sw: 0.0,
|
||||||
|
se: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// How to paint some text on screen.
|
/// How to paint some text on screen.
|
||||||
|
|
|
@ -179,20 +179,20 @@ impl Path {
|
||||||
|
|
||||||
pub mod path {
|
pub mod path {
|
||||||
//! Helpers for constructing paths
|
//! Helpers for constructing paths
|
||||||
|
use crate::shape::Rounding;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// overwrites existing points
|
/// overwrites existing points
|
||||||
pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, corner_radius: f32) {
|
pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, corner_radius: Rounding) {
|
||||||
path.clear();
|
path.clear();
|
||||||
|
|
||||||
let min = rect.min;
|
let min = rect.min;
|
||||||
let max = rect.max;
|
let max = rect.max;
|
||||||
|
|
||||||
let cr = corner_radius
|
let cr = clamp_radius(corner_radius, rect);
|
||||||
.min(rect.width() * 0.5)
|
|
||||||
.min(rect.height() * 0.5);
|
|
||||||
|
|
||||||
if cr <= 0.0 {
|
if cr == Rounding::none() {
|
||||||
let min = rect.min;
|
let min = rect.min;
|
||||||
let max = rect.max;
|
let max = rect.max;
|
||||||
path.reserve(4);
|
path.reserve(4);
|
||||||
|
@ -201,10 +201,10 @@ pub mod path {
|
||||||
path.push(pos2(max.x, max.y));
|
path.push(pos2(max.x, max.y));
|
||||||
path.push(pos2(min.x, max.y));
|
path.push(pos2(min.x, max.y));
|
||||||
} else {
|
} else {
|
||||||
add_circle_quadrant(path, pos2(max.x - cr, max.y - cr), cr, 0.0);
|
add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0);
|
||||||
add_circle_quadrant(path, pos2(min.x + cr, max.y - cr), cr, 1.0);
|
add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0);
|
||||||
add_circle_quadrant(path, pos2(min.x + cr, min.y + cr), cr, 2.0);
|
add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0);
|
||||||
add_circle_quadrant(path, pos2(max.x - cr, min.y + cr), cr, 3.0);
|
add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +242,20 @@ pub mod path {
|
||||||
path.push(center + radius * Vec2::angled(angle));
|
path.push(center + radius * Vec2::angled(angle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures the radius of each corner is within a valid range
|
||||||
|
fn clamp_radius(corner_radius: Rounding, rect: Rect) -> Rounding {
|
||||||
|
let half_width = rect.width() * 0.5;
|
||||||
|
let half_height = rect.height() * 0.5;
|
||||||
|
let max_cr = half_width.min(half_height);
|
||||||
|
|
||||||
|
Rounding {
|
||||||
|
nw: corner_radius.nw.at_most(max_cr),
|
||||||
|
ne: corner_radius.ne.at_most(max_cr),
|
||||||
|
sw: corner_radius.sw.at_most(max_cr),
|
||||||
|
se: corner_radius.se.at_most(max_cr),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue