Fixed reference cyle in ManagedWidget

This commit is contained in:
Jonathan Johnson 2023-12-22 09:51:06 -08:00
parent 564b9e5a96
commit 6b33b0f686
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
6 changed files with 113 additions and 118 deletions

View file

@ -29,6 +29,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[92]: https://github.com/khonsulabs/gooey/issues/92 [92]: https://github.com/khonsulabs/gooey/issues/92
### Fixed
- A memory leak has been fixed that prevented the underlying widget tree of each
window from being dropped. This was caused by a reference counting cycle, and
has been fixed by switching `MountedWidget` to use a weak reference internally
and having the window hold the strong reference to the tree.
### Added ### Added
- `Validations::validate_result` attaches a `Dynamic<Result<T,E>>` to the - `Validations::validate_result` attaches a `Dynamic<Result<T,E>>` to the

4
Cargo.lock generated
View file

@ -106,7 +106,7 @@ dependencies = [
[[package]] [[package]]
name = "appit" name = "appit"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/khonsulabs/appit#8ca300682c83d1bd6c698bc13ba7011b4be0ca8c" source = "git+https://github.com/khonsulabs/appit#1ff4b7b2a6f93e2c719787c76e585e6c2ecf290c"
dependencies = [ dependencies = [
"winit", "winit",
] ]
@ -1179,7 +1179,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "kludgine" name = "kludgine"
version = "0.6.1" version = "0.6.1"
source = "git+https://github.com/khonsulabs/kludgine#8327335a103ad4ba09093aa201c007d43b4a48d9" source = "git+https://github.com/khonsulabs/kludgine#e76fcc5c7a2ac96edcd51ee635d228cc9295d7ce"
dependencies = [ dependencies = [
"ahash", "ahash",
"alot", "alot",

View file

@ -22,6 +22,7 @@ use crate::styles::components::{
Opacity, TextSize, WidgetBackground, Opacity, TextSize, WidgetBackground,
}; };
use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair}; use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::tree::Tree;
use crate::utils::IgnorePoison; use crate::utils::IgnorePoison;
use crate::value::{IntoValue, Value}; use crate::value::{IntoValue, Value};
use crate::widget::{ use crate::widget::{
@ -162,7 +163,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
} }
pub(crate) fn hover(&mut self, location: Point<Px>) { pub(crate) fn hover(&mut self, location: Point<Px>) {
let changes = self.current_node.tree.hover(Some(&self.current_node)); let changes = self.tree.hover(Some(&self.current_node));
for unhovered in changes.unhovered { for unhovered in changes.unhovered {
let mut context = self.for_other(&unhovered); let mut context = self.for_other(&unhovered);
unhovered.lock().as_widget().unhover(&mut context); unhovered.lock().as_widget().unhover(&mut context);
@ -187,7 +188,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
} }
pub(crate) fn clear_hover(&mut self) { pub(crate) fn clear_hover(&mut self) {
let changes = self.current_node.tree.hover(None); let changes = self.tree.hover(None);
assert!(changes.hovered.is_empty()); assert!(changes.hovered.is_empty());
for old_hover in changes.unhovered { for old_hover in changes.unhovered {
@ -201,16 +202,13 @@ impl<'context, 'window> EventContext<'context, 'window> {
fn apply_pending_activation(&mut self) { fn apply_pending_activation(&mut self) {
let mut activation_changes = 0; let mut activation_changes = 0;
while activation_changes < Self::MAX_PENDING_CHANGE_CYCLES { while activation_changes < Self::MAX_PENDING_CHANGE_CYCLES {
let active = self let active = self.pending_state.active.and_then(|w| self.tree.widget(w));
.pending_state if self.tree.active_widget() == active.as_ref().map(|w| w.node_id) {
.active
.and_then(|w| self.current_node.tree.widget(w));
if self.current_node.tree.active_widget() == active.as_ref().map(|w| w.node_id) {
break; break;
} }
activation_changes += 1; activation_changes += 1;
let new = match self.current_node.tree.activate(active.as_ref()) { let new = match self.tree.activate(active.as_ref()) {
Ok(old) => { Ok(old) => {
if let Some(old) = old { if let Some(old) = old {
let mut old_context = self.for_other(&old); let mut old_context = self.for_other(&old);
@ -221,10 +219,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
Err(()) => false, Err(()) => false,
}; };
if new { if new {
let active = self let active = self.pending_state.active.and_then(|w| self.tree.widget(w));
.pending_state
.active
.and_then(|w| self.current_node.tree.widget(w));
if let Some(active) = &active { if let Some(active) = &active {
active active
.lock() .lock()
@ -246,11 +241,8 @@ impl<'context, 'window> EventContext<'context, 'window> {
fn apply_pending_focus(&mut self) { fn apply_pending_focus(&mut self) {
let mut focus_changes = 0; let mut focus_changes = 0;
while focus_changes < Self::MAX_PENDING_CHANGE_CYCLES { while focus_changes < Self::MAX_PENDING_CHANGE_CYCLES {
let focus = self let focus = self.pending_state.focus.and_then(|w| self.tree.widget(w));
.pending_state if self.tree.focused_widget() == focus.as_ref().map(|w| w.node_id) {
.focus
.and_then(|w| self.current_node.tree.widget(w));
if self.current_node.tree.focused_widget() == focus.as_ref().map(|w| w.node_id) {
break; break;
} }
focus_changes += 1; focus_changes += 1;
@ -270,7 +262,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
break self.next_focus_after(focus, self.pending_state.focus_is_advancing); break self.next_focus_after(focus, self.pending_state.focus_is_advancing);
} }
}); });
let new = match self.current_node.tree.focus(self.pending_state.focus) { let new = match self.tree.focus(self.pending_state.focus) {
Ok(old) => { Ok(old) => {
if let Some(old_widget) = old { if let Some(old_widget) = old {
let mut old_context = self.for_other(&old_widget); let mut old_context = self.for_other(&old_widget);
@ -280,7 +272,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
} else { } else {
// This widget is rejecting the focus change. // This widget is rejecting the focus change.
drop(old_context); drop(old_context);
let _result = self.current_node.tree.focus(Some(old_widget.id())); let _result = self.tree.focus(Some(old_widget.id()));
self.pending_state.focus = Some(old_widget.id()); self.pending_state.focus = Some(old_widget.id());
break; break;
} }
@ -290,11 +282,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
Err(()) => false, Err(()) => false,
}; };
if new { if new {
if let Some(focus) = self if let Some(focus) = self.pending_state.focus.and_then(|w| self.tree.widget(w)) {
.pending_state
.focus
.and_then(|w| self.current_node.tree.widget(w))
{
focus.lock().as_widget().focus(&mut self.for_other(&focus)); focus.lock().as_widget().focus(&mut self.for_other(&focus));
} }
} else { } else {
@ -319,8 +307,8 @@ impl<'context, 'window> EventContext<'context, 'window> {
self.apply_pending_focus(); self.apply_pending_focus();
// Check that our hover widget still exists. If not, we should try to find a new one. // Check that our hover widget still exists. If not, we should try to find a new one.
if let Some(hover) = self.current_node.tree.hovered_widget() { if let Some(hover) = self.tree.hovered_widget() {
if self.current_node.tree.widget_from_node(hover).is_none() { if self.tree.widget_from_node(hover).is_none() {
self.update_hovered_widget(); self.update_hovered_widget();
} }
} }
@ -329,7 +317,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
pub(crate) fn update_hovered_widget(&mut self) { pub(crate) fn update_hovered_widget(&mut self) {
self.cursor.widget = None; self.cursor.widget = None;
if let Some(location) = self.cursor.location { if let Some(location) = self.cursor.location {
for widget in self.current_node.tree.widgets_under_point(location) { for widget in self.tree.widgets_under_point(location) {
let mut widget_context = self.for_other(&widget); let mut widget_context = self.for_other(&widget);
let Some(widget_layout) = widget_context.last_layout() else { let Some(widget_layout) = widget_context.last_layout() else {
continue; continue;
@ -666,9 +654,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
"redraw called without set_widget_layout" "redraw called without set_widget_layout"
); );
self.current_node self.tree.note_widget_rendered(self.current_node.node_id);
.tree
.note_widget_rendered(self.current_node.node_id);
let widget = self.current_node.clone(); let widget = self.current_node.clone();
let mut widget = widget.lock(); let mut widget = widget.lock();
if !widget.as_widget().full_control_redraw() { if !widget.as_widget().full_control_redraw() {
@ -826,10 +812,7 @@ pub trait AsEventContext<'window> {
#[must_use] #[must_use]
fn push_child(&mut self, child: WidgetInstance) -> MountedWidget { fn push_child(&mut self, child: WidgetInstance) -> MountedWidget {
let mut context = self.as_event_context(); let mut context = self.as_event_context();
let pushed_widget = context let pushed_widget = context.tree.push_boxed(child, Some(&context.current_node));
.current_node
.tree
.push_boxed(child, Some(&context.current_node));
pushed_widget pushed_widget
.lock() .lock()
.as_widget() .as_widget()
@ -844,10 +827,7 @@ pub trait AsEventContext<'window> {
.lock() .lock()
.as_widget() .as_widget()
.unmounted(&mut context.for_other(child)); .unmounted(&mut context.for_other(child));
context context.tree.remove_child(child, &context.current_node);
.current_node
.tree
.remove_child(child, &context.current_node);
} }
} }
@ -869,6 +849,7 @@ impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '
/// specific widget. /// specific widget.
pub struct WidgetContext<'context, 'window> { pub struct WidgetContext<'context, 'window> {
current_node: MountedWidget, current_node: MountedWidget,
pub(crate) tree: Tree,
redraw_status: &'context InvalidationStatus, redraw_status: &'context InvalidationStatus,
window: &'context mut RunningWindow<'window>, window: &'context mut RunningWindow<'window>,
theme: Cow<'context, ThemePair>, theme: Cow<'context, ThemePair>,
@ -891,18 +872,18 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
kludgine: window.handle(), kludgine: window.handle(),
redraw_status: redraw_status.clone(), redraw_status: redraw_status.clone(),
}); });
let tree = current_node.tree();
Self { Self {
pending_state: PendingState::Owned(PendingWidgetState { pending_state: PendingState::Owned(PendingWidgetState {
focus: current_node focus: tree
.tree
.focused_widget() .focused_widget()
.and_then(|id| current_node.tree.widget_from_node(id).map(|w| w.id())), .and_then(|id| tree.widget_from_node(id).map(|w| w.id())),
active: current_node active: tree
.tree
.active_widget() .active_widget()
.and_then(|id| current_node.tree.widget_from_node(id).map(|w| w.id())), .and_then(|id| tree.widget_from_node(id).map(|w| w.id())),
focus_is_advancing: false, focus_is_advancing: false,
}), }),
tree,
effective_styles: current_node.effective_styles(), effective_styles: current_node.effective_styles(),
cache: WidgetCacheKey { cache: WidgetCacheKey {
theme_mode, theme_mode,
@ -919,6 +900,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns a new instance that borrows from `self`. /// Returns a new instance that borrows from `self`.
pub fn borrowed(&mut self) -> WidgetContext<'_, 'window> { pub fn borrowed(&mut self) -> WidgetContext<'_, 'window> {
WidgetContext { WidgetContext {
tree: self.tree.clone(),
current_node: self.current_node.clone(), current_node: self.current_node.clone(),
redraw_status: self.redraw_status, redraw_status: self.redraw_status,
window: &mut *self.window, window: &mut *self.window,
@ -958,6 +940,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
enabled: current_node.enabled(&self.handle()), enabled: current_node.enabled(&self.handle()),
}, },
current_node, current_node,
tree: self.tree.clone(),
redraw_status: self.redraw_status, redraw_status: self.redraw_status,
window: &mut *self.window, window: &mut *self.window,
theme, theme,
@ -970,8 +953,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns true if `possible_parent` is in this widget's parent list. /// Returns true if `possible_parent` is in this widget's parent list.
#[must_use] #[must_use]
pub fn is_child_of(&self, possible_parent: &WidgetInstance) -> bool { pub fn is_child_of(&self, possible_parent: &WidgetInstance) -> bool {
self.current_node self.tree
.tree
.is_child(self.current_node.node_id, possible_parent) .is_child(self.current_node.node_id, possible_parent)
} }
@ -1107,7 +1089,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// for more information. /// for more information.
#[must_use] #[must_use]
pub fn is_default(&self) -> bool { pub fn is_default(&self) -> bool {
self.current_node.tree.default_widget() == Some(self.current_node.node_id) self.tree.default_widget() == Some(self.current_node.node_id)
} }
/// Returns true if this widget is the target to activate when the user /// Returns true if this widget is the target to activate when the user
@ -1118,7 +1100,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// for more information. /// for more information.
#[must_use] #[must_use]
pub fn is_escape(&self) -> bool { pub fn is_escape(&self) -> bool {
self.current_node.tree.escape_widget() == Some(self.current_node.node_id) self.tree.escape_widget() == Some(self.current_node.node_id)
} }
/// Returns the widget this context is for. /// Returns the widget this context is for.
@ -1329,7 +1311,7 @@ impl ManageWidget for WidgetId {
type Managed = Option<MountedWidget>; type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
context.current_node.tree.widget(*self) context.tree.widget(*self)
} }
} }
@ -1337,7 +1319,7 @@ impl ManageWidget for WidgetInstance {
type Managed = Option<MountedWidget>; type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
context.current_node.tree.widget(self.id()) context.tree.widget(self.id())
} }
} }
@ -1346,7 +1328,7 @@ impl ManageWidget for WidgetRef {
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
match self { match self {
WidgetRef::Unmounted(instance) => context.current_node.tree.widget(instance.id()), WidgetRef::Unmounted(instance) => context.tree.widget(instance.id()),
WidgetRef::Mounted(instance) => Some(instance.clone()), WidgetRef::Mounted(instance) => Some(instance.clone()),
} }
} }

View file

@ -1,5 +1,5 @@
use std::mem; use std::mem;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex, Weak};
use ahash::AHashMap; use ahash::AHashMap;
use alot::{LotId, Lots}; use alot::{LotId, Lots};
@ -63,7 +63,7 @@ impl Tree {
MountedWidget { MountedWidget {
node_id, node_id,
widget, widget,
tree: self.clone(), tree: WeakTree(Arc::downgrade(&self.data)),
} }
} }
@ -421,7 +421,7 @@ impl TreeData {
Some(MountedWidget { Some(MountedWidget {
node_id, node_id,
widget: self.nodes[node_id].widget.clone(), widget: self.nodes[node_id].widget.clone(),
tree: tree.clone(), tree: WeakTree(Arc::downgrade(&tree.data)),
}) })
} }
@ -429,7 +429,7 @@ impl TreeData {
Some(MountedWidget { Some(MountedWidget {
node_id, node_id,
widget: self.nodes.get(node_id)?.widget.clone(), widget: self.nodes.get(node_id)?.widget.clone(),
tree: tree.clone(), tree: WeakTree(Arc::downgrade(&tree.data)),
}) })
} }
@ -621,3 +621,12 @@ struct CachedLayoutQuery {
constraints: Size<ConstraintLimit>, constraints: Size<ConstraintLimit>,
size: Size<UPx>, size: Size<UPx>,
} }
#[derive(Clone, Debug)]
pub struct WeakTree(Weak<Mutex<TreeData>>);
impl WeakTree {
pub fn upgrade(&self) -> Option<Tree> {
self.0.upgrade().map(|data| Tree { data })
}
}

View file

@ -34,7 +34,7 @@ use crate::styles::{
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue, ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue,
IntoDynamicComponentValue, Styles, ThemePair, VisualOrder, IntoDynamicComponentValue, Styles, ThemePair, VisualOrder,
}; };
use crate::tree::Tree; use crate::tree::{Tree, WeakTree};
use crate::utils::IgnorePoison; use crate::utils::IgnorePoison;
use crate::value::{Dynamic, Generation, IntoDynamic, IntoValue, Validation, Value}; use crate::value::{Dynamic, Generation, IntoDynamic, IntoValue, Validation, Value};
use crate::widgets::checkbox::{Checkable, CheckboxState}; use crate::widgets::checkbox::{Checkable, CheckboxState};
@ -1693,7 +1693,7 @@ where
pub struct MountedWidget { pub struct MountedWidget {
pub(crate) node_id: LotId, pub(crate) node_id: LotId,
pub(crate) widget: WidgetInstance, pub(crate) widget: WidgetInstance,
pub(crate) tree: Tree, pub(crate) tree: WeakTree,
} }
impl Debug for MountedWidget { impl Debug for MountedWidget {
@ -1703,6 +1703,10 @@ impl Debug for MountedWidget {
} }
impl MountedWidget { impl MountedWidget {
pub(crate) fn tree(&self) -> Tree {
self.tree.upgrade().expect("tree missing")
}
/// Locks the widget for exclusive access. Locking widgets should only be /// Locks the widget for exclusive access. Locking widgets should only be
/// done for brief moments of time when you are certain no deadlocks can /// done for brief moments of time when you are certain no deadlocks can
/// occur due to other widget locks being held. /// occur due to other widget locks being held.
@ -1713,11 +1717,14 @@ impl MountedWidget {
/// Invalidates this widget. /// Invalidates this widget.
pub fn invalidate(&self) { pub fn invalidate(&self) {
self.tree.invalidate(self.node_id, false); let Some(tree) = self.tree.upgrade() else {
return;
};
tree.invalidate(self.node_id, false);
} }
pub(crate) fn set_layout(&self, rect: Rect<Px>) { pub(crate) fn set_layout(&self, rect: Rect<Px>) {
self.tree.set_layout(self.node_id, rect); self.tree().set_layout(self.node_id, rect);
} }
/// Returns the unique id of this widget instance. /// Returns the unique id of this widget instance.
@ -1740,7 +1747,7 @@ impl MountedWidget {
pub fn next_focus(&self) -> Option<MountedWidget> { pub fn next_focus(&self) -> Option<MountedWidget> {
self.widget self.widget
.next_focus() .next_focus()
.and_then(|next_focus| self.tree.widget(next_focus)) .and_then(|next_focus| self.tree.upgrade()?.widget(next_focus))
} }
/// Returns the widget to focus before this widget. /// Returns the widget to focus before this widget.
@ -1749,7 +1756,7 @@ impl MountedWidget {
/// automatically using [`MakeWidget::with_next_focus()`]. /// automatically using [`MakeWidget::with_next_focus()`].
#[must_use] #[must_use]
pub fn previous_focus(&self) -> Option<MountedWidget> { pub fn previous_focus(&self) -> Option<MountedWidget> {
self.tree.previous_focus(self.id()) self.tree.upgrade()?.previous_focus(self.id())
} }
/// Returns the next or previous focus target, if one was set using /// Returns the next or previous focus target, if one was set using
@ -1766,85 +1773,89 @@ impl MountedWidget {
/// Returns the region that the widget was last rendered at. /// Returns the region that the widget was last rendered at.
#[must_use] #[must_use]
pub fn last_layout(&self) -> Option<Rect<Px>> { pub fn last_layout(&self) -> Option<Rect<Px>> {
self.tree.layout(self.node_id) self.tree.upgrade()?.layout(self.node_id)
} }
/// Returns the effective styles for the current tree. /// Returns the effective styles for the current tree.
#[must_use] #[must_use]
pub fn effective_styles(&self) -> Styles { pub fn effective_styles(&self) -> Styles {
self.tree.effective_styles(self.node_id) self.tree().effective_styles(self.node_id)
} }
/// Returns true if this widget is the currently active widget. /// Returns true if this widget is the currently active widget.
#[must_use] #[must_use]
pub fn active(&self) -> bool { pub fn active(&self) -> bool {
self.tree.active_widget() == Some(self.node_id) self.tree().active_widget() == Some(self.node_id)
} }
pub(crate) fn enabled(&self, handle: &WindowHandle) -> bool { pub(crate) fn enabled(&self, handle: &WindowHandle) -> bool {
self.tree.is_enabled(self.node_id, handle) self.tree().is_enabled(self.node_id, handle)
} }
/// Returns true if this widget is currently the hovered widget. /// Returns true if this widget is currently the hovered widget.
#[must_use] #[must_use]
pub fn hovered(&self) -> bool { pub fn hovered(&self) -> bool {
self.tree.is_hovered(self.node_id) self.tree().is_hovered(self.node_id)
} }
/// Returns true if this widget that is directly beneath the cursor. /// Returns true if this widget that is directly beneath the cursor.
#[must_use] #[must_use]
pub fn primary_hover(&self) -> bool { pub fn primary_hover(&self) -> bool {
self.tree.hovered_widget() == Some(self.node_id) self.tree().hovered_widget() == Some(self.node_id)
} }
/// Returns true if this widget is the currently focused widget. /// Returns true if this widget is the currently focused widget.
#[must_use] #[must_use]
pub fn focused(&self) -> bool { pub fn focused(&self) -> bool {
self.tree.focused_widget() == Some(self.node_id) self.tree().focused_widget() == Some(self.node_id)
} }
/// Returns the parent of this widget. /// Returns the parent of this widget.
#[must_use] #[must_use]
pub fn parent(&self) -> Option<MountedWidget> { pub fn parent(&self) -> Option<MountedWidget> {
self.tree let tree = self.tree.upgrade()?;
.parent(self.node_id)
.and_then(|id| self.tree.widget_from_node(id)) tree.parent(self.node_id)
.and_then(|id| tree.widget_from_node(id))
} }
/// Returns true if this node has a parent. /// Returns true if this node has a parent.
#[must_use] #[must_use]
pub fn has_parent(&self) -> bool { pub fn has_parent(&self) -> bool {
self.tree.parent(self.node_id).is_some() let Some(tree) = self.tree.upgrade() else {
return false;
};
tree.parent(self.node_id).is_some()
} }
pub(crate) fn attach_styles(&self, styles: Value<Styles>) { pub(crate) fn attach_styles(&self, styles: Value<Styles>) {
self.tree.attach_styles(self.node_id, styles); self.tree().attach_styles(self.node_id, styles);
} }
pub(crate) fn attach_theme(&self, theme: Value<ThemePair>) { pub(crate) fn attach_theme(&self, theme: Value<ThemePair>) {
self.tree.attach_theme(self.node_id, theme); self.tree().attach_theme(self.node_id, theme);
} }
pub(crate) fn attach_theme_mode(&self, theme: Value<ThemeMode>) { pub(crate) fn attach_theme_mode(&self, theme: Value<ThemeMode>) {
self.tree.attach_theme_mode(self.node_id, theme); self.tree().attach_theme_mode(self.node_id, theme);
} }
pub(crate) fn overidden_theme( pub(crate) fn overidden_theme(
&self, &self,
) -> (Styles, Option<Value<ThemePair>>, Option<Value<ThemeMode>>) { ) -> (Styles, Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
self.tree.overriden_theme(self.node_id) self.tree().overriden_theme(self.node_id)
} }
pub(crate) fn begin_layout(&self, constraints: Size<ConstraintLimit>) -> Option<Size<UPx>> { pub(crate) fn begin_layout(&self, constraints: Size<ConstraintLimit>) -> Option<Size<UPx>> {
self.tree.begin_layout(self.node_id, constraints) self.tree().begin_layout(self.node_id, constraints)
} }
pub(crate) fn persist_layout(&self, constraints: Size<ConstraintLimit>, size: Size<UPx>) { pub(crate) fn persist_layout(&self, constraints: Size<ConstraintLimit>, size: Size<UPx>) {
self.tree.persist_layout(self.node_id, constraints, size); self.tree().persist_layout(self.node_id, constraints, size);
} }
pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<MountedWidget> { pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<MountedWidget> {
self.tree.visually_ordered_children(self.node_id, order) self.tree().visually_ordered_children(self.node_id, order)
} }
} }
@ -2302,7 +2313,7 @@ impl WidgetId {
/// Finds this widget mounted in this window, if present. /// Finds this widget mounted in this window, if present.
#[must_use] #[must_use]
pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<MountedWidget> { pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<MountedWidget> {
context.widget().tree.widget(self) context.tree.widget(self)
} }
} }

View file

@ -400,6 +400,7 @@ pub trait WindowBehavior: Sized + 'static {
struct GooeyWindow<T> { struct GooeyWindow<T> {
behavior: T, behavior: T,
tree: Tree,
root: MountedWidget, root: MountedWidget,
contents: Drawing, contents: Drawing,
should_close: bool, should_close: bool,
@ -445,11 +446,11 @@ where
kludgine: &mut Kludgine, kludgine: &mut Kludgine,
) { ) {
if is_pressed { if is_pressed {
if let Some(default) = widget.and_then(|id| self.root.tree.widget_from_node(id)) { if let Some(default) = widget.and_then(|id| self.tree.widget_from_node(id)) {
if let Some(previously_active) = self if let Some(previously_active) = self
.keyboard_activated .keyboard_activated
.take() .take()
.and_then(|id| self.root.tree.widget(id)) .and_then(|id| self.tree.widget(id))
{ {
EventContext::new( EventContext::new(
WidgetContext::new( WidgetContext::new(
@ -481,7 +482,7 @@ where
} else if let Some(keyboard_activated) = self } else if let Some(keyboard_activated) = self
.keyboard_activated .keyboard_activated
.take() .take()
.and_then(|id| self.root.tree.widget(id)) .and_then(|id| self.tree.widget(id))
{ {
EventContext::new( EventContext::new(
WidgetContext::new( WidgetContext::new(
@ -509,7 +510,7 @@ where
let mut padding = Edges::<Px>::default(); let mut padding = Edges::<Px>::default();
loop { loop {
let Some(managed) = self.root.tree.widget(root_or_child.id()) else { let Some(managed) = self.tree.widget(root_or_child.id()) else {
break; break;
}; };
@ -681,7 +682,8 @@ where
&mut RunningWindow::new(window, &gooey, &focused, &occluded, &inner_size), &mut RunningWindow::new(window, &gooey, &focused, &occluded, &inner_size),
context.user, context.user,
); );
let root = Tree::default().push_boxed(behavior.make_root(), None); let tree = Tree::default();
let root = tree.push_boxed(behavior.make_root(), None);
let (current_theme, theme) = match theme { let (current_theme, theme) = match theme {
Value::Constant(theme) => (theme, None), Value::Constant(theme) => (theme, None),
@ -691,6 +693,7 @@ where
Self { Self {
behavior, behavior,
root, root,
tree,
contents: Drawing::default(), contents: Drawing::default(),
should_close: false, should_close: false,
cursor: CursorState { cursor: CursorState {
@ -731,8 +734,7 @@ where
self.redraw_status.refresh_received(); self.redraw_status.refresh_received();
graphics.reset_text_attributes(); graphics.reset_text_attributes();
self.root self.tree
.tree
.new_frame(self.redraw_status.invalidations().drain()); .new_frame(self.redraw_status.invalidations().drain());
let resizable = window.winit().is_resizable(); let resizable = window.winit().is_resizable();
@ -941,8 +943,8 @@ where
input: KeyEvent, input: KeyEvent,
is_synthetic: bool, is_synthetic: bool,
) { ) {
let target = self.root.tree.focused_widget().unwrap_or(self.root.node_id); let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
let Some(target) = self.root.tree.widget_from_node(target) else { let Some(target) = self.tree.widget_from_node(target) else {
return; return;
}; };
let mut window = RunningWindow::new( let mut window = RunningWindow::new(
@ -987,12 +989,8 @@ where
if input.state.is_pressed() { if input.state.is_pressed() {
let reverse = window.modifiers().state().shift_key(); let reverse = window.modifiers().state().shift_key();
let target = self.root.tree.focused_widget().unwrap_or(self.root.node_id); let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
let target = self let target = self.tree.widget_from_node(target).expect("missing widget");
.root
.tree
.widget_from_node(target)
.expect("missing widget");
let mut target = EventContext::new( let mut target = EventContext::new(
WidgetContext::new( WidgetContext::new(
target, target,
@ -1015,7 +1013,7 @@ where
Key::Named(NamedKey::Enter) => { Key::Named(NamedKey::Enter) => {
self.keyboard_activate_widget( self.keyboard_activate_widget(
input.state.is_pressed(), input.state.is_pressed(),
self.root.tree.default_widget(), self.tree.default_widget(),
&mut window, &mut window,
kludgine, kludgine,
); );
@ -1023,7 +1021,7 @@ where
Key::Named(NamedKey::Escape) => { Key::Named(NamedKey::Escape) => {
self.keyboard_activate_widget( self.keyboard_activate_widget(
input.state.is_pressed(), input.state.is_pressed(),
self.root.tree.escape_widget(), self.tree.escape_widget(),
&mut window, &mut window,
kludgine, kludgine,
); );
@ -1050,16 +1048,10 @@ where
phase: TouchPhase, phase: TouchPhase,
) { ) {
let widget = self let widget = self
.root
.tree .tree
.hovered_widget() .hovered_widget()
.and_then(|hovered| self.root.tree.widget_from_node(hovered)) .and_then(|hovered| self.tree.widget_from_node(hovered))
.unwrap_or_else(|| { .unwrap_or_else(|| self.tree.widget(self.root.id()).expect("missing widget"));
self.root
.tree
.widget(self.root.id())
.expect("missing widget")
});
let mut window = RunningWindow::new( let mut window = RunningWindow::new(
window, window,
@ -1093,16 +1085,10 @@ where
ime: Ime, ime: Ime,
) { ) {
let widget = self let widget = self
.root
.tree .tree
.focused_widget() .focused_widget()
.and_then(|hovered| self.root.tree.widget_from_node(hovered)) .and_then(|hovered| self.tree.widget_from_node(hovered))
.unwrap_or_else(|| { .unwrap_or_else(|| self.tree.widget(self.root.id()).expect("missing widget"));
self.root
.tree
.widget(self.root.id())
.expect("missing widget")
});
let mut window = RunningWindow::new( let mut window = RunningWindow::new(
window, window,
&self.gooey, &self.gooey,
@ -1160,7 +1146,7 @@ where
if let Some(state) = self.mouse_buttons.get(&device_id) { if let Some(state) = self.mouse_buttons.get(&device_id) {
// Mouse Drag // Mouse Drag
for (button, handler) in state { for (button, handler) in state {
let Some(handler) = self.root.tree.widget(*handler) else { let Some(handler) = self.tree.widget(*handler) else {
continue; continue;
}; };
let mut context = EventContext::new( let mut context = EventContext::new(
@ -1244,7 +1230,7 @@ where
if let (ElementState::Pressed, Some(location), Some(hovered)) = ( if let (ElementState::Pressed, Some(location), Some(hovered)) = (
state, state,
self.cursor.location, self.cursor.location,
self.cursor.widget.and_then(|id| self.root.tree.widget(id)), self.cursor.widget.and_then(|id| self.tree.widget(id)),
) { ) {
if let Some(handler) = recursively_handle_event( if let Some(handler) = recursively_handle_event(
&mut EventContext::new( &mut EventContext::new(
@ -1283,7 +1269,7 @@ where
if device_buttons.is_empty() { if device_buttons.is_empty() {
self.mouse_buttons.remove(&device_id); self.mouse_buttons.remove(&device_id);
} }
let Some(handler) = self.root.tree.widget(handler) else { let Some(handler) = self.tree.widget(handler) else {
return; return;
}; };
let cursor_location = self.cursor.location; let cursor_location = self.cursor.location;