diff --git a/crates/kas-core/src/action.rs b/crates/kas-core/src/action.rs index 40bba1144..d0656f54d 100644 --- a/crates/kas-core/src/action.rs +++ b/crates/kas-core/src/action.rs @@ -7,76 +7,34 @@ #[allow(unused)] use crate::event::{ConfigCx, EventCx, EventState}; -use std::ops::{BitOr, BitOrAssign, Deref}; /// Action: widget has moved/opened/closed /// -/// When the state is `true`, this indicates that the following should happen: +/// This action indicates that the following should happen: /// /// - Re-probe which widget is under the mouse / any touch instance / any /// other location picker since widgets may have moved /// - Redraw the window #[must_use] -#[derive(Copy, Clone, Debug, Default)] -pub struct ActionMoved(pub bool); - -impl BitOr for ActionMoved { - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self { - ActionMoved(self.0 | rhs.0) - } -} - -impl BitOrAssign for ActionMoved { - #[inline] - fn bitor_assign(&mut self, rhs: Self) { - self.0 |= rhs.0; - } -} - -impl Deref for ActionMoved { - type Target = bool; - #[inline] - fn deref(&self) -> &bool { - &self.0 - } -} +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct ActionMoved; /// Action: widget must be resized +/// +/// This type implies that either a local or full-window resize is required. #[must_use] -#[derive(Copy, Clone, Debug, Default)] -pub struct ActionResize(pub bool); - -impl ActionResize { - #[inline] - pub(crate) fn clear(&mut self) { - self.0 = false; - } -} +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct ActionResize; -impl BitOr for ActionResize { - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self { - ActionResize(self.0 | rhs.0) - } -} - -impl BitOrAssign for ActionResize { - #[inline] - fn bitor_assign(&mut self, rhs: Self) { - self.0 |= rhs.0; - } -} +/// Action: content must be redrawn +#[must_use] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct ActionRedraw; -impl Deref for ActionResize { - type Target = bool; - #[inline] - fn deref(&self) -> &bool { - &self.0 - } -} +/// Action: close window +#[must_use] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub(crate) struct ActionClose; bitflags! { /// Action: configuration data updates must be applied @@ -92,31 +50,10 @@ bitflags! { } } -bitflags! { - /// Action required after processing - /// - /// Some methods operate directly on a context ([`ConfigCx`] or [`EventCx`]) - /// while others don't reqiure a context but do require that some *action* - /// is performed afterwards. This enum is used to convey that action. - /// - /// A `WindowAction` produced at run-time should be passed to a context, usually - /// via [`EventState::action`] (to associate the `WindowAction` with a widget) - /// or [`EventState::window_action`] (if no particular widget is relevant). - /// - /// A `WindowAction` produced before starting the GUI may be discarded, for - /// example: `let _ = runner.config_mut().font.set_size(24.0);`. - /// - /// Two `WindowAction` values may be combined via bit-or (`a | b`). - #[must_use] - #[derive(Copy, Clone, Debug, Default)] - pub struct WindowAction: u32 { - /// The whole window requires redrawing - /// - /// See also [`EventState::redraw`]. - const REDRAW = 1 << 0; - /// The current window should be closed - /// - /// See also [`EventState::exit`] which closes the UI (all windows). - const CLOSE = 1 << 30; - } +/// Set of actions which may affect a window +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub(crate) struct WindowActions { + pub resize: Option, + pub redraw: Option, + pub close: Option, } diff --git a/crates/kas-core/src/core/events.rs b/crates/kas-core/src/core/events.rs index f6a077fb0..9c428e62b 100644 --- a/crates/kas-core/src/core/events.rs +++ b/crates/kas-core/src/core/events.rs @@ -315,14 +315,15 @@ pub trait Events: Widget + Sized { /// locally and should implement this method to do so /// (thus avoiding the need for a full-window resize). /// - /// Return `ActionResize(true)` if further resizing is needed, or - /// `ActionResize(false)` if resizing is complete. + /// Return `Some(ActionResize)` if further resizing is needed, or `None` if + /// resizing is complete. /// - /// The default implementation simply returns `ActionResize(true)`. + /// The default implementation simply returns `Some(ActionResize)`. #[inline] - fn handle_resize(&mut self, cx: &mut ConfigCx, data: &Self::Data) -> ActionResize { + #[must_use] + fn handle_resize(&mut self, cx: &mut ConfigCx, data: &Self::Data) -> Option { let _ = (cx, data); - ActionResize(true) + Some(ActionResize) } /// Handler for scrolling diff --git a/crates/kas-core/src/core/impls.rs b/crates/kas-core/src/core/impls.rs index 05faee3da..1e5f57279 100644 --- a/crates/kas-core/src/core/impls.rs +++ b/crates/kas-core/src/core/impls.rs @@ -41,7 +41,7 @@ pub fn _update(widget: &mut W, cx: &mut ConfigCx, data: &W::Data) { cx.update(node); } - if *cx.resize && widget.status().is_sized() { + if cx.resize.is_some() && widget.status().is_sized() { cx.resize = widget.handle_resize(cx, data); } } @@ -96,7 +96,7 @@ pub fn _send( ); } - if *cx.resize { + if cx.resize.is_some() { debug_assert!(widget.status().is_sized()); cx.resize = widget.handle_resize(cx, data); } @@ -144,7 +144,7 @@ pub fn _replay(widget: &mut W, cx: &mut EventCx, data: & ); } - if *cx.resize && widget.status().is_sized() { + if cx.resize.is_some() && widget.status().is_sized() { cx.resize = widget.handle_resize(cx, data); } diff --git a/crates/kas-core/src/event/components.rs b/crates/kas-core/src/event/components.rs index 92fc53265..5b07e6201 100644 --- a/crates/kas-core/src/event/components.rs +++ b/crates/kas-core/src/event/components.rs @@ -197,10 +197,11 @@ impl ScrollComponent { /// - `content_size`: size of scroll region on the inside (usually larger) /// /// Returns an [`ActionMoved`] indicating whether the scroll offset changed. - pub fn set_sizes(&mut self, window_size: Size, content_size: Size) -> ActionMoved { + #[must_use] + pub fn set_sizes(&mut self, window_size: Size, content_size: Size) -> Option { let max_offset = (Offset::conv(content_size) - Offset::conv(window_size)).max(Offset::ZERO); if max_offset == self.max_offset { - return ActionMoved(false); + return None; } self.max_offset = max_offset; self.set_offset(self.offset) @@ -212,14 +213,15 @@ impl ScrollComponent { /// /// Also cancels any kinetic scrolling, but only if `offset` is not equal /// to the current offset. - pub fn set_offset(&mut self, offset: Offset) -> ActionMoved { + #[must_use] + pub fn set_offset(&mut self, offset: Offset) -> Option { let offset = offset.clamp(Offset::ZERO, self.max_offset); if offset == self.offset { - ActionMoved(false) + None } else { self.kinetic.stop(); self.offset = offset; - ActionMoved(true) + Some(ActionMoved) } } @@ -231,7 +233,13 @@ impl ScrollComponent { /// - `window_rect`: the rect of the scroll window /// /// Sets [`Scroll::Rect`] to ensure correct scrolling of parents. - pub fn focus_rect(&mut self, cx: &mut EventCx, rect: Rect, window_rect: Rect) -> ActionMoved { + #[must_use] + pub fn focus_rect( + &mut self, + cx: &mut EventCx, + rect: Rect, + window_rect: Rect, + ) -> Option { let action = self.self_focus_rect(rect, window_rect); cx.set_scroll(Scroll::Rect(rect - self.offset)); action @@ -243,7 +251,8 @@ impl ScrollComponent { /// [`EventCx::set_scroll`], thus will not affect ancestors. #[cfg_attr(not(feature = "internal_doc"), doc(hidden))] #[cfg_attr(docsrs, doc(cfg(internal_doc)))] - pub fn self_focus_rect(&mut self, rect: Rect, window_rect: Rect) -> ActionMoved { + #[must_use] + pub fn self_focus_rect(&mut self, rect: Rect, window_rect: Rect) -> Option { self.kinetic.stop(); let max_vis = rect.pos - window_rect.pos; let extra_size = Offset::conv(rect.size) - Offset::conv(window_rect.size); diff --git a/crates/kas-core/src/event/config_cx.rs b/crates/kas-core/src/event/config_cx.rs index 8bc6dec70..57bb8d1ed 100644 --- a/crates/kas-core/src/event/config_cx.rs +++ b/crates/kas-core/src/event/config_cx.rs @@ -8,7 +8,7 @@ use crate::event::EventState; use crate::text::format::FormattableText; use crate::theme::{SizeCx, Text, ThemeSize}; -use crate::{ActionResize, Id, Node}; +use crate::{ActionRedraw, ActionResize, Id, Node}; use std::any::TypeId; use std::fmt::Debug; use std::ops::{Deref, DerefMut}; @@ -23,8 +23,8 @@ use std::ops::{Deref, DerefMut}; pub struct ConfigCx<'a> { pub(super) theme: &'a dyn ThemeSize, pub(crate) state: &'a mut EventState, - pub(crate) resize: ActionResize, - pub(crate) redraw: bool, + pub(crate) resize: Option, + pub(crate) redraw: Option, } impl<'a> ConfigCx<'a> { @@ -35,8 +35,8 @@ impl<'a> ConfigCx<'a> { ConfigCx { theme: sh, state: ev, - resize: ActionResize(false), - redraw: false, + resize: None, + redraw: None, } } @@ -64,7 +64,7 @@ impl<'a> ConfigCx<'a> { // (Except redraw: this doesn't matter.) let start_resize = std::mem::take(&mut self.resize); widget._configure(self, id); - self.resize |= start_resize; + self.resize = self.resize.or(start_resize); } /// Update a widget @@ -77,7 +77,7 @@ impl<'a> ConfigCx<'a> { // (Except redraw: this doesn't matter.) let start_resize = std::mem::take(&mut self.resize); widget._update(self); - self.resize |= start_resize; + self.resize = self.resize.or(start_resize); } /// Configure a text object @@ -126,7 +126,7 @@ impl<'a> ConfigCx<'a> { /// during the traversal unwind if possible. #[inline] pub fn redraw(&mut self) { - self.redraw = true; + self.redraw = Some(ActionRedraw); } /// Require that the current widget (and its descendants) be resized @@ -136,7 +136,7 @@ impl<'a> ConfigCx<'a> { /// during the traversal unwind if possible. #[inline] pub fn resize(&mut self) { - self.resize = ActionResize(true); + self.resize = Some(ActionResize); } } diff --git a/crates/kas-core/src/event/cx/mod.rs b/crates/kas-core/src/event/cx/mod.rs index df3375664..dda815183 100644 --- a/crates/kas-core/src/event/cx/mod.rs +++ b/crates/kas-core/src/event/cx/mod.rs @@ -24,7 +24,7 @@ use crate::runner::{Platform, RunnerT, WindowDataErased}; #[allow(unused)] use crate::theme::SizeCx; use crate::theme::ThemeSize; use crate::window::{PopupDescriptor, WindowId}; -use crate::{ActionMoved, ActionResize, ConfigAction, HasId, Id, Node, WindowAction}; +use crate::{ActionClose, ActionMoved, ActionRedraw, ActionResize, ConfigAction, HasId, Id, Node}; use key::PendingSelFocus; use nav::PendingNavFocus; @@ -104,8 +104,9 @@ pub struct EventState { // Optional new target for selection focus. bool is true if this also gains key focus. pending_sel_focus: Option, pending_nav_focus: PendingNavFocus, - pub(crate) action: WindowAction, - action_moved: ActionMoved, + action_moved: Option, + pub(crate) action_redraw: Option, + action_close: Option, } impl EventState { @@ -145,8 +146,9 @@ impl EventState { pending_update: None, pending_sel_focus: None, pending_nav_focus: PendingNavFocus::None, - action: WindowAction::empty(), - action_moved: ActionMoved(false), + action_moved: None, + action_redraw: None, + action_close: None, } } @@ -164,7 +166,12 @@ impl EventState { /// [`Id`] identifiers and call widgets' [`Events::configure`] /// method. Additionally, it updates the [`EventState`] to account for /// renamed and removed widgets. - pub(crate) fn full_configure(&mut self, sizer: &dyn ThemeSize, node: Node) -> ActionResize { + #[must_use] + pub(crate) fn full_configure( + &mut self, + sizer: &dyn ThemeSize, + node: Node, + ) -> Option { let id = Id::ROOT.make_child(self.window_id.get().cast()); log::debug!(target: "kas_core::event", "full_configure of Window{id}"); @@ -176,7 +183,7 @@ impl EventState { cx.configure(node, id); let resize = cx.resize; // Ignore cx.redraw: we can assume a redraw will happen - self.action_moved = ActionMoved(true); + self.action_moved = Some(ActionMoved); resize } @@ -184,13 +191,14 @@ impl EventState { /// /// Invokes the given closure on this [`EventCx`]. #[inline] + #[must_use] pub(crate) fn with<'a, F: FnOnce(&mut EventCx)>( &'a mut self, runner: &'a mut dyn RunnerT, theme: &'a dyn ThemeSize, window: &'a dyn WindowDataErased, f: F, - ) -> ActionResize { + ) -> Option { let mut cx = EventCx { runner, window, @@ -198,13 +206,12 @@ impl EventState { target_is_disabled: false, last_child: None, scroll: Scroll::None, - resize_window: ActionResize::default(), + resize_window: None, }; f(&mut cx); - let resize = cx.resize | cx.resize_window; - if cx.redraw { - self.action |= WindowAction::REDRAW; - } + let resize = cx.resize.or(cx.resize_window); + let redraw = cx.redraw; + self.action_redraw = self.action_redraw.or(redraw); resize } @@ -296,12 +303,15 @@ impl EventState { } /// Notify that a widget must be redrawn + /// + /// This method is designed to support partial redraws though these are not + /// currently implemented. See also [`Self::action_redraw`]. #[inline] pub fn redraw(&mut self, id: impl HasId) { // NOTE: redraws are fast enough not to bother handling locally let _ = id; - self.action |= WindowAction::REDRAW; + self.action_redraw = Some(ActionRedraw); } /// Redraw `id` if not `None` @@ -317,25 +327,27 @@ impl EventState { /// This updates the widget(s) under mouse and touch events. #[inline] pub fn region_moved(&mut self) { - self.action_moved = ActionMoved(true); + self.action_moved = Some(ActionMoved); } - /// Notify that a [`WindowAction`] should happen for the whole window + /// Request that the window be closed #[inline] - pub fn window_action(&mut self, action: impl Into) { - self.action |= action.into(); + pub fn close_own_window(&mut self) { + self.action_close = Some(ActionClose); } - /// Request that the window be closed + /// Notify of an [`ActionMoved`] or `Option` #[inline] - pub fn close_own_window(&mut self) { - self.action |= WindowAction::CLOSE; + pub fn action_moved(&mut self, action: impl Into>) { + self.action_moved = self.action_moved.or(action.into()); } - /// Notify of an [`ActionMoved`] + /// Notify of an [`ActionRedraw`] or `Option` + /// + /// This will force a redraw of the whole window. See also [`Self::redraw`]. #[inline] - pub fn action_moved(&mut self, action: ActionMoved) { - self.action_moved |= action; + pub fn action_redraw(&mut self, action: impl Into>) { + self.action_redraw = self.action_redraw.or(action.into()); } /// Request update to widget `id` @@ -362,7 +374,7 @@ pub struct EventCx<'a> { pub(crate) target_is_disabled: bool, last_child: Option, scroll: Scroll, - resize_window: ActionResize, + resize_window: Option, } impl<'a> Deref for EventCx<'a> { @@ -408,7 +420,7 @@ impl<'a> EventCx<'a> { /// Check for clean flags pre-recursion fn pre_recursion(&self) { - debug_assert!(!*self.resize); + debug_assert!(self.resize.is_none()); debug_assert!(self.scroll == Scroll::None); debug_assert!(self.last_child.is_none()); } @@ -417,7 +429,7 @@ impl<'a> EventCx<'a> { fn post_recursion(&mut self) { self.last_child = None; self.scroll = Scroll::None; - self.resize_window |= self.resize; - self.resize.clear(); + self.resize_window = self.resize_window.or(self.resize); + self.resize = None; } } diff --git a/crates/kas-core/src/event/cx/press/mouse.rs b/crates/kas-core/src/event/cx/press/mouse.rs index 3d7368e82..3f60fadbf 100644 --- a/crates/kas-core/src/event/cx/press/mouse.rs +++ b/crates/kas-core/src/event/cx/press/mouse.rs @@ -12,7 +12,7 @@ use crate::event::{ use crate::geom::{Affine, Coord, DVec2, Vec2}; use crate::window::WindowErased; use crate::window::WindowWidget; -use crate::{Id, Node, TileExt, WindowAction}; +use crate::{ActionRedraw, Id, Node, TileExt}; use cast::{CastApprox, CastFloat}; use std::time::{Duration, Instant}; use winit::cursor::CursorIcon; @@ -152,8 +152,8 @@ impl Mouse { self.over.clone() } - fn update_grab(&mut self) -> (bool, bool) { - let (mut cancel, mut redraw) = (false, false); + fn update_grab(&mut self) -> (bool, Option) { + let (mut cancel, mut redraw) = (false, None); if let Some(grab) = self.grab.as_mut() { cancel = grab.cancel; if let GrabDetails::Click = grab.details { @@ -161,11 +161,11 @@ impl Mouse { if grab.start_id == over { if grab.depress.as_ref() != over { grab.depress = over.cloned(); - redraw = true; + redraw = Some(ActionRedraw); } } else if grab.depress.is_some() { grab.depress = None; - redraw = true; + redraw = Some(ActionRedraw); } } } @@ -313,12 +313,9 @@ impl<'a> EventCx<'a> { if cancel { self.remove_mouse_grab(node.re(), false); } + self.action_redraw(redraw); - if redraw { - self.action |= WindowAction::REDRAW; - } - - if self.action_moved.0 { + if self.action_moved.is_some() { let over = node.try_probe(self.mouse.last_position.cast_nearest()); self.set_over(node, over); } diff --git a/crates/kas-core/src/event/cx/press/touch.rs b/crates/kas-core/src/event/cx/press/touch.rs index 02c861941..91d773a2b 100644 --- a/crates/kas-core/src/event/cx/press/touch.rs +++ b/crates/kas-core/src/event/cx/press/touch.rs @@ -9,7 +9,7 @@ use super::{GrabMode, Press, PressSource, velocity}; use crate::config::EventWindowConfig; use crate::event::{Event, EventCx, EventState, FocusSource, NavAdvance, PressStart}; use crate::geom::{Affine, DVec2, Vec2}; -use crate::{Id, Node, WindowAction}; +use crate::{ActionRedraw, Id, Node}; use cast::{Cast, CastApprox, CastFloat, Conv}; use smallvec::SmallVec; use winit::event::FingerId; @@ -33,19 +33,19 @@ pub(super) struct TouchGrab { } impl TouchGrab { - fn flush_click_move(&mut self) -> WindowAction { + fn flush_click_move(&mut self) -> Option { if self.mode == GrabMode::Click { if self.start_id == self.over { if self.depress != self.over { self.depress = self.over.clone(); - return WindowAction::REDRAW; + return Some(ActionRedraw); } } else if self.depress.is_some() { self.depress = None; - return WindowAction::REDRAW; + return Some(ActionRedraw); } } - WindowAction::empty() + None } } @@ -241,7 +241,7 @@ impl EventState { ); self.opt_redraw(grab.depress.clone()); self.touch.remove_pan_grab(grab.pan_grab); - self.window_action(grab.flush_click_move()); + self.action_redraw(grab.flush_click_move()); grab } } @@ -250,8 +250,8 @@ impl<'a> EventCx<'a> { pub(in crate::event::cx) fn touch_handle_pending(&mut self, mut node: Node<'_>) { let mut i = 0; while i < self.touch.touch_grab.len() { - let action = self.touch.touch_grab[i].flush_click_move(); - self.state.action |= action; + let redraw = self.touch.touch_grab[i].flush_click_move(); + self.action_redraw(redraw); if self.touch.touch_grab[i].cancel { let grab = self.remove_touch(i); @@ -271,7 +271,7 @@ impl<'a> EventCx<'a> { } } - if self.action_moved.0 { + if self.action_moved.is_some() { for grab in self.touch.touch_grab.iter_mut() { grab.over = node.try_probe(grab.last_position.cast_nearest()); } diff --git a/crates/kas-core/src/event/cx/window.rs b/crates/kas-core/src/event/cx/window.rs index e1ab6829e..5fcc23054 100644 --- a/crates/kas-core/src/event/cx/window.rs +++ b/crates/kas-core/src/event/cx/window.rs @@ -13,7 +13,7 @@ use crate::theme::ThemeSize; #[cfg(all(wayland_platform, feature = "clipboard"))] use crate::util::warn_about_error; use crate::window::{PopupDescriptor, Window, WindowId, WindowWidget}; -use crate::{ActionResize, Id, Node, WindowAction}; +use crate::{ActionRedraw, Id, Node, WindowActions}; use winit::event::{ButtonSource, ElementState, PointerKind, PointerSource}; use winit::window::ResizeDirection; @@ -58,13 +58,14 @@ impl EventState { } /// Handle all pending items before event loop sleeps + #[must_use] pub(crate) fn flush_pending<'a>( &'a mut self, runner: &'a mut dyn RunnerT, theme: &'a dyn ThemeSize, window: &'a dyn WindowDataErased, mut node: Node, - ) -> (ActionResize, WindowAction) { + ) -> WindowActions { if !self.pending_send_targets.is_empty() { runner.set_send_targets(&mut self.pending_send_targets); } @@ -116,9 +117,8 @@ impl EventState { } // Finally, clear the region_moved flag (mouse and touch sub-systems handle this). - if cx.action_moved.0 { - cx.action_moved.0 = false; - cx.action.insert(WindowAction::REDRAW); + if cx.action_moved.take().is_some() { + cx.action_redraw(ActionRedraw); } }); @@ -126,7 +126,11 @@ impl EventState { window.set_pointer_icon(icon); } - (resize, std::mem::take(&mut self.action)) + WindowActions { + resize, + redraw: self.action_redraw.take(), + close: self.action_close.take(), + } } /// Application suspended. Clean up temporary state. diff --git a/crates/kas-core/src/lib.rs b/crates/kas-core/src/lib.rs index 136efb777..dd1e913d5 100644 --- a/crates/kas-core/src/lib.rs +++ b/crates/kas-core/src/lib.rs @@ -28,7 +28,8 @@ pub mod widgets; pub mod window; pub use crate::core::*; -pub use action::{ActionMoved, ActionResize, ConfigAction, WindowAction}; +pub(crate) use action::{ActionClose, WindowActions}; +pub use action::{ActionMoved, ActionRedraw, ActionResize, ConfigAction}; pub use kas_macros::{autoimpl, extends, impl_default}; pub use kas_macros::{cell_collection, collection, impl_anon, impl_scope, impl_self}; pub use kas_macros::{layout, widget, widget_index}; diff --git a/crates/kas-core/src/prelude.rs b/crates/kas-core/src/prelude.rs index b6a063e14..3499cbec4 100644 --- a/crates/kas-core/src/prelude.rs +++ b/crates/kas-core/src/prelude.rs @@ -22,7 +22,7 @@ pub use crate::layout::{Align, AlignHints, AlignPair, AxisInfo, SizeRules, Stret #[doc(no_inline)] pub use crate::widget_index; #[doc(no_inline)] pub use crate::window::{Window, WindowId}; #[doc(no_inline)] -pub use crate::{ActionMoved, ActionResize}; +pub use crate::{ActionMoved, ActionRedraw, ActionResize}; #[doc(no_inline)] pub use crate::{ChildIndices, Node}; #[doc(no_inline)] pub use crate::{Events, Layout, Role, RoleCx, RoleCxExt, Tile, TileExt, Viewport, Widget}; diff --git a/crates/kas-core/src/runner/event_loop.rs b/crates/kas-core/src/runner/event_loop.rs index af84a7bf0..bb50e58c5 100644 --- a/crates/kas-core/src/runner/event_loop.rs +++ b/crates/kas-core/src/runner/event_loop.rs @@ -8,7 +8,7 @@ use super::{AppData, GraphicsInstance, Pending, Shared}; use super::{ProxyAction, Window}; use crate::theme::Theme; -use crate::{WindowAction, window::WindowId}; +use crate::window::WindowId; use std::collections::HashMap; use std::time::Instant; use winit::application::ApplicationHandler; @@ -170,7 +170,7 @@ where fn suspended(&mut self, _: &dyn ActiveEventLoop) { if !self.suspended { self.windows - .retain(|_, window| window.suspend(&mut self.shared, &self.data)); + .retain(|_, window| window.suspend(&mut self.shared, &self.data).is_none()); self.data.suspended(); self.shared.suspended(); self.suspended = true; @@ -286,16 +286,16 @@ where self.resumes.clear(); self.windows.retain(|window_id, window| { - let (action, resume) = window.flush_pending(&mut self.shared, &self.data); + let (actions, resume) = window.flush_pending(&mut self.shared, &self.data); if let Some(instant) = resume { self.resumes.push((instant, *window_id)); } - if close_all || action.contains(WindowAction::CLOSE) { + if close_all || actions.close.is_some() { window.suspend(&mut self.shared, &self.data); // Call flush_pending again since suspend may queue messages. - // We don't care about the returned WindowAction or resume times since + // We don't care about the returned WindowActions or resume times since // the window is being destroyed. let _ = window.flush_pending(&mut self.shared, &self.data); diff --git a/crates/kas-core/src/runner/window.rs b/crates/kas-core/src/runner/window.rs index 92c6dc227..144a4488f 100644 --- a/crates/kas-core/src/runner/window.rs +++ b/crates/kas-core/src/runner/window.rs @@ -18,7 +18,9 @@ use crate::layout::SolveCache; use crate::messages::Erased; use crate::theme::{DrawCx, SizeCx, Theme, ThemeDraw, Window as _}; use crate::window::{BoxedWindow, Decorations, PopupDescriptor, WindowId, WindowWidget}; -use crate::{ActionResize, ConfigAction, Id, Layout, Tile, Widget, WindowAction, autoimpl}; +use crate::{ + ActionClose, ActionResize, ConfigAction, Id, Layout, Tile, Widget, WindowActions, autoimpl, +}; #[cfg(windows_platform)] use raw_window_handle::HasWindowHandle; use std::cell::RefCell; @@ -123,7 +125,7 @@ impl> Window { let mut theme = shared.theme.new_window(config); let mut node = self.widget.as_node(data); - let _: ActionResize = self.ev_state.full_configure(theme.size(), node.re()); + let _: Option = self.ev_state.full_configure(theme.size(), node.re()); let mut cx = SizeCx::new(&mut self.ev_state, theme.size()); let mut solve_cache = SolveCache::default(); @@ -182,7 +184,7 @@ impl> Window { // Update text size which is assigned during configure let mut node = self.widget.as_node(data); - let _: ActionResize = self.ev_state.full_configure(theme.size(), node.re()); + let _: Option = self.ev_state.full_configure(theme.size(), node.re()); let mut cx = SizeCx::new(&mut self.ev_state, theme.size()); solve_cache.find_constraints(node, &mut cx); @@ -264,25 +266,25 @@ impl> Window { } /// Application suspended. Clean up temporary state. - /// - /// Returns `true` unless this `Window` should be destoyed. - pub(super) fn suspend(&mut self, shared: &mut Shared, data: &A) -> bool { + pub(super) fn suspend( + &mut self, + shared: &mut Shared, + data: &A, + ) -> Option { if let Some((ref theme, ref mut window)) = self.theme_and_window { self.ev_state.suspended(shared); - let (resize, action) = self.ev_state.flush_pending( + let actions = self.ev_state.flush_pending( shared, theme.size(), window, self.widget.as_node(data), ); - // NOTE: assume we don't need to resize - let _ = resize; - - !action.contains(WindowAction::CLOSE) + // NOTE: ignore resize, redraw actions + actions.close } else { - true + None } } @@ -360,10 +362,13 @@ impl> Window { } WindowEvent::RedrawRequested => return self.do_draw(shared, data).is_err(), event => { - let resize = self.ev_state.with(shared, theme.size(), window, |cx| { - cx.handle_winit(&mut self.widget, data, event); - }); - (*resize, *resize, false) + let resize = self + .ev_state + .with(shared, theme.size(), window, |cx| { + cx.handle_winit(&mut self.widget, data, event); + }) + .is_some(); + (resize, resize, false) } }; if apply_size { @@ -377,26 +382,26 @@ impl> Window { &mut self, shared: &mut Shared, data: &A, - ) -> (WindowAction, Option) { + ) -> (WindowActions, Option) { let Some((ref theme, ref mut window)) = self.theme_and_window else { - return (WindowAction::empty(), None); + return (WindowActions::default(), None); }; - let (resize, action) = + let actions = self.ev_state .flush_pending(shared, theme.size(), window, self.widget.as_node(data)); - if *resize { + if actions.resize.is_some() { self.apply_size(data, false, true); } - if action.contains(WindowAction::CLOSE) { - return (action, None); + if actions.close.is_some() { + return (actions, None); } let Some((_, ref mut window)) = self.theme_and_window else { unreachable!(); }; - if !action.is_empty() { + if actions != WindowActions::default() { window.need_redraw = true; } @@ -416,7 +421,7 @@ impl> Window { window.request_redraw(); } - (action, resume) + (actions, resume) } /// Send an erased message @@ -507,7 +512,7 @@ impl> Window { let resize = self.ev_state.with(shared, theme.size(), window, |cx| { cx.update_timer(widget); }); - if *resize { + if resize.is_some() { self.apply_size(data, false, true); } } else { @@ -545,10 +550,10 @@ impl> Window { return; }; - let resize: ActionResize = self + let resize: Option = self .ev_state .full_configure(theme.size(), self.widget.as_node(data)); - if *resize { + if resize.is_some() { self.apply_size(data, false, true); } @@ -564,9 +569,9 @@ impl> Window { let size = theme.size(); let mut cx = ConfigCx::new(&size, &mut self.ev_state); cx.update(self.widget.as_node(data)); - if *cx.resize { + if cx.resize.is_some() { self.apply_size(data, false, true); - } else if cx.redraw { + } else if cx.redraw.is_some() { window.request_redraw(); } @@ -628,7 +633,7 @@ impl> Window { let resize = self.ev_state.with(shared, theme.size(), window, |cx| { cx.frame_update(widget); }); - if *resize { + if resize.is_some() { self.apply_size(data, false, true); } let Some((ref mut theme, ref mut window)) = self.theme_and_window else { @@ -662,7 +667,7 @@ impl> Window { let time2 = Instant::now(); window.need_redraw = window.surface.common_mut().immediate_redraw(); - self.ev_state.action -= WindowAction::REDRAW; + self.ev_state.action_redraw = None; // NOTE: we used to return Err(()) if !action.is_empty() here, e.g. if a // widget requested a resize during draw. Likely it's better not to do // this even if the frame is imperfect. diff --git a/crates/kas-widgets/src/edit/edit_box.rs b/crates/kas-widgets/src/edit/edit_box.rs index 969f152e0..534e12b6b 100644 --- a/crates/kas-widgets/src/edit/edit_box.rs +++ b/crates/kas-widgets/src/edit/edit_box.rs @@ -180,20 +180,20 @@ mod EditBox { return; }; - if action.0 { - cx.action_moved(action); + if let Some(moved) = action { + cx.action_moved(moved); self.update_scroll_offset(cx); } } - fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> ActionResize { + fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> Option { let size = self.inner.rect().size; let axis = AxisInfo::new(false, Some(size.1)); let mut resize = self.inner.size_rules(&mut cx.size_cx(), axis).min_size() > size.0; let axis = AxisInfo::new(true, Some(size.0)); resize |= self.inner.size_rules(&mut cx.size_cx(), axis).min_size() > size.1; self.update_content_size(cx); - ActionResize(resize) + resize.then_some(ActionResize) } fn handle_scroll(&mut self, cx: &mut EventCx<'_>, _: &G::Data, scroll: Scroll) { diff --git a/crates/kas-widgets/src/scroll.rs b/crates/kas-widgets/src/scroll.rs index 4b14a4417..bfda0dcff 100644 --- a/crates/kas-widgets/src/scroll.rs +++ b/crates/kas-widgets/src/scroll.rs @@ -456,12 +456,12 @@ mod ScrollRegion { .update_offset(cx, data, self.inner.rect(), offset); } - fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> ActionResize { + fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> Option { let _ = self.size_rules(&mut cx.size_cx(), AxisInfo::new(false, None)); let width = self.rect().size.0; let _ = self.size_rules(&mut cx.size_cx(), AxisInfo::new(true, Some(width))); self.set_rect(&mut cx.size_cx(), self.rect(), self.hints); - ActionResize(false) + None } fn handle_scroll(&mut self, cx: &mut EventCx, data: &Self::Data, scroll: Scroll) { diff --git a/crates/kas-widgets/src/scroll_label.rs b/crates/kas-widgets/src/scroll_label.rs index 8fc98df10..b1127eba7 100644 --- a/crates/kas-widgets/src/scroll_label.rs +++ b/crates/kas-widgets/src/scroll_label.rs @@ -541,13 +541,13 @@ mod ScrollText { return; }; - if action.0 { - cx.action_moved(action); + if let Some(moved) = action { + cx.action_moved(moved); self.vert_bar.set_value(cx, self.scroll.offset().1); } } - fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> ActionResize { + fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> Option { let size = self.text.rect().size; let axis = AxisInfo::new(false, Some(size.1)); let mut resize = self.text.size_rules(&mut cx.size_cx(), axis).min_size() > size.0; @@ -556,7 +556,7 @@ mod ScrollText { self.text .set_rect(&mut cx.size_cx(), self.text.rect(), Default::default()); self.update_content_size(cx); - ActionResize(resize) + resize.then_some(ActionResize) } fn handle_scroll(&mut self, cx: &mut EventCx, _: &Self::Data, scroll: Scroll) {