atomize-element-interface #3
@ -89,7 +89,7 @@ func (window *Window) Adopt (child tomo.Element) {
|
|||||||
child.SetParentHooks (tomo.ParentHooks {
|
child.SetParentHooks (tomo.ParentHooks {
|
||||||
Draw: window.childDrawCallback,
|
Draw: window.childDrawCallback,
|
||||||
MinimumSizeChange: window.childMinimumSizeChangeCallback,
|
MinimumSizeChange: window.childMinimumSizeChangeCallback,
|
||||||
ExpandingHeightChange: window.resizeChildToFit,
|
FlexibleHeightChange: window.resizeChildToFit,
|
||||||
SelectionRequest: window.childSelectionRequestCallback,
|
SelectionRequest: window.childSelectionRequestCallback,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
12
element.go
12
element.go
@ -16,9 +16,9 @@ type ParentHooks struct {
|
|||||||
// event.
|
// event.
|
||||||
MinimumSizeChange func (width, height int)
|
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.
|
// element's expanding height have changed.
|
||||||
ExpandingHeightChange func ()
|
FlexibleHeightChange func ()
|
||||||
|
|
||||||
// SelectionRequest is called when the child element element wants
|
// SelectionRequest is called when the child element element wants
|
||||||
// itself to be selected. If the parent element chooses to grant the
|
// 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.
|
// nil. If it is nil, it does nothing.
|
||||||
func (hooks ParentHooks) RunExpandingHeightChange () {
|
func (hooks ParentHooks) RunFlexibleHeightChange () {
|
||||||
if hooks.ExpandingHeightChange != nil {
|
if hooks.FlexibleHeightChange != nil {
|
||||||
hooks.ExpandingHeightChange()
|
hooks.FlexibleHeightChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ type Container struct {
|
|||||||
warping bool
|
warping bool
|
||||||
selected bool
|
selected bool
|
||||||
selectable bool
|
selectable bool
|
||||||
|
flexible bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContainer creates a new container.
|
// NewContainer creates a new container.
|
||||||
@ -49,6 +50,7 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
|
|||||||
MinimumSizeChange: func (int, int) {
|
MinimumSizeChange: func (int, int) {
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
},
|
},
|
||||||
|
FlexibleHeightChange: element.updateMinimumSize,
|
||||||
SelectionRequest: func () (granted bool) {
|
SelectionRequest: func () (granted bool) {
|
||||||
child, selectable := child.(tomo.Selectable)
|
child, selectable := child.(tomo.Selectable)
|
||||||
if !selectable { return }
|
if !selectable { return }
|
||||||
@ -68,7 +70,7 @@ func (element *Container) Adopt (child tomo.Element, expand bool) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.updateSelectable()
|
element.reflectChildProperties()
|
||||||
if element.core.HasImage() && !element.warping {
|
if element.core.HasImage() && !element.warping {
|
||||||
element.recalculate()
|
element.recalculate()
|
||||||
element.draw()
|
element.draw()
|
||||||
@ -113,7 +115,7 @@ func (element *Container) Disown (child tomo.Element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.updateSelectable()
|
element.reflectChildProperties()
|
||||||
if element.core.HasImage() && !element.warping {
|
if element.core.HasImage() && !element.warping {
|
||||||
element.recalculate()
|
element.recalculate()
|
||||||
element.draw()
|
element.draw()
|
||||||
@ -126,7 +128,7 @@ func (element *Container) DisownAll () {
|
|||||||
element.children = nil
|
element.children = nil
|
||||||
|
|
||||||
element.updateMinimumSize()
|
element.updateMinimumSize()
|
||||||
element.updateSelectable()
|
element.reflectChildProperties()
|
||||||
if element.core.HasImage() && !element.warping {
|
if element.core.HasImage() && !element.warping {
|
||||||
element.recalculate()
|
element.recalculate()
|
||||||
element.draw()
|
element.draw()
|
||||||
@ -183,8 +185,6 @@ func (element *Container) Resize (width, height int) {
|
|||||||
element.draw()
|
element.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement KeyboardTarget
|
|
||||||
|
|
||||||
func (element *Container) HandleMouseDown (x, y int, button tomo.Button) {
|
func (element *Container) HandleMouseDown (x, y int, button tomo.Button) {
|
||||||
child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget)
|
child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget)
|
||||||
if !handlesMouse { return }
|
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 {
|
element.forSelected (func (child tomo.Selectable) bool {
|
||||||
child0, handlesKeyboard := child.(tomo.KeyboardTarget)
|
child0, handlesKeyboard := child.(tomo.KeyboardTarget)
|
||||||
if handlesKeyboard {
|
if handlesKeyboard {
|
||||||
@ -300,6 +300,11 @@ func (element *Container) HandleSelection (direction tomo.SelectionDirection) (o
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix this!
|
||||||
|
// func (element *Container) MinimumHeightFor (width int) (height int) {
|
||||||
|
// return element.layout.MinimumHeightFor(element.children, width)
|
||||||
|
// }
|
||||||
|
|
||||||
func (element *Container) HandleDeselection () {
|
func (element *Container) HandleDeselection () {
|
||||||
element.selected = false
|
element.selected = false
|
||||||
element.forSelected (func (child tomo.Selectable) bool {
|
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) {
|
func (element *Container) forSelectableBackward (callback func (child tomo.Selectable) bool) {
|
||||||
for index := len(element.children) - 1; index >= 0; index -- {
|
for index := len(element.children) - 1; index >= 0; index -- {
|
||||||
child, selectable := element.children[index].Element.(tomo.Selectable)
|
child, selectable := element.children[index].Element.(tomo.Selectable)
|
||||||
@ -345,12 +359,17 @@ func (element *Container) firstSelected () (index int) {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Container) updateSelectable () {
|
func (element *Container) reflectChildProperties () {
|
||||||
element.selectable = false
|
element.selectable = false
|
||||||
element.forSelectable (func (tomo.Selectable) bool {
|
element.forSelectable (func (tomo.Selectable) bool {
|
||||||
element.selectable = true
|
element.selectable = true
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
element.flexible = false
|
||||||
|
element.forFlexible (func (tomo.Flexible) bool {
|
||||||
|
element.flexible = true
|
||||||
|
return false
|
||||||
|
})
|
||||||
if !element.selectable {
|
if !element.selectable {
|
||||||
element.selected = false
|
element.selected = false
|
||||||
}
|
}
|
||||||
@ -374,8 +393,11 @@ func (element *Container) childSelectionRequestCallback (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (element *Container) updateMinimumSize () {
|
func (element *Container) updateMinimumSize () {
|
||||||
element.core.SetMinimumSize (
|
width, height := element.layout.MinimumSize(element.children)
|
||||||
element.layout.MinimumSize(element.children, 1e9))
|
if element.flexible {
|
||||||
|
height = element.layout.MinimumHeightFor(element.children, width)
|
||||||
|
}
|
||||||
|
element.core.SetMinimumSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Container) recalculate () {
|
func (element *Container) recalculate () {
|
||||||
|
@ -88,7 +88,7 @@ func (element *Label) updateMinimumSize () {
|
|||||||
if em < 1 { em = theme.Padding() }
|
if em < 1 { em = theme.Padding() }
|
||||||
element.core.SetMinimumSize (
|
element.core.SetMinimumSize (
|
||||||
em, element.drawer.LineHeight().Round())
|
em, element.drawer.LineHeight().Round())
|
||||||
element.core.NotifyExpandingHeightChange()
|
element.core.NotifyFlexibleHeightChange()
|
||||||
} else {
|
} else {
|
||||||
bounds := element.drawer.LayoutBounds()
|
bounds := element.drawer.LayoutBounds()
|
||||||
element.core.SetMinimumSize(bounds.Dx(), bounds.Dy())
|
element.core.SetMinimumSize(bounds.Dx(), bounds.Dy())
|
||||||
|
@ -144,10 +144,10 @@ func (control CoreControl) SetMinimumSize (width, height int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyExpandingHeightChange notifies the parent element that this element's
|
// NotifyFlexibleHeightChange notifies the parent element that this element's
|
||||||
// expanding height has changed.
|
// flexible height has changed.
|
||||||
func (control CoreControl) NotifyExpandingHeightChange () {
|
func (control CoreControl) NotifyFlexibleHeightChange () {
|
||||||
control.core.hooks.RunExpandingHeightChange()
|
control.core.hooks.RunFlexibleHeightChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstrainSize contstrains the specified width and height to the minimum width
|
// ConstrainSize contstrains the specified width and height to the minimum width
|
||||||
|
@ -17,7 +17,7 @@ func run () {
|
|||||||
container := basic.NewContainer(layouts.Vertical { true, true })
|
container := basic.NewContainer(layouts.Vertical { true, true })
|
||||||
window.Adopt(container)
|
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")
|
button := basic.NewButton("drawing pad")
|
||||||
okButton := basic.NewButton("OK")
|
okButton := basic.NewButton("OK")
|
||||||
button.OnClick (func () {
|
button.OnClick (func () {
|
||||||
|
11
layout.go
11
layout.go
@ -20,8 +20,11 @@ type Layout interface {
|
|||||||
Arrange (entries []LayoutEntry, width, height int)
|
Arrange (entries []LayoutEntry, width, height int)
|
||||||
|
|
||||||
// MinimumSize returns the minimum width and height that the layout
|
// MinimumSize returns the minimum width and height that the layout
|
||||||
// needs to properly arrange the given slice of layout entries, given a
|
// needs to properly arrange the given slice of layout entries.
|
||||||
// "suqeeze" width so that the height can be determined for elements
|
MinimumSize (entries []LayoutEntry) (width, height int)
|
||||||
// fulfilling the Expanding interface.
|
|
||||||
MinimumSize (entries []LayoutEntry, squeeze int) (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)
|
||||||
}
|
}
|
||||||
|
@ -25,17 +25,22 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
|
|||||||
freeSpace := height
|
freeSpace := height
|
||||||
expandingElements := 0
|
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
|
// 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 {
|
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 {
|
if entry.Expand {
|
||||||
expandingElements ++
|
expandingElements ++
|
||||||
} else {
|
} else {
|
||||||
_, entryMinHeight := entry.MinimumSize()
|
|
||||||
freeSpace -= entryMinHeight
|
freeSpace -= entryMinHeight
|
||||||
}
|
}
|
||||||
if index > 0 && layout.Gap {
|
if index > 0 && layout.Gap {
|
||||||
@ -62,7 +67,7 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
|
|||||||
if entry.Expand {
|
if entry.Expand {
|
||||||
entryHeight = expandingElementHeight
|
entryHeight = expandingElementHeight
|
||||||
} else {
|
} else {
|
||||||
_, entryHeight = entry.MinimumSize()
|
entryHeight = minimumHeights[index]
|
||||||
}
|
}
|
||||||
y += entryHeight
|
y += entryHeight
|
||||||
entryBounds := entry.Bounds()
|
entryBounds := entry.Bounds()
|
||||||
@ -76,7 +81,6 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, width, height int) {
|
|||||||
// arrange the given list of entries.
|
// arrange the given list of entries.
|
||||||
func (layout Vertical) MinimumSize (
|
func (layout Vertical) MinimumSize (
|
||||||
entries []tomo.LayoutEntry,
|
entries []tomo.LayoutEntry,
|
||||||
squeeze int,
|
|
||||||
) (
|
) (
|
||||||
width, height int,
|
width, height int,
|
||||||
) {
|
) {
|
||||||
@ -97,3 +101,31 @@ func (layout Vertical) MinimumSize (
|
|||||||
}
|
}
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user