package clui import ( xs "github.com/huandu/xstrings" term "github.com/nsf/termbox-go" мКнст "./пакКонстанты" ) // Window is an implemetation of View managed by Composer. type Window struct { BaseControl buttons мКнст.ViewButton maximized bool // maximization support origWidth int origHeight int origX int origY int hidden bool immovable bool fixedSize bool border мКнст.BorderStyle onClose func(мКнст.Event) bool onScreenResize func(мКнст.Event) onKeyDown *keyDownCb } type keyDownCb struct { data interface{} fn func(evt мКнст.Event, data interface{}) bool } func CreateWindow(x, y, w, h int, title string) *Window { wnd := new(Window) wnd.BaseControl = NewBaseControl() if w == мКнст.AutoSize || w < 1 || w > 1000 { w = 10 } if h == мКнст.AutoSize || h < 1 || h > 1000 { w = 5 } 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) wnd.SetBorder(мКнст.BorderAuto) return wnd } func (wnd *Window) buttonCount() int { cnt := 0 if wnd.buttons& мКнст.ButtonClose == мКнст.ButtonClose { cnt++ } if wnd.buttons& мКнст.ButtonMaximize == мКнст.ButtonMaximize { cnt++ } if wnd.buttons& мКнст.ButtonBottom == мКнст.ButtonBottom { cnt++ } return cnt } func (wnd *Window) drawFrame() { PushAttributes() defer PopAttributes() var bs мКнст.BorderStyle if wnd.border == мКнст.BorderAuto { if wnd.inactive { bs = мКнст.BorderThin } else { bs = мКнст.BorderThick } } else if wnd.border == мКнст.BorderNone { } else { bs = wnd.border } DrawFrame(wnd.x, wnd.y, wnd.width, wnd.height, bs) } func (wnd *Window) drawTitle() { PushAttributes() defer PopAttributes() btnCount := wnd.buttonCount() maxw := wnd.width - 2 - btnCount if btnCount > 0 { maxw -= 2 } fitTitle := wnd.title rawText := UnColorizeText(fitTitle) if xs.Len(rawText) > maxw { fitTitle = SliceColorized(fitTitle, 0, maxw-3) + "..." } DrawText(wnd.x+1, wnd.y, fitTitle) } func (wnd *Window) drawButtons() { btnCount := wnd.buttonCount() if btnCount == 0 { return } PushAttributes() defer PopAttributes() chars := []rune(SysObject(мКнст.ObjViewButtons)) cMax, cBottom, cClose, cOpenB, cCloseB := chars[0], chars[1], chars[2], chars[3], chars[4] pos := wnd.x + wnd.width - btnCount - 2 putCharUnsafe(pos, wnd.y, cOpenB) pos++ if wnd.buttons& мКнст.ButtonBottom == мКнст.ButtonBottom { putCharUnsafe(pos, wnd.y, cBottom) pos++ } if wnd.buttons& мКнст.ButtonMaximize == мКнст.ButtonMaximize { putCharUnsafe(pos, wnd.y, cMax) pos++ } if wnd.buttons& мКнст.ButtonClose == мКнст.ButtonClose { putCharUnsafe(pos, wnd.y, cClose) pos++ } putCharUnsafe(pos, wnd.y, cCloseB) } //Draw -- func (wnd *Window) Draw() { WindowManager().BeginUpdate() defer WindowManager().EndUpdate() PushAttributes() defer PopAttributes() fg, bg := RealColor(wnd.fg, wnd.Style(), мКнст.ColorViewText), RealColor(wnd.bg, wnd.Style(), мКнст.ColorViewBack) SetBackColor(bg) FillRect(wnd.x, wnd.y, wnd.width, wnd.height, ' ') wnd.DrawChildren() SetBackColor(bg) SetTextColor(fg) wnd.drawFrame() wnd.drawTitle() wnd.drawButtons() } 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 } hResult := мКнст.HitOutside if x == c.x && y == c.y { hResult = мКнст.HitTopLeft } else if x == c.x+c.width-1 && y == c.y { hResult = мКнст.HitTopRight } else if x == c.x && y == c.y+c.height-1 { hResult = мКнст.HitBottomLeft } else if x == c.x+c.width-1 && y == c.y+c.height-1 { hResult = мКнст.HitBottomRight } else if x == c.x && y > c.y && y < c.y+c.height-1 { hResult = мКнст.HitLeft } else if x == c.x+c.width-1 && y > c.y && y < c.y+c.height-1 { hResult = мКнст.HitRight } else if y == c.y && x > c.x && x < c.x+c.width-1 { btnCount := c.buttonCount() if x < c.x+c.width-1-btnCount { hResult = мКнст.HitTop } else { hitRes := []мКнст.HitResult{мКнст.HitTop, мКнст.HitTop, мКнст.HitTop} pos := 0 if c.buttons& мКнст.ButtonBottom == мКнст.ButtonBottom { hitRes[pos] = мКнст.HitButtonBottom pos++ } if c.buttons& мКнст.ButtonMaximize == мКнст.ButtonMaximize { hitRes[pos] = мКнст.HitButtonMaximize pos++ } if c.buttons& мКнст.ButtonClose == мКнст.ButtonClose { hitRes[pos] = мКнст.HitButtonClose pos++ } hResult = hitRes[x-(c.x+c.width-1-btnCount)] } } else if y == c.y+c.height-1 && x > c.x && x < c.x+c.width-1 { hResult = мКнст.HitBottom } if hResult != мКнст.HitOutside { if c.immovable && hResult == мКнст.HitTop { hResult = мКнст.HitInside } if c.fixedSize && (hResult == мКнст.HitBottom || hResult == мКнст.HitLeft || hResult == мКнст.HitRight || hResult == мКнст.HitTopLeft || hResult == мКнст.HitTopRight || hResult == мКнст.HitBottomRight || hResult == мКнст.HitBottomLeft || hResult == мКнст.HitButtonMaximize) { hResult = мКнст.HitInside } } return hResult } 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 } } return true case мКнст.EventKey: if ev.Key == term.KeyTab || ev.Key == term.KeyArrowUp || ev.Key == term.KeyArrowDown { if SendEventToChild(c, ev) { return true } aC := ActiveControl(c) nC := NextControl(c, aC, ev.Key != term.KeyArrowUp) var clipped Control if aC != nil && aC.Clipped() { clipped = aC } else if nC != nil { clipped = ClippedParent(nC) } if clipped != nil { dir := 1 if ev.Key != term.KeyArrowUp { dir = -1 } clipped.ProcessEvent(мКнст.Event{Type: мКнст.EventActivateChild, Target: nC, X: dir}) } 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}) } } return true } else { if SendEventToChild(c, ev) { return true } if c.onKeyDown != nil { return c.onKeyDown.fn(ev, c.onKeyDown.data) } return false } default: if ev.Type == мКнст.EventMouse && ev.Key == term.MouseLeft { DeactivateControls(c) } return SendEventToChild(c, ev) } return false } // OnClose sets the callback that is called when the Window is about to destroy func (w *Window) OnClose(fn func(мКнст.Event) bool) { w.onClose = fn } // OnKeyDown sets the callback that is called when a user presses a key // while the Window is active func (w *Window) OnKeyDown(fn func(мКнст.Event, interface{}) bool, data interface{}) { if fn == nil { w.onKeyDown = nil } else { w.onKeyDown = &keyDownCb{data: data, fn: fn} } } // OnScreenResize sets the callback that is called when size of terminal changes func (w *Window) OnScreenResize(fn func(мКнст.Event)) { w.onScreenResize = fn } // Border returns the default window border func (w *Window) Border() мКнст.BorderStyle { return w.border } // SetBorder changes the default window border func (w *Window) SetBorder(border мКнст.BorderStyle) { w.border = border } // 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() w.SetSize(width, height) } else { w.maximized = false w.SetPos(w.origX, w.origY) w.SetSize(w.origWidth, w.origHeight) } w.ResizeChildren() w.PlaceChildren() } // 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 WindowManager().topWindow() == w { WindowManager().moveActiveWindowToBottom() } } else { WindowManager().activateWindow(w) } } } // Movable returns if the Window can be moved with mouse or keyboard func (w *Window) Movable() bool { return !w.immovable } // Sizable returns if size of the Window can be changed with mouse or keyboard func (w *Window) Sizable() bool { return !w.fixedSize } // SetMovable turns on and off ability to change Window position with mouse // or keyboard func (w *Window) SetMovable(movable bool) { w.immovable = !movable } // SetSizable turns on and off ability to change Window size with mouse // or keyboard func (w *Window) SetSizable(sizable bool) { w.fixedSize = !sizable } // TitleButtons returns a set of buttons shown in the Window title bar func (w *Window) TitleButtons() мКнст.ViewButton { return w.buttons } // SetTitleButtons sets the title bar buttons available for a user func (w *Window) SetTitleButtons(buttons мКнст.ViewButton) { w.buttons = buttons }