Improve the UI for changing the egui theme (#4257)

I added a new demo - a `Frame` editor:

![frame-editor](https://github.com/emilk/egui/assets/1148717/d0bec169-c211-45a3-9f53-5059fb8fc224)

This whole menu is now just a a bit nicer to use:
<img width="406" alt="Screenshot 2024-03-28 at 09 49 16"
src="https://github.com/emilk/egui/assets/1148717/32d12067-7cf0-4312-aa12-42909a5ed5ac">
This commit is contained in:
Emil Ernerfeldt 2024-03-28 10:09:28 +01:00 committed by GitHub
parent e183655aac
commit 3dba73e63e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 635 additions and 334 deletions

View File

@ -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.

View File

@ -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<Arc<Style>>) {

View File

@ -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
}

View File

@ -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 {

View File

@ -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"));
}
}

View File

@ -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<TextStyle, FontId>) ->
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<f32>,
text: &'a str,
) -> impl Widget + 'a {
// TODO(emilk): improve and standardize
fn two_drag_values(value: &mut Vec2, range: std::ops::RangeInclusive<f32>) -> 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<WidgetText>) -> 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
}
}

View File

@ -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<T: Default + PartialEq>(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<T: Default + PartialEq>(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<T: PartialEq>(ui: &mut Ui, value: &mut T, reset_value: T) {
///
/// The `text` could be something like "Reset foo".
pub fn reset_button_with<T: PartialEq>(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<T: PartialEq>(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);
});
}

View File

@ -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",

View File

@ -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) {

View File

@ -29,6 +29,7 @@ impl Default for Demos {
Box::<super::drag_and_drop::DragAndDropDemo>::default(),
Box::<super::extra_viewport::ExtraViewport>::default(),
Box::<super::font_book::FontBook>::default(),
Box::<super::frame_demo::FrameDemo>::default(),
Box::<super::MiscDemoWindow>::default(),
Box::<super::multi_touch::MultiTouch>::default(),
Box::<super::painting::Painting>::default(),

View File

@ -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!()));
}
}

View File

@ -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");

View File

@ -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;

View File

@ -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| {

View File

@ -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();

View File

@ -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.");

View File

@ -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!());
});
}

View File

@ -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!());
});
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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!());
});
}

View File

@ -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!());
});
}

View File

@ -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();