Overhaul collection of containers
This commit is contained in:
parent
b8b80f8862
commit
cdf23c9b13
93
abstractcontainer.go
Normal file
93
abstractcontainer.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/tomo/event"
|
||||||
|
|
||||||
|
type abstractContainer struct {
|
||||||
|
box tomo.ContainerBox
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *abstractContainer) init (layout tomo.Layout, children ...tomo.Object) {
|
||||||
|
this.box = tomo.NewContainerBox()
|
||||||
|
this.SetLayout(layout)
|
||||||
|
for _, child := range children {
|
||||||
|
this.Add(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBox returns the underlying box.
|
||||||
|
func (this *abstractContainer) GetBox () tomo.Box {
|
||||||
|
return this.box
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentBounds returns the bounds of the inner content of the container
|
||||||
|
// relative to the container's InnerBounds.
|
||||||
|
func (this *abstractContainer) ContentBounds () image.Rectangle {
|
||||||
|
return this.box.ContentBounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScrollTo shifts the origin of the container's content to the origin of the
|
||||||
|
// container's InnerBounds, offset by the given point.
|
||||||
|
func (this *abstractContainer) ScrollTo (position image.Point) {
|
||||||
|
this.box.ScrollTo(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnContentBoundsChange specifies a function to be called when the container's
|
||||||
|
// ContentBounds or InnerBounds changes.
|
||||||
|
func (this *abstractContainer) OnContentBoundsChange (callback func ()) event.Cookie {
|
||||||
|
return this.box.OnContentBoundsChange(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLayout sets the layout of the container.
|
||||||
|
func (this *abstractContainer) SetLayout (layout tomo.Layout) {
|
||||||
|
if layout == nil {
|
||||||
|
this.box.UnsetAttr(tomo.AttrKindLayout)
|
||||||
|
} else {
|
||||||
|
this.box.SetAttr(tomo.ALayout(layout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAlign sets the X and Y alignment of the container.
|
||||||
|
func (this *abstractContainer) SetAlign (x, y tomo.Align) {
|
||||||
|
this.box.SetAttr(tomo.AAlign(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOverflow sets the X and Y overflow of the container.
|
||||||
|
func (this *abstractContainer) SetOverflow (x, y bool) {
|
||||||
|
this.box.SetAttr(tomo.AOverflow(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add appends a child object. If the object is already a child of another
|
||||||
|
// object, it will be removed from that object first.
|
||||||
|
func (this *abstractContainer) Add (object tomo.Object) {
|
||||||
|
this.box.Add(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a child object, if it is a child of this container.
|
||||||
|
func (this *abstractContainer) Remove (object tomo.Object) {
|
||||||
|
this.box.Remove(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts a child object before a specified object. If the before object
|
||||||
|
// is nil or is not contained within this container, the inserted object is
|
||||||
|
// appended. If the inserted object is already a child of another object, it
|
||||||
|
// will be removed from that object first.
|
||||||
|
func (this *abstractContainer) Insert (child tomo.Object, before tomo.Object) {
|
||||||
|
this.box.Insert(child, before)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all child objects.
|
||||||
|
func (this *abstractContainer) Clear () {
|
||||||
|
this.box.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns hte amount of child objects.
|
||||||
|
func (this *abstractContainer) Len () int {
|
||||||
|
return this.box.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the child object at the specified index.
|
||||||
|
func (this *abstractContainer) At (index int) tomo.Object {
|
||||||
|
return this.box.At(index)
|
||||||
|
}
|
128
container.go
128
container.go
@ -1,133 +1,21 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import "image"
|
|
||||||
import "git.tebibyte.media/tomo/tomo"
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
import "git.tebibyte.media/tomo/tomo/event"
|
|
||||||
|
|
||||||
var _ tomo.ContentObject = new(Container)
|
var _ tomo.ContentObject = new(Container)
|
||||||
|
|
||||||
// Container is an object that can contain other objects. It can be used as a
|
// Container is an object that can contain other objects. It is plain looking,
|
||||||
// primitive for building more complex layouts. It has two main variants: an
|
// and is intended to be used within other containers as a primitive for
|
||||||
// outer container, and an inner container. The outer container has padding
|
// building more complex layouts.
|
||||||
// around its edges, whereas the inner container does not. It also has a
|
|
||||||
// "sunken" variation designed to hold a scrolled list of items.
|
|
||||||
//
|
|
||||||
// Tags:
|
|
||||||
// - [outer] The container is the root of a window.
|
|
||||||
// - [inner] The container is within another container, and is part of a
|
|
||||||
// larger layout.
|
|
||||||
// - [sunken] The container holds a visually grouped, usually scrolled, list
|
|
||||||
// of items.
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
box tomo.ContainerBox
|
abstractContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainer (layout tomo.Layout, children ...tomo.Object) *Container {
|
// NewContainer creates a new container.
|
||||||
this := &Container {
|
func NewContainer (layout tomo.Layout, children ...tomo.Object) *Container {
|
||||||
box: tomo.NewContainerBox(),
|
this := &Container { }
|
||||||
}
|
this.init(layout, children...)
|
||||||
this.box.SetAttr(tomo.ALayout(layout))
|
|
||||||
for _, child := range children {
|
|
||||||
this.Add(child)
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewOuterContainer creates a new container that has padding around it, as well
|
|
||||||
// as a solid background color. It is meant to be used as a root container for a
|
|
||||||
// window, tab pane, etc.
|
|
||||||
func NewOuterContainer (layout tomo.Layout, children ...tomo.Object) *Container {
|
|
||||||
this := newContainer(layout, children...)
|
|
||||||
this.box.SetRole(tomo.R("objects", "Container"))
|
this.box.SetRole(tomo.R("objects", "Container"))
|
||||||
this.box.SetTag("outer", true)
|
this.box.SetTag("outer", true)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSunkenContainer creates a new container with a sunken style and padding
|
|
||||||
// around it. It is meant to be used as a root container for a ScrollContainer.
|
|
||||||
func NewSunkenContainer (layout tomo.Layout, children ...tomo.Object) *Container {
|
|
||||||
this := newContainer(layout, children...)
|
|
||||||
this.box.SetRole(tomo.R("objects", "Container"))
|
|
||||||
this.box.SetTag("sunken", true)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewInnerContainer creates a new container that has no padding around it.
|
|
||||||
func NewInnerContainer (layout tomo.Layout, children ...tomo.Object) *Container {
|
|
||||||
this := newContainer(layout, children...)
|
|
||||||
this.box.SetRole(tomo.R("objects", "Container"))
|
|
||||||
this.box.SetTag("inner", true)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBox returns the underlying box.
|
|
||||||
func (this *Container) GetBox () tomo.Box {
|
|
||||||
return this.box
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentBounds returns the bounds of the inner content of the container
|
|
||||||
// relative to the container's InnerBounds.
|
|
||||||
func (this *Container) ContentBounds () image.Rectangle {
|
|
||||||
return this.box.ContentBounds()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScrollTo shifts the origin of the container's content to the origin of the
|
|
||||||
// container's InnerBounds, offset by the given point.
|
|
||||||
func (this *Container) ScrollTo (position image.Point) {
|
|
||||||
this.box.ScrollTo(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnContentBoundsChange specifies a function to be called when the container's
|
|
||||||
// ContentBounds or InnerBounds changes.
|
|
||||||
func (this *Container) OnContentBoundsChange (callback func ()) event.Cookie {
|
|
||||||
return this.box.OnContentBoundsChange(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLayout sets the layout of the container.
|
|
||||||
func (this *Container) SetLayout (layout tomo.Layout) {
|
|
||||||
this.box.SetAttr(tomo.ALayout(layout))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAlign sets the X and Y alignment of the container.
|
|
||||||
func (this *Container) SetAlign (x, y tomo.Align) {
|
|
||||||
this.box.SetAttr(tomo.AAlign(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOverflow sets the X and Y overflow of the container.
|
|
||||||
func (this *Container) SetOverflow (x, y bool) {
|
|
||||||
this.box.SetAttr(tomo.AOverflow(x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add appends a child object. If the object is already a child of another
|
|
||||||
// object, it will be removed from that object first.
|
|
||||||
func (this *Container) Add (object tomo.Object) {
|
|
||||||
this.box.Add(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes a child object, if it is a child of this container.
|
|
||||||
func (this *Container) Remove (object tomo.Object) {
|
|
||||||
this.box.Remove(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert inserts a child object before a specified object. If the before object
|
|
||||||
// is nil or is not contained within this container, the inserted object is
|
|
||||||
// appended. If the inserted object is already a child of another object, it
|
|
||||||
// will be removed from that object first.
|
|
||||||
func (this *Container) Insert (child tomo.Object, before tomo.Object) {
|
|
||||||
this.box.Insert(child, before)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removes all child objects.
|
|
||||||
func (this *Container) Clear () {
|
|
||||||
this.box.Clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns hte amount of child objects.
|
|
||||||
func (this *Container) Len () int {
|
|
||||||
return this.box.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// At returns the child object at the specified index.
|
|
||||||
func (this *Container) At (index int) tomo.Object {
|
|
||||||
return this.box.At(index)
|
|
||||||
}
|
|
||||||
|
22
pegboard.go
Normal file
22
pegboard.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/objects/layouts"
|
||||||
|
|
||||||
|
var _ tomo.ContentObject = new(Pegboard)
|
||||||
|
|
||||||
|
// Pegboard is an object that can contain other objects. It is intended to
|
||||||
|
// contain a flowed list of objects which represent some data, such as files.
|
||||||
|
type Pegboard struct {
|
||||||
|
abstractContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPegboard creates a new pegboard. If the provided layout is nil, it will
|
||||||
|
// use a FlowHorizontal layout.
|
||||||
|
func NewPegboard (layout tomo.Layout, children ...tomo.Object) *Pegboard {
|
||||||
|
if layout == nil { layout = layouts.FlowHorizontal }
|
||||||
|
pegboard := &Pegboard { }
|
||||||
|
pegboard.init(layout, children...)
|
||||||
|
pegboard.box.SetRole(tomo.R("objects", "Pegboard"))
|
||||||
|
return pegboard
|
||||||
|
}
|
20
root.go
Normal file
20
root.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
|
||||||
|
var _ tomo.ContentObject = new(Root)
|
||||||
|
|
||||||
|
// Root is an object that can contain other objects. It is intended to be used
|
||||||
|
// as the root of a window in order to contain its segments.
|
||||||
|
type Root struct {
|
||||||
|
abstractContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoot creates a new container.
|
||||||
|
func NewRoot (layout tomo.Layout, children ...tomo.Object) *Container {
|
||||||
|
this := &Container { }
|
||||||
|
this.init(layout, children...)
|
||||||
|
this.box.SetRole(tomo.R("objects", "Container"))
|
||||||
|
this.box.SetTag("outer", true)
|
||||||
|
return this
|
||||||
|
}
|
100
segment.go
Normal file
100
segment.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package objects
|
||||||
|
|
||||||
|
import "git.tebibyte.media/tomo/tomo"
|
||||||
|
import "git.tebibyte.media/tomo/objects/layouts"
|
||||||
|
|
||||||
|
var _ tomo.ContentObject = new(Segment)
|
||||||
|
|
||||||
|
// Segment is an object that can contain other objects, and provides a way to
|
||||||
|
// categorize the functionality of a window. Segments are typically laid out
|
||||||
|
// vertically in a window. There are several variants, the main one being
|
||||||
|
// the content segment. They can all be created using specialized constructors.
|
||||||
|
//
|
||||||
|
// The following is a brief visual discription of how they are typically used,
|
||||||
|
// and in what order:
|
||||||
|
//
|
||||||
|
// ┌──────────────────────────┐
|
||||||
|
// │ ┌──┐ ┌──┐ ┌────────────┐ ├─ command
|
||||||
|
// │ │◄─│ │─►│ │/foo/bar/baz│ │
|
||||||
|
// │ └──┘ └──┘ └────────────┘ │
|
||||||
|
// ├──────────────────────────┤
|
||||||
|
// │ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ├─ content
|
||||||
|
// │ │ │ │ │ │ │ │ │ │
|
||||||
|
// │ └───┘ └───┘ └───┘ └───┘ │
|
||||||
|
// │ ┌───┐ │
|
||||||
|
// │ │ │ │
|
||||||
|
// │ └───┘ │
|
||||||
|
// ├──────────────────────────┤
|
||||||
|
// │ 5 items, 4KiB ├─ status
|
||||||
|
// └──────────────────────────┘
|
||||||
|
// ┌─────────────────┐
|
||||||
|
// │ ┌───┐ │
|
||||||
|
// │ │ ! │ Continue? │
|
||||||
|
// │ └───┘ │
|
||||||
|
// ├─────────────────┤
|
||||||
|
// │ ┌──┐ ┌───┐ ├────────── option
|
||||||
|
// │ │No│ │Yes│ │
|
||||||
|
// │ └──┘ └───┘ │
|
||||||
|
// └─────────────────┘
|
||||||
|
//
|
||||||
|
// Tags:
|
||||||
|
// - [command] The segment is a command segment.
|
||||||
|
// - [content] The segment is a content segment.
|
||||||
|
// - [status] The segment is a status segment.
|
||||||
|
// - [option] The segment is an option segment.
|
||||||
|
type Segment struct {
|
||||||
|
abstractContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSegment (kind string, layout tomo.Layout, children ...tomo.Object) *Segment {
|
||||||
|
segment := &Segment { }
|
||||||
|
segment.init(layout, children...)
|
||||||
|
segment.box.SetRole(tomo.R("objects", "Segment"))
|
||||||
|
segment.box.SetTag(kind, true)
|
||||||
|
return segment
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCommandSegment creates a new segment intended to hold the window's
|
||||||
|
// navigational controls and command functionality. If the provided layout is
|
||||||
|
// nil, it will use a ContractHorizontal layout.
|
||||||
|
func NewCommandSegment (layout tomo.Layout, children ...tomo.Object) *Segment {
|
||||||
|
if layout == nil { layout = layouts.ContractHorizontal }
|
||||||
|
return newSegment("command", layout, children...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContentSegment creates a new segment intended to hold the window's main
|
||||||
|
// content. If the provided layout is nil, it will use a ContractVertical
|
||||||
|
// layout.
|
||||||
|
func NewContentSegment (layout tomo.Layout, children ...tomo.Object) *Segment {
|
||||||
|
if layout == nil { layout = layouts.ContractVertical }
|
||||||
|
return newSegment("content", layout, children...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatusSegment creates a new segment intended to display the window's
|
||||||
|
// status. If the provided layout is nil, it will use a ContractHorizontal
|
||||||
|
// layout.
|
||||||
|
func NewStatusSegment (layout tomo.Layout, children ...tomo.Object) *Segment {
|
||||||
|
if layout == nil { layout = layouts.ContractHorizontal }
|
||||||
|
return newSegment("status", layout, children...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOptionSegment creates a new segment intended to hold the window's options.
|
||||||
|
// This is typically used for dialog boxes. If the provided layout is nil, it
|
||||||
|
// will use a ContractHorizontal layout. By default, it is end-aligned.
|
||||||
|
func NewOptionSegment (layout tomo.Layout, children ...tomo.Object) *Segment {
|
||||||
|
if layout == nil { layout = layouts.ContractHorizontal }
|
||||||
|
segment := newSegment("option", layout, children...)
|
||||||
|
segment.GetBox().SetAttr(tomo.AAlign(tomo.AlignEnd, tomo.AlignMiddle))
|
||||||
|
return segment
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO create constructors somewhere that make a window with segments and
|
||||||
|
// automatically applied layout
|
||||||
|
//
|
||||||
|
// window, content, err := NewContentWindow
|
||||||
|
// window, nav, content, status, err := NewNavContentStatusWindow
|
||||||
|
// window, content, control, err := NewContentControlWindow
|
||||||
|
//
|
||||||
|
// alternatively:
|
||||||
|
// (the constructor will create a column from the types of the segments)
|
||||||
|
// window, err := NewSegmentedWindow(NewNavigationSegment(), NewContentSegment(), NewStatusSegment())
|
Loading…
Reference in New Issue
Block a user