-
Notifications
You must be signed in to change notification settings - Fork 569
Disable widgets #1632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Disable widgets #1632
Changes from 28 commits
fe93a1b
3e1874b
d6e34b2
500954e
f606199
6fd0b71
25d19c9
e3d8b92
77d4f8f
4d48edd
9b2c8ba
d5116de
6c79e1e
1fe6971
40be21f
5520e5b
232e7ee
eccd596
9754051
74b86b2
c181f51
cfc544e
05d45fc
b9b9aca
f091bf9
4766ceb
a901d1c
eb3cfd7
fdd510a
4ef9697
4745fe9
4567f8f
ba58aad
8d6b784
d3e73d1
7be336c
a52437c
2919953
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,6 +106,21 @@ pub(crate) struct WidgetState { | |
| pub(crate) viewport_offset: Vec2, | ||
|
|
||
| // TODO: consider using bitflags for the booleans. | ||
| // `true` if a descendent of this widget changed its disabled state and should receive | ||
| // LifeCycle::DisabledChanged or InternalLifeCycle::RouteDisabledChanged | ||
| pub(crate) children_disabled_changed: bool, | ||
|
|
||
| // `true` if one of our ancestors is disabled (meaning we are also disabled). | ||
| pub(crate) ancestor_disabled: bool, | ||
|
|
||
| // `true` if this widget has been explicitly disabled. | ||
| // A widget can be disabled without being *explicitly* disabled if an ancestor is disabled. | ||
| pub(crate) is_explicitly_disabled: bool, | ||
|
|
||
| // `true` if this widget has been explicitly disabled, but has not yet seen one of | ||
| // LifeCycle::DisabledChanged or InternalLifeCycle::RouteDisabledChanged | ||
| pub(crate) is_explicitly_disabled_new: bool, | ||
|
|
||
| pub(crate) is_hot: bool, | ||
|
|
||
| pub(crate) is_active: bool, | ||
|
|
@@ -125,6 +140,8 @@ pub(crate) struct WidgetState { | |
| /// Any descendant has requested update. | ||
| pub(crate) request_update: bool, | ||
|
|
||
| pub(crate) update_focus_chain: bool, | ||
|
|
||
| pub(crate) focus_chain: Vec<WidgetId>, | ||
| pub(crate) request_focus: Option<FocusChange>, | ||
| pub(crate) children: Bloom<WidgetId>, | ||
|
|
@@ -908,11 +925,29 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> { | |
| } else { | ||
| if self.state.children_changed { | ||
| self.state.children.clear(); | ||
| self.state.focus_chain.clear(); | ||
| } | ||
| self.state.children_changed | ||
| } | ||
| } | ||
| InternalLifeCycle::RouteDisabledChanged => { | ||
| let was_disabled = self.state.is_disabled(); | ||
|
|
||
| self.state.is_explicitly_disabled = self.state.is_explicitly_disabled_new; | ||
|
|
||
| if was_disabled != self.state.is_disabled() { | ||
| // In case we change but none of our children we still need to update the | ||
| // focus-chain | ||
| self.state.update_focus_chain = true; | ||
| extra_event = Some(LifeCycle::DisabledChanged(self.state.is_disabled())); | ||
| //Each widget needs only one of DisabledChanged and RouteDisabledChanged | ||
| false | ||
| } else if self.state.children_disabled_changed { | ||
| self.state.update_focus_chain = true; | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
| } | ||
| InternalLifeCycle::RouteFocusChanged { old, new } => { | ||
| let this_changed = if *old == Some(self.state.id) { | ||
| Some(false) | ||
|
|
@@ -962,6 +997,8 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> { | |
| assert!(self.old_data.is_none()); | ||
| trace!("Received LifeCycle::WidgetAdded"); | ||
|
|
||
| self.state.update_focus_chain = true; | ||
|
|
||
| self.old_data = Some(data.clone()); | ||
| self.env = Some(env.clone()); | ||
|
|
||
|
|
@@ -980,13 +1017,37 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> { | |
| // This event was meant only for our parent, so don't recurse. | ||
| false | ||
| } | ||
| LifeCycle::DisabledChanged(ancestors_disabled) => { | ||
| let was_disabled = self.state.is_disabled(); | ||
|
|
||
| self.state.is_explicitly_disabled = self.state.is_explicitly_disabled_new; | ||
| self.state.ancestor_disabled = *ancestors_disabled; | ||
|
|
||
| // the change direction (true -> false or false -> true) of our parent and ourself | ||
| // is always the same, or we dont change at all, because we stay disabled if either | ||
| // we or our parent are disabled. | ||
| if was_disabled != self.state.is_disabled() { | ||
| self.state.update_focus_chain = true; | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
|
||
| } | ||
| //NOTE: this is not sent here, but from the special set_hot_state method | ||
| LifeCycle::HotChanged(_) => false, | ||
| LifeCycle::FocusChanged(_) => { | ||
| // We are a descendant of a widget that has/had focus. | ||
| // Descendants don't inherit focus, so don't recurse. | ||
| false | ||
| } | ||
| LifeCycle::BuildFocusChain => { | ||
| if self.state.update_focus_chain { | ||
| self.state.focus_chain.clear(); | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| let mut child_ctx = LifeCycleCtx { | ||
|
|
@@ -1002,18 +1063,40 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> { | |
| self.inner.lifecycle(&mut child_ctx, event, data, env); | ||
| } | ||
|
|
||
| ctx.widget_state.merge_up(&mut self.state); | ||
| // Sync our state with our parent's state after the event! | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm slightly scared of all the little bits of code scattered throughout this method, and I think it is going to be very important to carefully document what our invariants are, and what should go where. For instance: why does this need to come after
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed some of them and added documentation to explain the rest.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm :/ It wasn't as easy as i thought, but i think some of this complexity is necessary.
The current implementation always adds the own id when the focus_chain is cleared and focus_chain is set. Only in |
||
| // we need to (re)register children in case of one of the following events | ||
| match event { | ||
| // we need to (re)register children in case of one of the following events | ||
| LifeCycle::WidgetAdded | LifeCycle::Internal(InternalLifeCycle::RouteWidgetAdded) => { | ||
| self.state.children_changed = false; | ||
| ctx.widget_state.children = ctx.widget_state.children.union(self.state.children); | ||
| ctx.widget_state.focus_chain.extend(&self.state.focus_chain); | ||
| ctx.register_child(self.id()); | ||
| } | ||
| LifeCycle::DisabledChanged(_) | ||
| | LifeCycle::Internal(InternalLifeCycle::RouteDisabledChanged) => { | ||
| self.state.children_disabled_changed = false; | ||
|
|
||
| if self.state.is_disabled() && self.state.has_focus { | ||
| // This may gets overwritten. This is ok because it still ensures that a | ||
| // FocusChange is routed after we updated the focus-chain. | ||
| self.state.request_focus = Some(FocusChange::Resign); | ||
| } | ||
|
|
||
| // Delete changes of disabled state that happened during DisabledChanged to avoid | ||
| // recursions. | ||
| self.state.is_explicitly_disabled_new = self.state.is_explicitly_disabled; | ||
| } | ||
| // Update focus-chain of our parent | ||
| LifeCycle::BuildFocusChain => { | ||
| self.state.update_focus_chain = false; | ||
| if !self.state.is_disabled() { | ||
| ctx.widget_state.focus_chain.extend(&self.state.focus_chain); | ||
| } | ||
| } | ||
| _ => (), | ||
| } | ||
|
|
||
| ctx.widget_state.merge_up(&mut self.state); | ||
| } | ||
|
|
||
| /// Propagate a data update. | ||
|
|
@@ -1114,6 +1197,9 @@ impl WidgetState { | |
| paint_insets: Insets::ZERO, | ||
| invalid: Region::EMPTY, | ||
| viewport_offset: Vec2::ZERO, | ||
| children_disabled_changed: false, | ||
| ancestor_disabled: false, | ||
| is_explicitly_disabled: false, | ||
| baseline_offset: 0.0, | ||
| is_hot: false, | ||
| needs_layout: false, | ||
|
|
@@ -1130,10 +1216,21 @@ impl WidgetState { | |
| cursor_change: CursorChange::Default, | ||
| cursor: None, | ||
| sub_window_hosts: Vec::new(), | ||
| is_explicitly_disabled_new: false, | ||
| text_registrations: Vec::new(), | ||
| update_focus_chain: true, | ||
|
||
| } | ||
| } | ||
|
|
||
| pub(crate) fn is_disabled(&self) -> bool { | ||
| self.is_explicitly_disabled || self.ancestor_disabled | ||
| } | ||
|
|
||
| pub(crate) fn tree_disabled_changed(&self) -> bool { | ||
| self.children_disabled_changed | ||
| || self.is_explicitly_disabled != self.is_explicitly_disabled_new | ||
| } | ||
|
|
||
| pub(crate) fn add_timer(&mut self, timer_token: TimerToken) { | ||
| self.timers.insert(timer_token, self.id); | ||
| } | ||
|
|
@@ -1168,6 +1265,9 @@ impl WidgetState { | |
|
|
||
| self.needs_layout |= child_state.needs_layout; | ||
| self.request_anim |= child_state.request_anim; | ||
| self.children_disabled_changed |= child_state.children_disabled_changed; | ||
| self.children_disabled_changed |= | ||
| child_state.is_explicitly_disabled_new != child_state.is_explicitly_disabled; | ||
| self.has_active |= child_state.has_active; | ||
| self.has_focus |= child_state.has_focus; | ||
| self.children_changed |= child_state.children_changed; | ||
|
|
@@ -1176,6 +1276,7 @@ impl WidgetState { | |
| self.timers.extend_drain(&mut child_state.timers); | ||
| self.text_registrations | ||
| .extend(child_state.text_registrations.drain(..)); | ||
| self.update_focus_chain |= child_state.update_focus_chain; | ||
|
|
||
| // We reset `child_state.cursor` no matter what, so that on the every pass through the tree, | ||
| // things will be recalculated just from `cursor_change`. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be happy to have less update logic here and just always rebuild the focus chain after this event; it's harder for us to screw that up later when we make some other little change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok