diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 92113f4d..2847fdfd 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -52,6 +52,7 @@ use epaint::*; /// Note that you cannot change the margins after calling `begin`. #[doc(alias = "border")] #[derive(Clone, Copy, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[must_use = "You should call .show()"] pub struct Frame { /// Margin within the painted frame. diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 376c41d9..222f8ab0 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1567,7 +1567,7 @@ impl Context { /// The [`Style`] used by all new windows, panels etc. /// - /// You can also change this using [`Self::style_mut]` + /// You can also change this using [`Self::style_mut`] /// /// You can use [`Ui::style_mut`] to change the style of a single [`Ui`]. pub fn set_style(&self, style: impl Into>) { diff --git a/crates/egui/src/introspection.rs b/crates/egui/src/introspection.rs index 9c738153..f71e8219 100644 --- a/crates/egui/src/introspection.rs +++ b/crates/egui/src/introspection.rs @@ -150,23 +150,33 @@ impl Widget for &mut epaint::TessellationOptions { validate_meshes, } = self; - ui.checkbox(feathering, "Feathering (antialias)") - .on_hover_text("Apply feathering to smooth out the edges of shapes. Turn off for small performance gain."); - let feathering_slider = crate::Slider::new(feathering_size_in_pixels, 0.0..=10.0) - .smallest_positive(0.1) - .logarithmic(true) - .text("Feathering size in pixels"); - ui.add_enabled(*feathering, feathering_slider); + ui.horizontal(|ui| { + ui.checkbox(feathering, "Feathering (antialias)") + .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).clamp_range(0.0..=10.0).speed(0.1).suffix(" px")); + } + }); ui.checkbox(prerasterized_discs, "Speed up filled circles with pre-rasterization"); - ui.add( - crate::widgets::Slider::new(bezier_tolerance, 0.0001..=10.0) - .logarithmic(true) - .show_value(true) - .text("Spline Tolerance"), - ); - ui.collapsing("debug", |ui| { + ui.horizontal(|ui| { + ui.label("Spline tolerance"); + let speed = 0.01 * *bezier_tolerance; + ui.add( + crate::DragValue::new(bezier_tolerance).clamp_range(0.0001..=10.0) + .speed(speed) + ); + }); + + ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, "Parallelize tessellation") + ).on_hover_text("Only available if epaint was compiled with the rayon feature") + .on_disabled_hover_text("epaint was not compiled with the rayon feature"); + + ui.checkbox(validate_meshes, "Validate meshes").on_hover_text("Check that incoming meshes are valid, i.e. that all indices are in range, etc."); + + ui.collapsing("Debug", |ui| { ui.checkbox( coarse_tessellation_culling, "Do coarse culling in the tessellator", @@ -178,12 +188,6 @@ impl Widget for &mut epaint::TessellationOptions { ui.checkbox(debug_paint_clip_rects, "Paint clip rectangles"); ui.checkbox(debug_paint_text_rects, "Paint text bounds"); }); - - ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, "Parallelize tessellation") - ).on_hover_text("Only available if epaint was compiled with the rayon feature") - .on_disabled_hover_text("epaint was not compiled with the rayon feature"); - - ui.checkbox(validate_meshes, "Validate meshes").on_hover_text("Check that incoming meshes are valid, i.e. that all indices are in range, etc."); }) .response } diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index e64668ec..b8f0c8fa 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -431,7 +431,7 @@ pub use epaint::{ text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak}, textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta}, ClippedPrimitive, ColorImage, FontImage, ImageData, Margin, Mesh, PaintCallback, - PaintCallbackInfo, Rounding, Shape, Stroke, TextureHandle, TextureId, + PaintCallbackInfo, Rounding, Shadow, Shape, Stroke, TextureHandle, TextureId, }; pub mod text { diff --git a/crates/egui/src/memory.rs b/crates/egui/src/memory.rs index dc611787..b8430828 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -278,13 +278,15 @@ impl Options { }); CollapsingHeader::new("✒ Painting") - .default_open(true) + .default_open(false) .show(ui, |ui| { tessellation_options.ui(ui); - ui.vertical_centered(|ui| crate::reset_button(ui, tessellation_options)); + ui.vertical_centered(|ui| { + crate::reset_button(ui, tessellation_options, "Reset paint settings"); + }); }); - ui.vertical_centered(|ui| crate::reset_button(ui, self)); + ui.vertical_centered(|ui| crate::reset_button(ui, self, "Reset all")); } } diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 5fc3b39e..1523345f 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -7,8 +7,8 @@ use std::collections::BTreeMap; use epaint::{Rounding, Shadow, Stroke}; use crate::{ - ecolor::*, emath::*, ComboBox, CursorIcon, FontFamily, FontId, Margin, Response, RichText, - WidgetText, + ecolor::*, emath::*, ComboBox, CursorIcon, FontFamily, FontId, Grid, Margin, Response, + RichText, WidgetText, }; // ---------------------------------------------------------------------------- @@ -1305,19 +1305,21 @@ impl Style { visuals.light_dark_radio_buttons(ui); crate::Grid::new("_options").show(ui, |ui| { - ui.label("Override font id:"); - ui.horizontal(|ui| { - ui.radio_value(override_font_id, None, "None"); - if ui.radio(override_font_id.is_some(), "override").clicked() { - *override_font_id = Some(FontId::default()); - } + ui.label("Override font id"); + ui.vertical(|ui| { + ui.horizontal(|ui| { + ui.radio_value(override_font_id, None, "None"); + if ui.radio(override_font_id.is_some(), "override").clicked() { + *override_font_id = Some(FontId::default()); + } + }); if let Some(override_font_id) = override_font_id { crate::introspection::font_id_ui(ui, override_font_id); } }); ui.end_row(); - ui.label("Override text style:"); + ui.label("Override text style"); crate::ComboBox::from_id_source("Override text style") .selected_text(match override_text_style { None => "None".to_owned(), @@ -1334,7 +1336,7 @@ impl Style { }); ui.end_row(); - ui.label("Text style of DragValue:"); + ui.label("Text style of DragValue"); crate::ComboBox::from_id_source("drag_value_text_style") .selected_text(drag_value_text_style.to_string()) .show_ui(ui, |ui| { @@ -1347,10 +1349,11 @@ impl Style { }); ui.end_row(); - ui.label("Animation duration:"); + ui.label("Animation duration"); ui.add( - Slider::new(animation_time, 0.0..=1.0) - .clamp_to_range(true) + DragValue::new(animation_time) + .clamp_range(0.0..=1.0) + .speed(0.02) .suffix(" s"), ); ui.end_row(); @@ -1376,7 +1379,7 @@ impl Style { "If scrolling is enabled for only one direction, allow horizontal scrolling without pressing shift", ); - ui.vertical_centered(|ui| reset_button(ui, self)); + ui.vertical_centered(|ui| reset_button(ui, self, "Reset style")); } } @@ -1389,7 +1392,7 @@ fn text_styles_ui(ui: &mut Ui, text_styles: &mut BTreeMap) -> ui.end_row(); } }); - crate::reset_button_with(ui, text_styles, default_text_styles()); + crate::reset_button_with(ui, text_styles, "Reset text styles", default_text_styles()); }) .response } @@ -1418,72 +1421,85 @@ impl Spacing { scroll, } = self; - ui.add(slider_vec2(item_spacing, 0.0..=20.0, "Item spacing")); + Grid::new("spacing") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Item spacing"); + ui.add(two_drag_values(item_spacing, 0.0..=20.0)); + ui.end_row(); - margin_ui(ui, "Window margin:", window_margin); - margin_ui(ui, "Menu margin:", menu_margin); + ui.label("Window margin"); + ui.add(window_margin); + ui.end_row(); - ui.add(slider_vec2(button_padding, 0.0..=20.0, "Button padding")); - ui.add(slider_vec2(interact_size, 4.0..=60.0, "Interact size")) - .on_hover_text("Minimum size of an interactive widget"); - ui.horizontal(|ui| { - ui.add(DragValue::new(indent).clamp_range(0.0..=100.0)); - ui.label("Indent"); - }); - ui.horizontal(|ui| { - ui.add(DragValue::new(slider_width).clamp_range(0.0..=1000.0)); - ui.label("Slider width"); - }); - ui.horizontal(|ui| { - ui.add(DragValue::new(slider_rail_height).clamp_range(0.0..=50.0)); - ui.label("Slider rail height"); - }); - ui.horizontal(|ui| { - ui.add(DragValue::new(combo_width).clamp_range(0.0..=1000.0)); - ui.label("ComboBox width"); - }); - ui.horizontal(|ui| { - ui.add(DragValue::new(text_edit_width).clamp_range(0.0..=1000.0)); - ui.label("TextEdit width"); - }); + ui.label("Menu margin"); + ui.add(menu_margin); + ui.end_row(); - ui.collapsing("Scroll Area", |ui| { - scroll.ui(ui); - }); + ui.label("Button padding"); + ui.add(two_drag_values(button_padding, 0.0..=20.0)); + ui.end_row(); - ui.horizontal(|ui| { - ui.label("Checkboxes etc:"); - ui.add( - DragValue::new(icon_width) - .prefix("outer icon width:") - .clamp_range(0.0..=60.0), - ); - ui.add( - DragValue::new(icon_width_inner) - .prefix("inner icon width:") - .clamp_range(0.0..=60.0), - ); - ui.add( - DragValue::new(icon_spacing) - .prefix("spacing:") - .clamp_range(0.0..=10.0), - ); - }); + ui.label("Interact size") + .on_hover_text("Minimum size of an interactive widget"); + ui.add(two_drag_values(interact_size, 4.0..=60.0)); + ui.end_row(); - ui.horizontal(|ui| { - ui.add(DragValue::new(tooltip_width).clamp_range(0.0..=1000.0)); - ui.label("Tooltip wrap width"); - }); + ui.label("Indent"); + ui.add(DragValue::new(indent).clamp_range(0.0..=100.0)); + ui.end_row(); - ui.horizontal(|ui| { - ui.add(DragValue::new(menu_width).clamp_range(0.0..=1000.0)); - ui.label("Default width of a menu"); - }); + ui.label("Slider width"); + ui.add(DragValue::new(slider_width).clamp_range(0.0..=1000.0)); + ui.end_row(); - ui.horizontal(|ui| { - ui.add(DragValue::new(menu_spacing).clamp_range(0.0..=10.0)); - ui.label("Horizontal spacing between menus"); - }); + ui.label("Slider rail height"); + ui.add(DragValue::new(slider_rail_height).clamp_range(0.0..=50.0)); + ui.end_row(); + + ui.label("ComboBox width"); + ui.add(DragValue::new(combo_width).clamp_range(0.0..=1000.0)); + ui.end_row(); + + ui.label("TextEdit width"); + ui.add(DragValue::new(text_edit_width).clamp_range(0.0..=1000.0)); + ui.end_row(); + + ui.label("Tooltip wrap width"); + ui.add(DragValue::new(tooltip_width).clamp_range(0.0..=1000.0)); + ui.end_row(); + + ui.label("Default menu width"); + ui.add(DragValue::new(menu_width).clamp_range(0.0..=1000.0)); + ui.end_row(); + + ui.label("Menu spacing") + .on_hover_text("Horizontal spacing between menus"); + ui.add(DragValue::new(menu_spacing).clamp_range(0.0..=10.0)); + ui.end_row(); + + ui.label("Checkboxes etc"); + ui.vertical(|ui| { + ui.add( + DragValue::new(icon_width) + .prefix("outer icon width:") + .clamp_range(0.0..=60.0), + ); + ui.add( + DragValue::new(icon_width_inner) + .prefix("inner icon width:") + .clamp_range(0.0..=60.0), + ); + ui.add( + DragValue::new(icon_spacing) + .prefix("spacing:") + .clamp_range(0.0..=10.0), + ); + }); + ui.end_row(); + }); ui.checkbox( indent_ends_with_horizontal_line, @@ -1495,59 +1511,14 @@ impl Spacing { ui.add(DragValue::new(combo_height).clamp_range(0.0..=1000.0)); }); - ui.vertical_centered(|ui| reset_button(ui, self)); + ui.collapsing("Scroll Area", |ui| { + scroll.ui(ui); + }); + + ui.vertical_centered(|ui| reset_button(ui, self, "Reset spacing")); } } -fn margin_ui(ui: &mut Ui, text: &str, margin: &mut Margin) { - let margin_range = 0.0..=20.0; - - ui.horizontal(|ui| { - ui.label(text); - - let mut same = margin.is_same(); - ui.checkbox(&mut same, "Same"); - - if same { - let mut value = margin.left; - ui.add(DragValue::new(&mut value).clamp_range(margin_range.clone())); - *margin = Margin::same(value); - } else { - if margin.is_same() { - // HACK: prevent collapse: - margin.right = margin.left + 1.0; - margin.bottom = margin.left + 2.0; - margin.top = margin.left + 3.0; - } - - ui.add( - DragValue::new(&mut margin.left) - .clamp_range(margin_range.clone()) - .prefix("L: "), - ) - .on_hover_text("Left margin"); - ui.add( - DragValue::new(&mut margin.right) - .clamp_range(margin_range.clone()) - .prefix("R: "), - ) - .on_hover_text("Right margin"); - ui.add( - DragValue::new(&mut margin.top) - .clamp_range(margin_range.clone()) - .prefix("T: "), - ) - .on_hover_text("Top margin"); - ui.add( - DragValue::new(&mut margin.bottom) - .clamp_range(margin_range) - .prefix("B: "), - ) - .on_hover_text("Bottom margin"); - } - }); -} - impl Interaction { pub fn ui(&mut self, ui: &mut crate::Ui) { let Self { @@ -1559,21 +1530,42 @@ impl Interaction { selectable_labels, multi_widget_text_select, } = self; - ui.add(Slider::new(interact_radius, 0.0..=20.0).text("interact_radius")) - .on_hover_text("Interact with the closest widget within this radius."); - ui.add(Slider::new(resize_grab_radius_side, 0.0..=20.0).text("resize_grab_radius_side")); - ui.add( - Slider::new(resize_grab_radius_corner, 0.0..=20.0).text("resize_grab_radius_corner"), - ); + + ui.spacing_mut().item_spacing = vec2(12.0, 8.0); + + Grid::new("interaction") + .num_columns(2) + .striped(true) + .show(ui, |ui| { + ui.label("interact_radius") + .on_hover_text("Interact with the closest widget within this radius."); + ui.add(DragValue::new(interact_radius).clamp_range(0.0..=20.0)); + ui.end_row(); + + ui.label("resize_grab_radius_side").on_hover_text("Radius of the interactive area of the side of a window during drag-to-resize"); + ui.add(DragValue::new(resize_grab_radius_side).clamp_range(0.0..=20.0)); + ui.end_row(); + + ui.label("resize_grab_radius_corner").on_hover_text("Radius of the interactive area of the corner of a window during drag-to-resize."); + ui.add(DragValue::new(resize_grab_radius_corner).clamp_range(0.0..=20.0)); + ui.end_row(); + + ui.label("Tooltip delay").on_hover_text( + "Delay in seconds before showing tooltips after the mouse stops moving", + ); + ui.add( + DragValue::new(tooltip_delay) + .clamp_range(0.0..=1.0) + .speed(0.05) + .suffix(" s"), + ); + ui.end_row(); + }); + ui.checkbox( show_tooltips_only_when_still, "Only show tooltips if mouse is still", ); - ui.add( - Slider::new(tooltip_delay, 0.0..=1.0) - .suffix(" s") - .text("tooltip_delay"), - ); ui.horizontal(|ui| { ui.checkbox(selectable_labels, "Selectable text in labels"); @@ -1582,7 +1574,7 @@ impl Interaction { } }); - ui.vertical_centered(|ui| reset_button(ui, self)); + ui.vertical_centered(|ui| reset_button(ui, self, "Reset interaction settings")); } } @@ -1627,8 +1619,16 @@ impl Selection { pub fn ui(&mut self, ui: &mut crate::Ui) { let Self { bg_fill, stroke } = self; ui.label("Selectable labels"); - ui_color(ui, bg_fill, "background fill"); - stroke_ui(ui, stroke, "stroke"); + + Grid::new("selectiom").num_columns(2).show(ui, |ui| { + ui.label("Background fill"); + ui.color_edit_button_srgba(bg_fill); + ui.end_row(); + + ui.label("Stroke"); + ui.add(stroke); + ui.end_row(); + }); } } @@ -1642,17 +1642,39 @@ impl WidgetVisuals { fg_stroke, expansion, } = self; - ui_color(ui, weak_bg_fill, "optional background fill") - .on_hover_text("For buttons, combo-boxes, etc"); - ui_color(ui, mandatory_bg_fill, "mandatory background fill") - .on_hover_text("For checkboxes, sliders, etc"); - stroke_ui(ui, bg_stroke, "background stroke"); - rounding_ui(ui, rounding); + Grid::new("widget") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Optional background fill") + .on_hover_text("For buttons, combo-boxes, etc"); + ui.color_edit_button_srgba(weak_bg_fill); + ui.end_row(); - stroke_ui(ui, fg_stroke, "foreground stroke (text)"); - ui.add(Slider::new(expansion, -5.0..=5.0).text("expansion")) - .on_hover_text("make shapes this much larger"); + ui.label("Mandatory background fill") + .on_hover_text("For checkboxes, sliders, etc"); + ui.color_edit_button_srgba(mandatory_bg_fill); + ui.end_row(); + + ui.label("Background stroke"); + ui.add(bg_stroke); + ui.end_row(); + + ui.label("Rounding"); + ui.add(rounding); + ui.end_row(); + + ui.label("Foreground stroke (text)"); + ui.add(fg_stroke); + ui.end_row(); + + ui.label("Expansion") + .on_hover_text("make shapes this much larger"); + ui.add(DragValue::new(expansion).speed(0.1)); + ui.end_row(); + }); } } @@ -1745,79 +1767,123 @@ impl Visuals { }); ui.collapsing("Window", |ui| { - ui_color(ui, window_fill, "Fill"); - stroke_ui(ui, window_stroke, "Outline"); - rounding_ui(ui, window_rounding); - shadow_ui(ui, window_shadow, "Shadow"); + Grid::new("window") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Fill"); + ui.color_edit_button_srgba(window_fill); + ui.end_row(); + + ui.label("Stroke"); + ui.add(window_stroke); + ui.end_row(); + + ui.label("Rounding"); + ui.add(window_rounding); + ui.end_row(); + + ui.label("Shadow"); + ui.add(window_shadow); + ui.end_row(); + }); + ui.checkbox(window_highlight_topmost, "Highlight topmost Window"); }); ui.collapsing("Menus and popups", |ui| { - rounding_ui(ui, menu_rounding); - shadow_ui(ui, popup_shadow, "Shadow"); + Grid::new("menus_and_popups") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Rounding"); + ui.add(menu_rounding); + ui.end_row(); + + ui.label("Shadow"); + ui.add(popup_shadow); + ui.end_row(); + }); }); ui.collapsing("Widgets", |ui| widgets.ui(ui)); ui.collapsing("Selection", |ui| selection.ui(ui)); - ui.horizontal(|ui| { - ui_color( - ui, - &mut widgets.noninteractive.fg_stroke.color, - "Text color", - ); - ui_color(ui, warn_fg_color, RichText::new("Warnings")); - ui_color(ui, error_fg_color, RichText::new("Errors")); - }); - - ui_color(ui, code_bg_color, RichText::new("Code background").code()).on_hover_ui(|ui| { + ui.collapsing("Other colors", |ui| { ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 0.0; - ui.label("For monospaced inlined text "); - ui.code("like this"); - ui.label("."); + ui_color( + ui, + &mut widgets.noninteractive.fg_stroke.color, + "Text color", + ); + ui_color(ui, warn_fg_color, RichText::new("Warnings")); + ui_color(ui, error_fg_color, RichText::new("Errors")); + }); + + ui_color(ui, code_bg_color, RichText::new("Code background").code()).on_hover_ui( + |ui| { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("For monospaced inlined text "); + ui.code("like this"); + ui.label("."); + }); + }, + ); + + ui_color(ui, hyperlink_color, "hyperlink_color"); + + ui.horizontal(|ui| { + ui.label("Text cursor"); + ui.add(text_cursor); }); }); - ui_color(ui, hyperlink_color, "hyperlink_color"); - stroke_ui(ui, text_cursor, "Text Cursor"); + ui.collapsing("Misc", |ui| { + ui.add(Slider::new(resize_corner_size, 0.0..=20.0).text("resize_corner_size")); + ui.checkbox(text_cursor_preview, "Preview text cursor on hover"); + ui.add(Slider::new(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin")); - ui.add(Slider::new(resize_corner_size, 0.0..=20.0).text("resize_corner_size")); - ui.checkbox(text_cursor_preview, "Preview text cursor on hover"); - ui.add(Slider::new(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin")); + ui.checkbox(button_frame, "Button has a frame"); + ui.checkbox(collapsing_header_frame, "Collapsing header has a frame"); + ui.checkbox( + indent_has_left_vline, + "Paint a vertical line to the left of indented regions", + ); - ui.checkbox(button_frame, "Button has a frame"); - ui.checkbox(collapsing_header_frame, "Collapsing header has a frame"); - ui.checkbox( - indent_has_left_vline, - "Paint a vertical line to the left of indented regions", - ); + ui.checkbox(striped, "Default stripes on grids and tables"); - ui.checkbox(striped, "By default, add stripes to grids and tables?"); + ui.checkbox(slider_trailing_fill, "Add trailing color to sliders"); - ui.checkbox(slider_trailing_fill, "Add trailing color to sliders"); + handle_shape.ui(ui); - handle_shape.ui(ui); + ComboBox::from_label("Interact cursor") + .selected_text( + interact_cursor.map_or_else(|| "-".to_owned(), |cursor| format!("{cursor:?}")), + ) + .show_ui(ui, |ui| { + ui.selectable_value(interact_cursor, None, "-"); - ComboBox::from_label("Interact Cursor") - .selected_text(format!("{interact_cursor:?}")) - .show_ui(ui, |ui| { - ui.selectable_value(interact_cursor, None, "None"); + for cursor in CursorIcon::ALL { + ui.selectable_value(interact_cursor, Some(cursor), format!("{cursor:?}")) + .on_hover_cursor(cursor); + } + }) + .response + .on_hover_text("Use this cursor when hovering buttons etc"); - for icon in CursorIcon::ALL { - ui.selectable_value(interact_cursor, Some(icon), format!("{icon:?}")); - } + ui.checkbox(image_loading_spinners, "Image loading spinners") + .on_hover_text("Show a spinner when an Image is loading"); + + ui.horizontal(|ui| { + ui.label("Color picker type"); + numeric_color_space.toggle_button_ui(ui); }); - - ui.checkbox(image_loading_spinners, "Image loading spinners") - .on_hover_text("Show a spinner when an Image is loading"); - - ui.horizontal(|ui| { - ui.label("Color picker type:"); - numeric_color_space.toggle_button_ui(ui); }); - ui.vertical_centered(|ui| reset_button(ui, self)); + ui.vertical_centered(|ui| reset_button(ui, self, "Reset visuals")); } } @@ -1862,16 +1928,12 @@ impl DebugOptions { ui.checkbox(show_widget_hits, "Show widgets under mouse pointer"); - ui.vertical_centered(|ui| reset_button(ui, self)); + ui.vertical_centered(|ui| reset_button(ui, self, "Reset debug options")); } } -// TODO(emilk): improve and standardize `slider_vec2` -fn slider_vec2<'a>( - value: &'a mut Vec2, - range: std::ops::RangeInclusive, - text: &'a str, -) -> impl Widget + 'a { +// TODO(emilk): improve and standardize +fn two_drag_values(value: &mut Vec2, range: std::ops::RangeInclusive) -> impl Widget + '_ { move |ui: &mut crate::Ui| { ui.horizontal(|ui| { ui.add( @@ -1884,7 +1946,6 @@ fn slider_vec2<'a>( .clamp_range(range.clone()) .prefix("y: "), ); - ui.label(text); }) .response } @@ -1898,36 +1959,10 @@ fn ui_color(ui: &mut Ui, srgba: &mut Color32, label: impl Into) -> R .response } -fn rounding_ui(ui: &mut Ui, rounding: &mut Rounding) { - const MAX: f32 = 20.0; - let mut same = rounding.is_same(); - ui.group(|ui| { - ui.horizontal(|ui| { - ui.label("Rounding: "); - ui.radio_value(&mut same, true, "Same"); - ui.radio_value(&mut same, false, "Separate"); - }); - - if same { - let mut cr = rounding.nw; - ui.add(Slider::new(&mut cr, 0.0..=MAX)); - *rounding = Rounding::same(cr); - } else { - ui.add(Slider::new(&mut rounding.nw, 0.0..=MAX).text("North-West")); - ui.add(Slider::new(&mut rounding.ne, 0.0..=MAX).text("North-East")); - ui.add(Slider::new(&mut rounding.sw, 0.0..=MAX).text("South-West")); - ui.add(Slider::new(&mut rounding.se, 0.0..=MAX).text("South-East")); - if rounding.is_same() { - rounding.se *= 1.00001; - } - } - }); -} - impl HandleShape { pub fn ui(&mut self, ui: &mut Ui) { - ui.label("Widget handle shape"); ui.horizontal(|ui| { + ui.label("Slider handle"); ui.radio_value(self, Self::Circle, "Circle"); if ui .radio(matches!(self, Self::Rect { .. }), "Rectangle") @@ -1983,3 +2018,213 @@ impl std::fmt::Display for NumericColorSpace { } } } + +impl Widget for &mut Margin { + fn ui(self, ui: &mut Ui) -> Response { + let mut same = self.is_same(); + + let response = if same { + ui.horizontal(|ui| { + ui.checkbox(&mut same, "same"); + + let mut value = self.left; + ui.add(DragValue::new(&mut value)); + *self = Margin::same(value); + }) + .response + } else { + ui.vertical(|ui| { + ui.checkbox(&mut same, "same"); + + crate::Grid::new("margin").num_columns(2).show(ui, |ui| { + ui.label("Left"); + ui.add(DragValue::new(&mut self.left)); + ui.end_row(); + + ui.label("Right"); + ui.add(DragValue::new(&mut self.right)); + ui.end_row(); + + ui.label("Top"); + ui.add(DragValue::new(&mut self.top)); + ui.end_row(); + + ui.label("Bottom"); + ui.add(DragValue::new(&mut self.bottom)); + ui.end_row(); + }); + }) + .response + }; + + // Apply the checkbox: + if same { + *self = Margin::same((self.left + self.right + self.top + self.bottom) / 4.0); + } else if self.is_same() { + self.right *= 1.00001; // prevent collapsing into sameness + } + + response + } +} + +impl Widget for &mut Rounding { + fn ui(self, ui: &mut Ui) -> Response { + let mut same = self.is_same(); + + let response = if same { + ui.horizontal(|ui| { + ui.checkbox(&mut same, "same"); + + let mut cr = self.nw; + ui.add(DragValue::new(&mut cr).clamp_range(0.0..=f32::INFINITY)); + *self = Rounding::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).clamp_range(0.0..=f32::INFINITY)); + ui.end_row(); + + ui.label("NE"); + ui.add(DragValue::new(&mut self.ne).clamp_range(0.0..=f32::INFINITY)); + ui.end_row(); + + ui.label("SW"); + ui.add(DragValue::new(&mut self.sw).clamp_range(0.0..=f32::INFINITY)); + ui.end_row(); + + ui.label("SE"); + ui.add(DragValue::new(&mut self.se).clamp_range(0.0..=f32::INFINITY)); + ui.end_row(); + }); + }) + .response + }; + + // Apply the checkbox: + if same { + *self = Rounding::same((self.nw + self.ne + self.sw + self.se) / 4.0); + } else if self.is_same() { + self.se *= 1.00001; // prevent collapsing into sameness + } + + response + } +} + +impl Widget for &mut Shadow { + fn ui(self, ui: &mut Ui) -> Response { + let epaint::Shadow { + offset, + blur, + spread, + color, + } = self; + + ui.vertical(|ui| { + crate::Grid::new("shadow_ui").show(ui, |ui| { + ui.add( + DragValue::new(&mut offset.x) + .speed(1.0) + .clamp_range(-100.0..=100.0) + .prefix("x: "), + ); + ui.add( + DragValue::new(&mut offset.y) + .speed(1.0) + .clamp_range(-100.0..=100.0) + .prefix("y: "), + ); + ui.end_row(); + + ui.add( + DragValue::new(blur) + .speed(1.0) + .clamp_range(0.0..=100.0) + .prefix("blur: "), + ); + + ui.add( + DragValue::new(spread) + .speed(1.0) + .clamp_range(0.0..=100.0) + .prefix("spread: "), + ); + }); + ui.color_edit_button_srgba(color); + }) + .response + } +} + +impl Widget for &mut Stroke { + fn ui(self, ui: &mut Ui) -> Response { + let Stroke { width, color } = self; + + ui.horizontal(|ui| { + ui.add( + DragValue::new(width) + .speed(0.1) + .clamp_range(0.0..=f32::INFINITY), + ) + .on_hover_text("Width"); + ui.color_edit_button_srgba(color); + + // stroke preview: + let (_id, stroke_rect) = ui.allocate_space(ui.spacing().interact_size); + let left = stroke_rect.left_center(); + let right = stroke_rect.right_center(); + ui.painter().line_segment([left, right], (*width, *color)); + }) + .response + } +} + +impl Widget for &mut crate::Frame { + fn ui(self, ui: &mut Ui) -> Response { + let crate::Frame { + inner_margin, + outer_margin, + rounding, + shadow, + fill, + stroke, + } = self; + + crate::Grid::new("frame") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Inner margin"); + ui.add(inner_margin); + ui.end_row(); + + ui.label("Outer margin"); + ui.add(outer_margin); + ui.end_row(); + + ui.label("Rounding"); + ui.add(rounding); + ui.end_row(); + + ui.label("Shadow"); + ui.add(shadow); + ui.end_row(); + + ui.label("Fill"); + ui.color_edit_button_srgba(fill); + ui.end_row(); + + ui.label("Stroke"); + ui.add(stroke); + ui.end_row(); + }) + .response + } +} diff --git a/crates/egui/src/widgets/mod.rs b/crates/egui/src/widgets/mod.rs index 77ee8692..6b3fb72e 100644 --- a/crates/egui/src/widgets/mod.rs +++ b/crates/egui/src/widgets/mod.rs @@ -92,15 +92,19 @@ pub trait WidgetWithState { /// Show a button to reset a value to its default. /// The button is only enabled if the value does not already have its original value. -pub fn reset_button(ui: &mut Ui, value: &mut T) { - reset_button_with(ui, value, T::default()); +/// +/// The `text` could be something like "Reset foo". +pub fn reset_button(ui: &mut Ui, value: &mut T, text: &str) { + reset_button_with(ui, value, text, T::default()); } /// Show a button to reset a value to its default. /// The button is only enabled if the value does not already have its original value. -pub fn reset_button_with(ui: &mut Ui, value: &mut T, reset_value: T) { +/// +/// The `text` could be something like "Reset foo". +pub fn reset_button_with(ui: &mut Ui, value: &mut T, text: &str, reset_value: T) { if ui - .add_enabled(*value != reset_value, Button::new("Reset")) + .add_enabled(*value != reset_value, Button::new(text)) .clicked() { *value = reset_value; @@ -109,62 +113,11 @@ pub fn reset_button_with(ui: &mut Ui, value: &mut T, reset_value: // ---------------------------------------------------------------------------- +#[deprecated = "Use `ui.add(&mut stroke)` instead"] pub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) { - let epaint::Stroke { width, color } = stroke; ui.horizontal(|ui| { - ui.add(DragValue::new(width).speed(0.1).clamp_range(0.0..=5.0)) - .on_hover_text("Width"); - ui.color_edit_button_srgba(color); ui.label(text); - - // stroke preview: - let (_id, stroke_rect) = ui.allocate_space(ui.spacing().interact_size); - let left = stroke_rect.left_center(); - let right = stroke_rect.right_center(); - ui.painter().line_segment([left, right], (*width, *color)); - }); -} - -pub(crate) fn shadow_ui(ui: &mut Ui, shadow: &mut epaint::Shadow, text: &str) { - let epaint::Shadow { - offset, - blur, - spread, - color, - } = shadow; - - ui.label(text); - ui.indent(text, |ui| { - crate::Grid::new("shadow_ui").show(ui, |ui| { - ui.add( - DragValue::new(&mut offset.x) - .speed(1.0) - .clamp_range(-100.0..=100.0) - .prefix("x: "), - ); - ui.add( - DragValue::new(&mut offset.y) - .speed(1.0) - .clamp_range(-100.0..=100.0) - .prefix("y: "), - ); - ui.end_row(); - - ui.add( - DragValue::new(blur) - .speed(1.0) - .clamp_range(0.0..=100.0) - .prefix("Blur:"), - ); - - ui.add( - DragValue::new(spread) - .speed(1.0) - .clamp_range(0.0..=100.0) - .prefix("Spread:"), - ); - }); - ui.color_edit_button_srgba(color); + ui.add(stroke); }); } diff --git a/crates/egui_demo_app/src/apps/fractal_clock.rs b/crates/egui_demo_app/src/apps/fractal_clock.rs index 6afe6f04..1a6f4f55 100644 --- a/crates/egui_demo_app/src/apps/fractal_clock.rs +++ b/crates/egui_demo_app/src/apps/fractal_clock.rs @@ -79,7 +79,7 @@ impl FractalClock { ui.add(Slider::new(&mut self.luminance_factor, 0.0..=1.0).text("luminance factor")); ui.add(Slider::new(&mut self.width_factor, 0.0..=1.0).text("width factor")); - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.hyperlink_to( "Inspired by a screensaver by Rob Mayoff", diff --git a/crates/egui_demo_app/src/wrap_app.rs b/crates/egui_demo_app/src/wrap_app.rs index e87814cb..db3ee385 100644 --- a/crates/egui_demo_app/src/wrap_app.rs +++ b/crates/egui_demo_app/src/wrap_app.rs @@ -245,7 +245,13 @@ impl eframe::App for WrapApp { } fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] { - visuals.panel_fill.to_normalized_gamma_f32() + // Give the area behind the floating windows a different color, because it looks better: + let color = egui::lerp( + egui::Rgba::from(visuals.panel_fill)..=egui::Rgba::from(visuals.extreme_bg_color), + 0.5, + ); + let color = egui::Color32::from(color); + color.to_normalized_gamma_f32() } fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { 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 de1d96cd..505e938d 100644 --- a/crates/egui_demo_lib/src/demo/demo_app_windows.rs +++ b/crates/egui_demo_lib/src/demo/demo_app_windows.rs @@ -29,6 +29,7 @@ impl Default for Demos { Box::::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), Box::::default(), Box::::default(), diff --git a/crates/egui_demo_lib/src/demo/frame_demo.rs b/crates/egui_demo_lib/src/demo/frame_demo.rs new file mode 100644 index 00000000..b4013c93 --- /dev/null +++ b/crates/egui_demo_lib/src/demo/frame_demo.rs @@ -0,0 +1,73 @@ +/// Shows off a table with dynamic layout +#[derive(PartialEq)] +pub struct FrameDemo { + frame: egui::Frame, +} + +impl Default for FrameDemo { + fn default() -> Self { + Self { + frame: egui::Frame { + inner_margin: 12.0.into(), + outer_margin: 24.0.into(), + rounding: 14.0.into(), + shadow: egui::Shadow { + offset: [8.0, 12.0].into(), + blur: 16.0, + spread: 0.0, + color: egui::Color32::from_black_alpha(180), + }, + fill: egui::Color32::from_rgba_unmultiplied(97, 0, 255, 128), + stroke: egui::Stroke::new(1.0, egui::Color32::GRAY), + }, + } + } +} + +impl super::Demo for FrameDemo { + fn name(&self) -> &'static str { + "▣ Frame" + } + + fn show(&mut self, ctx: &egui::Context, open: &mut bool) { + egui::Window::new(self.name()) + .open(open) + .resizable(false) + .show(ctx, |ui| { + use super::View as _; + self.ui(ui); + }); + } +} + +impl super::View for FrameDemo { + fn ui(&mut self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.vertical(|ui| { + ui.add(&mut self.frame); + + ui.add_space(8.0); + ui.set_max_width(ui.min_size().x); + ui.vertical_centered(|ui| egui::reset_button(ui, self, "Reset")); + }); + + ui.separator(); + + ui.vertical(|ui| { + // 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) + .show(ui, |ui| { + self.frame.show(ui, |ui| { + ui.label(egui::RichText::new("Content").color(egui::Color32::WHITE)); + }); + }); + }); + }); + + ui.set_max_width(ui.min_size().x); + ui.separator(); + ui.vertical_centered(|ui| ui.add(crate::egui_github_link_file!())); + } +} 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 b4b28511..f3c7a8dc 100644 --- a/crates/egui_demo_lib/src/demo/misc_demo_window.rs +++ b/crates/egui_demo_lib/src/demo/misc_demo_window.rs @@ -328,7 +328,7 @@ impl Default for ColorWidgets { impl ColorWidgets { fn ui(&mut self, ui: &mut Ui) { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.label("egui lets you edit colors stored as either sRGBA or linear RGBA and with or without premultiplied alpha"); diff --git a/crates/egui_demo_lib/src/demo/mod.rs b/crates/egui_demo_lib/src/demo/mod.rs index d54498bb..8a911c9d 100644 --- a/crates/egui_demo_lib/src/demo/mod.rs +++ b/crates/egui_demo_lib/src/demo/mod.rs @@ -13,6 +13,7 @@ pub mod demo_app_windows; pub mod drag_and_drop; pub mod extra_viewport; pub mod font_book; +pub mod frame_demo; pub mod highlighting; pub mod layout_test; pub mod misc_demo_window; diff --git a/crates/egui_demo_lib/src/demo/paint_bezier.rs b/crates/egui_demo_lib/src/demo/paint_bezier.rs index ad507de5..99d477b4 100644 --- a/crates/egui_demo_lib/src/demo/paint_bezier.rs +++ b/crates/egui_demo_lib/src/demo/paint_bezier.rs @@ -43,13 +43,27 @@ impl Default for PaintBezier { impl PaintBezier { pub fn ui_control(&mut self, ui: &mut egui::Ui) { ui.collapsing("Colors", |ui| { - ui.horizontal(|ui| { - ui.label("Fill color:"); - ui.color_edit_button_srgba(&mut self.fill); - }); - egui::stroke_ui(ui, &mut self.stroke, "Curve Stroke"); - egui::stroke_ui(ui, &mut self.aux_stroke, "Auxiliary Stroke"); - egui::stroke_ui(ui, &mut self.bounding_box_stroke, "Bounding Box Stroke"); + Grid::new("colors") + .num_columns(2) + .spacing([12.0, 8.0]) + .striped(true) + .show(ui, |ui| { + ui.label("Fill color"); + ui.color_edit_button_srgba(&mut self.fill); + ui.end_row(); + + ui.label("Curve Stroke"); + ui.add(&mut self.stroke); + ui.end_row(); + + ui.label("Auxiliary Stroke"); + ui.add(&mut self.aux_stroke); + ui.end_row(); + + ui.label("Bounding Box Stroke"); + ui.add(&mut self.bounding_box_stroke); + ui.end_row(); + }); }); ui.collapsing("Global tessellation options", |ui| { diff --git a/crates/egui_demo_lib/src/demo/painting.rs b/crates/egui_demo_lib/src/demo/painting.rs index 57e5f114..d95e8534 100644 --- a/crates/egui_demo_lib/src/demo/painting.rs +++ b/crates/egui_demo_lib/src/demo/painting.rs @@ -20,7 +20,8 @@ impl Default for Painting { impl Painting { pub fn ui_control(&mut self, ui: &mut egui::Ui) -> egui::Response { ui.horizontal(|ui| { - egui::stroke_ui(ui, &mut self.stroke, "Stroke"); + ui.label("Stroke:"); + ui.add(&mut self.stroke); ui.separator(); if ui.button("Clear Painting").clicked() { self.lines.clear(); diff --git a/crates/egui_demo_lib/src/demo/plot_demo.rs b/crates/egui_demo_lib/src/demo/plot_demo.rs index 0f8b8edc..12e45aa0 100644 --- a/crates/egui_demo_lib/src/demo/plot_demo.rs +++ b/crates/egui_demo_lib/src/demo/plot_demo.rs @@ -62,7 +62,7 @@ impl super::Demo for PlotDemo { impl super::View for PlotDemo { fn ui(&mut self, ui: &mut Ui) { ui.horizontal(|ui| { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.collapsing("Instructions", |ui| { ui.label("Pan by dragging, or scroll (+ shift = horizontal)."); ui.label("Box zooming: Right click to zoom in and zoom out using a selection."); diff --git a/crates/egui_demo_lib/src/demo/scrolling.rs b/crates/egui_demo_lib/src/demo/scrolling.rs index 0530f2fe..9c490d23 100644 --- a/crates/egui_demo_lib/src/demo/scrolling.rs +++ b/crates/egui_demo_lib/src/demo/scrolling.rs @@ -336,7 +336,7 @@ impl super::View for ScrollTo { ui.separator(); ui.vertical_centered(|ui| { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.add(crate::egui_github_link_file!()); }); } diff --git a/crates/egui_demo_lib/src/demo/sliders.rs b/crates/egui_demo_lib/src/demo/sliders.rs index d2b06540..4142c7af 100644 --- a/crates/egui_demo_lib/src/demo/sliders.rs +++ b/crates/egui_demo_lib/src/demo/sliders.rs @@ -198,7 +198,7 @@ impl super::View for Sliders { ui.add_space(8.0); ui.vertical_centered(|ui| { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.add(crate::egui_github_link_file!()); }); } diff --git a/crates/egui_demo_lib/src/demo/strip_demo.rs b/crates/egui_demo_lib/src/demo/strip_demo.rs index defeac82..091d4f01 100644 --- a/crates/egui_demo_lib/src/demo/strip_demo.rs +++ b/crates/egui_demo_lib/src/demo/strip_demo.rs @@ -8,7 +8,7 @@ pub struct StripDemo {} impl super::Demo for StripDemo { fn name(&self) -> &'static str { - "▣ Strip Demo" + "▣ Strip" } fn show(&mut self, ctx: &egui::Context, open: &mut bool) { diff --git a/crates/egui_demo_lib/src/demo/table_demo.rs b/crates/egui_demo_lib/src/demo/table_demo.rs index f15d2df9..4cf0449f 100644 --- a/crates/egui_demo_lib/src/demo/table_demo.rs +++ b/crates/egui_demo_lib/src/demo/table_demo.rs @@ -40,7 +40,7 @@ impl Default for TableDemo { impl super::Demo for TableDemo { fn name(&self) -> &'static str { - "☰ Table Demo" + "☰ Table" } fn show(&mut self, ctx: &egui::Context, open: &mut bool) { diff --git a/crates/egui_demo_lib/src/demo/tests.rs b/crates/egui_demo_lib/src/demo/tests.rs index 8d45de1e..a29e37fe 100644 --- a/crates/egui_demo_lib/src/demo/tests.rs +++ b/crates/egui_demo_lib/src/demo/tests.rs @@ -131,7 +131,7 @@ impl super::Demo for ManualLayoutTest { impl super::View for ManualLayoutTest { fn ui(&mut self, ui: &mut egui::Ui) { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); let Self { widget_offset, @@ -298,7 +298,7 @@ impl super::View for TableTest { }); ui.vertical_centered(|ui| { - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.add(crate::egui_github_link_file!()); }); } diff --git a/crates/egui_demo_lib/src/demo/window_options.rs b/crates/egui_demo_lib/src/demo/window_options.rs index baa6eac6..54c77988 100644 --- a/crates/egui_demo_lib/src/demo/window_options.rs +++ b/crates/egui_demo_lib/src/demo/window_options.rs @@ -146,7 +146,7 @@ impl super::View for WindowOptions { if ui.button("Disable for 2 seconds").clicked() { self.disabled_time = ui.input(|i| i.time); } - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.add(crate::egui_github_link_file!()); }); } diff --git a/crates/egui_demo_lib/src/easy_mark/easy_mark_editor.rs b/crates/egui_demo_lib/src/easy_mark/easy_mark_editor.rs index 2a44d78f..0b0bb960 100644 --- a/crates/egui_demo_lib/src/easy_mark/easy_mark_editor.rs +++ b/crates/egui_demo_lib/src/easy_mark/easy_mark_editor.rs @@ -48,7 +48,7 @@ impl EasyMarkEditor { let _ = ui.button("Hotkeys").on_hover_ui(nested_hotkeys_ui); ui.checkbox(&mut self.show_rendered, "Show rendered"); ui.checkbox(&mut self.highlight_editor, "Highlight editor"); - egui::reset_button(ui, self); + egui::reset_button(ui, self, "Reset"); ui.end_row(); }); ui.separator();