clui/window.go

310 lines
5.9 KiB
Go
Raw Normal View History

2015-09-21 20:54:39 -07:00
package clui
import (
xs "github.com/huandu/xstrings"
2015-10-16 10:27:43 -07:00
term "github.com/nsf/termbox-go"
2015-09-21 20:54:39 -07:00
)
2015-10-29 16:13:31 -07:00
// Window is an implemetation of View managed by Composer.
2015-10-16 10:27:43 -07:00
type Window struct {
BaseControl
2015-10-30 15:12:35 -07:00
buttons ViewButton
maximized bool
// maximization support
origWidth int
origHeight int
origX int
origY int
hidden bool
onClose func(Event) bool
onKeyDown func(Event) bool
2015-10-16 10:27:43 -07:00
}
2015-09-21 20:54:39 -07:00
func CreateWindow(x, y, w, h int, title string) *Window {
wnd := new(Window)
if w == AutoSize || w < 1 || w > 1000 {
2015-10-20 09:43:56 -07:00
w = 10
}
if h == AutoSize || h < 1 || h > 1000 {
w = 5
2015-10-20 09:43:56 -07:00
}
wnd.SetConstraints(w, h)
wnd.SetSize(w, h)
wnd.SetPos(x, y)
wnd.SetTitle(title)
wnd.buttons = ButtonClose | ButtonBottom | ButtonMaximize
wnd.children = make([]Control, 0)
wnd.SetPaddings(1, 1)
wnd.SetGaps(1, 0)
wnd.SetScale(1)
2015-09-21 20:54:39 -07:00
return wnd
2015-09-21 20:54:39 -07:00
}
func (wnd *Window) buttonCount() int {
cnt := 0
if wnd.buttons&ButtonClose == ButtonClose {
cnt += 1
2015-09-21 20:54:39 -07:00
}
if wnd.buttons&ButtonMaximize == ButtonMaximize {
cnt += 1
2015-10-19 16:54:26 -07:00
}
if wnd.buttons&ButtonBottom == ButtonBottom {
cnt += 1
2015-10-19 16:54:26 -07:00
}
2015-09-21 20:54:39 -07:00
return cnt
2015-09-21 20:54:39 -07:00
}
func (wnd *Window) drawFrame() {
PushAttributes()
defer PopAttributes()
2015-09-21 20:54:39 -07:00
var bs BorderStyle
if wnd.inactive {
bs = BorderThin
} else {
bs = BorderThick
2015-09-21 20:54:39 -07:00
}
DrawFrame(wnd.x, wnd.y, wnd.width, wnd.height, bs)
2015-09-21 20:54:39 -07:00
}
func (wnd *Window) drawTitle() {
PushAttributes()
defer PopAttributes()
2015-09-21 20:54:39 -07:00
btnCount := wnd.buttonCount()
maxw := wnd.width - 2 - btnCount
if btnCount > 0 {
maxw -= 2
2015-09-21 20:54:39 -07:00
}
fitTitle := wnd.title
rawText := UnColorizeText(fitTitle)
if xs.Len(rawText) > maxw {
fitTitle = SliceColorized(fitTitle, 0, maxw-3) + "..."
2015-09-21 20:54:39 -07:00
}
DrawText(wnd.x+1, wnd.y, fitTitle)
2015-09-21 20:54:39 -07:00
}
func (wnd *Window) drawButtons() {
btnCount := wnd.buttonCount()
if btnCount == 0 {
2015-09-21 20:54:39 -07:00
return
}
PushAttributes()
defer PopAttributes()
2015-09-21 20:54:39 -07:00
chars := []rune(SysObject(ObjViewButtons))
2015-10-16 10:27:43 -07:00
cMax, cBottom, cClose, cOpenB, cCloseB := chars[0], chars[1], chars[2], chars[3], chars[4]
2015-09-21 20:54:39 -07:00
pos := wnd.x + wnd.width - btnCount - 2
putCharUnsafe(pos, wnd.y, cOpenB)
pos += 1
if wnd.buttons&ButtonBottom == ButtonBottom {
putCharUnsafe(pos, wnd.y, cBottom)
pos += 1
2015-09-21 20:54:39 -07:00
}
if wnd.buttons&ButtonMaximize == ButtonMaximize {
putCharUnsafe(pos, wnd.y, cMax)
pos += 1
2015-09-21 20:54:39 -07:00
}
if wnd.buttons&ButtonClose == ButtonClose {
putCharUnsafe(pos, wnd.y, cClose)
pos += 1
2015-09-21 20:54:39 -07:00
}
putCharUnsafe(pos, wnd.y, cCloseB)
2015-09-21 20:54:39 -07:00
}
func (wnd *Window) Draw() {
PushAttributes()
defer PopAttributes()
2015-09-21 20:54:39 -07:00
fg, bg := RealColor(wnd.fg, ColorViewText), RealColor(wnd.bg, ColorViewBack)
SetBackColor(bg)
2015-09-21 20:54:39 -07:00
FillRect(wnd.x, wnd.y, wnd.width, wnd.height, ' ')
2015-09-21 20:54:39 -07:00
wnd.DrawChildren()
2015-09-21 20:54:39 -07:00
SetBackColor(bg)
SetTextColor(fg)
2015-09-21 20:54:39 -07:00
wnd.drawFrame()
wnd.drawTitle()
wnd.drawButtons()
2015-09-21 20:54:39 -07:00
}
func (c *Window) HitTest(x, y int) HitResult {
if x > c.x && x < c.x+c.width-1 &&
y > c.y && y < c.y+c.height-1 {
return HitInside
2015-09-21 20:54:39 -07:00
}
if x == c.x && y == c.y {
return HitTopLeft
2015-09-21 20:54:39 -07:00
}
if x == c.x+c.width-1 && y == c.y {
return HitTopRight
2015-09-21 20:54:39 -07:00
}
if x == c.x && y == c.y+c.height-1 {
return HitBottomLeft
2015-09-21 20:54:39 -07:00
}
if x == c.x+c.width-1 && y == c.y+c.height-1 {
return HitBottomRight
2015-09-21 20:54:39 -07:00
}
if x == c.x && y > c.y && y < c.y+c.height-1 {
return HitLeft
2015-09-21 20:54:39 -07:00
}
if x == c.x+c.width-1 && y > c.y && y < c.y+c.height-1 {
return HitRight
2015-09-21 20:54:39 -07:00
}
if y == c.y && x > c.x && x < c.x+c.width-1 {
btnCount := c.buttonCount()
if x < c.x+c.width-1-btnCount {
return HitTop
2015-10-16 10:27:43 -07:00
}
2015-09-21 20:54:39 -07:00
hitRes := []HitResult{HitTop, HitTop, HitTop}
pos := 0
2015-09-21 20:54:39 -07:00
if c.buttons&ButtonBottom == ButtonBottom {
hitRes[pos] = HitButtonBottom
pos += 1
2015-10-16 10:27:43 -07:00
}
if c.buttons&ButtonMaximize == ButtonMaximize {
hitRes[pos] = HitButtonMaximize
pos += 1
2015-10-16 10:27:43 -07:00
}
if c.buttons&ButtonClose == ButtonClose {
hitRes[pos] = HitButtonClose
pos += 1
2015-10-16 10:27:43 -07:00
}
2015-09-21 20:54:39 -07:00
return hitRes[x-(c.x+c.width-1-btnCount)]
2015-09-21 20:54:39 -07:00
}
if y == c.y+c.height-1 && x > c.x && x < c.x+c.width-1 {
return HitBottom
2015-10-16 10:27:43 -07:00
}
2015-09-21 20:54:39 -07:00
return HitOutside
2015-09-21 20:54:39 -07:00
}
func (c *Window) ProcessEvent(ev Event) bool {
switch ev.Type {
case EventMove:
c.PlaceChildren()
case EventResize:
c.ResizeChildren()
c.PlaceChildren()
case EventClose:
if c.onClose != nil {
if !c.onClose(ev) {
return false
}
2015-10-16 10:27:43 -07:00
}
return true
case EventKey:
if ev.Key == term.KeyTab {
aC := ActiveControl(c)
nC := NextControl(c, aC, true)
if nC != aC {
if aC != nil {
aC.SetActive(false)
aC.ProcessEvent(Event{Type: EventActivate, X: 0})
}
if nC != nil {
nC.SetActive(true)
nC.ProcessEvent(Event{Type: EventActivate, X: 1})
}
2015-10-16 10:27:43 -07:00
}
return true
} else {
if SendEventToChild(c, ev) {
return true
}
if c.onKeyDown != nil {
return c.onKeyDown(ev)
}
return false
2015-10-16 10:27:43 -07:00
}
default:
if ev.Type == EventMouse && ev.Key == term.MouseLeft {
DeactivateControls(c)
2015-10-16 10:27:43 -07:00
}
return SendEventToChild(c, ev)
2015-10-16 10:27:43 -07:00
}
2015-09-21 20:54:39 -07:00
return false
2015-10-21 09:38:43 -07:00
}
2015-10-21 17:04:57 -07:00
func (w *Window) OnClose(fn func(Event) bool) {
2015-10-21 17:04:57 -07:00
w.onClose = fn
}
2015-10-30 15:12:35 -07:00
func (w *Window) OnKeyDown(fn func(Event) bool) {
w.onKeyDown = fn
}
2015-10-30 15:12:35 -07:00
// SetMaximized opens the view to full screen or restores its
// previous size
func (w *Window) SetMaximized(maximize bool) {
if maximize == w.maximized {
return
}
if maximize {
w.origX, w.origY = w.Pos()
w.origWidth, w.origHeight = w.Size()
w.maximized = true
w.SetPos(0, 0)
width, height := ScreenSize()
2015-10-30 15:12:35 -07:00
w.SetSize(width, height)
} else {
w.maximized = false
w.SetPos(w.origX, w.origY)
w.SetSize(w.origWidth, w.origHeight)
}
w.ResizeChildren()
w.PlaceChildren()
2015-10-30 15:12:35 -07:00
}
// Maximized returns if the view is in full screen mode
func (w *Window) Maximized() bool {
return w.maximized
}
// Visible returns if the window must be drawn on the screen
func (w *Window) Visible() bool {
return !w.hidden
}
// SetVisible allows to temporarily remove the window from screen
// and show it later without reconstruction
func (w *Window) SetVisible(visible bool) {
if w.hidden == visible {
w.hidden = !visible
if w.hidden {
w.SetModal(false)
if composer().topWindow() == w {
composer().moveActiveWindowToBottom()
}
} else {
composer().activateWindow(w)
}
}
}