void Grid::PostRender (Context *ctx, Region *region, bool skip_children) { // render our chidren if we need to if (!skip_children) { VisualTreeWalker walker = VisualTreeWalker (this, ZForward, false); while (UIElement *child = walker.Step ()) child->DoRender (ctx, region); } if (GetShowGridLines () && ctx->IsMutable ()) { double offset = 0; double dash = 4; ColumnDefinitionCollection *cols = GetColumnDefinitionsNoAutoCreate (); RowDefinitionCollection *rows = GetRowDefinitionsNoAutoCreate (); cairo_t *cr = ctx->Cairo (); int col_count = cols ? cols->GetCount () : 0; int row_count = rows ? rows->GetCount () : 0; cairo_save (cr); RenderLayoutClip (cr); cairo_set_line_width(cr, 1.0); // Initially render a blue color cairo_set_dash (cr, &dash, 1, offset); cairo_set_source_rgb (cr, 0.4, 0.4, 1.0); cairo_new_path (cr); // Draw gridlines between each pair of columns/rows for (int count = 0; count < 2; count++) { for (int i = 0, offset = 0; i < col_count - 1; i++) { ColumnDefinition *def = cols->GetValueAt (i)->AsColumnDefinition (); offset += def->GetActualWidth (); cairo_move_to (cr, offset, 0); cairo_line_to (cr, offset, GetActualHeight ()); } for (int i = 0, offset = 0; i < row_count - 1; i++) { RowDefinition *def = rows->GetValueAt (i)->AsRowDefinition (); offset += def->GetActualHeight (); cairo_move_to (cr, 0, offset); cairo_line_to (cr, GetActualWidth (), offset); } cairo_stroke (cr); // For the second pass render a yellow color in the gaps between the previous dashes cairo_set_dash (cr, &dash, 1, dash); cairo_set_source_rgb (cr, 1.0, 1.0, 0.3); } cairo_restore (cr); } // Chain up, but skip children since we've already rendered them here. UIElement::PostRender (ctx, region, true); }
Size Canvas::ArrangeOverrideWithError (Size finalSize, MoonError *error) { VisualTreeWalker walker = VisualTreeWalker (this); while (FrameworkElement *child = (FrameworkElement *)walker.Step ()) { Size desired = child->GetDesiredSize (); Rect child_final = Rect (GetLeft (child), GetTop (child), desired.width, desired.height); child->ArrangeWithError (child_final, error); //child->ClearValue (LayoutInformation::LayoutClipProperty); } return finalSize; }
Size Canvas::MeasureOverrideWithError (Size availableSize, MoonError *error) { Size childSize = Size (INFINITY, INFINITY); VisualTreeWalker walker = VisualTreeWalker (this); while (UIElement *child = walker.Step ()) { child->MeasureWithError (childSize, error); } Size desired = Size (0,0); return desired; }
Size Grid::ArrangeOverrideWithError (Size finalSize, MoonError *error) { ColumnDefinitionCollection *columns = GetColumnDefinitionsNoAutoCreate (); RowDefinitionCollection *rows = GetRowDefinitionsNoAutoCreate (); int col_count = columns ? columns->GetCount () : 0; int row_count = rows ? rows->GetCount () : 0; RestoreMeasureResults (); Size total_consumed = Size (0, 0); for (int c = 0; c < col_matrix_dim; c++) total_consumed.width += col_matrix [c][c].size; for (int r = 0; r < row_matrix_dim; r++) total_consumed.height += row_matrix [r][r].size; if (total_consumed.width != finalSize.width) ExpandStarCols (finalSize); if (total_consumed.height != finalSize.height) ExpandStarRows (finalSize); for (int c = 0; c < col_count; c++) columns->GetValueAt (c)->AsColumnDefinition ()->SetActualWidth (col_matrix [c][c].size); for (int r = 0; r < row_count; r++) rows->GetValueAt (r)->AsRowDefinition ()->SetActualHeight (row_matrix [r][r].size); VisualTreeWalker walker = VisualTreeWalker (this); while (UIElement *child = walker.Step ()) { gint32 col = MIN (Grid::GetColumn (child), col_matrix_dim - 1); gint32 row = MIN (Grid::GetRow (child), row_matrix_dim - 1); gint32 colspan = MIN (Grid::GetColumnSpan (child), col_matrix_dim - col); gint32 rowspan = MIN (Grid::GetRowSpan (child), row_matrix_dim - row); Rect child_final = Rect (0, 0, 0, 0); for (int c = 0; c < col; c++) child_final.x += col_matrix [c][c].size; for (int c = col; c < col + colspan; c++) child_final.width += col_matrix [c][c].size; for (int r = 0; r < row; r++) child_final.y += row_matrix [r][r].size; for (int r = row; r < row + rowspan; r++) child_final.height += row_matrix [r][r].size; child->ArrangeWithError (child_final, error); } return finalSize; }
Size Border::MeasureOverrideWithError (Size availableSize, MoonError *error) { Size desired = Size (0,0); Thickness border = *GetPadding () + *GetBorderThickness (); // Get the desired size of our child, and include any margins we set VisualTreeWalker walker = VisualTreeWalker (this); while (UIElement *child = walker.Step ()) { child->MeasureWithError (availableSize.GrowBy (-border), error); desired = child->GetDesiredSize (); } desired = desired.GrowBy (border); desired = desired.Min (availableSize); return desired; }
Size Border::ArrangeOverrideWithError (Size finalSize, MoonError *error) { Thickness border = *GetPadding () + *GetBorderThickness (); Size arranged = finalSize; VisualTreeWalker walker = VisualTreeWalker (this); while (UIElement *child = walker.Step ()) { Rect childRect (0,0,finalSize.width,finalSize.height); childRect = childRect.GrowBy (-border); child->ArrangeWithError (childRect, error); arranged = Size (childRect.width, childRect.height).GrowBy (border); arranged = arranged.Max (finalSize); } return finalSize; }
Size Grid::MeasureOverrideWithError (Size availableSize, MoonError *error) { Size totalSize = availableSize; ColumnDefinitionCollection *columns = GetColumnDefinitionsNoAutoCreate (); RowDefinitionCollection *rows = GetRowDefinitionsNoAutoCreate (); int col_count = columns ? columns->GetCount () : 0; int row_count = rows ? rows->GetCount () : 0; Size total_stars = Size (0,0); bool empty_rows = row_count == 0; bool empty_cols = col_count == 0; if (empty_rows) row_count = 1; if (empty_cols) col_count = 1; CreateMatrices (row_count, col_count); if (empty_rows) { row_matrix [0][0] = Segment (0.0, 0, INFINITY, GridUnitTypeStar); row_matrix [0][0].stars = 1.0; total_stars.height += 1.0; } else { for (int i = 0; i < row_count; i ++) { RowDefinition *rowdef = rows->GetValueAt (i)->AsRowDefinition (); GridLength* height = rowdef->GetHeight(); rowdef->SetActualHeight (INFINITY); row_matrix [i][i] = Segment (0.0, rowdef->GetMinHeight (), rowdef->GetMaxHeight (), height->type); if (height->type == GridUnitTypePixel) { row_matrix [i][i].size = Grid::Clamp (height->val, row_matrix [i][i].min, row_matrix [i][i].max); rowdef->SetActualHeight (row_matrix [i][i].size); } if (height->type == GridUnitTypeStar) { row_matrix [i][i].stars = height->val; total_stars.height += height->val; } } } if (empty_cols) { col_matrix [0][0] = Segment (0.0, 0, INFINITY, GridUnitTypeStar); col_matrix [0][0].stars = 1.0; total_stars.width += 1.0; } else { for (int i = 0; i < col_count; i ++) { ColumnDefinition *coldef = columns->GetValueAt (i)->AsColumnDefinition (); GridLength *width = coldef->GetWidth (); coldef->SetActualWidth (INFINITY); col_matrix [i][i] = Segment (0.0, coldef->GetMinWidth (), coldef->GetMaxWidth (), width->type); if (width->type == GridUnitTypePixel) { col_matrix [i][i].size = Grid::Clamp (width->val, col_matrix [i][i].min, col_matrix [i][i].max); coldef->SetActualWidth (col_matrix [i][i].size); } if (width->type == GridUnitTypeStar) { col_matrix [i][i].stars = width->val; total_stars.width += width->val; } } } List sizes; GridNode *node; GridNode *separator = new GridNode (NULL, 0, 0, 0); sizes.Append (separator); // Pre-process the grid children so that we know what types of elements we have so // we can apply our special measuring rules. GridWalker grid_walker (this, row_matrix, row_matrix_dim, col_matrix, col_matrix_dim); for (int i = 0; i < 6; i++) { // These bools tell us which grid element type we should be measuring. i.e. // 'star/auto' means we should measure elements with a star row and auto col bool auto_auto = i == 0; bool star_auto = i == 1; bool auto_star = i == 2; bool star_auto_again = i == 3; bool non_star = i == 4; bool remaining_star = i == 5; VisualTreeWalker walker = VisualTreeWalker (this); while (UIElement *child = walker.Step ()) { gint32 col, row; gint32 colspan, rowspan; Size child_size = Size (0,0); bool star_col = false; bool star_row = false; bool auto_col = false; bool auto_row = false; col = MIN (Grid::GetColumn (child), col_count - 1); row = MIN (Grid::GetRow (child), row_count - 1); colspan = MIN (Grid::GetColumnSpan (child), col_count - col); rowspan = MIN (Grid::GetRowSpan (child), row_count - row); for (int r = row; r < row + rowspan; r++) { star_row |= row_matrix [r][r].type == GridUnitTypeStar; auto_row |= row_matrix [r][r].type == GridUnitTypeAuto; } for (int c = col; c < col + colspan; c++) { star_col |= col_matrix [c][c].type == GridUnitTypeStar; auto_col |= col_matrix [c][c].type == GridUnitTypeAuto; } // This series of if statements checks whether or not we should measure // the current element and also if we need to override the sizes // passed to the Measure call. // If the element has Auto rows and Auto columns and does not span Star // rows/cols it should only be measured in the auto_auto phase. // There are similar rules governing auto/star and star/auto elements. // NOTE: star/auto elements are measured twice. The first time with // an override for height, the second time without it. if (auto_row && auto_col && !star_row && !star_col) { if (!auto_auto) continue; child_size.width = INFINITY; child_size.height = INFINITY; } else if (star_row && auto_col && !star_col) { if (!(star_auto || star_auto_again)) continue; if (star_auto && grid_walker.HasAutoStar ()) child_size.height = INFINITY; child_size.width = INFINITY; } else if (auto_row && star_col && !star_row) { if (!auto_star) continue; child_size.height = INFINITY; } else if ((auto_row || auto_col) && !(star_row || star_col)) { if (!non_star) continue; if (auto_row) child_size.height = INFINITY; if (auto_col) child_size.width = INFINITY; } else if (!(star_row || star_col)) { if (!non_star) continue; } else { if (!remaining_star) continue; } for (int r = row; r < row + rowspan; r++) { if (row_matrix [r][r].type == GridUnitTypeStar) { double v = availableSize.height * row_matrix [r][r].stars / total_stars.height; child_size.height += Grid::Clamp (v, row_matrix [r][r].min, row_matrix [r][r].max); } else { child_size.height += row_matrix [r][r].size; } } for (int c = col; c < col + colspan; c++) { if (col_matrix [c][c].type == GridUnitTypeStar) { double v = availableSize.width * col_matrix [c][c].stars / total_stars.width; child_size.width += Grid::Clamp (v, col_matrix [c][c].min, col_matrix [c][c].max); } else { child_size.width += col_matrix [c][c].size; } } child->MeasureWithError (child_size, error); Size desired = child->GetDesiredSize(); // Elements distribute their height based on two rules: // 1) Elements with rowspan/colspan == 1 distribute their height first // 2) Everything else distributes in a LIFO manner. // As such, add all UIElements with rowspan/colspan == 1 after the separator in // the list and everything else before it. Then to process, just keep popping // elements off the end of the list. if (!star_auto) { node = new GridNode (row_matrix, row + rowspan - 1, row, desired.height); sizes.InsertBefore (node, node->row == node->col ? separator->next : separator); } node = new GridNode (col_matrix, col + colspan - 1, col, desired.width); sizes.InsertBefore (node, node->row == node->col ? separator->next : separator); } sizes.Unlink (separator); while (GridNode *node= (GridNode *) sizes.Last ()) { node->matrix [node->row][node->col].size = MAX (node->matrix [node->row][node->col].size, node->size); AllocateGridSegments (row_count, col_count); sizes.Remove (node); } // Calculate how much unused space we have so the next round of // measurements uses the right sizes for Star segments availableSize = totalSize; for (int r = 0; r < row_matrix_dim; r++) if (row_matrix [r][r].type != GridUnitTypeStar) availableSize.height = MAX (availableSize.height - row_matrix [r][r].size, 0); for (int c = 0; c < col_matrix_dim; c++) if (col_matrix [c][c].type != GridUnitTypeStar) availableSize.width = MAX (availableSize.width - col_matrix [c][c].size, 0); sizes.Append (separator); } // Once we have measured and distributed all sizes, we have to store // the results. Every time we want to expand the rows/cols, this will // be used as the baseline. SaveMeasureResults (); sizes.Remove (separator); // We have to calulate the desired grid size before expanding // star segments to consume available space. Size grid_size = Size (0, 0); for (int c = 0; c < col_count; c ++) grid_size.width += col_matrix [c][c].size; for (int r = 0; r < row_count; r ++) grid_size.height += row_matrix [r][r].size; // This is where we do the final expansion of star rows. Right now the value // of 'size' for star segments in row_matrix and col_matrix contains the // size that the segments would naturally take up. bool hasChildren = GetChildren ()->GetCount () > 0; if (totalSize.width != INFINITY && hasChildren) ExpandStarCols (totalSize); if (totalSize.height != INFINITY && hasChildren) ExpandStarRows (totalSize); // now choose whichever is smaller, our chosen size or the availableSize. return grid_size; }