// Update desired size of the button. If the size changes, trigger layout // (which will in turn request repaints of affected areas) // To keep size of the button stable (to avoid re-layouts) we use font // line spacing to be the height of the button, even if text is empty. // Note: it might be that for some cases button with no text should collapse // in size but we don't have a need for that yet void Button::RecalculateSize(bool repaintIfSizeDidntChange) { Size prevSize = desiredSize; desiredSize = GetBorderAndPaddingSize(cachedStyle); Graphics *gfx = AllocGraphicsForMeasureText(); CachedStyle *s = cachedStyle; Font *font = GetCachedFont(s->fontName, s->fontSize, s->fontWeight); textDx = 0; float fontDy = font->GetHeight(gfx); RectF bbox; if (text) { bbox = MeasureText(gfx, font, text); textDx = (size_t)bbox.Width; // TODO: round up? // bbox shouldn't be bigger than fontDy. We apply magic adjustment because // bbox is bigger in n-th decimal point CrashIf(fontDy + .5f < bbox.Height); } desiredSize.Width += textDx; desiredSize.Height += (INT)fontDy; // TODO: round up? FreeGraphicsForMeasureText(gfx); if (!prevSize.Equals(desiredSize)) RequestLayout(this); else if (repaintIfSizeDidntChange) RequestRepaint(this); }
// TODO: the position still seems a bit off wrt. padding void ButtonVector::RecalculateSize(bool repaintIfSizeDidntChange) { Size prevSize = desiredSize; CachedStyle *s = cachedStyle; desiredSize = GetBorderAndPaddingSize(s); Rect bbox; Brush *brStroke = BrushFromColorData(s->stroke, bbox); if (0.f == s->strokeWidth) { graphicsPath->GetBounds(&bbox); } else { Pen pen(brStroke, s->strokeWidth); // pen widith is multiplied by MiterLimit(), which is 10 by default // so set it explicitly to 1 for the size we expect pen.SetMiterLimit(1.f); pen.SetAlignment(PenAlignmentInset); graphicsPath->GetBounds(&bbox, NULL, &pen); } desiredSize.Width += bbox.Width; desiredSize.Height += bbox.Height; if (!prevSize.Equals(desiredSize)) RequestLayout(this); else if (repaintIfSizeDidntChange) RequestRepaint(this); }
void ButtonVector::Paint(Graphics *gfx, int offX, int offY) { if (!IsVisible()) return; CachedStyle *s = cachedStyle; RectF bbox((REAL)offX, (REAL)offY, (REAL)pos.Width, (REAL)pos.Height); Brush *brBgColor = BrushFromColorData(s->bgColor, bbox); gfx->FillRectangle(brBgColor, bbox); Rect r(offX, offY, pos.Width, pos.Height); DrawBorder(gfx, r, s); if (!graphicsPath) return; // graphicsPath bbox can have non-zero X,Y Rect gpBbox; Brush *brFill = BrushFromColorData(s->fill, bbox); Brush *brStroke = BrushFromColorData(s->stroke, bbox); Pen pen(brStroke, s->strokeWidth); pen.SetMiterLimit(1.f); pen.SetAlignment(PenAlignmentInset); if (0.f == s->strokeWidth) graphicsPath->GetBounds(&gpBbox); else graphicsPath->GetBounds(&gpBbox, NULL, &pen); // calculate the position of graphics path within given button position, size // and desired vertical/horizontal alignment. // Note: alignment is calculated against the size after substracting // ncSize is the size of the non-client parts i.e. border and padding, on both sides Size ncSize = GetBorderAndPaddingSize(s); int elOffY = s->vertAlign.CalcOffset( gpBbox.Height, pos.Height - ncSize.Height); int elOffX = s->horizAlign.CalcOffset(gpBbox.Width, pos.Width - ncSize.Width ); int x = offX + elOffX + s->padding.left + (int)s->borderWidth.left + gpBbox.X; int y = offY + elOffY + s->padding.top + (int)s->borderWidth.top + gpBbox.Y; // TODO: can I avoid making a copy of GraphicsPath? GraphicsPath *tmp = graphicsPath->Clone(); Matrix m; m.Translate((float)x, (float)y); tmp->Transform(&m); gfx->FillPath(brFill, tmp); if (0.f != s->strokeWidth) gfx->DrawPath(&pen, tmp); }
// Update desired size of the button. If the size changes, trigger layout // (which will in turn request repaints of affected areas) // To keep size of the button stable (to avoid re-layouts) we use font // line spacing to be the height of the button, even if text is empty. // Note: it might be that for some cases button with no text should collapse // in size but we don't have a need for that yet void Button::RecalculateSize(bool repaintIfSizeDidntChange) { Size prevSize = desiredSize; desiredSize = GetBorderAndPaddingSize(cachedStyle); Graphics *gfx = AllocGraphicsForMeasureText(); CachedStyle *s = cachedStyle; Font *font = GetCachedFont(s->fontName, s->fontSize, s->fontWeight); textDx = 0; float fontDy = font->GetHeight(gfx); RectF bbox; if (text) { bbox = MeasureText(gfx, font, text); textDx = CeilI(bbox.Width); // I theorize that bbox shouldn't be bigger than fontDy. However, in practice // it is (e.g. for Lucida Grande and text "Page: 0 / 0", bbox.Dy is 19.00 // and fontDy is 18.11). I still want to know if the difference gets even bigger // than that float maxDiff = 1.f; if (bbox.Height > fontDy + maxDiff) { fontDy = bbox.Height; float diff = fontDy + maxDiff - bbox.Height; char *fontName = str::conv::ToUtf8(s->fontName); char *tmp = str::conv::ToUtf8(text); CrashLogFmt("fontDy=%.2f, bbox.Height=%.2f, diff=%.2f (should be > 0) font: %s, text='%s'\r\n", (float)fontDy, (float)bbox.Height, diff, fontName, tmp); CrashIf(diff < 0); } } desiredSize.Width += textDx; desiredSize.Height += CeilI(fontDy); FreeGraphicsForMeasureText(gfx); if (!prevSize.Equals(desiredSize)) RequestLayout(this); else if (repaintIfSizeDidntChange) RequestRepaint(this); }
Size Grid::Measure(const Size availableSize) { RebuildCellDataIfNeeded(); Size borderSize(GetBorderAndPaddingSize(cachedStyle)); Cell* cell; Control* el; for (Grid::CellData& d : els) { cell = GetCell(d.row, d.col); cell->desiredSize.Width = 0; cell->desiredSize.Height = 0; el = d.el; if (!el->IsVisible()) continue; // calculate max dx of each column (dx of widest cell in the row) // and max dy of each row (dy of tallest cell in the column) el->Measure(availableSize); // TODO: take cell's border and padding into account cell->desiredSize = el->DesiredSize(); // if a cell spans multiple columns, we don't count its size here if (d.colSpan == 1) { if (cell->desiredSize.Width > maxColWidth[d.col]) maxColWidth[d.col] = cell->desiredSize.Width; } if (cell->desiredSize.Height > maxRowHeight[d.row]) maxRowHeight[d.row] = cell->desiredSize.Height; } // account for cells with colSpan > 1. If cell.dx > total dx // of columns it spans, we widen the columns by equally // re-distributing the difference among columns for (Grid::CellData& d : els) { if (d.colSpan == 1) continue; cell = GetCell(d.row, d.col); int totalDx = 0; for (int i = d.col; i < d.col + d.colSpan; i++) { totalDx += maxColWidth[i]; } int diff = cell->desiredSize.Width - totalDx; if (diff > 0) { int diffPerCol = diff / d.colSpan; int rest = diff % d.colSpan; // note: we could try to redistribute rest for ideal sizing instead of // over-sizing but not sure if that would matter in practice if (rest > 0) diffPerCol += 1; CrashIf(diffPerCol * d.colSpan < diff); for (int i = d.col; i < d.col + d.colSpan; i++) { maxColWidth[i] += diffPerCol; } } } int desiredWidth = 0; int desiredHeight = 0; for (int row = 0; row < rows; row++) { desiredHeight += maxRowHeight[row]; } for (int col = 0; col < cols; col++) { desiredWidth += maxColWidth[col]; } // TODO: what to do if desired size is more than availableSize? desiredSize.Width = desiredWidth + borderSize.Width; desiredSize.Height = desiredHeight + borderSize.Height; return desiredSize; }