2009-06-17 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / border.cpp
blob8a547bc328e718e896b30eed82a145b3eca5be29
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);
30 Size specified = Size (GetWidth (), GetHeight ());
32 availableSize = availableSize.Max (specified);
33 availableSize = availableSize.Min (specified);
35 Thickness border = *GetPadding () + *GetBorderThickness ();
37 // Get the desired size of our child, and include any margins we set
38 VisualTreeWalker walker = VisualTreeWalker (this);
39 while (UIElement *child = walker.Step ()) {
40 if (child->GetVisibility () != VisibilityVisible)
41 continue;
43 child->Measure (availableSize.GrowBy (-border));
44 desired = child->GetDesiredSize ();
47 desired = desired.GrowBy (border);
49 desired = desired.Min (availableSize);
50 desired = desired.Max (specified);
51 desired = desired.Min (specified);
53 return desired;
56 Size
57 Border::ArrangeOverride (Size finalSize)
59 Thickness border = *GetPadding () + *GetBorderThickness ();
61 Size specified = Size (GetWidth (), GetHeight ());
63 finalSize = finalSize.Max (specified);
64 finalSize = finalSize.Min (specified);
66 Size arranged = finalSize;
68 VisualTreeWalker walker = VisualTreeWalker (this);
69 while (UIElement *child = walker.Step ()) {
70 if (child->GetVisibility () != VisibilityVisible)
71 continue;
73 Size desired = child->GetDesiredSize ();
74 Rect childRect (0,0,finalSize.width,finalSize.height);
76 childRect = childRect.GrowBy (-border);
78 if (GetHorizontalAlignment () != HorizontalAlignmentStretch && isnan (GetWidth ()))
79 childRect.width = MIN (desired.width, childRect.width);
81 if (GetVerticalAlignment () != VerticalAlignmentStretch && isnan (GetHeight ()))
82 childRect.height = MIN (desired.height, childRect.height);
84 child->Arrange (childRect);
85 arranged = child->GetRenderSize ();
86 arranged = arranged.GrowBy (border);
88 if (GetHorizontalAlignment () == HorizontalAlignmentStretch || !isnan (GetWidth ()))
89 arranged.width = MAX (arranged.width, finalSize.width);
91 if (GetVerticalAlignment () == VerticalAlignmentStretch || !isnan (GetHeight()))
92 arranged.height = MAX (arranged.height, finalSize.height);
95 return arranged;
98 void
99 Border::Render (cairo_t *cr, Region *region, bool path_only)
101 Brush *background = GetBackground ();
102 Brush *border_brush = GetBorderBrush ();
104 cairo_set_matrix (cr, &absolute_xform);
105 cairo_new_path (cr);
107 Geometry *clip = path_only ? NULL : LayoutInformation::GetLayoutClip (this);
108 if (clip) {
109 cairo_save (cr);
110 clip->Draw (cr);
111 cairo_clip (cr);
114 CornerRadius *round = GetCornerRadius ();
115 CornerRadius adjusted = CornerRadius (0);
116 Thickness thickness = *GetBorderThickness ();
117 Rect paint_border = extents;
118 Rect paint_background = paint_border.GrowBy (-thickness);
120 if (round) {
121 adjusted = *round;
122 adjusted.topLeft = MAX (round->topLeft - MAX (thickness.left, thickness.top), 0);
123 adjusted.topRight = MAX (round->topRight - MAX (thickness.right, thickness.top), 0);
124 adjusted.bottomRight = MAX (round->bottomRight - MAX (thickness.right, thickness.bottom), 0);
125 adjusted.bottomLeft = MAX (round->bottomLeft - MAX (thickness.left, thickness.bottom), 0);
129 * NOTE filling this way can leave alpha artifacts between the border fill and bg fill
130 * but some simple inspection of the ms results make me think that is what happens there
131 * too.
133 cairo_new_path (cr);
134 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
136 if (border_brush) {
137 border_brush->SetupBrush (cr, paint_border);
140 paint_border.Draw (cr, round);
141 paint_background.Draw (cr, round ? &adjusted : NULL);
143 if (!path_only)
144 border_brush->Fill (cr);
147 if (background) {
148 background->SetupBrush (cr, paint_background);
150 paint_background.Draw (cr, round ? &adjusted : NULL);
152 if (!path_only)
153 background->Fill (cr);
156 if (clip)
157 cairo_restore (cr);
160 void
161 Border::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
163 if (args->GetProperty ()->GetOwnerType() != Type::BORDER) {
164 FrameworkElement::OnPropertyChanged (args, error);
165 return;
168 if (args->GetId () == Border::ChildProperty){
169 if (args->GetOldValue() && args->GetOldValue()->AsUIElement()) {
170 ElementRemoved (args->GetOldValue()->AsUIElement ());
171 if (args->GetOldValue()->Is(Type::FRAMEWORKELEMENT)) {
172 args->GetOldValue()->AsFrameworkElement()->SetLogicalParent (NULL, error);
173 if (error->number)
174 return;
178 if (args->GetNewValue() && args->GetNewValue()->AsUIElement()) {
179 ElementAdded (args->GetNewValue()->AsUIElement ());
180 if (args->GetNewValue()->Is(Type::FRAMEWORKELEMENT)) {
181 FrameworkElement *fwe = args->GetNewValue()->AsFrameworkElement ();
182 if (fwe->GetLogicalParent() && fwe->GetLogicalParent() != this) {
183 MoonError::FillIn (error, MoonError::ARGUMENT, "Content is already a child of another element");
184 return;
187 args->GetNewValue()->AsFrameworkElement()->SetLogicalParent (this, error);
188 if (error->number)
189 return;
193 SetSubtreeObject (args->GetNewValue() ? args->GetNewValue()->AsUIElement() : NULL);
195 UpdateBounds ();
196 InvalidateMeasure ();
198 else if (args->GetId () == Border::PaddingProperty
199 || args->GetId () == Border::BorderThicknessProperty) {
200 InvalidateMeasure ();
201 } else if (args->GetId () == Border::BackgroundProperty) {
202 Invalidate ();
204 NotifyListenersOfPropertyChange (args, error);
207 bool
208 Border::InsideObject (cairo_t *cr, double x, double y)
210 if (GetBackground ())
211 return FrameworkElement::InsideObject (cr, x, y);
213 return false;
216 void
217 Border::HitTest (cairo_t *cr, Rect r, List *uielement_list)