ui/window_manager.go

149 lines
3.5 KiB
Go
Raw Normal View History

package ui
import (
"errors"
"fmt"
"git.kirsle.net/go/render"
)
/*
window_manager.go holds data types and Supervisor methods related to the
management of ui.Window widgets.
*/
// FocusedWindow is a doubly-linked list of recently focused Windows, with
// the current and most-recently focused on top. TODO make not exported.
type FocusedWindow struct {
window *Window
prev *FocusedWindow
next *FocusedWindow
}
// String of the FocusedWindow returns the underlying Window's String().
func (fw FocusedWindow) String() string {
return fw.window.String()
}
// Print the structure of the linked list from top to bottom.
func (fw *FocusedWindow) Print() {
var (
node = fw
i = 0
)
for node != nil {
fmt.Printf("[%d] window=%s prev=%s next=%s\n",
i, node.window, node.prev, node.next,
)
node = node.next
i++
}
}
// addWindow installs a Window into the supervisor to be managed. It is called
// by ui.Window.Supervise() and the newly added window becomes the focused
// one by default at the top of the linked list.
func (s *Supervisor) addWindow(win *Window) {
// Record in the window that it is managed by Supervisor, useful to control
// event propagation to non-focused windows.
win.managed = true
if s.winFocus == nil {
// First window added.
s.winFocus = &FocusedWindow{
window: win,
}
s.winTop = s.winFocus
s.winBottom = s.winFocus
win.SetFocus(true)
} else {
// New window, make it the top one.
oldTop := s.winFocus
s.winFocus = &FocusedWindow{
window: win,
next: oldTop,
}
oldTop.prev = s.winFocus
oldTop.window.SetFocus(false)
win.SetFocus(true)
}
}
// presentWindows draws the windows from bottom to top.
func (s *Supervisor) presentWindows(e render.Engine) {
item := s.winBottom
for item != nil {
item.window.Present(e, item.window.Point())
item = item.prev
}
}
// FocusWindow brings the given window to the top of the supervisor's focus.
//
// The window must have previously been added to the supervisor's Window Manager
// by calling the Supervise() method of the window.
func (s *Supervisor) FocusWindow(win *Window) error {
if s.winFocus == nil {
return errors.New("no windows managed by supervisor")
}
// If the top window is already the target, return.
if s.winFocus.window == win {
return nil
}
// Find the window in the linked list.
var (
item = s.winFocus // item as we iterate the list
oldTop = s.winFocus // original first item in the list
target *FocusedWindow // identified target window to raise
newBottom *FocusedWindow // if the target was the bottom, this is new bottom
i = 0
)
for item != nil {
if item.window == win {
// Found it!
target = item
// Is it the last window in the list? Record the new bottom node.
if item.next == nil && item.prev != nil {
newBottom = item.prev
}
// Remove it from its position in the linked list. Join its
// previous and next nodes to bridge the gap.
if item.next != nil {
item.next.prev = item.prev
}
if item.prev != nil {
item.prev.next = item.next
}
break
}
item = item.next
i++
}
// Found it?
if target != nil {
// Put the target at the top of the list, pointing to the old top.
target.next = oldTop
target.prev = nil
oldTop.prev = target
s.winFocus = target
// Fix the top and bottom pointers.
s.winTop = s.winFocus
if newBottom != nil {
s.winBottom = newBottom
}
// Toggle the focus states.
oldTop.window.SetFocus(false)
target.window.SetFocus(true)
}
return nil
}