Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

always allow width/height on grid containers #1731

Merged
merged 8 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- All vars defined in a scope are accessible everywhere in that scope, i.e., an object can use a var defined after itself. [#1695](https://github.com/terrastruct/d2/pull/1695)
- Encoding API switches to standard zlib encoding so that decoding doesn't depend on source. [#1709](https://github.com/terrastruct/d2/pull/1709)
- `currentcolor` is accepted as a color option to inherit parent colors. (ty @hboomsma) [#1700](https://github.com/terrastruct/d2/pull/1700)
- grid containers can now be sized with `width`/`height` even when using a layout plugin without that feature. [#1731](https://github.com/terrastruct/d2/pull/1731)

#### Bugfixes ⛑️

Expand Down
68 changes: 51 additions & 17 deletions d2layouts/d2grid/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"fmt"
"math"
"strconv"

"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
Expand Down Expand Up @@ -47,15 +48,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
verticalPadding = gd.verticalGap
}

// size shape according to grid
obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))

// compute where the grid should be placed inside shape
s := obj.ToShape()
innerBox := s.GetInnerBox()
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
}
contentWidth, contentHeight := gd.width, gd.height

var labelPosition, iconPosition label.Position
if obj.LabelPosition != nil {
Expand Down Expand Up @@ -83,7 +76,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
overflow := labelWidth - (obj.Width - float64(2*horizontalPadding))
overflow := labelWidth - contentWidth
if overflow > 0 {
padding.Left += overflow / 2
padding.Right += overflow / 2
Expand All @@ -95,7 +88,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
label.InsideMiddleLeft, label.InsideMiddleCenter, label.InsideMiddleRight,
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
overflow := labelHeight - (obj.Height - float64(2*verticalPadding))
overflow := labelHeight - contentHeight
if overflow > 0 {
padding.Top += overflow / 2
padding.Bottom += overflow / 2
Expand All @@ -112,7 +105,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
padding.Left = math.Max(padding.Left, iconSize)
padding.Right = math.Max(padding.Right, iconSize)
minWidth := 2*iconSize + float64(obj.LabelDimensions.Width) + 2*label.PADDING
overflow := minWidth - (obj.Width - float64(2*horizontalPadding))
overflow := minWidth - contentWidth
if overflow > 0 {
padding.Left = math.Max(padding.Left, overflow/2)
padding.Right = math.Max(padding.Right, overflow/2)
Expand All @@ -121,24 +114,65 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {

overflowTop := padding.Top - float64(verticalPadding)
if overflowTop > 0 {
obj.Height += overflowTop
contentHeight += overflowTop
dy += overflowTop
}
overflowBottom := padding.Bottom - float64(verticalPadding)
if overflowBottom > 0 {
obj.Height += overflowBottom
contentHeight += overflowBottom
}
overflowLeft := padding.Left - float64(horizontalPadding)
if overflowLeft > 0 {
obj.Width += overflowLeft
contentWidth += overflowLeft
dx += overflowLeft
}
overflowRight := padding.Right - float64(horizontalPadding)
if overflowRight > 0 {
obj.Width += overflowRight
contentWidth += overflowRight
}

// manually handle desiredWidth/Height so we can center the grid
var desiredWidth, desiredHeight int
var originalWidthAttr, originalHeightAttr *d2graph.Scalar
if obj.WidthAttr != nil {
desiredWidth, _ = strconv.Atoi(obj.WidthAttr.Value)
// SizeToContent without desired width
originalWidthAttr = obj.WidthAttr
obj.WidthAttr = nil
}
if obj.HeightAttr != nil {
desiredHeight, _ = strconv.Atoi(obj.HeightAttr.Value)
originalHeightAttr = obj.HeightAttr
obj.HeightAttr = nil
}
// size shape according to grid
obj.SizeToContent(contentWidth, contentHeight, float64(2*horizontalPadding), float64(2*verticalPadding))
if originalWidthAttr != nil {
obj.WidthAttr = originalWidthAttr
}
if originalHeightAttr != nil {
obj.HeightAttr = originalHeightAttr
}

if desiredWidth > 0 {
ddx := float64(desiredWidth) - obj.Width
if ddx > 0 {
dx += ddx / 2
obj.Width = float64(desiredWidth)
}
}
if desiredHeight > 0 {
ddy := float64(desiredHeight) - obj.Height
if ddy > 0 {
dy += ddy / 2
obj.Height = float64(desiredHeight)
}
}

// we need to center children if we have to expand to fit the container label
// compute where the grid should be placed inside shape
innerBox := obj.ToShape().GetInnerBox()
dx = innerBox.TopLeft.X + dx
dy = innerBox.TopLeft.Y + dy
if dx != 0 || dy != 0 {
gd.shift(dx, dy)
}
Expand Down
3 changes: 2 additions & 1 deletion d2plugin/plugin_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ func FeatureSupportCheck(info *PluginInfo, g *d2graph.Graph) error {
return fmt.Errorf(`Object "%s" has attribute "top" and/or "left" set, but layout engine "%s" does not support locked positions. See https://d2lang.com/tour/layouts/#layout-specific-functionality for more.`, obj.AbsID(), info.Name)
}
}
if (obj.WidthAttr != nil || obj.HeightAttr != nil) && len(obj.ChildrenArray) > 0 {
if (obj.WidthAttr != nil || obj.HeightAttr != nil) &&
len(obj.ChildrenArray) > 0 && !obj.IsGridDiagram() {
if _, ok := featureMap[CONTAINER_DIMENSIONS]; !ok {
return fmt.Errorf(`Object "%s" has attribute "width" and/or "height" set, but layout engine "%s" does not support dimensions set on containers. See https://d2lang.com/tour/layouts/#layout-specific-functionality for more.`, obj.AbsID(), info.Name)
}
Expand Down
1 change: 1 addition & 0 deletions e2etests/stable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2869,6 +2869,7 @@ y: profits {
loadFromFile(t, "grid_edge_across_cell"),
loadFromFile(t, "nesting_power"),
loadFromFile(t, "unfilled_triangle"),
loadFromFile(t, "grid_container_dimensions"),
loadFromFile(t, "grid_label_positions"),
}

Expand Down
11 changes: 11 additions & 0 deletions e2etests/testdata/files/grid_container_dimensions.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
grid: {
width: 200
height: 200
grid-gap: 0
grid-rows: 2
grid-columns: 2
a
b
c
d
}
Loading