-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Proposal: Aligned and Expandable BoxLayout
MainAxis and CrossAxis:
Axis | VBox | HBox |
---|---|---|
MainAxis | Vertical | Horizontal |
CrossAxis | Horizontal | Vertical |
Following the discussion in #1840, cross axis alignment would be useful for fyne.Layouts
to allow them be able to organize widgets with different cross axis sizes. Expandable feature is proposed here too, however I am not clear what is the best approach to this or if it can be discarded. This proposal invites us to discuss if the proposed cross axis alignment and expandable feature for BoxLayout should be added to Fyne core or not, or if it needs some design changes. Please add any discussion to the Discussion
section below.
Currently, Fyne has two kinds of layout to work with horizontal and vertical alignment:
-
BoxLayouts
=>VBox
andHBox
-
GridLayouts
=>GridWithColumns
andGridWithRows
BoxLayouts
resize objects to their min size in the main axis and stretch their cross axis size.
GridLayouts
resize objects to have the same size in the main axis and stretch their cross axis size.
None of them offers a way to align elements in the cross axis, although that behavior can be accomplished doing some workaround. For example if we want this result:
We should write something like:
c := container.NewVBox(
container.NewHBox(
widget.NewEntry(),
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentCopyIcon())),
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentPasteIcon())),
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentRemoveIcon())),
),
)
Proposed solution would allow us to write:
c := container.NewHBoxAligned(layout.CrossAlignmentEnd,
widget.NewEntry(),
createIcon(theme.ContentCopyIcon()),
createIcon(theme.ContentPasteIcon()),
createIcon(theme.ContentRemoveIcon()),
)
Now if we want to expand the entry to look like this:
We should write something like:
c := container.NewVBox(
container.NewBorder(nil, nil, nil,
container.NewHBox(
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentCopyIcon())),
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentPasteIcon())),
container.NewVBox(layout.NewSpacer(), createIcon(theme.ContentRemoveIcon())),
),
widget.NewEntry(),
),
layout.NewSpacer(),
)
Proposed solution will allow us to write:
c := container.NewHBoxAligned(layout.CrossAlignmentEnd,
container.NewHBoxExpanded(widget.NewEntry()),
createIcon(theme.ContentCopyIcon()),
createIcon(theme.ContentPasteIcon()),
createIcon(theme.ContentRemoveIcon()),
)
Proposed solution would also allow us to solve cross axis alignment problems for text-based widgets as discussed in #1701.
There should be a new type called CrossAlignment (name suggestions are welcome!):
// CrossAlignment defines cross axis alignment type.
type CrossAlignment int
// Cross Axis alignment options.
const (
CrossAlignmentStart CrossAlignment = iota
CrossAlignmentEnd
CrossAlignmentCenter
CrossAlignmentBaseline
CrossAlignmentStretch
)
This type would help us to specify the cross axis alignment in box layouts.
-
CrossAlignmentStart
: Align the content to the Top (HBox) or Left (VBox). -
CrossAlignmentEnd
: Align the content to the Bottom (HBox) or Right (VBox). -
CrossAlignmentCenter
: Center the content vertically (HBox) or horizontally (VBox). -
CrossAlignmentBaseline
: This option is only useful for HBox, it aligns the content to the text baseline. For VBox, this alignment would behave as CrossAlignmentStart. -
CrossAlignmentStretch
: Stretch the cross axis size of the children (current behavior).
So to support both expandable feature and cross axis alignment, boxLayout
should have new fields:
type boxLayout struct {
expanded bool
horizontal bool
crossAlignment CrossAlignment
}
Then, we would have new box layout containers like:
-
container.NewVBox(objects...)
: It would work just as before (setting crossAlignment to CrossAlignmentStretch). -
container.NewVBoxAligned(crossAlignment, objects...)
: It would work just likeNewVBox
, but instead of stretching the cross axis by default, user can set the desired cross alignment. -
container.NewVBoxExpanded(objects...)
: It would resize the objects to have the same size in the vertical axis, and stretching their horizontal axis (setting crossAlignment to CrossAlignmentStretch). -
container.NewVBoxExpandedAligned(crossAlignment, objects...)
: It would work just likeNewVBoxExpanded
, but instead of stretching the cross axis by default, user can set the desired cross alignment. -
container.NewHBox(objects...)
: It would work just as before (setting crossAlignment to CrossAlignmentStretch). -
container.NewHBoxAligned(crossAlignment, objects...)
: It would work just likeNewHBox
, but instead of stretching the cross axis by default, user can set the desired cross alignment. -
container.NewHBoxExpanded(objects...)
: It would resize the objects to have the same size in the horizontal axis, and stretching their vertical axis (setting crossAlignment to CrossAlignmentStretch). -
container.NewHBoxExpandedAligned(crossAlignment, objects...)
: It would work just likeNewHBoxExpanded
, but instead of stretching the cross axis by default, user can set the desired cross alignment.
A lot of constructors for BoxLayouts would maybe be quite confusing (indeed, I am almost sure, they are). So an alternative approach would be to only have:
container.NewVBox(objects...)
container.NewVBoxAligned(crossAlignment, objects...)
container.NewHBox(objects...)
container.NewHBoxAligned(crossAlignment, objects...)
And have a way to specify when we want to expand an object in its main axis. There could be a new container called ExpandedBox
(or maybe we can use the container.NewMax, although I think a better name for it should be ExpandedBox
), and internally the boxLayout will detect it and expand its main axis size (just like the spacers
). However, this new container would only be useful as a child of a VBox/HBox.
Maybe its definition could be:
container.NewExpandedBox will expand the main axis of its children if its parent is a VBox/HBox, otherwise it will expand on both axis (just like MaxLayout).
The initial implementation can be found in my fyne fork within the branch feature/aligned-expanded-boxlayout (while this remains as proposal): https://github.com/fpabl0/fyne/tree/feature/aligned-expanded-boxlayout
In the fork I am not replacing current VBox and HBox layouts, in order to compare them. I have added a new layout nboxLayout
to test this implementation (under cmd/nboxlayout_demo
).