@@ -7,7 +7,9 @@ use anathema_widgets::components::events::Event;
77use anathema_widgets:: error:: Result ;
88use anathema_widgets:: layout:: { Constraints , LayoutCtx , LayoutFilter , PositionFilter , Viewport } ;
99use anathema_widgets:: paint:: PaintFilter ;
10- use anathema_widgets:: { GlyphMap , LayoutForEach , PaintChildren , PositionChildren , WidgetTreeView } ;
10+ use anathema_widgets:: {
11+ DirtyWidgets , GlyphMap , Layout , LayoutForEach , PaintChildren , PositionChildren , WidgetTreeView ,
12+ } ;
1113
1214pub mod testing;
1315pub mod tui;
@@ -55,14 +57,7 @@ impl<'rt, 'bp, T: Backend> WidgetCycle<'rt, 'bp, T> {
5557 }
5658 }
5759
58- fn fixed ( & mut self , ctx : & mut LayoutCtx < ' _ , ' bp > , needs_layout : bool ) -> Result < ( ) > {
59- // -----------------------------------------------------------------------------
60- // - Layout -
61- // -----------------------------------------------------------------------------
62- if needs_layout {
63- self . layout ( ctx, LayoutFilter ) ?;
64- }
65-
60+ fn fixed ( & mut self , ctx : & mut LayoutCtx < ' _ , ' bp > ) -> Result < ( ) > {
6661 // -----------------------------------------------------------------------------
6762 // - Position -
6863 // -----------------------------------------------------------------------------
@@ -90,24 +85,107 @@ impl<'rt, 'bp, T: Backend> WidgetCycle<'rt, 'bp, T> {
9085 Ok ( ( ) )
9186 }
9287
93- pub fn run ( & mut self , ctx : & mut LayoutCtx < ' _ , ' bp > , needs_layout : bool ) -> Result < ( ) > {
94- self . fixed ( ctx, needs_layout) ?;
88+ pub fn run (
89+ & mut self ,
90+ ctx : & mut LayoutCtx < ' _ , ' bp > ,
91+ force_layout : bool ,
92+ dirty_widgets : & mut DirtyWidgets ,
93+ ) -> Result < ( ) > {
94+ // -----------------------------------------------------------------------------
95+ // - Layout -
96+ // -----------------------------------------------------------------------------
97+ self . layout ( ctx, LayoutFilter , dirty_widgets, force_layout) ?;
98+
99+ // -----------------------------------------------------------------------------
100+ // - Position and paint -
101+ // -----------------------------------------------------------------------------
102+ self . fixed ( ctx) ?;
95103 self . floating ( ctx) ?;
96104 Ok ( ( ) )
97105 }
98106
99- fn layout ( & mut self , ctx : & mut LayoutCtx < ' _ , ' bp > , filter : LayoutFilter ) -> Result < ( ) > {
107+ fn layout (
108+ & mut self ,
109+ ctx : & mut LayoutCtx < ' _ , ' bp > ,
110+ filter : LayoutFilter ,
111+ dirty_widgets : & mut DirtyWidgets ,
112+ force_layout : bool ,
113+ ) -> Result < ( ) > {
100114 #[ cfg( feature = "profile" ) ]
101115 puffin:: profile_function!( ) ;
102- let tree = self . tree . view ( ) ;
103-
104- let scope = Scope :: root ( ) ;
105- let mut for_each = LayoutForEach :: new ( tree, & scope, filter, None ) ;
106- let constraints = self . constraints ;
107- _ = for_each. each ( ctx, |ctx, widget, children| {
108- _ = widget. layout ( children, constraints, ctx) ?;
109- Ok ( ControlFlow :: Break ( ( ) ) )
110- } ) ?;
116+
117+ let mut tree = self . tree . view ( ) ;
118+
119+ if force_layout {
120+ // Perform a layout across the entire tree
121+ let scope = Scope :: root ( ) ;
122+ let mut for_each = LayoutForEach :: new ( tree, & scope, filter) ;
123+ let constraints = self . constraints ;
124+ _ = for_each. each ( ctx, |ctx, widget, children| {
125+ _ = widget. layout ( children, constraints, ctx) ?;
126+ Ok ( ControlFlow :: Break ( ( ) ) )
127+ } ) ?;
128+ return Ok ( ( ) ) ;
129+ }
130+
131+ // If a widget has changed, mark the parent as dirty
132+
133+ // Layout only changed widgets.
134+ // These are the parents of changed widgets.
135+ //
136+ // Investigate the possibility of attaching an offset as existing widgets don't need
137+ // to reflow unless the constraint has changed.
138+ //
139+ // This means `additional_widgets` needs to store (key, offset) where offset can be None
140+ //
141+ // If this is going to work we need to consider `expand` and `spacer`
142+ //
143+ // Since widgets can be made by anyone and they are always guaranteed to give
144+ // access to all their children this might not be a possibility.
145+ //
146+ // parent
147+ // widget 0
148+ // widget 1
149+ // widget 2 | <- if this changes, only reflow this, three and four
150+ // widget 3 |-- reflow
151+ // widget 4 |
152+
153+ // TODO: make `additional_widgets` a scratch buffer part of `DirtyWidgets`.
154+ // Also ensure that it tracks last id as well
155+ // ... and removes it when done!
156+ let mut additional_widgets = vec ! [ ] ;
157+
158+ loop {
159+ for widget_id in dirty_widgets. drain ( ) {
160+ if !tree. contains ( widget_id) {
161+ continue ;
162+ }
163+ tree. with_value_mut ( widget_id, |_, widget, children| {
164+ let scope = Scope :: root ( ) ;
165+ let mut children = LayoutForEach :: new ( children, & scope, filter) ;
166+ children. parent_element = Some ( widget_id) ;
167+ let parent_id = widget. parent_widget ;
168+ let anathema_widgets:: WidgetKind :: Element ( widget) = & mut widget. kind else { return } ;
169+
170+ let constraints = widget. constraints ( ) ;
171+ if let Ok ( Layout :: Changed ( _) ) = widget. layout ( children, constraints, ctx) {
172+ // write into scratch buffer
173+ if let Some ( id) = parent_id {
174+ additional_widgets. push ( id) ;
175+ }
176+ }
177+ } ) ;
178+ }
179+
180+ // merge the scratch if it's not empty
181+
182+ if additional_widgets. is_empty ( ) {
183+ break ;
184+ }
185+
186+ dirty_widgets. inner . append ( & mut additional_widgets) ;
187+ }
188+
111189 Ok ( ( ) )
112190 }
113191
0 commit comments