Half-working container flexibility propagation

This commit is contained in:
Sasha Koshka 2023-01-16 23:34:17 -05:00
parent 76d50bb01a
commit e94e170a04
8 changed files with 91 additions and 34 deletions

View File

@ -89,7 +89,7 @@ func (window *Window) Adopt (child tomo.Element) {
child.SetParentHooks (tomo.ParentHooks {
Draw: window.childDrawCallback,
MinimumSizeChange: window.childMinimumSizeChangeCallback,
ExpandingHeightChange: window.resizeChildToFit,
FlexibleHeightChange: window.resizeChildToFit,
SelectionRequest: window.childSelectionRequestCallback,
})

View File

@ -16,9 +16,9 @@ type ParentHooks struct {
// event.
MinimumSizeChange func (width, height int)
// ExpandingHeightChange is called when the parameters affecting the
// FlexibleHeightChange is called when the parameters affecting the
// element's expanding height have changed.
ExpandingHeightChange func ()
FlexibleHeightChange func ()
// SelectionRequest is called when the child element element wants
// itself to be selected. If the parent element chooses to grant the
@ -46,11 +46,11 @@ func (hooks ParentHooks) RunMinimumSizeChange (width, height int) {
}
}
// RunExpandingHeightChange runs the ExpandingHeightChange hook if it is not
// RunFlexibleHeightChange runs the ExpandingHeightChange hook if it is not
// nil. If it is nil, it does nothing.
func (hooks ParentHooks) RunExpandingHeightChange () {
if hooks.ExpandingHeightChange != nil {
hooks.ExpandingHeightChange()
func (hooks ParentHooks) RunFlexibleHeightChange () {
if hooks.FlexibleHeightChange != nil {
hooks.FlexibleHeightChange()
}
}

View File

@ -18,6 +18,7 @@ type Container struct {
warping bool
selected bool
selectable bool
flexible bool
}
// NewContainer creates a new container.
@ -49,6 +50,7 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
MinimumSizeChange: func (int, int) {
element.updateMinimumSize()
},
FlexibleHeightChange: element.updateMinimumSize,
SelectionRequest: func () (granted bool) {
child, selectable := child.(tomo.Selectable)
if !selectable { return }
@ -68,7 +70,7 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
})
element.updateMinimumSize()
element.updateSelectable()
element.reflectChildProperties()
if element.core.HasImage() && !element.warping {
element.recalculate()
element.draw()
@ -113,7 +115,7 @@ func (element *Container) Disown (child tomo.Element) {
}
element.updateMinimumSize()
element.updateSelectable()
element.reflectChildProperties()
if element.core.HasImage() && !element.warping {
element.recalculate()
element.draw()
@ -126,7 +128,7 @@ func (element *Container) DisownAll () {
element.children = nil
element.updateMinimumSize()
element.updateSelectable()
element.reflectChildProperties()
if element.core.HasImage() && !element.warping {
element.recalculate()
element.draw()
@ -183,8 +185,6 @@ func (element *Container) Resize (width, height int) {
element.draw()
}
// TODO: implement KeyboardTarget
func (element *Container) HandleMouseDown (x, y int, button tomo.Button) {
child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget)
if !handlesMouse { return }
@ -230,7 +230,7 @@ func (element *Container) HandleKeyDown (
})
}
func (element *Container) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) {
func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) {
element.forSelected (func (child tomo.Selectable) bool {
child0, handlesKeyboard := child.(tomo.KeyboardTarget)
if handlesKeyboard {
@ -300,6 +300,11 @@ func (element *Container) HandleSelection (direction tomo.SelectionDirection) (o
return false
}
// TODO: fix this!
// func (element *Container) MinimumHeightFor (width int) (height int) {
// return element.layout.MinimumHeightFor(element.children, width)
// }
func (element *Container) HandleDeselection () {
element.selected = false
element.forSelected (func (child tomo.Selectable) bool {
@ -326,6 +331,15 @@ func (element *Container) forSelectable (callback func (child tomo.Selectable) b
}
}
func (element *Container) forFlexible (callback func (child tomo.Flexible) bool) {
for _, entry := range element.children {
child, selectable := entry.Element.(tomo.Flexible)
if selectable {
if !callback(child) { break }
}
}
}
func (element *Container) forSelectableBackward (callback func (child tomo.Selectable) bool) {
for index := len(element.children) - 1; index >= 0; index -- {
child, selectable := element.children[index].Element.(tomo.Selectable)
@ -345,12 +359,17 @@ func (element *Container) firstSelected () (index int) {
return -1
}
func (element *Container) updateSelectable () {
func (element *Container) reflectChildProperties () {
element.selectable = false
element.forSelectable (func (tomo.Selectable) bool {
element.selectable = true
return false
})
element.flexible = false
element.forFlexible (func (tomo.Flexible) bool {
element.flexible = true
return false
})
if !element.selectable {
element.selected = false
}
@ -374,8 +393,11 @@ func (element *Container) childSelectionRequestCallback (
}
func (element *Container) updateMinimumSize () {
element.core.SetMinimumSize (
element.layout.MinimumSize(element.children, 1e9))
width, height := element.layout.MinimumSize(element.children)
if element.flexible {
height = element.layout.MinimumHeightFor(element.children, width)
}
element.core.SetMinimumSize(width, height)
}
func (element *Container) recalculate () {

View File

@ -88,7 +88,7 @@ func (element *Label) updateMinimumSize () {
if em < 1 { em = theme.Padding() }
element.core.SetMinimumSize (
em, element.drawer.LineHeight().Round())
element.core.NotifyExpandingHeightChange()
element.core.NotifyFlexibleHeightChange()
} else {
bounds := element.drawer.LayoutBounds()
element.core.SetMinimumSize(bounds.Dx(), bounds.Dy())

View File

@ -144,10 +144,10 @@ func (control CoreControl) SetMinimumSize (width, height int) {
}
}
// NotifyExpandingHeightChange notifies the parent element that this element's
// expanding height has changed.
func (control CoreControl) NotifyExpandingHeightChange () {
control.core.hooks.RunExpandingHeightChange()
// NotifyFlexibleHeightChange notifies the parent element that this element's
// flexible height has changed.
func (control CoreControl) NotifyFlexibleHeightChange () {
control.core.hooks.RunFlexibleHeightChange()
}
// ConstrainSize contstrains the specified width and height to the minimum width

View File

@ -17,7 +17,7 @@ func run () {
container := basic.NewContainer(layouts.Vertical { true, true })
window.Adopt(container)
label := basic.NewLabel("it is a label hehe", false)
label := basic.NewLabel("it is a label hehe", true)
button := basic.NewButton("drawing pad")
okButton := basic.NewButton("OK")
button.OnClick (func () {

View File

@ -20,8 +20,11 @@ type Layout interface {
Arrange (entries []LayoutEntry, width, height int)
// MinimumSize returns the minimum width and height that the layout
// needs to properly arrange the given slice of layout entries, given a
// "suqeeze" width so that the height can be determined for elements
// fulfilling the Expanding interface.
MinimumSize (entries []LayoutEntry, squeeze int) (width, height int)
// needs to properly arrange the given slice of layout entries.
MinimumSize (entries []LayoutEntry) (width, height int)
// MinimumHeightFor Returns the minimum height the layout needs to lay
// out the specified elements at the given width, taking into account
// flexible elements.
MinimumHeightFor (entries []LayoutEntry, squeeze int) (height int)
}

View File

@ -25,17 +25,22 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
freeSpace := height
expandingElements := 0
// TODO: find the width first, then store the minumum height of
// everything in a list, then arrange everything.
// minimumHeights := make([]int, len(entries))
// count the number of expanding elements and the amount of free space
// for them to collectively occupy
// for them to collectively occupy, while gathering minimum heights.
minimumHeights := make([]int, len(entries))
for index, entry := range entries {
var entryMinHeight int
if child, flexible := entry.Element.(tomo.Flexible); flexible {
entryMinHeight = child.MinimumHeightFor(width)
} else {
_, entryMinHeight = entry.MinimumSize()
}
minimumHeights[index] = entryMinHeight
if entry.Expand {
expandingElements ++
} else {
_, entryMinHeight := entry.MinimumSize()
freeSpace -= entryMinHeight
}
if index > 0 && layout.Gap {
@ -62,7 +67,7 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
if entry.Expand {
entryHeight = expandingElementHeight
} else {
_, entryHeight = entry.MinimumSize()
entryHeight = minimumHeights[index]
}
y += entryHeight
entryBounds := entry.Bounds()
@ -76,7 +81,6 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
// arrange the given list of entries.
func (layout Vertical) MinimumSize (
entries []tomo.LayoutEntry,
squeeze int,
) (
width, height int,
) {
@ -97,3 +101,31 @@ func (layout Vertical) MinimumSize (
}
return
}
// MinimumHeightFor Returns the minimum height the layout needs to lay out the
// specified elements at the given width, taking into account flexible elements.
func (layout Vertical) MinimumHeightFor (
entries []tomo.LayoutEntry,
squeeze int,
) (
height int,
) {
for index, entry := range entries {
child, flexible := entry.Element.(tomo.Flexible)
if flexible {
height += child.MinimumHeightFor(squeeze)
} else {
_, entryHeight := entry.MinimumSize()
height += entryHeight
}
if layout.Gap && index > 0 {
height += theme.Padding()
}
}
if layout.Pad {
height += theme.Padding() * 2
}
return
}