Noah Petherbridge
76ddda352d
It used to be possible to confuse the window manager in the CloseActiveWindow() function and the linked list got all broken. Seems more reliable now! Added function PrintWindows() to inspect the linked lists.
234 lines
5.4 KiB
Go
234 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.Destroy()
|
|
node.window.Hide()
|
|
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.Hide()
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|