149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
|
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
|
||
|
}
|