package objects import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo/input" import "git.tebibyte.media/tomo/objects/layouts" var _ tomo.Object = new(Notebook) // Notebook holds multiple objects, each in their own page. The user can click // the tab bar at the top to choose which one is activated. // // 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. // - Tab appears in the tab row for each tab in the notebook. The user can // click on the tab to switch to it. // // TabSpacer tags: // - [left] The spacer is on the left. // - [right] The spacer is on the right. // // Tab tags: // - [active] The tab is currently active and its page is visible. type Notebook struct { box tomo.ContainerBox leftSpacer tomo.Box rightSpacer tomo.Box tabsRow tomo.ContainerBox active string pages []*page } // NewNotebook creates a new tabbed notebook. func NewNotebook () *Notebook { notebook := &Notebook { box: tomo.NewContainerBox(), } notebook.box.SetRole(tomo.R("objects", "Notebook")) notebook.box.SetAttr(tomo.ALayout(layouts.Column { false, true })) notebook.tabsRow = tomo.NewContainerBox() notebook.tabsRow.SetRole(tomo.R("objects", "TabRow")) notebook.box.Add(notebook.tabsRow) 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) notebook.Clear() notebook.setTabRowLayout() return notebook } // GetBox returns the underlying box. func (this *Notebook) GetBox () tomo.Box { return this.box } // Activate switches to a named page. func (this *Notebook) Activate (name string) { if _, tab := this.findTab(this.active); tab != nil { tab.setActive(false) this.box.Remove(tab.root) } if _, tab := this.findTab(name); tab != nil { tab.setActive(true) this.box.Add(tab.root) } else { name = "" } this.active = name } // Add adds an object as a page with the specified name. func (this *Notebook) Add (name string, root tomo.Object) { pag := &page { TextBox: tomo.NewTextBox(), name: name, root: root, } pag.SetRole(tomo.R("objects", "Tab")) pag.SetText(name) pag.OnButtonDown(func (button input.Button) bool { if button != input.ButtonLeft { return false } this.Activate(name) return true }) pag.OnButtonUp(func (button input.Button) bool { if button != input.ButtonLeft { return false } return true }) this.pages = append(this.pages, pag) this.tabsRow.Insert(pag, this.rightSpacer) this.setTabRowLayout() // if the row was empty before, activate this tab if len(this.pages) == 1 { this.Activate(name) } } // Remove removes the named page. func (this *Notebook) Remove (name string) { index, tab := this.findTab(name) if index < 0 { return } nextIndex := index - 1 this.tabsRow.Remove(tab) this.pages = append(this.pages[:index], this.pages[index - 1:]...) this.setTabRowLayout() if nextIndex < 0 { nextIndex = 0 } if nextIndex >= len(this.pages) { nextIndex = len(this.pages) - 1 } if nextIndex < 0 { this.Activate("") } else { this.Activate(this.pages[nextIndex].name) } } // Clear removes all tabs. func (this *Notebook) Clear () { this.pages = nil this.tabsRow.Clear() this.tabsRow.Add(this.leftSpacer) this.tabsRow.Add(this.rightSpacer) } func (this *Notebook) setTabRowLayout () { row := make(layouts.Row, 1 + len(this.pages) + 1) row[len(row) - 1] = true this.tabsRow.SetAttr(tomo.ALayout(row)) } func (this *Notebook) findTab (name string) (int, *page) { for index, pag := range this.pages { if pag.name == name { return index, pag } } return -1, nil } type page struct { tomo.TextBox name string root tomo.Object } func (this *page) setActive (active bool) { if active { this.SetRole(tomo.R("objects", "Tab")) this.SetTag("active", true) } else { this.SetRole(tomo.R("objects", "Tab")) this.SetTag("active", false) } }