From 8eda32ec64c7b48c7b8f9195e3abc8c10efa8f00 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 Jan 2025 09:34:22 +0100 Subject: [PATCH 01/22] egui_kittest: succeed and keep going when `UPDATE_SNAPSHOTS` is set (#5649) It used to be that `UPDATE_SNAPSHOTS=true cargo test --all-features` would stop on the first crate with a failure, requiring you to run it multiple times, which is annoying, and a waste of time. --- crates/egui_kittest/README.md | 13 ++--- crates/egui_kittest/src/snapshot.rs | 79 +++++++++++++++++------------ 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/crates/egui_kittest/README.md b/crates/egui_kittest/README.md index 99fe9be65..a9c1286bf 100644 --- a/crates/egui_kittest/README.md +++ b/crates/egui_kittest/README.md @@ -1,4 +1,4 @@ -# egui_kittest +# egui_kittest Ui testing library for egui, based on [kittest](https://github.com/rerun-io/kittest) (an [AccessKit](https://github.com/AccessKit/accesskit) based testing library). @@ -14,16 +14,16 @@ fn main() { }; let mut harness = Harness::new_ui(app); - + let checkbox = harness.get_by_label("Check me!"); assert_eq!(checkbox.toggled(), Some(Toggled::False)); checkbox.click(); - + harness.run(); let checkbox = harness.get_by_label("Check me!"); assert_eq!(checkbox.toggled(), Some(Toggled::True)); - + // Shrink the window size to the smallest size possible harness.fit_contents(); @@ -38,9 +38,10 @@ There is a snapshot testing feature. To create snapshot tests, enable the `snaps Once enabled, you can call `Harness::snapshot` to render the ui and save the image to the `tests/snapshots` directory. To update the snapshots, run your tests with `UPDATE_SNAPSHOTS=true`, so e.g. `UPDATE_SNAPSHOTS=true cargo test`. -Running with `UPDATE_SNAPSHOTS=true` will still cause the tests to fail, but on the next run, the tests should pass. +Running with `UPDATE_SNAPSHOTS=true` will cause the tests to succeed. +This is so that you can set `UPDATE_SNAPSHOTS=true` and update _all_ tests, without `cargo test` failing on the first failing crate. -If you want to have multiple snapshots in the same test, it makes sense to collect the results in a `Vec` +If you want to have multiple snapshots in the same test, it makes sense to collect the results in a `Vec` ([look here](https://github.com/emilk/egui/blob/70a01138b77f9c5724a35a6ef750b9ae1ab9f2dc/crates/egui_demo_lib/src/demo/demo_app_windows.rs#L388-L427) for an example). This way they can all be updated at the same time. diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index be4f68a06..409c287c8 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -155,8 +155,15 @@ impl Display for SnapshotError { } } +/// If this is set, we update the snapshots (if different), +/// and _succeed_ the test. +/// This is so that you can set `UPDATE_SNAPSHOTS=true` and update _all_ tests, +/// without `cargo test` failing on the first failing crate. fn should_update_snapshots() -> bool { - std::env::var("UPDATE_SNAPSHOTS").is_ok() + match std::env::var("UPDATE_SNAPSHOTS") { + Ok(value) => !matches!(value.as_str(), "false" | "0" | "no" | "off"), + Err(_) => false, + } } /// Image snapshot test with custom options. @@ -203,23 +210,22 @@ pub fn try_image_snapshot_options( std::fs::remove_file(&old_backup_path).ok(); std::fs::remove_file(&new_path).ok(); - let maybe_update_snapshot = || { - if should_update_snapshots() { - // Keep the old version so the user can compare it: - std::fs::rename(&snapshot_path, &old_backup_path).ok(); + let update_snapshot = || { + // Keep the old version so the user can compare it: + std::fs::rename(&snapshot_path, &old_backup_path).ok(); - // Write the new file to the checked in path: - new.save(&snapshot_path) - .map_err(|err| SnapshotError::WriteSnapshot { - err, - path: snapshot_path.clone(), - })?; + // Write the new file to the checked in path: + new.save(&snapshot_path) + .map_err(|err| SnapshotError::WriteSnapshot { + err, + path: snapshot_path.clone(), + })?; - // No need for an explicit `.new` file: - std::fs::remove_file(&new_path).ok(); + // No need for an explicit `.new` file: + std::fs::remove_file(&new_path).ok(); + + println!("Updated snapshot: {snapshot_path:?}"); - println!("Updated snapshot: {snapshot_path:?}"); - } Ok(()) }; @@ -234,21 +240,27 @@ pub fn try_image_snapshot_options( Ok(image) => image.to_rgba8(), Err(err) => { // No previous snapshot - probablye a new test. - maybe_update_snapshot()?; - return Err(SnapshotError::OpenSnapshot { - path: snapshot_path.clone(), - err, - }); + if should_update_snapshots() { + return update_snapshot(); + } else { + return Err(SnapshotError::OpenSnapshot { + path: snapshot_path.clone(), + err, + }); + } } }; if previous.dimensions() != new.dimensions() { - maybe_update_snapshot()?; - return Err(SnapshotError::SizeMismatch { - name: name.to_owned(), - expected: previous.dimensions(), - actual: new.dimensions(), - }); + if should_update_snapshots() { + return update_snapshot(); + } else { + return Err(SnapshotError::SizeMismatch { + name: name.to_owned(), + expected: previous.dimensions(), + actual: new.dimensions(), + }); + } } // Compare existing image to the new one: @@ -262,12 +274,15 @@ pub fn try_image_snapshot_options( path: diff_path.clone(), err, })?; - maybe_update_snapshot()?; - Err(SnapshotError::Diff { - name: name.to_owned(), - diff, - diff_path, - }) + if should_update_snapshots() { + update_snapshot() + } else { + Err(SnapshotError::Diff { + name: name.to_owned(), + diff, + diff_path, + }) + } } else { Ok(()) } From ee5f0d6d52ea5311d6bd6acc13105faf3f0cf6a8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 Jan 2025 20:37:29 +0100 Subject: [PATCH 02/22] eframe web: forward cmd-S/O to egui app (stop default browser action) (#5655) This allow eframe apps to capture cmd-S and cmd-O to trigger their own save and open actions, instead of having the browser do something annoying. --- crates/eframe/src/web/events.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 7e2d07a1b..762f202fa 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -219,9 +219,16 @@ fn should_prevent_default_for_key( // * cmd-shift-C (debug tools) // * cmd/ctrl-c/v/x (lest we prevent copy/paste/cut events) - // Prevent ctrl-P from opening the print dialog. Users may want to use it for a command palette. - if egui_key == egui::Key::P && (modifiers.ctrl || modifiers.command || modifiers.mac_cmd) { - return true; + // Prevent cmd/ctrl plus these keys from triggering the default browser action: + let keys = [ + egui::Key::O, // open + egui::Key::P, // print (cmd-P is common for command palette) + egui::Key::S, // save + ]; + for key in keys { + if egui_key == key && (modifiers.ctrl || modifiers.command || modifiers.mac_cmd) { + return true; + } } if egui_key == egui::Key::Space && !runner.text_agent.has_focus() { From 04fca9c3245883018709f4a50572fd3d71c1be60 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 Jan 2025 20:51:12 +0100 Subject: [PATCH 03/22] Remove date button from Scene demo, so as not to fail tests each day (#5657) --- crates/egui_demo_lib/src/demo/scene.rs | 2 +- .../egui_demo_lib/src/demo/widget_gallery.rs | 19 ++++++++++++++++++- .../tests/snapshots/demos/Scene.png | 4 ++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/egui_demo_lib/src/demo/scene.rs b/crates/egui_demo_lib/src/demo/scene.rs index a7d268e1f..ba0dc5974 100644 --- a/crates/egui_demo_lib/src/demo/scene.rs +++ b/crates/egui_demo_lib/src/demo/scene.rs @@ -11,7 +11,7 @@ pub struct SceneDemo { impl Default for SceneDemo { fn default() -> Self { Self { - widget_gallery: Default::default(), + widget_gallery: widget_gallery::WidgetGallery::default().with_date_button(false), // disable date button so that we don't fail the snapshot test scene_rect: Rect::ZERO, // `egui::Scene` will initialize this to something valid } } diff --git a/crates/egui_demo_lib/src/demo/widget_gallery.rs b/crates/egui_demo_lib/src/demo/widget_gallery.rs index cfe746899..c51a9efd8 100644 --- a/crates/egui_demo_lib/src/demo/widget_gallery.rs +++ b/crates/egui_demo_lib/src/demo/widget_gallery.rs @@ -22,6 +22,8 @@ pub struct WidgetGallery { #[cfg(feature = "chrono")] #[cfg_attr(feature = "serde", serde(skip))] date: Option, + + with_date_button: bool, } impl Default for WidgetGallery { @@ -38,10 +40,23 @@ impl Default for WidgetGallery { animate_progress_bar: false, #[cfg(feature = "chrono")] date: None, + #[cfg(feature = "chrono")] + with_date_button: true, } } } +impl WidgetGallery { + #[inline] + pub fn with_date_button(mut self, _with_date_button: bool) -> Self { + #[cfg(feature = "chrono")] + { + self.with_date_button = _with_date_button; + } + self + } +} + impl crate::Demo for WidgetGallery { fn name(&self) -> &'static str { "🗄 Widget Gallery" @@ -124,6 +139,8 @@ impl WidgetGallery { animate_progress_bar, #[cfg(feature = "chrono")] date, + #[cfg(feature = "chrono")] + with_date_button, } = self; ui.add(doc_link_label("Label", "label")); @@ -226,7 +243,7 @@ impl WidgetGallery { ui.end_row(); #[cfg(feature = "chrono")] - { + if *with_date_button { let date = date.get_or_insert_with(|| chrono::offset::Utc::now().date_naive()); ui.add(doc_link_label_with_crate( "egui_extras", diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index d9f483ec1..ae1d6acf7 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d076f5365bfa87b7e61d3808b8b9b367157ea6e55ccf665720cbd2237d53793d -size 35563 +oid sha256:93f2bf32b0607b56a9eebefdc07b55d66988251eeb0ccf7799c2836281d5d5fb +size 35573 From 4b9da5f6504b693bb621f3952f967337d0421c10 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 Jan 2025 21:02:50 +0100 Subject: [PATCH 04/22] Remove `StrokeKind::default` (#5658) Since there is no natural default for `RectShape`. --- crates/epaint/src/stroke.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/epaint/src/stroke.rs b/crates/epaint/src/stroke.rs index fa85a9588..ce7d274dd 100644 --- a/crates/epaint/src/stroke.rs +++ b/crates/epaint/src/stroke.rs @@ -69,16 +69,10 @@ pub enum StrokeKind { Outside, } -impl Default for StrokeKind { - fn default() -> Self { - Self::Middle - } -} - /// Describes the width and color of paths. The color can either be solid or provided by a callback. For more information, see [`ColorMode`] /// /// The default stroke is the same as [`Stroke::NONE`]. -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct PathStroke { pub width: f32, @@ -86,6 +80,13 @@ pub struct PathStroke { pub kind: StrokeKind, } +impl Default for PathStroke { + #[inline] + fn default() -> Self { + Self::NONE + } +} + impl PathStroke { /// Same as [`PathStroke::default`]. pub const NONE: Self = Self { @@ -99,7 +100,7 @@ impl PathStroke { Self { width: width.into(), color: ColorMode::Solid(color.into()), - kind: StrokeKind::default(), + kind: StrokeKind::Middle, } } @@ -114,7 +115,7 @@ impl PathStroke { Self { width: width.into(), color: ColorMode::UV(Arc::new(callback)), - kind: StrokeKind::default(), + kind: StrokeKind::Middle, } } @@ -168,7 +169,7 @@ impl From for PathStroke { Self { width: value.width, color: ColorMode::Solid(value.color), - kind: StrokeKind::default(), + kind: StrokeKind::Middle, } } } From 50294b5d9f51d4c599c243669c464f826ac51728 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 30 Jan 2025 21:04:36 +0100 Subject: [PATCH 05/22] Be smarter when rounding rectangles to the pixel grid (#5656) --- .../tests/snapshots/demos/Scene.png | 4 +- crates/epaint/src/shapes/rect_shape.rs | 6 +- crates/epaint/src/tessellator.rs | 138 +++++++++++------- 3 files changed, 90 insertions(+), 58 deletions(-) diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index ae1d6acf7..78f697490 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93f2bf32b0607b56a9eebefdc07b55d66988251eeb0ccf7799c2836281d5d5fb -size 35573 +oid sha256:9fee66bab841602862c301a7df3add365ea93fde0ac9b71ca7f9b74a709a68d2 +size 35576 diff --git a/crates/epaint/src/shapes/rect_shape.rs b/crates/epaint/src/shapes/rect_shape.rs index ca7ab01f1..f691234b1 100644 --- a/crates/epaint/src/shapes/rect_shape.rs +++ b/crates/epaint/src/shapes/rect_shape.rs @@ -21,13 +21,13 @@ pub struct RectShape { pub stroke: Stroke, /// Is the stroke on the inside, outside, or centered on the rectangle? + /// + /// If you want to perfectly tile rectangles, use [`StrokeKind::Inside`]. pub stroke_kind: StrokeKind, /// Snap the rectangle to pixels? /// /// Rounding produces sharper rectangles. - /// It is the outside of the fill (=inside of the stroke) - /// that will be rounded to the physical pixel grid. /// /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used. pub round_to_pixels: Option, @@ -117,8 +117,6 @@ impl RectShape { /// Snap the rectangle to pixels? /// /// Rounding produces sharper rectangles. - /// It is the outside of the fill (=inside of the stroke) - /// that will be rounded to the physical pixel grid. /// /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used. #[inline] diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index f05d9b47a..45919759a 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -8,14 +8,12 @@ use emath::{pos2, remap, vec2, GuiRounding as _, NumExt, Pos2, Rect, Rot2, Vec2}; use crate::{ - color, emath, stroke, texture_atlas::PreparedDisc, CircleShape, ClippedPrimitive, ClippedShape, - Color32, CubicBezierShape, EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, - RectShape, Rounding, Shape, Stroke, StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, + color::ColorMode, emath, stroke::PathStroke, texture_atlas::PreparedDisc, CircleShape, + ClippedPrimitive, ClippedShape, Color32, CubicBezierShape, EllipseShape, Mesh, PathShape, + Primitive, QuadraticBezierShape, RectShape, Rounding, Shape, Stroke, StrokeKind, TextShape, + TextureId, Vertex, WHITE_UV, }; -use self::color::ColorMode; -use self::stroke::PathStroke; - // ---------------------------------------------------------------------------- #[allow(clippy::approx_constant)] @@ -920,13 +918,13 @@ fn fill_closed_path_with_uv( #[inline(always)] fn translate_stroke_point(p: &mut PathPoint, stroke: &PathStroke) { match stroke.kind { - stroke::StrokeKind::Middle => { /* Nothing to do */ } - stroke::StrokeKind::Outside => { - p.pos += p.normal * stroke.width * 0.5; - } - stroke::StrokeKind::Inside => { + StrokeKind::Inside => { p.pos -= p.normal * stroke.width * 0.5; } + StrokeKind::Middle => { /* Nothing to do */ } + StrokeKind::Outside => { + p.pos += p.normal * stroke.width * 0.5; + } } } @@ -947,7 +945,7 @@ fn stroke_path( let idx = out.vertices.len() as u32; // Translate the points along their normals if the stroke is outside or inside - if stroke.kind != stroke::StrokeKind::Middle { + if stroke.kind != StrokeKind::Middle { path.iter_mut() .for_each(|p| translate_stroke_point(p, stroke)); } @@ -1672,12 +1670,18 @@ impl Tessellator { /// * `rect`: the rectangle to tessellate. /// * `out`: triangles are appended to this. pub fn tessellate_rect(&mut self, rect_shape: &RectShape, out: &mut Mesh) { + if self.options.coarse_tessellation_culling + && !rect_shape.visual_bounding_rect().intersects(self.clip_rect) + { + return; + } + let brush = rect_shape.brush.as_ref(); let RectShape { mut rect, mut rounding, fill, - stroke, + mut stroke, stroke_kind, round_to_pixels, mut blur_width, @@ -1686,9 +1690,56 @@ impl Tessellator { let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels); - // Modify `rect` so that it represents the filled region, with the stroke on the outside: + // Important: round to pixels BEFORE applying stroke_kind + if round_to_pixels { + // The rounding is aware of the stroke kind. + // It is designed to be clever in trying to divine the intentions of the user. + match stroke_kind { + StrokeKind::Inside => { + // The stroke is inside the rect, so the rect defines the _outside_ of the stroke. + // We round the outside of the stroke on a pixel boundary. + // This will make the outside of the stroke crisp. + // + // Will make each stroke asymmetric if not an even multiple of physical pixels, + // but the left stroke will always be the mirror image of the right stroke, + // and the top stroke will always be the mirror image of the bottom stroke. + // + // This is so that a user can tile rectangles with `StrokeKind::Inside`, + // and get no pixel overlap between them. + rect = rect.round_to_pixels(self.pixels_per_point); + } + StrokeKind::Middle => { + // On this path we optimize for crisp and symmetric strokes. + // We put odd-width strokes in the center of pixels. + // To understand why, see `fn round_line_segment`. + if stroke.width <= self.feathering + || is_nearest_integer_odd(self.pixels_per_point * stroke.width) + { + rect = rect.round_to_pixel_center(self.pixels_per_point); + } else { + rect = rect.round_to_pixels(self.pixels_per_point); + } + } + StrokeKind::Outside => { + // Put the inside of the stroke on a pixel boundary. + // Makes the inside of the stroke and the filled rect crisp, + // but the outside of the stroke may become feathered (blurry). + // + // Will make each stroke asymmetric if not an even multiple of physical pixels, + // but the left stroke will always be the mirror image of the right stroke, + // and the top stroke will always be the mirror image of the bottom stroke. + rect = rect.round_to_pixels(self.pixels_per_point); + } + } + } + + // Modify `rect` so that it represents the filled region, with the stroke on the outside. + // Important: do this AFTER rounding to pixels match stroke_kind { StrokeKind::Inside => { + // Shrink the stroke so it fits inside the rect: + stroke.width = stroke.width.at_most(rect.size().min_elem() / 2.0); + rect = rect.shrink(stroke.width); } StrokeKind::Middle => { @@ -1699,28 +1750,6 @@ impl Tessellator { } } - if self.options.coarse_tessellation_culling - && !rect.expand(stroke.width).intersects(self.clip_rect) - { - return; - } - - if round_to_pixels { - // Since the stroke extends outside of the rectangle, - // we can round the rectangle sides to the physical pixel edges, - // and the filled rect will appear crisp, as will the inside of the stroke. - let Stroke { width, .. } = stroke; // Make sure we remember to update this if we change `stroke` to `PathStroke` - if width <= self.feathering && !stroke.is_empty() { - // If the stroke is thin, make sure its center is in the center of the pixel: - rect = rect - .expand(width / 2.0) - .round_to_pixel_center(self.pixels_per_point) - .shrink(width / 2.0); - } else { - rect = rect.round_to_pixels(self.pixels_per_point); - } - } - // It is common to (sometimes accidentally) create an infinitely sized rectangle. // Make sure we can handle that: rect.min = rect.min.at_least(pos2(-1e7, -1e7)); @@ -1751,6 +1780,7 @@ impl Tessellator { if rect.width() < 0.5 * self.feathering { // Very thin - approximate by a vertical line-segment: + // There is room for improvement here, but it is not critical. let line = [rect.center_top(), rect.center_bottom()]; if 0.0 < rect.width() && fill != Color32::TRANSPARENT { self.tessellate_line_segment(line, Stroke::new(rect.width(), fill), out); @@ -1761,6 +1791,7 @@ impl Tessellator { } } else if rect.height() < 0.5 * self.feathering { // Very thin - approximate by a horizontal line-segment: + // There is room for improvement here, but it is not critical. let line = [rect.left_center(), rect.right_center()]; if 0.0 < rect.height() && fill != Color32::TRANSPARENT { self.tessellate_line_segment(line, Stroke::new(rect.height(), fill), out); @@ -1776,22 +1807,25 @@ impl Tessellator { path.add_line_loop(&self.scratchpad_points); let path_stroke = PathStroke::from(stroke).outside(); - if let Some(brush) = brush { - let crate::Brush { - fill_texture_id, - uv, - } = **brush; - // Textured - let uv_from_pos = |p: Pos2| { - pos2( - remap(p.x, rect.x_range(), uv.x_range()), - remap(p.y, rect.y_range(), uv.y_range()), - ) - }; - path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out); - } else { - // Untextured - path.fill(self.feathering, fill, &path_stroke, out); + if rect.is_positive() { + // Fill + if let Some(brush) = brush { + // Textured + let crate::Brush { + fill_texture_id, + uv, + } = **brush; + let uv_from_pos = |p: Pos2| { + pos2( + remap(p.x, rect.x_range(), uv.x_range()), + remap(p.y, rect.y_range(), uv.y_range()), + ) + }; + path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out); + } else { + // Untextured + path.fill(self.feathering, fill, &path_stroke, out); + } } path.stroke_closed(self.feathering, &path_stroke, out); From 99369666eea587f98dc458347e0b9daa6ac99c99 Mon Sep 17 00:00:00 2001 From: Michael Grupp Date: Mon, 3 Feb 2025 14:26:02 +0100 Subject: [PATCH 06/22] Fix typo in kittest docs (#5667) * ~Closes ~ (just a quick typo fix) * [x] I have followed the instructions in the PR template --- crates/egui_kittest/src/snapshot.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index 409c287c8..0b68677b0 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -177,7 +177,7 @@ fn should_update_snapshots() -> bool { /// The snapshot files will be saved under [`SnapshotOptions::output_path`]. /// The snapshot will be saved under `{output_path}/{name}.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. -/// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. +/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// /// If the env-var `UPDATE_SNAPSHOTS` is set, then the old image will backed up under `{output_path}/{name}.old.png`. /// and then new image will be written to `{output_path}/{name}.png` @@ -296,7 +296,7 @@ pub fn try_image_snapshot_options( /// The snapshot files will be saved under [`SnapshotOptions::output_path`]. /// The snapshot will be saved under `{output_path}/{name}.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. -/// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. +/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// /// # Errors /// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error @@ -316,7 +316,7 @@ pub fn try_image_snapshot(current: &image::RgbaImage, name: &str) -> Result<(), /// The snapshot files will be saved under [`SnapshotOptions::output_path`]. /// The snapshot will be saved under `{output_path}/{name}.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. -/// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. +/// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// /// # Panics /// Panics if the image does not match the snapshot or if there was an error reading or writing the @@ -334,7 +334,7 @@ pub fn image_snapshot_options(current: &image::RgbaImage, name: &str, options: & /// Image snapshot test. /// The snapshot will be saved under `tests/snapshots/{name}.png`. /// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`. -/// If new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. +/// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. /// /// # Panics /// Panics if the image does not match the snapshot or if there was an error reading or writing the @@ -351,7 +351,7 @@ pub fn image_snapshot(current: &image::RgbaImage, name: &str) { #[cfg(feature = "wgpu")] impl Harness<'_, State> { - /// Render a image using the setup [`crate::TestRenderer`] and compare it to the snapshot + /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot /// with custom options. /// /// If you want to change the default options for your whole project, you could create an @@ -364,7 +364,7 @@ impl Harness<'_, State> { /// The snapshot files will be saved under [`SnapshotOptions::output_path`]. /// The snapshot will be saved under `{output_path}/{name}.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. - /// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. + /// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// /// # Errors /// Returns a [`SnapshotError`] if the image does not match the snapshot, if there was an @@ -380,10 +380,10 @@ impl Harness<'_, State> { try_image_snapshot_options(&image, name, options) } - /// Render a image using the setup [`crate::TestRenderer`] and compare it to the snapshot. + /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot. /// The snapshot will be saved under `tests/snapshots/{name}.png`. /// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`. - /// If new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. + /// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. /// /// # Errors /// Returns a [`SnapshotError`] if the image does not match the snapshot, if there was an @@ -395,7 +395,7 @@ impl Harness<'_, State> { try_image_snapshot(&image, name) } - /// Render a image using the setup [`crate::TestRenderer`] and compare it to the snapshot + /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot /// with custom options. /// /// If you want to change the default options for your whole project, you could create an @@ -408,7 +408,7 @@ impl Harness<'_, State> { /// The snapshot files will be saved under [`SnapshotOptions::output_path`]. /// The snapshot will be saved under `{output_path}/{name}.png`. /// The new image from the most recent test run will be saved under `{output_path}/{name}.new.png`. - /// If new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. + /// If the new image didn't match the snapshot, a diff image will be saved under `{output_path}/{name}.diff.png`. /// /// # Panics /// Panics if the image does not match the snapshot, if there was an error reading or writing the @@ -423,10 +423,10 @@ impl Harness<'_, State> { } } - /// Render a image using the setup [`crate::TestRenderer`] and compare it to the snapshot. + /// Render an image using the setup [`crate::TestRenderer`] and compare it to the snapshot. /// The snapshot will be saved under `tests/snapshots/{name}.png`. /// The new image from the last test run will be saved under `tests/snapshots/{name}.new.png`. - /// If new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. + /// If the new image didn't match the snapshot, a diff image will be saved under `tests/snapshots/{name}.diff.png`. /// /// # Panics /// Panics if the image does not match the snapshot, if there was an error reading or writing the From b6aa897b9dbaee34bb623217679f03ece4473adb Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 4 Feb 2025 10:16:51 +0100 Subject: [PATCH 07/22] Fix the format + check + test workflow (#5671) * [x] I have followed the instructions in the PR template This broke because upload-artefact v3 was deprecated: https://github.com/emilk/egui/actions/runs/13112546294/job/36579426301?pr=5670 From bac8ea09ac696ff66432111c9b487b192c8e40bc Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 4 Feb 2025 10:17:15 +0100 Subject: [PATCH 08/22] Add docs to `Frame::new` (#5670) * [x] I have followed the instructions in the PR template --- crates/egui/src/containers/frame.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index abe1b8afe..4386f8f86 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -163,6 +163,9 @@ impl Frame { shadow: Shadow::NONE, }; + /// No colors, no margins, no border. + /// + /// Same as [`Frame::NONE`]. pub const fn new() -> Self { Self::NONE } From 9e1117019ae5ae61edf15c7148134bd5931bd985 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Tue, 4 Feb 2025 04:29:26 -0500 Subject: [PATCH 09/22] Add `Color32::CYAN` and `Color32::MAGENTA` (#5663) I often find myself reaching out for these two colors so I figured I'd just add the constants next to `YELLOW` to have CMYK --- crates/ecolor/src/color32.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index c38a8d6b9..52e322667 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -56,7 +56,10 @@ impl Color32 { pub const RED: Self = Self::from_rgb(255, 0, 0); pub const LIGHT_RED: Self = Self::from_rgb(255, 128, 128); + pub const CYAN: Self = Self::from_rgb(0, 255, 255); + pub const MAGENTA: Self = Self::from_rgb(255, 0, 255); pub const YELLOW: Self = Self::from_rgb(255, 255, 0); + pub const ORANGE: Self = Self::from_rgb(255, 165, 0); pub const LIGHT_YELLOW: Self = Self::from_rgb(255, 255, 0xE0); pub const KHAKI: Self = Self::from_rgb(240, 230, 140); From 3c07e01d089f336ed30689179f38b6e752952201 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 11:30:12 +0100 Subject: [PATCH 10/22] Improve tessellation quality (#5669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Defining what `Rounding` is This PR defines what `Rounding` means: it is the corner radius of underlying `RectShape` rectangle. If you use `StrokeKind::Inside`, this means the rounding is of the outer part of the stroke. Conversely, if you use `StrokeKind::Outside`, the stroke is outside the rounded rectangle, so the stroke has an inner radius or `rounding`, and an outer radius that is larger by `stroke.width`. This definitions is the same as Figma uses. ## Improving general shape rendering The rendering of filled shapes (rectangles, circles, paths, bezier) has been rewritten. Instead of first painting the fill with the stroke on top, we now paint them as one single mesh with shared vertices at the border. This has several benefits: * Less work (faster and with fewer vertices produced) * No overdraw (nicer rendering of translucent shapes) * Correct blending of stroke and fill The logic for rendering thin strokes has also been improved, so that the width of a stroke of `StrokeKind::Outside` never affects the filled area (this used to be wrong for thin strokes). ## Improving of rectangle rendering Rectangles also has specific improvements in how thin rectangles are painted. The handling of "Blur width" is also a lot better, and now works for rectangles with strokes. There also used to be bugs with specific combinations of corner radius and stroke width, that are now fixed. ## But why? With the new `egui::Scene` we end up with a lot of zoomed out shapes, with sub-pixel strokes. These need to look good! One thing led to another, and then I became obsessive 😅 ## Tessellation Test In order to investigate the rendering, I created a Tessellation Test in the `egui_demo_lib`. [Try it here](https://egui-pr-preview.github.io/pr/5669-emilkimprove-tessellator) ![Screenshot 2025-02-04 at 08 45 50](https://github.com/user-attachments/assets/20b47a30-de6a-4ff5-885b-2e2fd6d88321) ![image](https://github.com/user-attachments/assets/e17c50eb-5ae7-48d4-bb0d-4f2165075897) --- crates/ecolor/src/color32.rs | 38 ++ crates/egui/src/containers/frame.rs | 15 +- crates/egui/src/containers/window.rs | 3 +- crates/egui/src/introspection.rs | 2 +- .../tests/snapshots/imageviewer.png | 4 +- .../src/demo/demo_app_windows.rs | 1 + .../src/demo/misc_demo_window.rs | 6 +- crates/egui_demo_lib/src/demo/tests/mod.rs | 2 + .../src/demo/tests/tessellation_test.rs | 364 +++++++++++ .../tests/snapshots/demos/Bézier Curve.png | 4 +- .../tests/snapshots/demos/Code Editor.png | 4 +- .../tests/snapshots/demos/Code Example.png | 4 +- .../tests/snapshots/demos/Context Menus.png | 4 +- .../tests/snapshots/demos/Dancing Strings.png | 4 +- .../tests/snapshots/demos/Drag and Drop.png | 4 +- .../tests/snapshots/demos/Extra Viewport.png | 4 +- .../tests/snapshots/demos/Font Book.png | 4 +- .../tests/snapshots/demos/Frame.png | 4 +- .../tests/snapshots/demos/Highlighting.png | 4 +- .../snapshots/demos/Interactive Container.png | 4 +- .../tests/snapshots/demos/Misc Demos.png | 4 +- .../tests/snapshots/demos/Modals.png | 4 +- .../tests/snapshots/demos/Multi Touch.png | 4 +- .../tests/snapshots/demos/Painting.png | 4 +- .../tests/snapshots/demos/Panels.png | 4 +- .../tests/snapshots/demos/Scene.png | 4 +- .../tests/snapshots/demos/Screenshot.png | 4 +- .../tests/snapshots/demos/Scrolling.png | 4 +- .../tests/snapshots/demos/Sliders.png | 4 +- .../tests/snapshots/demos/Strip.png | 4 +- .../tests/snapshots/demos/Table.png | 4 +- .../tests/snapshots/demos/Text Layout.png | 4 +- .../tests/snapshots/demos/TextEdit.png | 4 +- .../tests/snapshots/demos/Tooltips.png | 4 +- .../tests/snapshots/demos/Undo Redo.png | 4 +- .../tests/snapshots/demos/Window Options.png | 4 +- .../tests/snapshots/modals_1.png | 4 +- .../tests/snapshots/modals_2.png | 4 +- .../tests/snapshots/modals_3.png | 4 +- ...rop_should_prevent_focusing_lower_area.png | 4 +- .../snapshots/rendering_test/dpi_1.00.png | 4 +- .../snapshots/rendering_test/dpi_1.25.png | 4 +- .../snapshots/rendering_test/dpi_1.50.png | 4 +- .../snapshots/rendering_test/dpi_1.67.png | 4 +- .../snapshots/rendering_test/dpi_1.75.png | 4 +- .../snapshots/rendering_test/dpi_2.00.png | 4 +- .../tests/snapshots/tessellation_test.png | 3 + .../tessellation_test/Blurred stroke.png | 3 + .../snapshots/tessellation_test/Blurred.png | 3 + .../tessellation_test/Minimal rounding.png | 3 + .../snapshots/tessellation_test/Normal.png | 3 + .../Thick stroke, minimal rounding.png | 3 + .../tessellation_test/Thin filled.png | 3 + .../tessellation_test/Thin stroked.png | 3 + .../tests/snapshots/widget_gallery.png | 4 +- crates/egui_kittest/src/snapshot.rs | 7 +- .../tests/snapshots/combobox_opened.png | 4 +- crates/epaint/src/mesh.rs | 7 + crates/epaint/src/rounding.rs | 46 +- crates/epaint/src/shapes/rect_shape.rs | 11 +- crates/epaint/src/shapes/shape.rs | 29 +- crates/epaint/src/stroke.rs | 8 + crates/epaint/src/tessellator.rs | 569 +++++++++++------- 63 files changed, 947 insertions(+), 345 deletions(-) create mode 100644 crates/egui_demo_lib/src/demo/tests/tessellation_test.rs create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index 52e322667..87f4915cf 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -72,6 +72,8 @@ impl Color32 { pub const BLUE: Self = Self::from_rgb(0, 0, 255); pub const LIGHT_BLUE: Self = Self::from_rgb(0xAD, 0xD8, 0xE6); + pub const PURPLE: Self = Self::from_rgb(0x80, 0, 0x80); + pub const GOLD: Self = Self::from_rgb(255, 215, 0); pub const DEBUG_COLOR: Self = Self::from_rgba_premultiplied(0, 200, 0, 128); @@ -233,6 +235,23 @@ impl Color32 { ]) } + /// Multiply with 127 to make color half as opaque, perceptually. + /// + /// Fast multiplication in gamma-space. + /// + /// This is perceptually even, and faster that [`Self::linear_multiply`]. + #[inline] + pub fn gamma_multiply_u8(self, factor: u8) -> Self { + let Self([r, g, b, a]) = self; + let factor = factor as u32; + Self([ + ((r as u32 * factor + 127) / 255) as u8, + ((g as u32 * factor + 127) / 255) as u8, + ((b as u32 * factor + 127) / 255) as u8, + ((a as u32 * factor + 127) / 255) as u8, + ]) + } + /// Multiply with 0.5 to make color half as opaque in linear space. /// /// This is using linear space, which is not perceptually even. @@ -271,6 +290,11 @@ impl Color32 { fast_round(lerp((self[3] as f32)..=(other[3] as f32), t)), ) } + + /// Blend two colors, so that `self` is behind the argument. + pub fn blend(self, on_top: Self) -> Self { + self.gamma_multiply_u8(255 - on_top.a()) + on_top + } } impl std::ops::Mul for Color32 { @@ -287,3 +311,17 @@ impl std::ops::Mul for Color32 { ]) } } + +impl std::ops::Add for Color32 { + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + Self([ + self[0].saturating_add(other[0]), + self[1].saturating_add(other[1]), + self[2].saturating_add(other[2]), + self[3].saturating_add(other[3]), + ]) + } +} diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 4386f8f86..07ab8e28a 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -115,7 +115,10 @@ pub struct Frame { #[doc(alias = "border")] pub stroke: Stroke, - /// The rounding of the corners of [`Self::stroke`] and [`Self::fill`]. + /// The rounding of the _outer_ corner of the [`Self::stroke`] + /// (or, if there is no stroke, the outer corner of [`Self::fill`]). + /// + /// In other words, this is the corner radius of the _widget rect_. pub rounding: Rounding, /// Margin outside the painted frame. @@ -269,7 +272,10 @@ impl Frame { self } - /// The rounding of the corners of [`Self::stroke`] and [`Self::fill`]. + /// The rounding of the _outer_ corner of the [`Self::stroke`] + /// (or, if there is no stroke, the outer corner of [`Self::fill`]). + /// + /// In other words, this is the corner radius of the _widget rect_. #[inline] pub fn rounding(mut self, rounding: impl Into) -> Self { self.rounding = rounding.into(); @@ -423,15 +429,14 @@ impl Frame { shadow, } = *self; - let fill_rect = self.fill_rect(content_rect); let widget_rect = self.widget_rect(content_rect); let frame_shape = Shape::Rect(epaint::RectShape::new( - fill_rect, + widget_rect, rounding, fill, stroke, - epaint::StrokeKind::Outside, + epaint::StrokeKind::Inside, )); if shadow == Default::default() { diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 215f6322d..75f4ac8e0 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -611,7 +611,8 @@ impl Window<'_> { title_bar.inner_rect.round_to_pixels(ctx.pixels_per_point()); if on_top && area_content_ui.visuals().window_highlight_topmost { - let mut round = window_frame.rounding; + let mut round = + window_frame.rounding - window_frame.stroke.width.round() as u8; if !is_collapsed { round.se = 0; diff --git a/crates/egui/src/introspection.rs b/crates/egui/src/introspection.rs index 85e1ee6c6..e21ddb3a5 100644 --- a/crates/egui/src/introspection.rs +++ b/crates/egui/src/introspection.rs @@ -160,7 +160,7 @@ impl Widget for &mut epaint::TessellationOptions { .on_hover_text("Apply feathering to smooth out the edges of shapes. Turn off for small performance gain."); if *feathering { - ui.add(crate::DragValue::new(feathering_size_in_pixels).range(0.0..=10.0).speed(0.1).suffix(" px")); + ui.add(crate::DragValue::new(feathering_size_in_pixels).range(0.0..=10.0).speed(0.025).suffix(" px")); } }); diff --git a/crates/egui_demo_app/tests/snapshots/imageviewer.png b/crates/egui_demo_app/tests/snapshots/imageviewer.png index 750fa3577..62624506d 100644 --- a/crates/egui_demo_app/tests/snapshots/imageviewer.png +++ b/crates/egui_demo_app/tests/snapshots/imageviewer.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2292f0f80bfd3c80055a72eb983549ac2875d36acb333732bd0a67e51b24ae4f -size 102983 +oid sha256:57274cec5ee7e5522073249b931ea65ead22752aea1de40666543e765c1b6b85 +size 102929 diff --git a/crates/egui_demo_lib/src/demo/demo_app_windows.rs b/crates/egui_demo_lib/src/demo/demo_app_windows.rs index c8b142cac..27f862ad0 100644 --- a/crates/egui_demo_lib/src/demo/demo_app_windows.rs +++ b/crates/egui_demo_lib/src/demo/demo_app_windows.rs @@ -100,6 +100,7 @@ impl Default for DemoGroups { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), ]), } diff --git a/crates/egui_demo_lib/src/demo/misc_demo_window.rs b/crates/egui_demo_lib/src/demo/misc_demo_window.rs index 5f1022ffc..ce154ab4a 100644 --- a/crates/egui_demo_lib/src/demo/misc_demo_window.rs +++ b/crates/egui_demo_lib/src/demo/misc_demo_window.rs @@ -124,9 +124,9 @@ impl View for MiscDemoWindow { ) .changed() { - self.checklist - .iter_mut() - .for_each(|checked| *checked = all_checked); + for check in &mut self.checklist { + *check = all_checked; + } } for (i, checked) in self.checklist.iter_mut().enumerate() { ui.checkbox(checked, format!("Item {}", i + 1)); diff --git a/crates/egui_demo_lib/src/demo/tests/mod.rs b/crates/egui_demo_lib/src/demo/tests/mod.rs index 13332fea7..d9fad5382 100644 --- a/crates/egui_demo_lib/src/demo/tests/mod.rs +++ b/crates/egui_demo_lib/src/demo/tests/mod.rs @@ -6,6 +6,7 @@ mod input_event_history; mod input_test; mod layout_test; mod manual_layout_test; +mod tessellation_test; mod window_resize_test; pub use clipboard_test::ClipboardTest; @@ -16,4 +17,5 @@ pub use input_event_history::InputEventHistory; pub use input_test::InputTest; pub use layout_test::LayoutTest; pub use manual_layout_test::ManualLayoutTest; +pub use tessellation_test::TessellationTest; pub use window_resize_test::WindowResizeTest; diff --git a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs new file mode 100644 index 000000000..033861016 --- /dev/null +++ b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs @@ -0,0 +1,364 @@ +use egui::{ + emath::{GuiRounding, TSTransform}, + epaint::{self, RectShape}, + vec2, Color32, Pos2, Rect, Sense, StrokeKind, Vec2, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct TessellationTest { + shape: RectShape, + + magnification_pixel_size: f32, + tessellation_options: epaint::TessellationOptions, + paint_edges: bool, +} + +impl Default for TessellationTest { + fn default() -> Self { + let shape = Self::interesting_shapes()[0].1.clone(); + Self { + shape, + magnification_pixel_size: 12.0, + tessellation_options: Default::default(), + paint_edges: false, + } + } +} + +impl TessellationTest { + fn interesting_shapes() -> Vec<(&'static str, RectShape)> { + fn sized(size: impl Into) -> Rect { + Rect::from_center_size(Pos2::ZERO, size.into()) + } + + let baby_blue = Color32::from_rgb(0, 181, 255); + + let mut shapes = vec![ + ( + "Normal", + RectShape::new( + sized([20.0, 16.0]), + 2.0, + baby_blue, + (1.0, Color32::WHITE), + StrokeKind::Inside, + ), + ), + ( + "Minimal rounding", + RectShape::new( + sized([20.0, 16.0]), + 1.0, + baby_blue, + (1.0, Color32::WHITE), + StrokeKind::Inside, + ), + ), + ( + "Thin filled", + RectShape::filled(sized([20.0, 0.5]), 2.0, baby_blue), + ), + ( + "Thin stroked", + RectShape::new( + sized([20.0, 0.5]), + 2.0, + baby_blue, + (0.5, Color32::WHITE), + StrokeKind::Inside, + ), + ), + ( + "Blurred", + RectShape::filled(sized([20.0, 16.0]), 2.0, baby_blue).with_blur_width(50.0), + ), + ( + "Thick stroke, minimal rounding", + RectShape::new( + sized([20.0, 16.0]), + 1.0, + baby_blue, + (3.0, Color32::WHITE), + StrokeKind::Inside, + ), + ), + ( + "Blurred stroke", + RectShape::new( + sized([20.0, 16.0]), + 0.0, + baby_blue, + (5.0, Color32::WHITE), + StrokeKind::Inside, + ) + .with_blur_width(5.0), + ), + ]; + + for (_name, shape) in &mut shapes { + shape.round_to_pixels = Some(true); + } + + shapes + } +} + +impl crate::Demo for TessellationTest { + fn name(&self) -> &'static str { + "Tessellation Test" + } + + fn show(&mut self, ctx: &egui::Context, open: &mut bool) { + egui::Window::new(self.name()) + .resizable(false) + .open(open) + .show(ctx, |ui| { + use crate::View as _; + self.ui(ui); + }); + } +} + +impl crate::View for TessellationTest { + fn ui(&mut self, ui: &mut egui::Ui) { + ui.add(crate::egui_github_link_file!()); + egui::reset_button(ui, self, "Reset"); + + ui.horizontal(|ui| { + ui.group(|ui| { + ui.vertical(|ui| { + rect_shape_ui(ui, &mut self.shape); + }); + }); + + ui.group(|ui| { + ui.vertical(|ui| { + ui.heading("Real size"); + egui::Frame::dark_canvas(ui.style()).show(ui, |ui| { + let (resp, painter) = + ui.allocate_painter(Vec2::splat(128.0), Sense::hover()); + let canvas = resp.rect; + + let pixels_per_point = ui.pixels_per_point(); + let pixel_size = 1.0 / pixels_per_point; + let mut shape = self.shape.clone(); + shape.rect = Rect::from_center_size(canvas.center(), shape.rect.size()) + .round_to_pixel_center(pixels_per_point) + .translate(Vec2::new(pixel_size / 3.0, pixel_size / 5.0)); // Intentionally offset to test the effect of rounding + painter.add(shape); + }); + }); + }); + }); + + ui.group(|ui| { + ui.heading("Zoomed in"); + let magnification_pixel_size = &mut self.magnification_pixel_size; + let tessellation_options = &mut self.tessellation_options; + + egui::Grid::new("TessellationOptions") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Magnification"); + ui.add( + egui::DragValue::new(magnification_pixel_size) + .speed(0.5) + .range(0.0..=64.0), + ); + ui.end_row(); + + ui.label("Feathering width"); + ui.horizontal(|ui| { + ui.checkbox(&mut tessellation_options.feathering, ""); + ui.add_enabled( + tessellation_options.feathering, + egui::DragValue::new( + &mut tessellation_options.feathering_size_in_pixels, + ) + .speed(0.1) + .range(0.0..=4.0) + .suffix(" px"), + ); + }); + ui.end_row(); + + ui.label("Paint edges"); + ui.checkbox(&mut self.paint_edges, ""); + ui.end_row(); + }); + + let magnification_pixel_size = *magnification_pixel_size; + + egui::Frame::dark_canvas(ui.style()).show(ui, |ui| { + let (resp, painter) = ui.allocate_painter( + magnification_pixel_size * (self.shape.rect.size() + Vec2::splat(8.0)), + Sense::hover(), + ); + let canvas = resp.rect; + + let mut shape = self.shape.clone(); + shape.rect = shape.rect.translate(Vec2::new(1.0 / 3.0, 1.0 / 5.0)); // Intentionally offset to test the effect of rounding + + let mut mesh = epaint::Mesh::default(); + let mut tessellator = epaint::Tessellator::new( + 1.0, + *tessellation_options, + ui.fonts(|f| f.font_image_size()), + vec![], + ); + tessellator.tessellate_rect(&shape, &mut mesh); + + // Scale and position the mesh: + mesh.transform( + TSTransform::from_translation(canvas.center().to_vec2()) + * TSTransform::from_scaling(magnification_pixel_size), + ); + let mesh = std::sync::Arc::new(mesh); + painter.add(epaint::Shape::mesh(mesh.clone())); + + if self.paint_edges { + let stroke = epaint::Stroke::new(0.5, Color32::MAGENTA); + for triangle in mesh.triangles() { + let a = mesh.vertices[triangle[0] as usize]; + let b = mesh.vertices[triangle[1] as usize]; + let c = mesh.vertices[triangle[2] as usize]; + + painter.line_segment([a.pos, b.pos], stroke); + painter.line_segment([b.pos, c.pos], stroke); + painter.line_segment([c.pos, a.pos], stroke); + } + } + + // Draw pixel centers: + let pixel_radius = 0.75; + let pixel_color = Color32::GRAY; + for yi in 0.. { + let y = (yi as f32 + 0.5) * magnification_pixel_size; + if y > canvas.height() / 2.0 { + break; + } + for xi in 0.. { + let x = (xi as f32 + 0.5) * magnification_pixel_size; + if x > canvas.width() / 2.0 { + break; + } + for offset in [vec2(x, y), vec2(x, -y), vec2(-x, y), vec2(-x, -y)] { + painter.circle_filled( + canvas.center() + offset, + pixel_radius, + pixel_color, + ); + } + } + } + }); + }); + } +} + +fn rect_shape_ui(ui: &mut egui::Ui, shape: &mut RectShape) { + egui::ComboBox::from_id_salt("prefabs") + .selected_text("Prefabs") + .show_ui(ui, |ui| { + for (name, prefab) in TessellationTest::interesting_shapes() { + ui.selectable_value(shape, prefab, name); + } + }); + + ui.add_space(4.0); + + let RectShape { + rect, + rounding, + fill, + stroke, + stroke_kind, + blur_width, + round_to_pixels, + brush: _, + } = shape; + + let round_to_pixels = round_to_pixels.get_or_insert(true); + + egui::Grid::new("RectShape") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Size"); + ui.horizontal(|ui| { + let mut size = rect.size(); + ui.add( + egui::DragValue::new(&mut size.x) + .speed(0.2) + .range(0.0..=64.0), + ); + ui.add( + egui::DragValue::new(&mut size.y) + .speed(0.2) + .range(0.0..=64.0), + ); + *rect = Rect::from_center_size(Pos2::ZERO, size); + }); + ui.end_row(); + + ui.label("Rounding"); + ui.add(rounding); + ui.end_row(); + + ui.label("Fill"); + ui.color_edit_button_srgba(fill); + ui.end_row(); + + ui.label("Stroke"); + ui.add(stroke); + ui.end_row(); + + ui.label("Stroke kind"); + ui.horizontal(|ui| { + ui.selectable_value(stroke_kind, StrokeKind::Inside, "Inside"); + ui.selectable_value(stroke_kind, StrokeKind::Middle, "Middle"); + ui.selectable_value(stroke_kind, StrokeKind::Outside, "Outside"); + }); + ui.end_row(); + + ui.label("Blur width"); + ui.add( + egui::DragValue::new(blur_width) + .speed(0.5) + .range(0.0..=20.0), + ); + ui.end_row(); + + ui.label("Round to pixels"); + ui.checkbox(round_to_pixels, ""); + ui.end_row(); + }); +} + +#[cfg(test)] +mod tests { + use crate::View as _; + + use super::*; + + #[test] + fn snapshot_tessellation_test() { + for (name, shape) in TessellationTest::interesting_shapes() { + let mut test = TessellationTest { + shape, + ..Default::default() + }; + let mut harness = egui_kittest::Harness::new_ui(|ui| { + test.ui(ui); + }); + + harness.fit_contents(); + harness.run(); + + harness.snapshot(&format!("tessellation_test/{name}")); + } + } +} diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png index 09dc7549a..16adbcdcf 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4cfd5191dc7046a782ef2350dc8e0547d2702182badcb15b6b928ce077b76c1 -size 32154 +oid sha256:23f19871720b67659a7b56cee8a78edc941c4bac86f55efc6fa549f10c4712fb +size 31754 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png index 6ab8e1327..10593d4e0 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4631f841b9e23833505af39eb1c45908013d3b1e1278d477bcedf6a460c71802 -size 27163 +oid sha256:e4d8fee9fd8e69ecd60ebd0dd41c29b61cc13e7013b1d20ad93d40fc4ed1cc03 +size 27091 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 3e05ffd4f..88cd2ffa3 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96e750ebfcc6ec2c130674407388c30cce844977cde19adfebf351fd08698a4f -size 81726 +oid sha256:b291e7efd895ab095590285b841903f05dc7d4dadbab7d9001b04a92953c1694 +size 81677 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png index 15123bc3b..2b46eaf44 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b03613e597631da3b922ef3d696c4ce74cec41f86a2779fc5b084a316fc9e8e8 -size 11764 +oid sha256:5d75230689807ae7fb692bac5c9b33ee04c02b9e54963e2d6ada05860157daf6 +size 11705 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png index feb573aa0..ba02ae257 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76d77e2df39af248d004a023b238bb61ed598ab2bea3e0c6f2885f9537ec1103 -size 25988 +oid sha256:f512159108c7681834ae2a52c5e1d4eb4dbe678a509f3dab3384e35d6c8dbee2 +size 25865 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png index 34315f422..7b47f16ff 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:003d905893b80ffc132a783155971ad3054229e9d6c046e2760c912559a66b3d -size 20869 +oid sha256:f2a4f5e67ff6615877794304e8be5a5db647d48e20e4248259179cddefbf4088 +size 20806 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png index 05d87fa8b..055f1651f 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be0f96c700b7662aab5098f8412dae3676116eeed65e70f6b295dd3375b329d0 -size 10968 +oid sha256:0bc474a37d6d3a7a08dd41963bf9009c05315de91bb515cc2b19443b79480bff +size 10723 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png index b5e5c4bec..647e3824f 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99c94a09c0f6984909481189f9a1415ea90bd7c45e42b4b3763ee36f3d831a65 -size 133231 +oid sha256:0bff769b3f9e46c5e38170885b2c8301294cbcc1c8ee22c17672205edd509924 +size 133170 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 689bb2153..3791d77a6 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4fef5fa8661f207bae2c58381e729cdaf77aecc8b3f178caf262dc310e3a490 -size 24206 +oid sha256:f90f94a842a1d0f1386c3cdd28e60e6ad6efe968f510a11bde418b5bc70a81d2 +size 23897 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png index b974f7489..3c1cf6de8 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b38828e423195628dcea09b0cbdd66aa4c077334ab7082efd358c7a3297009d -size 17827 +oid sha256:fdbdf1159f0ab4579bf9ceefbd8e40ac7af468368ab11ffec8578214b57d867c +size 17758 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png index 6853e8564..a3ef616d9 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c53403996451da14f7991ea61bd20b96dbc67eb67dd2041975dd6ce5015a6980 -size 22485 +oid sha256:ce647fd9626f126e7f19b4494bb98a2086071f9c45f7dd6ba4708632e7433a7b +size 22418 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png index baf613180..882914ff1 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63673b070951246b98ca07613fa81496dbfdd10627bac3c9c4356ebff1a36b20 -size 64319 +oid sha256:a843e6772809a1c904968fac19b49973675e726a3b7727ca1b898ad3b9072b0c +size 64257 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png index 4764035ca..bf3a487df 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:374b4095a3c7b827b80d6ab01b945458ae0128a2974c2af8aaf6b7e9fef6b459 -size 32554 +oid sha256:b437f27c46ddf82e4268d9bb86d33f43c382d1ab3ed45297bc136600cdc9960c +size 32493 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png index 7e6254b74..7ef97c87b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e756c90069803bb4d2821fff723877bffffd44b26613f5b06c8a042d6901ca4 -size 36578 +oid sha256:2cd0bda8b4d3d7f833273097c8bb52cfd8ea63a405d6798b9aeb7002b143ac74 +size 36459 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png index 2aee66f72..5e5d369a1 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6efc59cb9908533baa1a7346b359e9e21c5faf0e373dac6fa7db5476e644233d -size 17678 +oid sha256:a57c3bf373a283b79188080e2ddf7407c2974655fc5ad59222442e14faae055c +size 17508 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png index fa400718f..3e751d554 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb631bd2608aec6c83f8815b9db0b28627bf82692fd2b1cb793119083b4f8ad1 -size 264496 +oid sha256:161b5853f528206f2531587753369f2f6f3c52203af668eba0d81a41c2a915e0 +size 264432 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index 78f697490..d51bbd358 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fee66bab841602862c301a7df3add365ea93fde0ac9b71ca7f9b74a709a68d2 -size 35576 +oid sha256:118413a5ec56c6589914c5cb59bda2884a4d6acec3b34bbc367bc893c3de8127 +size 35409 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png index 023aaa104..8cf0ed424 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0870e9e1c9dc718690124a4d490f1061414f15fa40571e571d9319c3b65e74e -size 23709 +oid sha256:361791f30739f841c46433f5d16d281ba9d0c52027b4cf156c3ac293aa795f46 +size 23592 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png index 7efa04ccc..16b5868cc 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:532dbcbec94bb9c9fb8cc0665646790a459088e98077118b5fbb2112898e1a43 -size 183854 +oid sha256:81338d7b0412989590bc0808419d93788e972e7ab6f70f481f86bd23263a5395 +size 183821 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png index b03b3dcc7..38fe97fed 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fd584643b68084ec4b65369e08d229e2d389551bbefa59c84ad4b58621593f7 -size 117754 +oid sha256:41c51350c09360738ca284b05c944a11164e4a614beb95ce4cc962222af33d87 +size 117764 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png index 972368971..3d586971f 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e2b854d99c9b17d15ef92188cdac521d7c0635aef9ba457cd3801884a784009 -size 26159 +oid sha256:794475478cfe2fc954731742165b1f0419a0e64616e72b3766c1e031dbba7ca1 +size 26092 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png index 9c5e33304..2658a2535 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Table.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Table.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0b7dc029de8e669d76f3be5e0800e973c354edcf7cefa239faed07c2cd5e0d5 -size 70452 +oid sha256:7073aa30f3e7dfd116d9a4f02f6c5a075ce068d2e6f43eaede3c4dd6c56f925e +size 70439 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png index b73935d7e..48cad20b9 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64ba40810a6480e511e8d198b0dfb39e8b220eb2f5592877e27b17ee8d47b9c3 -size 66387 +oid sha256:4bb3cd05f253c0e109b83a0556b975af7a96d57678e57de3b9fa130cc8a8de1c +size 66318 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png index 07e9177b7..6a5551c45 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a8151f5bd01b2cb5183c532d11f57bbb7e8cc1e77a3c4b890537d157922c961 -size 21261 +oid sha256:5b550769d5ec5d834f89fba56375d10e5f9b3ba703599d90a5b6607aff1c2b06 +size 21194 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png index e53122482..ee474166b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96ce36fcc334793801ab724c711f592faf086a9c98c812684e6b76010e9d1974 -size 59714 +oid sha256:45f343b55be98976de32bd3c5438212a46b787123ea3096cf8fc6bec99493184 +size 59699 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png index 028346098..5e1cb1b35 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8155d93b78692ced67ddee4011526ef627838ede78351871df5feef8aa512857 -size 13141 +oid sha256:8d25f774320ca844fa4dfba5215ed66f067d0f30c6d7c8ad7ec29d97ee7732e0 +size 13073 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png index 70cf2378e..0cd7c200a 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf6da022ab97f9d4b862cc8e91bdfd7f9785a3ab0540aa1c2bd2646bd30a3450 -size 35115 +oid sha256:540a05c5b1de7e362bcb48a04611323fbc65c8787b8ae852ada8aa145803753d +size 34968 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_1.png b/crates/egui_demo_lib/tests/snapshots/modals_1.png index 2ce31fe45..1c0bdc0d2 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_1.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:035b35ed053cabd5022d9a97f7d4d5a79d194c9f14594e7602ecd9ef94c24ae5 -size 48053 +oid sha256:2276b8221389da4f644cad43ce446cd7d28f41820cea36fa3ea860808710053d +size 47878 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_2.png b/crates/egui_demo_lib/tests/snapshots/modals_2.png index 5ea3c5b3e..943b4a38a 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_2.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80a2968e211c83639b60e84b805f1327fb37b87144cada672a403c7e92ace8a8 -size 48066 +oid sha256:054d606681e08bf61762e5c7d7596c4f483e66ac889795e4be4956103328e0bb +size 47862 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_3.png b/crates/egui_demo_lib/tests/snapshots/modals_3.png index 5bb1fe23e..bc6071dbb 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_3.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86df5dc4b4ddd6f44226242b6d9b5e9f2aacd45193ae9f784fb5084a7a509e0b -size 43987 +oid sha256:f7fb29285e53b619f333757052d05f86d973680ac1ce6d1b25d28b7824bbce51 +size 43725 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png index 2c868b440..9a52f0498 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1138bbc3b7e73cccd555f0fd58c27a5bda4d84484fdc1bd5223fc9802d0c5328 -size 44089 +oid sha256:be50a396838d4fe29bcfd0807377ad0cda538fea8569acf709da0b13505bf09b +size 43871 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png index 3e3ec6991..b0f087acd 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3562bb87b155b2c406a72a651ffb1991305aa1e15273ce9f32cedc5766318537 -size 554922 +oid sha256:9c6ce16a8a8c34d882485da6bbe08039fb55f90636da8136f68b1bb9baf0effb +size 557610 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png index d5d26d2bb..efa1c8d5b 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba6c0bd127ab02375f2ae5fbc3eeef33a1bdf074cbb180de2733c700b81df3e5 -size 771069 +oid sha256:a49c052578a46adb41bc02c6d7fdc264ed0ab8ae636cc8a11ac729fe1e48091b +size 791802 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png index 98f73cee9..18b0233d3 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d85ab6d04059009fd2c3ad8001332b27e710c46c9300f2f1f409b882c49211dc -size 918967 +oid sha256:989c55a83b8bc7cce4f459d8b835962377927a98f2bce085e92cba8438438ecd +size 943736 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png index 6de95b07d..b3a865457 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a64f1bdec565357fe4dee3acb46b12eeb0492b522fb3bb9697d39dadce2e8c21 -size 1039455 +oid sha256:01ac61dc5bcecf6bf0d13c8399460b1afae652efe6fecc1d0e4b2f27d9f1c5a4 +size 1046906 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png index 1d373b92b..1b44fad93 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca008dca03372bb334564e55fa2d1d25a36751a43df6001a1c1cf3e4db9bcd4 -size 1130930 +oid sha256:3348923ecbad34e385e3ed52ab9b7c88b5d4fc07de00620302d5b191d90a453f +size 1140236 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png index b8b2a7658..f1f12eb1f 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f695127e7fe6cb3b752a0acd71db85d51d2de68e45051a7afe91f4d960acf27 -size 1311641 +oid sha256:179c17b0405c6e87bd3cafaa7272e3e6d6eefd462b4406cca2a7abfe8af6f2bd +size 1317569 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test.png new file mode 100644 index 000000000..7c65aef2c --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa7d25b097911f6b18308bab56d302e3dae9f8f9916f563d5703632a26eda260 +size 72501 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png new file mode 100644 index 000000000..3140fbc94 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c39868f184364555ae90fbfc035aa668f61189be7aeee6bec4e45a8de438ad8e +size 87661 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png new file mode 100644 index 000000000..51b0bd901 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd029fdc49e6d4078337472c39b9d58bf69073c1b7750c6dd1b7ccd450d52395 +size 119869 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png new file mode 100644 index 000000000..290216b1b --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e698ba12efd129099877248f9630ba983d683e1b495b2523ed3569989341e905 +size 51735 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png new file mode 100644 index 000000000..ff42489e8 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18b81b5cd88372b65b1ecc62e9a5e894960279310b05a1bd5c8df5bffa244ad0 +size 54922 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png new file mode 100644 index 000000000..8fa9370ff --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf5df173431d330e4b6045a72227c2bb7613ec98c63f013ea899a3a57cd6617a +size 55522 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png new file mode 100644 index 000000000..882691e82 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:987c162842a08271e833c41a55573d9f30cf045bf7ca3cb03e81d0cc13d5a16e +size 36763 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png new file mode 100644 index 000000000..6741df537 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c5c3055cd190823a4204aa6f23362a88bc5ab5ed5453d9be1b6077dded6cd54 +size 36809 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery.png index 84e731171..87f13e8e5 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a291f3a5724aefc59ba7881f48752ccc826ca5e480741c221d195061f562ccc9 -size 158220 +oid sha256:946bf96ae558ee7373b50bf11959e82b1f4d91866ec61b04b0336ae170b6f7b2 +size 158553 diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index 0b68677b0..dc49caec3 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -195,7 +195,12 @@ pub fn try_image_snapshot_options( output_path, } = options; - std::fs::create_dir_all(output_path).ok(); + let parent_path = if let Some(parent) = PathBuf::from(name).parent() { + output_path.join(parent) + } else { + output_path.clone() + }; + std::fs::create_dir_all(parent_path).ok(); // The one that is checked in to git let snapshot_path = output_path.join(format!("{name}.png")); diff --git a/crates/egui_kittest/tests/snapshots/combobox_opened.png b/crates/egui_kittest/tests/snapshots/combobox_opened.png index 9020c95c2..ef84c8a77 100644 --- a/crates/egui_kittest/tests/snapshots/combobox_opened.png +++ b/crates/egui_kittest/tests/snapshots/combobox_opened.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64fd46da67cab2afae0ea8997a88fb43fd207e24cc3943086d978a8de717320f -size 7542 +oid sha256:f65efbf60e190d83d187ec51f3f7811eb55135ef4feb9586e931e8498bc05d64 +size 7430 diff --git a/crates/epaint/src/mesh.rs b/crates/epaint/src/mesh.rs index 495759d04..930cb7716 100644 --- a/crates/epaint/src/mesh.rs +++ b/crates/epaint/src/mesh.rs @@ -98,6 +98,13 @@ impl Mesh { self.indices.is_empty() && self.vertices.is_empty() } + /// Iterate over the triangles of this mesh, returning vertex indices. + pub fn triangles(&self) -> impl Iterator + '_ { + self.indices + .chunks_exact(3) + .map(|chunk| [chunk[0], chunk[1], chunk[2]]) + } + /// Calculate a bounding rectangle. pub fn calc_bounds(&self) -> Rect { let mut bounds = Rect::NOTHING; diff --git a/crates/epaint/src/rounding.rs b/crates/epaint/src/rounding.rs index 12695f387..e0d79b14c 100644 --- a/crates/epaint/src/rounding.rs +++ b/crates/epaint/src/rounding.rs @@ -1,5 +1,9 @@ /// How rounded the corners of things should be. /// +/// This specific the _corner radius_ of the underlying geometric shape (e.g. rectangle). +/// If there is a stroke, then the stroke will have an inner and outer corner radius +/// which will depends on its width and [`crate::StrokeKind`]. +/// /// The rounding uses `u8` to save space, /// so the amount of rounding is limited to integers in the range `[0, 255]`. /// @@ -100,10 +104,23 @@ impl std::ops::Add for Rounding { #[inline] fn add(self, rhs: Self) -> Self { Self { - nw: self.nw + rhs.nw, - ne: self.ne + rhs.ne, - sw: self.sw + rhs.sw, - se: self.se + rhs.se, + nw: self.nw.saturating_add(rhs.nw), + ne: self.ne.saturating_add(rhs.ne), + sw: self.sw.saturating_add(rhs.sw), + se: self.se.saturating_add(rhs.se), + } + } +} + +impl std::ops::Add for Rounding { + type Output = Self; + #[inline] + fn add(self, rhs: u8) -> Self { + Self { + nw: self.nw.saturating_add(rhs), + ne: self.ne.saturating_add(rhs), + sw: self.sw.saturating_add(rhs), + se: self.se.saturating_add(rhs), } } } @@ -112,10 +129,10 @@ impl std::ops::AddAssign for Rounding { #[inline] fn add_assign(&mut self, rhs: Self) { *self = Self { - nw: self.nw + rhs.nw, - ne: self.ne + rhs.ne, - sw: self.sw + rhs.sw, - se: self.se + rhs.se, + nw: self.nw.saturating_add(rhs.nw), + ne: self.ne.saturating_add(rhs.ne), + sw: self.sw.saturating_add(rhs.sw), + se: self.se.saturating_add(rhs.se), }; } } @@ -145,6 +162,19 @@ impl std::ops::Sub for Rounding { } } +impl std::ops::Sub for Rounding { + type Output = Self; + #[inline] + fn sub(self, rhs: u8) -> Self { + Self { + nw: self.nw.saturating_sub(rhs), + ne: self.ne.saturating_sub(rhs), + sw: self.sw.saturating_sub(rhs), + se: self.se.saturating_sub(rhs), + } + } +} + impl std::ops::SubAssign for Rounding { #[inline] fn sub_assign(&mut self, rhs: Self) { diff --git a/crates/epaint/src/shapes/rect_shape.rs b/crates/epaint/src/shapes/rect_shape.rs index f691234b1..cd54dc8e9 100644 --- a/crates/epaint/src/shapes/rect_shape.rs +++ b/crates/epaint/src/shapes/rect_shape.rs @@ -8,7 +8,16 @@ use crate::*; pub struct RectShape { pub rect: Rect, - /// How rounded the corners are. Use `Rounding::ZERO` for no rounding. + /// How rounded the corners of the rectangle are. + /// + /// Use `Rounding::ZERO` for for sharp corners. + /// + /// This is the corner radii of the rectangle. + /// If there is a stroke, then the stroke will have an inner and outer corner radius, + /// and those will depend on [`StrokeKind`] and the stroke width. + /// + /// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle, + /// so the rounding will in this case specify the outer corner radius. pub rounding: Rounding, /// How to fill the rectangle. diff --git a/crates/epaint/src/shapes/shape.rs b/crates/epaint/src/shapes/shape.rs index 2e43fc585..ddbaacfd3 100644 --- a/crates/epaint/src/shapes/shape.rs +++ b/crates/epaint/src/shapes/shape.rs @@ -451,8 +451,9 @@ impl Shape { } Self::Rect(rect_shape) => { rect_shape.rect = transform * rect_shape.rect; - rect_shape.stroke.width *= transform.scaling; rect_shape.rounding *= transform.scaling; + rect_shape.stroke.width *= transform.scaling; + rect_shape.blur_width *= transform.scaling; } Self::Text(text_shape) => { text_shape.pos = transform * text_shape.pos; @@ -472,17 +473,17 @@ impl Shape { Self::Mesh(mesh) => { Arc::make_mut(mesh).transform(transform); } - Self::QuadraticBezier(bezier_shape) => { - bezier_shape.points[0] = transform * bezier_shape.points[0]; - bezier_shape.points[1] = transform * bezier_shape.points[1]; - bezier_shape.points[2] = transform * bezier_shape.points[2]; - bezier_shape.stroke.width *= transform.scaling; - } - Self::CubicBezier(cubic_curve) => { - for p in &mut cubic_curve.points { + Self::QuadraticBezier(bezier) => { + for p in &mut bezier.points { *p = transform * *p; } - cubic_curve.stroke.width *= transform.scaling; + bezier.stroke.width *= transform.scaling; + } + Self::CubicBezier(bezier) => { + for p in &mut bezier.points { + *p = transform * *p; + } + bezier.stroke.width *= transform.scaling; } Self::Callback(shape) => { shape.rect = transform * shape.rect; @@ -502,7 +503,7 @@ fn points_from_line( shapes: &mut Vec, ) { let mut position_on_segment = 0.0; - path.windows(2).for_each(|window| { + for window in path.windows(2) { let (start, end) = (window[0], window[1]); let vector = end - start; let segment_length = vector.length(); @@ -512,7 +513,7 @@ fn points_from_line( position_on_segment += spacing; } position_on_segment -= segment_length; - }); + } } /// Creates dashes from a line. @@ -529,7 +530,7 @@ fn dashes_from_line( let mut drawing_dash = false; let mut step = 0; let steps = dash_lengths.len(); - path.windows(2).for_each(|window| { + for window in path.windows(2) { let (start, end) = (window[0], window[1]); let vector = end - start; let segment_length = vector.length(); @@ -560,5 +561,5 @@ fn dashes_from_line( } position_on_segment -= segment_length; - }); + } } diff --git a/crates/epaint/src/stroke.rs b/crates/epaint/src/stroke.rs index ce7d274dd..5d82c1963 100644 --- a/crates/epaint/src/stroke.rs +++ b/crates/epaint/src/stroke.rs @@ -119,7 +119,13 @@ impl PathStroke { } } + #[inline] + pub fn with_kind(self, kind: StrokeKind) -> Self { + Self { kind, ..self } + } + /// Set the stroke to be painted right on the edge of the shape, half inside and half outside. + #[inline] pub fn middle(self) -> Self { Self { kind: StrokeKind::Middle, @@ -128,6 +134,7 @@ impl PathStroke { } /// Set the stroke to be painted entirely outside of the shape + #[inline] pub fn outside(self) -> Self { Self { kind: StrokeKind::Outside, @@ -136,6 +143,7 @@ impl PathStroke { } /// Set the stroke to be painted entirely inside of the shape + #[inline] pub fn inside(self) -> Self { Self { kind: StrokeKind::Inside, diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 45919759a..b16d22f52 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -10,7 +10,7 @@ use emath::{pos2, remap, vec2, GuiRounding as _, NumExt, Pos2, Rect, Rot2, Vec2} use crate::{ color::ColorMode, emath, stroke::PathStroke, texture_atlas::PreparedDisc, CircleShape, ClippedPrimitive, ClippedShape, Color32, CubicBezierShape, EllipseShape, Mesh, PathShape, - Primitive, QuadraticBezierShape, RectShape, Rounding, Shape, Stroke, StrokeKind, TextShape, + Primitive, QuadraticBezierShape, RectShape, Roundingf, Shape, Stroke, StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, }; @@ -475,6 +475,20 @@ impl Path { } } + /// The path is taken to be closed (i.e. returning to the start again). + /// + /// Calling this may reverse the vertices in the path if they are wrong winding order. + /// The preferred winding order is clockwise. + pub fn fill_and_stroke( + &mut self, + feathering: f32, + fill: Color32, + stroke: &PathStroke, + out: &mut Mesh, + ) { + stroke_and_fill_path(feathering, &mut self.0, PathType::Closed, stroke, fill, out); + } + /// Open-ended. pub fn stroke_open(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) { stroke_path(feathering, &mut self.0, PathType::Open, stroke, out); @@ -498,12 +512,9 @@ impl Path { /// The path is taken to be closed (i.e. returning to the start again). /// /// Calling this may reverse the vertices in the path if they are wrong winding order. - /// /// The preferred winding order is clockwise. - /// - /// The stroke colors is used for color-correct feathering. - pub fn fill(&mut self, feathering: f32, color: Color32, stroke: &PathStroke, out: &mut Mesh) { - fill_closed_path(feathering, &mut self.0, color, stroke, out); + pub fn fill(&mut self, feathering: f32, color: Color32, out: &mut Mesh) { + fill_closed_path(feathering, &mut self.0, color, out); } /// Like [`Self::fill`] but with texturing. @@ -523,11 +534,11 @@ impl Path { pub mod path { //! Helpers for constructing paths - use crate::Rounding; + use crate::Roundingf; use emath::{pos2, Pos2, Rect}; /// overwrites existing points - pub fn rounded_rectangle(path: &mut Vec, rect: Rect, rounding: Rounding) { + pub fn rounded_rectangle(path: &mut Vec, rect: Rect, rounding: Roundingf) { path.clear(); let min = rect.min; @@ -535,7 +546,7 @@ pub mod path { let r = clamp_rounding(rounding, rect); - if r == Rounding::ZERO { + if r == Roundingf::ZERO { path.reserve(4); path.push(pos2(min.x, min.y)); // left top path.push(pos2(max.x, min.y)); // right top @@ -546,8 +557,6 @@ pub mod path { // Duplicated vertices can happen when one side is all rounding, with no straight edge between. let eps = f32::EPSILON * rect.size().max_elem(); - let r = crate::Roundingf::from(r); - add_circle_quadrant(path, pos2(max.x - r.se, max.y - r.se), r.se, 0.0); // south east if rect.width() <= r.se + r.sw + eps { @@ -624,11 +633,11 @@ pub mod path { } // Ensures the radius of each corner is within a valid range - fn clamp_rounding(rounding: Rounding, rect: Rect) -> Rounding { + fn clamp_rounding(rounding: Roundingf, rect: Rect) -> Roundingf { let half_width = rect.width() * 0.5; let half_height = rect.height() * 0.5; let max_cr = half_width.min(half_height); - rounding.at_most(max_cr.floor() as _).at_least(0) + rounding.at_most(max_cr).at_least(0.0) } } @@ -753,36 +762,17 @@ fn cw_signed_area(path: &[PathPoint]) -> f64 { /// Calling this may reverse the vertices in the path if they are wrong winding order. /// /// The preferred winding order is clockwise. -/// -/// A stroke is required so that the fill's feathering can fade to the right color. You can pass `&PathStroke::NONE` if -/// this path won't be stroked. -fn fill_closed_path( - feathering: f32, - path: &mut [PathPoint], - color: Color32, - stroke: &PathStroke, - out: &mut Mesh, -) { - if color == Color32::TRANSPARENT { +fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32, out: &mut Mesh) { + if fill_color == Color32::TRANSPARENT { return; } - // TODO(juancampa): This bounding box is computed twice per shape: once here and another when tessellating the - // stroke, consider hoisting that logic to the tessellator/scratchpad. - let bbox = if matches!(stroke.color, ColorMode::UV(_)) { - Rect::from_points(&path.iter().map(|p| p.pos).collect::>()).expand(feathering) - } else { - Rect::NAN - }; - - let stroke_color = &stroke.color; - let get_stroke_color: Box Color32> = match stroke_color { - ColorMode::Solid(col) => Box::new(|_pos: Pos2| *col), - ColorMode::UV(fun) => Box::new(|pos: Pos2| fun(bbox, pos)), - }; - let n = path.len() as u32; - if feathering > 0.0 { + if n < 3 { + return; + } + + if 0.0 < feathering { if cw_signed_area(path) < 0.0 { // Wrong winding order - fix: path.reverse(); @@ -809,10 +799,9 @@ fn fill_closed_path( let pos_inner = p1.pos - dm; let pos_outer = p1.pos + dm; - let color_outer = get_stroke_color(pos_outer); - out.colored_vertex(pos_inner, color); - out.colored_vertex(pos_outer, color_outer); + out.colored_vertex(pos_inner, fill_color); + out.colored_vertex(pos_outer, Color32::TRANSPARENT); out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0); out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1); i0 = i1; @@ -823,7 +812,7 @@ fn fill_closed_path( out.vertices.extend(path.iter().map(|p| Vertex { pos: p.pos, uv: WHITE_UV, - color, + color: fill_color, })); for i in 2..n { out.add_triangle(idx, idx + i - 1, idx + i); @@ -856,7 +845,7 @@ fn fill_closed_path_with_uv( } let n = path.len() as u32; - if feathering > 0.0 { + if 0.0 < feathering { if cw_signed_area(path) < 0.0 { // Wrong winding order - fix: path.reverse(); @@ -914,20 +903,6 @@ fn fill_closed_path_with_uv( } } -/// Translate a point along their normals according to the stroke kind. -#[inline(always)] -fn translate_stroke_point(p: &mut PathPoint, stroke: &PathStroke) { - match stroke.kind { - StrokeKind::Inside => { - p.pos -= p.normal * stroke.width * 0.5; - } - StrokeKind::Middle => { /* Nothing to do */ } - StrokeKind::Outside => { - p.pos += p.normal * stroke.width * 0.5; - } - } -} - /// Tessellate the given path as a stroke with thickness. fn stroke_path( feathering: f32, @@ -935,55 +910,122 @@ fn stroke_path( path_type: PathType, stroke: &PathStroke, out: &mut Mesh, +) { + let fill = Color32::TRANSPARENT; + stroke_and_fill_path(feathering, path, path_type, stroke, fill, out); +} + +/// Tessellate the given path as a stroke with thickness, with optional fill color. +/// +/// Calling this may reverse the vertices in the path if they are wrong winding order. +/// +/// The preferred winding order is clockwise. +fn stroke_and_fill_path( + feathering: f32, + path: &mut [PathPoint], + path_type: PathType, + stroke: &PathStroke, + color_fill: Color32, + out: &mut Mesh, ) { let n = path.len() as u32; - if stroke.is_empty() || n < 2 { + if n < 2 { return; } + if stroke.width == 0.0 { + // Skip the stroke, just fill. + return fill_closed_path(feathering, path, color_fill, out); + } + + if color_fill != Color32::TRANSPARENT && cw_signed_area(path) < 0.0 { + // Wrong winding order - fix: + path.reverse(); + for point in &mut *path { + point.normal = -point.normal; + } + } + + if stroke.color == ColorMode::TRANSPARENT { + // Skip the stroke, just fill. But subtract the width from the path: + match stroke.kind { + StrokeKind::Inside => { + for point in &mut *path { + point.pos -= stroke.width * point.normal; + } + } + StrokeKind::Middle => { + for point in &mut *path { + point.pos -= 0.5 * stroke.width * point.normal; + } + } + StrokeKind::Outside => {} + } + + // Skip the stroke, just fill. + return fill_closed_path(feathering, path, color_fill, out); + } + let idx = out.vertices.len() as u32; - // Translate the points along their normals if the stroke is outside or inside - if stroke.kind != StrokeKind::Middle { - path.iter_mut() - .for_each(|p| translate_stroke_point(p, stroke)); + // Move the points so that the stroke is on middle of the path. + match stroke.kind { + StrokeKind::Inside => { + for point in &mut *path { + point.pos -= 0.5 * stroke.width * point.normal; + } + } + StrokeKind::Middle => { + // correct + } + StrokeKind::Outside => { + for point in &mut *path { + point.pos += 0.5 * stroke.width * point.normal; + } + } } // Expand the bounding box to include the thickness of the path - let bbox = if matches!(stroke.color, ColorMode::UV(_)) { + let uv_bbox = if matches!(stroke.color, ColorMode::UV(_)) { Rect::from_points(&path.iter().map(|p| p.pos).collect::>()) .expand((stroke.width / 2.0) + feathering) } else { Rect::NAN }; - let get_color = |col: &ColorMode, pos: Pos2| match col { ColorMode::Solid(col) => *col, - ColorMode::UV(fun) => fun(bbox, pos), + ColorMode::UV(fun) => fun(uv_bbox, pos), }; - if feathering > 0.0 { - let color_inner = &stroke.color; + if 0.0 < feathering { let color_outer = Color32::TRANSPARENT; + let color_middle = &stroke.color; let thin_line = stroke.width <= feathering; if thin_line { - /* - We paint the line using three edges: outer, inner, outer. - - . o i o outer, inner, outer - . |---| feathering (pixel width) - */ - - // Fade out as it gets thinner: - if let ColorMode::Solid(col) = color_inner { - let color_inner = mul_color(*col, stroke.width / feathering); - if color_inner == Color32::TRANSPARENT { - return; + // If the stroke is painted smaller than the pixel width (=feathering width), + // then we risk severe aliasing. + // Instead, we paint the stroke as a triangular ridge, two feather-widths wide, + // and lessen the opacity of the middle part instead of making it thinner. + if color_fill != Color32::TRANSPARENT && stroke.width < feathering { + // If this is filled shape, then we need to also compensate so that the + // filled area remains the same as it would have been without the + // artificially wide line. + for point in &mut *path { + point.pos += 0.5 * (feathering - stroke.width) * point.normal; } } + let opacity = stroke.width / feathering; + + /* + We paint the line using three edges: outer, middle, fill. + + . o m i outer, middle, fill + . |---| feathering (pixel width) + */ + out.reserve_triangles(4 * n as usize); out.reserve_vertices(3 * n as usize); @@ -994,11 +1036,8 @@ fn stroke_path( let p = p1.pos; let n = p1.normal; out.colored_vertex(p + n * feathering, color_outer); - out.colored_vertex( - p, - mul_color(get_color(color_inner, p), stroke.width / feathering), - ); - out.colored_vertex(p - n * feathering, color_outer); + out.colored_vertex(p, mul_color(get_color(color_middle, p), opacity)); + out.colored_vertex(p - n * feathering, color_fill); if connect_with_previous { out.add_triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0); @@ -1007,15 +1046,24 @@ fn stroke_path( out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1); out.add_triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2); } + i0 = i1; } + + if color_fill != Color32::TRANSPARENT { + out.reserve_triangles(n as usize - 2); + let idx_fill = idx + 2; + for i in 2..n { + out.add_triangle(idx_fill + 3 * (i - 1), idx_fill, idx_fill + 3 * i); + } + } } else { // thick anti-aliased line /* - We paint the line using four edges: outer, inner, inner, outer + We paint the line using four edges: outer, middle, middle, fill - . o i p i o outer, inner, point, inner, outer + . o m p m f outer, middle, point, middle, fill . |---| feathering (pixel width) . |--------------| width . |---------| outer_rad @@ -1038,13 +1086,13 @@ fn stroke_path( out.colored_vertex(p + n * outer_rad, color_outer); out.colored_vertex( p + n * inner_rad, - get_color(color_inner, p + n * inner_rad), + get_color(color_middle, p + n * inner_rad), ); out.colored_vertex( p - n * inner_rad, - get_color(color_inner, p - n * inner_rad), + get_color(color_middle, p - n * inner_rad), ); - out.colored_vertex(p - n * outer_rad, color_outer); + out.colored_vertex(p - n * outer_rad, color_fill); out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0); out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1); @@ -1057,6 +1105,14 @@ fn stroke_path( i0 = i1; } + + if color_fill != Color32::TRANSPARENT { + out.reserve_triangles(n as usize - 2); + let idx_fill = idx + 3; + for i in 2..n { + out.add_triangle(idx_fill + 4 * (i - 1), idx_fill, idx_fill + 4 * i); + } + } } PathType::Open => { // Anti-alias the ends by extruding the outer edge and adding @@ -1084,11 +1140,11 @@ fn stroke_path( out.colored_vertex(p + n * outer_rad + back_extrude, color_outer); out.colored_vertex( p + n * inner_rad, - get_color(color_inner, p + n * inner_rad), + get_color(color_middle, p + n * inner_rad), ); out.colored_vertex( p - n * inner_rad, - get_color(color_inner, p - n * inner_rad), + get_color(color_middle, p - n * inner_rad), ); out.colored_vertex(p - n * outer_rad + back_extrude, color_outer); @@ -1104,11 +1160,11 @@ fn stroke_path( out.colored_vertex(p + n * outer_rad, color_outer); out.colored_vertex( p + n * inner_rad, - get_color(color_inner, p + n * inner_rad), + get_color(color_middle, p + n * inner_rad), ); out.colored_vertex( p - n * inner_rad, - get_color(color_inner, p - n * inner_rad), + get_color(color_middle, p - n * inner_rad), ); out.colored_vertex(p - n * outer_rad, color_outer); @@ -1133,11 +1189,11 @@ fn stroke_path( out.colored_vertex(p + n * outer_rad + back_extrude, color_outer); out.colored_vertex( p + n * inner_rad, - get_color(color_inner, p + n * inner_rad), + get_color(color_middle, p + n * inner_rad), ); out.colored_vertex( p - n * inner_rad, - get_color(color_inner, p - n * inner_rad), + get_color(color_middle, p - n * inner_rad), ); out.colored_vertex(p - n * outer_rad + back_extrude, color_outer); @@ -1183,32 +1239,21 @@ fn stroke_path( let thin_line = stroke.width <= feathering; if thin_line { // Fade out thin lines rather than making them thinner + let opacity = stroke.width / feathering; let radius = feathering / 2.0; - if let ColorMode::Solid(color) = stroke.color { - let color = mul_color(color, stroke.width / feathering); - if color == Color32::TRANSPARENT { - return; - } - } - for p in path { + for p in path.iter_mut() { out.colored_vertex( p.pos + radius * p.normal, - mul_color( - get_color(&stroke.color, p.pos + radius * p.normal), - stroke.width / feathering, - ), + mul_color(get_color(&stroke.color, p.pos + radius * p.normal), opacity), ); out.colored_vertex( p.pos - radius * p.normal, - mul_color( - get_color(&stroke.color, p.pos - radius * p.normal), - stroke.width / feathering, - ), + mul_color(get_color(&stroke.color, p.pos - radius * p.normal), opacity), ); } } else { let radius = stroke.width / 2.0; - for p in path { + for p in path.iter_mut() { out.colored_vertex( p.pos + radius * p.normal, get_color(&stroke.color, p.pos + radius * p.normal), @@ -1219,6 +1264,18 @@ fn stroke_path( ); } } + + if color_fill != Color32::TRANSPARENT { + // We Need to create new vertices, because the ones we used for the stroke + // has the wrong color. + + // Shrink to ignore the stroke… + for point in &mut *path { + point.pos -= 0.5 * stroke.width * point.normal; + } + // …then fill: + fill_closed_path(feathering, path, color_fill, out); + } } } @@ -1467,9 +1524,7 @@ impl Tessellator { self.scratchpad_path.clear(); self.scratchpad_path.add_circle(center, radius); self.scratchpad_path - .fill(self.feathering, fill, &path_stroke, out); - self.scratchpad_path - .stroke_closed(self.feathering, &path_stroke, out); + .fill_and_stroke(self.feathering, fill, &path_stroke, out); } /// Tessellate a single [`EllipseShape`] into a [`Mesh`]. @@ -1536,9 +1591,7 @@ impl Tessellator { self.scratchpad_path.clear(); self.scratchpad_path.add_line_loop(&points); self.scratchpad_path - .fill(self.feathering, fill, &path_stroke, out); - self.scratchpad_path - .stroke_closed(self.feathering, &path_stroke, out); + .fill_and_stroke(self.feathering, fill, &path_stroke, out); } /// Tessellate a single [`Mesh`] into a [`Mesh`]. @@ -1642,27 +1695,24 @@ impl Tessellator { } = path_shape; self.scratchpad_path.clear(); + if *closed { self.scratchpad_path.add_line_loop(points); - } else { - self.scratchpad_path.add_open_points(points); - } - if *fill != Color32::TRANSPARENT { - debug_assert!( - closed, + self.scratchpad_path + .fill_and_stroke(self.feathering, *fill, stroke, out); + } else { + debug_assert_eq!( + *fill, + Color32::TRANSPARENT, "You asked to fill a path that is not closed. That makes no sense." ); + + self.scratchpad_path.add_open_points(points); + self.scratchpad_path - .fill(self.feathering, *fill, stroke, out); + .stroke(self.feathering, PathType::Open, stroke, out); } - let typ = if *closed { - PathType::Closed - } else { - PathType::Open - }; - self.scratchpad_path - .stroke(self.feathering, typ, stroke, out); } /// Tessellate a single [`Rect`] into a [`Mesh`]. @@ -1679,18 +1729,69 @@ impl Tessellator { let brush = rect_shape.brush.as_ref(); let RectShape { mut rect, - mut rounding, - fill, + rounding, + mut fill, mut stroke, - stroke_kind, + mut stroke_kind, round_to_pixels, mut blur_width, brush: _, // brush is extracted on its own, because it is not Copy } = *rect_shape; + let mut rounding = Roundingf::from(rounding); let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels); + let pixel_size = 1.0 / self.pixels_per_point; - // Important: round to pixels BEFORE applying stroke_kind + if stroke.width == 0.0 { + stroke.color = Color32::TRANSPARENT; + } + + // It is common to (sometimes accidentally) create an infinitely sized rectangle. + // Make sure we can handle that: + rect.min = rect.min.at_least(pos2(-1e7, -1e7)); + rect.max = rect.max.at_most(pos2(1e7, 1e7)); + + if !stroke.is_empty() { + // Check if the stroke covers the whole rectangle + let rect_with_stroke = match stroke_kind { + StrokeKind::Inside => rect, + StrokeKind::Middle => rect.expand(stroke.width / 2.0), + StrokeKind::Outside => rect.expand(stroke.width), + }; + + if rect_with_stroke.size().min_elem() <= 2.0 * stroke.width + 0.5 * self.feathering { + // The stroke covers the fill. + // Change this to be a fill-only shape, using the stroke color as the new fill color. + rect = rect_with_stroke; + + // We blend so that if the stroke is semi-transparent, + // the fill still shines through. + fill = stroke.color; + + stroke = Stroke::NONE; + } + } + + if stroke.is_empty() { + // Approximate thin rectangles with line segments. + // This is important so that thin rectangles look good. + if rect.width() <= 2.0 * self.feathering { + return self.tessellate_line_segment( + [rect.center_top(), rect.center_bottom()], + (rect.width(), fill), + out, + ); + } + if rect.height() <= 2.0 * self.feathering { + return self.tessellate_line_segment( + [rect.left_center(), rect.right_center()], + (rect.height(), fill), + out, + ); + } + } + + // Important: round to pixels BEFORE modifying/applying stroke_kind if round_to_pixels { // The rounding is aware of the stroke kind. // It is designed to be clever in trying to divine the intentions of the user. @@ -1712,7 +1813,9 @@ impl Tessellator { // On this path we optimize for crisp and symmetric strokes. // We put odd-width strokes in the center of pixels. // To understand why, see `fn round_line_segment`. - if stroke.width <= self.feathering + if stroke.width <= 0.0 { + rect = rect.round_to_pixels(self.pixels_per_point); + } else if stroke.width <= pixel_size || is_nearest_integer_odd(self.pixels_per_point * stroke.width) { rect = rect.round_to_pixel_center(self.pixels_per_point); @@ -1733,28 +1836,6 @@ impl Tessellator { } } - // Modify `rect` so that it represents the filled region, with the stroke on the outside. - // Important: do this AFTER rounding to pixels - match stroke_kind { - StrokeKind::Inside => { - // Shrink the stroke so it fits inside the rect: - stroke.width = stroke.width.at_most(rect.size().min_elem() / 2.0); - - rect = rect.shrink(stroke.width); - } - StrokeKind::Middle => { - rect = rect.shrink(stroke.width / 2.0); - } - StrokeKind::Outside => { - // Already good - } - } - - // It is common to (sometimes accidentally) create an infinitely sized rectangle. - // Make sure we can handle that: - rect.min = rect.min.at_least(pos2(-1e7, -1e7)); - rect.max = rect.max.at_most(pos2(1e7, 1e7)); - let old_feathering = self.feathering; if self.feathering < blur_width { @@ -1762,73 +1843,102 @@ impl Tessellator { // Feathering is usually used to make the edges of a shape softer for anti-aliasing. // The tessellator can't handle blurring/feathering larger than the smallest side of the rect. - // Thats because the tessellator approximate very thin rectangles as line segments, - // and these line segments don't have rounded corners. - // When the feathering is small (the size of a pixel), this is usually fine, - // but here we have a huge feathering to simulate blur, - // so we need to avoid this optimization in the tessellator, - // which is also why we add this rather big epsilon: - let eps = 0.1; + let eps = 0.1; // avoid numerical problems blur_width = blur_width - .at_most(rect.size().min_elem() - eps) + .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width) .at_least(0.0); - rounding += Rounding::from(0.5 * blur_width); + rounding += 0.5 * blur_width; self.feathering = self.feathering.max(blur_width); } - if rect.width() < 0.5 * self.feathering { - // Very thin - approximate by a vertical line-segment: - // There is room for improvement here, but it is not critical. - let line = [rect.center_top(), rect.center_bottom()]; - if 0.0 < rect.width() && fill != Color32::TRANSPARENT { - self.tessellate_line_segment(line, Stroke::new(rect.width(), fill), out); - } - if !stroke.is_empty() { - self.tessellate_line_segment(line, stroke, out); // back… - self.tessellate_line_segment(line, stroke, out); // …and forth - } - } else if rect.height() < 0.5 * self.feathering { - // Very thin - approximate by a horizontal line-segment: - // There is room for improvement here, but it is not critical. - let line = [rect.left_center(), rect.right_center()]; - if 0.0 < rect.height() && fill != Color32::TRANSPARENT { - self.tessellate_line_segment(line, Stroke::new(rect.height(), fill), out); - } - if !stroke.is_empty() { - self.tessellate_line_segment(line, stroke, out); // back… - self.tessellate_line_segment(line, stroke, out); // …and forth - } - } else { - let path = &mut self.scratchpad_path; - path.clear(); - path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding); - path.add_line_loop(&self.scratchpad_points); - let path_stroke = PathStroke::from(stroke).outside(); + { + // Modify `rect` so that it represents the OUTER border + // We do this because `path::rounded_rectangle` uses the + // corner radius to pick the fidelity/resolution of the corner. - if rect.is_positive() { - // Fill - if let Some(brush) = brush { - // Textured - let crate::Brush { - fill_texture_id, - uv, - } = **brush; - let uv_from_pos = |p: Pos2| { - pos2( - remap(p.x, rect.x_range(), uv.x_range()), - remap(p.y, rect.y_range(), uv.y_range()), - ) - }; - path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out); - } else { - // Untextured - path.fill(self.feathering, fill, &path_stroke, out); + let original_rounding = rounding; + + match stroke_kind { + StrokeKind::Inside => {} + StrokeKind::Middle => { + rect = rect.expand(stroke.width / 2.0); + rounding += stroke.width / 2.0; + } + StrokeKind::Outside => { + rect = rect.expand(stroke.width); + rounding += stroke.width; } } - path.stroke_closed(self.feathering, &path_stroke, out); + stroke_kind = StrokeKind::Inside; + + // A small rounding is incompatible with a wide stroke, + // because the small bend will be extruded inwards and cross itself. + // There are two ways to solve this (wile maintaining constant stroke width): + // either we increase the rounding, or we set it to zero. + // We choose the former: if the user asks for _any_ rounding, they should get it. + + let min_inside_rounding = 0.1; // Large enough to avoid numerical issues + let min_outside_rounding = stroke.width + min_inside_rounding; + + let extra_rounding_tweak = 0.4; // Otherwise is doesn't _feels_ enough. + + if 0.0 < original_rounding.nw { + rounding.nw += extra_rounding_tweak; + rounding.nw = rounding.nw.at_least(min_outside_rounding); + } + if 0.0 < original_rounding.ne { + rounding.ne += extra_rounding_tweak; + rounding.ne = rounding.ne.at_least(min_outside_rounding); + } + if 0.0 < original_rounding.sw { + rounding.sw += extra_rounding_tweak; + rounding.sw = rounding.sw.at_least(min_outside_rounding); + } + if 0.0 < original_rounding.se { + rounding.se += extra_rounding_tweak; + rounding.se = rounding.se.at_least(min_outside_rounding); + } + } + + let path = &mut self.scratchpad_path; + path.clear(); + path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding); + path.add_line_loop(&self.scratchpad_points); + + let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind); + + if let Some(brush) = brush { + // Textured fill + + let fill_rect = match stroke_kind { + StrokeKind::Inside => rect.shrink(stroke.width), + StrokeKind::Middle => rect.shrink(stroke.width / 2.0), + StrokeKind::Outside => rect, + }; + + if fill_rect.is_positive() { + let crate::Brush { + fill_texture_id, + uv, + } = **brush; + let uv_from_pos = |p: Pos2| { + pos2( + remap(p.x, rect.x_range(), uv.x_range()), + remap(p.y, rect.y_range(), uv.y_range()), + ) + }; + path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out); + } + + if !stroke.is_empty() { + path.stroke_closed(self.feathering, &path_stroke, out); + } + } else { + // Stroke and maybe fill + path.fill_and_stroke(self.feathering, fill, &path_stroke, out); } self.feathering = old_feathering; // restore @@ -2029,24 +2139,21 @@ impl Tessellator { self.scratchpad_path.clear(); if closed { self.scratchpad_path.add_line_loop(points); - } else { - self.scratchpad_path.add_open_points(points); - } - if fill != Color32::TRANSPARENT { - debug_assert!( - closed, - "You asked to fill a path that is not closed. That makes no sense." - ); + self.scratchpad_path - .fill(self.feathering, fill, stroke, out); - } - let typ = if closed { - PathType::Closed + .fill_and_stroke(self.feathering, fill, stroke, out); } else { - PathType::Open - }; - self.scratchpad_path - .stroke(self.feathering, typ, stroke, out); + debug_assert_eq!( + fill, + Color32::TRANSPARENT, + "You asked to fill a bezier path that is not closed. That makes no sense." + ); + + self.scratchpad_path.add_open_points(points); + + self.scratchpad_path + .stroke(self.feathering, PathType::Open, stroke, out); + } } } From 23ed49334e77342da5bf5c7c67b05e78eb510c1b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 12:53:18 +0100 Subject: [PATCH 11/22] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Rename=20`Rounding`?= =?UTF-8?q?=20to=20`CornerRadius`=20(#5673)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking change! * `Rounding` -> `CornerRadius` * `rounding` -> `corner_radius` This is to: * Clarify * Conform to other systems (e.g. Figma) * Avoid confusion with `GuiRounding` --- .../egui/src/containers/collapsing_header.rs | 4 +- crates/egui/src/containers/combo_box.rs | 2 +- crates/egui/src/containers/frame.rs | 36 ++++--- crates/egui/src/containers/scroll_area.rs | 4 +- crates/egui/src/containers/window.rs | 80 ++++++--------- crates/egui/src/lib.rs | 9 +- crates/egui/src/menu.rs | 2 +- crates/egui/src/painter.rs | 16 +-- crates/egui/src/style.rs | 97 ++++++++++--------- crates/egui/src/ui.rs | 2 +- crates/egui/src/widgets/button.rs | 28 +++--- crates/egui/src/widgets/checkbox.rs | 2 +- crates/egui/src/widgets/color_picker.rs | 4 +- crates/egui/src/widgets/image.rs | 46 ++++++--- crates/egui/src/widgets/image_button.rs | 21 +++- crates/egui/src/widgets/progress_bar.rs | 40 ++++---- crates/egui/src/widgets/selected_label.rs | 2 +- crates/egui/src/widgets/slider.rs | 12 +-- crates/egui/src/widgets/text_edit/builder.rs | 6 +- crates/egui_demo_app/src/frame_history.rs | 2 +- crates/egui_demo_app/src/wrap_app.rs | 2 +- crates/egui_demo_lib/src/demo/frame_demo.rs | 4 +- .../src/demo/misc_demo_window.rs | 8 +- .../src/demo/tests/tessellation_test.rs | 6 +- .../tests/snapshots/demos/Frame.png | 4 +- .../tessellation_test/Blurred stroke.png | 4 +- .../snapshots/tessellation_test/Blurred.png | 4 +- .../tessellation_test/Minimal rounding.png | 4 +- .../snapshots/tessellation_test/Normal.png | 4 +- .../Thick stroke, minimal rounding.png | 4 +- .../tessellation_test/Thin filled.png | 4 +- .../tessellation_test/Thin stroked.png | 4 +- crates/egui_extras/src/layout.rs | 6 +- .../src/{rounding.rs => corner_radius.rs} | 36 +++---- .../{roundingf.rs => corner_radius_f32.rs} | 56 +++++------ crates/epaint/src/lib.rs | 11 ++- crates/epaint/src/shadow.rs | 8 +- crates/epaint/src/shape_transform.rs | 2 +- crates/epaint/src/shapes/rect_shape.rs | 16 +-- crates/epaint/src/shapes/shape.rs | 12 +-- crates/epaint/src/tessellator.rs | 82 ++++++++-------- examples/custom_window_frame/src/main.rs | 2 +- examples/images/src/main.rs | 2 +- tests/test_viewports/src/main.rs | 8 +- 44 files changed, 378 insertions(+), 330 deletions(-) rename crates/epaint/src/{rounding.rs => corner_radius.rs} (88%) rename crates/epaint/src/{roundingf.rs => corner_radius_f32.rs} (79%) diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs index 4ac57d36d..1ed2a27fb 100644 --- a/crates/egui/src/containers/collapsing_header.rs +++ b/crates/egui/src/containers/collapsing_header.rs @@ -573,7 +573,7 @@ impl CollapsingHeader { if ui.visuals().collapsing_header_frame || show_background { ui.painter().add(epaint::RectShape::new( header_response.rect.expand(visuals.expansion), - visuals.rounding, + visuals.corner_radius, visuals.weak_bg_fill, visuals.bg_stroke, StrokeKind::Inside, @@ -586,7 +586,7 @@ impl CollapsingHeader { ui.painter().rect( rect, - visuals.rounding, + visuals.corner_radius, visuals.bg_fill, visuals.bg_stroke, StrokeKind::Inside, diff --git a/crates/egui/src/containers/combo_box.rs b/crates/egui/src/containers/combo_box.rs index d6a71c2e4..98cf0182e 100644 --- a/crates/egui/src/containers/combo_box.rs +++ b/crates/egui/src/containers/combo_box.rs @@ -471,7 +471,7 @@ fn button_frame( where_to_put_background, epaint::RectShape::new( outer_rect.expand(visuals.expansion), - visuals.rounding, + visuals.corner_radius, visuals.weak_bg_fill, visuals.bg_stroke, epaint::StrokeKind::Inside, diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 07ab8e28a..343897dcc 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -4,7 +4,7 @@ use crate::{ epaint, layers::ShapeIdx, InnerResponse, Response, Sense, Style, Ui, UiBuilder, UiKind, UiStackInfo, }; -use epaint::{Color32, Margin, Marginf, Rect, Rounding, Shadow, Shape, Stroke}; +use epaint::{Color32, CornerRadius, Margin, Marginf, Rect, Shadow, Shape, Stroke}; /// A frame around some content, including margin, colors, etc. /// @@ -119,7 +119,7 @@ pub struct Frame { /// (or, if there is no stroke, the outer corner of [`Self::fill`]). /// /// In other words, this is the corner radius of the _widget rect_. - pub rounding: Rounding, + pub corner_radius: CornerRadius, /// Margin outside the painted frame. /// @@ -161,7 +161,7 @@ impl Frame { inner_margin: Margin::ZERO, stroke: Stroke::NONE, fill: Color32::TRANSPARENT, - rounding: Rounding::ZERO, + corner_radius: CornerRadius::ZERO, outer_margin: Margin::ZERO, shadow: Shadow::NONE, }; @@ -182,7 +182,7 @@ impl Frame { pub fn group(style: &Style) -> Self { Self::new() .inner_margin(6) - .rounding(style.visuals.widgets.noninteractive.rounding) + .corner_radius(style.visuals.widgets.noninteractive.corner_radius) .stroke(style.visuals.widgets.noninteractive.bg_stroke) } @@ -199,7 +199,7 @@ impl Frame { pub fn window(style: &Style) -> Self { Self::new() .inner_margin(style.spacing.window_margin) - .rounding(style.visuals.window_rounding) + .corner_radius(style.visuals.window_corner_radius) .shadow(style.visuals.window_shadow) .fill(style.visuals.window_fill()) .stroke(style.visuals.window_stroke()) @@ -208,7 +208,7 @@ impl Frame { pub fn menu(style: &Style) -> Self { Self::new() .inner_margin(style.spacing.menu_margin) - .rounding(style.visuals.menu_rounding) + .corner_radius(style.visuals.menu_corner_radius) .shadow(style.visuals.popup_shadow) .fill(style.visuals.window_fill()) .stroke(style.visuals.window_stroke()) @@ -217,7 +217,7 @@ impl Frame { pub fn popup(style: &Style) -> Self { Self::new() .inner_margin(style.spacing.menu_margin) - .rounding(style.visuals.menu_rounding) + .corner_radius(style.visuals.menu_corner_radius) .shadow(style.visuals.popup_shadow) .fill(style.visuals.window_fill()) .stroke(style.visuals.window_stroke()) @@ -230,7 +230,7 @@ impl Frame { pub fn canvas(style: &Style) -> Self { Self::new() .inner_margin(2) - .rounding(style.visuals.widgets.noninteractive.rounding) + .corner_radius(style.visuals.widgets.noninteractive.corner_radius) .fill(style.visuals.extreme_bg_color) .stroke(style.visuals.window_stroke()) } @@ -277,11 +277,21 @@ impl Frame { /// /// In other words, this is the corner radius of the _widget rect_. #[inline] - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.rounding = rounding.into(); + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.corner_radius = corner_radius.into(); self } + /// The rounding of the _outer_ corner of the [`Self::stroke`] + /// (or, if there is no stroke, the outer corner of [`Self::fill`]). + /// + /// In other words, this is the corner radius of the _widget rect_. + #[inline] + #[deprecated = "Renamed to `corner_radius`"] + pub fn rounding(self, corner_radius: impl Into) -> Self { + self.corner_radius(corner_radius) + } + /// Margin outside the painted frame. /// /// Similar to what is called `margin` in CSS. @@ -424,7 +434,7 @@ impl Frame { inner_margin: _, fill, stroke, - rounding, + corner_radius, outer_margin: _, shadow, } = *self; @@ -433,7 +443,7 @@ impl Frame { let frame_shape = Shape::Rect(epaint::RectShape::new( widget_rect, - rounding, + corner_radius, fill, stroke, epaint::StrokeKind::Inside, @@ -442,7 +452,7 @@ impl Frame { if shadow == Default::default() { frame_shape } else { - let shadow = shadow.as_shape(widget_rect, rounding); + let shadow = shadow.as_shape(widget_rect, corner_radius); Shape::Vec(vec![Shape::from(shadow), frame_shape]) } } diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 3f6804212..727a9da7b 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -1240,7 +1240,7 @@ impl Prepared { // Background: ui.painter().add(epaint::Shape::rect_filled( outer_scroll_bar_rect, - visuals.rounding, + visuals.corner_radius, ui.visuals() .extreme_bg_color .gamma_multiply(background_opacity), @@ -1249,7 +1249,7 @@ impl Prepared { // Handle: ui.painter().add(epaint::Shape::rect_filled( handle_rect, - visuals.rounding, + visuals.corner_radius, handle_color.gamma_multiply(handle_opacity), )); } diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 75f4ac8e0..7582bd701 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use emath::GuiRounding as _; -use epaint::{RectShape, Roundingf}; +use epaint::{CornerRadiusF32, RectShape}; use crate::collapsing_header::CollapsingState; use crate::*; @@ -485,8 +485,8 @@ impl Window<'_> { .at_least(style.spacing.interact_size.y); let title_bar_inner_height = title_bar_inner_height + window_frame.inner_margin.sum().y; let half_height = (title_bar_inner_height / 2.0).round() as _; - window_frame.rounding.ne = window_frame.rounding.ne.clamp(0, half_height); - window_frame.rounding.nw = window_frame.rounding.nw.clamp(0, half_height); + window_frame.corner_radius.ne = window_frame.corner_radius.ne.clamp(0, half_height); + window_frame.corner_radius.nw = window_frame.corner_radius.nw.clamp(0, half_height); let title_content_spacing = if is_collapsed { 0.0 @@ -612,7 +612,7 @@ impl Window<'_> { if on_top && area_content_ui.visuals().window_highlight_topmost { let mut round = - window_frame.rounding - window_frame.stroke.width.round() as u8; + window_frame.corner_radius - window_frame.stroke.width.round() as u8; if !is_collapsed { round.se = 0; @@ -667,28 +667,28 @@ fn paint_resize_corner( window_frame: &Frame, i: ResizeInteraction, ) { - let rounding = window_frame.rounding; + let cr = window_frame.corner_radius; let (corner, radius, corner_response) = if possible.resize_right && possible.resize_bottom { - (Align2::RIGHT_BOTTOM, rounding.se, i.right & i.bottom) + (Align2::RIGHT_BOTTOM, cr.se, i.right & i.bottom) } else if possible.resize_left && possible.resize_bottom { - (Align2::LEFT_BOTTOM, rounding.sw, i.left & i.bottom) + (Align2::LEFT_BOTTOM, cr.sw, i.left & i.bottom) } else if possible.resize_left && possible.resize_top { - (Align2::LEFT_TOP, rounding.nw, i.left & i.top) + (Align2::LEFT_TOP, cr.nw, i.left & i.top) } else if possible.resize_right && possible.resize_top { - (Align2::RIGHT_TOP, rounding.ne, i.right & i.top) + (Align2::RIGHT_TOP, cr.ne, i.right & i.top) } else { // We're not in two directions, but it is still nice to tell the user // we're resizable by painting the resize corner in the expected place // (i.e. for windows only resizable in one direction): if possible.resize_right || possible.resize_bottom { - (Align2::RIGHT_BOTTOM, rounding.se, i.right & i.bottom) + (Align2::RIGHT_BOTTOM, cr.se, i.right & i.bottom) } else if possible.resize_left || possible.resize_bottom { - (Align2::LEFT_BOTTOM, rounding.sw, i.left & i.bottom) + (Align2::LEFT_BOTTOM, cr.sw, i.left & i.bottom) } else if possible.resize_left || possible.resize_top { - (Align2::LEFT_TOP, rounding.nw, i.left & i.top) + (Align2::LEFT_TOP, cr.nw, i.left & i.top) } else if possible.resize_right || possible.resize_top { - (Align2::RIGHT_TOP, rounding.ne, i.right & i.top) + (Align2::RIGHT_TOP, cr.ne, i.right & i.top) } else { return; } @@ -1054,7 +1054,7 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction) bottom = interaction.bottom.hover; } - let rounding = Roundingf::from(ui.visuals().window_rounding); + let cr = CornerRadiusF32::from(ui.visuals().window_corner_radius); // Put the rect in the center of the fixed window stroke: let rect = rect.shrink(interaction.window_frame.stroke.width / 2.0); @@ -1072,56 +1072,36 @@ fn paint_frame_interaction(ui: &Ui, rect: Rect, interaction: ResizeInteraction) let mut points = Vec::new(); if right && !bottom && !top { - points.push(pos2(max.x, min.y + rounding.ne)); - points.push(pos2(max.x, max.y - rounding.se)); + points.push(pos2(max.x, min.y + cr.ne)); + points.push(pos2(max.x, max.y - cr.se)); } if right && bottom { - points.push(pos2(max.x, min.y + rounding.ne)); - points.push(pos2(max.x, max.y - rounding.se)); - add_circle_quadrant( - &mut points, - pos2(max.x - rounding.se, max.y - rounding.se), - rounding.se, - 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 bottom { - points.push(pos2(max.x - rounding.se, max.y)); - points.push(pos2(min.x + rounding.sw, max.y)); + points.push(pos2(max.x - cr.se, max.y)); + points.push(pos2(min.x + cr.sw, max.y)); } if left && bottom { - add_circle_quadrant( - &mut points, - pos2(min.x + rounding.sw, max.y - rounding.sw), - rounding.sw, - 1.0, - ); + add_circle_quadrant(&mut points, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); } if left { - points.push(pos2(min.x, max.y - rounding.sw)); - points.push(pos2(min.x, min.y + rounding.nw)); + points.push(pos2(min.x, max.y - cr.sw)); + points.push(pos2(min.x, min.y + cr.nw)); } if left && top { - add_circle_quadrant( - &mut points, - pos2(min.x + rounding.nw, min.y + rounding.nw), - rounding.nw, - 2.0, - ); + add_circle_quadrant(&mut points, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); } if top { - points.push(pos2(min.x + rounding.nw, min.y)); - points.push(pos2(max.x - rounding.ne, min.y)); + points.push(pos2(min.x + cr.nw, min.y)); + points.push(pos2(max.x - cr.ne, min.y)); } if right && top { - add_circle_quadrant( - &mut points, - pos2(max.x - rounding.ne, min.y + rounding.ne), - rounding.ne, - 3.0, - ); - points.push(pos2(max.x, min.y + rounding.ne)); - points.push(pos2(max.x, max.y - rounding.se)); + 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, stroke)); diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 15f4faf3c..759f7e41a 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -464,8 +464,8 @@ pub use epaint::{ mutex, text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak}, textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta}, - ClippedPrimitive, ColorImage, FontImage, ImageData, Margin, Mesh, PaintCallback, - PaintCallbackInfo, Rounding, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, + ClippedPrimitive, ColorImage, CornerRadius, FontImage, ImageData, Margin, Mesh, PaintCallback, + PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, }; pub mod text { @@ -510,6 +510,9 @@ pub use self::{ widgets::*, }; +#[deprecated = "Renamed to CornerRadius"] +pub type Rounding = CornerRadius; + // ---------------------------------------------------------------------------- /// Helper function that adds a label when compiling with debug assertions enabled. @@ -538,7 +541,7 @@ pub fn warn_if_debug_build(ui: &mut crate::Ui) { /// ui.add( /// egui::Image::new(egui::include_image!("../assets/ferris.png")) /// .max_width(200.0) -/// .rounding(10.0), +/// .corner_radius(10), /// ); /// /// let image_source: egui::ImageSource = egui::include_image!("../assets/ferris.png"); diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 301c69dd3..96aeeac61 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -580,7 +580,7 @@ impl SubMenuButton { if ui.visuals().button_frame { ui.painter().rect_filled( rect.expand(visuals.expansion), - visuals.rounding, + visuals.corner_radius, visuals.weak_bg_fill, ); } diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index 4ed74cddb..aef97cccf 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use emath::GuiRounding as _; use epaint::{ text::{Fonts, Galley, LayoutJob}, - CircleShape, ClippedShape, PathStroke, RectShape, Rounding, Shape, Stroke, StrokeKind, + CircleShape, ClippedShape, CornerRadius, PathStroke, RectShape, Shape, Stroke, StrokeKind, }; use crate::{ @@ -412,14 +412,14 @@ impl Painter { pub fn rect( &self, rect: Rect, - rounding: impl Into, + corner_radius: impl Into, fill_color: impl Into, stroke: impl Into, stroke_kind: StrokeKind, ) -> ShapeIdx { self.add(RectShape::new( rect, - rounding, + corner_radius, fill_color, stroke, stroke_kind, @@ -429,21 +429,21 @@ impl Painter { pub fn rect_filled( &self, rect: Rect, - rounding: impl Into, + corner_radius: impl Into, fill_color: impl Into, ) -> ShapeIdx { - self.add(RectShape::filled(rect, rounding, fill_color)) + self.add(RectShape::filled(rect, corner_radius, fill_color)) } /// The stroke extends _outside_ the [`Rect`]. pub fn rect_stroke( &self, rect: Rect, - rounding: impl Into, + corner_radius: impl Into, stroke: impl Into, stroke_kind: StrokeKind, ) -> ShapeIdx { - self.add(RectShape::stroke(rect, rounding, stroke, stroke_kind)) + self.add(RectShape::stroke(rect, corner_radius, stroke, stroke_kind)) } /// Show an arrow starting at `origin` and going in the direction of `vec`, with the length `vec.length()`. @@ -472,7 +472,7 @@ impl Painter { /// # egui::__run_test_ui(|ui| { /// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0)); /// egui::Image::new(egui::include_image!("../assets/ferris.png")) - /// .rounding(5.0) + /// .corner_radius(5) /// .tint(egui::Color32::LIGHT_BLUE) /// .paint_at(ui, rect); /// # }); diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 1d5a1b25c..c84f2dc8a 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -5,7 +5,7 @@ use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc}; use emath::Align; -use epaint::{text::FontTweak, Rounding, Shadow, Stroke}; +use epaint::{text::FontTweak, CornerRadius, Shadow, Stroke}; use crate::{ ecolor::Color32, @@ -915,7 +915,7 @@ pub struct Visuals { /// A good color for error text (e.g. red). pub error_fg_color: Color32, - pub window_rounding: Rounding, + pub window_corner_radius: CornerRadius, pub window_shadow: Shadow, pub window_fill: Color32, pub window_stroke: Stroke, @@ -923,7 +923,7 @@ pub struct Visuals { /// Highlight the topmost window. pub window_highlight_topmost: bool, - pub menu_rounding: Rounding, + pub menu_corner_radius: CornerRadius, /// Panel background color pub panel_fill: Color32, @@ -1107,7 +1107,7 @@ pub struct WidgetVisuals { pub bg_stroke: Stroke, /// Button frames etc. - pub rounding: Rounding, + pub corner_radius: CornerRadius, /// Stroke and text color of the interactive part of a component (button text, slider grab, check-mark, …). pub fg_stroke: Stroke, @@ -1121,6 +1121,11 @@ impl WidgetVisuals { pub fn text_color(&self) -> Color32 { self.fg_stroke.color } + + #[deprecated = "Renamed to corner_radius"] + pub fn rounding(&self) -> CornerRadius { + self.corner_radius + } } /// Options for help debug egui by adding extra visualization @@ -1291,7 +1296,7 @@ impl Visuals { warn_fg_color: Color32::from_rgb(255, 143, 0), // orange error_fg_color: Color32::from_rgb(255, 0, 0), // red - window_rounding: Rounding::same(6), + window_corner_radius: CornerRadius::same(6), window_shadow: Shadow { offset: [10, 20], blur: 15, @@ -1302,7 +1307,7 @@ impl Visuals { window_stroke: Stroke::new(1.0, Color32::from_gray(60)), window_highlight_topmost: true, - menu_rounding: Rounding::same(6), + menu_corner_radius: CornerRadius::same(6), panel_fill: Color32::from_gray(27), @@ -1412,7 +1417,7 @@ impl Widgets { bg_fill: Color32::from_gray(27), bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, inactive: WidgetVisuals { @@ -1420,7 +1425,7 @@ impl Widgets { bg_fill: Color32::from_gray(60), // checkbox background bg_stroke: Default::default(), fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, hovered: WidgetVisuals { @@ -1428,7 +1433,7 @@ impl Widgets { 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)), - rounding: Rounding::same(3), + corner_radius: CornerRadius::same(3), expansion: 1.0, }, active: WidgetVisuals { @@ -1436,7 +1441,7 @@ impl Widgets { bg_fill: Color32::from_gray(55), bg_stroke: Stroke::new(1.0, Color32::WHITE), fg_stroke: Stroke::new(2.0, Color32::WHITE), - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 1.0, }, open: WidgetVisuals { @@ -1444,7 +1449,7 @@ impl Widgets { 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)), - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, } @@ -1457,7 +1462,7 @@ impl Widgets { bg_fill: Color32::from_gray(248), bg_stroke: Stroke::new(1.0, Color32::from_gray(190)), // separators, indentation lines fg_stroke: Stroke::new(1.0, Color32::from_gray(80)), // normal text color - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, inactive: WidgetVisuals { @@ -1465,7 +1470,7 @@ impl Widgets { bg_fill: Color32::from_gray(230), // checkbox background bg_stroke: Default::default(), fg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // button text - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, hovered: WidgetVisuals { @@ -1473,7 +1478,7 @@ impl Widgets { bg_fill: Color32::from_gray(220), 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), - rounding: Rounding::same(3), + corner_radius: CornerRadius::same(3), expansion: 1.0, }, active: WidgetVisuals { @@ -1481,7 +1486,7 @@ impl Widgets { bg_fill: Color32::from_gray(165), bg_stroke: Stroke::new(1.0, Color32::BLACK), fg_stroke: Stroke::new(2.0, Color32::BLACK), - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 1.0, }, open: WidgetVisuals { @@ -1489,7 +1494,7 @@ impl Widgets { bg_fill: Color32::from_gray(220), bg_stroke: Stroke::new(1.0, Color32::from_gray(160)), fg_stroke: Stroke::new(1.0, Color32::BLACK), - rounding: Rounding::same(2), + corner_radius: CornerRadius::same(2), expansion: 0.0, }, } @@ -1924,7 +1929,7 @@ impl WidgetVisuals { weak_bg_fill, bg_fill: mandatory_bg_fill, bg_stroke, - rounding, + corner_radius, fg_stroke, expansion, } = self; @@ -1948,8 +1953,8 @@ impl WidgetVisuals { ui.add(bg_stroke); ui.end_row(); - ui.label("Rounding"); - ui.add(rounding); + ui.label("Corner radius"); + ui.add(corner_radius); ui.end_row(); ui.label("Foreground stroke (text)"); @@ -1978,13 +1983,13 @@ impl Visuals { warn_fg_color, error_fg_color, - window_rounding, + window_corner_radius, window_shadow, window_fill, window_stroke, window_highlight_topmost, - menu_rounding, + menu_corner_radius, panel_fill, @@ -2066,8 +2071,8 @@ impl Visuals { ui.add(window_stroke); ui.end_row(); - ui.label("Rounding"); - ui.add(window_rounding); + ui.label("Corner radius"); + ui.add(window_corner_radius); ui.end_row(); ui.label("Shadow"); @@ -2084,8 +2089,8 @@ impl Visuals { .spacing([12.0, 8.0]) .striped(true) .show(ui, |ui| { - ui.label("Rounding"); - ui.add(menu_rounding); + ui.label("Corner radius"); + ui.add(menu_corner_radius); ui.end_row(); ui.label("Shadow"); @@ -2388,7 +2393,7 @@ impl Widget for &mut Margin { } } -impl Widget for &mut Rounding { +impl Widget for &mut CornerRadius { fn ui(self, ui: &mut Ui) -> Response { let mut same = self.is_same(); @@ -2398,37 +2403,39 @@ impl Widget for &mut Rounding { let mut cr = self.nw; ui.add(DragValue::new(&mut cr).range(0.0..=f32::INFINITY)); - *self = Rounding::same(cr); + *self = CornerRadius::same(cr); }) .response } else { ui.vertical(|ui| { ui.checkbox(&mut same, "same"); - crate::Grid::new("rounding").num_columns(2).show(ui, |ui| { - ui.label("NW"); - ui.add(DragValue::new(&mut self.nw).range(0.0..=f32::INFINITY)); - ui.end_row(); + crate::Grid::new("Corner radius") + .num_columns(2) + .show(ui, |ui| { + ui.label("NW"); + ui.add(DragValue::new(&mut self.nw).range(0.0..=f32::INFINITY)); + ui.end_row(); - ui.label("NE"); - ui.add(DragValue::new(&mut self.ne).range(0.0..=f32::INFINITY)); - ui.end_row(); + ui.label("NE"); + ui.add(DragValue::new(&mut self.ne).range(0.0..=f32::INFINITY)); + ui.end_row(); - ui.label("SW"); - ui.add(DragValue::new(&mut self.sw).range(0.0..=f32::INFINITY)); - ui.end_row(); + ui.label("SW"); + ui.add(DragValue::new(&mut self.sw).range(0.0..=f32::INFINITY)); + ui.end_row(); - ui.label("SE"); - ui.add(DragValue::new(&mut self.se).range(0.0..=f32::INFINITY)); - ui.end_row(); - }); + ui.label("SE"); + ui.add(DragValue::new(&mut self.se).range(0.0..=f32::INFINITY)); + ui.end_row(); + }); }) .response }; // Apply the checkbox: if same { - *self = Rounding::from(self.average()); + *self = CornerRadius::from(self.average()); } else { // Make sure we aren't same: if self.is_same() { @@ -2513,7 +2520,7 @@ impl Widget for &mut crate::Frame { let crate::Frame { inner_margin, outer_margin, - rounding, + corner_radius, shadow, fill, stroke, @@ -2533,8 +2540,8 @@ impl Widget for &mut crate::Frame { ui.push_id("outer", |ui| ui.add(outer_margin)); ui.end_row(); - ui.label("Rounding"); - ui.add(rounding); + ui.label("Corner radius"); + ui.add(corner_radius); ui.end_row(); ui.label("Shadow"); diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 1fdfccdc6..8422c3ce8 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2127,7 +2127,7 @@ impl Ui { /// ui.add( /// egui::Image::new(egui::include_image!("../assets/ferris.png")) /// .max_width(200.0) - /// .rounding(10.0), + /// .corner_radius(10), /// ); /// # }); /// ``` diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 9e44e940e..578aedcd6 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -1,5 +1,5 @@ use crate::{ - widgets, Align, Color32, Image, NumExt, Rect, Response, Rounding, Sense, Stroke, TextStyle, + widgets, Align, Color32, CornerRadius, Image, NumExt, Rect, Response, Sense, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType, }; @@ -35,7 +35,7 @@ pub struct Button<'a> { small: bool, frame: Option, min_size: Vec2, - rounding: Option, + corner_radius: Option, selected: bool, image_tint_follows_text_color: bool, } @@ -69,7 +69,7 @@ impl<'a> Button<'a> { small: false, frame: None, min_size: Vec2::ZERO, - rounding: None, + corner_radius: None, selected: false, image_tint_follows_text_color: false, } @@ -153,11 +153,17 @@ impl<'a> Button<'a> { /// Set the rounding of the button. #[inline] - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.rounding = Some(rounding.into()); + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.corner_radius = Some(corner_radius.into()); self } + #[inline] + #[deprecated = "Renamed to `corner_radius`"] + pub fn rounding(self, corner_radius: impl Into) -> Self { + self.corner_radius(corner_radius) + } + /// If true, the tint of the image is multiplied by the widget text color. /// /// This makes sense for images that are white, that should have the same color as the text color. @@ -202,7 +208,7 @@ impl Widget for Button<'_> { small, frame, min_size, - rounding, + corner_radius, selected, image_tint_follows_text_color, } = self; @@ -292,11 +298,11 @@ impl Widget for Button<'_> { if ui.is_rect_visible(rect) { let visuals = ui.style().interact(&response); - let (frame_expansion, frame_rounding, frame_fill, frame_stroke) = if selected { + let (frame_expansion, frame_cr, frame_fill, frame_stroke) = if selected { let selection = ui.visuals().selection; ( Vec2::ZERO, - Rounding::ZERO, + CornerRadius::ZERO, selection.bg_fill, selection.stroke, ) @@ -304,19 +310,19 @@ impl Widget for Button<'_> { let expansion = Vec2::splat(visuals.expansion); ( expansion, - visuals.rounding, + visuals.corner_radius, visuals.weak_bg_fill, visuals.bg_stroke, ) } else { Default::default() }; - let frame_rounding = rounding.unwrap_or(frame_rounding); + let frame_cr = corner_radius.unwrap_or(frame_cr); let frame_fill = fill.unwrap_or(frame_fill); let frame_stroke = stroke.unwrap_or(frame_stroke); ui.painter().rect( rect.expand2(frame_expansion), - frame_rounding, + frame_cr, frame_fill, frame_stroke, epaint::StrokeKind::Inside, diff --git a/crates/egui/src/widgets/checkbox.rs b/crates/egui/src/widgets/checkbox.rs index f54478984..7bdb6c86f 100644 --- a/crates/egui/src/widgets/checkbox.rs +++ b/crates/egui/src/widgets/checkbox.rs @@ -104,7 +104,7 @@ impl Widget for Checkbox<'_> { let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect); ui.painter().add(epaint::RectShape::new( big_icon_rect.expand(visuals.expansion), - visuals.rounding, + visuals.corner_radius, visuals.bg_fill, visuals.bg_stroke, epaint::StrokeKind::Inside, diff --git a/crates/egui/src/widgets/color_picker.rs b/crates/egui/src/widgets/color_picker.rs index b56f3c4e9..a9906cef1 100644 --- a/crates/egui/src/widgets/color_picker.rs +++ b/crates/egui/src/widgets/color_picker.rs @@ -100,10 +100,10 @@ fn color_button(ui: &mut Ui, color: Color32, open: bool) -> Response { let stroke_width = 1.0; show_color_at(ui.painter(), color, rect.shrink(stroke_width)); - let rounding = visuals.rounding.at_most(2); // Can't do more rounding because the background grid doesn't do any rounding + let corner_radius = visuals.corner_radius.at_most(2); // Can't do more rounding because the background grid doesn't do any rounding ui.painter().rect_stroke( rect, - rounding, + corner_radius, (stroke_width, visuals.bg_fill), // Using fill for stroke is intentional, because default style has no border StrokeKind::Inside, ); diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index 2290eabbd..89974dcaf 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -8,7 +8,7 @@ use epaint::{ use crate::{ load::{Bytes, SizeHint, SizedTexture, TextureLoadResult, TexturePoll}, - pos2, Color32, Context, Id, Mesh, Painter, Rect, Response, Rounding, Sense, Shape, Spinner, + pos2, Color32, Context, CornerRadius, Id, Mesh, Painter, Rect, Response, Sense, Shape, Spinner, TextStyle, TextureOptions, Ui, Vec2, Widget, WidgetInfo, WidgetType, }; @@ -29,7 +29,7 @@ use crate::{ /// # egui::__run_test_ui(|ui| { /// ui.add( /// egui::Image::new(egui::include_image!("../../assets/ferris.png")) -/// .rounding(5.0) +/// .corner_radius(5) /// ); /// # }); /// ``` @@ -39,7 +39,7 @@ use crate::{ /// # egui::__run_test_ui(|ui| { /// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0)); /// egui::Image::new(egui::include_image!("../../assets/ferris.png")) -/// .rounding(5.0) +/// .corner_radius(5) /// .tint(egui::Color32::LIGHT_BLUE) /// .paint_at(ui, rect); /// # }); @@ -233,25 +233,37 @@ impl<'a> Image<'a> { #[inline] pub fn rotate(mut self, angle: f32, origin: Vec2) -> Self { self.image_options.rotation = Some((Rot2::from_angle(angle), origin)); - self.image_options.rounding = Rounding::ZERO; // incompatible with rotation + self.image_options.corner_radius = CornerRadius::ZERO; // incompatible with rotation self } /// Round the corners of the image. /// - /// The default is no rounding ([`Rounding::ZERO`]). + /// The default is no rounding ([`CornerRadius::ZERO`]). /// /// Due to limitations in the current implementation, /// this will turn off any rotation of the image. #[inline] - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.image_options.rounding = rounding.into(); - if self.image_options.rounding != Rounding::ZERO { + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.image_options.corner_radius = corner_radius.into(); + if self.image_options.corner_radius != CornerRadius::ZERO { self.image_options.rotation = None; // incompatible with rounding } self } + /// Round the corners of the image. + /// + /// The default is no rounding ([`CornerRadius::ZERO`]). + /// + /// Due to limitations in the current implementation, + /// this will turn off any rotation of the image. + #[inline] + #[deprecated = "Renamed to `corner_radius`"] + pub fn rounding(self, corner_radius: impl Into) -> Self { + self.corner_radius(corner_radius) + } + /// Show a spinner when the image is loading. /// /// By default this uses the value of [`crate::Visuals::image_loading_spinners`]. @@ -354,7 +366,7 @@ impl<'a> Image<'a> { /// # egui::__run_test_ui(|ui| { /// # let rect = egui::Rect::from_min_size(Default::default(), egui::Vec2::splat(100.0)); /// egui::Image::new(egui::include_image!("../../assets/ferris.png")) - /// .rounding(5.0) + /// .corner_radius(5) /// .tint(egui::Color32::LIGHT_BLUE) /// .paint_at(ui, rect); /// # }); @@ -778,11 +790,11 @@ pub struct ImageOptions { /// Round the corners of the image. /// - /// The default is no rounding ([`Rounding::ZERO`]). + /// The default is no rounding ([`CornerRadius::ZERO`]). /// /// Due to limitations in the current implementation, /// this will turn off any rotation of the image. - pub rounding: Rounding, + pub corner_radius: CornerRadius, } impl Default for ImageOptions { @@ -792,7 +804,7 @@ impl Default for ImageOptions { bg_fill: Default::default(), tint: Color32::WHITE, rotation: None, - rounding: Rounding::ZERO, + corner_radius: CornerRadius::ZERO, } } } @@ -804,7 +816,11 @@ pub fn paint_texture_at( texture: &SizedTexture, ) { if options.bg_fill != Default::default() { - painter.add(RectShape::filled(rect, options.rounding, options.bg_fill)); + painter.add(RectShape::filled( + rect, + options.corner_radius, + options.bg_fill, + )); } match options.rotation { @@ -812,7 +828,7 @@ pub fn paint_texture_at( // TODO(emilk): implement this using `PathShape` (add texture support to it). // This will also give us anti-aliasing of rotated images. debug_assert!( - options.rounding == Rounding::ZERO, + options.corner_radius == CornerRadius::ZERO, "Image had both rounding and rotation. Please pick only one" ); @@ -823,7 +839,7 @@ pub fn paint_texture_at( } None => { painter.add( - RectShape::filled(rect, options.rounding, options.tint) + RectShape::filled(rect, options.corner_radius, options.tint) .with_texture(texture.id, options.uv), ); } diff --git a/crates/egui/src/widgets/image_button.rs b/crates/egui/src/widgets/image_button.rs index 7bf55cb60..b1dddbf7c 100644 --- a/crates/egui/src/widgets/image_button.rs +++ b/crates/egui/src/widgets/image_button.rs @@ -1,5 +1,5 @@ use crate::{ - widgets, Color32, Image, Rect, Response, Rounding, Sense, Ui, Vec2, Widget, WidgetInfo, + widgets, Color32, CornerRadius, Image, Rect, Response, Sense, Ui, Vec2, Widget, WidgetInfo, WidgetType, }; @@ -62,13 +62,24 @@ impl<'a> ImageButton<'a> { } /// Set rounding for the `ImageButton`. + /// /// If the underlying image already has rounding, this /// will override that value. #[inline] - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.image = self.image.rounding(rounding.into()); + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.image = self.image.corner_radius(corner_radius.into()); self } + + /// Set rounding for the `ImageButton`. + /// + /// If the underlying image already has rounding, this + /// will override that value. + #[inline] + #[deprecated = "Renamed to `corner_radius`"] + pub fn rounding(self, corner_radius: impl Into) -> Self { + self.corner_radius(corner_radius) + } } impl Widget for ImageButton<'_> { @@ -100,7 +111,7 @@ impl Widget for ImageButton<'_> { let selection = ui.visuals().selection; ( Vec2::ZERO, - self.image.image_options().rounding, + self.image.image_options().corner_radius, selection.bg_fill, selection.stroke, ) @@ -109,7 +120,7 @@ impl Widget for ImageButton<'_> { let expansion = Vec2::splat(visuals.expansion); ( expansion, - self.image.image_options().rounding, + self.image.image_options().corner_radius, visuals.weak_bg_fill, visuals.bg_stroke, ) diff --git a/crates/egui/src/widgets/progress_bar.rs b/crates/egui/src/widgets/progress_bar.rs index a8c27f5c3..44c9b8971 100644 --- a/crates/egui/src/widgets/progress_bar.rs +++ b/crates/egui/src/widgets/progress_bar.rs @@ -1,5 +1,5 @@ use crate::{ - lerp, vec2, Color32, NumExt, Pos2, Rect, Response, Rgba, Rounding, Sense, Shape, Stroke, + lerp, vec2, Color32, CornerRadius, NumExt, Pos2, Rect, Response, Rgba, Sense, Shape, Stroke, TextStyle, TextWrapMode, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType, }; @@ -19,7 +19,7 @@ pub struct ProgressBar { text: Option, fill: Option, animate: bool, - rounding: Option, + corner_radius: Option, } impl ProgressBar { @@ -32,7 +32,7 @@ impl ProgressBar { text: None, fill: None, animate: false, - rounding: None, + corner_radius: None, } } @@ -75,7 +75,7 @@ impl ProgressBar { /// Note that this will cause the UI to be redrawn. /// Defaults to `false`. /// - /// If [`Self::rounding`] and [`Self::animate`] are used simultaneously, the animation is not + /// If [`Self::corner_radius`] and [`Self::animate`] are used simultaneously, the animation is not /// rendered, since it requires a perfect circle to render correctly. However, the UI is still /// redrawn. #[inline] @@ -86,14 +86,20 @@ impl ProgressBar { /// Set the rounding of the progress bar. /// - /// If [`Self::rounding`] and [`Self::animate`] are used simultaneously, the animation is not + /// If [`Self::corner_radius`] and [`Self::animate`] are used simultaneously, the animation is not /// rendered, since it requires a perfect circle to render correctly. However, the UI is still /// redrawn. #[inline] - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.rounding = Some(rounding.into()); + pub fn corner_radius(mut self, corner_radius: impl Into) -> Self { + self.corner_radius = Some(corner_radius.into()); self } + + #[inline] + #[deprecated = "Renamed to `corner_radius`"] + pub fn rounding(self, corner_radius: impl Into) -> Self { + self.corner_radius(corner_radius) + } } impl Widget for ProgressBar { @@ -105,7 +111,7 @@ impl Widget for ProgressBar { text, fill, animate, - rounding, + corner_radius, } = self; let animate = animate && progress < 1.0; @@ -133,13 +139,13 @@ impl Widget for ProgressBar { } let visuals = ui.style().visuals.clone(); - let is_custom_rounding = rounding.is_some(); - let corner_radius = outer_rect.height() / 2.0; - let rounding = rounding.unwrap_or_else(|| corner_radius.into()); + let has_custom_cr = corner_radius.is_some(); + let half_height = outer_rect.height() / 2.0; + let corner_radius = corner_radius.unwrap_or_else(|| half_height.into()); ui.painter() - .rect_filled(outer_rect, rounding, visuals.extreme_bg_color); + .rect_filled(outer_rect, corner_radius, visuals.extreme_bg_color); let min_width = - 2.0 * f32::max(rounding.sw as _, rounding.nw as _).at_most(corner_radius); + 2.0 * f32::max(corner_radius.sw as _, corner_radius.nw as _).at_most(half_height); let filled_width = (outer_rect.width() * progress).at_least(min_width); let inner_rect = Rect::from_min_size(outer_rect.min, vec2(filled_width, outer_rect.height())); @@ -154,25 +160,25 @@ impl Widget for ProgressBar { ui.painter().rect_filled( inner_rect, - rounding, + corner_radius, Color32::from( Rgba::from(fill.unwrap_or(visuals.selection.bg_fill)) * color_factor as f32, ), ); - if animate && !is_custom_rounding { + if animate && !has_custom_cr { let n_points = 20; let time = ui.input(|i| i.time); let start_angle = time * std::f64::consts::TAU; let end_angle = start_angle + 240f64.to_radians() * time.sin(); - let circle_radius = corner_radius - 2.0; + let circle_radius = half_height - 2.0; let points: Vec = (0..n_points) .map(|i| { let angle = lerp(start_angle..=end_angle, i as f64 / n_points as f64); let (sin, cos) = angle.sin_cos(); inner_rect.right_center() + circle_radius * vec2(cos as f32, sin as f32) - + vec2(-corner_radius, 0.0) + + vec2(-half_height, 0.0) }) .collect(); ui.painter() diff --git a/crates/egui/src/widgets/selected_label.rs b/crates/egui/src/widgets/selected_label.rs index 16978794b..dfed4d2ba 100644 --- a/crates/egui/src/widgets/selected_label.rs +++ b/crates/egui/src/widgets/selected_label.rs @@ -71,7 +71,7 @@ impl Widget for SelectableLabel { ui.painter().rect( rect, - visuals.rounding, + visuals.corner_radius, visuals.weak_bg_fill, visuals.bg_stroke, epaint::StrokeKind::Inside, diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index f721ce101..590282074 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -760,10 +760,10 @@ impl Slider<'_> { let rail_radius = (spacing.slider_rail_height / 2.0).at_least(0.0); let rail_rect = self.rail_rect(rect, rail_radius); - let rounding = widget_visuals.inactive.rounding; + let corner_radius = widget_visuals.inactive.corner_radius; ui.painter() - .rect_filled(rail_rect, rounding, widget_visuals.inactive.bg_fill); + .rect_filled(rail_rect, corner_radius, widget_visuals.inactive.bg_fill); let position_1d = self.position_from_value(value, position_range); let center = self.marker_center(position_1d, &rail_rect); @@ -780,16 +780,16 @@ impl Slider<'_> { // The trailing rect has to be drawn differently depending on the orientation. match self.orientation { SliderOrientation::Horizontal => { - trailing_rail_rect.max.x = center.x + rounding.nw as f32; + trailing_rail_rect.max.x = center.x + corner_radius.nw as f32; } SliderOrientation::Vertical => { - trailing_rail_rect.min.y = center.y - rounding.se as f32; + trailing_rail_rect.min.y = center.y - corner_radius.se as f32; } }; ui.painter().rect_filled( trailing_rail_rect, - rounding, + corner_radius, ui.visuals().selection.bg_fill, ); } @@ -817,7 +817,7 @@ impl Slider<'_> { let rect = Rect::from_center_size(center, 2.0 * v); ui.painter().rect( rect, - visuals.rounding, + visuals.corner_radius, visuals.bg_fill, visuals.fg_stroke, epaint::StrokeKind::Inside, diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 05a8470c1..2f685bbc1 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -442,7 +442,7 @@ impl TextEdit<'_> { if output.response.has_focus() { epaint::RectShape::new( frame_rect, - visuals.rounding, + visuals.corner_radius, background_color, ui.visuals().selection.stroke, StrokeKind::Inside, @@ -450,7 +450,7 @@ impl TextEdit<'_> { } else { epaint::RectShape::new( frame_rect, - visuals.rounding, + visuals.corner_radius, background_color, visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop". StrokeKind::Inside, @@ -460,7 +460,7 @@ impl TextEdit<'_> { let visuals = &ui.style().visuals.widgets.inactive; epaint::RectShape::stroke( frame_rect, - visuals.rounding, + visuals.corner_radius, visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop". StrokeKind::Inside, ) diff --git a/crates/egui_demo_app/src/frame_history.rs b/crates/egui_demo_app/src/frame_history.rs index b1091faba..231ba4a4f 100644 --- a/crates/egui_demo_app/src/frame_history.rs +++ b/crates/egui_demo_app/src/frame_history.rs @@ -72,7 +72,7 @@ impl FrameHistory { let mut shapes = Vec::with_capacity(3 + 2 * history.len()); shapes.push(Shape::Rect(epaint::RectShape::new( rect, - style.rounding, + style.corner_radius, ui.visuals().extreme_bg_color, ui.style().noninteractive().bg_stroke, egui::StrokeKind::Inside, diff --git a/crates/egui_demo_app/src/wrap_app.rs b/crates/egui_demo_app/src/wrap_app.rs index 6409eb902..eb26a5d7c 100644 --- a/crates/egui_demo_app/src/wrap_app.rs +++ b/crates/egui_demo_app/src/wrap_app.rs @@ -47,7 +47,7 @@ impl eframe::App for FractalClockApp { .frame( egui::Frame::dark_canvas(&ctx.style()) .stroke(egui::Stroke::NONE) - .rounding(0), + .corner_radius(0), ) .show(ctx, |ui| { self.fractal_clock diff --git a/crates/egui_demo_lib/src/demo/frame_demo.rs b/crates/egui_demo_lib/src/demo/frame_demo.rs index 71c5e9ab2..8c4fc984f 100644 --- a/crates/egui_demo_lib/src/demo/frame_demo.rs +++ b/crates/egui_demo_lib/src/demo/frame_demo.rs @@ -10,7 +10,7 @@ impl Default for FrameDemo { frame: egui::Frame::new() .inner_margin(12) .outer_margin(24) - .rounding(14) + .corner_radius(14) .shadow(egui::Shadow { offset: [8, 12], blur: 16, @@ -56,7 +56,7 @@ impl crate::View for FrameDemo { // We want to paint a background around the outer margin of the demonstration frame, so we use another frame around it: egui::Frame::default() .stroke(ui.visuals().widgets.noninteractive.bg_stroke) - .rounding(ui.visuals().widgets.noninteractive.rounding) + .corner_radius(ui.visuals().widgets.noninteractive.corner_radius) .show(ui, |ui| { self.frame.show(ui, |ui| { ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); diff --git a/crates/egui_demo_lib/src/demo/misc_demo_window.rs b/crates/egui_demo_lib/src/demo/misc_demo_window.rs index ce154ab4a..edb19c3ea 100644 --- a/crates/egui_demo_lib/src/demo/misc_demo_window.rs +++ b/crates/egui_demo_lib/src/demo/misc_demo_window.rs @@ -358,7 +358,7 @@ impl ColorWidgets { #[cfg_attr(feature = "serde", serde(default))] struct BoxPainting { size: Vec2, - rounding: f32, + corner_radius: f32, stroke_width: f32, num_boxes: usize, } @@ -367,7 +367,7 @@ impl Default for BoxPainting { fn default() -> Self { Self { size: vec2(64.0, 32.0), - rounding: 5.0, + corner_radius: 5.0, stroke_width: 2.0, num_boxes: 1, } @@ -378,7 +378,7 @@ impl BoxPainting { pub fn ui(&mut self, ui: &mut Ui) { ui.add(Slider::new(&mut self.size.x, 0.0..=500.0).text("width")); ui.add(Slider::new(&mut self.size.y, 0.0..=500.0).text("height")); - ui.add(Slider::new(&mut self.rounding, 0.0..=50.0).text("rounding")); + ui.add(Slider::new(&mut self.corner_radius, 0.0..=50.0).text("corner_radius")); ui.add(Slider::new(&mut self.stroke_width, 0.0..=10.0).text("stroke_width")); ui.add(Slider::new(&mut self.num_boxes, 0..=8).text("num_boxes")); @@ -387,7 +387,7 @@ impl BoxPainting { let (rect, _response) = ui.allocate_at_least(self.size, Sense::hover()); ui.painter().rect( rect, - self.rounding, + self.corner_radius, ui.visuals().text_color().gamma_multiply(0.5), Stroke::new(self.stroke_width, Color32::WHITE), egui::StrokeKind::Inside, diff --git a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs index 033861016..8c5a1adc3 100644 --- a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs +++ b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs @@ -271,7 +271,7 @@ fn rect_shape_ui(ui: &mut egui::Ui, shape: &mut RectShape) { let RectShape { rect, - rounding, + corner_radius, fill, stroke, stroke_kind, @@ -304,8 +304,8 @@ fn rect_shape_ui(ui: &mut egui::Ui, shape: &mut RectShape) { }); ui.end_row(); - ui.label("Rounding"); - ui.add(rounding); + ui.label("Corner radius"); + ui.add(corner_radius); ui.end_row(); ui.label("Fill"); diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 3791d77a6..547c65bee 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f90f94a842a1d0f1386c3cdd28e60e6ad6efe968f510a11bde418b5bc70a81d2 -size 23897 +oid sha256:244d539111e994a4ed2aa95a2b4d0ff12b948e21843c8a1dddcf54cbb388f1aa +size 24280 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png index 3140fbc94..8e97cb8e3 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c39868f184364555ae90fbfc035aa668f61189be7aeee6bec4e45a8de438ad8e -size 87661 +oid sha256:daa5ec4ddd2f983c4b9f2b0a73c973f58abcb186fbd0d68a9fd0ce7173e5d4e7 +size 88031 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png index 51b0bd901..da1f47816 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd029fdc49e6d4078337472c39b9d58bf69073c1b7750c6dd1b7ccd450d52395 -size 119869 +oid sha256:c09af9c7f4297e2d5b2305366ed57b3203807ca2426314acdf836e25f154d8eb +size 120244 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png index 290216b1b..7704e4ddf 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e698ba12efd129099877248f9630ba983d683e1b495b2523ed3569989341e905 -size 51735 +oid sha256:f621bcf7c8fd18156bef2428ab3b9994e6a4d475ae589eb734d50f9a4f3383bd +size 52101 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png index ff42489e8..0fcb1aeb4 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18b81b5cd88372b65b1ecc62e9a5e894960279310b05a1bd5c8df5bffa244ad0 -size 54922 +oid sha256:f1476e105a3e9c1ff7b2f4a82481462795e4708e3fcf6d495a042faae537184e +size 55298 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png index 8fa9370ff..4df96a3ec 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf5df173431d330e4b6045a72227c2bb7613ec98c63f013ea899a3a57cd6617a -size 55522 +oid sha256:695a731d9e302db2c5b7f4a0ef44794cf55eb0be093c070b8ffaeb28121569bc +size 55888 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png index 882691e82..c45845c66 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:987c162842a08271e833c41a55573d9f30cf045bf7ca3cb03e81d0cc13d5a16e -size 36763 +oid sha256:854c12c69b31c0c82a9596d167772e00d7a051600e4151535e2cec04491e57a6 +size 37139 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png index 6741df537..9cf019ea1 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c5c3055cd190823a4204aa6f23362a88bc5ab5ed5453d9be1b6077dded6cd54 -size 36809 +oid sha256:1aacb27847c2e942d56d60e4b72797d93f7265a6effe4e47437e1314e6477e6f +size 37184 diff --git a/crates/egui_extras/src/layout.rs b/crates/egui_extras/src/layout.rs index 0121f83e0..fb84169f3 100644 --- a/crates/egui_extras/src/layout.rs +++ b/crates/egui_extras/src/layout.rs @@ -128,7 +128,7 @@ impl<'l> StripLayout<'l> { if flags.striped { self.ui.painter().rect_filled( gapless_rect, - egui::Rounding::ZERO, + egui::CornerRadius::ZERO, self.ui.visuals().faint_bg_color, ); } @@ -136,7 +136,7 @@ impl<'l> StripLayout<'l> { if flags.selected { self.ui.painter().rect_filled( gapless_rect, - egui::Rounding::ZERO, + egui::CornerRadius::ZERO, self.ui.visuals().selection.bg_fill, ); } @@ -144,7 +144,7 @@ impl<'l> StripLayout<'l> { if flags.hovered && !flags.selected && self.sense.interactive() { self.ui.painter().rect_filled( gapless_rect, - egui::Rounding::ZERO, + egui::CornerRadius::ZERO, self.ui.visuals().widgets.hovered.bg_fill, ); } diff --git a/crates/epaint/src/rounding.rs b/crates/epaint/src/corner_radius.rs similarity index 88% rename from crates/epaint/src/rounding.rs rename to crates/epaint/src/corner_radius.rs index e0d79b14c..07bd56c9e 100644 --- a/crates/epaint/src/rounding.rs +++ b/crates/epaint/src/corner_radius.rs @@ -7,10 +7,10 @@ /// The rounding uses `u8` to save space, /// so the amount of rounding is limited to integers in the range `[0, 255]`. /// -/// For calculations, you may want to use [`crate::Roundingf`] instead, which uses `f32`. +/// For calculations, you may want to use [`crate::CornerRadiusF32`] instead, which uses `f32`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Rounding { +pub struct CornerRadius { /// Radius of the rounding of the North-West (left top) corner. pub nw: u8, @@ -24,28 +24,28 @@ pub struct Rounding { pub se: u8, } -impl Default for Rounding { +impl Default for CornerRadius { #[inline] fn default() -> Self { Self::ZERO } } -impl From for Rounding { +impl From for CornerRadius { #[inline] fn from(radius: u8) -> Self { Self::same(radius) } } -impl From for Rounding { +impl From for CornerRadius { #[inline] fn from(radius: f32) -> Self { Self::same(radius.round() as u8) } } -impl Rounding { +impl CornerRadius { /// No rounding on any corner. pub const ZERO: Self = Self { nw: 0, @@ -99,7 +99,7 @@ impl Rounding { } } -impl std::ops::Add for Rounding { +impl std::ops::Add for CornerRadius { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { @@ -112,7 +112,7 @@ impl std::ops::Add for Rounding { } } -impl std::ops::Add for Rounding { +impl std::ops::Add for CornerRadius { type Output = Self; #[inline] fn add(self, rhs: u8) -> Self { @@ -125,7 +125,7 @@ impl std::ops::Add for Rounding { } } -impl std::ops::AddAssign for Rounding { +impl std::ops::AddAssign for CornerRadius { #[inline] fn add_assign(&mut self, rhs: Self) { *self = Self { @@ -137,7 +137,7 @@ impl std::ops::AddAssign for Rounding { } } -impl std::ops::AddAssign for Rounding { +impl std::ops::AddAssign for CornerRadius { #[inline] fn add_assign(&mut self, rhs: u8) { *self = Self { @@ -149,7 +149,7 @@ impl std::ops::AddAssign for Rounding { } } -impl std::ops::Sub for Rounding { +impl std::ops::Sub for CornerRadius { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { @@ -162,7 +162,7 @@ impl std::ops::Sub for Rounding { } } -impl std::ops::Sub for Rounding { +impl std::ops::Sub for CornerRadius { type Output = Self; #[inline] fn sub(self, rhs: u8) -> Self { @@ -175,7 +175,7 @@ impl std::ops::Sub for Rounding { } } -impl std::ops::SubAssign for Rounding { +impl std::ops::SubAssign for CornerRadius { #[inline] fn sub_assign(&mut self, rhs: Self) { *self = Self { @@ -187,7 +187,7 @@ impl std::ops::SubAssign for Rounding { } } -impl std::ops::SubAssign for Rounding { +impl std::ops::SubAssign for CornerRadius { #[inline] fn sub_assign(&mut self, rhs: u8) { *self = Self { @@ -199,7 +199,7 @@ impl std::ops::SubAssign for Rounding { } } -impl std::ops::Div for Rounding { +impl std::ops::Div for CornerRadius { type Output = Self; #[inline] fn div(self, rhs: f32) -> Self { @@ -212,7 +212,7 @@ impl std::ops::Div for Rounding { } } -impl std::ops::DivAssign for Rounding { +impl std::ops::DivAssign for CornerRadius { #[inline] fn div_assign(&mut self, rhs: f32) { *self = Self { @@ -224,7 +224,7 @@ impl std::ops::DivAssign for Rounding { } } -impl std::ops::Mul for Rounding { +impl std::ops::Mul for CornerRadius { type Output = Self; #[inline] fn mul(self, rhs: f32) -> Self { @@ -237,7 +237,7 @@ impl std::ops::Mul for Rounding { } } -impl std::ops::MulAssign for Rounding { +impl std::ops::MulAssign for CornerRadius { #[inline] fn mul_assign(&mut self, rhs: f32) { *self = Self { diff --git a/crates/epaint/src/roundingf.rs b/crates/epaint/src/corner_radius_f32.rs similarity index 79% rename from crates/epaint/src/roundingf.rs rename to crates/epaint/src/corner_radius_f32.rs index b49cbc77f..0a88aaac7 100644 --- a/crates/epaint/src/roundingf.rs +++ b/crates/epaint/src/corner_radius_f32.rs @@ -1,11 +1,11 @@ -use crate::Rounding; +use crate::CornerRadius; /// How rounded the corners of things should be, in `f32`. /// -/// This is used for calculations, but storage is usually done with the more compact [`Rounding`]. +/// This is used for calculations, but storage is usually done with the more compact [`CornerRadius`]. #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Roundingf { +pub struct CornerRadiusF32 { /// Radius of the rounding of the North-West (left top) corner. pub nw: f32, @@ -19,38 +19,38 @@ pub struct Roundingf { pub se: f32, } -impl From for Roundingf { +impl From for CornerRadiusF32 { #[inline] - fn from(rounding: Rounding) -> Self { + fn from(cr: CornerRadius) -> Self { Self { - nw: rounding.nw as f32, - ne: rounding.ne as f32, - sw: rounding.sw as f32, - se: rounding.se as f32, + nw: cr.nw as f32, + ne: cr.ne as f32, + sw: cr.sw as f32, + se: cr.se as f32, } } } -impl From for Rounding { +impl From for CornerRadius { #[inline] - fn from(rounding: Roundingf) -> Self { + fn from(cr: CornerRadiusF32) -> Self { Self { - nw: rounding.nw.round() as u8, - ne: rounding.ne.round() as u8, - sw: rounding.sw.round() as u8, - se: rounding.se.round() as u8, + nw: cr.nw.round() as u8, + ne: cr.ne.round() as u8, + sw: cr.sw.round() as u8, + se: cr.se.round() as u8, } } } -impl Default for Roundingf { +impl Default for CornerRadiusF32 { #[inline] fn default() -> Self { Self::ZERO } } -impl From for Roundingf { +impl From for CornerRadiusF32 { #[inline] fn from(radius: f32) -> Self { Self { @@ -62,7 +62,7 @@ impl From for Roundingf { } } -impl Roundingf { +impl CornerRadiusF32 { /// No rounding on any corner. pub const ZERO: Self = Self { nw: 0.0, @@ -111,7 +111,7 @@ impl Roundingf { } } -impl std::ops::Add for Roundingf { +impl std::ops::Add for CornerRadiusF32 { type Output = Self; #[inline] fn add(self, rhs: Self) -> Self { @@ -124,7 +124,7 @@ impl std::ops::Add for Roundingf { } } -impl std::ops::AddAssign for Roundingf { +impl std::ops::AddAssign for CornerRadiusF32 { #[inline] fn add_assign(&mut self, rhs: Self) { *self = Self { @@ -136,7 +136,7 @@ impl std::ops::AddAssign for Roundingf { } } -impl std::ops::AddAssign for Roundingf { +impl std::ops::AddAssign for CornerRadiusF32 { #[inline] fn add_assign(&mut self, rhs: f32) { *self = Self { @@ -148,7 +148,7 @@ impl std::ops::AddAssign for Roundingf { } } -impl std::ops::Sub for Roundingf { +impl std::ops::Sub for CornerRadiusF32 { type Output = Self; #[inline] fn sub(self, rhs: Self) -> Self { @@ -161,7 +161,7 @@ impl std::ops::Sub for Roundingf { } } -impl std::ops::SubAssign for Roundingf { +impl std::ops::SubAssign for CornerRadiusF32 { #[inline] fn sub_assign(&mut self, rhs: Self) { *self = Self { @@ -173,7 +173,7 @@ impl std::ops::SubAssign for Roundingf { } } -impl std::ops::SubAssign for Roundingf { +impl std::ops::SubAssign for CornerRadiusF32 { #[inline] fn sub_assign(&mut self, rhs: f32) { *self = Self { @@ -185,7 +185,7 @@ impl std::ops::SubAssign for Roundingf { } } -impl std::ops::Div for Roundingf { +impl std::ops::Div for CornerRadiusF32 { type Output = Self; #[inline] fn div(self, rhs: f32) -> Self { @@ -198,7 +198,7 @@ impl std::ops::Div for Roundingf { } } -impl std::ops::DivAssign for Roundingf { +impl std::ops::DivAssign for CornerRadiusF32 { #[inline] fn div_assign(&mut self, rhs: f32) { *self = Self { @@ -210,7 +210,7 @@ impl std::ops::DivAssign for Roundingf { } } -impl std::ops::Mul for Roundingf { +impl std::ops::Mul for CornerRadiusF32 { type Output = Self; #[inline] fn mul(self, rhs: f32) -> Self { @@ -223,7 +223,7 @@ impl std::ops::Mul for Roundingf { } } -impl std::ops::MulAssign for Roundingf { +impl std::ops::MulAssign for CornerRadiusF32 { #[inline] fn mul_assign(&mut self, rhs: f32) { *self = Self { diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index b1d0045ee..ac0a90c60 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -25,13 +25,13 @@ mod brush; pub mod color; +mod corner_radius; +mod corner_radius_f32; pub mod image; mod margin; mod marginf; mod mesh; pub mod mutex; -mod rounding; -mod roundingf; mod shadow; pub mod shape_transform; mod shapes; @@ -48,12 +48,12 @@ mod viewport; pub use self::{ brush::Brush, color::ColorMode, + corner_radius::CornerRadius, + corner_radius_f32::CornerRadiusF32, image::{ColorImage, FontImage, ImageData, ImageDelta}, margin::Margin, marginf::Marginf, mesh::{Mesh, Mesh16, Vertex}, - rounding::Rounding, - roundingf::Roundingf, shadow::Shadow, shapes::{ CircleShape, CubicBezierShape, EllipseShape, PaintCallback, PaintCallbackInfo, PathShape, @@ -69,6 +69,9 @@ pub use self::{ viewport::ViewportInPixels, }; +#[deprecated = "Renamed to CornerRadius"] +pub type Rounding = CornerRadius; + #[allow(deprecated)] pub use tessellator::tessellate_shapes; diff --git a/crates/epaint/src/shadow.rs b/crates/epaint/src/shadow.rs index 959049ace..e05cbdbe4 100644 --- a/crates/epaint/src/shadow.rs +++ b/crates/epaint/src/shadow.rs @@ -1,4 +1,4 @@ -use crate::{Color32, Marginf, Rect, RectShape, Rounding, Vec2}; +use crate::{Color32, CornerRadius, Marginf, Rect, RectShape, Vec2}; /// The color and fuzziness of a fuzzy shape. /// @@ -44,7 +44,7 @@ impl Shadow { }; /// The argument is the rectangle of the shadow caster. - pub fn as_shape(&self, rect: Rect, rounding: impl Into) -> RectShape { + pub fn as_shape(&self, rect: Rect, corner_radius: impl Into) -> RectShape { // tessellator.clip_rect = clip_rect; // TODO(emilk): culling let Self { @@ -58,9 +58,9 @@ impl Shadow { let rect = rect .translate(Vec2::new(offset_x as _, offset_y as _)) .expand(spread as _); - let rounding = rounding.into() + Rounding::from(spread); + let corner_radius = corner_radius.into() + CornerRadius::from(spread); - RectShape::filled(rect, rounding, color).with_blur_width(blur as _) + RectShape::filled(rect, corner_radius, color).with_blur_width(blur as _) } /// How much larger than the parent rect are we in each direction? diff --git a/crates/epaint/src/shape_transform.rs b/crates/epaint/src/shape_transform.rs index 45805a276..469f2e521 100644 --- a/crates/epaint/src/shape_transform.rs +++ b/crates/epaint/src/shape_transform.rs @@ -60,7 +60,7 @@ pub fn adjust_colors( }) | Shape::Rect(RectShape { rect: _, - rounding: _, + corner_radius: _, fill, stroke, stroke_kind: _, diff --git a/crates/epaint/src/shapes/rect_shape.rs b/crates/epaint/src/shapes/rect_shape.rs index cd54dc8e9..ead5b7af2 100644 --- a/crates/epaint/src/shapes/rect_shape.rs +++ b/crates/epaint/src/shapes/rect_shape.rs @@ -10,7 +10,7 @@ pub struct RectShape { /// How rounded the corners of the rectangle are. /// - /// Use `Rounding::ZERO` for for sharp corners. + /// Use [`CornerRadius::ZERO`] for for sharp corners. /// /// This is the corner radii of the rectangle. /// If there is a stroke, then the stroke will have an inner and outer corner radius, @@ -18,7 +18,7 @@ pub struct RectShape { /// /// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle, /// so the rounding will in this case specify the outer corner radius. - pub rounding: Rounding, + pub corner_radius: CornerRadius, /// How to fill the rectangle. pub fill: Color32, @@ -73,14 +73,14 @@ impl RectShape { #[inline] pub fn new( rect: Rect, - rounding: impl Into, + corner_radius: impl Into, fill_color: impl Into, stroke: impl Into, stroke_kind: StrokeKind, ) -> Self { Self { rect, - rounding: rounding.into(), + corner_radius: corner_radius.into(), fill: fill_color.into(), stroke: stroke.into(), stroke_kind, @@ -93,12 +93,12 @@ impl RectShape { #[inline] pub fn filled( rect: Rect, - rounding: impl Into, + corner_radius: impl Into, fill_color: impl Into, ) -> Self { Self::new( rect, - rounding, + corner_radius, fill_color, Stroke::NONE, StrokeKind::Outside, // doesn't matter @@ -108,12 +108,12 @@ impl RectShape { #[inline] pub fn stroke( rect: Rect, - rounding: impl Into, + corner_radius: impl Into, stroke: impl Into, stroke_kind: StrokeKind, ) -> Self { let fill = Color32::TRANSPARENT; - Self::new(rect, rounding, fill, stroke, stroke_kind) + Self::new(rect, corner_radius, fill, stroke, stroke_kind) } /// Set if the stroke is on the inside, outside, or centered on the rectangle. diff --git a/crates/epaint/src/shapes/shape.rs b/crates/epaint/src/shapes/shape.rs index ddbaacfd3..6c24881de 100644 --- a/crates/epaint/src/shapes/shape.rs +++ b/crates/epaint/src/shapes/shape.rs @@ -7,7 +7,7 @@ use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2}; use crate::{ stroke::PathStroke, text::{FontId, Fonts, Galley}, - Color32, Mesh, Rounding, Stroke, StrokeKind, TextureId, + Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId, }; use super::{ @@ -279,21 +279,21 @@ impl Shape { #[inline] pub fn rect_filled( rect: Rect, - rounding: impl Into, + corner_radius: impl Into, fill_color: impl Into, ) -> Self { - Self::Rect(RectShape::filled(rect, rounding, fill_color)) + Self::Rect(RectShape::filled(rect, corner_radius, fill_color)) } /// See also [`Self::rect_filled`]. #[inline] pub fn rect_stroke( rect: Rect, - rounding: impl Into, + corner_radius: impl Into, stroke: impl Into, stroke_kind: StrokeKind, ) -> Self { - Self::Rect(RectShape::stroke(rect, rounding, stroke, stroke_kind)) + Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind)) } #[allow(clippy::needless_pass_by_value)] @@ -451,7 +451,7 @@ impl Shape { } Self::Rect(rect_shape) => { rect_shape.rect = transform * rect_shape.rect; - rect_shape.rounding *= transform.scaling; + rect_shape.corner_radius *= transform.scaling; rect_shape.stroke.width *= transform.scaling; rect_shape.blur_width *= transform.scaling; } diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index b16d22f52..608317dc6 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -9,8 +9,8 @@ use emath::{pos2, remap, vec2, GuiRounding as _, NumExt, Pos2, Rect, Rot2, Vec2} use crate::{ color::ColorMode, emath, stroke::PathStroke, texture_atlas::PreparedDisc, CircleShape, - ClippedPrimitive, ClippedShape, Color32, CubicBezierShape, EllipseShape, Mesh, PathShape, - Primitive, QuadraticBezierShape, RectShape, Roundingf, Shape, Stroke, StrokeKind, TextShape, + ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape, EllipseShape, Mesh, + PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke, StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, }; @@ -534,19 +534,19 @@ impl Path { pub mod path { //! Helpers for constructing paths - use crate::Roundingf; + use crate::CornerRadiusF32; use emath::{pos2, Pos2, Rect}; /// overwrites existing points - pub fn rounded_rectangle(path: &mut Vec, rect: Rect, rounding: Roundingf) { + pub fn rounded_rectangle(path: &mut Vec, rect: Rect, cr: CornerRadiusF32) { path.clear(); let min = rect.min; let max = rect.max; - let r = clamp_rounding(rounding, rect); + let cr = clamp_corner_radius(cr, rect); - if r == Roundingf::ZERO { + if cr == CornerRadiusF32::ZERO { path.reserve(4); path.push(pos2(min.x, min.y)); // left top path.push(pos2(max.x, min.y)); // right top @@ -557,27 +557,27 @@ pub mod path { // Duplicated vertices can happen when one side is all rounding, with no straight edge between. let eps = f32::EPSILON * rect.size().max_elem(); - add_circle_quadrant(path, pos2(max.x - r.se, max.y - r.se), r.se, 0.0); // south east + add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); // south east - if rect.width() <= r.se + r.sw + eps { + if rect.width() <= cr.se + cr.sw + eps { path.pop(); // avoid duplicated vertex } - add_circle_quadrant(path, pos2(min.x + r.sw, max.y - r.sw), r.sw, 1.0); // south west + add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); // south west - if rect.height() <= r.sw + r.nw + eps { + if rect.height() <= cr.sw + cr.nw + eps { path.pop(); // avoid duplicated vertex } - add_circle_quadrant(path, pos2(min.x + r.nw, min.y + r.nw), r.nw, 2.0); // north west + add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); // north west - if rect.width() <= r.nw + r.ne + eps { + if rect.width() <= cr.nw + cr.ne + eps { path.pop(); // avoid duplicated vertex } - add_circle_quadrant(path, pos2(max.x - r.ne, min.y + r.ne), r.ne, 3.0); // north east + add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); // north east - if rect.height() <= r.ne + r.se + eps { + if rect.height() <= cr.ne + cr.se + eps { path.pop(); // avoid duplicated vertex } } @@ -633,11 +633,11 @@ pub mod path { } // Ensures the radius of each corner is within a valid range - fn clamp_rounding(rounding: Roundingf, rect: Rect) -> Roundingf { + fn clamp_corner_radius(cr: CornerRadiusF32, rect: Rect) -> CornerRadiusF32 { let half_width = rect.width() * 0.5; let half_height = rect.height() * 0.5; let max_cr = half_width.min(half_height); - rounding.at_most(max_cr).at_least(0.0) + cr.at_most(max_cr).at_least(0.0) } } @@ -1729,7 +1729,7 @@ impl Tessellator { let brush = rect_shape.brush.as_ref(); let RectShape { mut rect, - rounding, + corner_radius, mut fill, mut stroke, mut stroke_kind, @@ -1738,7 +1738,7 @@ impl Tessellator { brush: _, // brush is extracted on its own, because it is not Copy } = *rect_shape; - let mut rounding = Roundingf::from(rounding); + let mut corner_radius = CornerRadiusF32::from(corner_radius); let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels); let pixel_size = 1.0 / self.pixels_per_point; @@ -1848,7 +1848,7 @@ impl Tessellator { .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width) .at_least(0.0); - rounding += 0.5 * blur_width; + corner_radius += 0.5 * blur_width; self.feathering = self.feathering.max(blur_width); } @@ -1858,54 +1858,54 @@ impl Tessellator { // We do this because `path::rounded_rectangle` uses the // corner radius to pick the fidelity/resolution of the corner. - let original_rounding = rounding; + let original_cr = corner_radius; match stroke_kind { StrokeKind::Inside => {} StrokeKind::Middle => { rect = rect.expand(stroke.width / 2.0); - rounding += stroke.width / 2.0; + corner_radius += stroke.width / 2.0; } StrokeKind::Outside => { rect = rect.expand(stroke.width); - rounding += stroke.width; + corner_radius += stroke.width; } } stroke_kind = StrokeKind::Inside; - // A small rounding is incompatible with a wide stroke, + // A small corner_radius is incompatible with a wide stroke, // because the small bend will be extruded inwards and cross itself. // There are two ways to solve this (wile maintaining constant stroke width): - // either we increase the rounding, or we set it to zero. - // We choose the former: if the user asks for _any_ rounding, they should get it. + // either we increase the corner_radius, or we set it to zero. + // We choose the former: if the user asks for _any_ corner_radius, they should get it. - let min_inside_rounding = 0.1; // Large enough to avoid numerical issues - let min_outside_rounding = stroke.width + min_inside_rounding; + let min_inside_cr = 0.1; // Large enough to avoid numerical issues + let min_outside_cr = stroke.width + min_inside_cr; - let extra_rounding_tweak = 0.4; // Otherwise is doesn't _feels_ enough. + let extra_cr_tweak = 0.4; // Otherwise is doesn't _feels_ enough. - if 0.0 < original_rounding.nw { - rounding.nw += extra_rounding_tweak; - rounding.nw = rounding.nw.at_least(min_outside_rounding); + if 0.0 < original_cr.nw { + corner_radius.nw += extra_cr_tweak; + corner_radius.nw = corner_radius.nw.at_least(min_outside_cr); } - if 0.0 < original_rounding.ne { - rounding.ne += extra_rounding_tweak; - rounding.ne = rounding.ne.at_least(min_outside_rounding); + if 0.0 < original_cr.ne { + corner_radius.ne += extra_cr_tweak; + corner_radius.ne = corner_radius.ne.at_least(min_outside_cr); } - if 0.0 < original_rounding.sw { - rounding.sw += extra_rounding_tweak; - rounding.sw = rounding.sw.at_least(min_outside_rounding); + if 0.0 < original_cr.sw { + corner_radius.sw += extra_cr_tweak; + corner_radius.sw = corner_radius.sw.at_least(min_outside_cr); } - if 0.0 < original_rounding.se { - rounding.se += extra_rounding_tweak; - rounding.se = rounding.se.at_least(min_outside_rounding); + if 0.0 < original_cr.se { + corner_radius.se += extra_cr_tweak; + corner_radius.se = corner_radius.se.at_least(min_outside_cr); } } let path = &mut self.scratchpad_path; path.clear(); - path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding); + path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius); path.add_line_loop(&self.scratchpad_points); let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind); diff --git a/examples/custom_window_frame/src/main.rs b/examples/custom_window_frame/src/main.rs index aefcdc677..eef0db002 100644 --- a/examples/custom_window_frame/src/main.rs +++ b/examples/custom_window_frame/src/main.rs @@ -47,7 +47,7 @@ fn custom_window_frame(ctx: &egui::Context, title: &str, add_contents: impl FnOn let panel_frame = egui::Frame::new() .fill(ctx.style().visuals.window_fill()) - .rounding(10) + .corner_radius(10) .stroke(ctx.style().visuals.widgets.noninteractive.fg_stroke) .outer_margin(1); // so the stroke is within the bounds diff --git a/examples/images/src/main.rs b/examples/images/src/main.rs index a8373774a..d7ccfd7b4 100644 --- a/examples/images/src/main.rs +++ b/examples/images/src/main.rs @@ -35,7 +35,7 @@ impl eframe::App for MyApp { .on_hover_text_at_pointer("Svg"); let url = "https://picsum.photos/seed/1.759706314/1024"; - ui.add(egui::Image::new(url).rounding(10.0)) + ui.add(egui::Image::new(url).corner_radius(10)) .on_hover_text_at_pointer(url); }); }); diff --git a/tests/test_viewports/src/main.rs b/tests/test_viewports/src/main.rs index 9cdb0b967..3f9964c94 100644 --- a/tests/test_viewports/src/main.rs +++ b/tests/test_viewports/src/main.rs @@ -478,7 +478,13 @@ fn drop_target( ui.painter().set( background_id, - egui::epaint::RectShape::new(rect, style.rounding, fill, stroke, egui::StrokeKind::Inside), + egui::epaint::RectShape::new( + rect, + style.corner_radius, + fill, + stroke, + egui::StrokeKind::Inside, + ), ); egui::InnerResponse::new(ret, response) From b8051cc3014f06c02f826f68db966b4975b67a06 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 4 Feb 2025 14:01:32 +0100 Subject: [PATCH 12/22] Add `SnapshotResults` struct to egui_kittest (#5672) I got annoyed by all the slightly different variations of "collect snapshot results and unwrap them at the end of test" I've written, so I added a struct to make this nice and simple. One controversial thing: It panics when dropped. I wanted to ensure people cannot forget to unwrap the results at the end, and this was the best thing I could come up with. I don't think this is possible via clippy lint or something like that. * [x] I have followed the instructions in the PR template --- .github/workflows/rust.yml | 4 +- crates/egui/src/painter.rs | 1 - crates/egui_demo_app/tests/test_demo_app.rs | 11 +- .../src/demo/demo_app_windows.rs | 11 +- crates/egui_demo_lib/src/demo/modals.rs | 14 +-- .../egui_demo_lib/src/demo/widget_gallery.rs | 1 + crates/egui_demo_lib/src/rendering_test.rs | 10 +- crates/egui_kittest/src/snapshot.rs | 116 +++++++++++++++++- crates/egui_kittest/tests/regression_tests.rs | 14 +-- crates/egui_kittest/tests/tests.rs | 6 +- 10 files changed, 135 insertions(+), 53 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f0e3b3a72..eadce83be 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,7 +9,7 @@ env: jobs: fmt-crank-check-test: - name: Format + check + test + name: Format + check runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -223,7 +223,7 @@ jobs: tests: name: Run tests - # We run the tests on macOS because it will run with a actual GPU + # We run the tests on macOS because it will run with an actual GPU runs-on: macos-latest steps: diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index aef97cccf..22a0a0a9d 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -435,7 +435,6 @@ impl Painter { self.add(RectShape::filled(rect, corner_radius, fill_color)) } - /// The stroke extends _outside_ the [`Rect`]. pub fn rect_stroke( &self, rect: Rect, diff --git a/crates/egui_demo_app/tests/test_demo_app.rs b/crates/egui_demo_app/tests/test_demo_app.rs index fc4940fb9..0247b9fc2 100644 --- a/crates/egui_demo_app/tests/test_demo_app.rs +++ b/crates/egui_demo_app/tests/test_demo_app.rs @@ -2,6 +2,7 @@ use egui::accesskit::Role; use egui::Vec2; use egui_demo_app::{Anchor, WrapApp}; use egui_kittest::kittest::Queryable; +use egui_kittest::SnapshotResults; #[test] fn test_demo_app() { @@ -27,7 +28,7 @@ fn test_demo_app() { "Expected to find the Custom3d app.", ); - let mut results = vec![]; + let mut results = SnapshotResults::new(); for (name, anchor) in apps { harness.get_by_role_and_label(Role::Button, name).click(); @@ -68,12 +69,6 @@ fn test_demo_app() { // Can't use Harness::run because fractal clock keeps requesting repaints harness.run_steps(2); - if let Err(e) = harness.try_snapshot(&anchor.to_string()) { - results.push(e); - } - } - - if let Some(error) = results.first() { - panic!("{error}"); + results.add(harness.try_snapshot(&anchor.to_string())); } } diff --git a/crates/egui_demo_lib/src/demo/demo_app_windows.rs b/crates/egui_demo_lib/src/demo/demo_app_windows.rs index 27f862ad0..6f753c3a4 100644 --- a/crates/egui_demo_lib/src/demo/demo_app_windows.rs +++ b/crates/egui_demo_lib/src/demo/demo_app_windows.rs @@ -366,13 +366,13 @@ mod tests { use crate::{demo::demo_app_windows::DemoGroups, Demo}; use egui::Vec2; use egui_kittest::kittest::Queryable; - use egui_kittest::{Harness, SnapshotOptions}; + use egui_kittest::{Harness, SnapshotOptions, SnapshotResults}; #[test] fn demos_should_match_snapshot() { let demos = DemoGroups::default().demos; - let mut errors = Vec::new(); + let mut results = SnapshotResults::new(); for mut demo in demos.demos { // Widget Gallery needs to be customized (to set a specific date) and has its own test @@ -406,12 +406,7 @@ mod tests { options.threshold = 2.1; } - let result = harness.try_snapshot_options(&format!("demos/{name}"), &options); - if let Err(err) = result { - errors.push(err.to_string()); - } + results.add(harness.try_snapshot_options(&format!("demos/{name}"), &options)); } - - assert!(errors.is_empty(), "Errors: {errors:#?}"); } } diff --git a/crates/egui_demo_lib/src/demo/modals.rs b/crates/egui_demo_lib/src/demo/modals.rs index 833e07a28..d344d99c0 100644 --- a/crates/egui_demo_lib/src/demo/modals.rs +++ b/crates/egui_demo_lib/src/demo/modals.rs @@ -165,7 +165,7 @@ mod tests { use egui::accesskit::Role; use egui::Key; use egui_kittest::kittest::Queryable; - use egui_kittest::Harness; + use egui_kittest::{Harness, SnapshotResults}; #[test] fn clicking_escape_when_popup_open_should_not_close_modal() { @@ -233,22 +233,18 @@ mod tests { initial_state, ); - let mut results = Vec::new(); + let mut results = SnapshotResults::new(); harness.run(); - results.push(harness.try_snapshot("modals_1")); + results.add(harness.try_snapshot("modals_1")); harness.get_by_label("Save").click(); harness.run_ok(); - results.push(harness.try_snapshot("modals_2")); + results.add(harness.try_snapshot("modals_2")); harness.get_by_label("Yes Please").click(); harness.run_ok(); - results.push(harness.try_snapshot("modals_3")); - - for result in results { - result.unwrap(); - } + results.add(harness.try_snapshot("modals_3")); } // This tests whether the backdrop actually prevents interaction with lower layers. diff --git a/crates/egui_demo_lib/src/demo/widget_gallery.rs b/crates/egui_demo_lib/src/demo/widget_gallery.rs index c51a9efd8..eae07ccd5 100644 --- a/crates/egui_demo_lib/src/demo/widget_gallery.rs +++ b/crates/egui_demo_lib/src/demo/widget_gallery.rs @@ -23,6 +23,7 @@ pub struct WidgetGallery { #[cfg_attr(feature = "serde", serde(skip))] date: Option, + #[cfg(feature = "chrono")] with_date_button: bool, } diff --git a/crates/egui_demo_lib/src/rendering_test.rs b/crates/egui_demo_lib/src/rendering_test.rs index d43116e0b..e83e643bb 100644 --- a/crates/egui_demo_lib/src/rendering_test.rs +++ b/crates/egui_demo_lib/src/rendering_test.rs @@ -688,10 +688,11 @@ fn mul_color_gamma(left: Color32, right: Color32) -> Color32 { mod tests { use crate::ColorTest; use egui_kittest::kittest::Queryable as _; + use egui_kittest::SnapshotResults; #[test] pub fn rendering_test() { - let mut errors = vec![]; + let mut results = SnapshotResults::new(); for dpi in [1.0, 1.25, 1.5, 1.75, 1.6666667, 2.0] { let mut color_test = ColorTest::default(); let mut harness = egui_kittest::Harness::builder() @@ -708,12 +709,7 @@ mod tests { harness.fit_contents(); - let result = harness.try_snapshot(&format!("rendering_test/dpi_{dpi:.2}")); - if let Err(err) = result { - errors.push(err); - } + results.add(harness.try_snapshot(&format!("rendering_test/dpi_{dpi:.2}"))); } - - assert!(errors.is_empty(), "Errors: {errors:#?}"); } } diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index dc49caec3..86ed053d2 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -4,6 +4,8 @@ use std::fmt::Display; use std::io::ErrorKind; use std::path::PathBuf; +pub type SnapshotResult = Result<(), SnapshotError>; + #[non_exhaustive] pub struct SnapshotOptions { /// The threshold for the image comparison. @@ -189,7 +191,7 @@ pub fn try_image_snapshot_options( new: &image::RgbaImage, name: &str, options: &SnapshotOptions, -) -> Result<(), SnapshotError> { +) -> SnapshotResult { let SnapshotOptions { threshold, output_path, @@ -306,7 +308,7 @@ pub fn try_image_snapshot_options( /// # Errors /// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error /// reading or writing the snapshot. -pub fn try_image_snapshot(current: &image::RgbaImage, name: &str) -> Result<(), SnapshotError> { +pub fn try_image_snapshot(current: &image::RgbaImage, name: &str) -> SnapshotResult { try_image_snapshot_options(current, name, &SnapshotOptions::default()) } @@ -378,7 +380,7 @@ impl Harness<'_, State> { &mut self, name: &str, options: &SnapshotOptions, - ) -> Result<(), SnapshotError> { + ) -> SnapshotResult { let image = self .render() .map_err(|err| SnapshotError::RenderError { err })?; @@ -393,7 +395,7 @@ impl Harness<'_, State> { /// # Errors /// Returns a [`SnapshotError`] if the image does not match the snapshot, if there was an /// error reading or writing the snapshot, if the rendering fails or if no default renderer is available. - pub fn try_snapshot(&mut self, name: &str) -> Result<(), SnapshotError> { + pub fn try_snapshot(&mut self, name: &str) -> SnapshotResult { let image = self .render() .map_err(|err| SnapshotError::RenderError { err })?; @@ -460,7 +462,7 @@ impl Harness<'_, State> { &mut self, name: &str, options: &SnapshotOptions, - ) -> Result<(), SnapshotError> { + ) -> SnapshotResult { self.try_snapshot_options(name, options) } @@ -468,7 +470,7 @@ impl Harness<'_, State> { since = "0.31.0", note = "Use `try_snapshot` instead. This function will be removed in 0.32" )] - pub fn try_wgpu_snapshot(&mut self, name: &str) -> Result<(), SnapshotError> { + pub fn try_wgpu_snapshot(&mut self, name: &str) -> SnapshotResult { self.try_snapshot(name) } @@ -488,3 +490,105 @@ impl Harness<'_, State> { self.snapshot(name); } } + +/// Utility to collect snapshot errors and display them at the end of the test. +/// +/// # Example +/// ``` +/// # let harness = MockHarness; +/// # struct MockHarness; +/// # impl MockHarness { +/// # fn try_snapshot(&self, _: &str) -> Result<(), egui_kittest::SnapshotError> { Ok(()) } +/// # } +/// +/// // [...] Construct a Harness +/// +/// let mut results = egui_kittest::SnapshotResults::new(); +/// +/// // Call add for each snapshot in your test +/// results.add(harness.try_snapshot("my_test")); +/// +/// // If there are any errors, SnapshotResults will panic once dropped. +/// ``` +/// +/// # Panics +/// Panics if there are any errors when dropped (this way it is impossible to forget to call `unwrap`). +/// If you don't want to panic, you can use [`SnapshotResults::into_result`] or [`SnapshotResults::into_inner`]. +/// If you want to panic early, you can use [`SnapshotResults::unwrap`]. +#[derive(Debug, Default)] +pub struct SnapshotResults { + errors: Vec, +} + +impl Display for SnapshotResults { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.errors.is_empty() { + write!(f, "All snapshots passed") + } else { + writeln!(f, "Snapshot errors:")?; + for error in &self.errors { + writeln!(f, " {error}")?; + } + Ok(()) + } + } +} + +impl SnapshotResults { + pub fn new() -> Self { + Default::default() + } + + /// Check if the result is an error and add it to the list of errors. + pub fn add(&mut self, result: SnapshotResult) { + if let Err(err) = result { + self.errors.push(err); + } + } + + /// Check if there are any errors. + pub fn has_errors(&self) -> bool { + !self.errors.is_empty() + } + + /// Convert this into a `Result<(), Self>`. + #[allow(clippy::missing_errors_doc)] + pub fn into_result(self) -> Result<(), Self> { + if self.has_errors() { + Err(self) + } else { + Ok(()) + } + } + + pub fn into_inner(mut self) -> Vec { + std::mem::take(&mut self.errors) + } + + /// Panics if there are any errors, displaying each. + #[allow(clippy::unused_self)] + #[track_caller] + pub fn unwrap(self) { + // Panic is handled in drop + } +} + +impl From for Vec { + fn from(results: SnapshotResults) -> Self { + results.into_inner() + } +} + +impl Drop for SnapshotResults { + #[track_caller] + fn drop(&mut self) { + // Don't panic if we are already panicking (the test probably failed for another reason) + if std::thread::panicking() { + return; + } + #[allow(clippy::manual_assert)] + if self.has_errors() { + panic!("{}", self); + } + } +} diff --git a/crates/egui_kittest/tests/regression_tests.rs b/crates/egui_kittest/tests/regression_tests.rs index 8567e10ea..e7186dcac 100644 --- a/crates/egui_kittest/tests/regression_tests.rs +++ b/crates/egui_kittest/tests/regression_tests.rs @@ -1,6 +1,6 @@ use egui::accesskit::Role; use egui::{Button, ComboBox, Image, Vec2, Widget}; -use egui_kittest::{kittest::Queryable, Harness}; +use egui_kittest::{kittest::Queryable, Harness, SnapshotResults}; #[test] pub fn focus_should_skip_over_disabled_buttons() { @@ -64,10 +64,10 @@ fn test_combobox() { harness.run(); - let mut results = vec![]; + let mut results = SnapshotResults::new(); #[cfg(all(feature = "wgpu", feature = "snapshot"))] - results.push(harness.try_snapshot("combobox_closed")); + results.add(harness.try_snapshot("combobox_closed")); let combobox = harness.get_by_role_and_label(Role::ComboBox, "Select Something"); combobox.click(); @@ -75,7 +75,7 @@ fn test_combobox() { harness.run(); #[cfg(all(feature = "wgpu", feature = "snapshot"))] - results.push(harness.try_snapshot("combobox_opened")); + results.add(harness.try_snapshot("combobox_opened")); let item_2 = harness.get_by_role_and_label(Role::Button, "Item 2"); // Node::click doesn't close the popup, so we use simulate_click @@ -87,10 +87,4 @@ fn test_combobox() { // Popup should be closed now assert!(harness.query_by_label("Item 2").is_none()); - - for result in results { - if let Err(err) = result { - panic!("{}", err); - } - } } diff --git a/crates/egui_kittest/tests/tests.rs b/crates/egui_kittest/tests/tests.rs index bf7d0bdb2..29b4c7b11 100644 --- a/crates/egui_kittest/tests/tests.rs +++ b/crates/egui_kittest/tests/tests.rs @@ -1,4 +1,4 @@ -use egui_kittest::Harness; +use egui_kittest::{Harness, SnapshotResults}; #[test] fn test_shrink() { @@ -10,6 +10,8 @@ fn test_shrink() { harness.fit_contents(); + let mut results = SnapshotResults::new(); + #[cfg(all(feature = "snapshot", feature = "wgpu"))] - harness.snapshot("test_shrink"); + results.add(harness.try_snapshot("test_shrink")); } From c90b97f4ef822d555c8574b1c58aeaa273c0afd2 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 14:36:29 +0100 Subject: [PATCH 13/22] Fix sharp corners for rectangles with StrokeKind != Inside (#5675) Oops! --- .../src/demo/tests/tessellation_test.rs | 13 +++++++++++++ .../tests/snapshots/rendering_test/dpi_1.00.png | 4 ++-- .../tests/snapshots/rendering_test/dpi_1.25.png | 4 ++-- .../tests/snapshots/rendering_test/dpi_1.50.png | 4 ++-- .../tests/snapshots/rendering_test/dpi_1.67.png | 4 ++-- .../tests/snapshots/rendering_test/dpi_1.75.png | 4 ++-- .../tests/snapshots/rendering_test/dpi_2.00.png | 4 ++-- .../tessellation_test/Additive rectangle.png | 3 +++ crates/epaint/src/tessellator.rs | 16 ++++++++++++---- 9 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png diff --git a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs index 8c5a1adc3..58f53560d 100644 --- a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs +++ b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs @@ -93,6 +93,19 @@ impl TessellationTest { ) .with_blur_width(5.0), ), + ( + "Additive rectangle", + RectShape::new( + sized([24.0, 12.0]), + 0.0, + egui::Color32::LIGHT_RED.additive().linear_multiply(0.025), + ( + 1.0, + egui::Color32::LIGHT_BLUE.additive().linear_multiply(0.1), + ), + StrokeKind::Outside, + ), + ), ]; for (_name, shape) in &mut shapes { diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png index b0f087acd..c6d381558 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c6ce16a8a8c34d882485da6bbe08039fb55f90636da8136f68b1bb9baf0effb -size 557610 +oid sha256:c03f90098fb2b2ff846586cc608126580050303b48e7c918b135efcdc6d52686 +size 554947 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png index efa1c8d5b..a6f969040 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a49c052578a46adb41bc02c6d7fdc264ed0ab8ae636cc8a11ac729fe1e48091b -size 791802 +oid sha256:6ee96b64140d91e1b98cbf5dfef9ac5654050c48b3f9d36889f83a62c9ec985a +size 788322 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png index 18b0233d3..a9055f50c 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:989c55a83b8bc7cce4f459d8b835962377927a98f2bce085e92cba8438438ecd -size 943736 +oid sha256:c82611e7b3ec5c7b82e552421aeef6ad5562d649339708ab6283e0c5af3589c6 +size 939339 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png index b3a865457..0cd4d8d88 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01ac61dc5bcecf6bf0d13c8399460b1afae652efe6fecc1d0e4b2f27d9f1c5a4 -size 1046906 +oid sha256:3b315e64464b060c4ef2b508bcb223663e0560871b82c9513fe232f92bd33609 +size 1041890 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png index 1b44fad93..5562fed0b 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3348923ecbad34e385e3ed52ab9b7c88b5d4fc07de00620302d5b191d90a453f -size 1140236 +oid sha256:1dc4d7c51eb301ab3e0679b0247c96997ffa30b763c7b203a91fff706264d929 +size 1134943 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png index f1f12eb1f..0e531d131 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:179c17b0405c6e87bd3cafaa7272e3e6d6eefd462b4406cca2a7abfe8af6f2bd -size 1317569 +oid sha256:92c0615b09916c9a70fc898fbbde2921363044a3bcfcf82410e1d3f6e466bb8a +size 1311678 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png new file mode 100644 index 000000000..dbd8f31d3 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70fd682bf6ba07b235cf2db643e9d050522aeb3e6ef28426c2b3d23c09920d22 +size 46196 diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 608317dc6..0e2dd4c8e 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1885,19 +1885,27 @@ impl Tessellator { let extra_cr_tweak = 0.4; // Otherwise is doesn't _feels_ enough. - if 0.0 < original_cr.nw { + if original_cr.nw == 0.0 { + corner_radius.nw = 0.0; + } else { corner_radius.nw += extra_cr_tweak; corner_radius.nw = corner_radius.nw.at_least(min_outside_cr); } - if 0.0 < original_cr.ne { + if original_cr.ne == 0.0 { + corner_radius.ne = 0.0; + } else { corner_radius.ne += extra_cr_tweak; corner_radius.ne = corner_radius.ne.at_least(min_outside_cr); } - if 0.0 < original_cr.sw { + if original_cr.sw == 0.0 { + corner_radius.sw = 0.0; + } else { corner_radius.sw += extra_cr_tweak; corner_radius.sw = corner_radius.sw.at_least(min_outside_cr); } - if 0.0 < original_cr.se { + if original_cr.se == 0.0 { + corner_radius.se = 0.0; + } else { corner_radius.se += extra_cr_tweak; corner_radius.se = corner_radius.se.at_least(min_outside_cr); } From c6bda9a38c08c8e3e21e2062a2ede5b59d1c6a20 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 15:31:51 +0100 Subject: [PATCH 14/22] Make the ends of vline/hline sharper (#5676) TL;DR: line caps are annoying in two ways: A) we only add them for lines wider than a pixel B) they always make the line longer (if added) --- .../egui_demo_app/tests/snapshots/clock.png | 4 +- .../tests/snapshots/easymarkeditor.png | 4 +- .../tests/snapshots/demos/Bézier Curve.png | 4 +- .../tests/snapshots/demos/Code Editor.png | 4 +- .../tests/snapshots/demos/Code Example.png | 4 +- .../tests/snapshots/demos/Context Menus.png | 4 +- .../tests/snapshots/demos/Dancing Strings.png | 4 +- .../tests/snapshots/demos/Drag and Drop.png | 4 +- .../tests/snapshots/demos/Extra Viewport.png | 4 +- .../tests/snapshots/demos/Font Book.png | 4 +- .../tests/snapshots/demos/Frame.png | 4 +- .../tests/snapshots/demos/Highlighting.png | 4 +- .../snapshots/demos/Interactive Container.png | 4 +- .../tests/snapshots/demos/Misc Demos.png | 4 +- .../tests/snapshots/demos/Modals.png | 4 +- .../tests/snapshots/demos/Multi Touch.png | 4 +- .../tests/snapshots/demos/Painting.png | 4 +- .../tests/snapshots/demos/Panels.png | 4 +- .../tests/snapshots/demos/Scene.png | 4 +- .../tests/snapshots/demos/Screenshot.png | 4 +- .../tests/snapshots/demos/Scrolling.png | 4 +- .../tests/snapshots/demos/Sliders.png | 4 +- .../tests/snapshots/demos/Strip.png | 4 +- .../tests/snapshots/demos/Table.png | 4 +- .../tests/snapshots/demos/Text Layout.png | 4 +- .../tests/snapshots/demos/TextEdit.png | 4 +- .../tests/snapshots/demos/Tooltips.png | 4 +- .../tests/snapshots/demos/Undo Redo.png | 4 +- .../tests/snapshots/demos/Window Options.png | 4 +- .../tests/snapshots/modals_1.png | 4 +- .../tests/snapshots/modals_2.png | 4 +- .../tests/snapshots/modals_3.png | 4 +- ...rop_should_prevent_focusing_lower_area.png | 4 +- .../snapshots/rendering_test/dpi_1.00.png | 4 +- .../snapshots/rendering_test/dpi_1.25.png | 4 +- .../snapshots/rendering_test/dpi_1.50.png | 4 +- .../snapshots/rendering_test/dpi_1.67.png | 4 +- .../snapshots/rendering_test/dpi_1.75.png | 4 +- .../snapshots/rendering_test/dpi_2.00.png | 4 +- .../tessellation_test/Additive rectangle.png | 4 +- .../tessellation_test/Blurred stroke.png | 4 +- .../snapshots/tessellation_test/Blurred.png | 4 +- .../tessellation_test/Minimal rounding.png | 4 +- .../snapshots/tessellation_test/Normal.png | 4 +- .../Thick stroke, minimal rounding.png | 4 +- .../tessellation_test/Thin filled.png | 4 +- .../tessellation_test/Thin stroked.png | 4 +- .../tests/snapshots/test_shrink.png | 4 +- crates/epaint/src/tessellator.rs | 40 ++++++++++++++++++- 49 files changed, 135 insertions(+), 97 deletions(-) diff --git a/crates/egui_demo_app/tests/snapshots/clock.png b/crates/egui_demo_app/tests/snapshots/clock.png index d97230494..80fef31cc 100644 --- a/crates/egui_demo_app/tests/snapshots/clock.png +++ b/crates/egui_demo_app/tests/snapshots/clock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aeab31841dd95b5e0f4bd0af0c0ba49a862d50836dbafdf2172fbbab950c105 -size 327741 +oid sha256:2c15a74a1b1ed3b52a53966a3df2901ca520b92fbfbd10503e32ddb8431e1467 +size 335399 diff --git a/crates/egui_demo_app/tests/snapshots/easymarkeditor.png b/crates/egui_demo_app/tests/snapshots/easymarkeditor.png index 23e1c59b0..014264330 100644 --- a/crates/egui_demo_app/tests/snapshots/easymarkeditor.png +++ b/crates/egui_demo_app/tests/snapshots/easymarkeditor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bcf6e2977bed682d7bdaa0b6a6786e528662dd0791d2e6f83cf1b4852035838 -size 182833 +oid sha256:d8f1046ee5d50d73a17009fd1f11f056b5828fedc62908d00730a6aa77125473 +size 182900 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png index 16adbcdcf..63b57f0df 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23f19871720b67659a7b56cee8a78edc941c4bac86f55efc6fa549f10c4712fb -size 31754 +oid sha256:73b24bec3383450b627683370467b68f0195bd43ce205a44dd49b60cca187b1b +size 31773 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png index 10593d4e0..bf53d1b11 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4d8fee9fd8e69ecd60ebd0dd41c29b61cc13e7013b1d20ad93d40fc4ed1cc03 -size 27091 +oid sha256:062b6e6b6bd7167df24545308afbc3e38ce328fff0b76ca9921180010766f827 +size 27153 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 88cd2ffa3..4131e24f8 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b291e7efd895ab095590285b841903f05dc7d4dadbab7d9001b04a92953c1694 -size 81677 +oid sha256:d6287aa1fd30baee49bd5fdf3fb307f49c2ecdcb5e6a83c309ec4c051d7506a4 +size 81718 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png index 2b46eaf44..877ce849a 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d75230689807ae7fb692bac5c9b33ee04c02b9e54963e2d6ada05860157daf6 -size 11705 +oid sha256:5dfdfe8ed7edf0074b9a4f4dd0f9c27f4c9d7411398c6dba590f032a342ad0e4 +size 11716 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png index ba02ae257..7938ab998 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f512159108c7681834ae2a52c5e1d4eb4dbe678a509f3dab3384e35d6c8dbee2 -size 25865 +oid sha256:16fdf761b370962678b938735d41221b70658d5b003794c71f73327bfd7d2017 +size 25907 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png index 7b47f16ff..d8922e3bf 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2a4f5e67ff6615877794304e8be5a5db647d48e20e4248259179cddefbf4088 -size 20806 +oid sha256:f789cc43f83f01b014b07e93e33ed85ecc0ef323fec404f602ca44ba6fc0ce9e +size 20819 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png index 055f1651f..5eccebf88 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bc474a37d6d3a7a08dd41963bf9009c05315de91bb515cc2b19443b79480bff -size 10723 +oid sha256:baca02791fa90a063dff7f365ce7a0a748f7b892a920cddebd9d2593a8231786 +size 10769 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png index 647e3824f..59efbb6c1 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bff769b3f9e46c5e38170885b2c8301294cbcc1c8ee22c17672205edd509924 -size 133170 +oid sha256:23ddfd103996b7a6d71ada175b714a3d1813ef6f521fea307b5d9cb2908cb638 +size 133212 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 547c65bee..ac6deb6b8 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:244d539111e994a4ed2aa95a2b4d0ff12b948e21843c8a1dddcf54cbb388f1aa -size 24280 +oid sha256:0be2bf06ea69579fe72d03abd0c5cc17fefcac4f403f5e6c6343b8b275080f29 +size 24324 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png index 3c1cf6de8..6bf98330c 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fdbdf1159f0ab4579bf9ceefbd8e40ac7af468368ab11ffec8578214b57d867c -size 17758 +oid sha256:094db03eeea90a1e901c581b19c6ab733f50a396eb9927d82dd558c719404524 +size 17805 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png index a3ef616d9..bb337964b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce647fd9626f126e7f19b4494bb98a2086071f9c45f7dd6ba4708632e7433a7b -size 22418 +oid sha256:234ae929c7f8b3f623b7e09690f8069d98ba72052c08fc58c85fd9a3dfee04a1 +size 22430 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png index 882914ff1..b39b7110a 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a843e6772809a1c904968fac19b49973675e726a3b7727ca1b898ad3b9072b0c -size 64257 +oid sha256:e8251378ba4c74fd29a6aff82517ec6b4c322c5589f85398c6d5e757986a3d83 +size 64300 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png index bf3a487df..2fccd9242 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b437f27c46ddf82e4268d9bb86d33f43c382d1ab3ed45297bc136600cdc9960c -size 32493 +oid sha256:8679ac7ec6e90cfe9164b66a02ce3c9db403df0f20f45211c73b1d9a950d2718 +size 32522 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png index 7ef97c87b..6a4f65e64 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cd0bda8b4d3d7f833273097c8bb52cfd8ea63a405d6798b9aeb7002b143ac74 -size 36459 +oid sha256:05987e9dfd291b0db57d7611c1feb6742198c14546b779975366643d64ef9ea5 +size 36539 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png index 5e5d369a1..cab418b5b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a57c3bf373a283b79188080e2ddf7407c2974655fc5ad59222442e14faae055c -size 17508 +oid sha256:1107f4ab1411e054ecefe19e0de6a7276ec2ab81c2c9221056860a3d62263ebf +size 17596 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png index 3e751d554..72bb68bbf 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:161b5853f528206f2531587753369f2f6f3c52203af668eba0d81a41c2a915e0 -size 264432 +oid sha256:8ca983bab555739e9a2eefb518a8009e1de9638c70efb50edb407cc26973d1eb +size 264478 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index d51bbd358..42e3f7625 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:118413a5ec56c6589914c5cb59bda2884a4d6acec3b34bbc367bc893c3de8127 -size 35409 +oid sha256:0728689de055812e85ccb15e5f4175dd7f9096e610d4ac04256367afa4e036a5 +size 35477 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png index 8cf0ed424..523014112 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:361791f30739f841c46433f5d16d281ba9d0c52027b4cf156c3ac293aa795f46 -size 23592 +oid sha256:ffc6e6011ab774613f1a937dace27129bf9c5e5ee10a3a3cfa4973d82b8e5429 +size 23604 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png index 16b5868cc..2d9d0a5ba 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81338d7b0412989590bc0808419d93788e972e7ab6f70f481f86bd23263a5395 -size 183821 +oid sha256:d84c161d7fd0b1d5670b79e360d6737a3c004998c94835592180a12c055959de +size 183870 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png index 38fe97fed..eec4c34fe 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41c51350c09360738ca284b05c944a11164e4a614beb95ce4cc962222af33d87 -size 117764 +oid sha256:32a9eba39d88023484abf29ab0bd7a29bfc2ba6b1f7ca42496f87a854616f424 +size 117783 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png index 3d586971f..01e6abdd6 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:794475478cfe2fc954731742165b1f0419a0e64616e72b3766c1e031dbba7ca1 -size 26092 +oid sha256:e91609711f26a067de0e9bfd5f6f1680339e8137ab1a8325290ce399857d2797 +size 26135 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png index 2658a2535..eecf9aa0f 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Table.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Table.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7073aa30f3e7dfd116d9a4f02f6c5a075ce068d2e6f43eaede3c4dd6c56f925e -size 70439 +oid sha256:b5171572d2bfee9f44cb8fc6aeba2ff3035823035d27ec42480e4bd49e6f5e63 +size 70524 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png index 48cad20b9..8eae9dc33 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bb3cd05f253c0e109b83a0556b975af7a96d57678e57de3b9fa130cc8a8de1c -size 66318 +oid sha256:f7b21826be83c1a16b9fa7b19130f40261a9735c3eb450eaf9f8d5a3bbfccb7a +size 66365 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png index 6a5551c45..181223e48 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b550769d5ec5d834f89fba56375d10e5f9b3ba703599d90a5b6607aff1c2b06 -size 21194 +oid sha256:7f7986da3f4a3b94e701bf94066896aedb41458f12f6cdaf93a4fd371b1705da +size 21206 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png index ee474166b..af9ee2651 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45f343b55be98976de32bd3c5438212a46b787123ea3096cf8fc6bec99493184 -size 59699 +oid sha256:8fb28c16dc2e4359ada002a5fb98e955db606f44db4bdd1a400ab2e4c43c850e +size 59701 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png index 5e1cb1b35..9f2d64d17 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d25f774320ca844fa4dfba5215ed66f067d0f30c6d7c8ad7ec29d97ee7732e0 -size 13073 +oid sha256:2a8a3e5953a5ee41d95f2a23b580bb9c82e869b7bad0f2611ed866c55bc12b07 +size 13102 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png index 0cd7c200a..2128b9d05 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:540a05c5b1de7e362bcb48a04611323fbc65c8787b8ae852ada8aa145803753d -size 34968 +oid sha256:12fdbe6805dbce0d993236f30ac14d9c03411b6918399ec5bfa7aa39104d0640 +size 35182 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_1.png b/crates/egui_demo_lib/tests/snapshots/modals_1.png index 1c0bdc0d2..177e90cc3 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_1.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2276b8221389da4f644cad43ce446cd7d28f41820cea36fa3ea860808710053d -size 47878 +oid sha256:3d270118b982413bd0c05e9f20a3ee53b88a1681e273f73a81d1733b05c7a006 +size 47889 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_2.png b/crates/egui_demo_lib/tests/snapshots/modals_2.png index 943b4a38a..7e5d7ff82 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_2.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:054d606681e08bf61762e5c7d7596c4f483e66ac889795e4be4956103328e0bb -size 47862 +oid sha256:9f4ad4ac06f2113533943fc00ed9747c237c9b7b6f0890ccf9857e606290c7ec +size 47871 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_3.png b/crates/egui_demo_lib/tests/snapshots/modals_3.png index bc6071dbb..12c338393 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_3.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7fb29285e53b619f333757052d05f86d973680ac1ce6d1b25d28b7824bbce51 -size 43725 +oid sha256:5fee7a7f4ebcda3fd55d1d31e21b46246c4d5f166fb1f0de04265e7db424d9c8 +size 43733 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png index 9a52f0498..5d0da39d8 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be50a396838d4fe29bcfd0807377ad0cda538fea8569acf709da0b13505bf09b -size 43871 +oid sha256:510a3b5d8b10013eed234977bf46575a39cc951611a073154652ce6bf2bc3ecc +size 43880 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png index c6d381558..e1b600fe5 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c03f90098fb2b2ff846586cc608126580050303b48e7c918b135efcdc6d52686 -size 554947 +oid sha256:03ee62427611101758958adf2650a4a0eea4e023f07c9ec4ebc63425233e8a04 +size 554949 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png index a6f969040..ab9c8b818 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ee96b64140d91e1b98cbf5dfef9ac5654050c48b3f9d36889f83a62c9ec985a -size 788322 +oid sha256:82ef265f0e22649c7fcdb9556879c1a30df582bd4e97c647258b3e5acc03d112 +size 771298 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png index a9055f50c..b2d8c3113 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c82611e7b3ec5c7b82e552421aeef6ad5562d649339708ab6283e0c5af3589c6 -size 939339 +oid sha256:cad71b486a479eb9c5339a93f4acc3df2d0b6b188ad023b9b044be7311b0ab72 +size 918775 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png index 0cd4d8d88..2056c3fa0 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b315e64464b060c4ef2b508bcb223663e0560871b82c9513fe232f92bd33609 -size 1041890 +oid sha256:dc9ed4d29f4227b9d38b477ee8f546ea8597acda56a6909ba4826891ebdbea01 +size 1039263 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png index 5562fed0b..586916e56 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dc4d7c51eb301ab3e0679b0247c96997ffa30b763c7b203a91fff706264d929 -size 1134943 +oid sha256:e9bf826bee811d8af345ec1281266fc9bef6d7c3782279516984a6c75130a929 +size 1130895 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png index 0e531d131..b764e7bee 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92c0615b09916c9a70fc898fbbde2921363044a3bcfcf82410e1d3f6e466bb8a -size 1311678 +oid sha256:9345de28f09e2891fd01db20bb0b94176ec3c89d8c2f344a6640d33e97ab5400 +size 1311417 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png index dbd8f31d3..979991d13 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70fd682bf6ba07b235cf2db643e9d050522aeb3e6ef28426c2b3d23c09920d22 -size 46196 +oid sha256:5524138c3cb98aa71ef67083ad2d01813ab2394f93f9a7897f2e465ef5a1d0bc +size 46270 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png index 8e97cb8e3..249b5db4d 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:daa5ec4ddd2f983c4b9f2b0a73c973f58abcb186fbd0d68a9fd0ce7173e5d4e7 -size 88031 +oid sha256:bdf06c41b69eef1eadc8b46020e6e2a7b985a54e1cf75646ca47caaaea525b95 +size 88092 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png index da1f47816..c81deb054 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c09af9c7f4297e2d5b2305366ed57b3203807ca2426314acdf836e25f154d8eb -size 120244 +oid sha256:c5ca9c97cef8242ee6ff73d571479be12a8d4e9b3508b3eb6cdf93abda62f4e6 +size 120314 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png index 7704e4ddf..2a29cd732 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f621bcf7c8fd18156bef2428ab3b9994e6a4d475ae589eb734d50f9a4f3383bd -size 52101 +oid sha256:f0481c97c34693b32575d96b1d4bc1238cbb0eb75a934661072f1b52ffee71cf +size 52171 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png index 0fcb1aeb4..b7d315e47 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1476e105a3e9c1ff7b2f4a82481462795e4708e3fcf6d495a042faae537184e -size 55298 +oid sha256:883fdf81e51bfe6333ddcad7998458db251f9cf513c9433179061d7d086eebe0 +size 55367 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png index 4df96a3ec..2fe78dd62 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:695a731d9e302db2c5b7f4a0ef44794cf55eb0be093c070b8ffaeb28121569bc -size 55888 +oid sha256:4294949669042e009ac6825ea599dc96e33cdde25e21174b01e3ef108ad478d5 +size 55944 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png index c45845c66..603442ac0 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:854c12c69b31c0c82a9596d167772e00d7a051600e4151535e2cec04491e57a6 -size 37139 +oid sha256:39e5d196ddcaa213b30b0655fe29881a1551c3036c2262f84af8960f66365300 +size 37207 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png index 9cf019ea1..266c77826 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aacb27847c2e942d56d60e4b72797d93f7265a6effe4e47437e1314e6477e6f -size 37184 +oid sha256:f9cf9d7f1921bfc0d61a2ae31e69a98d28280e4699823de5e732cdb102aee5ac +size 37253 diff --git a/crates/egui_kittest/tests/snapshots/test_shrink.png b/crates/egui_kittest/tests/snapshots/test_shrink.png index 10967a3d5..a6e6b1f3a 100644 --- a/crates/egui_kittest/tests/snapshots/test_shrink.png +++ b/crates/egui_kittest/tests/snapshots/test_shrink.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7008bdb595a19782c4f724bed363e51bd93121f5211186aa0e8014c8ba1007c2 -size 3005 +oid sha256:b5aa7db1bb52481607069ee4a81209ece81b0c70801969b33bc2d1b2f7087de7 +size 2911 diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 0e2dd4c8e..e8f8ad52c 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1002,7 +1002,11 @@ fn stroke_and_fill_path( let color_outer = Color32::TRANSPARENT; let color_middle = &stroke.color; - let thin_line = stroke.width <= feathering; + // We add a bit of an epsilon here, because when we round to pixels, + // we can get rounding errors (unless pixels_per_point is an integer). + // And it's better to err on the side of the nicer rendering with line caps + // (the thin-line optimization has no line caps). + let thin_line = stroke.width <= 0.9 * feathering; if thin_line { // If the stroke is painted smaller than the pixel width (=feathering width), // then we risk severe aliasing. @@ -1017,6 +1021,8 @@ fn stroke_and_fill_path( } } + // TODO(emilk): add line caps (if this is an open line). + let opacity = stroke.width / feathering; /* @@ -1129,6 +1135,10 @@ fn stroke_and_fill_path( // (in the future it would be great with an option to add a circular end instead) + // TODO(emilk): we should probably shrink before adding the line caps, + // so that we don't add to the area of the line. + // TODO(emilk): make line caps optional. + out.reserve_triangles(6 * n as usize + 4); out.reserve_vertices(4 * n as usize); @@ -1637,6 +1647,11 @@ impl Tessellator { } if self.options.round_line_segments_to_pixels { + let feathering = self.feathering; + let pixels_per_point = self.pixels_per_point; + + let quarter_pixel = 0.25 * feathering; // Used to avoid fence post problem. + let [a, b] = &mut points; if a.x == b.x { // Vertical line @@ -1644,6 +1659,20 @@ impl Tessellator { round_line_segment(&mut x, &stroke, self.pixels_per_point); a.x = x; b.x = x; + + // Often the ends of the line are exactly on a pixel boundary, + // but we extend line segments with a cap that is a pixel wide… + // Solution: first shrink the line segment (on each end), + // then round to pixel center! + // We shrink by half-a-pixel n total (a quarter on each end), + // so that on average we avoid the fence-post-problem after rounding. + if a.y < b.y { + a.y = (a.y + quarter_pixel).round_to_pixel_center(pixels_per_point); + b.y = (b.y - quarter_pixel).round_to_pixel_center(pixels_per_point); + } else { + a.y = (a.y - quarter_pixel).round_to_pixel_center(pixels_per_point); + b.y = (b.y + quarter_pixel).round_to_pixel_center(pixels_per_point); + } } if a.y == b.y { // Horizontal line @@ -1651,6 +1680,15 @@ impl Tessellator { round_line_segment(&mut y, &stroke, self.pixels_per_point); a.y = y; b.y = y; + + // See earlier comment for vertical lines + if a.x < b.x { + a.x = (a.x + quarter_pixel).round_to_pixel_center(pixels_per_point); + b.x = (b.x - quarter_pixel).round_to_pixel_center(pixels_per_point); + } else { + a.x = (a.x - quarter_pixel).round_to_pixel_center(pixels_per_point); + b.x = (b.x + quarter_pixel).round_to_pixel_center(pixels_per_point); + } } } From 7ec1fbf467d39fbe269c08058137e5f2de0159d7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 16:05:36 +0100 Subject: [PATCH 15/22] Fix minor glitch in rendering of selected windows (#5678) --- crates/egui/src/containers/window.rs | 4 +--- crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Code Example.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png | 4 ++-- .../egui_demo_lib/tests/snapshots/demos/Dancing Strings.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Font Book.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Frame.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png | 4 ++-- .../tests/snapshots/demos/Interactive Container.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Modals.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Painting.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Panels.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Scene.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Sliders.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Strip.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Table.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/demos/Window Options.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/modals_1.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/modals_2.png | 4 ++-- crates/egui_demo_lib/tests/snapshots/modals_3.png | 4 ++-- .../modals_backdrop_should_prevent_focusing_lower_area.png | 4 ++-- 32 files changed, 63 insertions(+), 65 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 7582bd701..6075377ff 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -607,8 +607,6 @@ impl Window<'_> { title_bar.inner_rect = outer_rect.shrink(window_frame.stroke.width); title_bar.inner_rect.max.y = title_bar.inner_rect.min.y + title_bar_height_with_margin; - title_bar.inner_rect = - title_bar.inner_rect.round_to_pixels(ctx.pixels_per_point()); if on_top && area_content_ui.visuals().window_highlight_topmost { let mut round = @@ -1211,7 +1209,7 @@ impl TitleBar { .center(); let button_size = Vec2::splat(ui.spacing().icon_width); let button_rect = Rect::from_center_size(button_center, button_size); - let button_rect = button_rect.round_to_pixels(ui.pixels_per_point()); + let button_rect = button_rect.round_ui(); ui.allocate_new_ui(UiBuilder::new().max_rect(button_rect), |ui| { collapsing.show_default_button_with_size(ui, button_size); diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png index 63b57f0df..190f7055c 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73b24bec3383450b627683370467b68f0195bd43ce205a44dd49b60cca187b1b -size 31773 +oid sha256:536fa3adb51f69fac91396b50e26b3b18e0aa8ff245e4a187087b02240839a90 +size 31780 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png index bf53d1b11..8574b6d22 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:062b6e6b6bd7167df24545308afbc3e38ce328fff0b76ca9921180010766f827 -size 27153 +oid sha256:c129436a0b1dbfae999adfe0dcc6f5c4e0683c4e9b9a1e52f4b7bbb85ce3a462 +size 27162 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 4131e24f8..ed731869b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6287aa1fd30baee49bd5fdf3fb307f49c2ecdcb5e6a83c309ec4c051d7506a4 -size 81718 +oid sha256:322a50c522ba4ac67206332e1d251e121c8c3d5538ca7961880623b20f4933e5 +size 81732 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png index 877ce849a..ea25033bf 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dfdfe8ed7edf0074b9a4f4dd0f9c27f4c9d7411398c6dba590f032a342ad0e4 -size 11716 +oid sha256:d4cc8e0919fed5bd1ef981658626dba728435ab95da8ee96ced1fb4838d535ff +size 11741 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png index 7938ab998..d216ff1fe 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16fdf761b370962678b938735d41221b70658d5b003794c71f73327bfd7d2017 -size 25907 +oid sha256:d6ba28dacacf5b6f67746fb5187b601e222fd6baf190af2248fdc98909fc17fd +size 25921 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png index d8922e3bf..097415b60 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f789cc43f83f01b014b07e93e33ed85ecc0ef323fec404f602ca44ba6fc0ce9e -size 20819 +oid sha256:8196e08717f16c5ad17d0f84a4e57e63bb5a51c8f2b171071bf983af18ec161d +size 20834 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png index 5eccebf88..cc8c50e54 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baca02791fa90a063dff7f365ce7a0a748f7b892a920cddebd9d2593a8231786 -size 10769 +oid sha256:df029c69651ee452cc4b265828280e47ffbcafb2958d71d67a5fe38f5211afe7 +size 10788 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png index 59efbb6c1..230c7151e 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23ddfd103996b7a6d71ada175b714a3d1813ef6f521fea307b5d9cb2908cb638 -size 133212 +oid sha256:0a62d309912501be8a5de7af4f1039a2a5731b1ed76fad17527f5783a5375f42 +size 133230 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index ac6deb6b8..bfba77f18 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0be2bf06ea69579fe72d03abd0c5cc17fefcac4f403f5e6c6343b8b275080f29 -size 24324 +oid sha256:9870334dd6091fa684b78f487ad9a1bb39e6e8d97f987eb74a55de2d7b764f70 +size 24345 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png index 6bf98330c..7b14e8870 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:094db03eeea90a1e901c581b19c6ab733f50a396eb9927d82dd558c719404524 -size 17805 +oid sha256:bae5f410ed30ef4dba6f3b529ae20e34a26f6c15c4cafd197899cf876271f5f1 +size 17828 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png index bb337964b..d118733ba 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:234ae929c7f8b3f623b7e09690f8069d98ba72052c08fc58c85fd9a3dfee04a1 -size 22430 +oid sha256:68ded8dccceb3da2764243f2a554c2b4cf825fca09008d60dd520c7fbb2c5d3e +size 22445 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png index b39b7110a..8aff2977f 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8251378ba4c74fd29a6aff82517ec6b4c322c5589f85398c6d5e757986a3d83 -size 64300 +oid sha256:d55baa6e3d4af44a35ec847639c35f968b05ad907352c45b3eb09cce6cd24280 +size 64357 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png index 2fccd9242..7c20eab75 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8679ac7ec6e90cfe9164b66a02ce3c9db403df0f20f45211c73b1d9a950d2718 -size 32522 +oid sha256:98f7210fa72bdb00364e3576aefca126a6f31eff52870d116ba74c167354b13b +size 32533 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png index 6a4f65e64..bd25e38a8 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05987e9dfd291b0db57d7611c1feb6742198c14546b779975366643d64ef9ea5 -size 36539 +oid sha256:7868d662bd61d490dce9c049fca6c6e6b978255664fa709e959891bb40a7d434 +size 36577 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png index cab418b5b..749ecd471 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1107f4ab1411e054ecefe19e0de6a7276ec2ab81c2c9221056860a3d62263ebf -size 17596 +oid sha256:fb3d031b8f658a90cf98e7a7bc5e0d7a3b601d742e2a9469cd115e7466e06524 +size 17628 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png index 72bb68bbf..a4527a73b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ca983bab555739e9a2eefb518a8009e1de9638c70efb50edb407cc26973d1eb -size 264478 +oid sha256:7ace9a6626446f8e29ec4c3f688e60cbeb86e79ad962044858aabe33a9c3d0e9 +size 264538 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index 42e3f7625..e79968364 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0728689de055812e85ccb15e5f4175dd7f9096e610d4ac04256367afa4e036a5 -size 35477 +oid sha256:7d412700c156c641f0184a239198f33bd2427a1ea998a3ee07160cf0f837df94 +size 35451 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png index 523014112..803a39f99 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffc6e6011ab774613f1a937dace27129bf9c5e5ee10a3a3cfa4973d82b8e5429 -size 23604 +oid sha256:89efd018caac097a5f9be37dcae15fc60b1475c72fc913ec9940540344e0b09f +size 23622 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png index 2d9d0a5ba..2e2af10c9 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d84c161d7fd0b1d5670b79e360d6737a3c004998c94835592180a12c055959de -size 183870 +oid sha256:22c89f7b9b84563d6ee7db0d9a66f6b95c9034261fdffca53ae9737d70d2b376 +size 183881 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png index eec4c34fe..e47f11082 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32a9eba39d88023484abf29ab0bd7a29bfc2ba6b1f7ca42496f87a854616f424 -size 117783 +oid sha256:316c172a936f215afdcc45e7f5b32400e6acd759551adb2cc741f7121b9d83eb +size 117790 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png index 01e6abdd6..ea9a21881 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Strip.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Strip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e91609711f26a067de0e9bfd5f6f1680339e8137ab1a8325290ce399857d2797 -size 26135 +oid sha256:88913690a2b225ca634e38406a6a852250019a19d9bb33a4242e77c10fe88422 +size 26142 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png index eecf9aa0f..3012f2f36 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Table.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Table.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5171572d2bfee9f44cb8fc6aeba2ff3035823035d27ec42480e4bd49e6f5e63 -size 70524 +oid sha256:43b8dae4a936bf56b92368fcef64ff2ce2518aabc534a77fa578730493034f0f +size 70536 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png index 8eae9dc33..84f7cc7bd 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7b21826be83c1a16b9fa7b19130f40261a9735c3eb450eaf9f8d5a3bbfccb7a -size 66365 +oid sha256:a307ac48abc79548c16468b3606a5df283ab2a5ac28345bd801bcc3887063414 +size 66384 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png index 181223e48..93789ff45 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f7986da3f4a3b94e701bf94066896aedb41458f12f6cdaf93a4fd371b1705da -size 21206 +oid sha256:6311be2b850b5e41ac6dadf639b00584438b56f651a3c8d75ac8f5e06c9ad6fa +size 21224 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png index af9ee2651..6d9aced18 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fb28c16dc2e4359ada002a5fb98e955db606f44db4bdd1a400ab2e4c43c850e -size 59701 +oid sha256:c80158ac9c823f94d2830d1423236ad441dc7da31e748b6815c69663fa2a03d0 +size 59662 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png index 9f2d64d17..5f77c2db3 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a8a3e5953a5ee41d95f2a23b580bb9c82e869b7bad0f2611ed866c55bc12b07 -size 13102 +oid sha256:3584f16229bae50cc04b31df6bf5ccf43288fd05b447b34b29f118eb7435a090 +size 13103 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png index 2128b9d05..21b08252a 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12fdbe6805dbce0d993236f30ac14d9c03411b6918399ec5bfa7aa39104d0640 -size 35182 +oid sha256:3411a4a8939b7e731c9c1a6331921b0ac905f4e3e86a51af70bdb38d9446f5e1 +size 35193 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_1.png b/crates/egui_demo_lib/tests/snapshots/modals_1.png index 177e90cc3..2036be3c9 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_1.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d270118b982413bd0c05e9f20a3ee53b88a1681e273f73a81d1733b05c7a006 -size 47889 +oid sha256:1ac48ec9f7bde9869f1b3097e9f897b5e8df96cd6159a6ded542582dc69ab32c +size 47913 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_2.png b/crates/egui_demo_lib/tests/snapshots/modals_2.png index 7e5d7ff82..60e6ddef5 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_2.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f4ad4ac06f2113533943fc00ed9747c237c9b7b6f0890ccf9857e606290c7ec -size 47871 +oid sha256:795e16389b31ad719050247eb9e736782380a83fa71b5b35b50e17812c8d9bdd +size 47886 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_3.png b/crates/egui_demo_lib/tests/snapshots/modals_3.png index 12c338393..1ae5a3314 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_3.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fee7a7f4ebcda3fd55d1d31e21b46246c4d5f166fb1f0de04265e7db424d9c8 -size 43733 +oid sha256:a62a286e29aa0e0f949088ddefe01137535877408ba88778f61cbfe8d50c2261 +size 43750 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png index 5d0da39d8..e059b556f 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:510a3b5d8b10013eed234977bf46575a39cc951611a073154652ce6bf2bc3ecc -size 43880 +oid sha256:9c1bc8e22aa1050a4e7d1b2abe407251e22d338c38a7e41c045a384c9139b4de +size 43895 From 95ab92ca4a1919d5371c26d5cf993396479e99d0 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 16:28:27 +0100 Subject: [PATCH 16/22] Protect against mistyping "tessellator" --- .typos.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.typos.toml b/.typos.toml index de51a691c..db5bf48b4 100644 --- a/.typos.toml +++ b/.typos.toml @@ -7,5 +7,15 @@ ime = "ime" # Input Method Editor nknown = "nknown" # part of @55nknown username ro = "ro" # read-only, also part of the username @Phen-Ro +# I mistype these so often +tesalator = "tessellator" +teselator = "tessellator" +tessalator = "tessellator" +tesselator = "tessellator" +tesalation = "tessellation" +teselation = "tessellation" +tessalation = "tessellation" +tesselation = "tessellation" + [files] extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated From f162c3bcbfdf3f3420a2112960d19e306ced9c8d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 16:28:52 +0100 Subject: [PATCH 17/22] Explain how to set up generate_changelog.py --- scripts/generate_changelog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/generate_changelog.py b/scripts/generate_changelog.py index a3ee04a4a..7e3ae4284 100755 --- a/scripts/generate_changelog.py +++ b/scripts/generate_changelog.py @@ -5,6 +5,8 @@ Summarizes recent PRs based on their GitHub labels. The result can be copy-pasted into CHANGELOG.md, though it often needs some manual editing too. + +Setup: pip install GitPython requests tqdm """ import argparse From d97cd8233858be557025d86ec5b2d59bc62ba669 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 16:28:36 +0100 Subject: [PATCH 18/22] Update RELEASES.md --- RELEASES.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index a96b35be0..5850713db 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -36,14 +36,9 @@ We don't update the MSRV in a patch release, unless we really, really need to. ## Release testing * [ ] `cargo r -p egui_demo_app` and click around for while -* [ ] `./scripts/build_demo_web.sh --release -g` - - check frame-rate and wasm size - - test on mobile - - test on chromium - - check the in-browser profiler -* [ ] check the color test * [ ] update `eframe_template` and test * [ ] update `egui_plot` and test +* [ ] update `egui_table` and test * [ ] update `egui_tiles` and test * [ ] test with Rerun * [ ] `./scripts/check.sh` @@ -98,3 +93,5 @@ I usually do this all on the `master` branch, but doing it in a release branch i * [ ] publish new `egui_plot` * [ ] publish new `egui_table` * [ ] publish new `egui_tiles` +* [ ] make a PR to `egui_commonmark` +* [ ] make a PR to `rerun` From 5c372a7b367a7aa7ed6e82ada9f57a8a11e8d596 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 4 Feb 2025 16:47:56 +0100 Subject: [PATCH 19/22] Release 0.31.0 - Scene container, improved rendering quality --- CHANGELOG.md | 82 ++++++++++++++++++++++++ Cargo.lock | 30 ++++----- Cargo.toml | 26 ++++---- RELEASES.md | 6 +- crates/ecolor/CHANGELOG.md | 4 ++ crates/eframe/CHANGELOG.md | 8 +++ crates/egui-wgpu/CHANGELOG.md | 6 ++ crates/egui-winit/CHANGELOG.md | 5 ++ crates/egui_extras/CHANGELOG.md | 6 ++ crates/egui_glow/CHANGELOG.md | 4 ++ crates/egui_kittest/CHANGELOG.md | 12 ++++ crates/epaint/CHANGELOG.md | 21 ++++++ crates/epaint_default_fonts/CHANGELOG.md | 4 ++ 13 files changed, 183 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6733405c..8877c9905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,88 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 - Scene container, improved rendering quality + +### Highlights ✨ + +#### Scene container +This release adds the `Scene` container to egui. It is a pannable, zoomable canvas that can contain `Widget`s and child `Ui`s. +This will make it easier to e.g. implement a graph editor. + +![image](https://github.com/user-attachments/assets/b37d56ef-f6ef-46be-bb99-6eb25b6efca9) + +#### Clearer, pixel perfect rendering +The tessellator has been updated for improved rendering quality and better performance. It will produce fewer vertices +and shapes will have less overdraw. We've also defined what `CornerRadius` (previously `Rounding`) means. + +We've also added a tessellator test to the [demo app](https://www.egui.rs/), where you can play around with different +values to see what's produced: + + +https://github.com/user-attachments/assets/adf55e3b-fb48-4df0-aaa2-150ee3163684 + + +Check the [PR](https://github.com/emilk/egui/pull/5669) for more details. + +#### `CornerRadius`, `Margin`, `Shadow` size reduction +In order to pave the path for more complex and customizable styling solutions, we've reduced the size of +`CornerRadius`, `Margin` and `Shadow` values to `i8` and `u8`. + + + +### Migration guide +- Add a `StrokeKind` to all your `Painter::rect` calls [#5648](https://github.com/emilk/egui/pull/5648) +- `StrokeKind::default` was removed, since the 'normal' value depends on the context [#5658](https://github.com/emilk/egui/pull/5658) + - You probably want to use `StrokeKind::Inside` when drawing rectangles + - You probably want to use `StrokeKind::Middle` when drawing open paths +- Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) +- `CornerRadius`, `Margin` and `Shadow` have been updated to use `i8` and `u8` [#5563](https://github.com/emilk/egui/pull/5563), [#5567](https://github.com/emilk/egui/pull/5567), [#5568](https://github.com/emilk/egui/pull/5568) + - Remove the .0 from your values + - Cast dynamic values with `as i8` / `as u8` or `as _` if you want Rust to infer the type + - Rust will do a 'saturating' cast, so if your `f32` value is bigger than `127` it will be clamped to `127` +- `RectShape` parameters changed [#5565](https://github.com/emilk/egui/pull/5565) + - Prefer to use the builder methods to create it instead of initializing it directly +- `Frame` now takes the `Stroke` width into account for its sizing, so check all views of your app to make sure they still look right. + Read the [PR](https://github.com/emilk/egui/pull/5575) for more info. + +### ⭐ Added +* Add `egui::Scene` for panning/zooming a `Ui` [#5505](https://github.com/emilk/egui/pull/5505) by [@grtlr](https://github.com/grtlr) +* Animated WebP support [#5470](https://github.com/emilk/egui/pull/5470) by [@Aely0](https://github.com/Aely0) +* Improve tessellation quality [#5669](https://github.com/emilk/egui/pull/5669) by [@emilk](https://github.com/emilk) +* Add `OutputCommand` for copying text and opening URL:s [#5532](https://github.com/emilk/egui/pull/5532) by [@emilk](https://github.com/emilk) +* Add `Context::copy_image` [#5533](https://github.com/emilk/egui/pull/5533) by [@emilk](https://github.com/emilk) +* Add `WidgetType::Image` and `Image::alt_text` [#5534](https://github.com/emilk/egui/pull/5534) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `epaint::Brush` for controlling `RectShape` texturing [#5565](https://github.com/emilk/egui/pull/5565) by [@emilk](https://github.com/emilk) +* Implement `nohash_hasher::IsEnabled` for `Id` [#5628](https://github.com/emilk/egui/pull/5628) by [@emilk](https://github.com/emilk) +* Add keys for `!`, `{`, `}` [#5548](https://github.com/emilk/egui/pull/5548) by [@Its-Just-Nans](https://github.com/Its-Just-Nans) +* Add `RectShape::stroke_kind ` to control if stroke is inside/outside/centered [#5647](https://github.com/emilk/egui/pull/5647) by [@emilk](https://github.com/emilk) + +### 🔧 Changed +* ⚠️ `Frame` now includes stroke width as part of padding [#5575](https://github.com/emilk/egui/pull/5575) by [@emilk](https://github.com/emilk) +* Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) by [@emilk](https://github.com/emilk) +* Require a `StrokeKind` when painting rectangles with strokes [#5648](https://github.com/emilk/egui/pull/5648) by [@emilk](https://github.com/emilk) +* Round widget coordinates to even multiple of 1/32 [#5517](https://github.com/emilk/egui/pull/5517) by [@emilk](https://github.com/emilk) +* Make all lines and rectangles crisp [#5518](https://github.com/emilk/egui/pull/5518) by [@emilk](https://github.com/emilk) +* Tweak window resize handles [#5524](https://github.com/emilk/egui/pull/5524) by [@emilk](https://github.com/emilk) + +### 🔥 Removed +* Remove `egui::special_emojis::TWITTER` [#5622](https://github.com/emilk/egui/pull/5622) by [@emilk](https://github.com/emilk) +* Remove `StrokeKind::default` [#5658](https://github.com/emilk/egui/pull/5658) by [@emilk](https://github.com/emilk) + +### 🐛 Fixed +* Use correct minimum version of `profiling` crate [#5494](https://github.com/emilk/egui/pull/5494) by [@lucasmerlin](https://github.com/lucasmerlin) +* Fix interactive widgets sometimes being incorrectly marked as hovered [#5523](https://github.com/emilk/egui/pull/5523) by [@emilk](https://github.com/emilk) +* Fix panic due to non-total ordering in `Area::compare_order()` [#5569](https://github.com/emilk/egui/pull/5569) by [@HactarCE](https://github.com/HactarCE) +* Fix hovering through custom menu button [#5555](https://github.com/emilk/egui/pull/5555) by [@M4tthewDE](https://github.com/M4tthewDE) + +### 🚀 Performance +* Use `u8` in `CornerRadius`, and introduce `CornerRadiusF32` [#5563](https://github.com/emilk/egui/pull/5563) by [@emilk](https://github.com/emilk) +* Store `Margin` using `i8` to reduce its size [#5567](https://github.com/emilk/egui/pull/5567) by [@emilk](https://github.com/emilk) +* Shrink size of `Shadow` by using `i8/u8` instead of `f32` [#5568](https://github.com/emilk/egui/pull/5568) by [@emilk](https://github.com/emilk) +* Avoid allocations for loader cache lookup [#5584](https://github.com/emilk/egui/pull/5584) by [@mineichen](https://github.com/mineichen) +* Use bitfield instead of bools in `Response` and `Sense` [#5556](https://github.com/emilk/egui/pull/5556) by [@polwel](https://github.com/polwel) + + ## 0.30.0 - 2024-12-16 - Modals and better layer support ### ✨ Highlights diff --git a/Cargo.lock b/Cargo.lock index 709b7b77a..4956e1436 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1197,7 +1197,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "ecolor" -version = "0.30.0" +version = "0.31.0" dependencies = [ "bytemuck", "cint", @@ -1209,7 +1209,7 @@ dependencies = [ [[package]] name = "eframe" -version = "0.30.0" +version = "0.31.0" dependencies = [ "ahash", "bytemuck", @@ -1249,7 +1249,7 @@ dependencies = [ [[package]] name = "egui" -version = "0.30.0" +version = "0.31.0" dependencies = [ "accesskit", "ahash", @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.30.0" +version = "0.31.0" dependencies = [ "ahash", "bytemuck", @@ -1285,7 +1285,7 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.30.0" +version = "0.31.0" dependencies = [ "accesskit_winit", "ahash", @@ -1306,7 +1306,7 @@ dependencies = [ [[package]] name = "egui_demo_app" -version = "0.30.0" +version = "0.31.0" dependencies = [ "bytemuck", "chrono", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "egui_demo_lib" -version = "0.30.0" +version = "0.31.0" dependencies = [ "chrono", "criterion", @@ -1347,7 +1347,7 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.30.0" +version = "0.31.0" dependencies = [ "ahash", "chrono", @@ -1366,7 +1366,7 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.30.0" +version = "0.31.0" dependencies = [ "ahash", "bytemuck", @@ -1386,7 +1386,7 @@ dependencies = [ [[package]] name = "egui_kittest" -version = "0.30.0" +version = "0.31.0" dependencies = [ "dify", "document-features", @@ -1421,7 +1421,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "emath" -version = "0.30.0" +version = "0.31.0" dependencies = [ "bytemuck", "document-features", @@ -1512,7 +1512,7 @@ dependencies = [ [[package]] name = "epaint" -version = "0.30.0" +version = "0.31.0" dependencies = [ "ab_glyph", "ahash", @@ -1533,7 +1533,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" -version = "0.30.0" +version = "0.31.0" [[package]] name = "equivalent" @@ -3099,7 +3099,7 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "popups" -version = "0.30.0" +version = "0.31.0" dependencies = [ "eframe", "env_logger", @@ -5105,7 +5105,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xtask" -version = "0.30.0" +version = "0.31.0" [[package]] name = "yaml-rust" diff --git a/Cargo.toml b/Cargo.toml index b8a58df8b..3934c44b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ edition = "2021" license = "MIT OR Apache-2.0" rust-version = "1.81" -version = "0.30.0" +version = "0.31.0" [profile.release] @@ -55,18 +55,18 @@ opt-level = 2 [workspace.dependencies] -emath = { version = "0.30.0", path = "crates/emath", default-features = false } -ecolor = { version = "0.30.0", path = "crates/ecolor", default-features = false } -epaint = { version = "0.30.0", path = "crates/epaint", default-features = false } -epaint_default_fonts = { version = "0.30.0", path = "crates/epaint_default_fonts" } -egui = { version = "0.30.0", path = "crates/egui", default-features = false } -egui-winit = { version = "0.30.0", path = "crates/egui-winit", default-features = false } -egui_extras = { version = "0.30.0", path = "crates/egui_extras", default-features = false } -egui-wgpu = { version = "0.30.0", path = "crates/egui-wgpu", default-features = false } -egui_demo_lib = { version = "0.30.0", path = "crates/egui_demo_lib", default-features = false } -egui_glow = { version = "0.30.0", path = "crates/egui_glow", default-features = false } -egui_kittest = { version = "0.30.0", path = "crates/egui_kittest", default-features = false } -eframe = { version = "0.30.0", path = "crates/eframe", default-features = false } +emath = { version = "0.31.0", path = "crates/emath", default-features = false } +ecolor = { version = "0.31.0", path = "crates/ecolor", default-features = false } +epaint = { version = "0.31.0", path = "crates/epaint", default-features = false } +epaint_default_fonts = { version = "0.31.0", path = "crates/epaint_default_fonts" } +egui = { version = "0.31.0", path = "crates/egui", default-features = false } +egui-winit = { version = "0.31.0", path = "crates/egui-winit", default-features = false } +egui_extras = { version = "0.31.0", path = "crates/egui_extras", default-features = false } +egui-wgpu = { version = "0.31.0", path = "crates/egui-wgpu", default-features = false } +egui_demo_lib = { version = "0.31.0", path = "crates/egui_demo_lib", default-features = false } +egui_glow = { version = "0.31.0", path = "crates/egui_glow", default-features = false } +egui_kittest = { version = "0.31.0", path = "crates/egui_kittest", default-features = false } +eframe = { version = "0.31.0", path = "crates/eframe", default-features = false } ahash = { version = "0.8.11", default-features = false, features = [ "no-rng", # we don't need DOS-protection, so we let users opt-in to it instead diff --git a/RELEASES.md b/RELEASES.md index 5850713db..a8e629a03 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -46,7 +46,7 @@ We don't update the MSRV in a patch release, unless we really, really need to. ## Preparation * [ ] run `scripts/generate_example_screenshots.sh` if needed -* [ ] write a short release note that fits in a tweet +* [ ] write a short release note that fits in a bluesky post * [ ] record gif for `CHANGELOG.md` release note (and later bluesky post) * [ ] update changelogs using `scripts/generate_changelog.py --version 0.x.0 --write` * [ ] bump version numbers in workspace `Cargo.toml` @@ -55,9 +55,9 @@ We don't update the MSRV in a patch release, unless we really, really need to. I usually do this all on the `master` branch, but doing it in a release branch is also fine, as long as you remember to merge it into `master` later. * [ ] Run `typos` -* [ ] `git commit -m 'Release 0.x.0 - summary'` +* [ ] `git commit -m 'Release 0.x.0 - '` * [ ] `cargo publish` (see below) -* [ ] `git tag -a 0.x.0 -m 'Release 0.x.0 - summary'` +* [ ] `git tag -a 0.x.0 -m 'Release 0.x.0 - '` * [ ] `git pull --tags ; git tag -d latest && git tag -a latest -m 'Latest release' && git push --tags origin latest --force ; git push --tags` * [ ] merge release PR or push to `master` * [ ] check that CI is green diff --git a/crates/ecolor/CHANGELOG.md b/crates/ecolor/CHANGELOG.md index 37b5555b6..61cfce610 100644 --- a/crates/ecolor/CHANGELOG.md +++ b/crates/ecolor/CHANGELOG.md @@ -6,6 +6,10 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Add `Color32::CYAN` and `Color32::MAGENTA` [#5663](https://github.com/emilk/egui/pull/5663) by [@juancampa](https://github.com/juancampa) + + ## 0.30.0 - 2024-12-16 * Use boxed slice for lookup table to avoid stack overflow [#5212](https://github.com/emilk/egui/pull/5212) by [@YgorSouza](https://github.com/YgorSouza) * Add `Color32::mul` [#5437](https://github.com/emilk/egui/pull/5437) by [@emilk](https://github.com/emilk) diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index 0ddfa4b0e..967b69e3b 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -7,6 +7,14 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Web: Fix incorrect scale when moving to screen with new DPI [#5631](https://github.com/emilk/egui/pull/5631) by [@emilk](https://github.com/emilk) +* Re-enable IME support on Linux [#5198](https://github.com/emilk/egui/pull/5198) by [@YgorSouza](https://github.com/YgorSouza) +* Serialize window maximized state in `WindowSettings` [#5554](https://github.com/emilk/egui/pull/5554) by [@landaire](https://github.com/landaire) +* Save state on suspend on Android and iOS [#5601](https://github.com/emilk/egui/pull/5601) by [@Pandicon](https://github.com/Pandicon) +* Eframe web: forward cmd-S/O to egui app (stop default browser action) [#5655](https://github.com/emilk/egui/pull/5655) by [@emilk](https://github.com/emilk) + + ## 0.30.0 - 2024-12-16 - Android support NOTE: you now need to enable the `wayland` or `x11` features to get Linux support, including getting it to work on most CI systems. diff --git a/crates/egui-wgpu/CHANGELOG.md b/crates/egui-wgpu/CHANGELOG.md index d92e867a7..a20cafa14 100644 --- a/crates/egui-wgpu/CHANGELOG.md +++ b/crates/egui-wgpu/CHANGELOG.md @@ -6,6 +6,12 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Upgrade to wgpu 24 [#5610](https://github.com/emilk/egui/pull/5610) by [@torokati44](https://github.com/torokati44) +* Extend `WgpuSetup`, `egui_kittest` now prefers software rasterizers for testing [#5506](https://github.com/emilk/egui/pull/5506) by [@Wumpf](https://github.com/Wumpf) +* Wgpu resources are no longer wrapped in `Arc` (since they are now `Clone`) [#5612](https://github.com/emilk/egui/pull/5612) by [@Wumpf](https://github.com/Wumpf) + + ## 0.30.0 - 2024-12-16 * Fix docs.rs build [#5204](https://github.com/emilk/egui/pull/5204) by [@lucasmerlin](https://github.com/lucasmerlin) * Free textures after submitting queue instead of before with wgpu renderer [#5226](https://github.com/emilk/egui/pull/5226) by [@Rusty-Cube](https://github.com/Rusty-Cube) diff --git a/crates/egui-winit/CHANGELOG.md b/crates/egui-winit/CHANGELOG.md index d16446420..7cc8667c5 100644 --- a/crates/egui-winit/CHANGELOG.md +++ b/crates/egui-winit/CHANGELOG.md @@ -5,6 +5,11 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Re-enable IME support on Linux [#5198](https://github.com/emilk/egui/pull/5198) by [@YgorSouza](https://github.com/YgorSouza) +* Update to winit 0.30.7 [#5516](https://github.com/emilk/egui/pull/5516) by [@emilk](https://github.com/emilk) + + ## 0.30.0 - 2024-12-16 * iOS: Support putting UI next to the dynamic island [#5211](https://github.com/emilk/egui/pull/5211) by [@frederik-uni](https://github.com/frederik-uni) * Remove implicit `accesskit_winit` feature [#5316](https://github.com/emilk/egui/pull/5316) by [@waywardmonkeys](https://github.com/waywardmonkeys) diff --git a/crates/egui_extras/CHANGELOG.md b/crates/egui_extras/CHANGELOG.md index 7617b1863..d6cc2e9c8 100644 --- a/crates/egui_extras/CHANGELOG.md +++ b/crates/egui_extras/CHANGELOG.md @@ -5,6 +5,12 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Animated WebP support [#5470](https://github.com/emilk/egui/pull/5470), [#5586](https://github.com/emilk/egui/pull/5586) by [@Aely0](https://github.com/Aely0) +* Make image extension check case-insensitive [#5501](https://github.com/emilk/egui/pull/5501) by [@RyanBluth](https://github.com/RyanBluth) +* Avoid allocations for loader cache lookup [#5584](https://github.com/emilk/egui/pull/5584) by [@mineichen](https://github.com/mineichen) + + ## 0.30.0 - 2024-12-16 * Use `Table::id_salt` on `ScrollArea` [#5282](https://github.com/emilk/egui/pull/5282) by [@jwhear](https://github.com/jwhear) * Use proper `image` crate URI and MIME support detection [#5324](https://github.com/emilk/egui/pull/5324) by [@xangelix](https://github.com/xangelix) diff --git a/crates/egui_glow/CHANGELOG.md b/crates/egui_glow/CHANGELOG.md index dc3d6dfda..abdfa240e 100644 --- a/crates/egui_glow/CHANGELOG.md +++ b/crates/egui_glow/CHANGELOG.md @@ -6,6 +6,10 @@ Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +### ⭐ Added +* Add `Harness::new_eframe` and `TestRenderer` trait [#5539](https://github.com/emilk/egui/pull/5539) by [@lucasmerlin](https://github.com/lucasmerlin) +* Change `Harness::run` to run until no more repaints are requested [#5580](https://github.com/emilk/egui/pull/5580) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `SnapshotResults` struct to `egui_kittest` [#5672](https://github.com/emilk/egui/pull/5672) by [@lucasmerlin](https://github.com/lucasmerlin) + +### 🔧 Changed +* Extend `WgpuSetup`, `egui_kittest` now prefers software rasterizers for testing [#5506](https://github.com/emilk/egui/pull/5506) by [@Wumpf](https://github.com/Wumpf) +* Write `.old.png` files when updating images [#5578](https://github.com/emilk/egui/pull/5578) by [@emilk](https://github.com/emilk) +* Succeed and keep going when `UPDATE_SNAPSHOTS` is set [#5649](https://github.com/emilk/egui/pull/5649) by [@emilk](https://github.com/emilk) + + ## 0.30.0 - 2024-12-16 - Initial relrease * Support for egui 0.30.0 * Automate clicks and text input diff --git a/crates/epaint/CHANGELOG.md b/crates/epaint/CHANGELOG.md index 742cf0acd..cee374ce6 100644 --- a/crates/epaint/CHANGELOG.md +++ b/crates/epaint/CHANGELOG.md @@ -5,6 +5,27 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +### ⭐ Added +* Improve tessellation quality [#5669](https://github.com/emilk/egui/pull/5669) by [@emilk](https://github.com/emilk) +* Add `epaint::Brush` for controlling `RectShape` texturing [#5565](https://github.com/emilk/egui/pull/5565) by [@emilk](https://github.com/emilk) +* Add `RectShape::stroke_kind ` to control if stroke is inside/outside/centered [#5647](https://github.com/emilk/egui/pull/5647) by [@emilk](https://github.com/emilk) + +### 🔧 Changed +* Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) by [@emilk](https://github.com/emilk) +* Make all lines and rectangles crisp [#5518](https://github.com/emilk/egui/pull/5518) by [@emilk](https://github.com/emilk) +* Better rounding of rectangles with thin outlines [#5571](https://github.com/emilk/egui/pull/5571) by [@emilk](https://github.com/emilk) +* Require a `StrokeKind` when painting rectangles with strokes [#5648](https://github.com/emilk/egui/pull/5648) by [@emilk](https://github.com/emilk) + +### 🔥 Removed +* Remove `StrokeKind::default` [#5658](https://github.com/emilk/egui/pull/5658) by [@emilk](https://github.com/emilk) + +### 🚀 Performance +* Use `u8` in `Rounding`, and introduce `Roundingf` [#5563](https://github.com/emilk/egui/pull/5563) by [@emilk](https://github.com/emilk) +* Store `Margin` using `i8` to reduce its size [#5567](https://github.com/emilk/egui/pull/5567) by [@emilk](https://github.com/emilk) +* Shrink size of `Shadow` by using `i8/u8` instead of `f32` [#5568](https://github.com/emilk/egui/pull/5568) by [@emilk](https://github.com/emilk) + + ## 0.30.0 - 2024-12-16 * Expand max font atlas size from 8k to 16k [#5257](https://github.com/emilk/egui/pull/5257) by [@rustbasic](https://github.com/rustbasic) * Put font data into `Arc` to reduce memory consumption [#5276](https://github.com/emilk/egui/pull/5276) by [@StarStarJ](https://github.com/StarStarJ) diff --git a/crates/epaint_default_fonts/CHANGELOG.md b/crates/epaint_default_fonts/CHANGELOG.md index 42cd89ba5..8d9eff7cd 100644 --- a/crates/epaint_default_fonts/CHANGELOG.md +++ b/crates/epaint_default_fonts/CHANGELOG.md @@ -5,6 +5,10 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.0 - 2025-02-04 +* Update `egui_default_fonts` license [#5361](https://github.com/emilk/egui/pull/5361) by [@pombredanne](https://github.com/pombredanne) + + ## 0.30.0 - 2024-12-16 Nothing new From d7453e42d3be72b1d5e22bd623c4db8817c99314 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 17:03:38 +0100 Subject: [PATCH 20/22] Fix gif in CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8877c9905..b61c6398d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,13 +22,13 @@ Changes since the last release can be found at Date: Tue, 4 Feb 2025 17:04:29 +0100 Subject: [PATCH 21/22] Fix warning --- crates/egui_demo_lib/src/demo/widget_gallery.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/egui_demo_lib/src/demo/widget_gallery.rs b/crates/egui_demo_lib/src/demo/widget_gallery.rs index eae07ccd5..6005d6aec 100644 --- a/crates/egui_demo_lib/src/demo/widget_gallery.rs +++ b/crates/egui_demo_lib/src/demo/widget_gallery.rs @@ -48,6 +48,7 @@ impl Default for WidgetGallery { } impl WidgetGallery { + #[allow(unused_mut)] // if not chrono #[inline] pub fn with_date_button(mut self, _with_date_button: bool) -> Self { #[cfg(feature = "chrono")] From 0db56dc9f1a8459b5b9376159fab7d7048b19b65 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 4 Feb 2025 17:35:55 +0100 Subject: [PATCH 22/22] Fix the magnification range of the tessellation test --- .../src/demo/tests/tessellation_test.rs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs index 58f53560d..031f3e2ce 100644 --- a/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs +++ b/crates/egui_demo_lib/src/demo/tests/tessellation_test.rs @@ -178,7 +178,7 @@ impl crate::View for TessellationTest { ui.add( egui::DragValue::new(magnification_pixel_size) .speed(0.5) - .range(0.0..=64.0), + .range(1.0..=32.0), ); ui.end_row(); @@ -244,25 +244,27 @@ impl crate::View for TessellationTest { } } - // Draw pixel centers: - let pixel_radius = 0.75; - let pixel_color = Color32::GRAY; - for yi in 0.. { - let y = (yi as f32 + 0.5) * magnification_pixel_size; - if y > canvas.height() / 2.0 { - break; - } - for xi in 0.. { - let x = (xi as f32 + 0.5) * magnification_pixel_size; - if x > canvas.width() / 2.0 { + if 3.0 < magnification_pixel_size { + // Draw pixel centers: + let pixel_radius = 0.75; + let pixel_color = Color32::GRAY; + for yi in 0.. { + let y = (yi as f32 + 0.5) * magnification_pixel_size; + if y > canvas.height() / 2.0 { break; } - for offset in [vec2(x, y), vec2(x, -y), vec2(-x, y), vec2(-x, -y)] { - painter.circle_filled( - canvas.center() + offset, - pixel_radius, - pixel_color, - ); + for xi in 0.. { + let x = (xi as f32 + 0.5) * magnification_pixel_size; + if x > canvas.width() / 2.0 { + break; + } + for offset in [vec2(x, y), vec2(x, -y), vec2(-x, y), vec2(-x, -y)] { + painter.circle_filled( + canvas.center() + offset, + pixel_radius, + pixel_color, + ); + } } } }