1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * grid.cpp: canvas definitions.
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.
23 #include "namescope.h"
24 #include "collection.h"
28 SetObjectType (Type::GRID
);
39 Grid::Clamp (double val
, double min
, double max
)
49 Grid::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
51 if (args
->GetProperty ()->GetOwnerType() != Type::GRID
) {
52 Panel::OnPropertyChanged (args
, error
);
56 if (args
->GetId () == Grid::ShowGridLinesProperty
){
62 NotifyListenersOfPropertyChange (args
, error
);
66 Grid::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
68 if (col
== GetColumnDefinitions () ||
69 col
== GetRowDefinitions ()) {
70 //InvalidateMeasure ();
72 Panel::OnCollectionChanged (col
, args
);
79 Grid::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
81 if (col
== GetChildren ()) {
82 if (args
->GetId () == Grid::ColumnProperty
83 || args
->GetId () == Grid::RowProperty
84 || args
->GetId () == Grid::ColumnSpanProperty
85 || args
->GetId () == Grid::RowSpanProperty
) {
89 } else if (col
== GetColumnDefinitions () || col
== GetRowDefinitions ()) {
90 if (args
->GetId() != ColumnDefinition::ActualWidthProperty
91 && args
->GetId() != RowDefinition::ActualHeightProperty
) {
97 Panel::OnCollectionItemChanged (col
, obj
, args
);
101 Grid::MeasureOverride (Size availableSize
)
103 Size results
= availableSize
;
105 ColumnDefinitionCollection
*columns
= GetColumnDefinitions ();
106 RowDefinitionCollection
*rows
= GetRowDefinitions ();
107 bool free_col
= false;
108 bool free_row
= false;
110 int col_count
= columns
->GetCount ();
111 int row_count
= rows
->GetCount ();
112 Size total_stars
= Size (0,0);
114 if (col_count
== 0) {
115 columns
= new ColumnDefinitionCollection ();
116 ColumnDefinition
*coldef
= new ColumnDefinition ();
117 columns
->Add (coldef
);
123 if (row_count
== 0) {
124 rows
= new RowDefinitionCollection ();
125 RowDefinition
*rowdef
= new RowDefinition ();
132 CreateMatrices (row_count
, col_count
);
134 for (int i
= 0; i
< row_count
; i
++) {
135 RowDefinition
*rowdef
= rows
->GetValueAt (i
)->AsRowDefinition ();
136 GridLength
* height
= rowdef
->GetHeight();
138 rowdef
->SetActualHeight (INFINITY
);
139 row_matrix
[i
][i
] = Segment (0.0, rowdef
->GetMinHeight (), rowdef
->GetMaxHeight (), height
->type
);
141 if (height
->type
== GridUnitTypePixel
) {
142 row_matrix
[i
][i
].size
= Grid::Clamp (height
->val
, row_matrix
[i
][i
].min
, row_matrix
[i
][i
].max
);
143 rowdef
->SetActualHeight (row_matrix
[i
][i
].size
);
145 if (height
->type
== GridUnitTypeStar
)
146 total_stars
.height
+= height
->val
;
149 for (int i
= 0; i
< col_count
; i
++) {
150 ColumnDefinition
*coldef
= columns
->GetValueAt (i
)->AsColumnDefinition ();
151 GridLength
*width
= coldef
->GetWidth ();
153 coldef
->SetActualWidth (INFINITY
);
154 col_matrix
[i
][i
] = Segment (0.0, coldef
->GetMinWidth (), coldef
->GetMaxWidth (), width
->type
);
156 if (width
->type
== GridUnitTypePixel
) {
157 col_matrix
[i
][i
].size
= Grid::Clamp (width
->val
, col_matrix
[i
][i
].min
, col_matrix
[i
][i
].max
);
158 coldef
->SetActualWidth (col_matrix
[i
][i
].size
);
160 if (width
->type
== GridUnitTypeStar
)
161 total_stars
.width
+= width
->val
;
166 GridNode
*separator
= new GridNode (NULL
, 0, 0, 0);
167 sizes
.Append (separator
);
169 VisualTreeWalker walker
= VisualTreeWalker (this);
170 while (UIElement
*child
= walker
.Step ()) {
171 if (child
->GetVisibility () != VisibilityVisible
)
175 gint32 colspan
, rowspan
;
177 col
= MIN (Grid::GetColumn (child
), col_count
- 1);
178 row
= MIN (Grid::GetRow (child
), row_count
- 1);
179 colspan
= MIN (Grid::GetColumnSpan (child
), col_count
- col
);
180 rowspan
= MIN (Grid::GetRowSpan (child
), row_count
- row
);
181 Size pixels
= Size ();
182 Size stars
= Size ();
183 Size automatic
= Size ();
186 Size child_size
= Size (0,0);
188 for (int r
= row
; r
< row
+ rowspan
; r
++) {
189 RowDefinition
*rowdef
= rows
->GetValueAt (r
)->AsRowDefinition ();
190 GridLength
* height
= rowdef
->GetHeight();
192 switch (height
->type
) {
193 case GridUnitTypePixel
:
194 value
= Grid::Clamp (height
->val
, rowdef
->GetMinHeight (), rowdef
->GetMaxHeight ());
195 child_size
.height
+= value
;
196 pixels
.height
+= height
->val
;
198 case GridUnitTypeAuto
:
199 automatic
.height
+= 1;
200 child_size
.height
= INFINITY
;
202 case GridUnitTypeStar
:
203 stars
.height
+= height
->val
;
204 child_size
.height
+= availableSize
.height
* height
->val
/ total_stars
.height
;
209 for (int c
= col
; c
< col
+ colspan
; c
++) {
210 ColumnDefinition
*coldef
= columns
->GetValueAt (c
)->AsColumnDefinition ();
211 GridLength
* width
= coldef
->GetWidth();
213 switch (width
->type
) {
214 case GridUnitTypePixel
:
215 value
= Grid::Clamp (width
->val
, coldef
->GetMinWidth (), coldef
->GetMaxWidth ());
216 child_size
.width
+= value
;
217 pixels
.width
+= width
->val
;
219 case GridUnitTypeAuto
:
220 automatic
.width
+= 1;
221 child_size
.width
= INFINITY
;
223 case GridUnitTypeStar
:
224 stars
.width
+= width
->val
;
225 child_size
.width
+= availableSize
.width
* width
->val
/ total_stars
.width
;
230 child
->Measure (child_size
);
231 Size desired
= child
->GetDesiredSize();
233 // Elements distribute their height based on two rules:
234 // 1) Elements with rowspan/colspan == 1 distribute their height first
235 // 2) Everything else distributes in a LIFO manner.
236 // As such, add all UIElements with rowspan/colspan == 1 after the separator in
237 // the list and everything else before it. Then to process, just keep popping
238 // elements off the end of the list.
239 node
= new GridNode (row_matrix
, row
+ rowspan
- 1, row
, desired
.height
);
240 sizes
.InsertBefore (node
, node
->row
== node
->col
? separator
->next
: separator
);
242 node
= new GridNode (col_matrix
, col
+ colspan
- 1, col
, desired
.width
);
243 sizes
.InsertBefore (node
, node
->row
== node
->col
? separator
->next
: separator
);
246 sizes
.Remove (separator
);
248 while (GridNode
*node
= (GridNode
*) sizes
.Last ()) {
249 node
->matrix
[node
->row
][node
->col
].size
= MAX (node
->matrix
[node
->row
][node
->col
].size
, node
->size
);
250 AllocateGridSegments (row_count
, col_count
);
255 for (int r
= 0; r
< row_count
; r
++)
256 grid_size
.height
+= row_matrix
[r
][r
].size
;
258 for (int c
= 0; c
< col_count
; c
++)
259 grid_size
.width
+= col_matrix
[c
][c
].size
;
261 grid_size
= grid_size
.Max (GetWidth (), GetHeight ());
262 results
= results
.Min (grid_size
);
271 // now choose whichever is smaller, our chosen size or the availableSize.
276 Grid::AllocateGridSegments (int row_count
, int col_count
)
278 // First allocate the heights of the RowDefinitions, then allocate
279 // the widths of the ColumnDefinitions.
280 for (int i
= 0; i
< 2; i
++) {
281 Segment
**matrix
= i
== 0 ? row_matrix
: col_matrix
;
282 int count
= i
== 0 ? row_count
: col_count
;
284 for (int row
= count
- 1; row
>= 0; row
--) {
285 for (int col
= row
; col
>= 0; col
--) {
286 bool spans_star
= false;
287 for (int j
= row
; j
>= col
; j
--)
288 spans_star
|= matrix
[j
][j
].type
== GridUnitTypeStar
;
290 // This is the amount of pixels which must be available between the grid rows
291 // at index 'col' and 'row'. i.e. if 'row' == 0 and 'col' == 2, there must
292 // be at least 'matrix [row][col].size' pixels of height allocated between
293 // all the rows in the range col -> row.
294 double current
= matrix
[row
][col
].size
;
296 // Count how many pixels have already been allocated between the grid rows
297 // in the range col -> row. The amount of pixels allocated to each grid row/column
298 // is found on the diagonal of the matrix.
299 double total_allocated
= 0;
300 for (int i
= row
; i
>= col
; i
--)
301 total_allocated
+= matrix
[i
][i
].size
;
303 // If the size requirement has not been met, allocate the additional required
304 // size between 'pixel' rows, then 'star' rows, finally 'auto' rows, until all
305 // height has been assigned.
306 if (total_allocated
< current
) {
307 double additional
= current
- total_allocated
;
308 // Note that multiple passes may be required at each level depending on whether or not
309 // the MaxHeight/MaxWidth value prevents the row/column from accepting the full contribution
311 while (AssignSize (matrix
, col
, row
, &additional
, GridUnitTypeStar
)) { }
313 while (AssignSize (matrix
, col
, row
, &additional
, GridUnitTypePixel
)) { }
314 while (AssignSize (matrix
, col
, row
, &additional
, GridUnitTypeAuto
)) { }
323 Grid::AssignSize (Segment
**matrix
, int start
, int end
, double *size
, GridUnitType type
)
325 bool assigned
= false;
327 double contribution
= *size
;
329 for (int i
= start
; i
<= end
; i
++) {
330 if (matrix
[i
][i
].type
== type
&& matrix
[i
][i
].size
< matrix
[i
][i
].max
)
334 contribution
/= count
;
336 for (int i
= start
; i
<= end
; i
++) {
337 if (!(matrix
[i
][i
].type
== type
&& matrix
[i
][i
].size
< matrix
[i
][i
].max
))
339 double newsize
= contribution
+ matrix
[i
][i
].size
;
340 newsize
= MIN (newsize
, matrix
[i
][i
].max
);
341 assigned
|= newsize
> matrix
[i
][i
].size
;
342 *size
-= newsize
- matrix
[i
][i
].size
;
343 matrix
[i
][i
].size
= newsize
;
349 Grid::DestroyMatrices ()
351 if (row_matrix
!= NULL
) {
352 for (int i
= 0; i
< row_matrix_dim
; i
++)
353 delete [] row_matrix
[i
];
354 delete [] row_matrix
;
358 if (col_matrix
!= NULL
) {
359 for (int i
= 0; i
< col_matrix_dim
; i
++)
360 delete [] col_matrix
[i
];
361 delete [] col_matrix
;
367 Grid::CreateMatrices (int row_count
, int col_count
)
371 row_matrix_dim
= row_count
;
372 col_matrix_dim
= col_count
;
374 row_matrix
= new Segment
*[row_count
];
375 for (int i
= 0; i
< row_count
; i
++) {
376 row_matrix
[i
] = new Segment
[row_count
];
377 for (int j
= 0; j
< row_count
; j
++)
378 row_matrix
[i
][j
] = Segment ();
381 col_matrix
= new Segment
*[col_count
];
382 for (int i
= 0; i
< col_count
; i
++) {
383 col_matrix
[i
] = new Segment
[col_count
];
384 for (int j
= 0; j
< col_count
; j
++)
385 col_matrix
[i
][j
] = Segment ();
390 Grid::ComputeBounds ()
392 Panel::ComputeBounds ();
394 if (GetShowGridLines ()) {
395 extents
= Rect (0,0,GetActualWidth (),GetActualHeight ());
396 bounds
= IntersectBoundsWithClipPath (extents
, false).Transform (&absolute_xform
);
397 bounds_with_children
= bounds_with_children
.Union (bounds
);
401 Grid::PostRender (cairo_t
*cr
, Region
*region
, bool front_to_back
)
403 // render our chidren if not in front to back mode
404 if (!front_to_back
) {
405 VisualTreeWalker walker
= VisualTreeWalker (this, ZForward
);
406 while (UIElement
*child
= walker
.Step ())
407 child
->DoRender (cr
, region
);
410 if (GetShowGridLines ()) {
413 ColumnDefinitionCollection
*cols
= GetColumnDefinitions ();
414 RowDefinitionCollection
*rows
= GetRowDefinitions ();
416 cairo_set_line_width(cr
, 1.0);
417 // Initially render a blue color
418 cairo_set_dash (cr
, &dash
, 1, offset
);
419 cairo_set_source_rgb (cr
, 0.4, 0.4, 1.0);
421 // Draw gridlines between each pair of columns/rows
422 for (int count
= 0; count
< 2; count
++) {
424 for (int i
= 0, offset
= 0; i
< cols
->GetCount () - 1; i
++) {
425 ColumnDefinition
*def
= cols
->GetValueAt (i
)->AsColumnDefinition ();
426 offset
+= def
->GetActualWidth ();
427 cairo_move_to (cr
, offset
, 0);
428 cairo_line_to (cr
, offset
, GetActualHeight ());
431 for (int i
= 0, offset
= 0; i
< rows
->GetCount () -1; i
++) {
432 RowDefinition
*def
= rows
->GetValueAt (i
)->AsRowDefinition ();
433 offset
+= def
->GetActualHeight ();
434 cairo_move_to (cr
, 0, offset
);
435 cairo_line_to (cr
, GetActualWidth (), offset
);
440 // For the second pass render a yellow color in the gaps between the previous dashes
441 cairo_set_dash (cr
, &dash
, 1, dash
);
442 cairo_set_source_rgb (cr
, 1.0, 1.0, 0.3);
446 // Chain up in front_to_back mode since we've alread rendered content
447 UIElement::PostRender (cr
, region
, true);
451 Grid::ArrangeOverride (Size finalSize
)
453 ColumnDefinitionCollection
*columns
= GetColumnDefinitions ();
454 RowDefinitionCollection
*rows
= GetRowDefinitions ();
455 bool free_col
= false;
456 bool free_row
= false;
458 int col_count
= columns
->GetCount ();
459 int row_count
= rows
->GetCount ();
461 if (col_count
== 0) {
462 columns
= new ColumnDefinitionCollection ();
463 ColumnDefinition
*coldef
= new ColumnDefinition ();
464 columns
->Add (coldef
);
470 if (row_count
== 0) {
471 rows
= new RowDefinitionCollection ();
472 RowDefinition
*rowdef
= new RowDefinition ();
479 for (int i
= 0; i
< row_count
; i
++)
480 rows
->GetValueAt (i
)->AsRowDefinition ()->SetActualHeight (row_matrix
[i
][i
].size
);
482 for (int i
= 0; i
< col_count
; i
++)
483 columns
->GetValueAt (i
)->AsColumnDefinition ()->SetActualWidth (col_matrix
[i
][i
].size
);
485 Size requested
= Size ();
486 Size star_size
= Size ();
487 double row_stars
= 0.0;
488 HorizontalAlignment horiz
= !isnan (GetWidth ()) ? HorizontalAlignmentStretch
: GetHorizontalAlignment ();
489 VerticalAlignment vert
= !isnan (GetHeight ()) ? VerticalAlignmentStretch
: GetVerticalAlignment ();
491 for (int i
= 0; i
< row_count
; i
++) {
492 RowDefinition
*rowdef
= rows
->GetValueAt (i
)->AsRowDefinition ();
493 GridLength
* height
= rowdef
->GetHeight();
495 switch (height
->type
) {
496 case GridUnitTypeStar
:
497 // Star columns distribute evenly
498 requested
.height
+= rowdef
->GetActualHeight ();
499 star_size
.height
+= rowdef
->GetActualHeight ();
500 //if (vert == VerticalAlignmentStretch)
501 // rowdef->SetActualHeight (0.0);
503 row_stars
+= height
->val
;
505 case GridUnitTypePixel
:
506 requested
.height
+= rowdef
->GetActualHeight ();
508 case GridUnitTypeAuto
:
509 requested
.height
+= rowdef
->GetActualHeight ();
514 double col_stars
= 0.0;
515 for (int i
= 0; i
< col_count
; i
++) {
516 ColumnDefinition
*coldef
= columns
->GetValueAt (i
)->AsColumnDefinition ();
517 GridLength
* width
= coldef
->GetWidth();
519 switch (width
->type
) {
520 case GridUnitTypeStar
:
521 // Star columns distribute evenly
522 requested
.width
+= coldef
->GetActualWidth ();
523 star_size
.width
+= coldef
->GetActualWidth ();
524 //if (horiz == HorizontalAlignmentStretch)
525 // coldef->SetActualWidth (0.0);
527 col_stars
+= width
->val
;
529 case GridUnitTypePixel
:
530 requested
.width
+= coldef
->GetActualWidth ();
532 case GridUnitTypeAuto
:
533 requested
.width
+= coldef
->GetActualWidth ();
538 Size remaining
= Size (finalSize
.width
- requested
.width
, finalSize
.height
- requested
.height
);
540 if (horiz
!= HorizontalAlignmentStretch
)
541 remaining
.width
= MIN (remaining
.width
, 0);
543 if (vert
!= VerticalAlignmentStretch
)
544 remaining
.height
= MIN (remaining
.height
, 0);
546 if (remaining
.height
!= 0) {
547 remaining
.height
+= star_size
.height
;
549 for (int i
= 0; i
< row_count
; i
++) {
550 RowDefinition
*rowdef
= rows
->GetValueAt (i
)->AsRowDefinition ();
551 GridLength
* height
= rowdef
->GetHeight();
553 if (height
->type
== GridUnitTypeStar
)
554 rowdef
->SetActualHeight (MAX ((remaining
.height
* height
->val
/ row_stars
), 0));
558 if (remaining
.width
!= 0) {
559 remaining
.width
+= star_size
.width
;
561 for (int i
= 0; i
< col_count
; i
++) {
562 ColumnDefinition
*coldef
= columns
->GetValueAt (i
)->AsColumnDefinition ();
563 GridLength
* width
= coldef
->GetWidth();
565 if (width
->type
== GridUnitTypeStar
)
566 coldef
->SetActualWidth (MAX ((remaining
.width
* width
->val
/ col_stars
), 0));
571 Size arranged
= finalSize
;
573 VisualTreeWalker walker
= VisualTreeWalker (this);
574 while (UIElement
*child
= walker
.Step ()) {
575 if (child
->GetVisibility () != VisibilityVisible
)
578 gint32 col
= MIN (Grid::GetColumn (child
), col_count
- 1);
579 gint32 row
= MIN (Grid::GetRow (child
), row_count
- 1);
580 gint32 colspan
= MIN (Grid::GetColumnSpan (child
), col_count
- col
);
581 gint32 rowspan
= MIN (Grid::GetRowSpan (child
), row_count
- row
);
583 Rect child_final
= Rect (0, 0, 0, 0);
592 for (int r
= 0; r
< row
+ rowspan
; r
++) {
593 RowDefinition
*rowdef
= rows
->GetValueAt (r
)->AsRowDefinition ();
596 child_final
.y
+= rowdef
->GetActualHeight ();
598 child_final
.height
+= rowdef
->GetActualHeight ();
600 min_size
.height
+= rowdef
->GetMinHeight ();
601 max_size
.height
+= rowdef
->GetMaxHeight ();
605 for (int c
= 0; c
< col
+ colspan
; c
++) {
606 ColumnDefinition
*coldef
= columns
->GetValueAt (c
)->AsColumnDefinition ();
609 child_final
.x
+= coldef
->GetActualWidth ();
611 child_final
.width
+= coldef
->GetActualWidth ();
613 min_size
.width
+= coldef
->GetMinWidth ();
614 max_size
.width
+= coldef
->GetMaxWidth ();
618 child
->Arrange (child_final
);
619 Size child_arranged
= child
->GetRenderSize ();
621 if (horiz
== HorizontalAlignmentStretch
)
622 arranged
.width
= MAX (child_final
.x
+ child_final
.width
, finalSize
.width
);
624 arranged
.width
= MAX (child_final
.x
+ child_final
.width
, arranged
.width
);
626 if (vert
== VerticalAlignmentStretch
)
627 arranged
.height
= MAX (child_final
.y
+ child_final
.height
, finalSize
.height
);
629 arranged
.height
= MAX (child_final
.y
+ child_final
.height
, arranged
.height
);
642 // ColumnDefinitionCollection
645 ColumnDefinitionCollection::ColumnDefinitionCollection ()
647 SetObjectType (Type::COLUMNDEFINITION_COLLECTION
);
650 ColumnDefinitionCollection::~ColumnDefinitionCollection ()
655 ColumnDefinitionCollection::AddedToCollection (Value
*value
, MoonError
*error
)
657 if (Contains (value
)) {
658 MoonError::FillIn (error
, MoonError::ARGUMENT
, "ColumnDefinition is already a member of this collection.");
661 return DependencyObjectCollection::AddedToCollection (value
, error
);
668 ColumnDefinition::ColumnDefinition ()
670 SetObjectType (Type::COLUMNDEFINITION
);
673 ColumnDefinition::~ColumnDefinition ()
678 // RowDefinitionCollection
681 RowDefinitionCollection::RowDefinitionCollection ()
683 SetObjectType (Type::ROWDEFINITION_COLLECTION
);
686 RowDefinitionCollection::~RowDefinitionCollection ()
691 RowDefinitionCollection::AddedToCollection (Value
*value
, MoonError
*error
)
693 if (Contains (value
)) {
694 MoonError::FillIn (error
, MoonError::ARGUMENT
, "RowDefinition is already a member of this collection.");
697 return DependencyObjectCollection::AddedToCollection (value
, error
);
704 RowDefinition::RowDefinition ()
706 SetObjectType (Type::ROWDEFINITION
);
709 RowDefinition::~RowDefinition ()
715 Init (0.0, 0.0, INFINITY
, GridUnitTypePixel
);
718 Segment::Segment (double size
, double min
, double max
, GridUnitType type
)
720 Init (size
, min
, max
, type
);
724 Segment::Init (double size
, double min
, double max
, GridUnitType type
)
730 this->size
= Grid::Clamp (size
, min
, max
);