2009-12-01 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / frameworkelement.cpp
blob5ce600ff86e47f5b4022ef7b62b0d3d5d73eb354
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * frameworkelement.cpp:
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
8 *
9 */
11 #include <config.h>
13 #include <math.h>
15 #include "debug.h"
16 #include "geometry.h"
17 #include "application.h"
18 #include "deployment.h"
19 #include "runtime.h"
20 #include "namescope.h"
21 #include "frameworkelement.h"
22 #include "trigger.h"
23 #include "thickness.h"
24 #include "collection.h"
25 #include "style.h"
26 #include "validators.h"
28 #define MAX_LAYOUT_PASSES 250
30 FrameworkElementProvider::FrameworkElementProvider (DependencyObject *obj, PropertyPrecedence precedence) : PropertyValueProvider (obj, precedence)
32 actual_height_value = NULL;
33 actual_width_value = NULL;
34 last = Size (-INFINITY, -INFINITY);
37 FrameworkElementProvider::~FrameworkElementProvider ()
39 delete actual_height_value;
40 delete actual_width_value;
43 void
44 FrameworkElement::Dispose ()
46 if (default_template != NULL) {
47 default_template->SetParent (NULL, NULL);
48 default_template = NULL;
50 UIElement::Dispose ();
53 Value *
54 FrameworkElementProvider::GetPropertyValue (DependencyProperty *property)
56 if (property->GetId () != FrameworkElement::ActualHeightProperty &&
57 property->GetId () != FrameworkElement::ActualWidthProperty)
58 return NULL;
60 FrameworkElement *element = (FrameworkElement *) obj;
62 Size actual = element->ComputeActualSize ();
64 if (last != actual) {
65 last = actual;
67 if (actual_height_value)
68 delete actual_height_value;
70 if (actual_width_value)
71 delete actual_width_value;
73 actual_height_value = new Value (actual.height);
74 actual_width_value = new Value (actual.width);
77 if (property->GetId () == FrameworkElement::ActualHeightProperty) {
78 return actual_height_value;
79 } else {
80 return actual_width_value;
84 FrameworkElement::FrameworkElement ()
86 SetObjectType (Type::FRAMEWORKELEMENT);
88 default_template = NULL;
89 default_style_applied = false;
90 get_default_template_cb = NULL;
91 measure_cb = NULL;
92 arrange_cb = NULL;
93 loaded_cb = NULL;
94 bounds_with_children = Rect ();
95 logical_parent = NULL;
97 providers[PropertyPrecedence_LocalStyle] = new StylePropertyValueProvider (this, PropertyPrecedence_LocalStyle);
98 providers[PropertyPrecedence_DefaultStyle] = new StylePropertyValueProvider (this, PropertyPrecedence_DefaultStyle);
99 providers[PropertyPrecedence_DynamicValue] = new FrameworkElementProvider (this, PropertyPrecedence_DynamicValue);
102 FrameworkElement::~FrameworkElement ()
106 void
107 FrameworkElement::RenderLayoutClip (cairo_t *cr)
109 FrameworkElement *element = this;
110 cairo_matrix_t xform;
112 /* store off the current transform since the following loop is about the blow it away */
113 cairo_get_matrix (cr, &xform);
115 while (element) {
116 Geometry *geom = LayoutInformation::GetLayoutClip (element);
118 if (geom) {
119 geom->Draw (cr);
120 cairo_clip (cr);
123 // translate by the negative visual offset of the
124 // element to get the parent's coordinate space.
125 Point *visual_offset = LayoutInformation::GetVisualOffset (element);
126 if (visual_offset)
127 cairo_translate (cr, -visual_offset->x, -visual_offset->y);
129 element = (FrameworkElement*)element->GetVisualParent ();
132 /* restore the transform after we're done clipping */
133 cairo_set_matrix (cr, &xform);
136 Point
137 FrameworkElement::GetTransformOrigin ()
139 Point *user_xform_origin = GetRenderTransformOrigin ();
141 double width = GetActualWidth ();
142 double height = GetActualHeight ();
144 return Point (width * user_xform_origin->x,
145 height * user_xform_origin->y);
148 void
149 FrameworkElement::SetLogicalParent (DependencyObject *logical_parent, MoonError *error)
151 if (logical_parent && this->logical_parent && this->logical_parent != logical_parent) {
152 MoonError::FillIn (error, MoonError::INVALID_OPERATION, "Element is a child of another element");
153 return;
156 this->logical_parent = logical_parent;
159 void
160 FrameworkElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
162 if (args->GetProperty ()->GetOwnerType() != Type::FRAMEWORKELEMENT) {
163 UIElement::OnPropertyChanged (args, error);
164 return;
167 if (args->GetId () == FrameworkElement::WidthProperty ||
168 args->GetId () == FrameworkElement::MaxWidthProperty ||
169 args->GetId () == FrameworkElement::MinWidthProperty ||
170 args->GetId () == FrameworkElement::MaxHeightProperty ||
171 args->GetId () == FrameworkElement::MinHeightProperty ||
172 args->GetId () == FrameworkElement::HeightProperty ||
173 args->GetId () == FrameworkElement::MarginProperty) {
175 Point *p = GetRenderTransformOrigin ();
177 /* normally we'd only update the bounds of this
178 element on a width/height change, but if the render
179 transform is someplace other than (0,0), the
180 transform needs to be updated as well. */
181 FullInvalidate (p->x != 0.0 || p->y != 0.0);
183 FrameworkElement *visual_parent = (FrameworkElement *)GetVisualParent ();
184 if (visual_parent) {
185 visual_parent->InvalidateMeasure ();
188 InvalidateMeasure ();
189 InvalidateArrange ();
190 UpdateBounds ();
192 else if (args->GetId () == FrameworkElement::StyleProperty) {
193 if (args->GetNewValue()) {
194 Style *s = args->GetNewValue()->AsStyle ();
195 if (s) {
196 // this has a side effect of calling
197 // ProviderValueChanged on all values
198 // in the style, so we might end up
199 // with lots of property notifications
200 // here (reentrancy ok?)
202 Application::GetCurrent()->ApplyStyle (this, s);
204 ((StylePropertyValueProvider*)providers[PropertyPrecedence_LocalStyle])->SealStyle (s);
208 else if (args->GetId () == FrameworkElement::HorizontalAlignmentProperty ||
209 args->GetId () == FrameworkElement::VerticalAlignmentProperty) {
210 InvalidateArrange ();
211 FullInvalidate (true);
214 NotifyListenersOfPropertyChange (args, error);
217 Size
218 FrameworkElement::ApplySizeConstraints (const Size &size)
220 Size specified (GetWidth (), GetHeight ());
221 Size constrained (GetMinWidth (), GetMinHeight ());
223 constrained = constrained.Max (size);
225 if (!isnan (specified.width))
226 constrained.width = specified.width;
228 if (!isnan (specified.height))
229 constrained.height = specified.height;
231 constrained = constrained.Min (GetMaxWidth (), GetMaxHeight ());
232 constrained = constrained.Max (GetMinWidth (), GetMinHeight ());
234 if (GetUseLayoutRounding ()) {
235 constrained.width = round (constrained.width);
236 constrained.height = round (constrained.height);
238 return constrained;
241 void
242 FrameworkElement::ComputeBounds ()
244 Size size (GetActualWidth (), GetActualHeight ());
245 size = ApplySizeConstraints (size);
247 extents = Rect (0, 0, size.width, size.height);
249 bounds = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
250 bounds_with_children = bounds;
252 VisualTreeWalker walker = VisualTreeWalker (this);
253 while (UIElement *item = walker.Step ()) {
254 if (!item->GetRenderVisible ())
255 continue;
257 bounds_with_children = bounds_with_children.Union (item->GetSubtreeBounds ());
261 Rect
262 FrameworkElement::GetSubtreeBounds ()
264 VisualTreeWalker walker = VisualTreeWalker (this);
265 if (GetSubtreeObject () != NULL)
266 return bounds_with_children;
268 return bounds;
271 Size
272 FrameworkElement::ComputeActualSize ()
274 UIElement *parent = GetVisualParent ();
276 if (GetVisibility () != VisibilityVisible)
277 return Size (0.0, 0.0);
279 if ((parent && !parent->Is (Type::CANVAS)) || (IsLayoutContainer ()))
280 return GetRenderSize ();
282 Size actual (0, 0);
284 actual = ApplySizeConstraints (actual);
286 return actual;
289 bool
290 FrameworkElement::InsideLayoutClip (double x, double y)
292 Geometry *layout_clip = LayoutInformation::GetClip (this);
293 bool inside = true;
295 if (!layout_clip)
296 return inside;
298 TransformPoint (&x, &y);
299 inside = layout_clip->GetBounds ().PointInside (x, y);
300 layout_clip->unref ();
302 return inside;
305 bool
306 FrameworkElement::InsideObject (cairo_t *cr, double x, double y)
308 Size framework (GetActualWidth (), GetActualHeight ());
309 double nx = x, ny = y;
311 TransformPoint (&nx, &ny);
312 if (nx < 0 || ny < 0 || nx > framework.width || ny > framework.height)
313 return false;
315 if (!InsideLayoutClip (x, y))
316 return false;
318 return UIElement::InsideObject (cr, x, y);
321 void
322 FrameworkElement::HitTest (cairo_t *cr, Point p, List *uielement_list)
324 if (!GetRenderVisible ())
325 return;
327 if (!GetHitTestVisible ())
328 return;
330 // first a quick bounds check
331 if (!GetSubtreeBounds().PointInside (p.x, p.y))
332 return;
334 /* the clip property is global so we can short out here */
335 if (!InsideClip (cr, p.x, p.y))
336 return;
338 /* create our node and stick it on front */
339 List::Node *us = uielement_list->Prepend (new UIElementNode (this));
340 bool hit = false;
342 VisualTreeWalker walker = VisualTreeWalker (this, ZReverse);
343 while (UIElement *child = walker.Step ()) {
344 child->HitTest (cr, p, uielement_list);
346 if (us != uielement_list->First ()) {
347 hit = true;
348 break;
352 if (!hit && !InsideObject (cr, p.x, p.y))
353 uielement_list->Remove (us);
356 void
357 FrameworkElement::FindElementsInHostCoordinates (cairo_t *cr, Point host, List *uielement_list)
359 if (GetVisibility () != VisibilityVisible)
360 return;
362 if (!GetHitTestVisible ())
363 return;
365 if (bounds_with_children.height <= 0)
366 return;
368 /* the clip property is global so we can short out here */
369 if (!InsideClip (cr, host.x, host.y))
370 return;
372 cairo_save (cr);
374 /* create our node and stick it on front */
375 List::Node *us = uielement_list->Prepend (new UIElementNode (this));
377 VisualTreeWalker walker = VisualTreeWalker (this, ZForward);
378 while (UIElement *child = walker.Step ())
379 child->FindElementsInHostCoordinates (cr, host, uielement_list);
381 if (us == uielement_list->First ()) {
382 cairo_new_path (cr);
383 cairo_identity_matrix (cr);
385 if (!CanFindElement () || !InsideObject (cr, host.x, host.y))
386 uielement_list->Remove (us);
388 cairo_restore (cr);
391 // FIXME: This is not the fastest way of implementing this, decomposing the rectangle into
392 // a series of points is probably going to be quite slow. It's a good first effort.
393 void
394 FrameworkElement::FindElementsInHostCoordinates (cairo_t *cr, Rect r, List *uielement_list)
396 bool res = false;
397 if (GetVisibility () != VisibilityVisible)
398 return;
400 if (!GetHitTestVisible ())
401 return;
403 if (bounds_with_children.height <= 0)
404 return;
406 if (!bounds_with_children.IntersectsWith (r))
407 return;
409 cairo_save (cr);
410 cairo_new_path (cr);
412 Geometry *clip = GetClip ();
413 if (clip) {
414 if (!r.IntersectsWith (clip->GetBounds ().Transform (&absolute_xform)))
415 return;
416 r = r.Intersection (clip->GetBounds ().Transform (&absolute_xform));
419 /* create our node and stick it on front */
420 List::Node *us = uielement_list->Prepend (new UIElementNode (this));
422 VisualTreeWalker walker = VisualTreeWalker (this, ZForward);
423 while (UIElement *child = walker.Step ())
424 child->FindElementsInHostCoordinates (cr, r, uielement_list);
426 if (us == uielement_list->First ()) {
427 cairo_new_path (cr);
428 cairo_identity_matrix (cr);
430 res = false;
431 if (CanFindElement ()) {
432 res = bounds.Intersection (r) == bounds;
434 for (int i= r.x; i < (r.x + r.width) && !res; i++)
435 for (int j= r.y; j < (r.y + r.height) && !res; j++)
436 res = InsideObject (cr, i, j);
439 if (!res)
440 uielement_list->Remove (us);
442 cairo_restore (cr);
445 void
446 FrameworkElement::GetSizeForBrush (cairo_t *cr, double *width, double *height)
448 *width = GetActualWidth ();
449 *height = GetActualHeight ();
452 void
453 FrameworkElement::Measure (Size availableSize)
455 //LOG_LAYOUT ("measuring %p %s %g,%g\n", this, GetTypeName (), availableSize.width, availableSize.height);
457 Size *last = LayoutInformation::GetPreviousConstraint (this);
458 bool domeasure = (this->dirty_flags & DirtyMeasure) > 0;
460 domeasure |= !last || last->width != availableSize.width || last->height != availableSize.height;
462 if (GetVisibility () != VisibilityVisible) {
463 LayoutInformation::SetPreviousConstraint (this, &availableSize);
464 SetDesiredSize (Size (0,0));
465 return;
468 ApplyTemplate ();
470 UIElement *parent = GetVisualParent ();
471 /* unit tests show a short circuit in this case */
473 if (!parent && !IsContainer () && (!GetSurface () || (GetSurface () && !GetSurface ()->IsTopLevel (this)))) {
474 SetDesiredSize (Size (0,0));
475 return;
479 if (!domeasure)
480 return;
482 LayoutInformation::SetPreviousConstraint (this, &availableSize);
484 InvalidateArrange ();
485 UpdateBounds ();
487 dirty_flags &= ~DirtyMeasure;
489 Thickness margin = *GetMargin ();
490 Size size = availableSize.GrowBy (-margin);
492 size = ApplySizeConstraints (size);
494 if (measure_cb)
495 size = (*measure_cb)(size);
496 else
497 size = MeasureOverride (size);
499 hidden_desire = size;
501 if (!parent || parent->Is (Type::CANVAS)) {
502 if (Is (Type::CANVAS) || !IsLayoutContainer ()) {
503 SetDesiredSize (Size (0,0));
504 return;
508 // postcondition the results
509 size = ApplySizeConstraints (size);
511 size = size.GrowBy (margin);
512 size = size.Min (availableSize);
514 if (GetUseLayoutRounding ()) {
515 size.width = round (size.width);
516 size.height = round (size.height);
519 SetDesiredSize (size);
522 Size
523 FrameworkElement::MeasureOverride (Size availableSize)
525 Size desired = Size (0,0);
527 availableSize = availableSize.Max (desired);
529 VisualTreeWalker walker = VisualTreeWalker (this);
530 while (UIElement *child = walker.Step ()) {
531 child->Measure (availableSize);
532 desired = child->GetDesiredSize ();
535 return desired.Min (availableSize);
539 // not sure about the disconnect between these two methods.. I would
540 // imagine both should take Rects and ArrangeOverride would return a
541 // rectangle as well..
542 void
543 FrameworkElement::Arrange (Rect finalRect)
545 //LOG_LAYOUT ("arranging %p %s %g,%g,%g,%g\n", this, GetTypeName (), finalRect.x, finalRect.y, finalRect.width, finalRect.height);
546 Rect *slot = LayoutInformation::GetLayoutSlot (this);
547 bool doarrange = this->dirty_flags & DirtyArrange;
549 if (GetUseLayoutRounding ()) {
550 Rect rounded;
551 rounded.x = round (finalRect.x);
552 rounded.y = round (finalRect.y);
553 //rounded.width = MAX (round ((finalRect.x + finalRect.width) - rounded.x), 0);
554 //rounded.height = MAX (round ((finalRect.y + finalRect.height) - rounded.y), 0);
555 rounded.width = round (finalRect.width);
556 rounded.height = round (finalRect.height);
557 finalRect = rounded;
560 doarrange |= slot ? *slot != finalRect : true;
562 if (finalRect.width < 0 || finalRect.height < 0
563 || isinf (finalRect.width) || isinf (finalRect.height)
564 || isnan (finalRect.width) || isnan (finalRect.height)) {
565 Size desired = GetDesiredSize ();
566 g_warning ("invalid arguments to Arrange (%g,%g,%g,%g) Desired = (%g,%g)", finalRect.x, finalRect.y, finalRect.width, finalRect.height, desired.width, desired.height);
567 return;
570 UIElement *parent = GetVisualParent ();
571 /* unit tests show a short circuit in this case */
573 if (!parent && !IsContainer () && (!GetSurface () || (GetSurface () && !GetSurface ()->IsTopLevel (this)))) {
574 return;
577 if (GetVisibility () != VisibilityVisible) {
578 LayoutInformation::SetLayoutSlot (this, &finalRect);
579 return;
582 if (!doarrange)
583 return;
586 * FIXME I'm not happy with doing this here but until I come
587 * up with a better plan make sure that layout elements have
588 * been measured at least once
590 Size *measure = LayoutInformation::GetPreviousConstraint (this);
591 if (IsContainer () && !measure)
592 Measure (Size (finalRect.width, finalRect.height));
593 measure = LayoutInformation::GetPreviousConstraint (this);
595 ClearValue (LayoutInformation::LayoutClipProperty);
597 this->dirty_flags &= ~DirtyArrange;
599 Thickness margin = *GetMargin ();
600 Rect child_rect = finalRect.GrowBy (-margin);
602 cairo_matrix_init_translate (&layout_xform, child_rect.x, child_rect.y);
603 UpdateTransform ();
604 UpdateBounds ();
605 this->dirty_flags &= ~DirtyArrange;
607 Size offer = hidden_desire;
608 Size response;
610 Size stretched = ApplySizeConstraints (Size (child_rect.width, child_rect.height));
611 Size framework = ApplySizeConstraints (Size ());
613 HorizontalAlignment horiz = GetHorizontalAlignment ();
614 VerticalAlignment vert = GetVerticalAlignment ();
616 if (horiz == HorizontalAlignmentStretch)
617 framework.width = MAX (framework.width, stretched.width);
619 if (vert == VerticalAlignmentStretch)
620 framework.height = MAX (framework.height, stretched.height);
622 offer = offer.Max (framework);
624 LayoutInformation::SetLayoutSlot (this, &finalRect);
626 if (arrange_cb)
627 response = (*arrange_cb)(offer);
628 else
629 response = ArrangeOverride (offer);
631 Point visual_offset (child_rect.x, child_rect.y);
632 LayoutInformation::SetVisualOffset (this, &visual_offset);
634 Size old_size = GetRenderSize ();
636 if (GetUseLayoutRounding ()) {
637 response.width = round (response.width);
638 response.height = round (response.height);
641 SetRenderSize (response);
643 if (!parent || parent->Is (Type::CANVAS)) {
644 if (!IsLayoutContainer ()) {
645 SetRenderSize (Size (0,0));
646 return;
650 Size constrainedResponse = response.Min (ApplySizeConstraints (response));
652 /* it doesn't appear we apply aligment or layout clipping to toplevel elements */
653 bool toplevel = IsAttached () && GetDeployment ()->GetSurface ()->IsTopLevel (this);
655 if (!toplevel) {
656 switch (horiz) {
657 case HorizontalAlignmentLeft:
658 break;
659 case HorizontalAlignmentRight:
660 visual_offset.x += child_rect.width - constrainedResponse.width;
661 break;
662 case HorizontalAlignmentCenter:
663 visual_offset.x += (child_rect.width - constrainedResponse.width) * .5;
664 break;
665 default:
666 visual_offset.x += MAX ((child_rect.width - constrainedResponse.width) * .5, 0);
667 break;
670 switch (vert) {
671 case VerticalAlignmentTop:
672 break;
673 case VerticalAlignmentBottom:
674 visual_offset.y += child_rect.height - constrainedResponse.height;
675 break;
676 case VerticalAlignmentCenter:
677 visual_offset.y += (child_rect.height - constrainedResponse.height) * .5;
678 break;
679 default:
680 visual_offset.y += MAX ((child_rect.height - constrainedResponse.height) * .5, 0);
682 break;
686 cairo_matrix_init_translate (&layout_xform, visual_offset.x, visual_offset.y);
687 LayoutInformation::SetVisualOffset (this, &visual_offset);
689 Rect element (0, 0, response.width, response.height);
690 Rect layout_clip = child_rect;
691 layout_clip.x = MAX (child_rect.x - visual_offset.x, 0);
692 layout_clip.y = MAX (child_rect.y - visual_offset.y, 0);
693 if (GetUseLayoutRounding ()) {
694 layout_clip.x = round (layout_clip.x);
695 layout_clip.y = round (layout_clip.y);
698 if (((!toplevel && element != element.Intersection (layout_clip)) || constrainedResponse != response) && !Is (Type::CANVAS) && ((parent && !parent->Is (Type::CANVAS)) || IsContainer ())) {
699 Size framework_clip = ApplySizeConstraints (Size (INFINITY, INFINITY));
700 layout_clip = layout_clip.Intersection (Rect (0, 0, framework_clip.width, framework_clip.height));
701 RectangleGeometry *rectangle = new RectangleGeometry ();
702 rectangle->SetRect (&layout_clip);
703 LayoutInformation::SetLayoutClip (this, rectangle);
704 rectangle->unref ();
707 if (old_size != response) { // || (old_offset && *old_offset != visual_offset)) {
708 if (!LayoutInformation::GetLastRenderSize (this)) {
709 LayoutInformation::SetLastRenderSize (this, &old_size);
710 PropagateFlagUp (DIRTY_SIZE_HINT);
715 Size
716 FrameworkElement::ArrangeOverride (Size finalSize)
718 Size arranged = finalSize;
720 VisualTreeWalker walker = VisualTreeWalker (this);
721 while (UIElement *child = walker.Step ()) {
722 Rect childRect (0,0,finalSize.width,finalSize.height);
724 child->Arrange (childRect);
725 arranged = arranged.Max (finalSize);
728 return arranged;
731 void
732 FrameworkElement::UpdateLayout ()
734 UIElement *element = this;
735 UIElement *parent = NULL;
737 // Seek to the top
738 while ((parent = element->GetVisualParent ())) {
739 element = parent;
742 LOG_LAYOUT ("\nFrameworkElement::UpdateLayout: ");
743 List *measure_list = new List ();
744 List *arrange_list = new List ();
745 List *size_list = new List ();
746 bool updated = false;
747 int i = 0;
748 while (i < MAX_LAYOUT_PASSES) {
749 LOG_LAYOUT ("\u267c");
751 // If we abort the arrange phase because InvalidateMeasure was called or if
752 // we abort the size phase because InvalidateMeasure/InvalidateArrange
753 // was called, we need to put the hint flags back otherwise we'll skip that
754 // branch during the next pass.
755 while (UIElementNode *node = (UIElementNode *)arrange_list->First ()) {
756 node->uielement->PropagateFlagUp (DIRTY_ARRANGE_HINT);
757 arrange_list->Remove (node);
759 while (UIElementNode *node = (UIElementNode *)size_list->First ()) {
760 node->uielement->PropagateFlagUp (DIRTY_SIZE_HINT);
761 size_list->Remove (node);
764 i++;
765 // Figure out which type of elements we should be selected - dirty measure, arrange or size
766 UIElementFlags flag = NONE;
767 if (element->HasFlag (DIRTY_MEASURE_HINT))
768 flag = DIRTY_MEASURE_HINT;
769 else if (element->HasFlag (DIRTY_ARRANGE_HINT))
770 flag = DIRTY_ARRANGE_HINT;
771 else if (element->HasFlag (DIRTY_SIZE_HINT))
772 flag = DIRTY_SIZE_HINT;
774 if (flag != NONE) {
775 DeepTreeWalker measure_walker (element);
776 while (FrameworkElement *child = (FrameworkElement *)measure_walker.Step ()) {
777 if (child->GetVisibility () != VisibilityVisible || !child->HasFlag (flag)) {
778 measure_walker.SkipBranch ();
779 continue;
782 child->ClearFlag (flag);
783 switch (flag) {
784 case DIRTY_MEASURE_HINT:
785 if (child->dirty_flags & DirtyMeasure)
786 measure_list->Append (new UIElementNode (child));
787 break;
788 case DIRTY_ARRANGE_HINT:
789 if (child->dirty_flags & DirtyArrange)
790 arrange_list->Append (new UIElementNode (child));
791 break;
792 case DIRTY_SIZE_HINT:
793 if (child->ReadLocalValue (LayoutInformation::LastRenderSizeProperty))
794 size_list->Append (new UIElementNode (child));
795 break;
796 default:
797 break;
802 if (flag == DIRTY_MEASURE_HINT) {
803 while (UIElementNode* node = (UIElementNode*)measure_list->First ()) {
804 measure_list->Unlink (node);
806 node->uielement->DoMeasure ();
808 updated = true;
809 delete (node);
811 } else if (flag == DIRTY_ARRANGE_HINT) {
812 while (UIElementNode *node = (UIElementNode*)arrange_list->First ()) {
813 arrange_list->Unlink (node);
815 node->uielement->DoArrange ();
817 updated = true;
818 delete (node);
819 if (element->HasFlag (DIRTY_MEASURE_HINT))
820 break;
822 } else if (flag == DIRTY_SIZE_HINT) {
823 while (UIElementNode *node = (UIElementNode*)size_list->First ()) {
824 if (element->HasFlag (DIRTY_MEASURE_HINT) ||
825 element->HasFlag (DIRTY_ARRANGE_HINT)) {
826 break;
829 size_list->Unlink (node);
830 FrameworkElement *fe = (FrameworkElement*) node->uielement;
832 updated = true;
833 Size *last = LayoutInformation::GetLastRenderSize (fe);
834 if (last) {
835 SizeChangedEventArgs *args = new SizeChangedEventArgs (*last, fe->GetRenderSize ());
836 fe->ClearValue (LayoutInformation::LastRenderSizeProperty, false);
837 fe->Emit (FrameworkElement::SizeChangedEvent, args);
839 delete (node);
841 } else {
842 if (updated)
843 GetDeployment ()->LayoutUpdated ();
845 // If emitting LayoutUpdate invalidated measures/arranges, we should loop again
846 if (element->HasFlag (DIRTY_MEASURE_HINT) || element->HasFlag (DIRTY_ARRANGE_HINT))
847 updated = false;
848 else
849 break;
853 delete measure_list;
854 delete arrange_list;
855 delete size_list;
857 if (i >= MAX_LAYOUT_PASSES) {
858 // FIXME we shouldn't have to do this updated call here but otherwise we'll miss it completely
859 if (updated)
860 GetDeployment ()->LayoutUpdated ();
861 g_warning ("\n************** UpdateLayout Bailing Out after %d Passes *******************\n", i);
862 } else {
863 LOG_LAYOUT (" (%d)\n", i);
865 #if SANITY
866 DeepTreeWalker verifier (element);
867 while (UIElement *e = verifier.Step ()) {
868 if (e->GetVisibility () != VisibilityVisible) {
869 verifier.SkipBranch ();
870 continue;
872 if (e->dirty_flags & DirtyMeasure)
873 g_warning ("%s still has dirty measure after the layout pass\n", e->GetType ()->GetName());
874 if (e->dirty_flags & DirtyArrange)
875 g_warning ("%s still has dirty arrange after the layout pass\n", e->GetType ()->GetName());
876 if (e->ReadLocalValue (LayoutInformation::LastRenderSizeProperty))
877 g_warning ("%s still has LastRenderSize after the layout pass\n", e->GetType ()->GetName());
879 #endif
883 void
884 FrameworkElement::RegisterManagedOverrides (MeasureOverrideCallback measure_cb, ArrangeOverrideCallback arrange_cb,
885 GetDefaultTemplateCallback get_default_template_cb, LoadedCallback loaded_cb)
887 this->measure_cb = measure_cb;
888 this->arrange_cb = arrange_cb;
889 this->get_default_template_cb = get_default_template_cb;
890 this->loaded_cb = loaded_cb;
893 void
894 FrameworkElement::SetDefaultStyle (Style *style)
896 if (style) {
897 Application::GetCurrent()->ApplyStyle (this, style);
898 default_style_applied = true;
899 ((StylePropertyValueProvider*)providers[PropertyPrecedence_DefaultStyle])->SealStyle (style);
904 void
905 FrameworkElement::OnLoaded ()
907 UIElement::OnLoaded ();
909 if (loaded_cb)
910 (*loaded_cb) (this);
913 bool
914 FrameworkElement::ApplyTemplate ()
916 if (GetSubtreeObject ())
917 return false;
919 bool result = DoApplyTemplate ();
920 if (result)
921 OnApplyTemplate ();
922 return result;
925 bool
926 FrameworkElement::DoApplyTemplate ()
928 UIElement *e = GetDefaultTemplate ();
929 if (e) {
930 MoonError err;
931 e->SetParent (this, &err);
932 if (default_template)
933 default_template->SetParent (NULL, NULL);
934 default_template = e;
935 SetSubtreeObject (e);
936 ElementAdded (e);
938 return e != NULL;
941 void
942 FrameworkElement::OnApplyTemplate ()
944 Emit (TemplateAppliedEvent);
947 void
948 FrameworkElement::ElementRemoved (UIElement *obj)
950 if (GetSubtreeObject () == obj) {
951 MoonError e;
952 obj->SetParent (NULL, &e);
953 SetSubtreeObject (NULL);
955 UIElement::ElementRemoved (obj);
958 UIElement *
959 FrameworkElement::GetDefaultTemplate ()
961 if (get_default_template_cb)
962 return get_default_template_cb (this);
963 return NULL;