add the 2.1-bootstrap dir to MONO_PATH when running smcs
[moon.git] / src / grid.cpp
blob4449600ffe41adc5b1e1e2be960a2936f0f18b4a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * grid.cpp: canvas definitions.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <math.h>
18 #include "brush.h"
19 #include "rect.h"
20 #include "canvas.h"
21 #include "grid.h"
22 #include "runtime.h"
23 #include "namescope.h"
24 #include "collection.h"
26 Grid::Grid ()
28 SetObjectType (Type::GRID);
31 Grid::~Grid ()
35 void
36 Grid::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
38 if (args->GetProperty ()->GetOwnerType() != Type::GRID) {
39 Panel::OnPropertyChanged (args, error);
40 return;
43 if (args->GetId () == Grid::ShowGridLinesProperty){
44 Invalidate ();
47 InvalidateMeasure ();
49 NotifyListenersOfPropertyChange (args, error);
52 void
53 Grid::OnCollectionChanged (Collection *col, CollectionChangedEventArgs *args)
55 if (col == GetColumnDefinitions () ||
56 col == GetRowDefinitions ()) {
57 //InvalidateMeasure ();
58 } else {
59 Panel::OnCollectionChanged (col, args);
62 InvalidateMeasure ();
65 void
66 Grid::OnCollectionItemChanged (Collection *col, DependencyObject *obj, PropertyChangedEventArgs *args)
68 if (col == GetChildren ()) {
69 if (args->GetId () == Grid::ColumnProperty
70 || args->GetId () == Grid::RowProperty
71 || args->GetId () == Grid::ColumnSpanProperty
72 || args->GetId () == Grid::RowSpanProperty) {
73 InvalidateMeasure ();
74 return;
76 } else if (col == GetColumnDefinitions () || col == GetRowDefinitions ()) {
77 if (args->GetId() != ColumnDefinition::ActualWidthProperty
78 && args->GetId() != RowDefinition::ActualHeightProperty) {
79 InvalidateMeasure ();
81 return;
84 Panel::OnCollectionItemChanged (col, obj, args);
87 Size
88 Grid::MeasureOverride (Size availableSize)
90 Size results = availableSize;
92 ColumnDefinitionCollection *columns = GetColumnDefinitions ();
93 RowDefinitionCollection *rows = GetRowDefinitions ();
94 bool free_col = false;
95 bool free_row = false;
97 int col_count = columns->GetCount ();
98 int row_count = rows->GetCount ();
99 Size total_stars = Size (0,0);
101 if (col_count == 0) {
102 columns = new ColumnDefinitionCollection ();
103 ColumnDefinition *coldef = new ColumnDefinition ();
104 columns->Add (coldef);
105 coldef->unref ();
106 free_col = true;
107 col_count = 1;
110 if (row_count == 0) {
111 rows = new RowDefinitionCollection ();
112 RowDefinition *rowdef = new RowDefinition ();
113 rows->Add (rowdef);
114 rowdef->unref ();
115 free_row = true;
116 row_count = 1;
119 for (int i = 0; i < row_count; i ++) {
120 RowDefinition *rowdef = rows->GetValueAt (i)->AsRowDefinition ();
121 GridLength* height = rowdef->GetHeight();
123 rowdef->SetActualHeight (0.0);
125 if (height->type == GridUnitTypePixel)
126 rowdef->SetActualHeight (height->val);
127 if (height->type == GridUnitTypeStar)
128 total_stars.height += height->val;
131 for (int i = 0; i < col_count; i ++) {
132 ColumnDefinition *coldef = columns->GetValueAt (i)->AsColumnDefinition ();
133 GridLength *width = coldef->GetWidth ();
135 coldef->SetActualWidth (0.0);
137 if (width->type == GridUnitTypePixel)
138 coldef->SetActualWidth (width->val);
139 if (width->type == GridUnitTypeStar)
140 total_stars.width += width->val;
143 magic = Size ();
144 VisualTreeWalker walker = VisualTreeWalker (this);
145 while (UIElement *child = walker.Step ()) {
146 if (child->GetVisibility () != VisibilityVisible)
147 continue;
149 gint32 col, row;
150 gint32 colspan, rowspan;
152 col = MIN (Grid::GetColumn (child), col_count - 1);
153 row = MIN (Grid::GetRow (child), row_count - 1);
154 colspan = MIN (Grid::GetColumnSpan (child), col_count - col);
155 rowspan = MIN (Grid::GetRowSpan (child), row_count - row);
156 Size pixels = Size ();
157 Size stars = Size ();
158 Size automatic = Size ();
161 Size child_size = Size (0,0);
162 Size min_size = Size (0,0);
163 Size max_size = Size (0,0);
165 for (int r = row; r < row + rowspan; r++) {
166 RowDefinition *rowdef = rows->GetValueAt (r)->AsRowDefinition ();
167 GridLength* height = rowdef->GetHeight();
169 switch (height->type) {
170 case GridUnitTypePixel:
171 pixels.height += height->val;
172 child_size.height += height->val;
173 break;
174 case GridUnitTypeAuto:
175 automatic.height += 1;
176 child_size.height += rowdef->GetMaxHeight ();
177 break;
178 case GridUnitTypeStar:
179 stars.height += height->val;
180 child_size.height += availableSize.height * stars.height / total_stars.height;
181 break;
184 min_size.height += rowdef->GetMinHeight ();
185 max_size.height += rowdef->GetMaxHeight ();
188 for (int c = col; c < col + colspan; c++) {
189 ColumnDefinition *coldef = columns->GetValueAt (c)->AsColumnDefinition ();
190 GridLength* width = coldef->GetWidth();
192 switch (width->type) {
193 case GridUnitTypePixel:
194 pixels.width += width->val;
195 child_size.width += width->val;
196 break;
197 case GridUnitTypeAuto:
198 automatic.width += 1;
199 child_size.width += coldef->GetMaxWidth ();
200 break;
201 case GridUnitTypeStar:
202 stars.width += width->val;
203 child_size.width += availableSize.width * stars.width / total_stars.width;
204 break;
207 min_size.width += coldef->GetMinWidth ();
208 max_size.width += coldef->GetMaxWidth ();
211 child_size = child_size.Min (max_size);
212 child_size = child_size.Max (min_size);
214 child->Measure (child_size);
215 Size desired = child->GetDesiredSize();
217 Size remaining = desired;
218 for (int c = col; c < col + colspan; c++){
219 ColumnDefinition *coldef = columns->GetValueAt (c)->AsColumnDefinition ();
220 GridLength *width = coldef->GetWidth ();
222 double contribution = 0;
223 switch (width->type) {
224 case GridUnitTypeAuto:
225 if (stars.width <= 0)
226 contribution = (desired.width - pixels.width) / automatic.width;
227 break;
228 case GridUnitTypePixel:
229 contribution = width->val;
230 break;
231 default:
232 contribution = remaining.width;
233 break;
236 contribution = MAX (contribution, coldef->GetMinWidth ());
237 contribution = MIN (contribution, coldef->GetMaxWidth ());
239 coldef->SetActualWidth (MAX(coldef->GetActualWidth (), contribution));
240 remaining.width -= contribution;
243 for (int r = row; r < row + rowspan; r++){
244 RowDefinition *rowdef = rows->GetValueAt (r)->AsRowDefinition ();
245 GridLength *height = rowdef->GetHeight ();
247 double contribution = 0;
248 switch (height->type) {
249 case GridUnitTypeAuto:
250 if (stars.height <= 0)
251 contribution = (desired.height - pixels.height) / automatic.height;
252 break;
253 case GridUnitTypePixel:
254 contribution = height->val;
255 break;
256 default:
257 contribution = remaining.height;
258 break;
261 contribution = MAX (contribution, rowdef->GetMinHeight ());
262 contribution = MIN (contribution, rowdef->GetMaxHeight ());
264 rowdef->SetActualHeight (MAX (rowdef->GetActualHeight (), contribution));
265 remaining.height -= contribution;
269 Size grid_size;
270 for (int r = 0; r < row_count; r ++) {
271 grid_size.height += rows->GetValueAt (r)->AsRowDefinition ()->GetActualHeight ();
274 for (int c = 0; c < col_count; c ++) {
275 grid_size.width += columns->GetValueAt (c)->AsColumnDefinition ()->GetActualWidth ();
278 grid_size = grid_size.Max (GetWidth (), GetHeight ());
279 results = results.Min (grid_size);
281 if (free_col) {
282 magic.width = columns->GetValueAt (0)->AsColumnDefinition ()->GetActualWidth ();
283 columns->unref ();
286 if (free_row) {
287 magic.height = rows->GetValueAt (0)->AsRowDefinition ()->GetActualHeight ();
288 rows->unref ();
290 // now choose whichever is smaller, our chosen size or the availableSize.
291 return results;
294 void
295 Grid::ComputeBounds ()
297 Panel::ComputeBounds ();
299 if (GetShowGridLines ()) {
300 extents = Rect (0,0,GetActualWidth (),GetActualHeight ());
301 bounds = IntersectBoundsWithClipPath (extents, false).Transform (&absolute_xform);
302 bounds_with_children = bounds_with_children.Union (bounds);
305 void
306 Grid::PostRender (cairo_t *cr, Region *region, bool front_to_back)
308 // render our chidren if not in front to back mode
309 if (!front_to_back) {
310 VisualTreeWalker walker = VisualTreeWalker (this, ZForward);
311 while (UIElement *child = walker.Step ())
312 child->DoRender (cr, region);
315 if (GetShowGridLines ()) {
316 double offset = 0;
317 double dash = 4;
318 ColumnDefinitionCollection *cols = GetColumnDefinitions ();
319 RowDefinitionCollection *rows = GetRowDefinitions ();
321 cairo_set_line_width(cr, 1.0);
322 // Initially render a blue color
323 cairo_set_dash (cr, &dash, 1, offset);
324 cairo_set_source_rgb (cr, 0.4, 0.4, 1.0);
326 // Draw gridlines between each pair of columns/rows
327 for (int count = 0; count < 2; count++) {
329 for (int i = 0, offset = 0; i < cols->GetCount () - 1; i++) {
330 ColumnDefinition *def = cols->GetValueAt (i)->AsColumnDefinition ();
331 offset += def->GetActualWidth ();
332 cairo_move_to (cr, offset, 0);
333 cairo_line_to (cr, offset, GetActualHeight ());
336 for (int i = 0, offset = 0; i < rows->GetCount () -1; i++) {
337 RowDefinition *def = rows->GetValueAt (i)->AsRowDefinition ();
338 offset += def->GetActualHeight ();
339 cairo_move_to (cr, 0, offset);
340 cairo_line_to (cr, GetActualWidth (), offset);
343 cairo_stroke (cr);
345 // For the second pass render a yellow color in the gaps between the previous dashes
346 cairo_set_dash (cr, &dash, 1, dash);
347 cairo_set_source_rgb (cr, 1.0, 1.0, 0.3);
351 // Chain up in front_to_back mode since we've alread rendered content
352 UIElement::PostRender (cr, region, true);
355 Size
356 Grid::ArrangeOverride (Size finalSize)
358 ColumnDefinitionCollection *columns = GetColumnDefinitions ();
359 RowDefinitionCollection *rows = GetRowDefinitions ();
360 bool free_col = false;
361 bool free_row = false;
363 int col_count = columns->GetCount ();
364 int row_count = rows->GetCount ();
366 if (col_count == 0) {
367 columns = new ColumnDefinitionCollection ();
368 ColumnDefinition *coldef = new ColumnDefinition ();
369 coldef->SetActualWidth (magic.width);
370 columns->Add (coldef);
371 coldef->unref ();
372 free_col = true;
373 col_count = 1;
376 if (row_count == 0) {
377 rows = new RowDefinitionCollection ();
378 RowDefinition *rowdef = new RowDefinition ();
379 rowdef->SetActualHeight (magic.height);
380 rows->Add (rowdef);
381 rowdef->unref ();
382 free_row = true;
383 row_count = 1;
386 Size requested = Size ();
387 Size star_size = Size ();
388 double row_stars = 0.0;
389 HorizontalAlignment horiz = !isnan (GetWidth ()) ? HorizontalAlignmentStretch : GetHorizontalAlignment ();
390 VerticalAlignment vert = !isnan (GetHeight ()) ? VerticalAlignmentStretch : GetVerticalAlignment ();
392 for (int i = 0; i < row_count; i ++) {
393 RowDefinition *rowdef = rows->GetValueAt (i)->AsRowDefinition ();
394 GridLength* height = rowdef->GetHeight();
396 switch (height->type) {
397 case GridUnitTypeStar:
398 // Star columns distribute evenly
399 requested.height += rowdef->GetActualHeight ();
400 star_size.height += rowdef->GetActualHeight ();
401 //if (vert == VerticalAlignmentStretch)
402 // rowdef->SetActualHeight (0.0);
404 row_stars += height->val;
405 break;
406 case GridUnitTypePixel:
407 requested.height += rowdef->GetActualHeight ();
408 break;
409 case GridUnitTypeAuto:
410 requested.height += rowdef->GetActualHeight ();
411 break;
415 double col_stars = 0.0;
416 for (int i = 0; i < col_count; i ++) {
417 ColumnDefinition *coldef = columns->GetValueAt (i)->AsColumnDefinition ();
418 GridLength* width = coldef->GetWidth();
420 switch (width->type) {
421 case GridUnitTypeStar:
422 // Star columns distribute evenly
423 requested.width += coldef->GetActualWidth ();
424 star_size.width += coldef->GetActualWidth ();
425 //if (horiz == HorizontalAlignmentStretch)
426 // coldef->SetActualWidth (0.0);
428 col_stars += width->val;
429 break;
430 case GridUnitTypePixel:
431 requested.width += coldef->GetActualWidth ();
432 break;
433 case GridUnitTypeAuto:
434 requested.width += coldef->GetActualWidth ();
435 break;
439 Size remaining = Size (finalSize.width - requested.width, finalSize.height - requested.height);
441 if (horiz != HorizontalAlignmentStretch)
442 remaining.width = MIN (remaining.width, 0);
444 if (vert != VerticalAlignmentStretch)
445 remaining.height = MIN (remaining.height, 0);
447 if (remaining.height != 0) {
448 remaining.height += star_size.height;
450 for (int i = 0; i < row_count; i ++) {
451 RowDefinition *rowdef = rows->GetValueAt (i)->AsRowDefinition ();
452 GridLength* height = rowdef->GetHeight();
454 if (height->type == GridUnitTypeStar)
455 rowdef->SetActualHeight (MAX ((remaining.height * height->val / row_stars), 0));
459 if (remaining.width != 0) {
460 remaining.width += star_size.width;
462 for (int i = 0; i < col_count; i ++) {
463 ColumnDefinition *coldef = columns->GetValueAt (i)->AsColumnDefinition ();
464 GridLength* width = coldef->GetWidth();
466 if (width->type == GridUnitTypeStar)
467 coldef->SetActualWidth (MAX ((remaining.width * width->val / col_stars), 0));
471 bool first = true;
472 Size arranged = finalSize;
474 VisualTreeWalker walker = VisualTreeWalker (this);
475 while (UIElement *child = walker.Step ()) {
476 if (child->GetVisibility () != VisibilityVisible)
477 continue;
479 gint32 col = MIN (Grid::GetColumn (child), col_count - 1);
480 gint32 row = MIN (Grid::GetRow (child), row_count - 1);
481 gint32 colspan = MIN (Grid::GetColumnSpan (child), col_count - col);
482 gint32 rowspan = MIN (Grid::GetRowSpan (child), row_count - row);
484 Rect child_final = Rect (0, 0, 0, 0);
485 Size min_size;
486 Size max_size;
488 if (first) {
489 arranged = Size ();
490 first = false;
493 for (int r = 0; r < row + rowspan; r++) {
494 RowDefinition *rowdef = rows->GetValueAt (r)->AsRowDefinition ();
496 if (r < row) {
497 child_final.y += rowdef->GetActualHeight ();
498 } else {
499 child_final.height += rowdef->GetActualHeight ();
501 min_size.height += rowdef->GetMinHeight ();
502 max_size.height += rowdef->GetMaxHeight ();
506 for (int c = 0; c < col + colspan; c++) {
507 ColumnDefinition *coldef = columns->GetValueAt (c)->AsColumnDefinition ();
509 if (c < col) {
510 child_final.x += coldef->GetActualWidth ();
511 } else {
512 child_final.width += coldef->GetActualWidth ();
514 min_size.width += coldef->GetMinWidth ();
515 max_size.width += coldef->GetMaxWidth ();
519 child->Arrange (child_final);
520 Size child_arranged = child->GetRenderSize ();
522 if (horiz == HorizontalAlignmentStretch)
523 arranged.width = MAX (child_final.x + child_final.width, finalSize.width);
524 else
525 arranged.width = MAX (child_final.x + child_final.width, arranged.width);
527 if (vert == VerticalAlignmentStretch)
528 arranged.height = MAX (child_final.y + child_final.height, finalSize.height);
529 else
530 arranged.height = MAX (child_final.y + child_final.height, arranged.height);
533 if (free_col)
534 columns->unref ();
536 if (free_row)
537 rows->unref ();
539 return arranged;
543 // ColumnDefinitionCollection
546 ColumnDefinitionCollection::ColumnDefinitionCollection ()
548 SetObjectType (Type::COLUMNDEFINITION_COLLECTION);
551 ColumnDefinitionCollection::~ColumnDefinitionCollection ()
555 bool
556 ColumnDefinitionCollection::AddedToCollection (Value *value, MoonError *error)
558 if (Contains (value)) {
559 MoonError::FillIn (error, MoonError::ARGUMENT, "ColumnDefinition is already a member of this collection.");
560 return false;
562 return DependencyObjectCollection::AddedToCollection (value, error);
566 // ColumnDefinition
569 ColumnDefinition::ColumnDefinition ()
571 SetObjectType (Type::COLUMNDEFINITION);
574 ColumnDefinition::~ColumnDefinition ()
579 // RowDefinitionCollection
582 RowDefinitionCollection::RowDefinitionCollection ()
584 SetObjectType (Type::ROWDEFINITION_COLLECTION);
587 RowDefinitionCollection::~RowDefinitionCollection ()
591 bool
592 RowDefinitionCollection::AddedToCollection (Value *value, MoonError *error)
594 if (Contains (value)) {
595 MoonError::FillIn (error, MoonError::ARGUMENT, "RowDefinition is already a member of this collection.");
596 return false;
598 return DependencyObjectCollection::AddedToCollection (value, error);
602 // RowDefinition
605 RowDefinition::RowDefinition ()
607 SetObjectType (Type::ROWDEFINITION);
610 RowDefinition::~RowDefinition ()