Vertical layout partially works
This commit is contained in:
parent
9e16f7b532
commit
986315d5db
@ -34,8 +34,10 @@ func (ent *entity) unlink () {
|
||||
delete(ent.window.system.drawingInvalid, child)
|
||||
return true
|
||||
})
|
||||
|
||||
delete(ent.window.system.drawingInvalid, ent)
|
||||
|
||||
if ent.window != nil {
|
||||
delete(ent.window.system.drawingInvalid, ent)
|
||||
}
|
||||
ent.parent = nil
|
||||
ent.window = nil
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (system *system) afterEvent () {
|
||||
}
|
||||
|
||||
func (system *system) layout (entity *entity, force bool) {
|
||||
if entity == nil { return }
|
||||
if entity == nil || !entity.isContainer { return }
|
||||
if entity.layoutInvalid == true || force {
|
||||
entity.element.(tomo.Container).Layout()
|
||||
entity.layoutInvalid = false
|
||||
|
@ -1,258 +0,0 @@
|
||||
package containers
|
||||
|
||||
import "image"
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/input"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/default/config"
|
||||
|
||||
// Container is an element capable of containg other elements, and arranging
|
||||
// them in a layout.
|
||||
type Container struct {
|
||||
*core.Core
|
||||
*core.Propagator
|
||||
core core.CoreControl
|
||||
|
||||
layout tomo.Layout
|
||||
children []tomo.LayoutEntry
|
||||
warping bool
|
||||
|
||||
config config.Wrapped
|
||||
theme theme.Wrapped
|
||||
|
||||
onFocusRequest func () (granted bool)
|
||||
onFocusMotionRequest func (input.KeynavDirection) (granted bool)
|
||||
}
|
||||
|
||||
// NewContainer creates a new container.
|
||||
func NewContainer (layout tomo.Layout) (element *Container) {
|
||||
element = &Container { }
|
||||
element.theme.Case = tomo.C("tomo", "container")
|
||||
element.Core, element.core = core.NewCore(element, element.redoAll)
|
||||
element.Propagator = core.NewPropagator(element, element.core)
|
||||
element.SetLayout(layout)
|
||||
return
|
||||
}
|
||||
|
||||
// SetLayout sets the layout of this container.
|
||||
func (element *Container) SetLayout (layout tomo.Layout) {
|
||||
element.layout = layout
|
||||
element.updateMinimumSize()
|
||||
if element.core.HasImage() {
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
}
|
||||
|
||||
// Adopt adds a new child element to the container. If expand is set to true,
|
||||
// the element will expand (instead of contract to its minimum size), in
|
||||
// whatever way is defined by the current layout.
|
||||
func (element *Container) Adopt (child tomo.Element, expand bool) {
|
||||
if child0, ok := child.(tomo.Themeable); ok {
|
||||
child0.SetTheme(element.theme.Theme)
|
||||
}
|
||||
if child0, ok := child.(tomo.Configurable); ok {
|
||||
child0.SetConfig(element.config.Config)
|
||||
}
|
||||
child.SetParent(element)
|
||||
|
||||
// add child
|
||||
element.children = append (element.children, tomo.LayoutEntry {
|
||||
Element: child,
|
||||
Expand: expand,
|
||||
})
|
||||
|
||||
// refresh stale data
|
||||
element.updateMinimumSize()
|
||||
if element.core.HasImage() && !element.warping {
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
}
|
||||
|
||||
// Warp runs the specified callback, deferring all layout and rendering updates
|
||||
// until the callback has finished executing. This allows for aplications to
|
||||
// perform batch gui updates without flickering and stuff.
|
||||
func (element *Container) Warp (callback func ()) {
|
||||
if element.warping {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
element.warping = true
|
||||
callback()
|
||||
element.warping = false
|
||||
|
||||
// TODO: create some sort of task list so we don't do a full recalculate
|
||||
// and redraw every time, because although that is the most likely use
|
||||
// case, it is not the only one.
|
||||
if element.core.HasImage() {
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
}
|
||||
|
||||
// Disown removes the given child from the container if it is contained within
|
||||
// it.
|
||||
func (element *Container) Disown (child tomo.Element) {
|
||||
for index, entry := range element.children {
|
||||
if entry.Element == child {
|
||||
element.clearChildEventHandlers(entry.Element)
|
||||
element.children = append (
|
||||
element.children[:index],
|
||||
element.children[index + 1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
element.updateMinimumSize()
|
||||
if element.core.HasImage() && !element.warping {
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
}
|
||||
|
||||
func (element *Container) clearChildEventHandlers (child tomo.Element) {
|
||||
child.DrawTo(nil, image.Rectangle { }, nil)
|
||||
child.SetParent(nil)
|
||||
|
||||
if child, ok := child.(tomo.Focusable); ok {
|
||||
if child.Focused() {
|
||||
child.HandleUnfocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DisownAll removes all child elements from the container at once.
|
||||
func (element *Container) DisownAll () {
|
||||
for _, entry := range element.children {
|
||||
element.clearChildEventHandlers(entry.Element)
|
||||
}
|
||||
element.children = nil
|
||||
|
||||
element.updateMinimumSize()
|
||||
if element.core.HasImage() && !element.warping {
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
}
|
||||
|
||||
// Children returns a slice containing this element's children.
|
||||
func (element *Container) Children () (children []tomo.Element) {
|
||||
children = make([]tomo.Element, len(element.children))
|
||||
for index, entry := range element.children {
|
||||
children[index] = entry.Element
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CountChildren returns the amount of children contained within this element.
|
||||
func (element *Container) CountChildren () (count int) {
|
||||
return len(element.children)
|
||||
}
|
||||
|
||||
// Child returns the child at the specified index. If the index is out of
|
||||
// bounds, this method will return nil.
|
||||
func (element *Container) Child (index int) (child tomo.Element) {
|
||||
if index < 0 || index > len(element.children) { return }
|
||||
return element.children[index].Element
|
||||
}
|
||||
|
||||
// ChildAt returns the child that contains the specified x and y coordinates. If
|
||||
// there are no children at the coordinates, this method will return nil.
|
||||
func (element *Container) ChildAt (point image.Point) (child tomo.Element) {
|
||||
for _, entry := range element.children {
|
||||
if point.In(entry.Bounds) {
|
||||
child = entry.Element
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (element *Container) redoAll () {
|
||||
if !element.core.HasImage() { return }
|
||||
|
||||
// remove child canvasses so that any operations done in here will not
|
||||
// cause a child to draw to a wack ass canvas.
|
||||
for _, entry := range element.children {
|
||||
entry.DrawTo(nil, entry.Bounds, nil)
|
||||
}
|
||||
|
||||
// do a layout
|
||||
element.doLayout()
|
||||
|
||||
// draw a background
|
||||
rocks := make([]image.Rectangle, len(element.children))
|
||||
for index, entry := range element.children {
|
||||
rocks[index] = entry.Bounds
|
||||
}
|
||||
|
||||
element.core.DrawBackgroundBoundsShatter (
|
||||
element.theme.Pattern(tomo.PatternBackground, tomo.State { }),
|
||||
element.Bounds(),
|
||||
rocks...)
|
||||
|
||||
// cut our canvas up and give peices to child elements
|
||||
for _, entry := range element.children {
|
||||
entry.DrawTo (
|
||||
canvas.Cut(element.core, entry.Bounds),
|
||||
entry.Bounds, func (region image.Rectangle) {
|
||||
element.core.DamageRegion(region)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (element *Container) Window () tomo.Window {
|
||||
return element.core.Window()
|
||||
}
|
||||
|
||||
// NotifyMinimumSizeChange notifies the container that the minimum size of a
|
||||
// child element has changed.
|
||||
func (element *Container) NotifyMinimumSizeChange (child tomo.Element) {
|
||||
element.updateMinimumSize()
|
||||
element.redoAll()
|
||||
element.core.DamageAll()
|
||||
}
|
||||
|
||||
// DrawBackground draws a portion of the container's background pattern within
|
||||
// the specified bounds. The container will not push these changes.
|
||||
func (element *Container) DrawBackground (bounds image.Rectangle) {
|
||||
element.core.DrawBackgroundBounds (
|
||||
element.theme.Pattern(tomo.PatternBackground, tomo.State { }),
|
||||
bounds)
|
||||
}
|
||||
|
||||
// SetTheme sets the element's theme.
|
||||
func (element *Container) SetTheme (new tomo.Theme) {
|
||||
if new == element.theme.Theme { return }
|
||||
element.theme.Theme = new
|
||||
element.Propagator.SetTheme(new)
|
||||
element.updateMinimumSize()
|
||||
element.redoAll()
|
||||
}
|
||||
|
||||
// SetConfig sets the element's configuration.
|
||||
func (element *Container) SetConfig (new tomo.Config) {
|
||||
if new == element.config.Config { return }
|
||||
element.Propagator.SetConfig(new)
|
||||
element.updateMinimumSize()
|
||||
element.redoAll()
|
||||
}
|
||||
|
||||
func (element *Container) updateMinimumSize () {
|
||||
margin := element.theme.Margin(tomo.PatternBackground)
|
||||
padding := element.theme.Padding(tomo.PatternBackground)
|
||||
width, height := element.layout.MinimumSize (
|
||||
element.children, margin, padding)
|
||||
element.core.SetMinimumSize(width, height)
|
||||
}
|
||||
|
||||
func (element *Container) doLayout () {
|
||||
margin := element.theme.Margin(tomo.PatternBackground)
|
||||
padding := element.theme.Padding(tomo.PatternBackground)
|
||||
element.layout.Arrange (
|
||||
element.children, margin,
|
||||
padding, element.Bounds())
|
||||
}
|
178
elements/containers/vbox.go
Normal file
178
elements/containers/vbox.go
Normal file
@ -0,0 +1,178 @@
|
||||
package containers
|
||||
|
||||
import "image"
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/shatter"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
||||
|
||||
type scratchEntry struct {
|
||||
expand bool
|
||||
minimum float64
|
||||
}
|
||||
|
||||
type VBox struct {
|
||||
entity tomo.ContainerEntity
|
||||
scratch map[tomo.Element] scratchEntry
|
||||
theme theme.Wrapped
|
||||
padding bool
|
||||
margin bool
|
||||
}
|
||||
|
||||
func NewVBox (padding, margin bool) (element *VBox) {
|
||||
element = &VBox { padding: padding, margin: margin }
|
||||
element.scratch = make(map[tomo.Element] scratchEntry)
|
||||
element.theme.Case = tomo.C("tomo", "vBox")
|
||||
element.entity = tomo.NewEntity(element).(tomo.ContainerEntity)
|
||||
return
|
||||
}
|
||||
|
||||
func (element *VBox) Entity () tomo.Entity {
|
||||
return element.entity
|
||||
}
|
||||
|
||||
func (element *VBox) Draw (destination canvas.Canvas) {
|
||||
rocks := make([]image.Rectangle, element.entity.CountChildren())
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
rocks[index] = element.entity.Child(index).Entity().Bounds()
|
||||
}
|
||||
|
||||
tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
|
||||
for _, tile := range tiles {
|
||||
element.entity.DrawBackground(canvas.Cut(destination, tile))
|
||||
}
|
||||
}
|
||||
|
||||
func (element *VBox) Layout () {
|
||||
margin := element.theme.Margin(tomo.PatternBackground)
|
||||
padding := element.theme.Padding(tomo.PatternBackground)
|
||||
bounds := element.entity.Bounds()
|
||||
if element.padding { bounds = padding.Apply(bounds) }
|
||||
|
||||
freeSpace, nExpanding := element.freeSpace()
|
||||
expandingElementHeight := freeSpace / nExpanding
|
||||
|
||||
// set the size and position of each element
|
||||
x := float64(bounds.Min.X)
|
||||
y := float64(bounds.Min.Y)
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
entry := element.scratch[element.entity.Child(index)]
|
||||
|
||||
var height float64; if entry.expand {
|
||||
height = expandingElementHeight
|
||||
} else {
|
||||
height = entry.minimum
|
||||
}
|
||||
|
||||
element.entity.PlaceChild (index, tomo.Bounds (
|
||||
int(x), int(y),
|
||||
bounds.Dx(), int(height)))
|
||||
|
||||
y += height
|
||||
if element.margin { y += float64(margin.Y) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (element *VBox) Adopt (child tomo.Element, expand bool) {
|
||||
element.entity.Adopt(child)
|
||||
element.scratch[child] = scratchEntry { expand: expand }
|
||||
element.updateMinimumSize()
|
||||
element.entity.Invalidate()
|
||||
element.entity.InvalidateLayout()
|
||||
}
|
||||
|
||||
func (element *VBox) Disown (child tomo.Element) {
|
||||
index := element.entity.IndexOf(child)
|
||||
if index < 0 { return }
|
||||
element.entity.Disown(index)
|
||||
delete(element.scratch, child)
|
||||
element.updateMinimumSize()
|
||||
element.entity.Invalidate()
|
||||
element.entity.InvalidateLayout()
|
||||
}
|
||||
|
||||
func (element *VBox) DisownAll () {
|
||||
func () {
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
index := index
|
||||
defer element.entity.Disown(index)
|
||||
}
|
||||
} ()
|
||||
element.scratch = make(map[tomo.Element] scratchEntry)
|
||||
element.updateMinimumSize()
|
||||
element.entity.Invalidate()
|
||||
element.entity.InvalidateLayout()
|
||||
}
|
||||
|
||||
func (element *VBox) HandleChildMinimumSizeChange (child tomo.Element) {
|
||||
element.updateMinimumSize()
|
||||
element.entity.Invalidate()
|
||||
element.entity.InvalidateLayout()
|
||||
}
|
||||
|
||||
func (element *VBox) DrawBackground (destination canvas.Canvas) {
|
||||
element.entity.DrawBackground(destination)
|
||||
}
|
||||
|
||||
// SetTheme sets the element's theme.
|
||||
func (element *VBox) SetTheme (theme tomo.Theme) {
|
||||
if theme == element.theme.Theme { return }
|
||||
element.theme.Theme = theme
|
||||
element.updateMinimumSize()
|
||||
element.entity.Invalidate()
|
||||
element.entity.InvalidateLayout()
|
||||
}
|
||||
|
||||
func (element *VBox) freeSpace () (space float64, nExpanding float64) {
|
||||
margin := element.theme.Margin(tomo.PatternBackground)
|
||||
padding := element.theme.Padding(tomo.PatternBackground)
|
||||
space = float64(element.entity.Bounds().Dy())
|
||||
|
||||
for _, entry := range element.scratch {
|
||||
if entry.expand {
|
||||
nExpanding ++;
|
||||
} else {
|
||||
space -= float64(entry.minimum)
|
||||
}
|
||||
}
|
||||
|
||||
if element.padding {
|
||||
space -= float64(padding.Vertical())
|
||||
}
|
||||
if element.margin {
|
||||
space -= float64(margin.Y * len(element.scratch) - 1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (element *VBox) updateMinimumSize () {
|
||||
margin := element.theme.Margin(tomo.PatternBackground)
|
||||
padding := element.theme.Padding(tomo.PatternBackground)
|
||||
var width, height int
|
||||
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
childWidth, childHeight := element.entity.ChildMinimumSize(index)
|
||||
|
||||
key := element.entity.Child(index)
|
||||
entry := element.scratch[key]
|
||||
entry.minimum = float64(childHeight)
|
||||
element.scratch[key] = entry
|
||||
|
||||
if childWidth > width {
|
||||
width = childWidth
|
||||
}
|
||||
height += childHeight
|
||||
if element.margin && index > 0 {
|
||||
height += margin.Y
|
||||
}
|
||||
}
|
||||
|
||||
if element.padding {
|
||||
width += padding.Horizontal()
|
||||
height += padding.Vertical()
|
||||
}
|
||||
|
||||
element.entity.SetMinimumSize(width, height)
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
package main
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/layouts"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/elements/testing"
|
||||
// import "git.tebibyte.media/sashakoshka/tomo/elements/testing"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/elements/containers"
|
||||
import _ "git.tebibyte.media/sashakoshka/tomo/backends/all"
|
||||
|
||||
@ -15,8 +14,7 @@ func run () {
|
||||
window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 128, 128))
|
||||
window.SetTitle("vertical stack")
|
||||
|
||||
container := containers.NewContainer(layouts.Vertical { true, true })
|
||||
window.Adopt(container)
|
||||
container := containers.NewVBox(true, true)
|
||||
|
||||
label := elements.NewLabel("it is a label hehe", true)
|
||||
button := elements.NewButton("drawing pad")
|
||||
@ -24,7 +22,7 @@ func run () {
|
||||
button.OnClick (func () {
|
||||
container.DisownAll()
|
||||
container.Adopt(elements.NewLabel("Draw here:", false), false)
|
||||
container.Adopt(testing.NewMouse(), true)
|
||||
// container.Adopt(testing.NewMouse(), true)
|
||||
container.Adopt(okButton, false)
|
||||
okButton.Focus()
|
||||
})
|
||||
@ -35,6 +33,7 @@ func run () {
|
||||
container.Adopt(okButton, false)
|
||||
okButton.Focus()
|
||||
|
||||
window.Adopt(container)
|
||||
window.OnClose(tomo.Stop)
|
||||
window.Show()
|
||||
}
|
||||
|
Reference in New Issue
Block a user