Merge branch 'loaded-branch'
[moon.git] / src / border.cpp
blobaaf4d6261824dda848022cdcc641121a52eebe52
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * border.cpp:
5 * Copyright 2007 Novell, Inc. (http://www.novell.com)
7 * See the LICENSE file included with the distribution for details.
8 *
9 */
11 #include <config.h>
13 #include <math.h>
15 #include "geometry.h"
16 #include "runtime.h"
17 #include "brush.h"
18 #include "border.h"
19 #include "thickness.h"
21 Border::Border()
23 SetObjectType (Type::BORDER);
26 Size
27 Border::MeasureOverride (Size availableSize)
29 Size desired = Size (0,0);
31 Thickness border = *GetPadding () + *GetBorderThickness ();
33 // Get the desired size of our child, and include any margins we set
34 VisualTreeWalker walker = VisualTreeWalker (this);
35 while (UIElement *child = walker.Step ()) {
36 if (child->GetVisibility () != VisibilityVisible)
37 continue;
39 child->Measure (ApplySizeConstraints (availableSize.GrowBy (-border)));
40 desired = child->GetDesiredSize ();
43 desired = desired.GrowBy (border);
45 desired = ApplySizeConstraints (desired);
47 desired = desired.Min (availableSize);
49 return desired;
52 Size
53 Border::ArrangeOverride (Size finalSize)
55 Thickness border = *GetPadding () + *GetBorderThickness ();
57 finalSize = ApplySizeConstraints (finalSize);
59 Size arranged = finalSize;
61 VisualTreeWalker walker = VisualTreeWalker (this);
62 while (UIElement *child = walker.Step ()) {
63 if (child->GetVisibility () != VisibilityVisible)
64 continue;
66 Size desired = child->GetDesiredSize ();
67 Rect childRect (0,0,finalSize.width,finalSize.height);
69 childRect = childRect.GrowBy (-border);
72 child->Arrange (childRect);
73 arranged = child->GetRenderSize ();
74 arranged = arranged.GrowBy (border);
76 arranged = arranged.Max (finalSize);
78 arranged = ApplySizeConstraints (arranged);
81 return finalSize;
84 void
85 Border::Render (cairo_t *cr, Region *region, bool path_only)
87 Brush *background = GetBackground ();
88 Brush *border_brush = GetBorderBrush ();
90 cairo_set_matrix (cr, &absolute_xform);
91 cairo_new_path (cr);
93 cairo_save (cr);
94 if (!path_only)
95 RenderLayoutClip (cr);
97 CornerRadius *round = GetCornerRadius ();
98 CornerRadius inner_adjusted = CornerRadius (0);
99 CornerRadius outer_adjusted = CornerRadius (0);
100 Thickness thickness = *GetBorderThickness ();
101 Rect paint_border = extents;
102 Rect paint_background = paint_border.GrowBy (-thickness);
104 if (round) {
105 inner_adjusted = *round;
106 inner_adjusted.topLeft = MAX (round->topLeft - MAX (thickness.left, thickness.top) * .5, 0);
107 inner_adjusted.topRight = MAX (round->topRight - MAX (thickness.right, thickness.top) * .5, 0);
108 inner_adjusted.bottomRight = MAX (round->bottomRight - MAX (thickness.right, thickness.bottom) * .5, 0);
109 inner_adjusted.bottomLeft = MAX (round->bottomLeft - MAX (thickness.left, thickness.bottom) * .5, 0);
111 outer_adjusted = *round;
112 outer_adjusted.topLeft = outer_adjusted.topLeft ? MAX (round->topLeft + MAX (thickness.left, thickness.top) * .5, 0) : 0;
113 outer_adjusted.topRight = outer_adjusted.topRight ? MAX (round->topRight + MAX (thickness.right, thickness.top) * .5, 0) : 0;
114 outer_adjusted.bottomRight = outer_adjusted.bottomRight ? MAX (round->bottomRight + MAX (thickness.right, thickness.bottom) * .5, 0) : 0;
115 outer_adjusted.bottomLeft = outer_adjusted.bottomLeft ? MAX (round->bottomLeft + MAX (thickness.left, thickness.bottom) * .5, 0) : 0;
119 * NOTE filling this way can leave alpha artifacts between the border fill and bg fill
120 * but some simple inspection of the ms results make me think that is what happens there
121 * too.
123 cairo_new_path (cr);
124 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
126 if (border_brush) {
127 border_brush->SetupBrush (cr, paint_border);
130 paint_border.Draw (cr, &outer_adjusted);
131 paint_background.Draw (cr, round ? &inner_adjusted : NULL);
133 if (!path_only)
134 border_brush->Fill (cr);
137 if (background) {
138 background->SetupBrush (cr, round ? paint_background : NULL);
140 paint_background.Draw (cr, round ? &inner_adjusted : NULL);
142 if (!path_only)
143 background->Fill (cr);
146 cairo_restore (cr);
149 void
150 Border::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
152 if (args->GetProperty ()->GetOwnerType() != Type::BORDER) {
153 FrameworkElement::OnPropertyChanged (args, error);
154 return;
157 if (args->GetId () == Border::ChildProperty){
158 if (args->GetOldValue() && args->GetOldValue()->AsUIElement()) {
159 ElementRemoved (args->GetOldValue()->AsUIElement ());
160 SetSubtreeObject (NULL);
161 if (args->GetOldValue()->Is(Type::FRAMEWORKELEMENT)) {
162 args->GetOldValue()->AsFrameworkElement()->SetLogicalParent (NULL, error);
163 if (error->number)
164 return;
168 if (args->GetNewValue() && args->GetNewValue()->AsUIElement()) {
169 SetSubtreeObject (args->GetNewValue()->AsUIElement());
170 ElementAdded (args->GetNewValue()->AsUIElement ());
171 if (args->GetNewValue()->Is(Type::FRAMEWORKELEMENT)) {
172 FrameworkElement *fwe = args->GetNewValue()->AsFrameworkElement ();
173 if (fwe->GetLogicalParent() && fwe->GetLogicalParent() != this) {
174 MoonError::FillIn (error, MoonError::ARGUMENT, "Content is already a child of another element");
175 return;
178 args->GetNewValue()->AsFrameworkElement()->SetLogicalParent (this, error);
179 if (error->number)
180 return;
184 UpdateBounds ();
185 InvalidateMeasure ();
187 else if (args->GetId () == Border::PaddingProperty
188 || args->GetId () == Border::BorderThicknessProperty) {
189 InvalidateMeasure ();
190 } else if (args->GetId () == Border::BackgroundProperty) {
191 Invalidate ();
193 NotifyListenersOfPropertyChange (args, error);
196 void
197 Border::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
199 if (prop && (prop->GetId () == Border::BackgroundProperty || prop->GetId () == Border::BorderBrushProperty)) {
200 Invalidate ();
202 else
203 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
206 bool
207 Border::InsideObject (cairo_t *cr, double x, double y)
209 if (!FrameworkElement::InsideObject (cr, x, y))
210 return false;
212 cairo_save (cr);
213 cairo_set_matrix (cr, &absolute_xform);
215 TransformPoint (&x, &y);
217 Render (cr, NULL, true);
218 bool inside = cairo_in_fill (cr, x, y);
219 cairo_restore (cr);
221 return inside;
224 void
225 Border::HitTest (cairo_t *cr, Rect r, List *uielement_list)