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 bounds
= Rect (0,0,0,0);
48 cairo_matrix_init_identity (&absolute_xform
);
50 emitting_loaded
= false;
51 dirty_flags
= DirtyMeasure
;
52 up_dirty_node
= down_dirty_node
= NULL
;
53 force_invalidate_of_new_bounds
= false;
54 dirty_region
= new Region ();
56 desired_size
= Size (0, 0);
57 render_size
= Size (0, 0);
59 ComputeLocalTransform ();
60 ComputeTotalRenderVisibility ();
61 ComputeTotalHitTestVisibility ();
64 UIElement::~UIElement()
70 UIElement::IsSubtreeLoaded (UIElement
*element
)
72 while (element
&& !element
->IsLoaded ())
73 element
= element
->GetVisualParent ();
80 TriggerCollection
*triggers
= GetTriggers ();
82 if (triggers
!= NULL
) {
83 for (int i
= 0; i
< triggers
->GetCount (); i
++)
84 triggers
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
88 VisualTreeWalker
walker (this);
89 while (UIElement
*child
= walker
.Step ())
90 child
->SetVisualParent (NULL
);
94 subtree_object
->unref ();
95 subtree_object
= NULL
;
98 DependencyObject::Dispose();
102 UIElement::SetSurface (Surface
*s
)
104 if (GetSurface() == s
)
107 if (s
== NULL
&& GetSurface()) {
108 /* we're losing our surface, delete ourselves from the dirty list if we're on it */
109 GetSurface()->RemoveDirtyElement (this);
112 if (subtree_object
!= NULL
&& subtree_object
->Is(Type::UIELEMENT
))
113 subtree_object
->SetSurface (s
);
115 DependencyObject::SetSurface (s
);
119 UIElement::IntersectBoundsWithClipPath (Rect unclipped
, bool transform
)
121 Geometry
*clip
= GetClip ();
122 Geometry
*layout_clip
= transform
? NULL
: LayoutInformation::GetLayoutClip (this);
125 if (!clip
&& !layout_clip
)
129 box
= clip
->GetBounds ();
131 box
= layout_clip
->GetBounds ();
134 box
= box
.Intersection (layout_clip
->GetBounds());
136 if (!GetRenderVisible())
137 box
= Rect (0,0,0,0);
140 box
= box
.Transform (&absolute_xform
);
142 return box
.Intersection (unclipped
);
146 UIElement::RenderClipPath (cairo_t
*cr
, bool path_only
)
149 cairo_set_matrix (cr
, &absolute_xform
);
151 Geometry
*geometry
= GetClip();
161 UIElement::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
163 if (args
->GetProperty ()->GetOwnerType() != Type::UIELEMENT
) {
164 DependencyObject::OnPropertyChanged (args
, error
);
168 if (args
->GetId () == UIElement::OpacityProperty
) {
169 InvalidateVisibility ();
170 } else if (args
->GetId () == UIElement::VisibilityProperty
) {
171 // note: invalid enum values are only validated in 1.1 (managed code),
172 // the default value for VisibilityProperty is VisibilityCollapsed
173 // (see bug #340799 for more details)
174 if (args
->GetNewValue()->AsInt32() == VisibilityVisible
)
175 flags
|= UIElement::RENDER_VISIBLE
;
177 flags
&= ~UIElement::RENDER_VISIBLE
;
179 InvalidateVisibility ();
180 InvalidateMeasure ();
181 if (GetVisualParent ())
182 GetVisualParent ()->InvalidateMeasure ();
183 } else if (args
->GetId () == UIElement::IsHitTestVisibleProperty
) {
184 if (args
->GetNewValue()->AsBool())
185 flags
|= UIElement::HIT_TEST_VISIBLE
;
187 flags
&= ~UIElement::HIT_TEST_VISIBLE
;
189 UpdateTotalHitTestVisibility();
190 } else if (args
->GetId () == UIElement::ClipProperty
) {
192 } else if (args
->GetId () == UIElement::OpacityMaskProperty
) {
193 opacityMask
= args
->GetNewValue() ? args
->GetNewValue()->AsBrush() : NULL
;
195 } else if (args
->GetId () == UIElement::RenderTransformProperty
196 || args
->GetId () == UIElement::RenderTransformOriginProperty
) {
199 else if (args
->GetId () == UIElement::TriggersProperty
) {
200 if (args
->GetOldValue()) {
201 // remove the old trigger targets
202 TriggerCollection
*triggers
= args
->GetOldValue()->AsTriggerCollection();
203 for (int i
= 0; i
< triggers
->GetCount (); i
++)
204 triggers
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
207 if (args
->GetNewValue()) {
209 TriggerCollection
*triggers
= args
->GetNewValue()->AsTriggerCollection();
210 for (int i
= 0; i
< triggers
->GetCount (); i
++)
211 triggers
->GetValueAt (i
)->AsEventTrigger ()->SetTarget (this);
213 } else if (args
->GetId () == UIElement::UseLayoutRoundingProperty
) {
214 InvalidateMeasure ();
215 InvalidateArrange ();
218 NotifyListenersOfPropertyChange (args
, error
);
222 UIElement::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
224 if (col
== GetTriggers ()) {
225 switch (args
->GetChangedAction()) {
226 case CollectionChangedActionReplace
:
227 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
229 case CollectionChangedActionAdd
:
230 args
->GetNewItem()->AsEventTrigger ()->SetTarget (this);
232 case CollectionChangedActionRemove
:
233 args
->GetOldItem()->AsEventTrigger ()->RemoveTarget (this);
235 case CollectionChangedActionClearing
:
236 for (int i
= 0; i
< col
->GetCount (); i
++)
237 col
->GetValueAt (i
)->AsEventTrigger ()->RemoveTarget (this);
239 case CollectionChangedActionCleared
:
240 // nothing needed here.
245 DependencyObject::OnCollectionChanged (col
, args
);
251 UIElement::DumpHierarchy (UIElement
*obj
)
256 int n
= DumpHierarchy (obj
->GetVisualParent ());
257 for (int i
= 0; i
< n
; i
++)
259 printf ("%s (%p)\n", obj
->GetTypeName(), obj
);
265 UIElement::UpdateBounds (bool force_redraw
)
267 //InvalidateMeasure ();
268 //InvalidateArrange ();
270 Surface
*surface
= GetSurface ();
272 surface
->AddDirtyElement (this, DirtyBounds
);
274 force_invalidate_of_new_bounds
|= force_redraw
;
278 UIElement::UpdateTotalRenderVisibility ()
280 Surface
*surface
= GetSurface ();
282 surface
->AddDirtyElement (this, DirtyRenderVisibility
);
286 UIElement::UpdateTotalHitTestVisibility ()
288 VisualTreeWalker
walker (this);
289 while (UIElement
*child
= walker
.Step ())
290 child
->UpdateTotalHitTestVisibility ();
293 GetSurface ()->AddDirtyElement (this, DirtyHitTestVisibility
);
297 UIElement::GetActualTotalRenderVisibility ()
299 bool visible
= (flags
& UIElement::RENDER_VISIBLE
) != 0;
300 bool parent_visible
= true;
302 total_opacity
= GetOpacity ();
304 if (GetVisualParent ()) {
305 GetVisualParent ()->ComputeTotalRenderVisibility ();
306 parent_visible
= visible
&& GetVisualParent ()->GetRenderVisible ();
307 total_opacity
*= GetVisualParent ()->total_opacity
;
310 visible
= visible
&& parent_visible
;
316 UIElement::ComputeTotalRenderVisibility ()
318 if (GetActualTotalRenderVisibility ())
319 flags
|= UIElement::TOTAL_RENDER_VISIBLE
;
321 flags
&= ~UIElement::TOTAL_RENDER_VISIBLE
;
325 UIElement::GetActualTotalHitTestVisibility ()
327 bool visible
= (flags
& UIElement::HIT_TEST_VISIBLE
) != 0;
329 if (visible
&& GetVisualParent ()) {
330 GetVisualParent ()->ComputeTotalHitTestVisibility ();
331 visible
= visible
&& GetVisualParent ()->GetHitTestVisible ();
338 UIElement::ComputeTotalHitTestVisibility ()
340 if (GetActualTotalHitTestVisibility ())
341 flags
|= UIElement::TOTAL_HIT_TEST_VISIBLE
;
343 flags
&= ~UIElement::TOTAL_HIT_TEST_VISIBLE
;
347 UIElement::UpdateTransform ()
349 InvalidateArrange ();
352 GetSurface()->AddDirtyElement (this, DirtyLocalTransform
);
357 UIElement::ComputeLocalTransform ()
359 Transform
*transform
= GetRenderTransform ();
360 Point transform_origin
= GetTransformOrigin ();
361 cairo_matrix_t render
;
363 cairo_matrix_init_identity (&render
);
364 cairo_matrix_init_identity (&local_xform
);
366 if (transform
== NULL
)
369 transform
->GetTransform (&render
);
370 cairo_matrix_translate (&local_xform
, transform_origin
.x
, transform_origin
.y
);
371 cairo_matrix_multiply (&local_xform
, &render
, &local_xform
);
372 cairo_matrix_translate (&local_xform
, -transform_origin
.x
, -transform_origin
.y
);
376 UIElement::TransformBounds (cairo_matrix_t
*old
, cairo_matrix_t
*current
)
380 cairo_matrix_t tween
= *old
;
381 cairo_matrix_invert (&tween
);
382 cairo_matrix_multiply (&tween
, &tween
, current
);
389 p0
= p0
- p0
.Transform (&tween
);
390 p1
= p1
- p1
.Transform (&tween
);
391 p2
= p2
- p2
.Transform (&tween
);
392 p3
= p3
- p3
.Transform (&tween
);
394 if (p0
== p1
&& p1
== p2
&& p2
== p3
) {
395 //printf ("shifting position\n");
396 ShiftPosition (bounds
.GetTopLeft ().Transform (&tween
));
404 UIElement::ComputeTransform ()
406 cairo_matrix_t old
= absolute_xform
;
407 cairo_matrix_init_identity (&absolute_xform
);
409 if (GetVisualParent () != NULL
) {
410 absolute_xform
= GetVisualParent ()->absolute_xform
;
411 } else if (GetParent () != NULL
&& GetParent ()->Is (Type::POPUP
)) {
412 // FIXME we shouldn't be examing a subclass type here but we'll do this
413 // for now to get popups in something approaching the right place while
414 // we figure out a cleaner way to handle it long term.
415 Popup
*popup
= (Popup
*)GetParent ();
416 absolute_xform
= popup
->absolute_xform
;
417 cairo_matrix_translate (&absolute_xform
, popup
->GetHorizontalOffset (), popup
->GetVerticalOffset ());
420 cairo_matrix_multiply (&absolute_xform
, &layout_xform
, &absolute_xform
);
421 cairo_matrix_multiply (&absolute_xform
, &local_xform
, &absolute_xform
);
423 if (moonlight_flags
& RUNTIME_INIT_USE_UPDATE_POSITION
)
424 TransformBounds (&old
, &absolute_xform
);
431 UIElement::ComputeBounds ()
433 g_warning ("UIElement:ComputeBounds has been called. The derived class %s should have overridden it.",
438 UIElement::ShiftPosition (Point p
)
445 UIElement::OnSubPropertyChanged (DependencyProperty
*prop
, DependencyObject
*obj
, PropertyChangedEventArgs
*subobj_args
)
447 if (prop
&& prop
->GetId () == UIElement::RenderTransformProperty
) {
450 else if (prop
&& prop
->GetId () == UIElement::ClipProperty
) {
453 else if (prop
&& prop
->GetId () == UIElement::OpacityMaskProperty
) {
457 DependencyObject::OnSubPropertyChanged (prop
, obj
, subobj_args
);
461 UIElement::CacheInvalidateHint ()
463 VisualTreeWalker
walker (this);
464 while (UIElement
*child
= walker
.Step ())
465 child
->CacheInvalidateHint ();
469 UIElement::SetVisualParent (UIElement
*visual_parent
)
471 this->visual_parent
= visual_parent
;
473 if (visual_parent
&& visual_parent
->GetSurface () != GetSurface())
474 SetSurface (visual_parent
->GetSurface());
478 UIElement::SetSubtreeObject (DependencyObject
*value
)
480 if (subtree_object
== value
)
484 subtree_object
->unref ();
486 subtree_object
= value
;
489 subtree_object
->ref ();
493 UIElement::ElementRemoved (UIElement
*item
)
495 // Invalidate ourself in the size of the item's subtree
496 Invalidate (item
->GetSubtreeBounds());
499 GetSurface()->RemoveDirtyElement (item
);
500 item
->SetVisualParent (NULL
);
501 item
->CacheInvalidateHint ();
502 item
->ClearLoaded ();
504 InvalidateMeasure ();
508 UIElement::ElementAdded (UIElement
*item
)
510 item
->SetVisualLevel (GetVisualLevel() + 1);
511 item
->SetVisualParent (this);
512 item
->UpdateTotalRenderVisibility ();
513 item
->UpdateTotalHitTestVisibility ();
514 //item->UpdateBounds (true);
517 if (0 != (flags
& (UIElement::IS_LOADED
| UIElement::PENDING_LOADED
))) {
518 InheritedPropertyValueProvider::PropagateInheritedPropertiesOnAddingToTree (item
);
521 List
*load_list
= item
->WalkTreeForLoaded (&delay
);
523 // if we're loaded, key the delay state off of the
524 // WalkTreeForLoaded call.
526 // if we're actually pending loaded, forcibly delay
527 // the new element's Loaded firing as well.
529 if (0 != (flags
& UIElement::PENDING_LOADED
))
533 PostSubtreeLoad (load_list
);
534 // PostSubtreeLoad will take care of deleting the list for us.
537 EmitSubtreeLoad (load_list
);
544 InvalidateMeasure ();
545 item
->SetRenderSize (Size (0,0));
546 item
->UpdateTransform ();
547 item
->InvalidateMeasure ();
548 item
->InvalidateArrange ();
552 UIElement::InvalidateMeasure ()
554 dirty_flags
|= DirtyMeasure
;
557 if ((surface
= GetSurface ()))
558 surface
->needs_measure
= true;
563 UIElement::InvalidateArrange ()
565 dirty_flags
|= DirtyArrange
;
568 if ((surface
= GetSurface ()))
569 surface
->needs_arrange
= true;
573 UIElement::DoMeasure ()
575 Size
*last
= LayoutInformation::GetPreviousConstraint (this);
576 UIElement
*parent
= GetVisualParent ();
577 Size
infinite (INFINITY
, INFINITY
);
579 if (!GetSurface () && !last
&& !parent
&& IsLayoutContainer ()) {
584 Size previous_desired
= GetDesiredSize ();
586 // This will be a noop on non layout elements
589 if (previous_desired
== GetDesiredSize ())
593 // a canvas doesn't care about the child size changing like this
594 if (parent
&& (!parent
->Is (Type::CANVAS
) || IsLayoutContainer ()))
595 parent
->InvalidateMeasure ();
597 dirty_flags
&= ~DirtyMeasure
;
601 UIElement::DoArrange ()
603 Rect
*last
= LayoutInformation::GetLayoutSlot (this);
604 Size previous_render
= Size ();
605 UIElement
*parent
= GetVisualParent ();
609 Size desired
= Size ();
610 Size available
= Size ();
611 Surface
*surface
= GetSurface ();
613 if (IsLayoutContainer ()) {
614 desired
= GetDesiredSize ();
615 if (surface
&& this == surface
->GetToplevel ()) {
616 Size
*measure
= LayoutInformation::GetPreviousConstraint (this);
618 desired
= desired
.Max (*LayoutInformation::GetPreviousConstraint (this));
620 desired
= Size (surface
->GetWindow ()->GetWidth (), surface
->GetWindow ()->GetHeight ());
623 FrameworkElement
*fe
= (FrameworkElement
*)this;
624 desired
= Size (fe
->GetActualWidth (), fe
->GetActualHeight ());
627 viewport
= Rect (Canvas::GetLeft (this),
628 Canvas::GetTop (this),
629 desired
.width
, desired
.height
);
635 previous_render
= GetRenderSize ();
638 if (previous_render
== GetRenderSize ())
642 if (parent
&& (!parent
->Is (Type::CANVAS
) || (IsLayoutContainer () || !last
)))
643 parent
->InvalidateArrange ();
648 LayoutInformation::SetLastRenderSize (this, &previous_render
);
652 UIElement::InsideClip (cairo_t
*cr
, double x
, double y
)
665 TransformPoint (&nx
, &ny
);
666 if (!clip
->GetBounds ().PointInside (nx
, ny
))
672 if (cairo_in_fill (cr
, nx
, ny
))
682 UIElement::InsideObject (cairo_t
*cr
, double x
, double y
)
684 return InsideClip (cr
, x
, y
);
687 class LoadedState
: public EventObject
{
689 LoadedState (List
*list
) { this->list
= list
; }
690 ~LoadedState () { delete list
; }
692 List
* GetUIElementList () { return list
; }
699 UIElement::emit_delayed_loaded (EventObject
*data
)
701 LoadedState
*state
= (LoadedState
*)data
;
703 EmitSubtreeLoad (state
->GetUIElementList ());
707 UIElement::EmitSubtreeLoad (List
*l
)
709 while (UIElementNode
* node
= (UIElementNode
*)l
->First()) {
712 node
->uielement
->OnLoaded ();
719 UIElement::PostSubtreeLoad (List
*load_list
)
721 LoadedState
*state
= new LoadedState (load_list
);
723 GetDeployment()->GetSurface()->GetTimeManager()->AddTickCall (emit_delayed_loaded
, state
);
729 UIElement::WalkTreeForLoaded (bool *delay
)
731 List
*walk_list
= new List();
732 List
*load_list
= new List();
737 walk_list
->Append (new UIElementNode (this));
739 while (UIElementNode
*next
= (UIElementNode
*)walk_list
->First ()) {
740 // remove it from the walk list
741 walk_list
->Unlink (next
);
743 if (next
->uielement
->Is(Type::CONTROL
)) {
744 Control
*control
= (Control
*)next
->uielement
;
745 if (!control
->default_style_applied
) {
746 ManagedTypeInfo
*key
= control
->GetDefaultStyleKey ();
748 if (Application::GetCurrent () == NULL
)
749 g_warning ("attempting to use a null application when applying default style when emitting Loaded event.");
751 Application::GetCurrent()->ApplyDefaultStyle (control
, key
);
755 if (control
->GetStyle() || control
->default_style_applied
)
761 // add it to the front of the load list, and mark it as PENDING_LOADED
762 next
->uielement
->flags
|= UIElement::PENDING_LOADED
;
763 load_list
->Prepend (next
);
765 // and add its children to the front of the walk list
766 VisualTreeWalker
walker (next
->uielement
);
767 while (UIElement
*child
= walker
.Step ())
768 walk_list
->Prepend (new UIElementNode (child
));
778 UIElement::OnLoaded ()
780 if (emitting_loaded
|| IsLoaded())
783 emitting_loaded
= true;
785 flags
|= UIElement::IS_LOADED
;
787 flags
&= ~UIElement::PENDING_LOADED
;
789 Emit (LoadedEvent
, new RoutedEventArgs(this));
791 emitting_loaded
= false;
795 UIElement::ClearLoaded ()
798 Surface
*s
= Deployment::GetCurrent ()->GetSurface ();
799 if (s
->GetFocusedElement () == this)
800 s
->FocusElement (NULL
);
805 flags
&= ~UIElement::IS_LOADED
;
807 Emit (UnloadedEvent
, new RoutedEventArgs(this), true);
808 VisualTreeWalker
walker (this);
809 while ((e
= walker
.Step ()))
814 UIElement::Focus (bool recurse
)
820 // Queues the invalidate for the current region, performs any
821 // updates to the RenderTransform (optional) and queues a
822 // new redraw with the new bounding box
825 UIElement::FullInvalidate (bool rendertransform
)
830 UpdateBounds (true /* force an invalidate here, even if the bounds don't change */);
834 UIElement::Invalidate (Rect r
)
836 if (!GetRenderVisible() || IS_INVISIBLE(total_opacity
))
839 #ifdef DEBUG_INVALIDATE
840 printf ("Requesting invalidate for object %p %s (%s) at %f %f - %f %f\n",
841 this, GetName(), GetTypeName(),
848 GetSurface()->AddDirtyElement (this, DirtyInvalidate
);
850 dirty_region
->Union (r
);
852 GetSurface()->GetTimeManager()->NeedRedraw ();
854 Emit (InvalidatedEvent
);
859 UIElement::Invalidate (Region
*region
)
861 if (!GetRenderVisible () || IS_INVISIBLE (total_opacity
))
865 GetSurface()->AddDirtyElement (this, DirtyInvalidate
);
867 dirty_region
->Union (region
);
869 GetSurface()->GetTimeManager()->NeedRedraw ();
871 Emit (InvalidatedEvent
);
876 UIElement::Invalidate ()
883 UIElement::InvalidatePaint ()
890 UIElement::InvalidateSubtreePaint ()
892 Invalidate (GetSubtreeBounds ());
896 UIElement::InvalidateClip ()
898 InvalidateSubtreePaint ();
903 UIElement::InvalidateMask ()
905 InvalidateSubtreePaint ();
909 UIElement::InvalidateVisibility ()
911 UpdateTotalRenderVisibility ();
912 InvalidateSubtreePaint ();
917 UIElement::InvalidateIntrisicSize ()
919 InvalidateMeasure ();
920 InvalidateArrange ();
926 UIElement::HitTest (cairo_t
*cr
, Point p
, List
*uielement_list
)
928 uielement_list
->Prepend (new UIElementNode (this));
932 UIElement::HitTest (cairo_t
*cr
, Rect r
, List
*uielement_list
)
937 UIElement::FindElementsInHostCoordinates_p (Point p
, HitTestCollection
*uielement_list
)
939 List
*list
= new List ();
940 cairo_t
*ctx
= measuring_context_create ();
942 FindElementsInHostCoordinates (ctx
, p
, 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
);
956 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Point P
, List
*uielement_list
)
958 uielement_list
->Prepend (new UIElementNode (this));
963 UIElement::FindElementsInHostCoordinates_r (Rect r
, HitTestCollection
*uielement_list
)
965 List
*list
= new List ();
966 cairo_t
*ctx
= measuring_context_create ();
968 FindElementsInHostCoordinates (ctx
, r
, list
);
970 UIElementNode
*node
= (UIElementNode
*) list
->First ();
972 uielement_list
->Add (new Value (node
->uielement
));
973 node
= (UIElementNode
*) node
->next
;
977 measuring_context_destroy (ctx
);
981 UIElement::FindElementsInHostCoordinates (cairo_t
*cr
, Rect r
, List
*uielement_list
)
983 uielement_list
->Prepend (new UIElementNode (this));
987 UIElement::EmitKeyDown (GdkEventKey
*event
)
989 return Emit (KeyDownEvent
, new KeyEventArgs (event
));
993 UIElement::EmitKeyUp (GdkEventKey
*event
)
995 return Emit (KeyUpEvent
, new KeyEventArgs (event
));
999 UIElement::EmitGotFocus ()
1001 return Emit (GotFocusEvent
, new RoutedEventArgs (this));
1005 UIElement::EmitLostFocus ()
1007 return Emit (LostFocusEvent
, new RoutedEventArgs (this));
1011 UIElement::EmitLostMouseCapture ()
1013 MouseEventArgs
*e
= new MouseEventArgs ();
1014 e
->SetSource (this);
1015 return Emit (LostMouseCaptureEvent
, e
);
1019 UIElement::CaptureMouse ()
1021 Surface
*s
= GetSurface ();
1025 return s
->SetMouseCapture (this);
1029 UIElement::ReleaseMouseCapture ()
1031 Surface
*s
= GetSurface ();
1035 s
->ReleaseMouseCapture (this);
1039 UIElement::DoRender (cairo_t
*cr
, Region
*parent_region
)
1041 Region
*region
= new Region (GetSubtreeBounds ());
1042 region
->Intersect (parent_region
);
1044 if (!GetRenderVisible() || IS_INVISIBLE (total_opacity
) || region
->IsEmpty ()) {
1049 #if FRONT_TO_BACK_STATS
1050 GetSurface()->uielements_rendered_back_to_front
++;
1053 STARTTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1055 PreRender (cr
, region
, false);
1057 Render (cr
, region
);
1059 PostRender (cr
, region
, false);
1061 ENDTIMER (UIElement_render
, Type::Find (GetObjectType())->name
);
1067 UIElement::UseBackToFront ()
1069 return VisualTreeWalker (this).GetCount () < MIN_FRONT_TO_BACK_COUNT
;
1073 UIElement::FrontToBack (Region
*surface_region
, List
*render_list
)
1075 double local_opacity
= GetOpacity ();
1077 if (surface_region
->RectIn (GetSubtreeBounds().RoundOut()) == GDK_OVERLAP_RECTANGLE_OUT
)
1080 if (!GetRenderVisible ()
1081 || IS_INVISIBLE (local_opacity
))
1084 if (!UseBackToFront ()) {
1085 Region
*self_region
= new Region (surface_region
);
1086 self_region
->Intersect (GetSubtreeBounds().RoundOut());
1088 // we need to include our children in this one, since
1089 // we'll be rendering them in the PostRender method.
1090 if (!self_region
->IsEmpty())
1091 render_list
->Prepend (new RenderNode (this, self_region
, true,
1092 UIElement::CallPreRender
, UIElement::CallPostRender
));
1093 // don't remove the region from surface_region because
1094 // there are likely holes in it
1100 bool can_subtract_self
;
1103 && !GetOpacityMask ()
1104 && !IS_TRANSLUCENT (GetOpacity ())) {
1105 region
= surface_region
;
1106 delete_region
= false;
1107 can_subtract_self
= true;
1110 region
= new Region (surface_region
);
1111 delete_region
= true;
1112 can_subtract_self
= false;
1115 RenderNode
*cleanup_node
= new RenderNode (this, NULL
, false, NULL
, UIElement::CallPostRender
);
1117 render_list
->Prepend (cleanup_node
);
1119 Region
*self_region
= new Region (region
);
1121 VisualTreeWalker
walker (this, ZReverse
);
1122 while (UIElement
*child
= walker
.Step ())
1123 child
->FrontToBack (region
, render_list
);
1125 if (!GetOpacityMask () && !IS_TRANSLUCENT (local_opacity
)) {
1127 if (GetRenderBounds().IsEmpty ()) { // empty bounds mean that this element draws nothing itself
1128 self_region
= new Region ();
1131 self_region
= new Region (region
);
1132 self_region
->Intersect (GetRenderBounds().RoundOut ()); // note the RoundOut
1135 self_region
->Intersect (GetSubtreeBounds().RoundOut ()); // note the RoundOut
1138 if (self_region
->IsEmpty() && render_list
->First() == cleanup_node
) {
1139 /* we don't intersect the surface region, and none of
1140 our children did either, remove the cleanup node */
1141 render_list
->Remove (render_list
->First());
1148 render_list
->Prepend (new RenderNode (this, self_region
, !self_region
->IsEmpty(), UIElement::CallPreRender
, NULL
));
1150 if (!self_region
->IsEmpty()) {
1151 if (((absolute_xform
.yx
== 0 && absolute_xform
.xy
== 0) /* no skew/rotation */
1152 || (absolute_xform
.xx
== 0 && absolute_xform
.yy
== 0)) /* allow 90 degree rotations */
1153 && can_subtract_self
)
1154 region
->Subtract (GetCoverageBounds ());
1162 UIElement::PreRender (cairo_t
*cr
, Region
*region
, bool skip_children
)
1164 double local_opacity
= GetOpacity ();
1168 cairo_set_matrix (cr
, &absolute_xform
);
1169 RenderClipPath (cr
);
1171 if (opacityMask
|| IS_TRANSLUCENT (local_opacity
)) {
1172 Rect r
= GetSubtreeBounds ().RoundOut();
1173 cairo_identity_matrix (cr
);
1175 // we need this check because ::PreRender can (and
1176 // will) be called for elements with empty regions.
1178 // The region passed in here is the redraw region
1179 // intersected with the render bounds of a given
1180 // element. For Panels with no width/height specified
1181 // in the xaml, this region will be empty. (check
1182 // panel.cpp::FrontToBack - we insert the ::PreRender
1183 // calling node if either the panel background or any
1184 // of the children intersect the redraw region.) We
1185 // can't clip to the empty region, obviously, as it
1186 // will keep all descendents from drawing to the
1189 if (!region
->IsEmpty()) {
1196 cairo_set_matrix (cr
, &absolute_xform
);
1199 if (ClipToExtents ()) {
1205 if (IS_TRANSLUCENT (local_opacity
))
1206 cairo_push_group (cr
);
1208 if (opacityMask
!= NULL
)
1209 cairo_push_group (cr
);
1213 UIElement::PostRender (cairo_t
*cr
, Region
*region
, bool front_to_back
)
1215 // if we didn't render front to back, then render the children here
1216 if (!front_to_back
) {
1217 VisualTreeWalker
walker (this, ZForward
);
1218 while (UIElement
*child
= walker
.Step ())
1219 child
->DoRender (cr
, region
);
1222 double local_opacity
= GetOpacity ();
1224 if (opacityMask
!= NULL
) {
1225 cairo_pattern_t
*mask
;
1226 cairo_pattern_t
*data
= cairo_pop_group (cr
);
1227 Point p
= GetOriginPoint ();
1228 Rect area
= Rect (p
.x
, p
.y
, 0.0, 0.0);
1229 GetSizeForBrush (cr
, &(area
.width
), &(area
.height
));
1230 opacityMask
->SetupBrush (cr
, area
);
1231 mask
= cairo_get_source (cr
);
1232 cairo_pattern_reference (mask
);
1233 cairo_set_source (cr
, data
);
1234 cairo_mask (cr
, mask
);
1235 cairo_pattern_destroy (mask
);
1236 cairo_pattern_destroy (data
);
1239 if (IS_TRANSLUCENT (local_opacity
)) {
1240 cairo_pop_group_to_source (cr
);
1241 cairo_paint_with_alpha (cr
, local_opacity
);
1246 if (moonlight_flags
& RUNTIME_INIT_SHOW_CLIPPING
) {
1248 cairo_new_path (cr
);
1249 cairo_set_matrix (cr
, &absolute_xform
);
1250 cairo_set_line_width (cr
, 1);
1252 Geometry
*geometry
= GetClip ();
1254 geometry
->Draw (cr
);
1255 cairo_set_source_rgba (cr
, 0.0, 1.0, 1.0, 1.0);
1262 if (moonlight_flags
& RUNTIME_INIT_SHOW_BOUNDING_BOXES
) {
1264 cairo_new_path (cr
);
1265 //RenderClipPath (cr);
1266 cairo_identity_matrix (cr
);
1267 cairo_set_source_rgba (cr
, 1.0, 0.5, 0.2, 1.0);
1268 cairo_set_line_width (cr
, 1);
1269 cairo_rectangle (cr
, bounds
.x
+ .5, bounds
.y
+ .5, bounds
.width
- .5, bounds
.height
- .5);
1276 UIElement::Paint (cairo_t
*ctx
, Region
*region
, cairo_matrix_t
*xform
)
1278 // FIXME xform is ignored for now
1280 g_warning ("passing a transform to UIElement::Paint is not yet supported");
1282 #if FRONT_TO_BACK_STATS
1283 uielements_rendered_front_to_back
= 0;
1284 uielements_rendered_back_to_front
= 0;
1287 bool did_front_to_back
= false;
1288 List
*render_list
= new List ();
1290 if (moonlight_flags
& RUNTIME_INIT_RENDER_FRONT_TO_BACK
) {
1291 Region
*copy
= new Region (region
);
1292 FrontToBack (copy
, render_list
);
1294 if (!render_list
->IsEmpty ()) {
1295 while (RenderNode
*node
= (RenderNode
*)render_list
->First()) {
1296 #if FRONT_TO_BACK_STATS
1297 uielements_rendered_front_to_back
++;
1301 render_list
->Remove (node
);
1304 did_front_to_back
= true;
1311 if (!did_front_to_back
) {
1312 DoRender (ctx
, region
);
1315 #if FRONT_TO_BACK_STATS
1316 printf ("UIElements rendered front-to-back for: %s(%p)\n", uielements_rendered_front_to_back
, GetName (), this);
1317 printf ("UIElements rendered back-to-front for: %s(%p)\n", uielements_rendered_back_to_front
, GetName (), this);
1322 UIElement::CallPreRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1324 element
->PreRender (cr
, region
, front_to_back
);
1328 UIElement::CallPostRender (cairo_t
*cr
, UIElement
*element
, Region
*region
, bool front_to_back
)
1330 element
->PostRender (cr
, region
, front_to_back
);
1334 UIElement::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
1336 /* do nothing by default */
1340 UIElement::GetSizeForBrush (cairo_t
*cr
, double *width
, double *height
)
1342 g_warning ("UIElement:GetSizeForBrush has been called. The derived class %s should have overridden it.",
1344 *height
= *width
= 0.0;
1348 UIElement::GetTimeManager ()
1350 Surface
*surface
= GetSurface ();
1351 Deployment
*deployment
;
1353 if (surface
== NULL
) {
1354 deployment
= GetDeployment ();
1355 if (deployment
!= NULL
)
1356 surface
= deployment
->GetSurface ();
1359 return surface
? surface
->GetTimeManager() : NULL
;
1363 UIElement::GetTransformToUIElementWithError (UIElement
*to_element
, MoonError
*error
)
1365 /* walk from this up to the root. if we hit null before we hit the toplevel, it's an error */
1366 UIElement
*visual
= this;
1369 if (visual
&& GetSurface()) {
1371 if (GetSurface()->IsTopLevel (visual
))
1373 visual
= visual
->GetVisualParent ();
1377 if (!ok
|| (to_element
&& !to_element
->GetSurface ())) {
1378 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1383 if (to_element
&& !to_element
->GetSurface()->IsTopLevel (to_element
)) {
1384 /* if @to_element is specified we also need to make sure there's a path to the root from it */
1386 visual
= to_element
->GetVisualParent ();
1387 if (visual
&& to_element
->GetSurface()) {
1389 if (to_element
->GetSurface()->IsTopLevel (visual
))
1391 visual
= visual
->GetVisualParent ();
1396 MoonError::FillIn (error
, MoonError::ARGUMENT
, 1001,
1402 cairo_matrix_t result
;
1403 // A = From, B = To, M = what we want
1405 // => M = A * inv (B)
1407 cairo_matrix_t inverse
= to_element
->absolute_xform
;
1408 cairo_matrix_invert (&inverse
);
1409 cairo_matrix_multiply (&result
, &absolute_xform
, &inverse
);
1412 result
= absolute_xform
;
1415 Matrix
*matrix
= new Matrix (&result
);
1417 MatrixTransform
*transform
= new MatrixTransform ();
1418 transform
->SetValue (MatrixTransform::MatrixProperty
, matrix
);
1425 UIElement::TransformPoint (double *x
, double *y
)
1427 cairo_matrix_t inverse
= absolute_xform
;
1428 cairo_matrix_invert (&inverse
);
1430 cairo_matrix_transform_point (&inverse
, x
, y
);