Noah Petherbridge
7d9ba79cd2
* Adds Window Manager support to the Supervisor, so that Window widgets can be dragged by their title bar, clicked to focus, etc. * Create a ui.Window as normal, but instead of Packing or Placing it into a parent container as before, you call .Supervise() and give it your Supervisor. The window registers itself to be managed and drawn by the Supervisor itself. * Supervisor manages the focused window order using a doubly linked list. When a window takes focus it moves to the top of the list. Widgets in the active window take event priority. * Extended DragDrop API to support holding a widget pointer in the drag operation. * Changed widget event Handle functions to return an error: so that they could return ErrStopPropagation to prevent events going to more widgets once handled (for important events). Some bugs remain around overlapping windows and event propagation.
232 lines
5.3 KiB
Go
232 lines
5.3 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.kirsle.net/go/render"
|
|
)
|
|
|
|
// Window is a frame with a title bar.
|
|
type Window struct {
|
|
BaseWidget
|
|
Title string
|
|
Active bool
|
|
|
|
// Title bar colors. Sensible defaults are chosen in NewWindow but you
|
|
// may customize after the fact.
|
|
ActiveTitleBackground render.Color
|
|
ActiveTitleForeground render.Color
|
|
InactiveTitleBackground render.Color
|
|
InactiveTitleForeground render.Color
|
|
|
|
// Private widgets.
|
|
body *Frame
|
|
titleBar *Frame
|
|
titleLabel *Label
|
|
content *Frame
|
|
|
|
// Window manager controls.
|
|
dragging bool
|
|
startDragAt render.Point // cursor position when drag began
|
|
dragOrigPoint render.Point // original position of window at drag start
|
|
focused bool
|
|
managed bool // window is managed by Supervisor
|
|
}
|
|
|
|
// NewWindow creates a new window.
|
|
func NewWindow(title string) *Window {
|
|
w := &Window{
|
|
Title: title,
|
|
body: NewFrame("body:" + title),
|
|
|
|
// Default title bar colors.
|
|
ActiveTitleBackground: render.Blue,
|
|
ActiveTitleForeground: render.White,
|
|
InactiveTitleBackground: render.Grey,
|
|
InactiveTitleForeground: render.Black,
|
|
}
|
|
w.IDFunc(func() string {
|
|
return fmt.Sprintf("Window<%s %+v>",
|
|
w.Title, w.focused,
|
|
)
|
|
})
|
|
|
|
w.body.Configure(Config{
|
|
Background: render.Grey,
|
|
BorderSize: 2,
|
|
BorderStyle: BorderRaised,
|
|
})
|
|
|
|
// Title bar widget.
|
|
titleBar, titleLabel := w.setupTitleBar()
|
|
w.body.Pack(titleBar, Pack{
|
|
Side: N,
|
|
Fill: true,
|
|
})
|
|
w.titleBar = titleBar
|
|
w.titleLabel = titleLabel
|
|
|
|
// Window content frame.
|
|
content := NewFrame("content:" + title)
|
|
content.Configure(Config{
|
|
Background: render.Grey,
|
|
})
|
|
w.body.Pack(content, Pack{
|
|
Side: N,
|
|
Fill: true,
|
|
})
|
|
w.content = content
|
|
|
|
// Set up parent/child relationships
|
|
w.body.SetParent(w)
|
|
|
|
return w
|
|
}
|
|
|
|
// setupTitlebar creates the title bar frame of the window.
|
|
func (w *Window) setupTitleBar() (*Frame, *Label) {
|
|
frame := NewFrame("Titlebar for Windows: " + w.Title)
|
|
frame.Configure(Config{
|
|
Background: w.ActiveTitleBackground,
|
|
})
|
|
|
|
label := NewLabel(Label{
|
|
TextVariable: &w.Title,
|
|
Font: render.Text{
|
|
Color: w.ActiveTitleForeground,
|
|
Size: 10,
|
|
Stroke: w.ActiveTitleBackground.Darken(40),
|
|
Padding: 2,
|
|
},
|
|
})
|
|
frame.Pack(label, Pack{
|
|
Side: W,
|
|
})
|
|
|
|
return frame, label
|
|
}
|
|
|
|
// Supervise enables the window to be dragged around by its title bar by
|
|
// adding its relevant event hooks to your Supervisor.
|
|
func (w *Window) Supervise(s *Supervisor) {
|
|
// Add a click handler to the title bar to enable dragging.
|
|
w.titleBar.Handle(MouseDown, func(ed EventData) error {
|
|
w.startDragAt = ed.Point
|
|
w.dragOrigPoint = w.Point()
|
|
fmt.Printf("Clicked at %s window at %s!\n", ed.Point, w.dragOrigPoint)
|
|
|
|
s.DragStartWidget(w)
|
|
return nil
|
|
})
|
|
|
|
// Clicking anywhere in the window focuses the window.
|
|
w.Handle(MouseDown, func(ed EventData) error {
|
|
s.FocusWindow(w)
|
|
fmt.Printf("%s handles click event\n", w)
|
|
return nil
|
|
})
|
|
w.Handle(Click, func(ed EventData) error {
|
|
return nil
|
|
})
|
|
|
|
// Window as a whole receives DragMove events while being dragged.
|
|
w.Handle(DragMove, func(ed EventData) error {
|
|
// Get the delta of movement from where we began.
|
|
delta := w.startDragAt.Compare(ed.Point)
|
|
if delta != render.Origin {
|
|
fmt.Printf(" Dragged to: %s Delta: %s\n", ed.Point, delta)
|
|
moveTo := w.dragOrigPoint
|
|
moveTo.Add(delta)
|
|
w.MoveTo(moveTo)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
// Add the title bar to the supervisor.
|
|
s.Add(w.titleBar)
|
|
s.Add(w)
|
|
|
|
// Add the window to the focus list of the supervisor.
|
|
s.addWindow(w)
|
|
}
|
|
|
|
// Focused returns whether the window is focused.
|
|
func (w *Window) Focused() bool {
|
|
return w.focused
|
|
}
|
|
|
|
// SetFocus sets the window's focus value. Note: if you're using the Supervisor
|
|
// to manage the windows, do NOT call this method -- window focus is managed
|
|
// by the Supervisor.
|
|
func (w *Window) SetFocus(v bool) {
|
|
w.focused = v
|
|
|
|
// Update the title bar colors.
|
|
var (
|
|
bg = w.ActiveTitleBackground
|
|
fg = w.ActiveTitleForeground
|
|
)
|
|
if !w.focused {
|
|
bg = w.InactiveTitleBackground
|
|
fg = w.InactiveTitleForeground
|
|
}
|
|
w.titleBar.SetBackground(bg)
|
|
w.titleLabel.Font.Color = fg
|
|
w.titleLabel.Font.Stroke = bg.Darken(40)
|
|
}
|
|
|
|
// Children returns the window's child widgets.
|
|
func (w *Window) Children() []Widget {
|
|
return []Widget{
|
|
w.body,
|
|
}
|
|
}
|
|
|
|
// Pack a child widget into the window's main frame.
|
|
func (w *Window) Pack(child Widget, config ...Pack) {
|
|
w.content.Pack(child, config...)
|
|
}
|
|
|
|
// Place a child widget into the window's main frame.
|
|
func (w *Window) Place(child Widget, config Place) {
|
|
w.content.Place(child, config)
|
|
}
|
|
|
|
// TitleBar returns the title bar widget.
|
|
func (w *Window) TitleBar() *Frame {
|
|
return w.titleBar
|
|
}
|
|
|
|
// Configure the widget. Color and style changes are passed down to the inner
|
|
// content frame of the window.
|
|
func (w *Window) Configure(C Config) {
|
|
w.BaseWidget.Configure(C)
|
|
w.body.Configure(C)
|
|
|
|
// Don't pass dimensions down any further than the body.
|
|
C.Width = 0
|
|
C.Height = 0
|
|
w.content.Configure(C)
|
|
}
|
|
|
|
// ConfigureTitle configures the title bar widget.
|
|
func (w *Window) ConfigureTitle(C Config) {
|
|
w.titleBar.Configure(C)
|
|
}
|
|
|
|
// Compute the window.
|
|
func (w *Window) Compute(e render.Engine) {
|
|
w.body.Compute(e)
|
|
|
|
// Call the BaseWidget Compute in case we have subscribers.
|
|
w.BaseWidget.Compute(e)
|
|
}
|
|
|
|
// Present the window.
|
|
func (w *Window) Present(e render.Engine, P render.Point) {
|
|
w.body.Present(e, P)
|
|
|
|
// Call the BaseWidget Present in case we have subscribers.
|
|
w.BaseWidget.Present(e, P)
|
|
}
|