2009-11-12 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / uielement.cpp
blobdbdf00841ac0b9c8826ebf0a49aa8cb5e6cfbf92
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * uielement.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 <stdlib.h>
14 #include <math.h>
16 #include "trigger.h"
17 #include "uielement.h"
18 #include "collection.h"
19 #include "canvas.h"
20 #include "brush.h"
21 #include "transform.h"
22 #include "runtime.h"
23 #include "geometry.h"
24 #include "shape.h"
25 #include "dirty.h"
26 #include "eventargs.h"
27 #include "timemanager.h"
28 #include "media.h"
29 #include "resources.h"
30 #include "popup.h"
31 #include "provider.h"
33 //#define DEBUG_INVALIDATE 0
34 #define MIN_FRONT_TO_BACK_COUNT 25
36 UIElement::UIElement ()
38 SetObjectType (Type::UIELEMENT);
40 visual_level = 0;
41 visual_parent = NULL;
42 subtree_object = NULL;
43 opacityMask = NULL;
45 flags = UIElement::RENDER_VISIBLE | UIElement::HIT_TEST_VISIBLE;
47 hidden_desire = Size (-INFINITY, -INFINITY);
48 bounds = Rect (0,0,0,0);
49 cairo_matrix_init_identity (&absolute_xform);
51 emitting_loaded = false;
52 dirty_flags = DirtyMeasure;
53 up_dirty_node = down_dirty_node = NULL;
54 force_invalidate_of_new_bounds = false;
55 dirty_region = new Region ();
57 desired_size = Size (0, 0);
58 render_size = Size (0, 0);
60 ComputeLocalTransform ();
61 ComputeTotalRenderVisibility ();
62 ComputeTotalHitTestVisibility ();
65 UIElement::~UIElement()
67 delete dirty_region;
70 bool
71 UIElement::IsSubtreeLoaded (UIElement *element)
73 while (element && !element->IsLoaded ())
74 element = element->GetVisualParent ();
75 return element;
78 void
79 UIElement::Dispose()
81 TriggerCollection *triggers = GetTriggers ();
83 if (triggers != NULL) {
84 for (int i = 0; i < triggers->GetCount (); i++)
85 triggers->GetValueAt (i)->AsEventTrigger ()->RemoveTarget (this);
88 if (!IsDisposed ()) {
89 VisualTreeWalker walker (this);
90 while (UIElement *child = walker.Step ())
91 child->SetVisualParent (NULL);
94 if (subtree_object) {
95 subtree_object->unref ();
96 subtree_object = NULL;
99 DependencyObject::Dispose();
102 void
103 UIElement::SetSurface (Surface *s)
105 if (GetSurface() == s)
106 return;
108 if (s == NULL && GetSurface()) {
109 /* we're losing our surface, delete ourselves from the dirty list if we're on it */
110 GetSurface()->RemoveDirtyElement (this);
113 if (subtree_object != NULL && subtree_object->Is(Type::UIELEMENT))
114 subtree_object->SetSurface (s);
116 DependencyObject::SetSurface (s);
119 Rect
120 UIElement::IntersectBoundsWithClipPath (Rect unclipped, bool transform)
122 Geometry *clip = GetClip ();
123 Geometry *layout_clip = transform ? NULL : LayoutInformation::GetLayoutClip (this);
124 Rect box;
126 if (!clip && !layout_clip)
127 return unclipped;
129 if (clip)
130 box = clip->GetBounds ();
131 else
132 box = layout_clip->GetBounds ();
134 if (layout_clip)
135 box = box.Intersection (layout_clip->GetBounds());
137 if (!GetRenderVisible())
138 box = Rect (0,0,0,0);
140 if (transform)
141 box = box.Transform (&absolute_xform);
143 return box.Intersection (unclipped);
146 void
147 UIElement::RenderClipPath (cairo_t *cr, bool path_only)
149 cairo_new_path (cr);
150 cairo_set_matrix (cr, &absolute_xform);
152 Geometry *geometry = GetClip();
153 if (!geometry)
154 return;
156 geometry->Draw (cr);
157 if (!path_only)
158 cairo_clip (cr);
161 void
162 UIElement::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
164 if (args->GetProperty ()->GetOwnerType() != Type::UIELEMENT) {
165 DependencyObject::OnPropertyChanged (args, error);
166 return;
169 if (args->GetId () == UIElement::OpacityProperty) {
170 InvalidateVisibility ();
171 } else if (args->GetId () == UIElement::VisibilityProperty) {
172 // note: invalid enum values are only validated in 1.1 (managed code),
173 // the default value for VisibilityProperty is VisibilityCollapsed
174 // (see bug #340799 for more details)
175 if (args->GetNewValue()->AsInt32() == VisibilityVisible)
176 flags |= UIElement::RENDER_VISIBLE;
177 else
178 flags &= ~UIElement::RENDER_VISIBLE;
180 InvalidateVisibility ();
181 InvalidateMeasure ();
182 if (GetVisualParent ())
183 GetVisualParent ()->InvalidateMeasure ();
184 } else if (args->GetId () == UIElement::IsHitTestVisibleProperty) {
185 if (args->GetNewValue()->AsBool())
186 flags |= UIElement::HIT_TEST_VISIBLE;
187 else
188 flags &= ~UIElement::HIT_TEST_VISIBLE;
190 UpdateTotalHitTestVisibility();
191 } else if (args->GetId () == UIElement::ClipProperty) {
192 InvalidateClip ();
193 } else if (args->GetId () == UIElement::OpacityMaskProperty) {
194 opacityMask = args->GetNewValue() ? args->GetNewValue()->AsBrush() : NULL;
195 InvalidateMask ();
196 } else if (args->GetId () == UIElement::RenderTransformProperty
197 || args->GetId () == UIElement::RenderTransformOriginProperty) {
198 UpdateTransform ();
200 else if (args->GetId () == UIElement::TriggersProperty) {
201 if (args->GetOldValue()) {
202 // remove the old trigger targets
203 TriggerCollection *triggers = args->GetOldValue()->AsTriggerCollection();
204 for (int i = 0; i < triggers->GetCount (); i++)
205 triggers->GetValueAt (i)->AsEventTrigger ()->RemoveTarget (this);
208 if (args->GetNewValue()) {
209 // set the new ones
210 TriggerCollection *triggers = args->GetNewValue()->AsTriggerCollection();
211 for (int i = 0; i < triggers->GetCount (); i++)
212 triggers->GetValueAt (i)->AsEventTrigger ()->SetTarget (this);
214 } else if (args->GetId () == UIElement::UseLayoutRoundingProperty) {
215 InvalidateMeasure ();
216 InvalidateArrange ();
219 NotifyListenersOfPropertyChange (args, error);
222 void
223 UIElement::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
225 if (col == GetTriggers ()) {
226 switch (args->GetChangedAction()) {
227 case CollectionChangedActionReplace:
228 args->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
229 // fall thru to Add
230 case CollectionChangedActionAdd:
231 args->GetNewItem()->AsEventTrigger ()->SetTarget (this);
232 break;
233 case CollectionChangedActionRemove:
234 args->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
235 break;
236 case CollectionChangedActionClearing:
237 for (int i = 0; i < col->GetCount (); i++)
238 col->GetValueAt (i)->AsEventTrigger ()->RemoveTarget (this);
239 break;
240 case CollectionChangedActionCleared:
241 // nothing needed here.
242 break;
245 else {
246 DependencyObject::OnCollectionChanged (col, args);
250 #if 1
252 UIElement::DumpHierarchy (UIElement *obj)
254 if (obj == NULL)
255 return 0;
257 int n = DumpHierarchy (obj->GetVisualParent ());
258 for (int i = 0; i < n; i++)
259 putchar (' ');
260 printf ("%s (%p)\n", obj->GetTypeName(), obj);
261 return n + 4;
263 #endif
265 void
266 UIElement::UpdateBounds (bool force_redraw)
268 //InvalidateMeasure ();
269 //InvalidateArrange ();
271 Surface *surface = GetSurface ();
272 if (surface)
273 surface->AddDirtyElement (this, DirtyBounds);
275 force_invalidate_of_new_bounds |= force_redraw;
278 void
279 UIElement::UpdateTotalRenderVisibility ()
281 Surface *surface = GetSurface ();
282 if (surface)
283 surface->AddDirtyElement (this, DirtyRenderVisibility);
286 void
287 UIElement::UpdateTotalHitTestVisibility ()
289 VisualTreeWalker walker (this);
290 while (UIElement *child = walker.Step ())
291 child->UpdateTotalHitTestVisibility ();
293 if (GetSurface())
294 GetSurface ()->AddDirtyElement (this, DirtyHitTestVisibility);
297 bool
298 UIElement::GetActualTotalRenderVisibility ()
300 bool visible = (flags & UIElement::RENDER_VISIBLE) != 0;
301 bool parent_visible = true;
303 total_opacity = GetOpacity ();
305 if (GetVisualParent ()) {
306 GetVisualParent ()->ComputeTotalRenderVisibility ();
307 parent_visible = visible && GetVisualParent ()->GetRenderVisible ();
308 total_opacity *= GetVisualParent ()->total_opacity;
311 visible = visible && parent_visible;
313 return visible;
316 void
317 UIElement::ComputeTotalRenderVisibility ()
319 if (GetActualTotalRenderVisibility ())
320 flags |= UIElement::TOTAL_RENDER_VISIBLE;
321 else
322 flags &= ~UIElement::TOTAL_RENDER_VISIBLE;
325 bool
326 UIElement::GetActualTotalHitTestVisibility ()
328 bool visible = (flags & UIElement::HIT_TEST_VISIBLE) != 0;
330 if (visible && GetVisualParent ()) {
331 GetVisualParent ()->ComputeTotalHitTestVisibility ();
332 visible = visible && GetVisualParent ()->GetHitTestVisible ();
335 return visible;
338 void
339 UIElement::ComputeTotalHitTestVisibility ()
341 if (GetActualTotalHitTestVisibility ())
342 flags |= UIElement::TOTAL_HIT_TEST_VISIBLE;
343 else
344 flags &= ~UIElement::TOTAL_HIT_TEST_VISIBLE;
347 void
348 UIElement::UpdateTransform ()
350 if (GetSurface()) {
351 GetSurface()->AddDirtyElement (this, DirtyLocalTransform);
355 void
356 UIElement::ComputeLocalTransform ()
358 Transform *transform = GetRenderTransform ();
359 Point transform_origin = GetTransformOrigin ();
360 cairo_matrix_t render;
362 cairo_matrix_init_identity (&render);
363 cairo_matrix_init_identity (&local_xform);
365 if (transform == NULL)
366 return;
368 transform->GetTransform (&render);
369 cairo_matrix_translate (&local_xform, transform_origin.x, transform_origin.y);
370 cairo_matrix_multiply (&local_xform, &render, &local_xform);
371 cairo_matrix_translate (&local_xform, -transform_origin.x, -transform_origin.y);
374 void
375 UIElement::TransformBounds (cairo_matrix_t *old, cairo_matrix_t *current)
377 Rect updated;
379 cairo_matrix_t tween = *old;
380 cairo_matrix_invert (&tween);
381 cairo_matrix_multiply (&tween, &tween, current);
383 Point p0 (0,0);
384 Point p1 (1,0);
385 Point p2 (1,1);
386 Point p3 (0,1);
388 p0 = p0 - p0.Transform (&tween);
389 p1 = p1 - p1.Transform (&tween);
390 p2 = p2 - p2.Transform (&tween);
391 p3 = p3 - p3.Transform (&tween);
393 if (p0 == p1 && p1 == p2 && p2 == p3) {
394 //printf ("shifting position\n");
395 ShiftPosition (bounds.GetTopLeft ().Transform (&tween));
396 return;
399 UpdateBounds ();
402 void
403 UIElement::ComputeTransform ()
405 cairo_matrix_t old = absolute_xform;
406 cairo_matrix_init_identity (&absolute_xform);
408 if (GetVisualParent () != NULL) {
409 absolute_xform = GetVisualParent ()->absolute_xform;
410 } else if (GetParent () != NULL && GetParent ()->Is (Type::POPUP)) {
411 // FIXME we shouldn't be examing a subclass type here but we'll do this
412 // for now to get popups in something approaching the right place while
413 // we figure out a cleaner way to handle it long term.
414 Popup *popup = (Popup *)GetParent ();
415 absolute_xform = popup->absolute_xform;
416 cairo_matrix_translate (&absolute_xform, popup->GetHorizontalOffset (), popup->GetVerticalOffset ());
419 cairo_matrix_multiply (&absolute_xform, &layout_xform, &absolute_xform);
420 cairo_matrix_multiply (&absolute_xform, &local_xform, &absolute_xform);
422 if (moonlight_flags & RUNTIME_INIT_USE_UPDATE_POSITION)
423 TransformBounds (&old, &absolute_xform);
424 else {
425 UpdateBounds ();
429 void
430 UIElement::ComputeBounds ()
432 g_warning ("UIElement:ComputeBounds has been called. The derived class %s should have overridden it.",
433 GetTypeName ());
436 void
437 UIElement::ShiftPosition (Point p)
439 bounds.x = p.x;
440 bounds.y = p.y;
443 void
444 UIElement::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
446 if (prop && prop->GetId () == UIElement::RenderTransformProperty) {
447 UpdateTransform ();
449 else if (prop && prop->GetId () == UIElement::ClipProperty) {
450 InvalidateClip ();
452 else if (prop && prop->GetId () == UIElement::OpacityMaskProperty) {
453 InvalidateMask ();
456 DependencyObject::OnSubPropertyChanged (prop, obj, subobj_args);
459 void
460 UIElement::CacheInvalidateHint ()
462 VisualTreeWalker walker (this);
463 while (UIElement *child = walker.Step ())
464 child->CacheInvalidateHint ();
467 void
468 UIElement::SetVisualParent (UIElement *visual_parent)
470 this->visual_parent = visual_parent;
472 if (visual_parent && visual_parent->GetSurface () != GetSurface())
473 SetSurface (visual_parent->GetSurface());
476 void
477 UIElement::SetSubtreeObject (DependencyObject *value)
479 if (subtree_object == value)
480 return;
482 if (subtree_object)
483 subtree_object->unref ();
485 subtree_object = value;
487 if (subtree_object)
488 subtree_object->ref ();
491 void
492 UIElement::ElementRemoved (UIElement *item)
494 // Invalidate ourself in the size of the item's subtree
495 Invalidate (item->GetSubtreeBounds());
497 if (GetSurface ())
498 GetSurface()->RemoveDirtyElement (item);
499 item->SetVisualParent (NULL);
500 item->CacheInvalidateHint ();
501 item->ClearLoaded ();
503 Rect emptySlot (0,0,0,0);
504 LayoutInformation::SetLayoutSlot (item, &emptySlot);
505 item->ClearValue (LayoutInformation::LayoutClipProperty);
507 InvalidateMeasure ();
510 void
511 UIElement::ElementAdded (UIElement *item)
513 item->SetVisualLevel (GetVisualLevel() + 1);
514 item->SetVisualParent (this);
515 item->UpdateTotalRenderVisibility ();
516 item->UpdateTotalHitTestVisibility ();
517 //item->UpdateBounds (true);
518 item->Invalidate ();
520 if (0 != (flags & (UIElement::IS_LOADED | UIElement::PENDING_LOADED))) {
521 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (item);
523 bool post = false;
525 item->WalkTreeForLoadedHandlers (&post, true, false);
527 if (post)
528 Deployment::GetCurrent()->PostLoaded ();
531 UpdateBounds (true);
533 InvalidateMeasure ();
534 ClearValue (LayoutInformation::LayoutClipProperty);
535 ClearValue (LayoutInformation::PreviousConstraintProperty);
536 item->SetRenderSize (Size (0,0));
537 item->UpdateTransform ();
538 item->InvalidateMeasure ();
539 item->InvalidateArrange ();
542 void
543 UIElement::InvalidateMeasure ()
545 // g_print ("m(%s)", GetTypeName ());
547 dirty_flags |= DirtyMeasure;
549 Surface *surface;
550 if ((surface = GetSurface ()))
551 surface->needs_measure = true;
555 void
556 UIElement::InvalidateArrange ()
558 //g_print ("a(%s)", GetTypeName ());
560 dirty_flags |= DirtyArrange;
562 Surface *surface;
563 if ((surface = GetSurface ()))
564 surface->needs_arrange = true;
567 void
568 UIElement::DoMeasure ()
570 Size *last = LayoutInformation::GetPreviousConstraint (this);
571 UIElement *parent = GetVisualParent ();
572 Size infinite (INFINITY, INFINITY);
574 if (!GetSurface () && !last && !parent && IsLayoutContainer ()) {
575 last = &infinite;
578 if (last) {
579 Size previous_desired = GetDesiredSize ();
581 // This will be a noop on non layout elements
582 Measure (*last);
584 if (previous_desired == GetDesiredSize ())
585 return;
588 // a canvas doesn't care about the child size changing like this
589 if (parent)
590 parent->InvalidateMeasure ();
592 dirty_flags &= ~DirtyMeasure;
595 void
596 UIElement::DoArrange ()
598 Rect *last = LayoutInformation::GetLayoutSlot (this);
599 Size previous_render = Size ();
600 UIElement *parent = GetVisualParent ();
601 Rect viewport;
603 if (!parent) {
604 Size desired = Size ();
605 Size available = Size ();
606 Surface *surface = GetSurface ();
608 if (IsLayoutContainer ()) {
609 desired = GetDesiredSize ();
610 if (surface && surface->IsTopLevel (this) && !GetParent ()) {
611 Size *measure = LayoutInformation::GetPreviousConstraint (this);
612 if (measure)
613 desired = desired.Max (*LayoutInformation::GetPreviousConstraint (this));
614 else
615 desired = Size (surface->GetWindow ()->GetWidth (), surface->GetWindow ()->GetHeight ());
617 } else {
618 FrameworkElement *fe = (FrameworkElement*)this;
619 desired = Size (fe->GetActualWidth (), fe->GetActualHeight ());
622 viewport = Rect (Canvas::GetLeft (this),
623 Canvas::GetTop (this),
624 desired.width, desired.height);
626 last = &viewport;
629 if (last) {
630 Arrange (*last);
631 } else {
632 if (parent)
633 parent->InvalidateArrange ();
637 bool
638 UIElement::InsideClip (cairo_t *cr, double x, double y)
640 Geometry* clip;
641 bool inside = true;
642 double nx = x;
643 double ny = y;
645 clip = GetClip ();
646 if (!clip)
647 return true;
649 TransformPoint (&nx, &ny);
651 if (!clip->GetBounds ().PointInside (nx, ny))
652 return false;
654 cairo_save (cr);
655 cairo_new_path (cr);
657 clip->Draw (cr);
658 inside = cairo_in_fill (cr, nx, ny);
660 cairo_restore (cr);
662 return inside;
665 bool
666 UIElement::InsideObject (cairo_t *cr, double x, double y)
668 return InsideClip (cr, x, y);
672 UIElement::RemoveHandler (int event_id, EventHandler handler, gpointer data)
674 int token = DependencyObject::RemoveHandler (event_id, handler, data);
676 if (event_id == UIElement::LoadedEvent && token != -1)
677 Deployment::GetCurrent()->RemoveLoadedHandler (this, token);
679 return token;
682 void
683 UIElement::RemoveHandler (int event_id, int token)
685 DependencyObject::RemoveHandler (event_id, token);
687 if (event_id == UIElement::LoadedEvent)
688 Deployment::GetCurrent()->RemoveLoadedHandler (this, token);
691 void
692 UIElement::WalkTreeForLoadedHandlers (bool *post, bool only_unemitted, bool force_walk_up)
694 List *walk_list = new List();
695 bool post_loaded = false;
697 DeepTreeWalker *walker = new DeepTreeWalker (this);
699 // we need to make sure to apply the default style to all
700 // controls in the subtree
701 while (UIElement *element = (UIElement*)walker->Step ()) {
702 if (element->Is(Type::CONTROL)) {
703 Control *control = (Control*)element;
704 if (!control->default_style_applied) {
705 ManagedTypeInfo *key = control->GetDefaultStyleKey ();
706 if (key) {
707 if (Application::GetCurrent () == NULL)
708 g_warning ("attempting to use a null application when applying default style when emitting Loaded event.");
709 else
710 Application::GetCurrent()->ApplyDefaultStyle (control, key);
714 if (!control->GetTemplateRoot () /* we only need to worry about this if the template hasn't been expanded */
715 && control->GetTemplate())
716 post_loaded = true; //control->ReadLocalValue (Control::TemplateProperty) == NULL;
719 element->flags |= UIElement::PENDING_LOADED;
720 element->OnLoaded ();
721 if (element->HasHandlers (UIElement::LoadedEvent))
722 post_loaded = true;
725 if (force_walk_up || !post_loaded || HasHandlers (UIElement::LoadedEvent)) {
726 // we need to walk back up to the root to collect all loaded events
727 UIElement *parent = this;
728 while (parent->GetVisualParent())
729 parent = parent->GetVisualParent();
730 delete walker;
731 walker = new DeepTreeWalker (parent, Logical/*Reverse*/);
733 else {
734 // otherwise we only copy the events from the subtree
735 delete walker;
736 walker = new DeepTreeWalker (this, Logical/*Reverse*/);
739 while (UIElement *element = (UIElement*)walker->Step ()) {
740 walk_list->Prepend (new UIElementNode (element));
743 while (UIElementNode *ui = (UIElementNode*)walk_list->First ()) {
744 // remove it from the walk list
745 walk_list->Unlink (ui);
747 Deployment::GetCurrent()->AddAllLoadedHandlers (ui->uielement, only_unemitted);
750 if (post)
751 *post = post_loaded;
753 delete walker;
754 delete walk_list;
758 void
759 UIElement::OnLoaded ()
761 flags |= UIElement::IS_LOADED;
762 flags &= ~UIElement::PENDING_LOADED;
765 void
766 UIElement::ClearLoaded ()
768 UIElement *e = NULL;
769 Surface *s = Deployment::GetCurrent ()->GetSurface ();
770 if (s->GetFocusedElement () == this)
771 s->FocusElement (NULL);
773 ClearForeachGeneration (UIElement::LoadedEvent);
775 if (!IsLoaded ())
776 return;
778 flags &= ~UIElement::IS_LOADED;
780 VisualTreeWalker walker (this);
781 while ((e = walker.Step ()))
782 e->ClearLoaded ();
785 bool
786 UIElement::Focus (bool recurse)
788 return false;
792 // Queues the invalidate for the current region, performs any
793 // updates to the RenderTransform (optional) and queues a
794 // new redraw with the new bounding box
796 void
797 UIElement::FullInvalidate (bool rendertransform)
799 Invalidate ();
800 if (rendertransform)
801 UpdateTransform ();
802 UpdateBounds (true /* force an invalidate here, even if the bounds don't change */);
805 void
806 UIElement::Invalidate (Rect r)
808 if (!GetRenderVisible() || IS_INVISIBLE(total_opacity))
809 return;
811 #ifdef DEBUG_INVALIDATE
812 printf ("Requesting invalidate for object %p %s (%s) at %f %f - %f %f\n",
813 this, GetName(), GetTypeName(),
814 r.x, r.y,
815 r.w, r.h);
816 #endif
819 if (GetSurface ()) {
820 GetSurface()->AddDirtyElement (this, DirtyInvalidate);
822 dirty_region->Union (r);
824 GetSurface()->GetTimeManager()->NeedRedraw ();
826 Emit (InvalidatedEvent);
830 void
831 UIElement::Invalidate (Region *region)
833 if (!GetRenderVisible () || IS_INVISIBLE (total_opacity))
834 return;
836 if (GetSurface ()) {
837 GetSurface()->AddDirtyElement (this, DirtyInvalidate);
839 dirty_region->Union (region);
841 GetSurface()->GetTimeManager()->NeedRedraw ();
843 Emit (InvalidatedEvent);
847 void
848 UIElement::Invalidate ()
850 Invalidate (bounds);
854 void
855 UIElement::InvalidatePaint ()
857 Invalidate ();
861 void
862 UIElement::InvalidateSubtreePaint ()
864 Invalidate (GetSubtreeBounds ());
867 void
868 UIElement::InvalidateClip ()
870 InvalidateSubtreePaint ();
871 UpdateBounds (true);
874 void
875 UIElement::InvalidateMask ()
877 InvalidateSubtreePaint ();
880 void
881 UIElement::InvalidateVisibility ()
883 UpdateTotalRenderVisibility ();
884 InvalidateSubtreePaint ();
888 void
889 UIElement::InvalidateIntrisicSize ()
891 InvalidateMeasure ();
892 InvalidateArrange ();
893 UpdateBounds (true);
897 void
898 UIElement::HitTest (cairo_t *cr, Point p, List *uielement_list)
900 uielement_list->Prepend (new UIElementNode (this));
903 void
904 UIElement::HitTest (cairo_t *cr, Rect r, List *uielement_list)
908 void
909 UIElement::FindElementsInHostCoordinates_p (Point p, HitTestCollection *uielement_list)
911 List *list = new List ();
912 cairo_t *ctx = measuring_context_create ();
914 FindElementsInHostCoordinates (ctx, p, list);
916 UIElementNode *node = (UIElementNode *) list->First ();
917 while (node) {
918 uielement_list->Add (new Value (node->uielement));
919 node = (UIElementNode *) node->next;
922 delete list;
923 measuring_context_destroy (ctx);
927 void
928 UIElement::FindElementsInHostCoordinates (cairo_t *cr, Point P, List *uielement_list)
930 uielement_list->Prepend (new UIElementNode (this));
934 void
935 UIElement::FindElementsInHostCoordinates_r (Rect r, HitTestCollection *uielement_list)
937 List *list = new List ();
938 cairo_t *ctx = measuring_context_create ();
940 FindElementsInHostCoordinates (ctx, r, list);
942 UIElementNode *node = (UIElementNode *) list->First ();
943 while (node) {
944 uielement_list->Add (new Value (node->uielement));
945 node = (UIElementNode *) node->next;
948 delete list;
949 measuring_context_destroy (ctx);
952 void
953 UIElement::FindElementsInHostCoordinates (cairo_t *cr, Rect r, List *uielement_list)
955 uielement_list->Prepend (new UIElementNode (this));
958 bool
959 UIElement::EmitKeyDown (GdkEventKey *event)
961 return Emit (KeyDownEvent, new KeyEventArgs (event));
964 bool
965 UIElement::EmitKeyUp (GdkEventKey *event)
967 return Emit (KeyUpEvent, new KeyEventArgs (event));
970 bool
971 UIElement::EmitGotFocus ()
973 return Emit (GotFocusEvent, new RoutedEventArgs (this));
976 bool
977 UIElement::EmitLostFocus ()
979 return Emit (LostFocusEvent, new RoutedEventArgs (this));
982 bool
983 UIElement::EmitLostMouseCapture ()
985 MouseEventArgs *e = new MouseEventArgs ();
986 e->SetSource (this);
987 return Emit (LostMouseCaptureEvent, e);
990 bool
991 UIElement::CaptureMouse ()
993 Surface *s = GetSurface ();
994 if (s == NULL)
995 return false;
997 return s->SetMouseCapture (this);
1000 void
1001 UIElement::ReleaseMouseCapture ()
1003 Surface *s = GetSurface ();
1004 if (s == NULL)
1005 return;
1007 s->ReleaseMouseCapture (this);
1010 void
1011 UIElement::DoRender (cairo_t *cr, Region *parent_region)
1013 Region *region = new Region (GetSubtreeBounds ());
1014 region->Intersect (parent_region);
1016 if (!GetRenderVisible() || IS_INVISIBLE (total_opacity) || region->IsEmpty ()) {
1017 delete region;
1018 return;
1021 #if FRONT_TO_BACK_STATS
1022 GetSurface()->uielements_rendered_back_to_front ++;
1023 #endif
1025 STARTTIMER (UIElement_render, Type::Find (GetObjectType())->name);
1027 PreRender (cr, region, false);
1029 Render (cr, region);
1031 PostRender (cr, region, false);
1033 ENDTIMER (UIElement_render, Type::Find (GetObjectType())->name);
1035 delete region;
1038 bool
1039 UIElement::UseBackToFront ()
1041 return VisualTreeWalker (this).GetCount () < MIN_FRONT_TO_BACK_COUNT;
1044 void
1045 UIElement::FrontToBack (Region *surface_region, List *render_list)
1047 double local_opacity = GetOpacity ();
1049 if (surface_region->RectIn (GetSubtreeBounds().RoundOut()) == GDK_OVERLAP_RECTANGLE_OUT)
1050 return;
1052 if (!GetRenderVisible ()
1053 || IS_INVISIBLE (local_opacity))
1054 return;
1056 if (!UseBackToFront ()) {
1057 Region *self_region = new Region (surface_region);
1058 self_region->Intersect (GetSubtreeBounds().RoundOut());
1060 // we need to include our children in this one, since
1061 // we'll be rendering them in the PostRender method.
1062 if (!self_region->IsEmpty())
1063 render_list->Prepend (new RenderNode (this, self_region, true,
1064 UIElement::CallPreRender, UIElement::CallPostRender));
1065 // don't remove the region from surface_region because
1066 // there are likely holes in it
1067 return;
1070 Region *region;
1071 bool delete_region;
1072 bool can_subtract_self;
1074 if (!GetClip ()
1075 && !GetOpacityMask ()
1076 && !IS_TRANSLUCENT (GetOpacity ())) {
1077 region = surface_region;
1078 delete_region = false;
1079 can_subtract_self = true;
1081 else {
1082 region = new Region (surface_region);
1083 delete_region = true;
1084 can_subtract_self = false;
1087 RenderNode *cleanup_node = new RenderNode (this, NULL, false, NULL, UIElement::CallPostRender);
1089 render_list->Prepend (cleanup_node);
1091 Region *self_region = new Region (region);
1093 VisualTreeWalker walker (this, ZReverse);
1094 while (UIElement *child = walker.Step ())
1095 child->FrontToBack (region, render_list);
1097 if (!GetOpacityMask () && !IS_TRANSLUCENT (local_opacity)) {
1098 delete self_region;
1099 if (GetRenderBounds().IsEmpty ()) { // empty bounds mean that this element draws nothing itself
1100 self_region = new Region ();
1102 else {
1103 self_region = new Region (region);
1104 self_region->Intersect (GetRenderBounds().RoundOut ()); // note the RoundOut
1106 } else {
1107 self_region->Intersect (GetSubtreeBounds().RoundOut ()); // note the RoundOut
1110 if (self_region->IsEmpty() && render_list->First() == cleanup_node) {
1111 /* we don't intersect the surface region, and none of
1112 our children did either, remove the cleanup node */
1113 render_list->Remove (render_list->First());
1114 delete self_region;
1115 if (delete_region)
1116 delete region;
1117 return;
1120 render_list->Prepend (new RenderNode (this, self_region, !self_region->IsEmpty(), UIElement::CallPreRender, NULL));
1122 if (!self_region->IsEmpty()) {
1123 if (((absolute_xform.yx == 0 && absolute_xform.xy == 0) /* no skew/rotation */
1124 || (absolute_xform.xx == 0 && absolute_xform.yy == 0)) /* allow 90 degree rotations */
1125 && can_subtract_self)
1126 region->Subtract (GetCoverageBounds ());
1129 if (delete_region)
1130 delete region;
1133 void
1134 UIElement::PreRender (cairo_t *cr, Region *region, bool skip_children)
1136 double local_opacity = GetOpacity ();
1138 cairo_save (cr);
1140 cairo_set_matrix (cr, &absolute_xform);
1141 RenderClipPath (cr);
1143 if (opacityMask || IS_TRANSLUCENT (local_opacity)) {
1144 Rect r = GetSubtreeBounds ().RoundOut();
1145 cairo_identity_matrix (cr);
1147 // we need this check because ::PreRender can (and
1148 // will) be called for elements with empty regions.
1150 // The region passed in here is the redraw region
1151 // intersected with the render bounds of a given
1152 // element. For Panels with no width/height specified
1153 // in the xaml, this region will be empty. (check
1154 // panel.cpp::FrontToBack - we insert the ::PreRender
1155 // calling node if either the panel background or any
1156 // of the children intersect the redraw region.) We
1157 // can't clip to the empty region, obviously, as it
1158 // will keep all descendents from drawing to the
1159 // screen.
1161 if (!region->IsEmpty()) {
1162 region->Draw (cr);
1163 cairo_clip (cr);
1165 r.Draw (cr);
1166 cairo_clip (cr);
1168 cairo_set_matrix (cr, &absolute_xform);
1171 if (ClipToExtents ()) {
1172 extents.Draw (cr);
1173 cairo_clip (cr);
1177 if (IS_TRANSLUCENT (local_opacity))
1178 cairo_push_group (cr);
1180 if (opacityMask != NULL)
1181 cairo_push_group (cr);
1184 void
1185 UIElement::PostRender (cairo_t *cr, Region *region, bool front_to_back)
1187 // if we didn't render front to back, then render the children here
1188 if (!front_to_back) {
1189 VisualTreeWalker walker (this, ZForward);
1190 while (UIElement *child = walker.Step ())
1191 child->DoRender (cr, region);
1194 double local_opacity = GetOpacity ();
1196 if (opacityMask != NULL) {
1197 cairo_pattern_t *mask;
1198 cairo_pattern_t *data = cairo_pop_group (cr);
1199 Point p = GetOriginPoint ();
1200 Rect area = Rect (p.x, p.y, 0.0, 0.0);
1201 GetSizeForBrush (cr, &(area.width), &(area.height));
1202 opacityMask->SetupBrush (cr, area);
1203 mask = cairo_get_source (cr);
1204 cairo_pattern_reference (mask);
1205 cairo_set_source (cr, data);
1206 cairo_mask (cr, mask);
1207 cairo_pattern_destroy (mask);
1208 cairo_pattern_destroy (data);
1211 if (IS_TRANSLUCENT (local_opacity)) {
1212 cairo_pop_group_to_source (cr);
1213 cairo_paint_with_alpha (cr, local_opacity);
1216 cairo_restore (cr);
1218 if (moonlight_flags & RUNTIME_INIT_SHOW_CLIPPING) {
1219 cairo_save (cr);
1220 cairo_new_path (cr);
1221 cairo_set_matrix (cr, &absolute_xform);
1222 cairo_set_line_width (cr, 1);
1224 Geometry *geometry = GetClip ();
1225 if (geometry) {
1226 geometry->Draw (cr);
1227 cairo_set_source_rgba (cr, 0.0, 1.0, 1.0, 1.0);
1228 cairo_stroke (cr);
1231 geometry = LayoutInformation::GetClip ((FrameworkElement *)this);
1232 if (geometry) {
1233 geometry->Draw (cr);
1234 cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
1235 cairo_stroke (cr);
1238 cairo_restore (cr);
1241 if (moonlight_flags & RUNTIME_INIT_SHOW_BOUNDING_BOXES) {
1242 cairo_save (cr);
1243 cairo_new_path (cr);
1244 //RenderClipPath (cr);
1245 cairo_identity_matrix (cr);
1246 cairo_set_source_rgba (cr, 1.0, 0.5, 0.2, 1.0);
1247 cairo_set_line_width (cr, 1);
1248 cairo_rectangle (cr, bounds.x + .5, bounds.y + .5, bounds.width - .5, bounds.height - .5);
1249 cairo_stroke (cr);
1250 cairo_restore (cr);
1254 void
1255 UIElement::Paint (cairo_t *ctx, Region *region, cairo_matrix_t *xform)
1257 // FIXME xform is ignored for now
1258 if (xform)
1259 g_warning ("passing a transform to UIElement::Paint is not yet supported");
1261 #if FRONT_TO_BACK_STATS
1262 uielements_rendered_front_to_back = 0;
1263 uielements_rendered_back_to_front = 0;
1264 #endif
1266 bool did_front_to_back = false;
1267 List *render_list = new List ();
1269 if (moonlight_flags & RUNTIME_INIT_RENDER_FRONT_TO_BACK) {
1270 Region *copy = new Region (region);
1271 FrontToBack (copy, render_list);
1273 if (!render_list->IsEmpty ()) {
1274 while (RenderNode *node = (RenderNode*)render_list->First()) {
1275 #if FRONT_TO_BACK_STATS
1276 uielements_rendered_front_to_back ++;
1277 #endif
1278 node->Render (ctx);
1280 render_list->Remove (node);
1283 did_front_to_back = true;
1286 delete render_list;
1287 delete copy;
1290 if (!did_front_to_back) {
1291 DoRender (ctx, region);
1294 #if FRONT_TO_BACK_STATS
1295 printf ("UIElements rendered front-to-back for: %s(%p)\n", uielements_rendered_front_to_back, GetName (), this);
1296 printf ("UIElements rendered back-to-front for: %s(%p)\n", uielements_rendered_back_to_front, GetName (), this);
1297 #endif
1300 void
1301 UIElement::CallPreRender (cairo_t *cr, UIElement *element, Region *region, bool front_to_back)
1303 element->PreRender (cr, region, front_to_back);
1306 void
1307 UIElement::CallPostRender (cairo_t *cr, UIElement *element, Region *region, bool front_to_back)
1309 element->PostRender (cr, region, front_to_back);
1312 void
1313 UIElement::Render (cairo_t *cr, Region *region, bool path_only)
1315 /* do nothing by default */
1318 void
1319 UIElement::GetSizeForBrush (cairo_t *cr, double *width, double *height)
1321 g_warning ("UIElement:GetSizeForBrush has been called. The derived class %s should have overridden it.",
1322 GetTypeName ());
1323 *height = *width = 0.0;
1326 TimeManager *
1327 UIElement::GetTimeManager ()
1329 Surface *surface = GetSurface ();
1330 Deployment *deployment;
1332 if (surface == NULL) {
1333 deployment = GetDeployment ();
1334 if (deployment != NULL)
1335 surface = deployment->GetSurface ();
1338 return surface ? surface->GetTimeManager() : NULL;
1341 GeneralTransform *
1342 UIElement::GetTransformToUIElementWithError (UIElement *to_element, MoonError *error)
1344 /* walk from this up to the root. if we hit null before we hit the toplevel, it's an error */
1345 UIElement *visual = this;
1346 bool ok = false;
1348 if (visual && GetSurface()) {
1349 while (visual) {
1350 if (GetSurface()->IsTopLevel (visual))
1351 ok = true;
1352 visual = visual->GetVisualParent ();
1356 if (!ok || (to_element && !to_element->GetSurface ())) {
1357 MoonError::FillIn (error, MoonError::ARGUMENT, 1001,
1358 "visual");
1359 return NULL;
1362 if (to_element && !to_element->GetSurface()->IsTopLevel (to_element)) {
1363 /* if @to_element is specified we also need to make sure there's a path to the root from it */
1364 ok = false;
1365 visual = to_element->GetVisualParent ();
1366 if (visual && to_element->GetSurface()) {
1367 while (visual) {
1368 if (to_element->GetSurface()->IsTopLevel (visual))
1369 ok = true;
1370 visual = visual->GetVisualParent ();
1374 if (!ok) {
1375 MoonError::FillIn (error, MoonError::ARGUMENT, 1001,
1376 "visual");
1377 return NULL;
1381 cairo_matrix_t result;
1382 // A = From, B = To, M = what we want
1383 // A = M * B
1384 // => M = A * inv (B)
1385 if (to_element) {
1386 cairo_matrix_t inverse = to_element->absolute_xform;
1387 cairo_matrix_invert (&inverse);
1388 cairo_matrix_multiply (&result, &absolute_xform, &inverse);
1390 else {
1391 result = absolute_xform;
1394 Matrix *matrix = new Matrix (&result);
1396 MatrixTransform *transform = new MatrixTransform ();
1397 transform->SetValue (MatrixTransform::MatrixProperty, matrix);
1398 matrix->unref ();
1400 return transform;
1403 void
1404 UIElement::TransformPoint (double *x, double *y)
1406 cairo_matrix_t inverse = absolute_xform;
1407 cairo_matrix_invert (&inverse);
1409 cairo_matrix_transform_point (&inverse, x, y);