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
);
50 cairo_matrix_init_identity (&layout_xform
);
51 cairo_matrix_init_identity (&local_xform
);
53 emitting_loaded
= false;
54 dirty_flags
= DirtyMeasure
;
55 up_dirty_node
= down_dirty_node
= NULL
;
56 force_invalidate_of_new_bounds
= false;
57 dirty_region
= new Region ();
59 desired_size
= Size (0, 0);
60 render_size
= Size (0, 0);
62 ComputeLocalTransform ();
63 ComputeTotalRenderVisibility ();
64 ComputeTotalHitTestVisibility ();
67 UIElement::~UIElement()
73 UIElement::IsSubtreeLoaded (UIElement
*element
)
75 while (element
&& !element
->IsLoaded ())
76 element
= element
->GetVisualParent ();
83 TriggerCollection
*triggers
= GetTriggers ();
85 if (triggers
!= NULL
) {
86 for (int i
= 0; i
< triggers
->GetCount (); i
++)
87 triggers
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
91 VisualTreeWalker
walker (this);
92 while (UIElement
*child
= walker
.Step ())
93 child
->SetVisualParent (NULL
);
97 subtree_object
->unref ();
98 subtree_object
= NULL
;
101 DependencyObject::Dispose();
105 UIElement::SetIsAttached (bool value
)
107 if (IsAttached () == value
)
110 if (!value
&& IsAttached ()) {
111 /* we're losing our surface, delete ourselves from the dirty list if we're on it */
112 Surface
*surface
= GetDeployment ()->GetSurface ();
114 surface
->RemoveDirtyElement (this);
117 if (subtree_object
!= NULL
&& subtree_object
->Is(Type::UIELEMENT
))
118 subtree_object
->SetIsAttached (value
);
120 DependencyObject::SetIsAttached (value
);
124 UIElement::IntersectBoundsWithClipPath (Rect unclipped
, bool transform
)
126 Geometry
*clip
= GetClip ();
127 Geometry
*layout_clip
= transform
? NULL
: LayoutInformation::GetLayoutClip (this);
130 if (!clip
&& !layout_clip
)
134 box
= clip
->GetBounds ();
136 box
= layout_clip
->GetBounds ();
139 box
= box
.Intersection (layout_clip
->GetBounds());
141 if (!GetRenderVisible())
142 box
= Rect (0,0,0,0);
145 box
= box
.Transform (&absolute_xform
);
147 return box
.Intersection (unclipped
);
151 UIElement::RenderClipPath (cairo_t
*cr
, bool path_only
)
154 cairo_set_matrix (cr
, &absolute_xform
);
156 Geometry
*geometry
= GetClip();
166 UIElement::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
168 if (args
->GetProperty ()->GetOwnerType() != Type::UIELEMENT
) {
169 DependencyObject::OnPropertyChanged (args
, error
);
173 if (args
->GetId () == UIElement::OpacityProperty
) {
174 InvalidateVisibility ();
175 } else if (args
->GetId () == UIElement::VisibilityProperty
) {
176 // note: invalid enum values are only validated in 1.1 (managed code),
177 // the default value for VisibilityProperty is VisibilityCollapsed
178 // (see bug #340799 for more details)
179 if (args
->GetNewValue()->AsInt32() == VisibilityVisible
)
180 flags
|= UIElement::RENDER_VISIBLE
;
182 flags
&= ~UIElement::RENDER_VISIBLE
;
184 InvalidateVisibility ();
185 InvalidateMeasure ();
186 if (GetVisualParent ())
187 GetVisualParent ()->InvalidateMeasure ();
188 } else if (args
->GetId () == UIElement::IsHitTestVisibleProperty
) {
189 if (args
->GetNewValue()->AsBool())
190 flags
|= UIElement::HIT_TEST_VISIBLE
;
192 flags
&= ~UIElement::HIT_TEST_VISIBLE
;
194 UpdateTotalHitTestVisibility();
195 } else if (args
->GetId () == UIElement::ClipProperty
) {
197 } else if (args
->GetId () == UIElement::OpacityMaskProperty
) {
198 opacityMask
= args
->GetNewValue() ? args
->GetNewValue()->AsBrush() : NULL
;
200 } else if (args
->GetId () == UIElement::RenderTransformProperty
201 || args
->GetId () == UIElement::RenderTransformOriginProperty
) {
204 else if (args
->GetId () == UIElement::TriggersProperty
) {
205 if (args
->GetOldValue()) {
206 // remove the old trigger targets
207 TriggerCollection
*triggers
= args
->GetOldValue()->AsTriggerCollection();
208 for (int i
= 0; i
< triggers
->GetCount (); i
++)
209 triggers
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
212 if (args
->GetNewValue()) {
214 TriggerCollection
*triggers
= args
->GetNewValue()->AsTriggerCollection();
215 for (int i
= 0; i
< triggers
->GetCount (); i
++)
216 triggers
->GetValueAt (i
)->AsEventTrigger ()->SetTarget (this);
218 } else if (args
->GetId () == UIElement::UseLayoutRoundingProperty
) {
219 InvalidateMeasure ();
220 InvalidateArrange ();
223 NotifyListenersOfPropertyChange (args
, error
);
227 UIElement::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
229 if (col
== GetTriggers ()) {
230 switch (args
->GetChangedAction()) {
231 case CollectionChangedActionReplace
:
232 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
234 case CollectionChangedActionAdd
:
235 args
->GetNewItem()->AsEventTrigger ()->SetTarget (this);
237 case CollectionChangedActionRemove
:
238 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
240 case CollectionChangedActionClearing
:
241 for (int i
= 0; i
< col
->GetCount (); i
++)
242 col
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
244 case CollectionChangedActionCleared
:
245 // nothing needed here.
250 DependencyObject::OnCollectionChanged (col
, args
);
256 UIElement::DumpHierarchy (UIElement
*obj
)
261 int n
= DumpHierarchy (obj
->GetVisualParent ());
262 for (int i
= 0; i
< n
; i
++)
264 printf ("%s (%p)\n", obj
->GetTypeName(), obj
);
270 UIElement::UpdateBounds (bool force_redraw
)
272 //InvalidateMeasure ();
273 //InvalidateArrange ();
276 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyBounds
);
278 force_invalidate_of_new_bounds
|= force_redraw
;
282 UIElement::UpdateTotalRenderVisibility ()
285 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyRenderVisibility
);
289 UIElement::UpdateTotalHitTestVisibility ()
291 VisualTreeWalker
walker (this);
292 while (UIElement
*child
= walker
.Step ())
293 child
->UpdateTotalHitTestVisibility ();
296 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyHitTestVisibility
);
300 UIElement::GetActualTotalRenderVisibility ()
302 bool visible
= (flags
& UIElement::RENDER_VISIBLE
) != 0;
303 bool parent_visible
= true;
305 total_opacity
= GetOpacity ();
307 if (GetVisualParent ()) {
308 GetVisualParent ()->ComputeTotalRenderVisibility ();
309 parent_visible
= visible
&& GetVisualParent ()->GetRenderVisible ();
310 total_opacity
*= GetVisualParent ()->total_opacity
;
313 visible
= visible
&& parent_visible
;
319 UIElement::ComputeTotalRenderVisibility ()
321 if (GetActualTotalRenderVisibility ())
322 flags
|= UIElement::TOTAL_RENDER_VISIBLE
;
324 flags
&= ~UIElement::TOTAL_RENDER_VISIBLE
;
328 UIElement::GetActualTotalHitTestVisibility ()
330 bool visible
= (flags
& UIElement::HIT_TEST_VISIBLE
) != 0;
332 if (visible
&& GetVisualParent ()) {
333 GetVisualParent ()->ComputeTotalHitTestVisibility ();
334 visible
= visible
&& GetVisualParent ()->GetHitTestVisible ();
341 UIElement::ComputeTotalHitTestVisibility ()
343 if (GetActualTotalHitTestVisibility ())
344 flags
|= UIElement::TOTAL_HIT_TEST_VISIBLE
;
346 flags
&= ~UIElement::TOTAL_HIT_TEST_VISIBLE
;
350 UIElement::UpdateTransform ()
353 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyLocalTransform
);
358 UIElement::ComputeLocalTransform ()
360 Transform
*transform
= GetRenderTransform ();
361 Point transform_origin
= GetTransformOrigin ();
362 cairo_matrix_t render
;
364 cairo_matrix_init_identity (&render
);
365 cairo_matrix_init_identity (&local_xform
);
367 if (transform
== NULL
)
370 transform
->GetTransform (&render
);
371 cairo_matrix_translate (&local_xform
, transform_origin
.x
, transform_origin
.y
);
372 cairo_matrix_multiply (&local_xform
, &render
, &local_xform
);
373 cairo_matrix_translate (&local_xform
, -transform_origin
.x
, -transform_origin
.y
);
377 UIElement::TransformBounds (cairo_matrix_t
*old
, cairo_matrix_t
*current
)
381 cairo_matrix_t tween
= *old
;
382 cairo_matrix_invert (&tween
);
383 cairo_matrix_multiply (&tween
, &tween
, current
);
390 p0
= p0
- p0
.Transform (&tween
);
391 p1
= p1
- p1
.Transform (&tween
);
392 p2
= p2
- p2
.Transform (&tween
);
393 p3
= p3
- p3
.Transform (&tween
);
395 if (p0
== p1
&& p1
== p2
&& p2
== p3
) {
396 //printf ("shifting position\n");
397 ShiftPosition (bounds
.GetTopLeft ().Transform (&tween
));
405 UIElement::ComputeTransform ()
407 cairo_matrix_t old
= absolute_xform
;
408 cairo_matrix_init_identity (&absolute_xform
);
410 if (GetVisualParent () != NULL
) {
411 absolute_xform
= GetVisualParent ()->absolute_xform
;
412 } else if (GetParent () != NULL
&& GetParent ()->Is (Type::POPUP
)) {
413 // FIXME we shouldn't be examing a subclass type here but we'll do this
414 // for now to get popups in something approaching the right place while
415 // we figure out a cleaner way to handle it long term.
416 Popup
*popup
= (Popup
*)GetParent ();
417 absolute_xform
= popup
->absolute_xform
;
418 cairo_matrix_translate (&absolute_xform
, popup
->GetHorizontalOffset (), popup
->GetVerticalOffset ());
421 cairo_matrix_multiply (&absolute_xform
, &layout_xform
, &absolute_xform
);
422 cairo_matrix_multiply (&absolute_xform
, &local_xform
, &absolute_xform
);
424 if (moonlight_flags
& RUNTIME_INIT_USE_UPDATE_POSITION
)
425 TransformBounds (&old
, &absolute_xform
);
432 UIElement::ComputeBounds ()
434 g_warning ("UIElement:ComputeBounds has been called. The derived class %s should have overridden it.",
439 UIElement::ShiftPosition (Point p
)
446 UIElement::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
448 if (prop
&& prop
->GetId () == UIElement::RenderTransformProperty
) {
451 else if (prop
&& prop
->GetId () == UIElement::ClipProperty
) {
454 else if (prop
&& prop
->GetId () == UIElement::OpacityMaskProperty
) {
458 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
462 UIElement::CacheInvalidateHint ()
464 VisualTreeWalker
walker (this);
465 while (UIElement
*child
= walker
.Step ())
466 child
->CacheInvalidateHint ();
470 UIElement::SetVisualParent (UIElement
*visual_parent
)
472 this->visual_parent
= visual_parent
;
474 if (visual_parent
&& visual_parent
->IsAttached () != IsAttached ())
475 SetIsAttached (visual_parent
->IsAttached ());
479 UIElement::SetSubtreeObject (DependencyObject
*value
)
481 if (subtree_object
== value
)
485 subtree_object
->unref ();
487 subtree_object
= value
;
490 subtree_object
->ref ();
494 UIElement::ElementRemoved (UIElement
*item
)
496 // Invalidate ourself in the size of the item's subtree
497 Invalidate (item
->GetSubtreeBounds());
500 GetDeployment ()->GetSurface ()->RemoveDirtyElement (item
);
501 item
->SetVisualParent (NULL
);
502 item
->CacheInvalidateHint ();
503 item
->ClearLoaded ();
505 Rect
emptySlot (0,0,0,0);
506 LayoutInformation::SetLayoutSlot (item
, &emptySlot
);
507 item
->ClearValue (LayoutInformation::LayoutClipProperty
);
509 InvalidateMeasure ();
513 UIElement::ElementAdded (UIElement
*item
)
515 item
->SetVisualLevel (GetVisualLevel() + 1);
516 item
->SetVisualParent (this);
517 item
->UpdateTotalRenderVisibility ();
518 item
->UpdateTotalHitTestVisibility ();
519 //item->UpdateBounds (true);
522 if (0 != (flags
& (UIElement::IS_LOADED
| UIElement::PENDING_LOADED
))) {
523 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (item
);
527 item
->WalkTreeForLoadedHandlers (&post
, true, false);
530 Deployment::GetCurrent()->PostLoaded ();
535 InvalidateMeasure ();
536 ClearValue (LayoutInformation::LayoutClipProperty
);
537 ClearValue (LayoutInformation::PreviousConstraintProperty
);
538 item
->SetRenderSize (Size (0,0));
539 item
->UpdateTransform ();
540 item
->InvalidateMeasure ();
541 item
->InvalidateArrange ();
542 if (item
->ReadLocalValue (LayoutInformation::LastRenderSizeProperty
))
543 PropagateFlagUp (DIRTY_SIZE_HINT
);
547 UIElement::InvalidateMeasure ()
549 dirty_flags
|= DirtyMeasure
;
550 PropagateFlagUp (DIRTY_MEASURE_HINT
);
554 UIElement::InvalidateArrange ()
556 dirty_flags
|= DirtyArrange
;
557 PropagateFlagUp (DIRTY_ARRANGE_HINT
);
561 UIElement::DoMeasure ()
563 Size
*last
= LayoutInformation::GetPreviousConstraint (this);
564 UIElement
*parent
= GetVisualParent ();
565 Size
infinite (INFINITY
, INFINITY
);
567 if (!IsAttached () && !last
&& !parent
&& IsLayoutContainer ()) {
572 Size previous_desired
= GetDesiredSize ();
574 // This will be a noop on non layout elements
577 if (previous_desired
== GetDesiredSize ())
581 // a canvas doesn't care about the child size changing like this
583 parent
->InvalidateMeasure ();
585 dirty_flags
&= ~DirtyMeasure
;
589 UIElement::DoArrange ()
591 Rect
*last
= LayoutInformation::GetLayoutSlot (this);
592 Size previous_render
= Size ();
593 UIElement
*parent
= GetVisualParent ();
597 Size desired
= Size ();
598 Size available
= Size ();
599 Surface
*surface
= GetDeployment ()->GetSurface ();
601 if (IsLayoutContainer ()) {
602 desired
= GetDesiredSize ();
603 if (IsAttached () && surface
->IsTopLevel (this) && !GetParent ()) {
604 Size
*measure
= LayoutInformation::GetPreviousConstraint (this);
606 desired
= desired
.Max (*LayoutInformation::GetPreviousConstraint (this));
608 desired
= Size (surface
->GetWindow ()->GetWidth (), surface
->GetWindow ()->GetHeight ());
611 FrameworkElement
*fe
= (FrameworkElement
*)this;
612 desired
= Size (fe
->GetActualWidth (), fe
->GetActualHeight ());
615 viewport
= Rect (Canvas::GetLeft (this),
616 Canvas::GetTop (this),
617 desired
.width
, desired
.height
);
626 parent
->InvalidateArrange ();
631 UIElement::InsideClip (cairo_t
*cr
, double x
, double y
)
642 TransformPoint (&nx
, &ny
);
644 if (!clip
->GetBounds ().PointInside (nx
, ny
))
651 inside
= cairo_in_fill (cr
, nx
, ny
);
659 UIElement::InsideObject (cairo_t
*cr
, double x
, double y
)
661 return InsideClip (cr
, x
, y
);
665 UIElement::AddHandler (int event_id
, EventHandler handler
, gpointer data
, GDestroyNotify data_dtor
)
667 int rv
= DependencyObject::AddHandler (event_id
, handler
, data
, data_dtor
);
668 if (event_id
== UIElement::LoadedEvent
) {
669 UIElement
*el
= this;
670 while (el
&& el
->HasBeenWalkedForLoaded ()) {
671 el
->ClearWalkedForLoaded ();
672 el
= el
->GetVisualParent ();
679 UIElement::PropagateFlagUp (UIElementFlags flag
)
683 while ((e
= e
->GetVisualParent ()) && !e
->HasFlag (flag
)) {
689 UIElement::RemoveHandler (int event_id
, EventHandler handler
, gpointer data
)
691 int token
= DependencyObject::RemoveHandler (event_id
, handler
, data
);
693 if (event_id
== UIElement::LoadedEvent
&& token
!= -1)
694 Deployment::GetCurrent()->RemoveLoadedHandler (this, token
);
700 UIElement::RemoveHandler (int event_id
, int token
)
702 DependencyObject::RemoveHandler (event_id
, token
);
704 if (event_id
== UIElement::LoadedEvent
)
705 Deployment::GetCurrent()->RemoveLoadedHandler (this, token
);
713 UIElement::WalkTreeForLoadedHandlers (bool *post
, bool only_unemitted
, bool force_walk_up
)
715 List
*walk_list
= new List();
716 List
*subtree_list
= new List ();
718 bool post_loaded
= false;
719 Deployment
*deployment
= GetDeployment ();
720 Application
*application
= deployment
->GetCurrentApplication ();
722 DeepTreeWalker
*walker
= new DeepTreeWalker (this);
724 // we need to make sure to apply the default style to all
725 // controls in the subtree
726 while (UIElement
*element
= (UIElement
*)walker
->Step ()) {
731 if (element
->HasBeenWalkedForLoaded ()) {
732 walker
->SkipBranch ();
736 if (element
->Is(Type::CONTROL
)) {
737 Control
*control
= (Control
*)element
;
738 if (!control
->default_style_applied
) {
739 ManagedTypeInfo
*key
= control
->GetDefaultStyleKey ();
741 if (application
== NULL
)
742 g_warning ("attempting to use a null application when applying default style when emitting Loaded event.");
744 application
->ApplyDefaultStyle (control
, key
);
748 if (!control
->GetTemplateRoot () /* we only need to worry about this if the template hasn't been expanded */
749 && control
->GetTemplate())
750 post_loaded
= true; //XXX do we need this? control->ReadLocalValue (Control::TemplateProperty) == NULL;
753 element
->flags
|= UIElement::PENDING_LOADED
;
754 element
->OnLoaded ();
755 if (element
->HasHandlers (UIElement::LoadedEvent
)) {
757 subtree_list
->Prepend (new UIElementNode (element
));
759 element
->SetWalkedForLoaded ();
762 if (force_walk_up
|| !post_loaded
|| HasHandlers (UIElement::LoadedEvent
)) {
763 // we need to walk back up to the root to collect all loaded events
764 UIElement
*parent
= this;
765 while (parent
->GetVisualParent())
766 parent
= parent
->GetVisualParent();
768 walker
= new DeepTreeWalker (parent
, Logical
/*Reverse*/);
770 while (UIElement
*element
= (UIElement
*)walker
->Step ()) {
775 if (element
== this) {
776 // we already walked this, so add our subtree list here.
777 walk_list
->Prepend (subtree_list
);
778 subtree_list
->Clear (false);
779 walker
->SkipBranch ();
781 else if (element
->HasBeenWalkedForLoaded ()) {
782 walker
->SkipBranch ();
785 walk_list
->Prepend (new UIElementNode (element
));
786 element
->SetWalkedForLoaded ();
790 // if we didn't add the subtree's loaded handlers
791 // (because somewhere up above the subtree we skipped
792 // the branch) add it here.
793 if (walk_list
->IsEmpty ()) {
794 walk_list
->Prepend (subtree_list
);
795 subtree_list
->Clear (false);
799 // otherwise we only copy the events from the subtree
800 walk_list
->Prepend (subtree_list
);
801 subtree_list
->Clear (false);
804 while (UIElementNode
*ui
= (UIElementNode
*)walk_list
->First ()) {
805 // remove it from the walk list
806 walk_list
->Unlink (ui
);
808 deployment
->AddAllLoadedHandlers (ui
->uielement
, only_unemitted
);
823 UIElement::OnLoaded ()
825 flags
|= UIElement::IS_LOADED
;
826 flags
&= ~UIElement::PENDING_LOADED
;
830 UIElement::ClearLoaded ()
833 Surface
*s
= Deployment::GetCurrent ()->GetSurface ();
834 if (s
->GetFocusedElement () == this)
835 s
->FocusElement (NULL
);
837 ClearForeachGeneration (UIElement::LoadedEvent
);
838 ClearWalkedForLoaded ();
843 flags
&= ~UIElement::IS_LOADED
;
845 VisualTreeWalker
walker (this);
846 while ((e
= walker
.Step ()))
851 UIElement::Focus (bool recurse
)
857 // Queues the invalidate for the current region, performs any
858 // updates to the RenderTransform (optional) and queues a
859 // new redraw with the new bounding box
862 UIElement::FullInvalidate (bool rendertransform
)
867 UpdateBounds (true /* force an invalidate here, even if the bounds don't change */);
871 UIElement::Invalidate (Rect r
)
873 if (!GetRenderVisible() || IS_INVISIBLE(total_opacity
))
876 #ifdef DEBUG_INVALIDATE
877 printf ("Requesting invalidate for object %p %s (%s) at %f %f - %f %f\n",
878 this, GetName(), GetTypeName(),
885 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyInvalidate
);
887 dirty_region
->Union (r
);
889 GetTimeManager()->NeedRedraw ();
891 Emit (InvalidatedEvent
);
896 UIElement::Invalidate (Region
*region
)
898 if (!GetRenderVisible () || IS_INVISIBLE (total_opacity
))
902 GetDeployment ()->GetSurface ()->AddDirtyElement (this, DirtyInvalidate
);
904 dirty_region
->Union (region
);
906 GetTimeManager()->NeedRedraw ();
908 Emit (InvalidatedEvent
);
913 UIElement::Invalidate ()
920 UIElement::InvalidatePaint ()
927 UIElement::InvalidateSubtreePaint ()
929 Invalidate (GetSubtreeBounds ());
933 UIElement::InvalidateClip ()
935 InvalidateSubtreePaint ();
940 UIElement::InvalidateMask ()
942 InvalidateSubtreePaint ();
946 UIElement::InvalidateVisibility ()
948 UpdateTotalRenderVisibility ();
949 InvalidateSubtreePaint ();
954 UIElement::InvalidateIntrisicSize ()
956 InvalidateMeasure ();
957 InvalidateArrange ();
963 UIElement::HitTest (cairo_t
*cr
, Point p
, List
*uielement_list
)
965 uielement_list
->Prepend (new UIElementNode (this));
969 UIElement::HitTest (cairo_t
*cr
, Rect r
, List
*uielement_list
)
974 UIElement::FindElementsInHostCoordinates_p (Point p
, HitTestCollection
*uielement_list
)
976 List
*list
= new List ();
977 cairo_t
*ctx
= measuring_context_create ();
979 FindElementsInHostCoordinates (ctx
, p
, list
);
981 UIElementNode
*node
= (UIElementNode
*) list
->First ();
983 uielement_list
->Add (new Value (node
->uielement
));
984 node
= (UIElementNode
*) node
->next
;
988 measuring_context_destroy (ctx
);
993 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Point P
, List
*uielement_list
)
995 uielement_list
->Prepend (new UIElementNode (this));
1000 UIElement::FindElementsInHostCoordinates_r (Rect r
, HitTestCollection
*uielement_list
)
1002 List
*list
= new List ();
1003 cairo_t
*ctx
= measuring_context_create ();
1005 FindElementsInHostCoordinates (ctx
, r
, list
);
1007 UIElementNode
*node
= (UIElementNode
*) list
->First ();
1009 uielement_list
->Add (new Value (node
->uielement
));
1010 node
= (UIElementNode
*) node
->next
;
1014 measuring_context_destroy (ctx
);
1018 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Rect r
, List
*uielement_list
)
1020 uielement_list
->Prepend (new UIElementNode (this));
1024 UIElement::EmitKeyDown (GdkEventKey
*event
)
1026 return Emit (KeyDownEvent
, new KeyEventArgs (event
));
1030 UIElement::EmitKeyUp (GdkEventKey
*event
)
1032 return Emit (KeyUpEvent
, new KeyEventArgs (event
));
1036 UIElement::EmitGotFocus ()
1038 return Emit (GotFocusEvent
, new RoutedEventArgs (this));
1042 UIElement::EmitLostFocus ()
1044 return Emit (LostFocusEvent
, new RoutedEventArgs (this));
1048 UIElement::EmitLostMouseCapture ()
1050 MouseEventArgs
*e
= new MouseEventArgs ();
1051 e
->SetSource (this);
1052 return Emit (LostMouseCaptureEvent
, e
);
1056 UIElement::CaptureMouse ()
1061 return GetDeployment ()->GetSurface ()->SetMouseCapture (this);
1065 UIElement::ReleaseMouseCapture ()
1070 GetDeployment ()->GetSurface ()->ReleaseMouseCapture (this);
1074 UIElement::DoRender (cairo_t
*cr
, Region
*parent_region
)
1076 Region
*region
= new Region (GetSubtreeBounds ());
1077 region
->Intersect (parent_region
);
1079 if (!GetRenderVisible() || IS_INVISIBLE (total_opacity
) || region
->IsEmpty ()) {
1084 #if FRONT_TO_BACK_STATS
1085 GetSurface()->uielements_rendered_back_to_front
++;
1088 STARTTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1090 PreRender (cr
, region
, false);
1092 Render (cr
, region
);
1094 PostRender (cr
, region
, false);
1096 ENDTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1102 UIElement::UseBackToFront ()
1104 return VisualTreeWalker (this).GetCount () < MIN_FRONT_TO_BACK_COUNT
;
1108 UIElement::FrontToBack (Region
*surface_region
, List
*render_list
)
1110 double local_opacity
= GetOpacity ();
1112 if (surface_region
->RectIn (GetSubtreeBounds().RoundOut()) == GDK_OVERLAP_RECTANGLE_OUT
)
1115 if (!GetRenderVisible ()
1116 || IS_INVISIBLE (local_opacity
))
1119 if (!UseBackToFront ()) {
1120 Region
*self_region
= new Region (surface_region
);
1121 self_region
->Intersect (GetSubtreeBounds().RoundOut());
1123 // we need to include our children in this one, since
1124 // we'll be rendering them in the PostRender method.
1125 if (!self_region
->IsEmpty())
1126 render_list
->Prepend (new RenderNode (this, self_region
, true,
1127 UIElement::CallPreRender
, UIElement::CallPostRender
));
1128 // don't remove the region from surface_region because
1129 // there are likely holes in it
1135 bool can_subtract_self
;
1138 && !GetOpacityMask ()
1139 && !IS_TRANSLUCENT (GetOpacity ())) {
1140 region
= surface_region
;
1141 delete_region
= false;
1142 can_subtract_self
= true;
1145 region
= new Region (surface_region
);
1146 delete_region
= true;
1147 can_subtract_self
= false;
1150 RenderNode
*cleanup_node
= new RenderNode (this, NULL
, false, NULL
, UIElement::CallPostRender
);
1152 render_list
->Prepend (cleanup_node
);
1154 Region
*self_region
= new Region (region
);
1156 VisualTreeWalker
walker (this, ZReverse
);
1157 while (UIElement
*child
= walker
.Step ())
1158 child
->FrontToBack (region
, render_list
);
1160 if (!GetOpacityMask () && !IS_TRANSLUCENT (local_opacity
)) {
1162 if (GetRenderBounds().IsEmpty ()) { // empty bounds mean that this element draws nothing itself
1163 self_region
= new Region ();
1166 self_region
= new Region (region
);
1167 self_region
->Intersect (GetRenderBounds().RoundOut ()); // note the RoundOut
1170 self_region
->Intersect (GetSubtreeBounds().RoundOut ()); // note the RoundOut
1173 if (self_region
->IsEmpty() && render_list
->First() == cleanup_node
) {
1174 /* we don't intersect the surface region, and none of
1175 our children did either, remove the cleanup node */
1176 render_list
->Remove (render_list
->First());
1183 render_list
->Prepend (new RenderNode (this, self_region
, !self_region
->IsEmpty(), UIElement::CallPreRender
, NULL
));
1185 if (!self_region
->IsEmpty()) {
1186 if (((absolute_xform
.yx
== 0 && absolute_xform
.xy
== 0) /* no skew/rotation */
1187 || (absolute_xform
.xx
== 0 && absolute_xform
.yy
== 0)) /* allow 90 degree rotations */
1188 && can_subtract_self
)
1189 region
->Subtract (GetCoverageBounds ());
1197 UIElement::PreRender (cairo_t
*cr
, Region
*region
, bool skip_children
)
1199 double local_opacity
= GetOpacity ();
1203 cairo_set_matrix (cr
, &absolute_xform
);
1204 RenderClipPath (cr
);
1206 if (opacityMask
|| IS_TRANSLUCENT (local_opacity
)) {
1207 Rect r
= GetSubtreeBounds ().RoundOut();
1208 cairo_identity_matrix (cr
);
1210 // we need this check because ::PreRender can (and
1211 // will) be called for elements with empty regions.
1213 // The region passed in here is the redraw region
1214 // intersected with the render bounds of a given
1215 // element. For Panels with no width/height specified
1216 // in the xaml, this region will be empty. (check
1217 // panel.cpp::FrontToBack - we insert the ::PreRender
1218 // calling node if either the panel background or any
1219 // of the children intersect the redraw region.) We
1220 // can't clip to the empty region, obviously, as it
1221 // will keep all descendents from drawing to the
1224 if (!region
->IsEmpty()) {
1231 cairo_set_matrix (cr
, &absolute_xform
);
1234 if (ClipToExtents ()) {
1240 if (IS_TRANSLUCENT (local_opacity
))
1241 cairo_push_group (cr
);
1243 if (opacityMask
!= NULL
)
1244 cairo_push_group (cr
);
1248 UIElement::PostRender (cairo_t
*cr
, Region
*region
, bool front_to_back
)
1250 // if we didn't render front to back, then render the children here
1251 if (!front_to_back
) {
1252 VisualTreeWalker
walker (this, ZForward
);
1253 while (UIElement
*child
= walker
.Step ())
1254 child
->DoRender (cr
, region
);
1257 double local_opacity
= GetOpacity ();
1259 if (opacityMask
!= NULL
) {
1260 cairo_pattern_t
*data
= cairo_pop_group (cr
);
1261 if (cairo_pattern_status (data
) == CAIRO_STATUS_SUCCESS
) {
1262 cairo_pattern_t
*mask
= NULL
;
1263 Point p
= GetOriginPoint ();
1264 Rect area
= Rect (p
.x
, p
.y
, 0.0, 0.0);
1265 GetSizeForBrush (cr
, &(area
.width
), &(area
.height
));
1266 opacityMask
->SetupBrush (cr
, area
);
1267 mask
= cairo_get_source (cr
);
1268 cairo_pattern_reference (mask
);
1269 cairo_set_source (cr
, data
);
1270 cairo_mask (cr
, mask
);
1271 cairo_pattern_destroy (mask
);
1273 cairo_pattern_destroy (data
);
1276 if (IS_TRANSLUCENT (local_opacity
)) {
1277 cairo_pattern_t
*data
= cairo_pop_group (cr
);
1278 if (cairo_pattern_status (data
) == CAIRO_STATUS_SUCCESS
) {
1279 cairo_set_source (cr
, data
);
1280 cairo_paint_with_alpha (cr
, local_opacity
);
1282 cairo_pattern_destroy (data
);
1287 if (moonlight_flags
& RUNTIME_INIT_SHOW_CLIPPING
) {
1289 cairo_new_path (cr
);
1290 cairo_set_matrix (cr
, &absolute_xform
);
1291 cairo_set_line_width (cr
, 1);
1293 Geometry
*geometry
= GetClip ();
1295 geometry
->Draw (cr
);
1296 cairo_set_source_rgba (cr
, 0.0, 1.0, 1.0, 1.0);
1300 geometry
= LayoutInformation::GetClip ((FrameworkElement
*)this);
1302 geometry
->Draw (cr
);
1303 cairo_set_source_rgba (cr
, 0.0, 0.0, 1.0, 1.0);
1310 if (moonlight_flags
& RUNTIME_INIT_SHOW_BOUNDING_BOXES
) {
1312 cairo_new_path (cr
);
1313 //RenderClipPath (cr);
1314 cairo_identity_matrix (cr
);
1315 cairo_set_source_rgba (cr
, 1.0, 0.5, 0.2, 1.0);
1316 cairo_set_line_width (cr
, 1);
1317 cairo_rectangle (cr
, bounds
.x
+ .5, bounds
.y
+ .5, bounds
.width
- .5, bounds
.height
- .5);
1324 UIElement::Paint (cairo_t
*ctx
, Region
*region
, cairo_matrix_t
*xform
)
1326 // FIXME xform is ignored for now
1328 g_warning ("passing a transform to UIElement::Paint is not yet supported");
1330 #if FRONT_TO_BACK_STATS
1331 uielements_rendered_front_to_back
= 0;
1332 uielements_rendered_back_to_front
= 0;
1335 bool did_front_to_back
= false;
1336 List
*render_list
= new List ();
1338 if (moonlight_flags
& RUNTIME_INIT_RENDER_FRONT_TO_BACK
) {
1339 Region
*copy
= new Region (region
);
1340 FrontToBack (copy
, render_list
);
1342 if (!render_list
->IsEmpty ()) {
1343 while (RenderNode
*node
= (RenderNode
*)render_list
->First()) {
1344 #if FRONT_TO_BACK_STATS
1345 uielements_rendered_front_to_back
++;
1349 render_list
->Remove (node
);
1352 did_front_to_back
= true;
1359 if (!did_front_to_back
) {
1360 DoRender (ctx
, region
);
1363 #if FRONT_TO_BACK_STATS
1364 printf ("UIElements rendered front-to-back for: %s(%p)\n", uielements_rendered_front_to_back
, GetName (), this);
1365 printf ("UIElements rendered back-to-front for: %s(%p)\n", uielements_rendered_back_to_front
, GetName (), this);
1370 UIElement::CallPreRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1372 element
->PreRender (cr
, region
, front_to_back
);
1376 UIElement::CallPostRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1378 element
->PostRender (cr
, region
, front_to_back
);
1382 UIElement::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
1384 /* do nothing by default */
1388 UIElement::GetSizeForBrush (cairo_t
*cr
, double *width
, double *height
)
1390 g_warning ("UIElement:GetSizeForBrush has been called. The derived class %s should have overridden it.",
1392 *height
= *width
= 0.0;
1396 UIElement::GetTimeManager ()
1398 return GetDeployment ()->GetSurface ()->GetTimeManager ();
1402 UIElement::GetTransformToUIElementWithError (UIElement
*to_element
, MoonError
*error
)
1404 /* walk from this up to the root. if we hit null before we hit the toplevel, it's an error */
1405 UIElement
*visual
= this;
1408 if (visual
&& IsAttached ()) {
1410 if (GetDeployment ()->GetSurface()->IsTopLevel (visual
))
1412 visual
= visual
->GetVisualParent ();
1416 if (!ok
|| (to_element
&& !to_element
->IsAttached ())) {
1417 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1422 if (to_element
&& !GetDeployment ()->GetSurface()->IsTopLevel (to_element
)) {
1423 /* if @to_element is specified we also need to make sure there's a path to the root from it */
1425 visual
= to_element
->GetVisualParent ();
1426 if (visual
&& to_element
->IsAttached ()) {
1428 if (GetDeployment ()->GetSurface()->IsTopLevel (visual
))
1430 visual
= visual
->GetVisualParent ();
1435 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1441 cairo_matrix_t result
;
1442 // A = From, B = To, M = what we want
1444 // => M = A * inv (B)
1446 cairo_matrix_t inverse
= to_element
->absolute_xform
;
1447 cairo_matrix_invert (&inverse
);
1448 cairo_matrix_multiply (&result
, &absolute_xform
, &inverse
);
1451 result
= absolute_xform
;
1454 Matrix
*matrix
= new Matrix (&result
);
1456 MatrixTransform
*transform
= new MatrixTransform ();
1457 transform
->SetValue (MatrixTransform::MatrixProperty
, matrix
);
1464 UIElement::TransformPoint (double *x
, double *y
)
1466 cairo_matrix_t inverse
= absolute_xform
;
1467 cairo_matrix_invert (&inverse
);
1469 cairo_matrix_transform_point (&inverse
, x
, y
);