1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
17 #include "uielement.h"
18 #include "collection.h"
21 #include "transform.h"
26 #include "eventargs.h"
27 #include "timemanager.h"
29 #include "resources.h"
33 //#define DEBUG_INVALIDATE 0
34 #define MIN_FRONT_TO_BACK_COUNT 25
36 UIElement::UIElement ()
38 SetObjectType (Type::UIELEMENT
);
42 subtree_object
= 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()
71 UIElement::IsSubtreeLoaded (UIElement
*element
)
73 while (element
&& !element
->IsLoaded ())
74 element
= element
->GetVisualParent ();
81 TriggerCollection
*triggers
= GetTriggers ();
83 if (triggers
!= NULL
) {
84 for (int i
= 0; i
< triggers
->GetCount (); i
++)
85 triggers
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
89 VisualTreeWalker
walker (this);
90 while (UIElement
*child
= walker
.Step ())
91 child
->SetVisualParent (NULL
);
95 subtree_object
->unref ();
96 subtree_object
= NULL
;
99 DependencyObject::Dispose();
103 UIElement::SetSurface (Surface
*s
)
105 if (GetSurface() == s
)
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
);
120 UIElement::IntersectBoundsWithClipPath (Rect unclipped
, bool transform
)
122 Geometry
*clip
= GetClip ();
123 Geometry
*layout_clip
= transform
? NULL
: LayoutInformation::GetLayoutClip (this);
126 if (!clip
&& !layout_clip
)
130 box
= clip
->GetBounds ();
132 box
= layout_clip
->GetBounds ();
135 box
= box
.Intersection (layout_clip
->GetBounds());
137 if (!GetRenderVisible())
138 box
= Rect (0,0,0,0);
141 box
= box
.Transform (&absolute_xform
);
143 return box
.Intersection (unclipped
);
147 UIElement::RenderClipPath (cairo_t
*cr
, bool path_only
)
150 cairo_set_matrix (cr
, &absolute_xform
);
152 Geometry
*geometry
= GetClip();
162 UIElement::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
164 if (args
->GetProperty ()->GetOwnerType() != Type::UIELEMENT
) {
165 DependencyObject::OnPropertyChanged (args
, error
);
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
;
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
;
188 flags
&= ~UIElement::HIT_TEST_VISIBLE
;
190 UpdateTotalHitTestVisibility();
191 } else if (args
->GetId () == UIElement::ClipProperty
) {
193 } else if (args
->GetId () == UIElement::OpacityMaskProperty
) {
194 opacityMask
= args
->GetNewValue() ? args
->GetNewValue()->AsBrush() : NULL
;
196 } else if (args
->GetId () == UIElement::RenderTransformProperty
197 || args
->GetId () == UIElement::RenderTransformOriginProperty
) {
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()) {
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
);
223 UIElement::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
225 if (col
== GetTriggers ()) {
226 switch (args
->GetChangedAction()) {
227 case CollectionChangedActionReplace
:
228 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
230 case CollectionChangedActionAdd
:
231 args
->GetNewItem()->AsEventTrigger ()->SetTarget (this);
233 case CollectionChangedActionRemove
:
234 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
236 case CollectionChangedActionClearing
:
237 for (int i
= 0; i
< col
->GetCount (); i
++)
238 col
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
240 case CollectionChangedActionCleared
:
241 // nothing needed here.
246 DependencyObject::OnCollectionChanged (col
, args
);
252 UIElement::DumpHierarchy (UIElement
*obj
)
257 int n
= DumpHierarchy (obj
->GetVisualParent ());
258 for (int i
= 0; i
< n
; i
++)
260 printf ("%s (%p)\n", obj
->GetTypeName(), obj
);
266 UIElement::UpdateBounds (bool force_redraw
)
268 //InvalidateMeasure ();
269 //InvalidateArrange ();
271 Surface
*surface
= GetSurface ();
273 surface
->AddDirtyElement (this, DirtyBounds
);
275 force_invalidate_of_new_bounds
|= force_redraw
;
279 UIElement::UpdateTotalRenderVisibility ()
281 Surface
*surface
= GetSurface ();
283 surface
->AddDirtyElement (this, DirtyRenderVisibility
);
287 UIElement::UpdateTotalHitTestVisibility ()
289 VisualTreeWalker
walker (this);
290 while (UIElement
*child
= walker
.Step ())
291 child
->UpdateTotalHitTestVisibility ();
294 GetSurface ()->AddDirtyElement (this, DirtyHitTestVisibility
);
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
;
317 UIElement::ComputeTotalRenderVisibility ()
319 if (GetActualTotalRenderVisibility ())
320 flags
|= UIElement::TOTAL_RENDER_VISIBLE
;
322 flags
&= ~UIElement::TOTAL_RENDER_VISIBLE
;
326 UIElement::GetActualTotalHitTestVisibility ()
328 bool visible
= (flags
& UIElement::HIT_TEST_VISIBLE
) != 0;
330 if (visible
&& GetVisualParent ()) {
331 GetVisualParent ()->ComputeTotalHitTestVisibility ();
332 visible
= visible
&& GetVisualParent ()->GetHitTestVisible ();
339 UIElement::ComputeTotalHitTestVisibility ()
341 if (GetActualTotalHitTestVisibility ())
342 flags
|= UIElement::TOTAL_HIT_TEST_VISIBLE
;
344 flags
&= ~UIElement::TOTAL_HIT_TEST_VISIBLE
;
348 UIElement::UpdateTransform ()
351 GetSurface()->AddDirtyElement (this, DirtyLocalTransform
);
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
)
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
);
375 UIElement::TransformBounds (cairo_matrix_t
*old
, cairo_matrix_t
*current
)
379 cairo_matrix_t tween
= *old
;
380 cairo_matrix_invert (&tween
);
381 cairo_matrix_multiply (&tween
, &tween
, current
);
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
));
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
);
430 UIElement::ComputeBounds ()
432 g_warning ("UIElement:ComputeBounds has been called. The derived class %s should have overridden it.",
437 UIElement::ShiftPosition (Point p
)
444 UIElement::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
446 if (prop
&& prop
->GetId () == UIElement::RenderTransformProperty
) {
449 else if (prop
&& prop
->GetId () == UIElement::ClipProperty
) {
452 else if (prop
&& prop
->GetId () == UIElement::OpacityMaskProperty
) {
456 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
460 UIElement::CacheInvalidateHint ()
462 VisualTreeWalker
walker (this);
463 while (UIElement
*child
= walker
.Step ())
464 child
->CacheInvalidateHint ();
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());
477 UIElement::SetSubtreeObject (DependencyObject
*value
)
479 if (subtree_object
== value
)
483 subtree_object
->unref ();
485 subtree_object
= value
;
488 subtree_object
->ref ();
492 UIElement::ElementRemoved (UIElement
*item
)
494 // Invalidate ourself in the size of the item's subtree
495 Invalidate (item
->GetSubtreeBounds());
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 ();
511 UIElement::ElementAdded (UIElement
*item
)
513 item
->SetVisualLevel (GetVisualLevel() + 1);
514 item
->SetVisualParent (this);
515 item
->UpdateTotalRenderVisibility ();
516 item
->UpdateTotalHitTestVisibility ();
517 //item->UpdateBounds (true);
520 if (0 != (flags
& (UIElement::IS_LOADED
| UIElement::PENDING_LOADED
))) {
521 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (item
);
525 item
->WalkTreeForLoadedHandlers (&post
, true, false);
528 Deployment::GetCurrent()->PostLoaded ();
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 ();
543 UIElement::InvalidateMeasure ()
545 // g_print ("m(%s)", GetTypeName ());
547 dirty_flags
|= DirtyMeasure
;
550 if ((surface
= GetSurface ()))
551 surface
->needs_measure
= true;
556 UIElement::InvalidateArrange ()
558 //g_print ("a(%s)", GetTypeName ());
560 dirty_flags
|= DirtyArrange
;
563 if ((surface
= GetSurface ()))
564 surface
->needs_arrange
= true;
568 UIElement::DoMeasure ()
570 Size
*last
= LayoutInformation::GetPreviousConstraint (this);
571 UIElement
*parent
= GetVisualParent ();
572 Size
infinite (INFINITY
, INFINITY
);
574 if (!GetSurface () && !last
&& !parent
&& IsLayoutContainer ()) {
579 Size previous_desired
= GetDesiredSize ();
581 // This will be a noop on non layout elements
584 if (previous_desired
== GetDesiredSize ())
588 // a canvas doesn't care about the child size changing like this
590 parent
->InvalidateMeasure ();
592 dirty_flags
&= ~DirtyMeasure
;
596 UIElement::DoArrange ()
598 Rect
*last
= LayoutInformation::GetLayoutSlot (this);
599 Size previous_render
= Size ();
600 UIElement
*parent
= GetVisualParent ();
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);
613 desired
= desired
.Max (*LayoutInformation::GetPreviousConstraint (this));
615 desired
= Size (surface
->GetWindow ()->GetWidth (), surface
->GetWindow ()->GetHeight ());
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
);
633 parent
->InvalidateArrange ();
638 UIElement::InsideClip (cairo_t
*cr
, double x
, double y
)
649 TransformPoint (&nx
, &ny
);
651 if (!clip
->GetBounds ().PointInside (nx
, ny
))
658 inside
= cairo_in_fill (cr
, nx
, ny
);
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
);
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
);
692 UIElement::WalkTreeForLoadedHandlers (bool *post
, bool only_unemitted
, bool force_walk_up
)
694 List
*walk_list
= new List();
695 bool post_loaded
= false;
696 Deployment
*deployment
= GetDeployment ();
697 Application
*application
= deployment
->GetCurrentApplication ();
699 DeepTreeWalker
*walker
= new DeepTreeWalker (this);
701 // we need to make sure to apply the default style to all
702 // controls in the subtree
703 while (UIElement
*element
= (UIElement
*)walker
->Step ()) {
704 if (element
->Is(Type::CONTROL
)) {
705 Control
*control
= (Control
*)element
;
706 if (!control
->default_style_applied
) {
707 ManagedTypeInfo
*key
= control
->GetDefaultStyleKey ();
709 if (application
== NULL
)
710 g_warning ("attempting to use a null application when applying default style when emitting Loaded event.");
712 application
->ApplyDefaultStyle (control
, key
);
716 if (!control
->GetTemplateRoot () /* we only need to worry about this if the template hasn't been expanded */
717 && control
->GetTemplate())
718 post_loaded
= true; //control->ReadLocalValue (Control::TemplateProperty) == NULL;
721 element
->flags
|= UIElement::PENDING_LOADED
;
722 element
->OnLoaded ();
723 if (element
->HasHandlers (UIElement::LoadedEvent
))
727 if (force_walk_up
|| !post_loaded
|| HasHandlers (UIElement::LoadedEvent
)) {
728 // we need to walk back up to the root to collect all loaded events
729 UIElement
*parent
= this;
730 while (parent
->GetVisualParent())
731 parent
= parent
->GetVisualParent();
733 walker
= new DeepTreeWalker (parent
, Logical
/*Reverse*/);
736 // otherwise we only copy the events from the subtree
738 walker
= new DeepTreeWalker (this, Logical
/*Reverse*/);
741 while (UIElement
*element
= (UIElement
*)walker
->Step ()) {
742 walk_list
->Prepend (new UIElementNode (element
));
745 while (UIElementNode
*ui
= (UIElementNode
*)walk_list
->First ()) {
746 // remove it from the walk list
747 walk_list
->Unlink (ui
);
749 deployment
->AddAllLoadedHandlers (ui
->uielement
, only_unemitted
);
761 UIElement::OnLoaded ()
763 flags
|= UIElement::IS_LOADED
;
764 flags
&= ~UIElement::PENDING_LOADED
;
768 UIElement::ClearLoaded ()
771 Surface
*s
= Deployment::GetCurrent ()->GetSurface ();
772 if (s
->GetFocusedElement () == this)
773 s
->FocusElement (NULL
);
775 ClearForeachGeneration (UIElement::LoadedEvent
);
780 flags
&= ~UIElement::IS_LOADED
;
782 VisualTreeWalker
walker (this);
783 while ((e
= walker
.Step ()))
788 UIElement::Focus (bool recurse
)
794 // Queues the invalidate for the current region, performs any
795 // updates to the RenderTransform (optional) and queues a
796 // new redraw with the new bounding box
799 UIElement::FullInvalidate (bool rendertransform
)
804 UpdateBounds (true /* force an invalidate here, even if the bounds don't change */);
808 UIElement::Invalidate (Rect r
)
810 if (!GetRenderVisible() || IS_INVISIBLE(total_opacity
))
813 #ifdef DEBUG_INVALIDATE
814 printf ("Requesting invalidate for object %p %s (%s) at %f %f - %f %f\n",
815 this, GetName(), GetTypeName(),
822 GetSurface()->AddDirtyElement (this, DirtyInvalidate
);
824 dirty_region
->Union (r
);
826 GetSurface()->GetTimeManager()->NeedRedraw ();
828 Emit (InvalidatedEvent
);
833 UIElement::Invalidate (Region
*region
)
835 if (!GetRenderVisible () || IS_INVISIBLE (total_opacity
))
839 GetSurface()->AddDirtyElement (this, DirtyInvalidate
);
841 dirty_region
->Union (region
);
843 GetSurface()->GetTimeManager()->NeedRedraw ();
845 Emit (InvalidatedEvent
);
850 UIElement::Invalidate ()
857 UIElement::InvalidatePaint ()
864 UIElement::InvalidateSubtreePaint ()
866 Invalidate (GetSubtreeBounds ());
870 UIElement::InvalidateClip ()
872 InvalidateSubtreePaint ();
877 UIElement::InvalidateMask ()
879 InvalidateSubtreePaint ();
883 UIElement::InvalidateVisibility ()
885 UpdateTotalRenderVisibility ();
886 InvalidateSubtreePaint ();
891 UIElement::InvalidateIntrisicSize ()
893 InvalidateMeasure ();
894 InvalidateArrange ();
900 UIElement::HitTest (cairo_t
*cr
, Point p
, List
*uielement_list
)
902 uielement_list
->Prepend (new UIElementNode (this));
906 UIElement::HitTest (cairo_t
*cr
, Rect r
, List
*uielement_list
)
911 UIElement::FindElementsInHostCoordinates_p (Point p
, HitTestCollection
*uielement_list
)
913 List
*list
= new List ();
914 cairo_t
*ctx
= measuring_context_create ();
916 FindElementsInHostCoordinates (ctx
, p
, list
);
918 UIElementNode
*node
= (UIElementNode
*) list
->First ();
920 uielement_list
->Add (new Value (node
->uielement
));
921 node
= (UIElementNode
*) node
->next
;
925 measuring_context_destroy (ctx
);
930 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Point P
, List
*uielement_list
)
932 uielement_list
->Prepend (new UIElementNode (this));
937 UIElement::FindElementsInHostCoordinates_r (Rect r
, HitTestCollection
*uielement_list
)
939 List
*list
= new List ();
940 cairo_t
*ctx
= measuring_context_create ();
942 FindElementsInHostCoordinates (ctx
, r
, list
);
944 UIElementNode
*node
= (UIElementNode
*) list
->First ();
946 uielement_list
->Add (new Value (node
->uielement
));
947 node
= (UIElementNode
*) node
->next
;
951 measuring_context_destroy (ctx
);
955 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Rect r
, List
*uielement_list
)
957 uielement_list
->Prepend (new UIElementNode (this));
961 UIElement::EmitKeyDown (GdkEventKey
*event
)
963 return Emit (KeyDownEvent
, new KeyEventArgs (event
));
967 UIElement::EmitKeyUp (GdkEventKey
*event
)
969 return Emit (KeyUpEvent
, new KeyEventArgs (event
));
973 UIElement::EmitGotFocus ()
975 return Emit (GotFocusEvent
, new RoutedEventArgs (this));
979 UIElement::EmitLostFocus ()
981 return Emit (LostFocusEvent
, new RoutedEventArgs (this));
985 UIElement::EmitLostMouseCapture ()
987 MouseEventArgs
*e
= new MouseEventArgs ();
989 return Emit (LostMouseCaptureEvent
, e
);
993 UIElement::CaptureMouse ()
995 Surface
*s
= GetSurface ();
999 return s
->SetMouseCapture (this);
1003 UIElement::ReleaseMouseCapture ()
1005 Surface
*s
= GetSurface ();
1009 s
->ReleaseMouseCapture (this);
1013 UIElement::DoRender (cairo_t
*cr
, Region
*parent_region
)
1015 Region
*region
= new Region (GetSubtreeBounds ());
1016 region
->Intersect (parent_region
);
1018 if (!GetRenderVisible() || IS_INVISIBLE (total_opacity
) || region
->IsEmpty ()) {
1023 #if FRONT_TO_BACK_STATS
1024 GetSurface()->uielements_rendered_back_to_front
++;
1027 STARTTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1029 PreRender (cr
, region
, false);
1031 Render (cr
, region
);
1033 PostRender (cr
, region
, false);
1035 ENDTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1041 UIElement::UseBackToFront ()
1043 return VisualTreeWalker (this).GetCount () < MIN_FRONT_TO_BACK_COUNT
;
1047 UIElement::FrontToBack (Region
*surface_region
, List
*render_list
)
1049 double local_opacity
= GetOpacity ();
1051 if (surface_region
->RectIn (GetSubtreeBounds().RoundOut()) == GDK_OVERLAP_RECTANGLE_OUT
)
1054 if (!GetRenderVisible ()
1055 || IS_INVISIBLE (local_opacity
))
1058 if (!UseBackToFront ()) {
1059 Region
*self_region
= new Region (surface_region
);
1060 self_region
->Intersect (GetSubtreeBounds().RoundOut());
1062 // we need to include our children in this one, since
1063 // we'll be rendering them in the PostRender method.
1064 if (!self_region
->IsEmpty())
1065 render_list
->Prepend (new RenderNode (this, self_region
, true,
1066 UIElement::CallPreRender
, UIElement::CallPostRender
));
1067 // don't remove the region from surface_region because
1068 // there are likely holes in it
1074 bool can_subtract_self
;
1077 && !GetOpacityMask ()
1078 && !IS_TRANSLUCENT (GetOpacity ())) {
1079 region
= surface_region
;
1080 delete_region
= false;
1081 can_subtract_self
= true;
1084 region
= new Region (surface_region
);
1085 delete_region
= true;
1086 can_subtract_self
= false;
1089 RenderNode
*cleanup_node
= new RenderNode (this, NULL
, false, NULL
, UIElement::CallPostRender
);
1091 render_list
->Prepend (cleanup_node
);
1093 Region
*self_region
= new Region (region
);
1095 VisualTreeWalker
walker (this, ZReverse
);
1096 while (UIElement
*child
= walker
.Step ())
1097 child
->FrontToBack (region
, render_list
);
1099 if (!GetOpacityMask () && !IS_TRANSLUCENT (local_opacity
)) {
1101 if (GetRenderBounds().IsEmpty ()) { // empty bounds mean that this element draws nothing itself
1102 self_region
= new Region ();
1105 self_region
= new Region (region
);
1106 self_region
->Intersect (GetRenderBounds().RoundOut ()); // note the RoundOut
1109 self_region
->Intersect (GetSubtreeBounds().RoundOut ()); // note the RoundOut
1112 if (self_region
->IsEmpty() && render_list
->First() == cleanup_node
) {
1113 /* we don't intersect the surface region, and none of
1114 our children did either, remove the cleanup node */
1115 render_list
->Remove (render_list
->First());
1122 render_list
->Prepend (new RenderNode (this, self_region
, !self_region
->IsEmpty(), UIElement::CallPreRender
, NULL
));
1124 if (!self_region
->IsEmpty()) {
1125 if (((absolute_xform
.yx
== 0 && absolute_xform
.xy
== 0) /* no skew/rotation */
1126 || (absolute_xform
.xx
== 0 && absolute_xform
.yy
== 0)) /* allow 90 degree rotations */
1127 && can_subtract_self
)
1128 region
->Subtract (GetCoverageBounds ());
1136 UIElement::PreRender (cairo_t
*cr
, Region
*region
, bool skip_children
)
1138 double local_opacity
= GetOpacity ();
1142 cairo_set_matrix (cr
, &absolute_xform
);
1143 RenderClipPath (cr
);
1145 if (opacityMask
|| IS_TRANSLUCENT (local_opacity
)) {
1146 Rect r
= GetSubtreeBounds ().RoundOut();
1147 cairo_identity_matrix (cr
);
1149 // we need this check because ::PreRender can (and
1150 // will) be called for elements with empty regions.
1152 // The region passed in here is the redraw region
1153 // intersected with the render bounds of a given
1154 // element. For Panels with no width/height specified
1155 // in the xaml, this region will be empty. (check
1156 // panel.cpp::FrontToBack - we insert the ::PreRender
1157 // calling node if either the panel background or any
1158 // of the children intersect the redraw region.) We
1159 // can't clip to the empty region, obviously, as it
1160 // will keep all descendents from drawing to the
1163 if (!region
->IsEmpty()) {
1170 cairo_set_matrix (cr
, &absolute_xform
);
1173 if (ClipToExtents ()) {
1179 if (IS_TRANSLUCENT (local_opacity
))
1180 cairo_push_group (cr
);
1182 if (opacityMask
!= NULL
)
1183 cairo_push_group (cr
);
1187 UIElement::PostRender (cairo_t
*cr
, Region
*region
, bool front_to_back
)
1189 // if we didn't render front to back, then render the children here
1190 if (!front_to_back
) {
1191 VisualTreeWalker
walker (this, ZForward
);
1192 while (UIElement
*child
= walker
.Step ())
1193 child
->DoRender (cr
, region
);
1196 double local_opacity
= GetOpacity ();
1198 if (opacityMask
!= NULL
) {
1199 cairo_pattern_t
*mask
;
1200 cairo_pattern_t
*data
= cairo_pop_group (cr
);
1201 Point p
= GetOriginPoint ();
1202 Rect area
= Rect (p
.x
, p
.y
, 0.0, 0.0);
1203 GetSizeForBrush (cr
, &(area
.width
), &(area
.height
));
1204 opacityMask
->SetupBrush (cr
, area
);
1205 mask
= cairo_get_source (cr
);
1206 cairo_pattern_reference (mask
);
1207 cairo_set_source (cr
, data
);
1208 cairo_mask (cr
, mask
);
1209 cairo_pattern_destroy (mask
);
1210 cairo_pattern_destroy (data
);
1213 if (IS_TRANSLUCENT (local_opacity
)) {
1214 cairo_pop_group_to_source (cr
);
1215 cairo_paint_with_alpha (cr
, local_opacity
);
1220 if (moonlight_flags
& RUNTIME_INIT_SHOW_CLIPPING
) {
1222 cairo_new_path (cr
);
1223 cairo_set_matrix (cr
, &absolute_xform
);
1224 cairo_set_line_width (cr
, 1);
1226 Geometry
*geometry
= GetClip ();
1228 geometry
->Draw (cr
);
1229 cairo_set_source_rgba (cr
, 0.0, 1.0, 1.0, 1.0);
1233 geometry
= LayoutInformation::GetClip ((FrameworkElement
*)this);
1235 geometry
->Draw (cr
);
1236 cairo_set_source_rgba (cr
, 0.0, 0.0, 1.0, 1.0);
1243 if (moonlight_flags
& RUNTIME_INIT_SHOW_BOUNDING_BOXES
) {
1245 cairo_new_path (cr
);
1246 //RenderClipPath (cr);
1247 cairo_identity_matrix (cr
);
1248 cairo_set_source_rgba (cr
, 1.0, 0.5, 0.2, 1.0);
1249 cairo_set_line_width (cr
, 1);
1250 cairo_rectangle (cr
, bounds
.x
+ .5, bounds
.y
+ .5, bounds
.width
- .5, bounds
.height
- .5);
1257 UIElement::Paint (cairo_t
*ctx
, Region
*region
, cairo_matrix_t
*xform
)
1259 // FIXME xform is ignored for now
1261 g_warning ("passing a transform to UIElement::Paint is not yet supported");
1263 #if FRONT_TO_BACK_STATS
1264 uielements_rendered_front_to_back
= 0;
1265 uielements_rendered_back_to_front
= 0;
1268 bool did_front_to_back
= false;
1269 List
*render_list
= new List ();
1271 if (moonlight_flags
& RUNTIME_INIT_RENDER_FRONT_TO_BACK
) {
1272 Region
*copy
= new Region (region
);
1273 FrontToBack (copy
, render_list
);
1275 if (!render_list
->IsEmpty ()) {
1276 while (RenderNode
*node
= (RenderNode
*)render_list
->First()) {
1277 #if FRONT_TO_BACK_STATS
1278 uielements_rendered_front_to_back
++;
1282 render_list
->Remove (node
);
1285 did_front_to_back
= true;
1292 if (!did_front_to_back
) {
1293 DoRender (ctx
, region
);
1296 #if FRONT_TO_BACK_STATS
1297 printf ("UIElements rendered front-to-back for: %s(%p)\n", uielements_rendered_front_to_back
, GetName (), this);
1298 printf ("UIElements rendered back-to-front for: %s(%p)\n", uielements_rendered_back_to_front
, GetName (), this);
1303 UIElement::CallPreRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1305 element
->PreRender (cr
, region
, front_to_back
);
1309 UIElement::CallPostRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1311 element
->PostRender (cr
, region
, front_to_back
);
1315 UIElement::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
1317 /* do nothing by default */
1321 UIElement::GetSizeForBrush (cairo_t
*cr
, double *width
, double *height
)
1323 g_warning ("UIElement:GetSizeForBrush has been called. The derived class %s should have overridden it.",
1325 *height
= *width
= 0.0;
1329 UIElement::GetTimeManager ()
1331 Surface
*surface
= GetSurface ();
1332 Deployment
*deployment
;
1334 if (surface
== NULL
) {
1335 deployment
= GetDeployment ();
1336 if (deployment
!= NULL
)
1337 surface
= deployment
->GetSurface ();
1340 return surface
? surface
->GetTimeManager() : NULL
;
1344 UIElement::GetTransformToUIElementWithError (UIElement
*to_element
, MoonError
*error
)
1346 /* walk from this up to the root. if we hit null before we hit the toplevel, it's an error */
1347 UIElement
*visual
= this;
1350 if (visual
&& GetSurface()) {
1352 if (GetSurface()->IsTopLevel (visual
))
1354 visual
= visual
->GetVisualParent ();
1358 if (!ok
|| (to_element
&& !to_element
->GetSurface ())) {
1359 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1364 if (to_element
&& !to_element
->GetSurface()->IsTopLevel (to_element
)) {
1365 /* if @to_element is specified we also need to make sure there's a path to the root from it */
1367 visual
= to_element
->GetVisualParent ();
1368 if (visual
&& to_element
->GetSurface()) {
1370 if (to_element
->GetSurface()->IsTopLevel (visual
))
1372 visual
= visual
->GetVisualParent ();
1377 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1383 cairo_matrix_t result
;
1384 // A = From, B = To, M = what we want
1386 // => M = A * inv (B)
1388 cairo_matrix_t inverse
= to_element
->absolute_xform
;
1389 cairo_matrix_invert (&inverse
);
1390 cairo_matrix_multiply (&result
, &absolute_xform
, &inverse
);
1393 result
= absolute_xform
;
1396 Matrix
*matrix
= new Matrix (&result
);
1398 MatrixTransform
*transform
= new MatrixTransform ();
1399 transform
->SetValue (MatrixTransform::MatrixProperty
, matrix
);
1406 UIElement::TransformPoint (double *x
, double *y
)
1408 cairo_matrix_t inverse
= absolute_xform
;
1409 cairo_matrix_invert (&inverse
);
1411 cairo_matrix_transform_point (&inverse
, x
, y
);