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"
25 #include "deployment.h"
27 class DirtyNode
: public List::Node
{
29 DirtyNode (UIElement
*element
)
31 this->element
= element
;
36 class DirtyList
: public List::Node
42 dirty_list
= new List();
50 List
* GetDirtyNodes ()
64 DirtyLists::DirtyLists (bool ascending
)
66 this->ascending
= ascending
;
67 this->lists
= new List();
70 DirtyLists::~DirtyLists ()
76 DirtyLists::GetList (int level
, bool create
)
80 for (dl
= (DirtyList
*)lists
->First(); dl
; dl
= (DirtyList
*)dl
->next
) {
81 if (dl
->GetLevel() == level
)
83 else if (dl
->GetLevel() > level
)
88 DirtyList
*new_dl
= new DirtyList (level
);
89 lists
->InsertBefore (new_dl
, dl
);
97 DirtyLists::RemoveList (int level
)
99 DirtyList
*dl
= (DirtyList
*)GetList (level
, false);
106 DirtyLists::AddDirtyNode (int level
, List::Node
*node
)
108 DirtyList
*dl
= (DirtyList
*)GetList (level
, true);
109 dl
->GetDirtyNodes()->Append(node
);
113 DirtyLists::RemoveDirtyNode (int level
, List::Node
*node
)
115 DirtyList
*dl
= (DirtyList
*)GetList (level
, false);
118 dl
->GetDirtyNodes()->Remove(node
);
119 if (dl
->GetDirtyNodes()->IsEmpty())
124 DirtyLists::GetFirst ()
129 dl
= (DirtyList
*)lists
->First();
132 dl
= (DirtyList
*)lists
->Last ();
138 return dl
->GetDirtyNodes()->First();
142 DirtyLists::IsEmpty ()
144 return lists
->IsEmpty();
148 DirtyLists::Clear (bool freeNodes
)
150 lists
->Clear (freeNodes
);
154 Surface::AddDirtyElement (UIElement
*element
, DirtyType dirt
)
156 // there's no point in adding an element to the dirty list if it
157 // isn't in the hierarchy. it will be added to the dirty list when
158 // it's added to the hierarchy anyway.
159 if (element
->GetVisualParent() == NULL
&& !IsTopLevel(element
))
162 // XXX this should really be here...
163 // if (element->dirty_flags & dirt)
166 element
->dirty_flags
|= dirt
;
168 //printf ("adding element %p (%s) to the dirty list\n", element, element->GetTypeName());
170 if (dirt
& DownDirtyState
) {
171 if (element
->down_dirty_node
)
173 element
->down_dirty_node
= new DirtyNode (element
);
175 down_dirty
->AddDirtyNode (element
->GetVisualLevel (), element
->down_dirty_node
);
178 if (dirt
& UpDirtyState
) {
179 if (element
->up_dirty_node
)
181 element
->up_dirty_node
= new DirtyNode (element
);
183 up_dirty
->AddDirtyNode (element
->GetVisualLevel (), element
->up_dirty_node
);
188 Surface::RemoveDirtyElement (UIElement
*element
)
190 if (element
->up_dirty_node
)
191 up_dirty
->RemoveDirtyNode (element
->GetVisualLevel(), element
->up_dirty_node
);
192 if (element
->down_dirty_node
)
193 down_dirty
->RemoveDirtyNode (element
->GetVisualLevel(), element
->down_dirty_node
);
195 element
->down_dirty_node
= NULL
;
196 element
->up_dirty_node
= NULL
;
201 ** There are 2 types of changes that need to propagate around the
204 ** 1. Those changes that needs to be propagated from parent to children
205 ** (transformation, opacity). We call these Downward Changes, and
206 ** the elements are placed in the down_dirty list.
208 ** 2. Those changes that need to be propagated from children to parent
209 ** (bounds, invalidation). We call these Upward Changes, and the
210 ** elements are placed in the up_dirty list.
213 ** Downward Changes can result in new Upward changes (when an
214 ** element's transform changes, usually its bounds change), so when
215 ** processing the dirty list we push changes down the tree before
216 ** handling the Upward Changes.
221 Surface::PropagateDirtyFlagToChildren (UIElement
*el
, DirtyType flags
)
223 VisualTreeWalker walker
= VisualTreeWalker (el
);
224 while (UIElement
*child
= walker
.Step ())
225 AddDirtyElement (child
, flags
);
229 Surface::ProcessDownDirtyElements ()
231 /* push down the transforms opacity, and visibility changes first */
232 while (DirtyNode
*node
= (DirtyNode
*)down_dirty
->GetFirst()) {
233 UIElement
* el
= (UIElement
*)node
->element
;
235 if (el
->dirty_flags
& DirtyRenderVisibility
) {
236 el
->dirty_flags
&= ~DirtyRenderVisibility
;
240 // Since we are not included in our parents subtree when we
241 // are collapsed we need to notify our parent that things may
243 if (el
->GetVisualParent ())
244 el
->GetVisualParent ()->UpdateBounds ();
246 el
->ComputeTotalRenderVisibility ();
248 if (!el
->GetRenderVisible ())
249 el
->CacheInvalidateHint ();
251 AddDirtyElement (el
, DirtyNewBounds
);
253 PropagateDirtyFlagToChildren (el
, DirtyRenderVisibility
);
256 if (el
->dirty_flags
& DirtyHitTestVisibility
) {
257 el
->dirty_flags
&= ~DirtyHitTestVisibility
;
259 el
->ComputeTotalHitTestVisibility ();
261 PropagateDirtyFlagToChildren (el
, DirtyHitTestVisibility
);
264 ** since we cache N's local (from N's parent to N)
265 ** transform, we need to catch if we're changing
266 ** something about that local transform and recompute
269 ** DirtyLocalTransform implies DirtyTransform, since
270 ** changing N's local transform requires updating the
271 ** transform of all descendents in the subtree rooted
274 if (el
->dirty_flags
& DirtyLocalTransform
) {
275 el
->dirty_flags
&= ~DirtyLocalTransform
;
277 el
->dirty_flags
|= DirtyTransform
;
279 el
->ComputeLocalTransform ();
282 if (el
->dirty_flags
& DirtyTransform
) {
283 el
->dirty_flags
&= ~DirtyTransform
;
286 el
->ComputeTransform ();
288 if (el
->GetVisualParent ())
289 el
->GetVisualParent ()->UpdateBounds ();
291 AddDirtyElement (el
, DirtyNewBounds
);
292 PropagateDirtyFlagToChildren (el
, DirtyTransform
);
295 if (el
->dirty_flags
& DirtyLocalClip
) {
296 el
->dirty_flags
&= ~DirtyLocalClip
;
297 el
->dirty_flags
|= DirtyClip
;
299 // XXX el->ComputeLocalClip ();
302 if (el
->dirty_flags
& DirtyClip
) {
303 el
->dirty_flags
&= ~DirtyTransform
;
305 // XXX el->ComputeClip ();
306 // XXX el->UpdateBounds ();
308 PropagateDirtyFlagToChildren (el
, DirtyClip
);
311 if (el
->dirty_flags
& DirtyChildrenZIndices
) {
312 el
->dirty_flags
&= ~DirtyChildrenZIndices
;
313 if (!el
->Is(Type::PANEL
)) {
314 g_warning ("DirtyChildrenZIndices is only applicable to Panel subclasses");
317 ((Panel
*)el
)->GetChildren ()->ResortByZIndex();
322 if (!(el
->dirty_flags
& DownDirtyState
)) {
323 down_dirty
->RemoveDirtyNode (el
->GetVisualLevel (), el
->down_dirty_node
);
324 el
->down_dirty_node
= NULL
;
328 if (!down_dirty
->IsEmpty())
329 g_warning ("after down dirty pass, down dirty list is not empty");
333 ** Note that since this calls GDK invalidation functions
334 ** it's a good idea to call it with a GDK lock held (all gtk callbacks
335 ** are automatically protected except for timeouts and idle)
338 Surface::ProcessUpDirtyElements ()
340 while (DirtyNode
*node
= (DirtyNode
*)up_dirty
->GetFirst()) {
341 UIElement
* el
= (UIElement
*)node
->element
;
343 // printf ("up processing element element %p (%s)\n", el, el->GetName());
344 // printf ("el->parent = %p\n", el->parent);
346 if (el
->dirty_flags
& DirtyBounds
) {
347 // printf (" + bounds\n");
348 el
->dirty_flags
&= ~DirtyBounds
;
350 Rect obounds
= el
->GetBounds ();
351 Rect osubtreebounds
= el
->GetSubtreeBounds ();
352 bool parent_bounds_updated
= false;
354 el
->ComputeBounds ();
356 // printf (" + + obounds = %f %f %f %f, nbounds = %f %f %f %f\n",
357 // obounds.x, obounds.y, obounds.w, obounds.h,
358 // el->GetBounds().x, el->GetBounds().y, el->GetBounds().w, el->GetBounds().h);
360 if (osubtreebounds
!= el
->GetSubtreeBounds ()) {
361 if (el
->GetVisualParent ()) {
362 el
->GetVisualParent ()->UpdateBounds ();
363 parent_bounds_updated
= true;
367 if (obounds
!= el
->GetBounds()) {
368 if (el
->GetVisualParent ()) {
369 // printf (" + + + calling UpdateBounds and Invalidate on parent\n");
370 if (!parent_bounds_updated
)
371 el
->GetVisualParent ()->UpdateBounds();
373 Region oregion
= Region (obounds
);
374 el
->GetVisualParent ()->Invalidate (&oregion
);
379 if (el
->force_invalidate_of_new_bounds
) {
380 el
->force_invalidate_of_new_bounds
= false;
381 // Invalidate everything including the
382 // visible area of our children.
383 el
->InvalidateSubtreePaint ();
386 if (el
->dirty_flags
& DirtyNewBounds
) {
388 el
->dirty_flags
&= ~DirtyNewBounds
;
390 if (el
->dirty_flags
& DirtyInvalidate
) {
391 // printf (" + invalidating %p (%s) %s, %f %f %f %f\n",
392 // el, el->GetTypeName(), el->GetName(), el->dirty_rect.x, el->dirty_rect.y, el->dirty_rect.w, el->dirty_rect.h);
394 el
->dirty_flags
&= ~DirtyInvalidate
;
396 Region
*dirty
= el
->dirty_region
;
400 dirty
->GetRectangles (&rects
, &count
);
401 Surface
*surface
= el
->GetDeployment ()->GetSurface ();
402 if (el
->IsAttached ()) {
404 Rect r
= Rect ((double)rects
[count
].x
,
405 (double)rects
[count
].y
,
406 (double)rects
[count
].width
,
407 (double)rects
[count
].height
);
408 //printf (" + + invalidating parent (%f,%f,%f,%f)\n",
414 surface
->Invalidate (r
);
419 delete el
->dirty_region
;
420 el
->dirty_region
= new Region ();
423 if (!(el
->dirty_flags
& UpDirtyState
)) {
424 up_dirty
->RemoveDirtyNode (el
->GetVisualLevel (), el
->up_dirty_node
);
425 el
->up_dirty_node
= NULL
;
429 if (!up_dirty
->IsEmpty())
430 g_warning ("after up dirty pass, up dirty list is not empty");
434 Surface::UpdateLayout ()
436 for (int i
= 0; i
< layers
->GetCount (); i
++) {
437 UIElement
*layer
= layers
->GetValueAt (i
)->AsUIElement ();
438 if (!layer
->HasFlag (UIElement::DIRTY_MEASURE_HINT
) && !layer
->HasFlag (UIElement::DIRTY_ARRANGE_HINT
))
441 // This is a hack to make sure the elements understand the currnet
442 // size of the surface until it is moved to a proper location.
443 Size
*last
= LayoutInformation::GetPreviousConstraint (layer
);
444 Size available
= Size (active_window
->GetWidth (), active_window
->GetHeight ());
445 if (layer
->IsContainer () && (!last
|| (*last
!= available
))) {
446 layer
->InvalidateMeasure ();
447 LayoutInformation::SetPreviousConstraint (layer
, &available
);
450 layer
->UpdateLayout ();
455 Surface::ProcessDirtyElements ()
458 bool dirty
= down_dirty
->IsEmpty() || !up_dirty
->IsEmpty();
459 ProcessDownDirtyElements ();
460 ProcessUpDirtyElements ();