225 lines
4.3 KiB
Go
225 lines
4.3 KiB
Go
package cview
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
)
|
|
|
|
// WindowManager provides an area which windows may be added to.
|
|
type WindowManager struct {
|
|
*Box
|
|
|
|
windows []*Window
|
|
|
|
sync.RWMutex
|
|
}
|
|
|
|
// NewWindowManager returns a new window manager.
|
|
func NewWindowManager() *WindowManager {
|
|
return &WindowManager{
|
|
Box: NewBox(),
|
|
}
|
|
}
|
|
|
|
// Add adds a window to the manager.
|
|
func (wm *WindowManager) Add(w ...*Window) {
|
|
wm.Lock()
|
|
defer wm.Unlock()
|
|
|
|
for _, window := range w {
|
|
window.SetBorder(true)
|
|
}
|
|
|
|
wm.windows = append(wm.windows, w...)
|
|
}
|
|
|
|
// Clear removes all windows from the manager.
|
|
func (wm *WindowManager) Clear() {
|
|
wm.Lock()
|
|
defer wm.Unlock()
|
|
|
|
wm.windows = nil
|
|
}
|
|
|
|
// Focus is called when this primitive receives focus.
|
|
func (wm *WindowManager) Focus(delegate func(p Primitive)) {
|
|
wm.Lock()
|
|
defer wm.Unlock()
|
|
|
|
if len(wm.windows) == 0 {
|
|
return
|
|
}
|
|
|
|
wm.windows[len(wm.windows)-1].Focus(delegate)
|
|
}
|
|
|
|
// HasFocus returns whether or not this primitive has focus.
|
|
func (wm *WindowManager) HasFocus() bool {
|
|
wm.RLock()
|
|
defer wm.RUnlock()
|
|
|
|
for _, w := range wm.windows {
|
|
if w.HasFocus() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Draw draws this primitive onto the screen.
|
|
func (wm *WindowManager) Draw(screen tcell.Screen) {
|
|
if !wm.GetVisible() {
|
|
return
|
|
}
|
|
|
|
wm.RLock()
|
|
defer wm.RUnlock()
|
|
|
|
wm.Box.Draw(screen)
|
|
|
|
x, y, width, height := wm.GetInnerRect()
|
|
|
|
var hasFullScreen bool
|
|
for _, w := range wm.windows {
|
|
if !w.fullscreen || !w.GetVisible() {
|
|
continue
|
|
}
|
|
|
|
hasFullScreen = true
|
|
w.SetRect(x-1, y, width+2, height+1)
|
|
|
|
w.Draw(screen)
|
|
}
|
|
if hasFullScreen {
|
|
return
|
|
}
|
|
|
|
for _, w := range wm.windows {
|
|
if !w.GetVisible() {
|
|
continue
|
|
}
|
|
|
|
// Reposition out of bounds windows
|
|
margin := 3
|
|
wx, wy, ww, wh := w.GetRect()
|
|
ox, oy := wx, wy
|
|
if wx > x+width-margin {
|
|
wx = x + width - margin
|
|
}
|
|
if wx+ww < x+margin {
|
|
wx = x - ww + margin
|
|
}
|
|
if wy > y+height-margin {
|
|
wy = y + height - margin
|
|
}
|
|
if wy < y {
|
|
wy = y // No top margin
|
|
}
|
|
if wx != ox || wy != oy {
|
|
w.SetRect(wx, wy, ww, wh)
|
|
}
|
|
|
|
w.Draw(screen)
|
|
}
|
|
}
|
|
|
|
// MouseHandler returns the mouse handler for this primitive.
|
|
func (wm *WindowManager) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
|
return wm.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
|
if !wm.InRect(event.Position()) {
|
|
return false, nil
|
|
}
|
|
|
|
if action == MouseMove {
|
|
mouseX, mouseY := event.Position()
|
|
|
|
for _, w := range wm.windows {
|
|
if w.dragWX != -1 || w.dragWY != -1 {
|
|
offsetX := w.x - mouseX
|
|
offsetY := w.y - mouseY
|
|
|
|
w.x -= offsetX + w.dragWX
|
|
w.y -= offsetY + w.dragWY
|
|
|
|
w.updateInnerRect()
|
|
consumed = true
|
|
}
|
|
|
|
if w.dragX != 0 {
|
|
if w.dragX == -1 {
|
|
offsetX := w.x - mouseX
|
|
|
|
if w.width+offsetX >= Styles.WindowMinWidth {
|
|
w.x -= offsetX
|
|
w.width += offsetX
|
|
}
|
|
} else {
|
|
offsetX := mouseX - (w.x + w.width) + 1
|
|
|
|
if w.width+offsetX >= Styles.WindowMinWidth {
|
|
w.width += offsetX
|
|
}
|
|
}
|
|
|
|
w.updateInnerRect()
|
|
consumed = true
|
|
}
|
|
|
|
if w.dragY != 0 {
|
|
if w.dragY == -1 {
|
|
offsetY := mouseY - (w.y + w.height) + 1
|
|
|
|
if w.height+offsetY >= Styles.WindowMinHeight {
|
|
w.height += offsetY
|
|
}
|
|
} else {
|
|
offsetY := w.y - mouseY
|
|
|
|
if w.height+offsetY >= Styles.WindowMinHeight {
|
|
w.y -= offsetY
|
|
w.height += offsetY
|
|
}
|
|
}
|
|
|
|
w.updateInnerRect()
|
|
consumed = true
|
|
}
|
|
}
|
|
} else if action == MouseLeftUp {
|
|
for _, w := range wm.windows {
|
|
w.dragX, w.dragY = 0, 0
|
|
w.dragWX, w.dragWY = -1, -1
|
|
}
|
|
}
|
|
|
|
// Focus window on mousedown
|
|
var (
|
|
focusWindow *Window
|
|
focusWindowIndex int
|
|
)
|
|
for i := len(wm.windows) - 1; i >= 0; i-- {
|
|
if wm.windows[i].InRect(event.Position()) {
|
|
focusWindow = wm.windows[i]
|
|
focusWindowIndex = i
|
|
break
|
|
}
|
|
}
|
|
if focusWindow != nil {
|
|
if action == MouseLeftDown || action == MouseMiddleDown || action == MouseRightDown {
|
|
for _, w := range wm.windows {
|
|
if w != focusWindow {
|
|
w.Blur()
|
|
}
|
|
}
|
|
|
|
wm.windows = append(append(wm.windows[:focusWindowIndex], wm.windows[focusWindowIndex+1:]...), focusWindow)
|
|
}
|
|
|
|
return focusWindow.MouseHandler()(action, event, setFocus)
|
|
}
|
|
|
|
return consumed, nil
|
|
})
|
|
}
|