2024-06-22 13:38:14 -06:00
|
|
|
package objects
|
|
|
|
|
|
|
|
import "git.tebibyte.media/tomo/tomo"
|
|
|
|
import "git.tebibyte.media/tomo/tomo/input"
|
|
|
|
import "git.tebibyte.media/tomo/objects/layouts"
|
|
|
|
|
2024-09-12 13:08:03 -06:00
|
|
|
var _ tomo.Object = new(Notebook)
|
2024-08-24 20:15:21 -06:00
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
// Notebook holds multiple objects, each in their own page. The user can click
|
2024-09-12 13:08:03 -06:00
|
|
|
// the tab bar at the top to choose which one is activated.
|
2024-08-25 00:36:05 -06:00
|
|
|
//
|
|
|
|
// Sub-components:
|
|
|
|
// - TabRow sits at the top of the container and contains a row of tabs.
|
|
|
|
// - TabSpacer sits at either end of the tab row, bookending the list of tabs.
|
2024-09-12 13:13:38 -06:00
|
|
|
// - Tab appears in the tab row for each tab in the notebook. The user can
|
2024-08-25 00:36:05 -06:00
|
|
|
// click on the tab to switch to it.
|
2024-09-12 13:44:11 -06:00
|
|
|
// - PageWrapper sits underneath the TabRow and contains the active page.
|
2024-08-25 00:36:05 -06:00
|
|
|
//
|
|
|
|
// TabSpacer tags:
|
|
|
|
// - [left] The spacer is on the left.
|
|
|
|
// - [right] The spacer is on the right.
|
|
|
|
//
|
|
|
|
// Tab tags:
|
2024-09-12 13:13:38 -06:00
|
|
|
// - [active] The tab is currently active and its page is visible.
|
2024-09-12 13:08:03 -06:00
|
|
|
type Notebook struct {
|
2024-08-24 20:15:21 -06:00
|
|
|
box tomo.ContainerBox
|
2024-06-22 13:38:14 -06:00
|
|
|
|
|
|
|
leftSpacer tomo.Box
|
|
|
|
rightSpacer tomo.Box
|
2024-09-12 13:13:38 -06:00
|
|
|
tabsRow tomo.ContainerBox
|
2024-09-12 13:44:11 -06:00
|
|
|
pageWrapper tomo.ContainerBox
|
2024-09-12 13:13:38 -06:00
|
|
|
active string
|
|
|
|
pages []*page
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
|
2024-09-12 13:08:03 -06:00
|
|
|
// NewNotebook creates a new tabbed notebook.
|
|
|
|
func NewNotebook () *Notebook {
|
2024-09-12 13:10:29 -06:00
|
|
|
notebook := &Notebook {
|
2024-08-24 20:15:21 -06:00
|
|
|
box: tomo.NewContainerBox(),
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
2024-09-12 13:10:29 -06:00
|
|
|
notebook.box.SetRole(tomo.R("objects", "Notebook"))
|
|
|
|
notebook.box.SetAttr(tomo.ALayout(layouts.Column { false, true }))
|
2024-06-22 13:38:14 -06:00
|
|
|
|
2024-09-12 13:10:29 -06:00
|
|
|
notebook.leftSpacer = tomo.NewBox()
|
|
|
|
notebook.leftSpacer.SetRole(tomo.R("objects", "TabSpacer"))
|
|
|
|
notebook.leftSpacer.SetTag("left", true)
|
|
|
|
notebook.rightSpacer = tomo.NewBox()
|
|
|
|
notebook.rightSpacer.SetRole(tomo.R("objects", "TabSpacer"))
|
|
|
|
notebook.rightSpacer.SetTag("right", true)
|
2024-06-22 13:38:14 -06:00
|
|
|
|
2024-09-12 13:44:11 -06:00
|
|
|
notebook.tabsRow = tomo.NewContainerBox()
|
|
|
|
notebook.tabsRow.SetRole(tomo.R("objects", "TabRow"))
|
|
|
|
notebook.box.Add(notebook.tabsRow)
|
|
|
|
|
|
|
|
notebook.pageWrapper = tomo.NewContainerBox()
|
|
|
|
notebook.pageWrapper.SetRole(tomo.R("objects", "PageWrapper"))
|
|
|
|
notebook.pageWrapper.SetAttr(tomo.ALayout(layouts.Column { true }))
|
|
|
|
notebook.box.Add(notebook.pageWrapper)
|
|
|
|
|
2024-09-12 13:10:29 -06:00
|
|
|
notebook.Clear()
|
|
|
|
notebook.setTabRowLayout()
|
|
|
|
return notebook
|
2024-08-24 20:15:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetBox returns the underlying box.
|
2024-09-12 13:08:03 -06:00
|
|
|
func (this *Notebook) GetBox () tomo.Box {
|
2024-08-24 20:15:21 -06:00
|
|
|
return this.box
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
// Activate switches to a named page.
|
2024-09-12 13:08:03 -06:00
|
|
|
func (this *Notebook) Activate (name string) {
|
2024-06-22 13:38:14 -06:00
|
|
|
if _, tab := this.findTab(this.active); tab != nil {
|
|
|
|
tab.setActive(false)
|
2024-09-12 13:44:11 -06:00
|
|
|
this.pageWrapper.Remove(tab.root)
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
if _, tab := this.findTab(name); tab != nil {
|
|
|
|
tab.setActive(true)
|
2024-09-12 13:44:11 -06:00
|
|
|
this.pageWrapper.Add(tab.root)
|
2024-06-22 13:38:14 -06:00
|
|
|
} else {
|
|
|
|
name = ""
|
|
|
|
}
|
|
|
|
this.active = name
|
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
// Add adds an object as a page with the specified name.
|
2024-09-12 13:10:29 -06:00
|
|
|
func (this *Notebook) Add (name string, root tomo.Object) {
|
2024-09-12 13:13:38 -06:00
|
|
|
pag := &page {
|
2024-06-22 13:38:14 -06:00
|
|
|
TextBox: tomo.NewTextBox(),
|
|
|
|
name: name,
|
|
|
|
root: root,
|
|
|
|
}
|
2024-09-12 13:13:38 -06:00
|
|
|
pag.SetRole(tomo.R("objects", "Tab"))
|
|
|
|
pag.SetText(name)
|
|
|
|
pag.OnButtonDown(func (button input.Button) bool {
|
2024-07-25 10:58:38 -06:00
|
|
|
if button != input.ButtonLeft { return false }
|
2024-06-22 13:38:14 -06:00
|
|
|
this.Activate(name)
|
2024-07-25 10:58:38 -06:00
|
|
|
return true
|
|
|
|
})
|
2024-09-12 13:13:38 -06:00
|
|
|
pag.OnButtonUp(func (button input.Button) bool {
|
2024-07-25 10:58:38 -06:00
|
|
|
if button != input.ButtonLeft { return false }
|
|
|
|
return true
|
2024-06-22 13:38:14 -06:00
|
|
|
})
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
this.pages = append(this.pages, pag)
|
|
|
|
this.tabsRow.Insert(pag, this.rightSpacer)
|
2024-06-22 13:38:14 -06:00
|
|
|
this.setTabRowLayout()
|
|
|
|
|
|
|
|
// if the row was empty before, activate this tab
|
2024-09-12 13:13:38 -06:00
|
|
|
if len(this.pages) == 1 {
|
2024-06-22 13:38:14 -06:00
|
|
|
this.Activate(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
// Remove removes the named page.
|
2024-09-12 13:10:29 -06:00
|
|
|
func (this *Notebook) Remove (name string) {
|
2024-06-22 13:38:14 -06:00
|
|
|
index, tab := this.findTab(name)
|
|
|
|
if index < 0 { return }
|
|
|
|
nextIndex := index - 1
|
|
|
|
|
|
|
|
this.tabsRow.Remove(tab)
|
2024-09-12 13:13:38 -06:00
|
|
|
this.pages = append(this.pages[:index], this.pages[index - 1:]...)
|
2024-06-22 13:38:14 -06:00
|
|
|
this.setTabRowLayout()
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
if nextIndex < 0 { nextIndex = 0 }
|
|
|
|
if nextIndex >= len(this.pages) { nextIndex = len(this.pages) - 1 }
|
2024-06-22 13:38:14 -06:00
|
|
|
if nextIndex < 0 {
|
|
|
|
this.Activate("")
|
|
|
|
} else {
|
2024-09-12 13:13:38 -06:00
|
|
|
this.Activate(this.pages[nextIndex].name)
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-12 13:10:29 -06:00
|
|
|
// Clear removes all tabs.
|
|
|
|
func (this *Notebook) Clear () {
|
2024-09-12 13:13:38 -06:00
|
|
|
this.pages = nil
|
2024-06-22 13:38:14 -06:00
|
|
|
this.tabsRow.Clear()
|
|
|
|
this.tabsRow.Add(this.leftSpacer)
|
|
|
|
this.tabsRow.Add(this.rightSpacer)
|
2024-09-12 13:44:11 -06:00
|
|
|
this.pageWrapper.Clear()
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
|
2024-09-12 13:08:03 -06:00
|
|
|
func (this *Notebook) setTabRowLayout () {
|
2024-09-12 13:13:38 -06:00
|
|
|
row := make(layouts.Row, 1 + len(this.pages) + 1)
|
2024-06-22 13:38:14 -06:00
|
|
|
row[len(row) - 1] = true
|
2024-07-25 10:58:38 -06:00
|
|
|
this.tabsRow.SetAttr(tomo.ALayout(row))
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
func (this *Notebook) findTab (name string) (int, *page) {
|
|
|
|
for index, pag := range this.pages {
|
|
|
|
if pag.name == name { return index, pag }
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
return -1, nil
|
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
type page struct {
|
2024-06-22 13:38:14 -06:00
|
|
|
tomo.TextBox
|
|
|
|
name string
|
|
|
|
root tomo.Object
|
|
|
|
}
|
|
|
|
|
2024-09-12 13:13:38 -06:00
|
|
|
func (this *page) setActive (active bool) {
|
2024-06-22 13:38:14 -06:00
|
|
|
if active {
|
2024-07-21 09:48:28 -06:00
|
|
|
this.SetRole(tomo.R("objects", "Tab"))
|
2024-08-14 09:45:30 -06:00
|
|
|
this.SetTag("active", true)
|
2024-06-22 13:38:14 -06:00
|
|
|
} else {
|
2024-07-21 09:48:28 -06:00
|
|
|
this.SetRole(tomo.R("objects", "Tab"))
|
2024-08-14 09:45:30 -06:00
|
|
|
this.SetTag("active", false)
|
2024-06-22 13:38:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|