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
);
36 Grid::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
38 if (args
->GetProperty ()->GetOwnerType() != Type::GRID
) {
39 Panel::OnPropertyChanged (args
, error
);
43 if (args
->GetId () == Grid::ShowGridLinesProperty
){
49 NotifyListenersOfPropertyChange (args
, error
);
53 Grid::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
55 if (col
== GetColumnDefinitions () ||
56 col
== GetRowDefinitions ()) {
57 //InvalidateMeasure ();
59 Panel::OnCollectionChanged (col
, args
);
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
) {
76 } else if (col
== GetColumnDefinitions () || col
== GetRowDefinitions ()) {
77 if (args
->GetId() != ColumnDefinition::ActualWidthProperty
78 && args
->GetId() != RowDefinition::ActualHeightProperty
) {
84 Panel::OnCollectionItemChanged (col
, obj
, args
);
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
);
110 if (row_count
== 0) {
111 rows
= new RowDefinitionCollection ();
112 RowDefinition
*rowdef
= new RowDefinition ();
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
;
144 VisualTreeWalker walker
= VisualTreeWalker (this);
145 while (UIElement
*child
= walker
.Step ()) {
146 if (child
->GetVisibility () != VisibilityVisible
)
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
;
174 case GridUnitTypeAuto
:
175 automatic
.height
+= 1;
176 child_size
.height
+= rowdef
->GetMaxHeight ();
178 case GridUnitTypeStar
:
179 stars
.height
+= height
->val
;
180 child_size
.height
+= availableSize
.height
* stars
.height
/ total_stars
.height
;
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
;
197 case GridUnitTypeAuto
:
198 automatic
.width
+= 1;
199 child_size
.width
+= coldef
->GetMaxWidth ();
201 case GridUnitTypeStar
:
202 stars
.width
+= width
->val
;
203 child_size
.width
+= availableSize
.width
* stars
.width
/ total_stars
.width
;
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
;
228 case GridUnitTypePixel
:
229 contribution
= width
->val
;
232 contribution
= remaining
.width
;
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
;
253 case GridUnitTypePixel
:
254 contribution
= height
->val
;
257 contribution
= remaining
.height
;
261 contribution
= MAX (contribution
, rowdef
->GetMinHeight ());
262 contribution
= MIN (contribution
, rowdef
->GetMaxHeight ());
264 rowdef
->SetActualHeight (MAX (rowdef
->GetActualHeight (), contribution
));
265 remaining
.height
-= contribution
;
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
);
282 magic
.width
= columns
->GetValueAt (0)->AsColumnDefinition ()->GetActualWidth ();
287 magic
.height
= rows
->GetValueAt (0)->AsRowDefinition ()->GetActualHeight ();
290 // now choose whichever is smaller, our chosen size or the availableSize.
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
);
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 ()) {
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
);
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);
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
);
376 if (row_count
== 0) {
377 rows
= new RowDefinitionCollection ();
378 RowDefinition
*rowdef
= new RowDefinition ();
379 rowdef
->SetActualHeight (magic
.height
);
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
;
406 case GridUnitTypePixel
:
407 requested
.height
+= rowdef
->GetActualHeight ();
409 case GridUnitTypeAuto
:
410 requested
.height
+= rowdef
->GetActualHeight ();
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
;
430 case GridUnitTypePixel
:
431 requested
.width
+= coldef
->GetActualWidth ();
433 case GridUnitTypeAuto
:
434 requested
.width
+= coldef
->GetActualWidth ();
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));
472 Size arranged
= finalSize
;
474 VisualTreeWalker walker
= VisualTreeWalker (this);
475 while (UIElement
*child
= walker
.Step ()) {
476 if (child
->GetVisibility () != VisibilityVisible
)
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);
493 for (int r
= 0; r
< row
+ rowspan
; r
++) {
494 RowDefinition
*rowdef
= rows
->GetValueAt (r
)->AsRowDefinition ();
497 child_final
.y
+= rowdef
->GetActualHeight ();
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 ();
510 child_final
.x
+= coldef
->GetActualWidth ();
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
);
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
);
530 arranged
.height
= MAX (child_final
.y
+ child_final
.height
, arranged
.height
);
543 // ColumnDefinitionCollection
546 ColumnDefinitionCollection::ColumnDefinitionCollection ()
548 SetObjectType (Type::COLUMNDEFINITION_COLLECTION
);
551 ColumnDefinitionCollection::~ColumnDefinitionCollection ()
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.");
562 return DependencyObjectCollection::AddedToCollection (value
, error
);
569 ColumnDefinition::ColumnDefinition ()
571 SetObjectType (Type::COLUMNDEFINITION
);
574 ColumnDefinition::~ColumnDefinition ()
579 // RowDefinitionCollection
582 RowDefinitionCollection::RowDefinitionCollection ()
584 SetObjectType (Type::ROWDEFINITION_COLLECTION
);
587 RowDefinitionCollection::~RowDefinitionCollection ()
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.");
598 return DependencyObjectCollection::AddedToCollection (value
, error
);
605 RowDefinition::RowDefinition ()
607 SetObjectType (Type::ROWDEFINITION
);
610 RowDefinition::~RowDefinition ()