Added support for horizontal layouts
This commit is contained in:
		
							parent
							
								
									0cd7fb9be9
								
							
						
					
					
						commit
						0a21f605fb
					
				@ -11,27 +11,40 @@ type scratchEntry struct {
 | 
			
		||||
	minimum float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type VBox struct {
 | 
			
		||||
	entity  tomo.ContainerEntity
 | 
			
		||||
	scratch map[tomo.Element] scratchEntry
 | 
			
		||||
	theme   theme.Wrapped
 | 
			
		||||
	padding bool
 | 
			
		||||
	margin  bool
 | 
			
		||||
// Box is a container that lays out its children horizontally or vertically.
 | 
			
		||||
// Child elements can be set to contract to their minimum size, or expand to
 | 
			
		||||
// fill remaining space. Boxes can be nested and used together to create more
 | 
			
		||||
// complex layouts.
 | 
			
		||||
type Box struct {
 | 
			
		||||
	entity   tomo.ContainerEntity
 | 
			
		||||
	scratch  map[tomo.Element] scratchEntry
 | 
			
		||||
	theme    theme.Wrapped
 | 
			
		||||
	padding  bool
 | 
			
		||||
	margin   bool
 | 
			
		||||
	vertical bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewVBox (padding, margin bool) (element *VBox) {
 | 
			
		||||
	element = &VBox { padding: padding, margin: margin }
 | 
			
		||||
// NewHBox creates a new horizontal box.
 | 
			
		||||
func NewHBox (padding, margin bool) (element *Box) {
 | 
			
		||||
	element = &Box { padding: padding, margin: margin }
 | 
			
		||||
	element.scratch = make(map[tomo.Element] scratchEntry)
 | 
			
		||||
	element.theme.Case = tomo.C("tomo", "vBox")
 | 
			
		||||
	element.theme.Case = tomo.C("tomo", "box")
 | 
			
		||||
	element.entity = tomo.NewEntity(element).(tomo.ContainerEntity)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) Entity () tomo.Entity {
 | 
			
		||||
// NewHBox creates a new vertical box.
 | 
			
		||||
func NewVBox (padding, margin bool) (element *Box) {
 | 
			
		||||
	element = NewHBox(padding, margin)
 | 
			
		||||
	element.vertical = true
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *Box) Entity () tomo.Entity {
 | 
			
		||||
	return element.entity
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) Draw (destination canvas.Canvas) {
 | 
			
		||||
func (element *Box) 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()
 | 
			
		||||
@ -43,14 +56,20 @@ func (element *VBox) Draw (destination canvas.Canvas) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) Layout () {
 | 
			
		||||
func (element *Box) Layout () {
 | 
			
		||||
	margin  := element.theme.Margin(tomo.PatternBackground)
 | 
			
		||||
	padding := element.theme.Padding(tomo.PatternBackground)
 | 
			
		||||
	bounds  := element.entity.Bounds()
 | 
			
		||||
	if element.padding { bounds = padding.Apply(bounds) }
 | 
			
		||||
 | 
			
		||||
	var marginSize float64; if element.vertical {
 | 
			
		||||
		marginSize = float64(margin.Y)
 | 
			
		||||
	} else {
 | 
			
		||||
		marginSize = float64(margin.X)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	freeSpace, nExpanding := element.freeSpace()
 | 
			
		||||
	expandingElementHeight := freeSpace / nExpanding
 | 
			
		||||
	expandingElementSize := freeSpace / nExpanding
 | 
			
		||||
 | 
			
		||||
	// set the size and position of each element
 | 
			
		||||
	x := float64(bounds.Min.X)
 | 
			
		||||
@ -58,23 +77,31 @@ func (element *VBox) Layout () {
 | 
			
		||||
	for index := 0; index < element.entity.CountChildren(); index ++ {
 | 
			
		||||
		entry := element.scratch[element.entity.Child(index)]
 | 
			
		||||
		
 | 
			
		||||
		var height float64; if entry.expand {
 | 
			
		||||
			height = expandingElementHeight
 | 
			
		||||
		var size float64; if entry.expand {
 | 
			
		||||
			size = expandingElementSize
 | 
			
		||||
		} else {
 | 
			
		||||
			height = entry.minimum
 | 
			
		||||
			size = 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) }
 | 
			
		||||
		var childBounds image.Rectangle; if element.vertical {
 | 
			
		||||
			childBounds = tomo.Bounds(int(x), int(y), bounds.Dx(), int(size))
 | 
			
		||||
		} else {
 | 
			
		||||
			childBounds = tomo.Bounds(int(x), int(y), int(size), bounds.Dy())
 | 
			
		||||
		}
 | 
			
		||||
		element.entity.PlaceChild(index, childBounds)
 | 
			
		||||
 | 
			
		||||
		if element.vertical {
 | 
			
		||||
			y += size
 | 
			
		||||
			if element.margin { y += marginSize }
 | 
			
		||||
		} else {
 | 
			
		||||
			x += size
 | 
			
		||||
			if element.margin { x += marginSize }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) Adopt (child tomo.Element, expand bool) {
 | 
			
		||||
func (element *Box) Adopt (child tomo.Element, expand bool) {
 | 
			
		||||
	element.entity.Adopt(child)
 | 
			
		||||
	element.scratch[child] = scratchEntry { expand: expand }
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
@ -82,7 +109,7 @@ func (element *VBox) Adopt (child tomo.Element, expand bool) {
 | 
			
		||||
	element.entity.InvalidateLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) Disown (child tomo.Element) {
 | 
			
		||||
func (element *Box) Disown (child tomo.Element) {
 | 
			
		||||
	index := element.entity.IndexOf(child)
 | 
			
		||||
	if index < 0 { return }
 | 
			
		||||
	element.entity.Disown(index)
 | 
			
		||||
@ -92,7 +119,7 @@ func (element *VBox) Disown (child tomo.Element) {
 | 
			
		||||
	element.entity.InvalidateLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) DisownAll () {
 | 
			
		||||
func (element *Box) DisownAll () {
 | 
			
		||||
	func () {
 | 
			
		||||
		for index := 0; index < element.entity.CountChildren(); index ++ {
 | 
			
		||||
			index := index
 | 
			
		||||
@ -105,18 +132,18 @@ func (element *VBox) DisownAll () {
 | 
			
		||||
	element.entity.InvalidateLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) HandleChildMinimumSizeChange (child tomo.Element) {
 | 
			
		||||
func (element *Box) HandleChildMinimumSizeChange (child tomo.Element) {
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.entity.Invalidate()
 | 
			
		||||
	element.entity.InvalidateLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) DrawBackground (destination canvas.Canvas) {
 | 
			
		||||
func (element *Box) DrawBackground (destination canvas.Canvas) {
 | 
			
		||||
	element.entity.DrawBackground(destination)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetTheme sets the element's theme.
 | 
			
		||||
func (element *VBox) SetTheme (theme tomo.Theme) {
 | 
			
		||||
func (element *Box) SetTheme (theme tomo.Theme) {
 | 
			
		||||
	if theme == element.theme.Theme { return }
 | 
			
		||||
	element.theme.Theme = theme
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
@ -124,10 +151,21 @@ func (element *VBox) SetTheme (theme tomo.Theme) {
 | 
			
		||||
	element.entity.InvalidateLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) freeSpace () (space float64, nExpanding float64) {
 | 
			
		||||
func (element *Box) freeSpace () (space float64, nExpanding float64) {
 | 
			
		||||
	margin  := element.theme.Margin(tomo.PatternBackground)
 | 
			
		||||
	padding := element.theme.Padding(tomo.PatternBackground)
 | 
			
		||||
	space    = float64(element.entity.Bounds().Dy())
 | 
			
		||||
 | 
			
		||||
	var marginSize int; if element.vertical {
 | 
			
		||||
		marginSize = margin.Y
 | 
			
		||||
	} else {
 | 
			
		||||
		marginSize = margin.X
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if element.vertical {
 | 
			
		||||
		space = float64(element.entity.Bounds().Dy())
 | 
			
		||||
	} else {
 | 
			
		||||
		space = float64(element.entity.Bounds().Dx())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, entry := range element.scratch {
 | 
			
		||||
		if entry.expand {
 | 
			
		||||
@ -141,34 +179,50 @@ func (element *VBox) freeSpace () (space float64, nExpanding float64) {
 | 
			
		||||
		space -= float64(padding.Vertical())
 | 
			
		||||
	}
 | 
			
		||||
	if element.margin {
 | 
			
		||||
		space -= float64(margin.Y * (len(element.scratch) - 1))
 | 
			
		||||
		space -= float64(marginSize * (len(element.scratch) - 1))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *VBox) updateMinimumSize () {
 | 
			
		||||
func (element *Box) updateMinimumSize () {
 | 
			
		||||
	margin  := element.theme.Margin(tomo.PatternBackground)
 | 
			
		||||
	padding := element.theme.Padding(tomo.PatternBackground)
 | 
			
		||||
	var width, height int
 | 
			
		||||
	var breadth, size int
 | 
			
		||||
	var marginSize int; if element.vertical {
 | 
			
		||||
		marginSize = margin.Y
 | 
			
		||||
	} else {
 | 
			
		||||
		marginSize = margin.X
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	for index := 0; index < element.entity.CountChildren(); index ++ {
 | 
			
		||||
		childWidth, childHeight :=  element.entity.ChildMinimumSize(index)
 | 
			
		||||
		childWidth, childHeight := element.entity.ChildMinimumSize(index)
 | 
			
		||||
		var childBreadth, childSize int; if element.vertical {
 | 
			
		||||
			childBreadth, childSize = childWidth, childHeight
 | 
			
		||||
		} else {
 | 
			
		||||
			childBreadth, childSize = childHeight, childWidth
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		key   := element.entity.Child(index)
 | 
			
		||||
		entry := element.scratch[key]
 | 
			
		||||
		entry.minimum = float64(childHeight)
 | 
			
		||||
		entry.minimum = float64(childSize)
 | 
			
		||||
		element.scratch[key] = entry
 | 
			
		||||
		
 | 
			
		||||
		if childWidth > width {
 | 
			
		||||
			width = childWidth
 | 
			
		||||
		if childBreadth > breadth {
 | 
			
		||||
			breadth = childBreadth
 | 
			
		||||
		}
 | 
			
		||||
		height += childHeight
 | 
			
		||||
		size += childSize
 | 
			
		||||
		if element.margin && index > 0 {
 | 
			
		||||
			height += margin.Y
 | 
			
		||||
			size += marginSize
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var width, height int; if element.vertical {
 | 
			
		||||
		width, height = breadth, size
 | 
			
		||||
	} else {
 | 
			
		||||
		width, height = size, breadth
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if element.padding {
 | 
			
		||||
		width  += padding.Horizontal()
 | 
			
		||||
		height += padding.Vertical()
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo"
 | 
			
		||||
// import "git.tebibyte.media/sashakoshka/tomo/popups"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/popups"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements/containers"
 | 
			
		||||
import _ "git.tebibyte.media/sashakoshka/tomo/backends/all"
 | 
			
		||||
@ -34,11 +34,11 @@ func run () {
 | 
			
		||||
	vsync := elements.NewCheckbox("Enable vsync", false)
 | 
			
		||||
	vsync.OnToggle (func () {
 | 
			
		||||
		if vsync.Value() {
 | 
			
		||||
			// popups.NewDialog (
 | 
			
		||||
				// popups.DialogKindInfo,
 | 
			
		||||
				// window,
 | 
			
		||||
				// "Ha!",
 | 
			
		||||
				// "That doesn't do anything.")
 | 
			
		||||
			popups.NewDialog (
 | 
			
		||||
				popups.DialogKindInfo,
 | 
			
		||||
				window,
 | 
			
		||||
				"Ha!",
 | 
			
		||||
				"That doesn't do anything.")
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	container.Adopt(vsync, false)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ package popups
 | 
			
		||||
 | 
			
		||||
import "image"
 | 
			
		||||
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/containers"
 | 
			
		||||
 | 
			
		||||
@ -16,6 +15,8 @@ const (
 | 
			
		||||
	DialogKindError
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TODO: add ability to have an icon for buttons
 | 
			
		||||
 | 
			
		||||
// Button represents a dialog response button.
 | 
			
		||||
type Button struct {
 | 
			
		||||
	// Name contains the text to display on the button.
 | 
			
		||||
@ -42,11 +43,11 @@ func NewDialog (
 | 
			
		||||
		window, _ = parent.NewModal(image.Rectangle { })
 | 
			
		||||
	}
 | 
			
		||||
	window.SetTitle(title)
 | 
			
		||||
	
 | 
			
		||||
	box        := containers.NewVBox(true,  true)
 | 
			
		||||
	messageRow := containers.NewHBox(false, true)
 | 
			
		||||
	controlRow := containers.NewHBox(false, true)
 | 
			
		||||
 | 
			
		||||
	container := containers.NewContainer(layouts.Dialog { true, true })
 | 
			
		||||
	window.Adopt(container)
 | 
			
		||||
 | 
			
		||||
	messageContainer := containers.NewContainer(layouts.Horizontal { true, false })
 | 
			
		||||
	iconId := tomo.IconInformation
 | 
			
		||||
	switch kind {
 | 
			
		||||
	case DialogKindInfo:     iconId = tomo.IconInformation
 | 
			
		||||
@ -55,15 +56,15 @@ func NewDialog (
 | 
			
		||||
	case DialogKindError:    iconId = tomo.IconError
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	messageContainer.Adopt(elements.NewIcon(iconId, tomo.IconSizeLarge), false)
 | 
			
		||||
	messageContainer.Adopt(elements.NewLabel(message, false), true)
 | 
			
		||||
	container.Adopt(messageContainer, true)
 | 
			
		||||
	messageRow.Adopt(elements.NewIcon(iconId, tomo.IconSizeLarge), false)
 | 
			
		||||
	messageRow.Adopt(elements.NewLabel(message, false), true)
 | 
			
		||||
	
 | 
			
		||||
	controlRow.Adopt(elements.NewSpacer(false), true)
 | 
			
		||||
	if len(buttons) == 0 {
 | 
			
		||||
		button := elements.NewButton("OK")
 | 
			
		||||
		button.SetIcon(tomo.IconYes)
 | 
			
		||||
		button.OnClick(window.Close)
 | 
			
		||||
		container.Adopt(button, false)
 | 
			
		||||
		controlRow.Adopt(button, false)
 | 
			
		||||
		button.Focus()
 | 
			
		||||
	} else {
 | 
			
		||||
		var button *elements.Button
 | 
			
		||||
@ -74,11 +75,14 @@ func NewDialog (
 | 
			
		||||
				buttonDescriptor.OnPress()
 | 
			
		||||
				window.Close()
 | 
			
		||||
			})
 | 
			
		||||
			container.Adopt(button, false)
 | 
			
		||||
			controlRow.Adopt(button, false)
 | 
			
		||||
		}
 | 
			
		||||
		button.Focus()
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	box.Adopt(messageRow, true)
 | 
			
		||||
	box.Adopt(controlRow, false)
 | 
			
		||||
	window.Adopt(box)
 | 
			
		||||
	window.Show()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user