2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / border.cpp
blobab2c70d9581f1aa0db0f5d626810cc83c8b627c6
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 child->Measure (availableSize.GrowBy (-border));
37 desired = child->GetDesiredSize ();
40 desired = desired.GrowBy (border);
42 desired = desired.Min (availableSize);
44 return desired;
47 Size
48 Border::ArrangeOverride (Size finalSize)
50 Thickness border = *GetPadding () + *GetBorderThickness ();
52 Size arranged = finalSize;
54 VisualTreeWalker walker = VisualTreeWalker (this);
55 while (UIElement *child = walker.Step ()) {
56 Rect childRect (0,0,finalSize.width,finalSize.height);
58 childRect = childRect.GrowBy (-border);
61 child->Arrange (childRect);
63 arranged = Size (childRect.width, childRect.height).GrowBy (border);
65 arranged = arranged.Max (finalSize);
68 return finalSize;
71 void
72 Border::Render (cairo_t *cr, Region *region, bool path_only)
74 Brush *background = GetBackground ();
75 Brush *border_brush = GetBorderBrush ();
77 cairo_set_matrix (cr, &absolute_xform);
78 cairo_new_path (cr);
80 cairo_save (cr);
81 if (!path_only)
82 RenderLayoutClip (cr);
84 CornerRadius *round = GetCornerRadius ();
85 CornerRadius inner_adjusted = CornerRadius (0);
86 CornerRadius outer_adjusted = CornerRadius (0);
87 Thickness thickness = *GetBorderThickness ();
88 Rect paint_border = extents;
89 Rect paint_background = paint_border.GrowBy (-thickness);
91 if (round) {
92 inner_adjusted = *round;
93 inner_adjusted.topLeft = MAX (round->topLeft - MAX (thickness.left, thickness.top) * .5, 0);
94 inner_adjusted.topRight = MAX (round->topRight - MAX (thickness.right, thickness.top) * .5, 0);
95 inner_adjusted.bottomRight = MAX (round->bottomRight - MAX (thickness.right, thickness.bottom) * .5, 0);
96 inner_adjusted.bottomLeft = MAX (round->bottomLeft - MAX (thickness.left, thickness.bottom) * .5, 0);
98 outer_adjusted = *round;
99 outer_adjusted.topLeft = outer_adjusted.topLeft ? MAX (round->topLeft + MAX (thickness.left, thickness.top) * .5, 0) : 0;
100 outer_adjusted.topRight = outer_adjusted.topRight ? MAX (round->topRight + MAX (thickness.right, thickness.top) * .5, 0) : 0;
101 outer_adjusted.bottomRight = outer_adjusted.bottomRight ? MAX (round->bottomRight + MAX (thickness.right, thickness.bottom) * .5, 0) : 0;
102 outer_adjusted.bottomLeft = outer_adjusted.bottomLeft ? MAX (round->bottomLeft + MAX (thickness.left, thickness.bottom) * .5, 0) : 0;
106 * NOTE filling this way can leave alpha artifacts between the border fill and bg fill
107 * but some simple inspection of the ms results make me think that is what happens there
108 * too.
110 cairo_new_path (cr);
111 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
113 if (border_brush) {
114 border_brush->SetupBrush (cr, paint_border);
117 paint_border.Draw (cr, &outer_adjusted);
118 paint_background.Draw (cr, round ? &inner_adjusted : NULL);
120 if (!path_only)
121 border_brush->Fill (cr);
124 if (background) {
125 background->SetupBrush (cr, round ? paint_background : NULL);
127 paint_background.Draw (cr, round ? &inner_adjusted : NULL);
129 if (!path_only)
130 background->Fill (cr);
133 cairo_restore (cr);
136 void
137 Border::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
139 if (args->GetProperty ()->GetOwnerType() != Type::BORDER) {
140 FrameworkElement::OnPropertyChanged (args, error);
141 return;
144 if (args->GetId () == Border::ChildProperty){
145 if (args->GetOldValue() && args->GetOldValue()->AsUIElement()) {
146 ElementRemoved (args->GetOldValue()->AsUIElement ());
147 SetSubtreeObject (NULL);
148 if (args->GetOldValue()->Is(GetDeployment (), Type::FRAMEWORKELEMENT)) {
149 args->GetOldValue()->AsFrameworkElement()->SetLogicalParent (NULL, error);
150 if (error->number)
151 return;
155 if (args->GetNewValue() && args->GetNewValue()->AsUIElement()) {
156 SetSubtreeObject (args->GetNewValue()->AsUIElement());
157 ElementAdded (args->GetNewValue()->AsUIElement ());
158 if (args->GetNewValue()->Is(GetDeployment (), Type::FRAMEWORKELEMENT)) {
159 FrameworkElement *fwe = args->GetNewValue()->AsFrameworkElement ();
160 if (fwe->GetLogicalParent() && fwe->GetLogicalParent() != this) {
161 MoonError::FillIn (error, MoonError::ARGUMENT, "Content is already a child of another element");
162 return;
165 args->GetNewValue()->AsFrameworkElement()->SetLogicalParent (this, error);
166 if (error->number)
167 return;
171 UpdateBounds ();
172 InvalidateMeasure ();
174 else if (args->GetId () == Border::PaddingProperty
175 || args->GetId () == Border::BorderThicknessProperty) {
176 InvalidateMeasure ();
177 } else if (args->GetId () == Border::BackgroundProperty) {
178 Invalidate ();
180 NotifyListenersOfPropertyChange (args, error);
183 void
184 Border::OnSubPropertyChanged (DependencyProperty *prop, DependencyObject *obj, PropertyChangedEventArgs *subobj_args)
186 if (prop && (prop->GetId () == Border::BackgroundProperty || prop->GetId () == Border::BorderBrushProperty)) {
187 Invalidate ();
189 else
190 FrameworkElement::OnSubPropertyChanged (prop, obj, subobj_args);
193 bool
194 Border::InsideObject (cairo_t *cr, double x, double y)
196 if (!FrameworkElement::InsideObject (cr, x, y))
197 return false;
199 cairo_save (cr);
200 cairo_new_path (cr);
201 cairo_set_matrix (cr, &absolute_xform);
203 TransformPoint (&x, &y);
205 Render (cr, NULL, true);
206 bool inside = cairo_in_fill (cr, x, y);
207 cairo_restore (cr);
209 return inside;
212 void
213 Border::HitTest (cairo_t *cr, Rect r, List *uielement_list)