From 6d0128059bc3bdd53462a8ddcb16ec448d0796af Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Wed, 1 Jan 2020 22:40:51 -0500 Subject: [PATCH] Finish support for a fake cursor --- internal/display/bufwindow.go | 24 ++++++++-------- internal/display/infowindow.go | 16 +++++------ internal/display/statusline.go | 12 ++++---- internal/display/tabwindow.go | 6 ++-- internal/display/termwindow.go | 8 +++--- internal/display/uiwindow.go | 2 +- internal/screen/screen.go | 51 ++++++++++++++++++++++++++++++++++ internal/util/util.go | 2 +- 8 files changed, 86 insertions(+), 35 deletions(-) diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index 0b4391ae..66ed3be7 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -102,7 +102,7 @@ func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) func (w *BufWindow) Clear() { for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { - screen.Screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) + screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) } } } @@ -298,9 +298,9 @@ func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) { break } } - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) vloc.X++ - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s) vloc.X++ } @@ -309,21 +309,21 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL // Write the spaces before the line number if necessary for i := 0; i < maxLineNumLength-len(lineNum); i++ { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } // Write the actual line number for _, ch := range lineNum { if softwrapped { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) } else { - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle) } vloc.X++ } // Write the extra space - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle) vloc.X++ } @@ -342,7 +342,7 @@ func (w *BufWindow) showCursor(x, y int, main bool) { if main { screen.ShowCursor(x, y) } else { - screen.ShowFakeCursor(x, y) + screen.ShowFakeCursorMulti(x, y) } } } @@ -513,7 +513,7 @@ func (w *BufWindow) displayBuffer() { } } - screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style) + screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style) if showcursor { for _, c := range cursors { @@ -591,7 +591,7 @@ func (w *BufWindow) displayBuffer() { curStyle = style.Background(fg) } } - screen.Screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle) + screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle) } draw(' ', curStyle, true) @@ -617,7 +617,7 @@ func (w *BufWindow) displayStatusLine() { } else if w.Y+w.Height != infoY { w.drawStatus = true for x := w.X; x < w.X+w.Width; x++ { - screen.Screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true)) + screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true)) } } else { w.drawStatus = false @@ -637,7 +637,7 @@ func (w *BufWindow) displayScrollBar() { } barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height)) for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ { - screen.Screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true)) + screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true)) } } } diff --git a/internal/display/infowindow.go b/internal/display/infowindow.go index 45dc2c65..afc08f1d 100644 --- a/internal/display/infowindow.go +++ b/internal/display/infowindow.go @@ -74,7 +74,7 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { func (i *InfoWindow) Clear() { for x := 0; x < i.Width; x++ { - screen.Screen.SetContent(x, i.Y, ' ', nil, i.defStyle()) + screen.SetContent(x, i.Y, ' ', nil, i.defStyle()) } } @@ -111,7 +111,7 @@ func (i *InfoWindow) displayBuffer() { if j > 0 { c = ' ' } - screen.Screen.SetContent(vlocX, i.Y, c, nil, style) + screen.SetContent(vlocX, i.Y, c, nil, style) } vlocX++ } @@ -167,9 +167,9 @@ func (i *InfoWindow) displayKeyMenu() { for y := 0; y < len(keydisplay); y++ { for x := 0; x < i.Width; x++ { if x < len(keydisplay[y]) { - screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle()) + screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle()) } else { - screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle()) + screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle()) } } } @@ -194,7 +194,7 @@ func (i *InfoWindow) Display() { display := i.Msg for _, c := range display { - screen.Screen.SetContent(x, i.Y, c, nil, style) + screen.SetContent(x, i.Y, c, nil, style) x += runewidth.RuneWidth(c) } @@ -219,13 +219,13 @@ func (i *InfoWindow) Display() { style = style.Reverse(true) } for _, r := range s { - screen.Screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style) + screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style) x++ if x >= i.Width { return } } - screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle) + screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle) x++ if x >= i.Width { return @@ -233,7 +233,7 @@ func (i *InfoWindow) Display() { } for x < i.Width { - screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle) + screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle) x++ } } diff --git a/internal/display/statusline.go b/internal/display/statusline.go index 0f40355f..c139c8c6 100644 --- a/internal/display/statusline.go +++ b/internal/display/statusline.go @@ -118,13 +118,13 @@ func (s *StatusLine) Display() { style = style.Reverse(true) } for _, r := range sug { - screen.Screen.SetContent(x, y-keymenuOffset, r, nil, style) + screen.SetContent(x, y-keymenuOffset, r, nil, style) x++ if x >= s.win.Width { return } } - screen.Screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle) + screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle) x++ if x >= s.win.Width { return @@ -132,7 +132,7 @@ func (s *StatusLine) Display() { } for x < s.win.Width { - screen.Screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle) + screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle) x++ } return @@ -184,7 +184,7 @@ func (s *StatusLine) Display() { c = ' ' x++ } - screen.Screen.SetContent(winX+x, y, c, nil, statusLineStyle) + screen.SetContent(winX+x, y, c, nil, statusLineStyle) } } else if x >= s.win.Width-rightLen && x < rightLen+s.win.Width-rightLen { r, size := utf8.DecodeRune(rightText) @@ -196,10 +196,10 @@ func (s *StatusLine) Display() { c = ' ' x++ } - screen.Screen.SetContent(winX+x, y, c, nil, statusLineStyle) + screen.SetContent(winX+x, y, c, nil, statusLineStyle) } } else { - screen.Screen.SetContent(winX+x, y, ' ', nil, statusLineStyle) + screen.SetContent(winX+x, y, ' ', nil, statusLineStyle) } } } diff --git a/internal/display/tabwindow.go b/internal/display/tabwindow.go index 3d25a42c..d32ea6e1 100644 --- a/internal/display/tabwindow.go +++ b/internal/display/tabwindow.go @@ -91,13 +91,13 @@ func (w *TabWindow) Display() { c = ' ' } if x == w.width-1 && !done { - screen.Screen.SetContent(w.width-1, w.Y, '>', nil, config.DefStyle.Reverse(true)) + screen.SetContent(w.width-1, w.Y, '>', nil, config.DefStyle.Reverse(true)) x++ break } else if x == 0 && w.hscroll > 0 { - screen.Screen.SetContent(0, w.Y, '<', nil, config.DefStyle.Reverse(true)) + screen.SetContent(0, w.Y, '<', nil, config.DefStyle.Reverse(true)) } else if x >= 0 && x < w.width { - screen.Screen.SetContent(x, w.Y, c, nil, config.DefStyle.Reverse(true)) + screen.SetContent(x, w.Y, c, nil, config.DefStyle.Reverse(true)) } x++ } diff --git a/internal/display/termwindow.go b/internal/display/termwindow.go index 23ae1dbe..c7d82ecb 100644 --- a/internal/display/termwindow.go +++ b/internal/display/termwindow.go @@ -51,7 +51,7 @@ func (w *TermWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc { func (w *TermWindow) Clear() { for y := 0; y < w.Height; y++ { for x := 0; x < w.Width; x++ { - screen.Screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) + screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle) } } } @@ -88,7 +88,7 @@ func (w *TermWindow) Display() { st = st.Reverse(true) } - screen.Screen.SetContent(w.X+x, w.Y+y, c, nil, st) + screen.SetContent(w.X+x, w.Y+y, c, nil, st) } } if config.GetGlobalOption("statusline").(bool) { @@ -103,9 +103,9 @@ func (w *TermWindow) Display() { if x < textLen { r, size := utf8.DecodeRune(text) text = text[size:] - screen.Screen.SetContent(w.X+x, w.Y+w.Height, r, nil, statusLineStyle) + screen.SetContent(w.X+x, w.Y+w.Height, r, nil, statusLineStyle) } else { - screen.Screen.SetContent(w.X+x, w.Y+w.Height, ' ', nil, statusLineStyle) + screen.SetContent(w.X+x, w.Y+w.Height, ' ', nil, statusLineStyle) } } } diff --git a/internal/display/uiwindow.go b/internal/display/uiwindow.go index bb273ecc..ff59718f 100644 --- a/internal/display/uiwindow.go +++ b/internal/display/uiwindow.go @@ -28,7 +28,7 @@ func (w *UIWindow) drawNode(n *views.Node) { if c.IsLeaf() && c.Kind == views.STVert { if i != len(cs)-1 { for h := 0; h < c.H; h++ { - screen.Screen.SetContent(c.X+c.W, c.Y+h, '|', nil, dividerStyle.Reverse(true)) + screen.SetContent(c.X+c.W, c.Y+h, '|', nil, dividerStyle.Reverse(true)) } } } else { diff --git a/internal/screen/screen.go b/internal/screen/screen.go index cca9036d..f9ab1bd8 100644 --- a/internal/screen/screen.go +++ b/internal/screen/screen.go @@ -17,26 +17,66 @@ import ( // screen. TODO: maybe we should worry about polling and drawing at the // same time too. var Screen tcell.Screen + +// The lock is necessary since the screen is polled on a separate thread var lock sync.Mutex + +// DrawChan is a channel that will cause the screen to redraw when +// written to even if no event user event has occurred var DrawChan chan bool +// Lock locks the screen lock func Lock() { lock.Lock() } +// Unlock unlocks the screen lock func Unlock() { lock.Unlock() } +// Redraw schedules a redraw with the draw channel func Redraw() { DrawChan <- true } +type screenCell struct { + x, y int + r rune + combc []rune + style tcell.Style +} + +var lastCursor screenCell + +// ShowFakeCursor displays a cursor at the given position by modifying the +// style of the given column instead of actually using the terminal cursor +// This can be useful in certain terminals such as the windows console where +// modifying the cursor location is slow and frequent modifications cause flashing +// This keeps track of the most recent fake cursor location and resets it when +// a new fake cursor location is specified func ShowFakeCursor(x, y int) { + r, combc, style, _ := Screen.GetContent(x, y) + Screen.SetContent(lastCursor.x, lastCursor.y, lastCursor.r, lastCursor.combc, lastCursor.style) + Screen.SetContent(x, y, r, combc, config.DefStyle.Reverse(true)) + + lastCursor.x, lastCursor.y = x, y + lastCursor.r = r + lastCursor.combc = combc + lastCursor.style = style +} + +// ShowFakeCursorMulti is the same as ShowFakeCursor except it does not +// reset previous locations of the cursor +// Fake cursors are also necessary to display multiple cursors +func ShowFakeCursorMulti(x, y int) { r, _, _, _ := Screen.GetContent(x, y) Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true)) } +// ShowCursor puts the cursor at the given location using a fake cursor +// if enabled or using the terminal cursor otherwise +// By default only the windows console will use a fake cursor func ShowCursor(x, y int) { if util.FakeCursor { ShowFakeCursor(x, y) @@ -45,6 +85,17 @@ func ShowCursor(x, y int) { } } +// SetContent sets a cell at a point on the screen and makes sure that it is +// synced with the last cursor location +func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) { + Screen.SetContent(x, y, mainc, combc, style) + if util.FakeCursor && lastCursor.x == x && lastCursor.y == y { + lastCursor.r = mainc + lastCursor.style = style + lastCursor.combc = combc + } +} + // TempFini shuts the screen down temporarily func TempFini() bool { screenWasNil := Screen == nil diff --git a/internal/util/util.go b/internal/util/util.go index 9bc3f7e1..c3624bab 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -33,7 +33,7 @@ var ( Debug = "ON" // FakeCursor is used to disable the terminal cursor and have micro // draw its own (enabled for windows consoles where the cursor is slow) - FakeCursor = true + FakeCursor = false ) func init() { -- 2.44.0