233 lines
5.4 KiB
Go
233 lines
5.4 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.
|
|
*/
|
|
|
|
// Window button options. OR these together in a call to Window.SetButtons().
|
|
const (
|
|
CloseButton = 0x01
|
|
|
|
// NOTICE: MaximizeButton behavior is currently buggy, window doesn't
|
|
// redraw itself at the new size properly.
|
|
MaximizeButton = 0x02
|
|
|
|
// Minimize button has no default behavior attached; you can bind it with
|
|
// window.Handle(MinimizeWindow) to set your own event handler.
|
|
MinimizeButton = 0x04
|
|
)
|
|
|
|
// 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.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)
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
if newBottom != nil {
|
|
s.winBottom = newBottom
|
|
}
|
|
|
|
// Toggle the focus states.
|
|
oldTop.window.SetFocus(false)
|
|
target.window.SetFocus(true)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsPointInWindow returns whether the given Point overlaps with a window managed
|
|
// by the Supervisor.
|
|
func (s *Supervisor) IsPointInWindow(point render.Point) bool {
|
|
node := s.winFocus
|
|
for node != nil {
|
|
if point.Inside(AbsoluteRect(node.window)) && !node.window.hidden {
|
|
return true
|
|
}
|
|
node = node.next
|
|
}
|
|
return false
|
|
}
|
|
|
|
// CloseAllWindows closes all open windows being managed by supervisor.
|
|
// Returns the number of windows closed.
|
|
func (s *Supervisor) CloseAllWindows() int {
|
|
var (
|
|
node = s.winFocus
|
|
i = 0
|
|
)
|
|
for node != nil {
|
|
i++
|
|
node.window.Close()
|
|
node = node.next
|
|
}
|
|
return i
|
|
}
|
|
|
|
// CloseActiveWindow closes the topmost active window.
|
|
func (s *Supervisor) CloseActiveWindow() bool {
|
|
var node = s.winFocus
|
|
if node != nil {
|
|
node.window.Close()
|
|
}
|
|
|
|
// Find the next visible window to focus.
|
|
for node != nil {
|
|
if !node.window.Hidden() {
|
|
s.FocusWindow(node.window)
|
|
break
|
|
}
|
|
node = node.next
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// PrintWindows is a debug function that walks the window tree and prints them to your console.
|
|
func (s *Supervisor) PrintWindows() {
|
|
var (
|
|
node = s.winBottom
|
|
i int
|
|
)
|
|
|
|
fmt.Println("From the bottom:")
|
|
for node != nil {
|
|
i++
|
|
fmt.Printf("%d. %s focused=%+v hidden=%+v\n", i, node.window, node.window.Focused(), node.window.Hidden())
|
|
node = node.prev
|
|
}
|
|
|
|
node = s.winFocus
|
|
i = 0
|
|
|
|
fmt.Println("Focus order:")
|
|
for node != nil {
|
|
i++
|
|
fmt.Printf("%d. %s focused=%+v hidden=%+v\n", i, node.window, node.window.Focused(), node.window.Hidden())
|
|
node = node.next
|
|
}
|
|
}
|
|
|
|
// presentWindows draws the windows from bottom to top.
|
|
func (s *Supervisor) presentWindows(e render.Engine) {
|
|
item := s.winBottom
|
|
for item != nil {
|
|
item.window.Compute(e)
|
|
item.window.Present(e, item.window.Point())
|
|
item = item.prev
|
|
}
|
|
}
|