mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-26 13:49:01 +08:00
687 lines
16 KiB
Go
687 lines
16 KiB
Go
package clui
|
|
|
|
import (
|
|
"github.com/VladimirMarkelov/termbox-go"
|
|
"log"
|
|
"os"
|
|
)
|
|
|
|
// An service object that manages Windows and console, processes events, and provides service methods
|
|
// One application must have only one object of this type
|
|
type Composer struct {
|
|
// list of visible Windows
|
|
windows map[WinId]Window
|
|
// console width and height
|
|
width, height int
|
|
// console canvas
|
|
screen *FrameBuffer
|
|
// Window draw order. The last Window is active Window
|
|
windowOrder []WinId
|
|
// ID assigned to the last created Window
|
|
lastWindowId WinId
|
|
// a channel to communicate with Windows(e.g, Windows send redraw event to this channel)
|
|
channel chan InternalEvent
|
|
// What kind of dragging is active: nothing is dragged, something is being moved, something is being resized
|
|
dragging DragAction
|
|
// Window ID that is currently dragged. NullWindow if no Window is dragged
|
|
dragObject WinId
|
|
// Console position where mouse button was pressed and drag action started
|
|
dragStartX, dragStartY int
|
|
// which part of dragged Window is active, it determines whether Window is resizing or dragging and what size is changing
|
|
dragHit HitResult
|
|
|
|
// current color scheme
|
|
themeManager *ThemeManager
|
|
|
|
// multi key sequences support. The flag below are true if the last keyboard combination was Ctrl+S or Ctrl+W respectively
|
|
ctrlSpressed bool
|
|
ctrlWpressed bool
|
|
// last pressed key - to make repeatable actions simpler, e.g, at first one presses Ctrl+S and then just repeatedly presses arrow lest to resize Window
|
|
lastKey termbox.Key
|
|
|
|
//debug
|
|
logger *log.Logger
|
|
}
|
|
|
|
func (c *Composer) initBuffer(w, h int) *FrameBuffer {
|
|
bf := NewFrameBuffer(w, h)
|
|
bf.Clear(ColorBlack)
|
|
return bf
|
|
}
|
|
|
|
// Initialize library and starts console management
|
|
func (c *Composer) Init() {
|
|
err := termbox.Init()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
termbox.HideCursor()
|
|
|
|
file, _ := os.OpenFile("debug.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
c.logger = log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
|
|
c.logger.Printf("----------------------------------")
|
|
|
|
c.lastWindowId = 0
|
|
c.channel = make(chan InternalEvent)
|
|
|
|
c.windows = make(map[WinId]Window)
|
|
c.windowOrder = make([]WinId, 0)
|
|
|
|
c.width, c.height = termbox.Size()
|
|
c.screen = c.initBuffer(c.width, c.height)
|
|
|
|
c.themeManager = NewThemeManager()
|
|
|
|
c.ctrlSpressed = false
|
|
c.ctrlWpressed = false
|
|
|
|
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
|
|
|
|
c.redrawAll()
|
|
}
|
|
|
|
func (c *Composer) redrawAll() {
|
|
for j := 0; j < c.height; j++ {
|
|
for i := 0; i < c.width; i++ {
|
|
sym, ok := c.screen.GetSymbol(i, j)
|
|
if ok {
|
|
termbox.SetCell(i, j, sym.ch, termbox.Attribute(sym.fg), termbox.Attribute(sym.bg))
|
|
}
|
|
}
|
|
}
|
|
|
|
termbox.Flush()
|
|
}
|
|
|
|
// Repaints all Windows on the screen. Now the method is not efficient: at first clears a console and then draws all Windows starting from the bottom
|
|
func (c *Composer) RefreshScreen() {
|
|
// TODO: make more intelligent (for each Cell ask starting from the top most if it is its Cell and write it)
|
|
for yy := 0; yy < c.height; yy++ {
|
|
for xx := 0; xx < c.width; xx++ {
|
|
c.screen.rawRuneOut(xx, yy, ' ', ColorWhite, ColorBlack)
|
|
}
|
|
}
|
|
|
|
for _, winId := range c.windowOrder {
|
|
wnd, ok := c.windows[winId]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
wnd.Redraw()
|
|
|
|
posx, posy := wnd.GetPos()
|
|
w, h := wnd.GetSize()
|
|
for y := posy; y < posy+h; y++ {
|
|
for x := posx; x < posx+w; x++ {
|
|
if x < c.width && y < c.height {
|
|
sym, ok := wnd.GetScreenSymbol(x, y)
|
|
if ok {
|
|
c.screen.rawRuneOut(x, y, sym.ch, sym.fg, sym.bg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
c.redrawAll()
|
|
}
|
|
|
|
func (c *Composer) makeTopViewActive() {
|
|
for i := 0; i < len(c.windowOrder)-1; i++ {
|
|
window, ok := c.windows[c.windowOrder[i]]
|
|
if ok {
|
|
window.SetActive(false)
|
|
}
|
|
}
|
|
|
|
window, ok := c.windows[c.windowOrder[len(c.windowOrder)-1]]
|
|
if ok {
|
|
window.SetActive(true)
|
|
}
|
|
}
|
|
|
|
func (c *Composer) CreateWindow(posX, posY, width, height int, title string, props Props) Window {
|
|
c.lastWindowId++
|
|
id := c.lastWindowId
|
|
view := NewView(c, id, posX, posY, width, height, title)
|
|
view.SetBorderIcons(IconClose | IconBottom)
|
|
|
|
c.windows[id] = view
|
|
view.Redraw()
|
|
c.windowOrder = append(c.windowOrder, id)
|
|
|
|
c.makeTopViewActive()
|
|
|
|
c.RefreshScreen()
|
|
|
|
return view
|
|
}
|
|
|
|
func (c *Composer) DestroyControl(parent Window, control Control) {
|
|
parent.RemoveControl(control)
|
|
}
|
|
|
|
func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (WinId, HitResult) {
|
|
if len(c.windowOrder) == 0 {
|
|
return NullWindow, HitOutside
|
|
}
|
|
|
|
for i := len(c.windowOrder) - 1; i >= 0; i-- {
|
|
window, ok := c.windows[c.windowOrder[i]]
|
|
if ok {
|
|
hit := window.HitTest(screenX, screenY)
|
|
if hit != HitOutside {
|
|
return c.windowOrder[i], hit
|
|
}
|
|
}
|
|
}
|
|
|
|
return NullWindow, HitOutside
|
|
}
|
|
|
|
func (c *Composer) getWindow(id WinId) Window {
|
|
wnd, ok := c.windows[id]
|
|
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
return wnd
|
|
}
|
|
|
|
func (c *Composer) getTopWindow() WinId {
|
|
if len(c.windowOrder) == 0 {
|
|
return NullWindow
|
|
}
|
|
|
|
return c.windowOrder[len(c.windowOrder)-1]
|
|
}
|
|
|
|
func (c *Composer) makeWindowTop(wid WinId) {
|
|
for i := 0; i < len(c.windowOrder); i++ {
|
|
if c.windowOrder[i] == wid {
|
|
c.windowOrder = append(c.windowOrder[:i], c.windowOrder[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
|
|
c.windowOrder = append(c.windowOrder, wid)
|
|
c.makeTopViewActive()
|
|
}
|
|
|
|
func (c *Composer) moveActiveWindowToBottom() bool {
|
|
if len(c.windowOrder) < 2 {
|
|
return false
|
|
}
|
|
|
|
event := Event{Type: EventActivate, X: 0} // send deactivated
|
|
c.sendEventToActiveView(event)
|
|
|
|
last := c.windowOrder[len(c.windowOrder)-1]
|
|
|
|
for i := len(c.windowOrder) - 1; i > 0; i-- {
|
|
c.windowOrder[i] = c.windowOrder[i-1]
|
|
}
|
|
|
|
c.windowOrder[0] = last
|
|
c.makeTopViewActive()
|
|
|
|
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
|
|
return true
|
|
}
|
|
|
|
func (c *Composer) termboxEventToLocal(ev termbox.Event) Event {
|
|
e := Event{Type: EventType(ev.Type), Ch: ev.Ch, Key: ev.Key, Err: ev.Err, X: ev.MouseX, Y: ev.MouseY, Mod: ev.Mod}
|
|
return e
|
|
}
|
|
|
|
func (c *Composer) sendEventToActiveView(ev Event) bool {
|
|
winid := c.getTopWindow()
|
|
if winid != NullWindow {
|
|
window := c.getWindow(winid)
|
|
if window != nil {
|
|
return window.ProcessEvent(ev)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Composer) resizeTopWindow(ev termbox.Event) bool {
|
|
if len(c.windowOrder) > 0 {
|
|
if ev.Mod&termbox.ModControl != 0 {
|
|
window := c.getWindow(c.getTopWindow())
|
|
if window != nil {
|
|
w, h := window.GetSize()
|
|
w1, h1 := w, h
|
|
minW, minH := window.GetConstraints()
|
|
if ev.Key == termbox.KeyArrowUp && (minH == -1 || minH < h) {
|
|
h--
|
|
} else if ev.Key == termbox.KeyArrowLeft && (minW < w || minW == -1) {
|
|
w--
|
|
}
|
|
|
|
if w1 != w || h1 != h {
|
|
window.SetSize(w, h)
|
|
event := Event{Type: EventResize, X: w, Y: h}
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (c *Composer) moveTopWindow(ev termbox.Event) bool {
|
|
if len(c.windowOrder) > 0 {
|
|
window := c.getWindow(c.getTopWindow())
|
|
if window != nil {
|
|
x, y := window.GetPos()
|
|
w, h := window.GetSize()
|
|
x1, y1 := x, y
|
|
cx, cy := termbox.Size()
|
|
if ev.Key == termbox.KeyArrowUp && y > 0 {
|
|
y--
|
|
} else if ev.Key == termbox.KeyArrowDown && y+h < cy {
|
|
y++
|
|
} else if ev.Key == termbox.KeyArrowLeft && x > 0 {
|
|
x--
|
|
} else if ev.Key == termbox.KeyArrowRight && x+w < cx {
|
|
x++
|
|
}
|
|
|
|
if x1 != x || y1 != y {
|
|
window.SetPos(x, y)
|
|
event := Event{Type: EventMove, X: x, Y: y}
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (c *Composer) isDeadKey(ev termbox.Event) bool {
|
|
if ev.Key == termbox.KeyCtrlS {
|
|
c.ctrlSpressed = true
|
|
c.lastKey = termbox.KeyEsc
|
|
return true
|
|
}
|
|
if ev.Key == termbox.KeyCtrlW {
|
|
c.ctrlWpressed = true
|
|
c.lastKey = termbox.KeyEsc
|
|
return true
|
|
}
|
|
|
|
c.ctrlSpressed = false
|
|
c.ctrlWpressed = false
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Composer) processKeySeq(ev termbox.Event) bool {
|
|
if !c.ctrlSpressed && !c.ctrlWpressed {
|
|
return false
|
|
}
|
|
|
|
if c.ctrlSpressed {
|
|
c.ctrlWpressed = false
|
|
|
|
if c.lastKey == termbox.KeyEsc {
|
|
c.lastKey = ev.Key
|
|
} else if c.lastKey != ev.Key {
|
|
c.ctrlSpressed = false
|
|
return false
|
|
}
|
|
|
|
switch ev.Key {
|
|
case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
|
|
evCopy := ev
|
|
evCopy.Mod = termbox.ModControl
|
|
c.resizeTopWindow(evCopy)
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
if c.ctrlWpressed {
|
|
c.ctrlSpressed = false
|
|
|
|
if c.lastKey == termbox.KeyEsc {
|
|
c.lastKey = ev.Key
|
|
} else if c.lastKey != ev.Key {
|
|
c.ctrlWpressed = false
|
|
return false
|
|
}
|
|
|
|
switch ev.Key {
|
|
case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
|
|
evCopy := ev
|
|
evCopy.Mod = termbox.ModAlt
|
|
c.moveTopWindow(evCopy)
|
|
return true
|
|
case termbox.KeyCtrlH:
|
|
if len(c.windowOrder) > 1 && ev.Mod&termbox.ModControl != 0 {
|
|
c.moveActiveWindowToBottom()
|
|
}
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
c.ctrlSpressed = false
|
|
c.ctrlWpressed = false
|
|
return true
|
|
}
|
|
|
|
func (c *Composer) processKey(ev termbox.Event) bool {
|
|
processed := false
|
|
|
|
if c.processKeySeq(ev) {
|
|
return false
|
|
}
|
|
if c.isDeadKey(ev) {
|
|
return false
|
|
}
|
|
|
|
switch ev.Key {
|
|
case termbox.KeyCtrlQ:
|
|
return true
|
|
case termbox.KeyArrowUp, termbox.KeyArrowDown, termbox.KeyArrowLeft, termbox.KeyArrowRight:
|
|
if ev.Mod&termbox.ModControl != 0 {
|
|
processed = processed || c.resizeTopWindow(ev)
|
|
}
|
|
|
|
if ev.Mod&termbox.ModAlt != 0 {
|
|
processed = processed || c.moveTopWindow(ev)
|
|
}
|
|
|
|
if !processed {
|
|
if c.sendEventToActiveView(c.termboxEventToLocal(ev)) {
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
case termbox.KeyEnd:
|
|
if len(c.windowOrder) > 1 && ev.Mod&termbox.ModControl != 0 {
|
|
processed = c.moveActiveWindowToBottom()
|
|
}
|
|
if !processed {
|
|
if c.sendEventToActiveView(c.termboxEventToLocal(ev)) {
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
default:
|
|
if c.sendEventToActiveView(c.termboxEventToLocal(ev)) {
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Composer) processMouseClick(ev termbox.Event) {
|
|
winId, hit := c.checkWindowUnderMouse(ev.MouseX, ev.MouseY)
|
|
if c.getTopWindow() != winId {
|
|
event := Event{Type: EventActivate, X: 0} // send 'deactivated'
|
|
c.sendEventToActiveView(event)
|
|
c.makeWindowTop(winId)
|
|
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
} else if hit == HitInside {
|
|
// c.logger.Printf("Clicked inside window %v", int(winId))
|
|
c.sendEventToActiveView(c.termboxEventToLocal(ev))
|
|
c.RefreshScreen()
|
|
} else if hit == HitButtonClose {
|
|
if len(c.windowOrder) > 1 {
|
|
event := Event{Type: EventClose}
|
|
c.sendEventToActiveView(event)
|
|
|
|
c.DestroyWindow(winId)
|
|
activate := c.windowOrder[len(c.windowOrder)-1]
|
|
c.makeWindowTop(activate)
|
|
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
|
c.sendEventToActiveView(event)
|
|
|
|
c.RefreshScreen()
|
|
}
|
|
} else if hit == HitButtonBottom {
|
|
c.moveActiveWindowToBottom()
|
|
}
|
|
}
|
|
|
|
func (c *Composer) stopDragging() {
|
|
c.dragging = DragNone
|
|
c.dragStartX, c.dragStartY = -1, -1
|
|
c.dragObject = NullWindow
|
|
}
|
|
|
|
func (c *Composer) processMouseRelease(ev termbox.Event) {
|
|
if c.dragging == DragNone {
|
|
c.sendEventToActiveView(c.termboxEventToLocal(ev))
|
|
}
|
|
c.stopDragging()
|
|
}
|
|
|
|
func (c *Composer) processMousePress(ev termbox.Event) {
|
|
winId, hit := c.checkWindowUnderMouse(ev.MouseX, ev.MouseY)
|
|
if c.getTopWindow() != winId {
|
|
event := Event{Type: EventActivate, X: 0} // send 'deactivated'
|
|
c.sendEventToActiveView(event)
|
|
c.makeWindowTop(winId)
|
|
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
} else {
|
|
if hit == HitHeader {
|
|
c.dragging = DragMove
|
|
c.dragObject = winId
|
|
c.dragStartX, c.dragStartY = ev.MouseX, ev.MouseY
|
|
} else if hit == HitLeftBorder || hit == HitRightBorder || hit == HitBottomBorder || hit == HitBottomLeft || hit == HitBottomRight || hit == HitTopLeft || hit == HitTopRight {
|
|
c.dragging = DragResize
|
|
c.dragObject = winId
|
|
c.dragStartX, c.dragStartY = ev.MouseX, ev.MouseY
|
|
c.dragHit = hit
|
|
} else if hit == HitInside {
|
|
c.sendEventToActiveView(c.termboxEventToLocal(ev))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Composer) moveAndResize(dx, dy int, wnd Window) bool {
|
|
if dx == 0 && dy == 0 {
|
|
return false
|
|
}
|
|
|
|
posX, posY := wnd.GetPos()
|
|
w, h := wnd.GetSize()
|
|
mnX, mnY := wnd.GetConstraints()
|
|
newW, newH := w, h
|
|
newX, newY := posX, posY
|
|
|
|
if c.dragHit == HitLeftBorder {
|
|
newX = posX + dx
|
|
newW = w - dx
|
|
} else if c.dragHit == HitRightBorder {
|
|
newW = w + dx
|
|
} else if c.dragHit == HitBottomBorder {
|
|
newH = h + dy
|
|
} else if c.dragHit == HitTopLeft {
|
|
newX, newY = posX+dx, posY+dy
|
|
newW, newH = w-dx, h-dy
|
|
} else if c.dragHit == HitBottomLeft {
|
|
newX = posX + dx
|
|
newW, newH = w-dx, h+dy
|
|
} else if c.dragHit == HitTopRight {
|
|
newY = posY + dy
|
|
newW, newH = w+dx, h-dy
|
|
} else if c.dragHit == HitBottomRight {
|
|
newW, newH = w+dx, h+dy
|
|
}
|
|
|
|
if (mnX != -1 && newW < mnX) || (mnY != -1 && newH < mnY) {
|
|
return false
|
|
}
|
|
|
|
wnd.SetPos(newX, newY)
|
|
wnd.SetSize(newW, newH)
|
|
|
|
if posX != newX || posY != newY {
|
|
event := Event{Type: EventMove, X: newX, Y: newY}
|
|
c.sendEventToActiveView(event)
|
|
}
|
|
if newW != w || newH != h {
|
|
event := Event{Type: EventResize, X: w, Y: h}
|
|
c.sendEventToActiveView(event)
|
|
}
|
|
|
|
wnd.Redraw()
|
|
|
|
return true
|
|
}
|
|
|
|
func (c *Composer) processMouseMove(ev termbox.Event) {
|
|
if c.dragging == DragNone {
|
|
c.sendEventToActiveView(c.termboxEventToLocal(ev))
|
|
c.RefreshScreen()
|
|
return
|
|
}
|
|
|
|
wnd, ok := c.windows[c.dragObject]
|
|
if !ok || c.dragObject == NullWindow {
|
|
c.stopDragging()
|
|
return
|
|
}
|
|
|
|
if c.dragging == DragMove {
|
|
dx, dy := ev.MouseX-c.dragStartX, ev.MouseY-c.dragStartY
|
|
if dx != 0 || dy != 0 {
|
|
c.dragStartY = ev.MouseY
|
|
c.dragStartX = ev.MouseX
|
|
posX, posY := wnd.GetPos()
|
|
wnd.SetPos(posX+dx, posY+dy)
|
|
event := Event{Type: EventMove, X: posX + dx, Y: posY + dy}
|
|
c.sendEventToActiveView(event)
|
|
c.RefreshScreen()
|
|
}
|
|
} else if c.dragging == DragResize && c.dragHit != HitOutside {
|
|
|
|
dx, dy := 0, 0
|
|
if c.dragHit == HitLeftBorder || c.dragHit == HitRightBorder {
|
|
dx = ev.MouseX - c.dragStartX
|
|
} else if c.dragHit == HitBottomBorder {
|
|
dy = ev.MouseY - c.dragStartY
|
|
} else if c.dragHit == HitTopLeft || c.dragHit == HitTopRight || c.dragHit == HitBottomLeft || c.dragHit == HitBottomRight {
|
|
dx = ev.MouseX - c.dragStartX
|
|
dy = ev.MouseY - c.dragStartY
|
|
}
|
|
|
|
if c.moveAndResize(dx, dy, wnd) {
|
|
c.dragStartX = ev.MouseX
|
|
c.dragStartY = ev.MouseY
|
|
|
|
c.RefreshScreen()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Asks a Composer to stops console management and quit application
|
|
func (c *Composer) Stop() {
|
|
ev := InternalEvent{act: EventQuit}
|
|
go c.SendEvent(ev)
|
|
}
|
|
|
|
// Main event loop
|
|
func (c *Composer) MainLoop() {
|
|
c.redrawAll()
|
|
|
|
eventQueue := make(chan termbox.Event)
|
|
go func() {
|
|
for {
|
|
eventQueue <- termbox.PollEvent()
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case ev := <-eventQueue:
|
|
switch ev.Type {
|
|
case termbox.EventMouseScroll:
|
|
if c.sendEventToActiveView(c.termboxEventToLocal(ev)) {
|
|
c.RefreshScreen()
|
|
}
|
|
case termbox.EventKey:
|
|
if c.processKey(ev) {
|
|
return
|
|
}
|
|
case termbox.EventMouse, termbox.EventMouseClick:
|
|
c.processMouseClick(ev)
|
|
case termbox.EventMouseRelease:
|
|
c.processMouseRelease(ev)
|
|
case termbox.EventMousePress:
|
|
c.processMousePress(ev)
|
|
case termbox.EventMouseMove:
|
|
c.processMouseMove(ev)
|
|
case termbox.EventError:
|
|
panic(ev.Err)
|
|
case termbox.EventResize:
|
|
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
|
|
c.width, c.height = termbox.Size()
|
|
c.screen = c.initBuffer(c.width, c.height)
|
|
c.RefreshScreen()
|
|
}
|
|
case cmd := <-c.channel:
|
|
if cmd.act == EventRedraw {
|
|
c.RefreshScreen()
|
|
} else if cmd.act == EventQuit {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send event to a Composer. Used by Windows to ask for repainting or for quitting the application
|
|
func (c *Composer) SendEvent(ev InternalEvent) {
|
|
c.channel <- ev
|
|
}
|
|
|
|
// Closes console management and makes a console cursor visible
|
|
func (c *Composer) Close() {
|
|
termbox.SetCursor(3, 3)
|
|
termbox.Close()
|
|
}
|
|
|
|
// Shows consolse cursor at given position. Setting cursor to -1,-1 hides cursor
|
|
func (c *Composer) SetCursorPos(x, y int) {
|
|
termbox.SetCursor(x, y)
|
|
}
|
|
|
|
// Returns thememanager to get current theme colors etc
|
|
func (c *Composer) GetThemeManager() *ThemeManager {
|
|
return c.themeManager
|
|
}
|
|
|
|
func (c *Composer) DestroyWindow(winId WinId) {
|
|
ev := Event{Type: EventClose}
|
|
c.sendEventToActiveView(ev)
|
|
|
|
newOrder := make([]WinId, 0)
|
|
for i := 0; i < len(c.windowOrder); i++ {
|
|
if c.windowOrder[i] != winId {
|
|
newOrder = append(newOrder, c.windowOrder[i])
|
|
}
|
|
}
|
|
c.windowOrder = newOrder
|
|
delete(c.windows, winId)
|
|
}
|