diff --git a/CHANGELOG.md b/CHANGELOG.md index b09a70c109..c7e25dbb62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ You can find its changes [documented below](#070---2021-01-01). - Flex values that are less than 0.0 will default to 0.0 and warn in release. It will panic in debug mode. ([#1691] by [@arthmis]) - Lens implemented for tuples of Lenses of length 2-8, Tuple2 removed ([#1654] by [@Maan2003]) - Window size and positioning code is now in display points ([#1713] by [@jneem]) +- Update look and feel of controls when disabled ([#1717] by [@xarvic]) ### Deprecated @@ -699,6 +700,7 @@ Last release without a changelog :( [#1702]: https://github.com/linebender/druid/pull/1702 [#1713]: https://github.com/linebender/druid/pull/1713 [#1715]: https://github.com/linebender/druid/pull/1715 +[#1717]: https://github.com/linebender/druid/pull/1717 [#1722]: https://github.com/linebender/druid/pull/1722 [#1724]: https://github.com/linebender/druid/pull/1724 [#1730]: https://github.com/linebender/druid/pull/1730 diff --git a/docs/book_examples/src/env_md.rs b/docs/book_examples/src/env_md.rs index b206a08a98..90b22485cf 100644 --- a/docs/book_examples/src/env_md.rs +++ b/docs/book_examples/src/env_md.rs @@ -14,7 +14,7 @@ fn make_labels() { // ANCHOR: env_scope fn scoped_label() { let my_label = Label::<()>::new("Warning!").env_scope(|env, _| { - env.set(druid::theme::LABEL_COLOR, Color::BLACK); + env.set(druid::theme::TEXT_COLOR, Color::BLACK); env.set(druid::theme::TEXT_SIZE_NORMAL, 18.0); }); } diff --git a/druid/examples/disabled.rs b/druid/examples/disabled.rs index ed22fb26cf..f6af92dc1a 100644 --- a/druid/examples/disabled.rs +++ b/druid/examples/disabled.rs @@ -42,53 +42,50 @@ fn main_widget() -> impl Widget { Flex::column() .with_child(named_child("text:", TextBox::new().lens(AppData::text))) .with_default_spacer() - .with_child(named_child( - "text (disabled):", - TextBox::new() - .lens(AppData::text) + .with_child( + named_child("text (disabled):", TextBox::new().lens(AppData::text)) .disabled_if(|data, _| data.disabled), - )) + ) .with_default_spacer() .with_child(named_child("text:", TextBox::new().lens(AppData::text))) .with_default_spacer() - .with_child(named_child( - "text (disabled):", - TextBox::new() - .lens(AppData::text) + .with_child( + named_child("text (disabled):", TextBox::new().lens(AppData::text)) .disabled_if(|data, _| data.disabled), - )) + ) .with_default_spacer() .with_default_spacer() - .with_child(named_child( - "value (disabled):", - Slider::new() - .with_range(0.0, 10.0) - .lens(AppData::value) - .disabled_if(|data, _| data.disabled), - )) + .with_child( + named_child( + "value (disabled):", + Slider::new().with_range(0.0, 10.0).lens(AppData::value), + ) + .disabled_if(|data, _| data.disabled), + ) .with_default_spacer() - .with_child(named_child( - "value (disabled):", - Stepper::new() - .with_range(0.0, 10.0) - .with_step(0.5) - .lens(AppData::value) - .disabled_if(|data, _| data.disabled), - )) + .with_child( + named_child( + "value (disabled):", + Stepper::new() + .with_range(0.0, 10.0) + .with_step(0.5) + .lens(AppData::value), + ) + .disabled_if(|data, _| data.disabled), + ) .with_default_spacer() - .with_child(named_child( - "option (disabled):", - Checkbox::new("option") - .lens(AppData::option) - .disabled_if(|data, _| data.disabled), - )) + .with_child( + named_child( + "option (disabled):", + Checkbox::new("option").lens(AppData::option), + ) + .disabled_if(|data, _| data.disabled), + ) .with_default_spacer() - .with_child(named_child( - "option (disabled):", - Switch::new() - .lens(AppData::option) + .with_child( + named_child("option (disabled):", Switch::new().lens(AppData::option)) .disabled_if(|data, _| data.disabled), - )) + ) .with_default_spacer() .with_child( Flex::row() diff --git a/druid/examples/event_viewer.rs b/druid/examples/event_viewer.rs index 75cbeb1332..44ead7e7e3 100644 --- a/druid/examples/event_viewer.rs +++ b/druid/examples/event_viewer.rs @@ -336,7 +336,7 @@ pub fn main() { .log_to_console() .configure_env(|env, _| { env.set(theme::UI_FONT, FontDescriptor::default().with_size(12.0)); - env.set(theme::LABEL_COLOR, TEXT_COLOR); + env.set(theme::TEXT_COLOR, TEXT_COLOR); env.set(theme::WIDGET_PADDING_HORIZONTAL, 2.0); env.set(theme::WIDGET_PADDING_VERTICAL, 2.0); }) diff --git a/druid/examples/sub_window.rs b/druid/examples/sub_window.rs index f994d82180..65b005b44d 100644 --- a/druid/examples/sub_window.rs +++ b/druid/examples/sub_window.rs @@ -327,7 +327,7 @@ impl> Controller for CancelClose { fn build_root_widget() -> impl Widget { let label = EnvScope::new( - |env, _t| env.set(theme::LABEL_COLOR, env.get(theme::PRIMARY_LIGHT)), + |env, _t| env.set(theme::TEXT_COLOR, env.get(theme::PRIMARY_LIGHT)), ControllerHost::new( Label::new(|data: &HelloState, _env: &Env| { format!("Hello {}! {} ", data.name, data.sub.my_stuff) diff --git a/druid/src/text/input_component.rs b/druid/src/text/input_component.rs index 58608c31a1..ef07092606 100644 --- a/druid/src/text/input_component.rs +++ b/druid/src/text/input_component.rs @@ -272,7 +272,7 @@ impl Widget for TextComponent { )] fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { match event { - Event::MouseDown(mouse) if self.can_write() => { + Event::MouseDown(mouse) if self.can_write() && !ctx.is_disabled() => { ctx.set_active(true); // ensure data is up to date before a click let needs_rebuild = self @@ -295,23 +295,26 @@ impl Widget for TextComponent { ctx.request_paint(); } Event::MouseMove(mouse) if self.can_write() => { - ctx.set_cursor(&Cursor::IBeam); - if ctx.is_active() { - let pre_sel = self.borrow().selection(); - self.borrow_mut().do_drag(mouse.pos); - if self.borrow().selection() != pre_sel { - self.borrow_mut() - .update_pending_invalidation(ImeInvalidation::SelectionChanged); - ctx.request_update(); - ctx.request_paint(); + if !ctx.is_disabled() { + ctx.set_cursor(&Cursor::IBeam); + if ctx.is_active() { + let pre_sel = self.borrow().selection(); + self.borrow_mut().do_drag(mouse.pos); + if self.borrow().selection() != pre_sel { + self.borrow_mut() + .update_pending_invalidation(ImeInvalidation::SelectionChanged); + ctx.request_update(); + ctx.request_paint(); + } } + } else { + ctx.set_disabled(false); + ctx.clear_cursor(); } } - Event::MouseUp(_) => { - if ctx.is_active() { - ctx.set_active(false); - ctx.request_paint(); - } + Event::MouseUp(_) if ctx.is_active() => { + ctx.set_active(false); + ctx.request_paint(); } Event::ImeStateChange => { assert!( @@ -380,6 +383,18 @@ impl Widget for TextComponent { } } } + LifeCycle::DisabledChanged(disabled) => { + if self.can_write() { + let color = if *disabled { + env.get(theme::DISABLED_TEXT_COLOR) + } else { + env.get(theme::TEXT_COLOR) + }; + + self.borrow_mut().layout.set_text_color(color); + } + ctx.request_layout(); + } _ => (), } } diff --git a/druid/src/text/layout.rs b/druid/src/text/layout.rs index b0f3cdf55a..1db9539e97 100644 --- a/druid/src/text/layout.rs +++ b/druid/src/text/layout.rs @@ -82,7 +82,7 @@ impl TextLayout { TextLayout { text: None, font: crate::theme::UI_FONT.into(), - text_color: crate::theme::LABEL_COLOR.into(), + text_color: crate::theme::TEXT_COLOR.into(), text_size_override: None, layout: None, wrap_width: f64::INFINITY, diff --git a/druid/src/theme.rs b/druid/src/theme.rs index 7d1b48d58d..a15c65e78b 100644 --- a/druid/src/theme.rs +++ b/druid/src/theme.rs @@ -23,7 +23,11 @@ use crate::{Env, FontDescriptor, FontFamily, FontStyle, FontWeight, Insets, Key} pub const WINDOW_BACKGROUND_COLOR: Key = Key::new("org.linebender.druid.theme.window_background_color"); -pub const LABEL_COLOR: Key = Key::new("org.linebender.druid.theme.label_color"); +#[deprecated(since = "0.8.0", note = "renamed to TEXT_COLOR")] +pub const LABEL_COLOR: Key = TEXT_COLOR; +pub const TEXT_COLOR: Key = Key::new("org.linebender.druid.theme.label_color"); +pub const DISABLED_TEXT_COLOR: Key = + Key::new("org.linebender.druid.theme.disabled_label_color"); pub const PLACEHOLDER_COLOR: Key = Key::new("org.linebender.druid.theme.placeholder_color"); pub const PRIMARY_LIGHT: Key = Key::new("org.linebender.druid.theme.primary_light"); @@ -34,8 +38,16 @@ pub const BACKGROUND_LIGHT: Key = Key::new("org.linebender.druid.theme.ba pub const BACKGROUND_DARK: Key = Key::new("org.linebender.druid.theme.background_dark"); pub const FOREGROUND_LIGHT: Key = Key::new("org.linebender.druid.theme.foreground_light"); pub const FOREGROUND_DARK: Key = Key::new("org.linebender.druid.theme.foreground_dark"); +pub const DISABLED_FOREGROUND_LIGHT: Key = + Key::new("org.linebender.druid.theme.disabled_foreground_light"); +pub const DISABLED_FOREGROUND_DARK: Key = + Key::new("org.linebender.druid.theme.disabled_foreground_dark"); pub const BUTTON_DARK: Key = Key::new("org.linebender.druid.theme.button_dark"); pub const BUTTON_LIGHT: Key = Key::new("org.linebender.druid.theme.button_light"); +pub const DISABLED_BUTTON_DARK: Key = + Key::new("org.linebender.druid.theme.disabled_button_dark"); +pub const DISABLED_BUTTON_LIGHT: Key = + Key::new("org.linebender.druid.theme.disabled_button_light"); pub const BUTTON_BORDER_RADIUS: Key = Key::new("org.linebender.druid.theme.button_radius"); pub const BUTTON_BORDER_WIDTH: Key = Key::new("org.linebender.druid.theme.button_border_width"); @@ -107,7 +119,8 @@ pub const SCROLLBAR_MIN_SIZE: Key = Key::new("org.linebender.theme.scrollba /// An initial theme. pub(crate) fn add_to_env(env: Env) -> Env { env.adding(WINDOW_BACKGROUND_COLOR, Color::rgb8(0x29, 0x29, 0x29)) - .adding(LABEL_COLOR, Color::rgb8(0xf0, 0xf0, 0xea)) + .adding(TEXT_COLOR, Color::rgb8(0xf0, 0xf0, 0xea)) + .adding(DISABLED_TEXT_COLOR, Color::rgb8(0xa0, 0xa0, 0x9a)) .adding(PLACEHOLDER_COLOR, Color::rgb8(0x80, 0x80, 0x80)) .adding(PRIMARY_LIGHT, Color::rgb8(0x5c, 0xc4, 0xff)) .adding(PRIMARY_DARK, Color::rgb8(0x00, 0x8d, 0xdd)) @@ -116,8 +129,12 @@ pub(crate) fn add_to_env(env: Env) -> Env { .adding(BACKGROUND_DARK, Color::rgb8(0x31, 0x31, 0x31)) .adding(FOREGROUND_LIGHT, Color::rgb8(0xf9, 0xf9, 0xf9)) .adding(FOREGROUND_DARK, Color::rgb8(0xbf, 0xbf, 0xbf)) + .adding(DISABLED_FOREGROUND_LIGHT, Color::rgb8(0x89, 0x89, 0x89)) + .adding(DISABLED_FOREGROUND_DARK, Color::rgb8(0x6f, 0x6f, 0x6f)) .adding(BUTTON_DARK, Color::BLACK) .adding(BUTTON_LIGHT, Color::rgb8(0x21, 0x21, 0x21)) + .adding(DISABLED_BUTTON_DARK, Color::grey8(0x28)) + .adding(DISABLED_BUTTON_LIGHT, Color::grey8(0x38)) .adding(BUTTON_BORDER_RADIUS, 4.) .adding(BUTTON_BORDER_WIDTH, 2.) .adding(BORDER_DARK, Color::rgb8(0x3a, 0x3a, 0x3a)) diff --git a/druid/src/widget/button.rs b/druid/src/widget/button.rs index 7e97ec60d7..e82cfde657 100644 --- a/druid/src/widget/button.rs +++ b/druid/src/widget/button.rs @@ -115,16 +115,18 @@ impl Widget for Button { fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut T, _env: &Env) { match event { Event::MouseDown(_) => { - ctx.set_active(true); - ctx.request_paint(); - trace!("Button {:?} pressed", ctx.widget_id()); + if !ctx.is_disabled() { + ctx.set_active(true); + ctx.request_paint(); + trace!("Button {:?} pressed", ctx.widget_id()); + } } Event::MouseUp(_) => { - if ctx.is_active() { - ctx.set_active(false); + if ctx.is_active() && !ctx.is_disabled() { ctx.request_paint(); trace!("Button {:?} released", ctx.widget_id()); } + ctx.set_active(false); } _ => (), } @@ -132,7 +134,7 @@ impl Widget for Button { #[instrument(name = "Button", level = "trace", skip(self, ctx, event, data, env))] fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { - if let LifeCycle::HotChanged(_) = event { + if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event { ctx.request_paint(); } self.label.lifecycle(ctx, event, data, env) @@ -165,7 +167,7 @@ impl Widget for Button { #[instrument(name = "Button", level = "trace", skip(self, ctx, data, env))] fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { - let is_active = ctx.is_active(); + let is_active = ctx.is_active() && !ctx.is_disabled(); let is_hot = ctx.is_hot(); let size = ctx.size(); let stroke_width = env.get(theme::BUTTON_BORDER_WIDTH); @@ -175,7 +177,16 @@ impl Widget for Button { .inset(-stroke_width / 2.0) .to_rounded_rect(env.get(theme::BUTTON_BORDER_RADIUS)); - let bg_gradient = if is_active { + let bg_gradient = if ctx.is_disabled() { + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::DISABLED_BUTTON_LIGHT), + env.get(theme::DISABLED_BUTTON_DARK), + ), + ) + } else if is_active { LinearGradient::new( UnitPoint::TOP, UnitPoint::BOTTOM, @@ -189,7 +200,7 @@ impl Widget for Button { ) }; - let border_color = if is_hot { + let border_color = if is_hot && !ctx.is_disabled() { env.get(theme::BORDER_LIGHT) } else { env.get(theme::BORDER_DARK) diff --git a/druid/src/widget/checkbox.rs b/druid/src/widget/checkbox.rs index 986f96d770..8299c3b5d5 100644 --- a/druid/src/widget/checkbox.rs +++ b/druid/src/widget/checkbox.rs @@ -44,13 +44,14 @@ impl Widget for Checkbox { fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut bool, _env: &Env) { match event { Event::MouseDown(_) => { - ctx.set_active(true); - ctx.request_paint(); - trace!("Checkbox {:?} pressed", ctx.widget_id()); + if !ctx.is_disabled() { + ctx.set_active(true); + ctx.request_paint(); + trace!("Checkbox {:?} pressed", ctx.widget_id()); + } } Event::MouseUp(_) => { - if ctx.is_active() { - ctx.set_active(false); + if ctx.is_active() && !ctx.is_disabled() { if ctx.is_hot() { if *data { *data = false; @@ -62,6 +63,7 @@ impl Widget for Checkbox { } ctx.request_paint(); } + ctx.set_active(false); } _ => (), } @@ -70,7 +72,7 @@ impl Widget for Checkbox { #[instrument(name = "CheckBox", level = "trace", skip(self, ctx, event, data, env))] fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &bool, env: &Env) { self.child_label.lifecycle(ctx, event, data, env); - if let LifeCycle::HotChanged(_) = event { + if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event { ctx.request_paint(); } } @@ -126,7 +128,7 @@ impl Widget for Checkbox { ctx.fill(rect, &background_gradient); - let border_color = if ctx.is_hot() { + let border_color = if ctx.is_hot() && !ctx.is_disabled() { env.get(theme::BORDER_LIGHT) } else { env.get(theme::BORDER_DARK) @@ -145,7 +147,13 @@ impl Widget for Checkbox { .line_cap(LineCap::Round) .line_join(LineJoin::Round); - ctx.stroke_styled(path, &env.get(theme::LABEL_COLOR), 2., &style); + let brush = if ctx.is_disabled() { + env.get(theme::DISABLED_TEXT_COLOR) + } else { + env.get(theme::TEXT_COLOR) + }; + + ctx.stroke_styled(path, &brush, 2., &style); } // Paint the text label diff --git a/druid/src/widget/click.rs b/druid/src/widget/click.rs index 9486a17bc9..ba340720e4 100644 --- a/druid/src/widget/click.rs +++ b/druid/src/widget/click.rs @@ -59,7 +59,7 @@ impl> Controller for Click { fn event(&mut self, child: &mut W, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { match event { Event::MouseDown(mouse_event) => { - if mouse_event.button == MouseButton::Left { + if mouse_event.button == MouseButton::Left && !ctx.is_disabled() { ctx.set_active(true); ctx.request_paint(); trace!("Widget {:?} pressed", ctx.widget_id()); @@ -68,7 +68,7 @@ impl> Controller for Click { Event::MouseUp(mouse_event) => { if ctx.is_active() && mouse_event.button == MouseButton::Left { ctx.set_active(false); - if ctx.is_hot() { + if ctx.is_hot() && !ctx.is_disabled() { (self.action)(ctx, data, env); } ctx.request_paint(); diff --git a/druid/src/widget/env_scope.rs b/druid/src/widget/env_scope.rs index 8e948a1665..a0277dd4a9 100644 --- a/druid/src/widget/env_scope.rs +++ b/druid/src/widget/env_scope.rs @@ -40,7 +40,7 @@ impl> EnvScope { /// # fn build_widget() -> impl Widget { /// EnvScope::new( /// |env, data| { - /// env.set(theme::LABEL_COLOR, Color::WHITE); + /// env.set(theme::TEXT_COLOR, Color::WHITE); /// }, /// Label::new("White text!") /// ) diff --git a/druid/src/widget/label.rs b/druid/src/widget/label.rs index 8fd403c296..cd3cb661b8 100644 --- a/druid/src/widget/label.rs +++ b/druid/src/widget/label.rs @@ -96,6 +96,9 @@ pub struct Label { pub struct RawLabel { layout: TextLayout, line_break_mode: LineBreaking, + + disabled: bool, + default_text_color: KeyOrValue, } /// Options for handling lines that are too wide for the label. @@ -152,6 +155,8 @@ impl RawLabel { Self { layout: TextLayout::new(), line_break_mode: LineBreaking::Overflow, + disabled: false, + default_text_color: crate::theme::TEXT_COLOR.into(), } } @@ -214,7 +219,11 @@ impl RawLabel { /// [`request_layout`]: ../struct.EventCtx.html#method.request_layout /// [`Key`]: ../struct.Key.html pub fn set_text_color(&mut self, color: impl Into>) { - self.layout.set_text_color(color); + let color = color.into(); + if !self.disabled { + self.layout.set_text_color(color.clone()); + } + self.default_text_color = color; } /// Set the text size. @@ -486,9 +495,9 @@ impl Widget for Label { if matches!(event, LifeCycle::WidgetAdded) { self.text.resolve(data, env); self.text_should_be_updated = false; - self.label - .lifecycle(ctx, event, &self.text.display_text(), env); } + self.label + .lifecycle(ctx, event, &self.text.display_text(), env); } #[instrument(name = "Label", level = "trace", skip(self, ctx, _old_data, data, env))] @@ -548,14 +557,22 @@ impl Widget for RawLabel { } } - #[instrument( - name = "RawLabel", - level = "trace", - skip(self, _ctx, event, data, _env) - )] - fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, _env: &Env) { - if matches!(event, LifeCycle::WidgetAdded) { - self.layout.set_text(data.to_owned()); + #[instrument(name = "RawLabel", level = "trace", skip(self, ctx, event, data, _env))] + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, _env: &Env) { + match event { + LifeCycle::WidgetAdded => { + self.layout.set_text(data.to_owned()); + } + LifeCycle::DisabledChanged(disabled) => { + let color = if *disabled { + KeyOrValue::Key(crate::theme::DISABLED_TEXT_COLOR) + } else { + self.default_text_color.clone() + }; + self.layout.set_text_color(color); + ctx.request_layout(); + } + _ => {} } } diff --git a/druid/src/widget/radio.rs b/druid/src/widget/radio.rs index 8f48db68de..04216fb490 100644 --- a/druid/src/widget/radio.rs +++ b/druid/src/widget/radio.rs @@ -66,19 +66,21 @@ impl Widget for Radio { fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, _env: &Env) { match event { Event::MouseDown(_) => { - ctx.set_active(true); - ctx.request_paint(); - trace!("Radio button {:?} pressed", ctx.widget_id()); + if !ctx.is_disabled() { + ctx.set_active(true); + ctx.request_paint(); + trace!("Radio button {:?} pressed", ctx.widget_id()); + } } Event::MouseUp(_) => { - if ctx.is_active() { - ctx.set_active(false); + if ctx.is_active() && !ctx.is_disabled() { if ctx.is_hot() { *data = self.variant.clone(); } ctx.request_paint(); trace!("Radio button {:?} released", ctx.widget_id()); } + ctx.set_active(false); } _ => (), } @@ -87,7 +89,7 @@ impl Widget for Radio { #[instrument(name = "Radio", level = "trace", skip(self, ctx, event, data, env))] fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { self.child_label.lifecycle(ctx, event, data, env); - if let LifeCycle::HotChanged(_) = event { + if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event { ctx.request_paint(); } } @@ -136,7 +138,7 @@ impl Widget for Radio { ctx.fill(circle, &background_gradient); - let border_color = if ctx.is_hot() { + let border_color = if ctx.is_hot() && !ctx.is_disabled() { env.get(theme::BORDER_LIGHT) } else { env.get(theme::BORDER_DARK) @@ -148,7 +150,13 @@ impl Widget for Radio { if *data == self.variant { let inner_circle = Circle::new((size / 2., size / 2.), INNER_CIRCLE_RADIUS); - ctx.fill(inner_circle, &env.get(theme::CURSOR_COLOR)); + let fill = if ctx.is_disabled() { + env.get(theme::DISABLED_TEXT_COLOR) + } else { + env.get(theme::CURSOR_COLOR) + }; + + ctx.fill(inner_circle, &fill); } // Paint the text label diff --git a/druid/src/widget/slider.rs b/druid/src/widget/slider.rs index 7ef62b3b12..acd5bf4810 100644 --- a/druid/src/widget/slider.rs +++ b/druid/src/widget/slider.rs @@ -84,45 +84,51 @@ impl Widget for Slider { match event { Event::MouseDown(mouse) => { - ctx.set_active(true); - if self.knob_hit_test(knob_size, mouse.pos) { - self.x_offset = self.knob_pos.x - mouse.pos.x - } else { - self.x_offset = 0.; - *data = self.calculate_value(mouse.pos.x, knob_size, slider_width); + if !ctx.is_disabled() { + ctx.set_active(true); + if self.knob_hit_test(knob_size, mouse.pos) { + self.x_offset = self.knob_pos.x - mouse.pos.x + } else { + self.x_offset = 0.; + *data = self.calculate_value(mouse.pos.x, knob_size, slider_width); + } + ctx.request_paint(); } - ctx.request_paint(); } Event::MouseUp(mouse) => { - if ctx.is_active() { - ctx.set_active(false); + if ctx.is_active() && !ctx.is_disabled() { *data = self.calculate_value(mouse.pos.x, knob_size, slider_width); ctx.request_paint(); } + ctx.set_active(false); } Event::MouseMove(mouse) => { - if ctx.is_active() { - *data = self.calculate_value(mouse.pos.x, knob_size, slider_width); - ctx.request_paint(); - } - if ctx.is_hot() { - let knob_hover = self.knob_hit_test(knob_size, mouse.pos); - if knob_hover != self.knob_hovered { - self.knob_hovered = knob_hover; + if !ctx.is_disabled() { + if ctx.is_active() { + *data = self.calculate_value(mouse.pos.x, knob_size, slider_width); ctx.request_paint(); } + if ctx.is_hot() { + let knob_hover = self.knob_hit_test(knob_size, mouse.pos); + if knob_hover != self.knob_hovered { + self.knob_hovered = knob_hover; + ctx.request_paint(); + } + } + } else { + ctx.set_active(false); } } _ => (), } } - #[instrument( - name = "Slider", - level = "trace", - skip(self, _ctx, _event, _data, _env) - )] - fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &f64, _env: &Env) {} + #[instrument(name = "Slider", level = "trace", skip(self, ctx, event, _data, _env))] + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &f64, _env: &Env) { + if let LifeCycle::DisabledChanged(_) = event { + ctx.request_paint(); + } + } #[instrument( name = "Slider", @@ -184,31 +190,37 @@ impl Widget for Slider { self.knob_pos = Point::new(knob_position, knob_size / 2.); let knob_circle = Circle::new(self.knob_pos, (knob_size - KNOB_STROKE_WIDTH) / 2.); - let normal_knob_gradient = LinearGradient::new( - UnitPoint::TOP, - UnitPoint::BOTTOM, - ( - env.get(theme::FOREGROUND_LIGHT), - env.get(theme::FOREGROUND_DARK), - ), - ); - let flipped_knob_gradient = LinearGradient::new( - UnitPoint::TOP, - UnitPoint::BOTTOM, - ( - env.get(theme::FOREGROUND_DARK), - env.get(theme::FOREGROUND_LIGHT), - ), - ); - - let knob_gradient = if is_active { - flipped_knob_gradient + let knob_gradient = if ctx.is_disabled() { + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::DISABLED_FOREGROUND_LIGHT), + env.get(theme::DISABLED_FOREGROUND_DARK), + ), + ) + } else if ctx.is_active() { + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::FOREGROUND_DARK), + env.get(theme::FOREGROUND_LIGHT), + ), + ) } else { - normal_knob_gradient + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::FOREGROUND_LIGHT), + env.get(theme::FOREGROUND_DARK), + ), + ) }; //Paint the border - let border_color = if is_hovered || is_active { + let border_color = if (is_hovered || is_active) && !ctx.is_disabled() { env.get(theme::FOREGROUND_LIGHT) } else { env.get(theme::FOREGROUND_DARK) diff --git a/druid/src/widget/spinner.rs b/druid/src/widget/spinner.rs index 8cfb43310c..82e4945cfe 100644 --- a/druid/src/widget/spinner.rs +++ b/druid/src/widget/spinner.rs @@ -62,7 +62,7 @@ impl Default for Spinner { fn default() -> Self { Spinner { t: 0.0, - color: theme::LABEL_COLOR.into(), + color: theme::TEXT_COLOR.into(), } } } diff --git a/druid/src/widget/stepper.rs b/druid/src/widget/stepper.rs index 5db881ff7a..e11b570caa 100644 --- a/druid/src/widget/stepper.rs +++ b/druid/src/widget/stepper.rs @@ -139,6 +139,15 @@ impl Widget for Stepper { let increase_button_rect = Rect::from_origin_size(increase_button_origin, button_size); let decrease_button_rect = Rect::from_origin_size(decrease_button_origin, button_size); + let disabled_gradient = LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::DISABLED_BUTTON_LIGHT), + env.get(theme::DISABLED_BUTTON_DARK), + ), + ); + let active_gradient = LinearGradient::new( UnitPoint::TOP, UnitPoint::BOTTOM, @@ -152,13 +161,17 @@ impl Widget for Stepper { ); // draw buttons that are currently triggered as active - if self.increase_active { + if ctx.is_disabled() { + ctx.fill(increase_button_rect, &disabled_gradient); + } else if self.increase_active { ctx.fill(increase_button_rect, &active_gradient); } else { ctx.fill(increase_button_rect, &inactive_gradient); }; - if self.decrease_active { + if ctx.is_disabled() { + ctx.fill(decrease_button_rect, &disabled_gradient); + } else if self.decrease_active { ctx.fill(decrease_button_rect, &active_gradient); } else { ctx.fill(decrease_button_rect, &inactive_gradient); @@ -176,7 +189,13 @@ impl Widget for Stepper { arrows.line_to(Point::new(width / 2., height - 4.)); arrows.close_path(); - ctx.fill(arrows, &env.get(theme::LABEL_COLOR)); + let color = if ctx.is_disabled() { + env.get(theme::DISABLED_TEXT_COLOR) + } else { + env.get(theme::TEXT_COLOR) + }; + + ctx.fill(arrows, &color); } #[instrument( @@ -205,19 +224,21 @@ impl Widget for Stepper { match event { Event::MouseDown(mouse) => { - ctx.set_active(true); + if !ctx.is_disabled() { + ctx.set_active(true); - if mouse.pos.y > height / 2. { - self.decrease_active = true; - self.decrement(data); - } else { - self.increase_active = true; - self.increment(data); - } + if mouse.pos.y > height / 2. { + self.decrease_active = true; + self.decrement(data); + } else { + self.increase_active = true; + self.increment(data); + } - self.timer_id = ctx.request_timer(STEPPER_REPEAT_DELAY); + self.timer_id = ctx.request_timer(STEPPER_REPEAT_DELAY); - ctx.request_paint(); + ctx.request_paint(); + } } Event::MouseUp(_) => { ctx.set_active(false); @@ -229,19 +250,27 @@ impl Widget for Stepper { ctx.request_paint(); } Event::Timer(id) if *id == self.timer_id => { - if self.increase_active { - self.increment(data); - } - if self.decrease_active { - self.decrement(data); + if !ctx.is_disabled() { + if self.increase_active { + self.increment(data); + } + if self.decrease_active { + self.decrement(data); + } + self.timer_id = ctx.request_timer(STEPPER_REPEAT); + } else { + ctx.set_active(false); } - self.timer_id = ctx.request_timer(STEPPER_REPEAT); } _ => (), } } - fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &f64, _env: &Env) {} + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &f64, _env: &Env) { + if let LifeCycle::DisabledChanged(_) = event { + ctx.request_paint(); + } + } #[instrument( name = "Stepper", diff --git a/druid/src/widget/switch.rs b/druid/src/widget/switch.rs index bcfab8fae2..aeb8635085 100644 --- a/druid/src/widget/switch.rs +++ b/druid/src/widget/switch.rs @@ -63,6 +63,9 @@ impl Switch { } fn paint_labels(&mut self, ctx: &mut PaintCtx, env: &Env, switch_width: f64) { + self.on_text.rebuild_if_needed(ctx.text(), env); + self.off_text.rebuild_if_needed(ctx.text(), env); + let switch_height = env.get(theme::BORDERED_WIDGET_HEIGHT); let knob_size = switch_height - 2. * SWITCH_PADDING; @@ -98,16 +101,20 @@ impl Widget for Switch { match event { Event::MouseDown(_) => { - ctx.set_active(true); - ctx.request_paint(); + if !ctx.is_disabled() { + ctx.set_active(true); + ctx.request_paint(); + } } Event::MouseUp(_) => { - if self.knob_dragged { - // toggle value when dragging if knob has been moved far enough - *data = self.knob_pos.x > switch_width / 2.; - } else if ctx.is_active() { - // toggle value on click - *data = !*data; + if !ctx.is_disabled() { + if self.knob_dragged { + // toggle value when dragging if knob has been moved far enough + *data = self.knob_pos.x > switch_width / 2.; + } else if ctx.is_active() { + // toggle value on click + *data = !*data; + } } ctx.set_active(false); @@ -117,14 +124,18 @@ impl Widget for Switch { ctx.request_anim_frame(); } Event::MouseMove(mouse) => { - if ctx.is_active() { - self.knob_pos.x = mouse.pos.x.min(on_pos).max(off_pos); - self.knob_dragged = true; - } - if ctx.is_hot() { - self.knob_hovered = self.knob_hit_test(knob_size, mouse.pos) + if !ctx.is_disabled() { + if ctx.is_active() { + self.knob_pos.x = mouse.pos.x.min(on_pos).max(off_pos); + self.knob_dragged = true; + } + if ctx.is_hot() { + self.knob_hovered = self.knob_hit_test(knob_size, mouse.pos) + } + ctx.request_paint(); + } else { + ctx.set_active(false); } - ctx.request_paint(); } Event::AnimFrame(interval) => { let delta = Duration::from_nanos(*interval).as_secs_f64(); @@ -159,9 +170,28 @@ impl Widget for Switch { #[instrument(name = "Switch", level = "trace", skip(self, ctx, event, _data, env))] fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &bool, env: &Env) { - if matches!(event, LifeCycle::WidgetAdded) { - self.on_text.rebuild_if_needed(ctx.text(), env); - self.off_text.rebuild_if_needed(ctx.text(), env); + match event { + LifeCycle::WidgetAdded => { + self.on_text.rebuild_if_needed(ctx.text(), env); + self.off_text.rebuild_if_needed(ctx.text(), env); + } + LifeCycle::DisabledChanged(true) if self.knob_dragged => { + self.knob_dragged = false; + self.animation_in_progress = true; + ctx.request_anim_frame(); + } + LifeCycle::DisabledChanged(disabled) => { + ctx.request_paint(); + let color = if *disabled { + theme::DISABLED_TEXT_COLOR + } else { + theme::TEXT_COLOR + }; + self.off_text.set_text_color(color.clone()); + self.on_text.set_text_color(color); + ctx.request_paint(); + } + _ => {} } } @@ -185,6 +215,8 @@ impl Widget for Switch { _data: &bool, env: &Env, ) -> Size { + self.on_text.rebuild_if_needed(ctx.text(), env); + let text_metrics = self.on_text.layout_metrics(); let height = env.get(theme::BORDERED_WIDGET_HEIGHT); let width = height * SWITCH_WIDTH_RATIO; @@ -228,7 +260,11 @@ impl Widget for Switch { // paint different background for on and off state // opacity of background color depends on knob position // todo: make color configurable - let opacity = (self.knob_pos.x - off_pos) / (on_pos - off_pos); + let opacity = if ctx.is_disabled() { + 0.0 + } else { + (self.knob_pos.x - off_pos) / (on_pos - off_pos) + }; let background_gradient_on_state = LinearGradient::new( UnitPoint::TOP, @@ -256,27 +292,33 @@ impl Widget for Switch { let is_active = ctx.is_active(); let is_hovered = self.knob_hovered; - let normal_knob_gradient = LinearGradient::new( - UnitPoint::TOP, - UnitPoint::BOTTOM, - ( - env.get(theme::FOREGROUND_LIGHT), - env.get(theme::FOREGROUND_DARK), - ), - ); - let flipped_knob_gradient = LinearGradient::new( - UnitPoint::TOP, - UnitPoint::BOTTOM, - ( - env.get(theme::FOREGROUND_DARK), - env.get(theme::FOREGROUND_LIGHT), - ), - ); - - let knob_gradient = if is_active { - flipped_knob_gradient + let knob_gradient = if ctx.is_disabled() { + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::DISABLED_FOREGROUND_LIGHT), + env.get(theme::DISABLED_FOREGROUND_DARK), + ), + ) + } else if is_active { + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::FOREGROUND_DARK), + env.get(theme::FOREGROUND_LIGHT), + ), + ) } else { - normal_knob_gradient + LinearGradient::new( + UnitPoint::TOP, + UnitPoint::BOTTOM, + ( + env.get(theme::FOREGROUND_LIGHT), + env.get(theme::FOREGROUND_DARK), + ), + ) }; // paint the border diff --git a/druid/src/widget/textbox.rs b/druid/src/widget/textbox.rs index 78f0c9000f..e6c54823fe 100644 --- a/druid/src/widget/textbox.rs +++ b/druid/src/widget/textbox.rs @@ -400,19 +400,26 @@ impl Widget for TextBox { } } Event::MouseDown(mouse) if self.text().can_write() => { - if !mouse.focus { - ctx.request_focus(); - self.was_focused_from_click = true; - self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION)); - } else { - ctx.set_handled(); + if !ctx.is_disabled() { + if !mouse.focus { + ctx.request_focus(); + self.was_focused_from_click = true; + self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION)); + } else { + ctx.set_handled(); + } } } Event::Timer(id) => { - if *id == self.cursor_timer && ctx.has_focus() { - self.cursor_on = !self.cursor_on; + if !ctx.is_disabled() { + if *id == self.cursor_timer && ctx.has_focus() { + self.cursor_on = !self.cursor_on; + ctx.request_paint(); + self.cursor_timer = ctx.request_timer(CURSOR_BLINK_DURATION); + } + } else if self.cursor_on { + self.cursor_on = false; ctx.request_paint(); - self.cursor_timer = ctx.request_timer(CURSOR_BLINK_DURATION); } } Event::ImeStateChange => { diff --git a/druid/src/widget/widget_ext.rs b/druid/src/widget/widget_ext.rs index 6066b47143..27dc7efe3b 100644 --- a/druid/src/widget/widget_ext.rs +++ b/druid/src/widget/widget_ext.rs @@ -281,6 +281,7 @@ pub trait WidgetExt: Widget + Sized + 'static { /// /// [`is_disabled`]: crate::EventCtx::is_disabled /// [`set_disabled`]: crate::EventCtx::set_disabled + /// [`DisabledIf`]: crate::widget::DisabledIf fn disabled_if(self, disabled_if: impl Fn(&T, &Env) -> bool + 'static) -> DisabledIf { DisabledIf::new(self, disabled_if) }