1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
18 #include "uielement.h"
26 class DirtyNode
: public List::Node
{
28 DirtyNode (UIElement
*element
)
30 this->element
= element
;
35 class DirtyList
: public List::Node
41 dirty_list
= new List();
49 List
* GetDirtyNodes ()
63 DirtyLists::DirtyLists (bool ascending
)
65 this->ascending
= ascending
;
66 this->lists
= new List();
69 DirtyLists::~DirtyLists ()
75 DirtyLists::GetList (int level
, bool create
)
79 for (dl
= (DirtyList
*)lists
->First(); dl
; dl
= (DirtyList
*)dl
->next
) {
80 if (dl
->GetLevel() == level
)
82 else if (dl
->GetLevel() > level
)
87 DirtyList
*new_dl
= new DirtyList (level
);
88 lists
->InsertBefore (new_dl
, dl
);
96 DirtyLists::RemoveList (int level
)
98 DirtyList
*dl
= (DirtyList
*)GetList (level
, false);
105 DirtyLists::AddDirtyNode (int level
, List::Node
*node
)
107 DirtyList
*dl
= (DirtyList
*)GetList (level
, true);
108 dl
->GetDirtyNodes()->Append(node
);
112 DirtyLists::RemoveDirtyNode (int level
, List::Node
*node
)
114 DirtyList
*dl
= (DirtyList
*)GetList (level
, false);
117 dl
->GetDirtyNodes()->Remove(node
);
118 if (dl
->GetDirtyNodes()->IsEmpty())
123 DirtyLists::GetFirst ()
128 dl
= (DirtyList
*)lists
->First();
131 dl
= (DirtyList
*)lists
->Last ();
137 return dl
->GetDirtyNodes()->First();
141 DirtyLists::IsEmpty ()
143 return lists
->IsEmpty();
147 DirtyLists::Clear (bool freeNodes
)
149 lists
->Clear (freeNodes
);
153 Surface::AddDirtyElement (UIElement
*element
, DirtyType dirt
)
155 // there's no point in adding an element to the dirty list if it
156 // isn't in the hierarchy. it will be added to the dirty list when
157 // it's added to the hierarchy anyway.
158 if (element
->GetVisualParent() == NULL
&& !IsTopLevel(element
))
161 // XXX this should really be here...
162 // if (element->dirty_flags & dirt)
165 element
->dirty_flags
|= dirt
;
167 //printf ("adding element %p (%s) to the dirty list\n", element, element->GetTypeName());
169 if (dirt
& DownDirtyState
) {
170 if (element
->down_dirty_node
)
172 element
->down_dirty_node
= new DirtyNode (element
);
174 down_dirty
->AddDirtyNode (element
->GetVisualLevel (), element
->down_dirty_node
);
177 if (dirt
& UpDirtyState
) {
178 if (element
->up_dirty_node
)
180 element
->up_dirty_node
= new DirtyNode (element
);
182 up_dirty
->AddDirtyNode (element
->GetVisualLevel (), element
->up_dirty_node
);
187 Surface::RemoveDirtyElement (UIElement
*element
)
189 if (element
->up_dirty_node
)
190 up_dirty
->RemoveDirtyNode (element
->GetVisualLevel(), element
->up_dirty_node
);
191 if (element
->down_dirty_node
)
192 down_dirty
->RemoveDirtyNode (element
->GetVisualLevel(), element
->down_dirty_node
);
194 element
->down_dirty_node
= NULL
;
195 element
->up_dirty_node
= NULL
;
200 ** There are 2 types of changes that need to propagate around the
203 ** 1. Those changes that needs to be propagated from parent to children
204 ** (transformation, opacity). We call these Downward Changes, and
205 ** the elements are placed in the down_dirty list.
207 ** 2. Those changes that need to be propagated from children to parent
208 ** (bounds, invalidation). We call these Upward Changes, and the
209 ** elements are placed in the up_dirty list.
212 ** Downward Changes can result in new Upward changes (when an
213 ** element's transform changes, usually its bounds change), so when
214 ** processing the dirty list we push changes down the tree before
215 ** handling the Upward Changes.
220 Surface::PropagateDirtyFlagToChildren (UIElement
*el
, DirtyType flags
)
222 VisualTreeWalker walker
= VisualTreeWalker (el
);
223 while (UIElement
*child
= walker
.Step ())
224 AddDirtyElement (child
, flags
);
228 Surface::ProcessDownDirtyElements ()
230 /* push down the transforms opacity, and visibility changes first */
231 while (DirtyNode
*node
= (DirtyNode
*)down_dirty
->GetFirst()) {
232 UIElement
* el
= (UIElement
*)node
->element
;
234 if (el
->dirty_flags
& DirtyRenderVisibility
) {
235 el
->dirty_flags
&= ~DirtyRenderVisibility
;
239 // Since we are not included in our parents subtree when we
240 // are collapsed we need to notify our parent that things may
242 if (el
->GetVisualParent ())
243 el
->GetVisualParent ()->UpdateBounds ();
245 el
->ComputeTotalRenderVisibility ();
247 if (!el
->GetRenderVisible ())
248 el
->CacheInvalidateHint ();
250 AddDirtyElement (el
, DirtyNewBounds
);
252 PropagateDirtyFlagToChildren (el
, DirtyRenderVisibility
);
255 if (el
->dirty_flags
& DirtyHitTestVisibility
) {
256 el
->dirty_flags
&= ~DirtyHitTestVisibility
;
258 el
->ComputeTotalHitTestVisibility ();
260 PropagateDirtyFlagToChildren (el
, DirtyHitTestVisibility
);
263 ** since we cache N's local (from N's parent to N)
264 ** transform, we need to catch if we're changing
265 ** something about that local transform and recompute
268 ** DirtyLocalTransform implies DirtyTransform, since
269 ** changing N's local transform requires updating the
270 ** transform of all descendents in the subtree rooted
273 if (el
->dirty_flags
& DirtyLocalTransform
) {
274 el
->dirty_flags
&= ~DirtyLocalTransform
;
276 el
->dirty_flags
|= DirtyTransform
;
278 el
->ComputeLocalTransform ();
281 if (el
->dirty_flags
& DirtyTransform
) {
282 el
->dirty_flags
&= ~DirtyTransform
;
285 el
->ComputeTransform ();
287 if (el
->GetVisualParent ())
288 el
->GetVisualParent ()->UpdateBounds ();
290 AddDirtyElement (el
, DirtyNewBounds
);
291 PropagateDirtyFlagToChildren (el
, DirtyTransform
);
294 if (el
->dirty_flags
& DirtyLocalClip
) {
295 el
->dirty_flags
&= ~DirtyLocalClip
;
296 el
->dirty_flags
|= DirtyClip
;
298 // XXX el->ComputeLocalClip ();
301 if (el
->dirty_flags
& DirtyClip
) {
302 el
->dirty_flags
&= ~DirtyTransform
;
304 // XXX el->ComputeClip ();
305 // XXX el->UpdateBounds ();
307 PropagateDirtyFlagToChildren (el
, DirtyClip
);
310 if (el
->dirty_flags
& DirtyChildrenZIndices
) {
311 el
->dirty_flags
&= ~DirtyChildrenZIndices
;
312 if (!el
->Is(Type::PANEL
)) {
313 g_warning ("DirtyChildrenZIndices is only applicable to Panel subclasses");
316 ((Panel
*)el
)->GetChildren ()->ResortByZIndex();
321 if (!(el
->dirty_flags
& DownDirtyState
)) {
322 down_dirty
->RemoveDirtyNode (el
->GetVisualLevel (), el
->down_dirty_node
);
323 el
->down_dirty_node
= NULL
;
327 if (!down_dirty
->IsEmpty())
328 g_warning ("after down dirty pass, down dirty list is not empty");
332 ** Note that since this calls GDK invalidation functions
333 ** it's a good idea to call it with a GDK lock held (all gtk callbacks
334 ** are automatically protected except for timeouts and idle)
337 Surface::ProcessUpDirtyElements ()
339 while (DirtyNode
*node
= (DirtyNode
*)up_dirty
->GetFirst()) {
340 UIElement
* el
= (UIElement
*)node
->element
;
342 // printf ("up processing element element %p (%s)\n", el, el->GetName());
343 // printf ("el->parent = %p\n", el->parent);
345 if (el
->dirty_flags
& DirtyBounds
) {
346 // printf (" + bounds\n");
347 el
->dirty_flags
&= ~DirtyBounds
;
349 Rect obounds
= el
->GetBounds ();
350 Rect osubtreebounds
= el
->GetSubtreeBounds ();
351 bool parent_bounds_updated
= false;
353 el
->ComputeBounds ();
355 // printf (" + + obounds = %f %f %f %f, nbounds = %f %f %f %f\n",
356 // obounds.x, obounds.y, obounds.w, obounds.h,
357 // el->GetBounds().x, el->GetBounds().y, el->GetBounds().w, el->GetBounds().h);
359 if (osubtreebounds
!= el
->GetSubtreeBounds ()) {
360 if (el
->GetVisualParent ()) {
361 el
->GetVisualParent ()->UpdateBounds ();
362 parent_bounds_updated
= true;
366 if (obounds
!= el
->GetBounds()) {
367 if (el
->GetVisualParent ()) {
368 // printf (" + + + calling UpdateBounds and Invalidate on parent\n");
369 if (!parent_bounds_updated
)
370 el
->GetVisualParent ()->UpdateBounds();
372 Region oregion
= Region (obounds
);
373 el
->GetVisualParent ()->Invalidate (&oregion
);
378 if (el
->force_invalidate_of_new_bounds
) {
379 el
->force_invalidate_of_new_bounds
= false;
380 // Invalidate everything including the
381 // visible area of our children.
382 el
->InvalidateSubtreePaint ();
385 if (el
->dirty_flags
& DirtyNewBounds
) {
387 el
->dirty_flags
&= ~DirtyNewBounds
;
389 if (el
->dirty_flags
& DirtyInvalidate
) {
390 // printf (" + invalidating %p (%s) %s, %f %f %f %f\n",
391 // el, el->GetTypeName(), el->GetName(), el->dirty_rect.x, el->dirty_rect.y, el->dirty_rect.w, el->dirty_rect.h);
393 el
->dirty_flags
&= ~DirtyInvalidate
;
395 Region
*dirty
= el
->dirty_region
;
399 dirty
->GetRectangles (&rects
, &count
);
400 Surface
*surface
= el
->GetSurface ();
403 Rect r
= Rect ((double)rects
[count
].x
,
404 (double)rects
[count
].y
,
405 (double)rects
[count
].width
,
406 (double)rects
[count
].height
);
407 //printf (" + + invalidating parent (%f,%f,%f,%f)\n",
413 surface
->Invalidate (r
);
418 delete el
->dirty_region
;
419 el
->dirty_region
= new Region ();
422 if (!(el
->dirty_flags
& UpDirtyState
)) {
423 up_dirty
->RemoveDirtyNode (el
->GetVisualLevel (), el
->up_dirty_node
);
424 el
->up_dirty_node
= NULL
;
428 if (!up_dirty
->IsEmpty())
429 g_warning ("after up dirty pass, up dirty list is not empty");
433 Surface::UpdateLayout ()
435 if (!needs_measure
&& !needs_arrange
)
438 needs_measure
= needs_arrange
= false;
440 for (int i
= 0; i
< layers
->GetCount (); i
++) {
441 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
443 // This is a hack to make sure the elements understand the currnet
444 // size of the surface until it is moved to a proper location.
445 Size
*last
= LayoutInformation::GetPreviousConstraint (layer
);
446 Size available
= Size (active_window
->GetWidth (), active_window
->GetHeight ());
447 if (!last
|| (*last
!= available
)) {
448 layer
->InvalidateMeasure ();
449 Size
size(active_window
->GetWidth (), active_window
->GetHeight ());
450 LayoutInformation::SetPreviousConstraint (layer
, &size
);
453 layer
->UpdateLayout ();
458 Surface::ProcessDirtyElements ()
461 bool dirty
= down_dirty
->IsEmpty() || !up_dirty
->IsEmpty();
462 ProcessDownDirtyElements ();
463 ProcessUpDirtyElements ();