mirror of
https://github.com/gdamore/tcell.git
synced 2025-04-26 13:48:53 +08:00
fixes #666 cursor color
This adds a new optional parameter to screen.SetCursorStyle, which is a color. The cursors demo is enhanced to show this. This ability is supported on screen types, provided the underlying terminal supports the capability.
This commit is contained in:
parent
652ba11803
commit
887cf2766e
@ -15,7 +15,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// beep makes a beep every second until you press ESC
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -52,6 +51,7 @@ func main() {
|
|||||||
style := tcell.StyleDefault
|
style := tcell.StyleDefault
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
s.Show()
|
||||||
ev := s.PollEvent()
|
ev := s.PollEvent()
|
||||||
switch ev := ev.(type) {
|
switch ev := ev.(type) {
|
||||||
case *tcell.EventKey:
|
case *tcell.EventKey:
|
||||||
@ -60,27 +60,26 @@ func main() {
|
|||||||
switch ev.Rune() {
|
switch ev.Rune() {
|
||||||
case '0':
|
case '0':
|
||||||
s.SetContent(2, 2, '0', nil, style)
|
s.SetContent(2, 2, '0', nil, style)
|
||||||
s.SetCursorStyle(tcell.CursorStyleDefault)
|
s.SetCursorStyle(tcell.CursorStyleDefault, tcell.ColorReset)
|
||||||
case '1':
|
case '1':
|
||||||
s.SetContent(2, 2, '1', nil, style)
|
s.SetContent(2, 2, '1', nil, style)
|
||||||
s.SetCursorStyle(tcell.CursorStyleBlinkingBlock)
|
s.SetCursorStyle(tcell.CursorStyleBlinkingBlock, tcell.ColorGreen)
|
||||||
case '2':
|
case '2':
|
||||||
s.SetCell(2, 2, tcell.StyleDefault, '2')
|
s.SetCell(2, 2, tcell.StyleDefault, '2')
|
||||||
s.SetCursorStyle(tcell.CursorStyleSteadyBlock)
|
s.SetCursorStyle(tcell.CursorStyleSteadyBlock, tcell.ColorBlue)
|
||||||
case '3':
|
case '3':
|
||||||
s.SetCell(2, 2, tcell.StyleDefault, '3')
|
s.SetCell(2, 2, tcell.StyleDefault, '3')
|
||||||
s.SetCursorStyle(tcell.CursorStyleBlinkingUnderline)
|
s.SetCursorStyle(tcell.CursorStyleBlinkingUnderline, tcell.ColorRed)
|
||||||
case '4':
|
case '4':
|
||||||
s.SetCell(2, 2, tcell.StyleDefault, '4')
|
s.SetCell(2, 2, tcell.StyleDefault, '4')
|
||||||
s.SetCursorStyle(tcell.CursorStyleSteadyUnderline)
|
s.SetCursorStyle(tcell.CursorStyleSteadyUnderline, tcell.ColorOrange)
|
||||||
case '5':
|
case '5':
|
||||||
s.SetCell(2, 2, tcell.StyleDefault, '5')
|
s.SetCell(2, 2, tcell.StyleDefault, '5')
|
||||||
s.SetCursorStyle(tcell.CursorStyleBlinkingBar)
|
s.SetCursorStyle(tcell.CursorStyleBlinkingBar, tcell.ColorYellow)
|
||||||
case '6':
|
case '6':
|
||||||
s.SetCell(2, 2, tcell.StyleDefault, '6')
|
s.SetCell(2, 2, tcell.StyleDefault, '6')
|
||||||
s.SetCursorStyle(tcell.CursorStyleSteadyBar)
|
s.SetCursorStyle(tcell.CursorStyleSteadyBar, tcell.ColorPink)
|
||||||
}
|
}
|
||||||
s.Show()
|
|
||||||
|
|
||||||
case tcell.KeyEscape, tcell.KeyEnter, tcell.KeyCtrlC:
|
case tcell.KeyEscape, tcell.KeyEnter, tcell.KeyCtrlC:
|
||||||
close(quit)
|
close(quit)
|
||||||
|
@ -49,6 +49,7 @@ type cScreen struct {
|
|||||||
oscreen consoleInfo
|
oscreen consoleInfo
|
||||||
ocursor cursorInfo
|
ocursor cursorInfo
|
||||||
cursorStyle CursorStyle
|
cursorStyle CursorStyle
|
||||||
|
cursorColor Color
|
||||||
oimode uint32
|
oimode uint32
|
||||||
oomode uint32
|
oomode uint32
|
||||||
cells CellBuffer
|
cells CellBuffer
|
||||||
@ -173,6 +174,8 @@ const (
|
|||||||
vtUnderColorReset = "\x1b[59m"
|
vtUnderColorReset = "\x1b[59m"
|
||||||
vtEnterUrl = "\x1b]8;%s;%s\x1b\\" // NB arg 1 is id, arg 2 is url
|
vtEnterUrl = "\x1b]8;%s;%s\x1b\\" // NB arg 1 is id, arg 2 is url
|
||||||
vtExitUrl = "\x1b]8;;\x1b\\"
|
vtExitUrl = "\x1b]8;;\x1b\\"
|
||||||
|
vtCursorColorRGB = "\x1b]12;#%02x%02x%02x\007"
|
||||||
|
vtCursorColorReset = "\x1b]112\007"
|
||||||
)
|
)
|
||||||
|
|
||||||
var vtCursorStyles = map[CursorStyle]string{
|
var vtCursorStyles = map[CursorStyle]string{
|
||||||
@ -344,6 +347,7 @@ func (s *cScreen) disengage() {
|
|||||||
|
|
||||||
if s.vten {
|
if s.vten {
|
||||||
s.emitVtString(vtCursorStyles[CursorStyleDefault])
|
s.emitVtString(vtCursorStyles[CursorStyleDefault])
|
||||||
|
s.emitVtString(vtCursorColorReset)
|
||||||
s.emitVtString(vtEnableAm)
|
s.emitVtString(vtEnableAm)
|
||||||
if !s.disableAlt {
|
if !s.disableAlt {
|
||||||
s.emitVtString(vtExitCA)
|
s.emitVtString(vtExitCA)
|
||||||
@ -435,6 +439,12 @@ func (s *cScreen) showCursor() {
|
|||||||
if s.vten {
|
if s.vten {
|
||||||
s.emitVtString(vtShowCursor)
|
s.emitVtString(vtShowCursor)
|
||||||
s.emitVtString(vtCursorStyles[s.cursorStyle])
|
s.emitVtString(vtCursorStyles[s.cursorStyle])
|
||||||
|
if s.cursorColor == ColorReset {
|
||||||
|
s.emitVtString(vtCursorColorReset)
|
||||||
|
} else if s.cursorColor.Valid() {
|
||||||
|
r, g, b := s.cursorColor.RGB()
|
||||||
|
s.emitVtString(fmt.Sprintf(vtCursorColorRGB, r, g, b))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
|
s.setCursorInfo(&cursorInfo{size: 100, visible: 1})
|
||||||
}
|
}
|
||||||
@ -458,11 +468,12 @@ func (s *cScreen) ShowCursor(x, y int) {
|
|||||||
s.Unlock()
|
s.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cScreen) SetCursorStyle(cs CursorStyle) {
|
func (s *cScreen) SetCursor(cs CursorStyle, cc Color) {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
if !s.fini {
|
if !s.fini {
|
||||||
if _, ok := vtCursorStyles[cs]; ok {
|
if _, ok := vtCursorStyles[cs]; ok {
|
||||||
s.cursorStyle = cs
|
s.cursorStyle = cs
|
||||||
|
s.cursorColor = cc
|
||||||
s.doCursor()
|
s.doCursor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1100,7 +1111,6 @@ func (s *cScreen) setCursorInfo(info *cursorInfo) {
|
|||||||
_, _, _ = procSetConsoleCursorInfo.Call(
|
_, _, _ = procSetConsoleCursorInfo.Call(
|
||||||
uintptr(s.out),
|
uintptr(s.out),
|
||||||
uintptr(unsafe.Pointer(info)))
|
uintptr(unsafe.Pointer(info)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *cScreen) setCursorPos(x, y int, vtEnable bool) {
|
func (s *cScreen) setCursorPos(x, y int, vtEnable bool) {
|
||||||
|
17
screen.go
17
screen.go
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 The TCell Authors
|
// Copyright 2024 The TCell Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use file except in compliance with the License.
|
// you may not use file except in compliance with the License.
|
||||||
@ -79,8 +79,9 @@ type Screen interface {
|
|||||||
|
|
||||||
// SetCursorStyle is used to set the cursor style. If the style
|
// SetCursorStyle is used to set the cursor style. If the style
|
||||||
// is not supported (or cursor styles are not supported at all),
|
// is not supported (or cursor styles are not supported at all),
|
||||||
// then this will have no effect.
|
// then this will have no effect. Color will be changed if supplied,
|
||||||
SetCursorStyle(CursorStyle)
|
// and the terminal supports doing so.
|
||||||
|
SetCursorStyle(CursorStyle, ...Color)
|
||||||
|
|
||||||
// Size returns the screen size as width, height. This changes in
|
// Size returns the screen size as width, height. This changes in
|
||||||
// response to a call to Clear or Flush.
|
// response to a call to Clear or Flush.
|
||||||
@ -312,7 +313,7 @@ type screenImpl interface {
|
|||||||
SetStyle(style Style)
|
SetStyle(style Style)
|
||||||
ShowCursor(x int, y int)
|
ShowCursor(x int, y int)
|
||||||
HideCursor()
|
HideCursor()
|
||||||
SetCursorStyle(CursorStyle)
|
SetCursor(CursorStyle, Color)
|
||||||
Size() (width, height int)
|
Size() (width, height int)
|
||||||
EnableMouse(...MouseFlags)
|
EnableMouse(...MouseFlags)
|
||||||
DisableMouse()
|
DisableMouse()
|
||||||
@ -464,3 +465,11 @@ func (b *baseScreen) PostEvent(ev Event) error {
|
|||||||
return ErrEventQFull
|
return ErrEventQFull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *baseScreen) SetCursorStyle(cs CursorStyle, ccs ...Color) {
|
||||||
|
if len(ccs) > 0 {
|
||||||
|
b.SetCursor(cs, ccs[0])
|
||||||
|
} else {
|
||||||
|
b.SetCursor(cs, ColorNone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2023 The TCell Authors
|
// Copyright 2024 The TCell Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use file except in compliance with the License.
|
// you may not use file except in compliance with the License.
|
||||||
@ -239,7 +239,7 @@ func (s *simscreen) hideCursor() {
|
|||||||
s.cursorvis = false
|
s.cursorvis = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *simscreen) SetCursorStyle(CursorStyle) {}
|
func (s *simscreen) SetCursor(CursorStyle, Color) {}
|
||||||
|
|
||||||
func (s *simscreen) Show() {
|
func (s *simscreen) Show() {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
|
@ -227,6 +227,9 @@ type Terminfo struct {
|
|||||||
CursorSteadyUnderline string
|
CursorSteadyUnderline string
|
||||||
CursorBlinkingBar string
|
CursorBlinkingBar string
|
||||||
CursorSteadyBar string
|
CursorSteadyBar string
|
||||||
|
CursorColor string // nothing uses it yet
|
||||||
|
CursorColorRGB string // Cs (but not really because Cs uses X11 color string)
|
||||||
|
CursorColorReset string // Cr
|
||||||
EnterUrl string
|
EnterUrl string
|
||||||
ExitUrl string
|
ExitUrl string
|
||||||
SetWindowSize string
|
SetWindowSize string
|
||||||
|
30
tscreen.go
30
tscreen.go
@ -160,6 +160,9 @@ type tScreen struct {
|
|||||||
underFg string
|
underFg string
|
||||||
cursorStyles map[CursorStyle]string
|
cursorStyles map[CursorStyle]string
|
||||||
cursorStyle CursorStyle
|
cursorStyle CursorStyle
|
||||||
|
cursorColor Color
|
||||||
|
cursorRGB string
|
||||||
|
cursorFg string
|
||||||
saved *term.State
|
saved *term.State
|
||||||
stopQ chan struct{}
|
stopQ chan struct{}
|
||||||
eventQ chan Event
|
eventQ chan Event
|
||||||
@ -460,7 +463,20 @@ func (t *tScreen) prepareCursorStyles() {
|
|||||||
CursorStyleSteadyBar: "\x1b[6 q",
|
CursorStyleSteadyBar: "\x1b[6 q",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.ti.CursorColorRGB != "" {
|
||||||
|
// if it was X11 style with just a single %p1%s, then convert
|
||||||
|
t.cursorRGB = t.ti.CursorColorRGB
|
||||||
|
}
|
||||||
|
if t.ti.CursorColorReset != "" {
|
||||||
|
t.cursorFg = t.ti.CursorColorReset
|
||||||
|
}
|
||||||
|
if t.cursorRGB == "" {
|
||||||
|
t.cursorRGB = "\x1b]12;%p1%s\007"
|
||||||
|
t.cursorFg = "\x1b]112\007"
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert XTERM style color names to RGB color code. We have no way to do palette colors
|
||||||
|
t.cursorRGB = strings.Replace(t.cursorRGB, "%p1%s", "#%p1%02x%p2%02x%p3%02x", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) prepareKey(key Key, val string) {
|
func (t *tScreen) prepareKey(key Key, val string) {
|
||||||
@ -912,9 +928,10 @@ func (t *tScreen) ShowCursor(x, y int) {
|
|||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) SetCursorStyle(cs CursorStyle) {
|
func (t *tScreen) SetCursor(cs CursorStyle, cc Color) {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
t.cursorStyle = cs
|
t.cursorStyle = cs
|
||||||
|
t.cursorColor = cc
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,6 +954,14 @@ func (t *tScreen) showCursor() {
|
|||||||
t.TPuts(esc)
|
t.TPuts(esc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.cursorRGB != "" {
|
||||||
|
if t.cursorColor == ColorReset {
|
||||||
|
t.TPuts(t.cursorFg)
|
||||||
|
} else if t.cursorColor.Valid() {
|
||||||
|
r, g, b := t.cursorColor.RGB()
|
||||||
|
t.TPuts(t.ti.TParm(t.cursorRGB, int(r), int(g), int(b)))
|
||||||
|
}
|
||||||
|
}
|
||||||
t.cx = x
|
t.cx = x
|
||||||
t.cy = y
|
t.cy = y
|
||||||
}
|
}
|
||||||
@ -1954,6 +1979,9 @@ func (t *tScreen) disengage() {
|
|||||||
if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault {
|
if t.cursorStyles != nil && t.cursorStyle != CursorStyleDefault {
|
||||||
t.TPuts(t.cursorStyles[CursorStyleDefault])
|
t.TPuts(t.cursorStyles[CursorStyleDefault])
|
||||||
}
|
}
|
||||||
|
if t.cursorFg != "" && t.cursorColor.Valid() {
|
||||||
|
t.TPuts(t.cursorFg)
|
||||||
|
}
|
||||||
t.TPuts(ti.ResetFgBg)
|
t.TPuts(ti.ResetFgBg)
|
||||||
t.TPuts(ti.AttrOff)
|
t.TPuts(ti.AttrOff)
|
||||||
t.TPuts(ti.ExitKeypad)
|
t.TPuts(ti.ExitKeypad)
|
||||||
|
@ -21,6 +21,7 @@ const beepAudio = new Audio("beep.wav");
|
|||||||
var cx = -1;
|
var cx = -1;
|
||||||
var cy = -1;
|
var cy = -1;
|
||||||
var cursorClass = "cursor-blinking-block";
|
var cursorClass = "cursor-blinking-block";
|
||||||
|
var cursorColor = "";
|
||||||
|
|
||||||
var content; // {data: row[height], dirty: bool}
|
var content; // {data: row[height], dirty: bool}
|
||||||
// row = {data: element[width], previous: span}
|
// row = {data: element[width], previous: span}
|
||||||
@ -185,12 +186,18 @@ function displayCursor() {
|
|||||||
content.data[cy].data[cx] = span;
|
content.data[cy].data[cx] = span;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cursorColor != "") {
|
||||||
|
term.style.setProperty("--cursor-color", cursorColor);
|
||||||
|
} else {
|
||||||
|
term.style.setProperty("--cursor-color", "lightgrey");
|
||||||
|
}
|
||||||
|
|
||||||
content.data[cy].data[cx].classList.add(cursorClass);
|
content.data[cy].data[cx].classList.add(cursorClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCursorStyle(newClass) {
|
function setCursorStyle(newClass, newColor) {
|
||||||
if (newClass == cursorClass) {
|
if (newClass == cursorClass && newColor == cursorColor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +214,7 @@ function setCursorStyle(newClass) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursorClass = newClass;
|
cursorClass = newClass;
|
||||||
|
cursorColor = newColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function beep() {
|
function beep() {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
-khtml-user-select: none;
|
-khtml-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
|
--cursor-color: lightgrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style attributes */
|
/* Style attributes */
|
||||||
@ -64,26 +65,26 @@
|
|||||||
/* Cursor styles */
|
/* Cursor styles */
|
||||||
|
|
||||||
.cursor-steady-block {
|
.cursor-steady-block {
|
||||||
background-color: lightgrey !important;
|
background-color: var(--cursor-color) !important;
|
||||||
}
|
}
|
||||||
.cursor-blinking-block {
|
.cursor-blinking-block {
|
||||||
animation: blinking-block 1s step-start infinite !important;
|
animation: blinking-block 1s step-start infinite !important;
|
||||||
}
|
}
|
||||||
@keyframes blinking-block {
|
@keyframes blinking-block {
|
||||||
50% {
|
50% {
|
||||||
background-color: lightgrey;
|
background-color: var(--cursor-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor-steady-underline {
|
.cursor-steady-underline {
|
||||||
text-decoration: underline lightgrey !important;
|
text-decoration: underline var(--cursor-color) !important;
|
||||||
}
|
}
|
||||||
.cursor-blinking-underline {
|
.cursor-blinking-underline {
|
||||||
animation: blinking-underline 1s step-start infinite !important;
|
animation: blinking-underline 1s step-start infinite !important;
|
||||||
}
|
}
|
||||||
@keyframes blinking-underline {
|
@keyframes blinking-underline {
|
||||||
50% {
|
50% {
|
||||||
text-decoration: underline lightgrey;
|
text-decoration: underline var(--cursor-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +94,7 @@
|
|||||||
.cursor-steady-bar:before {
|
.cursor-steady-bar:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
width: 2px;
|
width: 2px;
|
||||||
background-color: lightgrey !important;
|
background-color: var(--cursor-color) !important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.cursor-blinking-bar {
|
.cursor-blinking-bar {
|
||||||
@ -102,7 +103,7 @@
|
|||||||
.cursor-blinking-bar:before {
|
.cursor-blinking-bar:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
width: 2px;
|
width: 2px;
|
||||||
background-color: lightgrey !important;
|
background-color: var(--cursor-color) !important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: blinker 1s step-start infinite;
|
animation: blinker 1s step-start infinite;
|
||||||
}
|
}
|
||||||
|
11
wscreen.go
11
wscreen.go
@ -19,11 +19,13 @@ package tcell
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gdamore/tcell/v2/terminfo"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2/terminfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTerminfoScreen() (Screen, error) {
|
func NewTerminfoScreen() (Screen, error) {
|
||||||
@ -158,9 +160,12 @@ func (t *wScreen) ShowCursor(x, y int) {
|
|||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *wScreen) SetCursorStyle(cs CursorStyle) {
|
func (t *wScreen) SetCursor(cs CursorStyle, cc Color) {
|
||||||
|
if !cc.Valid() {
|
||||||
|
cc = ColorLightGray
|
||||||
|
}
|
||||||
t.Lock()
|
t.Lock()
|
||||||
js.Global().Call("setCursorStyle", curStyleClasses[cs])
|
js.Global().Call("setCursorStyle", curStyleClasses[cs], fmt.Sprintf("#%06x", cc.Hex()))
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user