Browse Source

Add ui.set_enabled(false) to disable all widgets in a Ui

Closes https://github.com/emilk/egui/issues/50
demo-node-graph
Emil Ernerfeldt 4 years ago
parent
commit
bca722ddf8
  1. 3
      CHANGELOG.md
  2. 28
      egui/src/containers/area.rs
  3. 22
      egui/src/containers/frame.rs
  4. 10
      egui/src/containers/window.rs
  5. 10
      egui/src/context.rs
  6. 37
      egui/src/painter.rs
  7. 24
      egui/src/response.rs
  8. 39
      egui/src/style.rs
  9. 40
      egui/src/ui.rs
  10. 22
      egui/src/widgets/button.rs
  11. 18
      egui/src/widgets/label.rs
  12. 3
      egui/src/widgets/selected_label.rs
  13. 16
      egui_demo_lib/src/apps/demo/drag_and_drop.rs
  14. 9
      egui_demo_lib/src/apps/demo/toggle_switch.rs
  15. 17
      egui_demo_lib/src/apps/demo/widget_gallery.rs
  16. 3
      egui_demo_lib/src/apps/demo/widgets.rs
  17. 16
      egui_demo_lib/src/apps/demo/window_options.rs
  18. 27
      epaint/src/color.rs
  19. 1
      epaint/src/lib.rs
  20. 35
      epaint/src/shape_transform.rs

3
CHANGELOG.md

@ -13,7 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Add support for secondary and middle mouse buttons.
* Add `Label` methods for code, strong, strikethrough, underline and italics.
* Add `ui.group(|ui| { … })` to visually group some widgets within a frame
* Add `ui.group(|ui| { … })` to visually group some widgets within a frame.
* Add `ui.set_enabled(false)` to disable all widgets in a `Ui` (grayed out and non-interactive).
* Add `TextEdit::hint_text` for showing a weak hint text when empty.
* `egui::popup::popup_below_widget`: show a popup area below another widget.
* Add `Slider::clamp_to_range(bool)`: if set, clamp the incoming and outgoing values to the slider range.

28
egui/src/containers/area.rs

@ -45,6 +45,7 @@ pub struct Area {
pub(crate) id: Id,
movable: bool,
interactable: bool,
enabled: bool,
order: Order,
default_pos: Option<Pos2>,
new_pos: Option<Pos2>,
@ -56,6 +57,7 @@ impl Area {
id: Id::new(id_source),
movable: true,
interactable: true,
enabled: true,
order: Order::Middle,
default_pos: None,
new_pos: None,
@ -71,6 +73,15 @@ impl Area {
LayerId::new(self.order, self.id)
}
/// If false, no content responds to click
/// and widgets will be shown grayed out.
/// You won't be able to move the window.
/// Default: `true`.
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
/// moveable by dragging the area?
pub fn movable(mut self, movable: bool) -> Self {
self.movable = movable;
@ -78,8 +89,12 @@ impl Area {
self
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn is_movable(&self) -> bool {
self.movable
self.movable && self.enabled
}
/// If false, clicks goes straight through to what is behind us.
@ -121,6 +136,7 @@ pub(crate) struct Prepared {
layer_id: LayerId,
state: State,
movable: bool,
enabled: bool,
}
impl Area {
@ -130,6 +146,7 @@ impl Area {
movable,
order,
interactable,
enabled,
default_pos,
new_pos,
} = self;
@ -149,6 +166,7 @@ impl Area {
layer_id,
state,
movable,
enabled,
}
}
@ -214,13 +232,15 @@ impl Prepared {
clip_rect = clip_rect.intersect(central_area);
}
Ui::new(
let mut ui = Ui::new(
ctx.clone(),
self.layer_id,
self.layer_id.id,
max_rect,
clip_rect,
)
);
ui.set_enabled(self.enabled);
ui
}
#[allow(clippy::needless_pass_by_value)] // intentional to swallow up `content_ui`.
@ -229,6 +249,7 @@ impl Prepared {
layer_id,
mut state,
movable,
enabled,
} = self;
state.size = content_ui.min_rect().size();
@ -247,6 +268,7 @@ impl Prepared {
interact_id,
state.rect(),
sense,
enabled,
);
if move_response.dragged() && movable {

22
egui/src/containers/frame.rs

@ -23,7 +23,7 @@ impl Frame {
Self {
margin: Vec2::new(8.0, 6.0),
corner_radius: 4.0,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
stroke: style.visuals.window_stroke(),
..Default::default()
}
}
@ -32,8 +32,8 @@ impl Frame {
Self {
margin: Vec2::new(8.0, 2.0),
corner_radius: 0.0,
fill: style.visuals.widgets.noninteractive.bg_fill,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(),
..Default::default()
}
}
@ -42,7 +42,7 @@ impl Frame {
Self {
margin: Vec2::new(8.0, 8.0),
corner_radius: 0.0,
fill: style.visuals.widgets.noninteractive.bg_fill,
fill: style.visuals.window_fill(),
stroke: Default::default(),
..Default::default()
}
@ -53,8 +53,8 @@ impl Frame {
margin: style.spacing.window_padding,
corner_radius: style.visuals.window_corner_radius,
shadow: style.visuals.window_shadow,
fill: style.visuals.widgets.noninteractive.bg_fill,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(),
}
}
@ -63,8 +63,8 @@ impl Frame {
margin: Vec2::splat(1.0),
corner_radius: 2.0,
shadow: Shadow::small(),
fill: style.visuals.widgets.noninteractive.bg_fill,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(),
}
}
@ -73,8 +73,8 @@ impl Frame {
margin: style.spacing.window_padding,
corner_radius: 5.0,
shadow: Shadow::small(),
fill: style.visuals.widgets.noninteractive.bg_fill,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
fill: style.visuals.window_fill(),
stroke: style.visuals.window_stroke(),
}
}
@ -84,7 +84,7 @@ impl Frame {
margin: Vec2::new(10.0, 10.0),
corner_radius: 5.0,
fill: Color32::from_black_alpha(250),
stroke: style.visuals.widgets.noninteractive.bg_stroke,
stroke: style.visuals.window_stroke(),
..Default::default()
}
}

10
egui/src/containers/window.rs

@ -70,6 +70,12 @@ impl<'open> Window<'open> {
self
}
/// If `false` the window will be grayed out and non-interactive.
pub fn enabled(mut self, enabled: bool) -> Self {
self.area = self.area.enabled(enabled);
self
}
/// Usage: `Window::new(...).mutate(|w| w.resize = w.resize.auto_expand_width(true))`
/// Not sure this is a good interface for this.
pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
@ -239,8 +245,8 @@ impl<'open> Window<'open> {
let is_maximized = !with_title_bar
|| collapsing_header::State::is_open(ctx, collapsing_id).unwrap_or_default();
let possible = PossibleInteractions {
movable: area.is_movable(),
resizable: resize.is_resizable() && is_maximized,
movable: area.is_enabled() && area.is_movable(),
resizable: area.is_enabled() && resize.is_resizable() && is_maximized,
};
let area = area.movable(false); // We move it manually

10
egui/src/context.rs

@ -218,6 +218,7 @@ impl CtxRef {
// ---------------------------------------------------------------------
/// Use `ui.interact` instead
#[allow(clippy::too_many_arguments)]
pub(crate) fn interact(
&self,
clip_rect: Rect,
@ -226,6 +227,7 @@ impl CtxRef {
id: Id,
rect: Rect,
sense: Sense,
enabled: bool,
) -> Response {
let gap = 0.5; // Just to make sure we don't accidentally hover two things at once (a small eps should be sufficient).
let interact_rect = rect.expand2(
@ -234,7 +236,7 @@ impl CtxRef {
.at_most(Vec2::splat(5.0)),
); // make it easier to click
let hovered = self.rect_contains_pointer(layer_id, clip_rect.intersect(interact_rect));
self.interact_with_hovered(layer_id, id, rect, sense, hovered)
self.interact_with_hovered(layer_id, id, rect, sense, enabled, hovered)
}
/// You specify if a thing is hovered, and the function gives a `Response`.
@ -244,8 +246,11 @@ impl CtxRef {
id: Id,
rect: Rect,
sense: Sense,
enabled: bool,
hovered: bool,
) -> Response {
let hovered = hovered && enabled; // can't even hover disabled widgets
let has_kb_focus = self.memory().has_kb_focus(id);
let lost_kb_focus = self.memory().lost_kb_focus(id);
@ -255,6 +260,7 @@ impl CtxRef {
id,
rect,
sense,
enabled,
hovered,
clicked: Default::default(),
double_clicked: Default::default(),
@ -266,7 +272,7 @@ impl CtxRef {
lost_kb_focus,
};
if sense == Sense::hover() || !layer_id.allow_interaction() {
if !enabled || sense == Sense::hover() || !layer_id.allow_interaction() {
// Not interested or allowed input:
return response;
}

37
egui/src/painter.rs

@ -22,6 +22,10 @@ pub struct Painter {
/// Everything painted in this `Painter` will be clipped against this.
/// This means nothing outside of this rectangle will be visible on screen.
clip_rect: Rect,
/// If set, all shapes will have their colors modified to be closer to this.
/// This is used to implement grayed out interfaces.
fade_to_color: Option<Color32>,
}
impl Painter {
@ -30,6 +34,7 @@ impl Painter {
ctx,
layer_id,
clip_rect,
fade_to_color: None,
}
}
@ -39,6 +44,7 @@ impl Painter {
ctx: self.ctx,
layer_id,
clip_rect: self.clip_rect,
fade_to_color: None,
}
}
@ -47,6 +53,11 @@ impl Painter {
self.layer_id = layer_id;
}
/// If set, colors will be modified to look like this
pub(crate) fn set_fade_to_color(&mut self, fade_to_color: Option<Color32>) {
self.fade_to_color = fade_to_color;
}
/// Create a painter for a sub-region of this `Painter`.
///
/// The clip-rect of the returned `Painter` will be the intersection
@ -106,10 +117,17 @@ impl Painter {
/// ## Low level
impl Painter {
fn transform_shape(&self, shape: &mut Shape) {
if let Some(fade_to_color) = self.fade_to_color {
tint_shape_towards(shape, fade_to_color);
}
}
/// It is up to the caller to make sure there is room for this.
/// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates!
pub fn add(&self, shape: Shape) -> ShapeIdx {
pub fn add(&self, mut shape: Shape) -> ShapeIdx {
self.transform_shape(&mut shape);
self.ctx
.graphics()
.list(self.layer_id)
@ -119,8 +137,14 @@ impl Painter {
/// Add many shapes at once.
///
/// Calling this once is generally faster than calling [`Self::add`] multiple times.
pub fn extend(&self, shapes: Vec<Shape>) {
pub fn extend(&self, mut shapes: Vec<Shape>) {
if !shapes.is_empty() {
if self.fade_to_color.is_some() {
for shape in &mut shapes {
self.transform_shape(shape);
}
}
self.ctx
.graphics()
.list(self.layer_id)
@ -129,7 +153,8 @@ impl Painter {
}
/// Modify an existing [`Shape`].
pub fn set(&self, idx: ShapeIdx, shape: Shape) {
pub fn set(&self, idx: ShapeIdx, mut shape: Shape) {
self.transform_shape(&mut shape);
self.ctx
.graphics()
.list(self.layer_id)
@ -294,3 +319,9 @@ impl Painter {
});
}
}
fn tint_shape_towards(shape: &mut Shape, target: Color32) {
epaint::shape_transform::adjust_colors(shape, &|color| {
*color = crate::color::tint_color_towards(*color, target);
});
}

24
egui/src/response.rs

@ -29,6 +29,10 @@ pub struct Response {
/// The senses (click and/or drag) that the widget was interested in (if any).
pub sense: Sense,
/// Was the widget enabled?
/// If `false`, there was no interaction attempted (not even hover).
pub(crate) enabled: bool,
// OUT:
/// The pointer is hovering above this widget or the widget was clicked/tapped this frame.
pub(crate) hovered: bool,
@ -68,6 +72,7 @@ impl std::fmt::Debug for Response {
id,
rect,
sense,
enabled,
hovered,
clicked,
double_clicked,
@ -83,6 +88,7 @@ impl std::fmt::Debug for Response {
.field("id", id)
.field("rect", rect)
.field("sense", sense)
.field("enabled", enabled)
.field("hovered", hovered)
.field("clicked", clicked)
.field("double_clicked", double_clicked)
@ -117,6 +123,13 @@ impl Response {
self.double_clicked[PointerButton::Primary as usize]
}
/// Was the widget enabled?
/// If false, there was no interaction attempted
/// and the widget should be drawn in a gray disabled look.
pub fn enabled(&self) -> bool {
self.enabled
}
/// The pointer is hovering above this widget or the widget was clicked/tapped this frame.
pub fn hovered(&self) -> bool {
self.hovered
@ -208,8 +221,14 @@ impl Response {
/// if response.clicked() { /* … */ }
/// ```
pub fn interact(&self, sense: Sense) -> Self {
self.ctx
.interact_with_hovered(self.layer_id, self.id, self.rect, sense, self.hovered)
self.ctx.interact_with_hovered(
self.layer_id,
self.id,
self.rect,
sense,
self.enabled,
self.hovered,
)
}
/// Move the scroll to this UI with the specified alignment.
@ -247,6 +266,7 @@ impl Response {
id: self.id,
rect: self.rect.union(other.rect),
sense: self.sense.union(other.sense),
enabled: self.enabled || other.enabled,
hovered: self.hovered || other.hovered,
clicked: [
self.clicked[0] || other.clicked[0],

39
egui/src/style.rs

@ -184,12 +184,20 @@ impl Visuals {
}
pub fn weak_text_color(&self) -> Color32 {
self.widgets.disabled.text_color()
crate::color::tint_color_towards(self.text_color(), self.window_fill())
}
pub fn strong_text_color(&self) -> Color32 {
self.widgets.active.text_color()
}
pub fn window_fill(&self) -> Color32 {
self.widgets.noninteractive.bg_fill
}
pub fn window_stroke(&self) -> Stroke {
self.widgets.noninteractive.bg_stroke
}
}
/// Selected text, selected elements etc
@ -210,8 +218,6 @@ pub struct Widgets {
/// * `noninteractive.bg_fill` is the background color of windows.
/// * `noninteractive.fg_stroke` is the normal text color.
pub noninteractive: WidgetVisuals,
/// The style of a disabled button.
pub disabled: WidgetVisuals,
/// The style of an interactive widget, such as a button, at rest.
pub inactive: WidgetVisuals,
/// The style of an interactive widget while you hover it.
@ -224,8 +230,6 @@ impl Widgets {
pub fn style(&self, response: &Response) -> &WidgetVisuals {
if response.is_pointer_button_down_on() || response.has_kb_focus {
&self.active
} else if response.sense == crate::Sense::hover() {
&self.disabled
} else if response.hovered() {
&self.hovered
} else {
@ -374,19 +378,12 @@ impl Widgets {
pub fn dark() -> Self {
Self {
noninteractive: WidgetVisuals {
bg_fill: Color32::from_gray(30), // window background
bg_stroke: Stroke::new(1.0, Color32::from_gray(65)), // window outline
bg_fill: Color32::from_gray(30), // window background
fg_stroke: Stroke::new(1.0, Color32::from_gray(160)), // normal text color
corner_radius: 4.0,
expansion: 0.0,
},
disabled: WidgetVisuals {
bg_fill: Color32::from_gray(40), // Should look grayed out
bg_stroke: Stroke::new(1.0, Color32::from_gray(70)),
fg_stroke: Stroke::new(1.0, Color32::from_gray(110)), // Should look grayed out. Also used for "weak" text color.
corner_radius: 4.0,
expansion: 0.0,
},
inactive: WidgetVisuals {
bg_fill: Color32::from_gray(70),
bg_stroke: Default::default(),
@ -414,16 +411,9 @@ impl Widgets {
pub fn light() -> Self {
Self {
noninteractive: WidgetVisuals {
bg_fill: Color32::from_gray(220), // window background
bg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // window outline
bg_fill: Color32::from_gray(220), // window background
fg_stroke: Stroke::new(1.0, Color32::from_gray(70)), // normal text color
corner_radius: 4.0,
expansion: 0.0,
},
disabled: WidgetVisuals {
bg_fill: Color32::from_gray(215), // Should look grayed out
bg_stroke: Stroke::new(1.0, Color32::from_gray(185)),
fg_stroke: Stroke::new(1.0, Color32::from_gray(145)), // Should look grayed out. Also used for "weak" text color.
fg_stroke: Stroke::new(1.0, Color32::from_gray(70)), // normal text color
corner_radius: 4.0,
expansion: 0.0,
},
@ -542,7 +532,6 @@ impl Widgets {
active,
hovered,
inactive,
disabled,
noninteractive,
} = self;
@ -550,10 +539,6 @@ impl Widgets {
ui.label("The style of a widget that you cannot interact with.");
noninteractive.ui(ui)
});
ui.collapsing("interactive & disabled", |ui| {
ui.label("The style of a disabled button.");
disabled.ui(ui)
});
ui.collapsing("interactive & inactive", |ui| {
ui.label("The style of an interactive widget, such as a button, at rest.");
inactive.ui(ui)

40
egui/src/ui.rs

@ -46,6 +46,10 @@ pub struct Ui {
/// Handles the `Ui` size and the placement of new widgets.
placer: Placer,
/// If false we are unresponsive to input,
/// and all widgets will assume a gray style.
enabled: bool,
}
impl Ui {
@ -60,6 +64,7 @@ impl Ui {
painter: Painter::new(ctx, layer_id, clip_rect),
style,
placer: Placer::new(max_rect, Layout::default()),
enabled: true,
}
}
@ -72,6 +77,7 @@ impl Ui {
painter: self.painter.clone(),
style: self.style.clone(),
placer: Placer::new(max_rect, layout),
enabled: self.enabled,
}
}
@ -166,6 +172,39 @@ impl Ui {
&self.painter
}
/// If `false`, the `Ui` does not allow any interaction and
/// the widgets in it will draw with a gray look.
pub fn enabled(&self) -> bool {
self.enabled
}
/// Calling `set_enabled(false)` will cause the `Ui` to deny all future interaction
/// and all the widgets will draw with a gray look.
///
/// Calling `set_enabled(true)` has no effect - it will NOT re-enable the `Ui` once disabled.
///
/// ### Example
/// ```
/// # let ui = &mut egui::Ui::__test();
/// # let mut enabled = true;
/// ui.group(|ui|{
/// ui.checkbox(&mut enabled, "Enable subsection");
/// ui.set_enabled(enabled);
/// if ui.button("Button that is not always clickable").clicked() {
/// /* … */
/// }
/// });
/// ```
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled &= enabled;
if self.enabled {
self.painter.set_fade_to_color(None);
} else {
self.painter
.set_fade_to_color(Some(self.visuals().window_fill()));
}
}
pub fn layout(&self) -> &Layout {
self.placer.layout()
}
@ -427,6 +466,7 @@ impl Ui {
id,
rect,
sense,
self.enabled,
)
}

22
egui/src/widgets/button.rs

@ -68,6 +68,8 @@ impl Button {
/// If you set this to `false`, the button will be grayed out and un-clickable.
/// `enabled(false)` has the same effect as calling `sense(Sense::hover())`.
///
/// This is a convenience for [`Ui::set_enabled`].
pub fn enabled(mut self, enabled: bool) -> Self {
if !enabled {
self.sense = Sense::hover();
@ -76,8 +78,8 @@ impl Button {
}
}
impl Widget for Button {
fn ui(self, ui: &mut Ui) -> Response {
impl Button {
fn enabled_ui(self, ui: &mut Ui) -> Response {
let Button {
text,
text_color,
@ -136,6 +138,22 @@ impl Widget for Button {
}
}
impl Widget for Button {
fn ui(self, ui: &mut Ui) -> Response {
let button_enabled = self.sense != Sense::hover();
if button_enabled || !ui.enabled() {
self.enabled_ui(ui)
} else {
// We need get a temporary disabled `Ui` to get that grayed out look:
ui.wrap(|ui| {
ui.set_enabled(false);
self.enabled_ui(ui)
})
.0
}
}
}
// ----------------------------------------------------------------------------
// TODO: allow checkbox without a text label

18
egui/src/widgets/label.rs

@ -160,15 +160,15 @@ impl Label {
..
} = *self;
let text_color = self.text_color.unwrap_or_else(|| {
if strong {
ui.visuals().strong_text_color()
} else if weak {
ui.visuals().weak_text_color()
} else {
ui.visuals().text_color()
}
});
let text_color = if let Some(text_color) = self.text_color {
text_color
} else if strong {
ui.visuals().strong_text_color()
} else if weak {
ui.visuals().weak_text_color()
} else {
ui.visuals().text_color()
};
if code {
background_color = ui.visuals().code_bg_color;

3
egui/src/widgets/selected_label.rs

@ -48,16 +48,19 @@ impl Widget for SelectableLabel {
if selected || response.hovered() {
let rect = rect.expand(visuals.expansion);
let fill = if selected {
ui.visuals().selection.bg_fill
} else {
Default::default()
};
let stroke = if selected {
ui.visuals().selection.stroke
} else {
visuals.bg_stroke
};
let corner_radius = 2.0;
ui.painter().rect(rect, corner_radius, fill, stroke);
}

16
egui_demo_lib/src/apps/demo/drag_and_drop.rs

@ -51,20 +51,24 @@ pub fn drop_target<R>(
let style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
ui.visuals().widgets.active
} else if is_being_dragged && can_accept_what_is_being_dragged {
ui.visuals().widgets.inactive
} else if is_being_dragged && !can_accept_what_is_being_dragged {
ui.visuals().widgets.disabled
} else {
ui.visuals().widgets.inactive
};
let mut fill = style.bg_fill;
let mut stroke = style.bg_stroke;
if is_being_dragged && !can_accept_what_is_being_dragged {
// gray out:
fill = color::tint_color_towards(fill, ui.visuals().window_fill());
stroke.color = color::tint_color_towards(stroke.color, ui.visuals().window_fill());
}
ui.painter().set(
where_to_put_background,
Shape::Rect {
corner_radius: style.corner_radius,
fill: style.bg_fill,
stroke: style.bg_stroke,
fill,
stroke,
rect,
},
);

9
egui_demo_lib/src/apps/demo/toggle_switch.rs

@ -83,9 +83,12 @@ fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
pub fn demo(ui: &mut egui::Ui, on: &mut bool) {
ui.horizontal_wrapped_for_text(egui::TextStyle::Button, |ui| {
ui.label("It's easy to create your own widgets!");
ui.label("This toggle switch is just one function and 15 lines of code:");
toggle(ui, on).on_hover_text("Click to toggle");
ui.add(crate::__egui_github_link_file!());
});
})
.1
.on_hover_text(
"It's easy to create your own widgets!\n\
This toggle switch is just one function and 20 lines of code.",
);
}

17
egui_demo_lib/src/apps/demo/widget_gallery.rs

@ -8,6 +8,7 @@ enum Enum {
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct WidgetGallery {
enabled: bool,
boolean: bool,
radio: Enum,
scalar: f32,
@ -18,6 +19,7 @@ pub struct WidgetGallery {
impl Default for WidgetGallery {
fn default() -> Self {
Self {
enabled: true,
boolean: false,
radio: Enum::First,
scalar: 42.0,
@ -46,6 +48,7 @@ impl super::Demo for WidgetGallery {
impl super::View for WidgetGallery {
fn ui(&mut self, ui: &mut egui::Ui) {
let Self {
enabled,
boolean,
radio,
scalar,
@ -53,6 +56,12 @@ impl super::View for WidgetGallery {
color,
} = self;
ui.horizontal(|ui| {
ui.radio_value(enabled, true, "Enabled");
ui.radio_value(enabled, false, "Disabled");
});
ui.set_enabled(*enabled);
let grid = egui::Grid::new("my_grid")
.striped(true)
.spacing([40.0, 4.0]);
@ -65,7 +74,7 @@ impl super::View for WidgetGallery {
ui.add(egui::Hyperlink::new("https://github.com/emilk/egui").text(" egui home page"));
ui.end_row();
ui.label("Text Input:");
ui.label("TextEdit:");
ui.add(egui::TextEdit::singleline(string).hint_text("Write something here"));
ui.end_row();
@ -73,7 +82,7 @@ impl super::View for WidgetGallery {
ui.checkbox(boolean, "Checkbox");
ui.end_row();
ui.label("Radio buttons:");
ui.label("RadioButton:");
ui.horizontal(|ui| {
ui.radio_value(radio, Enum::First, "First");
ui.radio_value(radio, Enum::Second, "Second");
@ -143,6 +152,10 @@ impl super::View for WidgetGallery {
});
});
ui.end_row();
ui.label("Custom widget");
super::toggle_switch::demo(ui, boolean);
ui.end_row();
});
ui.separator();

3
egui_demo_lib/src/apps/demo/widgets.rs

@ -134,8 +134,5 @@ impl Widgets {
ui.label("Multiline text input:");
ui.text_edit_multiline(&mut self.multiline_text_input);
ui.separator();
super::toggle_switch::demo(ui, &mut self.toggle_switch);
}
}

16
egui_demo_lib/src/apps/demo/window_options.rs

@ -7,6 +7,7 @@ pub struct WindowOptions {
collapsible: bool,
resizable: bool,
scroll: bool,
disabled_time: f64,
}
impl Default for WindowOptions {
@ -18,6 +19,7 @@ impl Default for WindowOptions {
collapsible: true,
resizable: true,
scroll: false,
disabled_time: f64::NEG_INFINITY,
}
}
}
@ -36,15 +38,22 @@ impl super::Demo for WindowOptions {
collapsible,
resizable,
scroll,
disabled_time,
} = self.clone();
let enabled = ctx.input().time - disabled_time > 2.0;
if !enabled {
ctx.request_repaint();
}
use super::View;
let mut window = egui::Window::new(title)
.id(egui::Id::new("demo_window_options")) // required since we change the title
.resizable(resizable)
.collapsible(collapsible)
.title_bar(title_bar)
.scroll(scroll);
.scroll(scroll)
.enabled(enabled);
if closable {
window = window.open(open);
}
@ -63,6 +72,7 @@ impl super::View for WindowOptions {
collapsible,
resizable,
scroll,
disabled_time,
} = self;
ui.horizontal(|ui| {
@ -77,5 +87,9 @@ impl super::View for WindowOptions {
ui.vertical_centered(|ui| {
ui.add(crate::__egui_github_link_file!());
});
if ui.button("Disable for 2 seconds").clicked() {
*disabled_time = ui.input().time;
}
}
}

27
epaint/src/color.rs

@ -682,3 +682,30 @@ impl From<Hsva> for HsvaGamma {
}
}
}
// ----------------------------------------------------------------------------
/// Cheap and ugly.
/// Made for graying out disabled `Ui`:s.
pub fn tint_color_towards(color: Color32, target: Color32) -> Color32 {
let [mut r, mut g, mut b, mut a] = color.to_array();
if a == 0 {
r /= 2;
g /= 2;
b /= 2;
} else if a < 170 {
// Cheapish and looks ok.
// Works for e.g. grid stripes.
let div = (2 * 255 / a as i32) as u8;
r = r / 2 + target.r() / div;
g = g / 2 + target.g() / div;
b = b / 2 + target.b() / div;
a /= 2;
} else {
r = r / 2 + target.r() / 2;
g = g / 2 + target.g() / 2;
b = b / 2 + target.b() / 2;
}
Color32::from_rgba_premultiplied(r, g, b, a)
}

1
epaint/src/lib.rs

@ -49,6 +49,7 @@ mod mesh;
pub mod mutex;
mod shadow;
mod shape;
pub mod shape_transform;
pub mod stats;
mod stroke;
pub mod tessellator;

35
epaint/src/shape_transform.rs

@ -0,0 +1,35 @@
use crate::*;
pub fn adjust_colors(shape: &mut Shape, adjust_color: &impl Fn(&mut Color32)) {
match shape {
Shape::Noop => {}
Shape::Vec(shapes) => {
for shape in shapes {
adjust_colors(shape, adjust_color)
}
}
Shape::Circle { fill, stroke, .. } => {
adjust_color(fill);
adjust_color(&mut stroke.color);
}
Shape::LineSegment { stroke, .. } => {
adjust_color(&mut stroke.color);
}
Shape::Path { fill, stroke, .. } => {
adjust_color(fill);
adjust_color(&mut stroke.color);
}
Shape::Rect { fill, stroke, .. } => {
adjust_color(fill);
adjust_color(&mut stroke.color);
}
Shape::Text { color, .. } => {
adjust_color(color);
}
Shape::Mesh(mesh) => {
for v in &mut mesh.vertices {
adjust_color(&mut v.color);
}
}
}
}
Loading…
Cancel
Save