mirror of https://github.com/gdamore/tcell.git
316 lines
7.1 KiB
Go
316 lines
7.1 KiB
Go
//go:build ignore
|
|
// +build ignore
|
|
|
|
// Copyright 2022 The TCell Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use file except in compliance with the License.
|
|
// You may obtain a copy of the license at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// mouse displays a text box and tests mouse interaction. As you click
|
|
// and drag, boxes are displayed on screen. Other events are reported in
|
|
// the box. Press ESC twice to exit the program.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/gdamore/tcell/v2/encoding"
|
|
|
|
"github.com/mattn/go-runewidth"
|
|
)
|
|
|
|
var defStyle tcell.Style
|
|
|
|
func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) {
|
|
for _, c := range str {
|
|
var comb []rune
|
|
w := runewidth.RuneWidth(c)
|
|
if w == 0 {
|
|
comb = []rune{c}
|
|
c = ' '
|
|
w = 1
|
|
}
|
|
s.SetContent(x, y, c, comb, style)
|
|
x += w
|
|
}
|
|
}
|
|
|
|
func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, r rune) {
|
|
if y2 < y1 {
|
|
y1, y2 = y2, y1
|
|
}
|
|
if x2 < x1 {
|
|
x1, x2 = x2, x1
|
|
}
|
|
|
|
for col := x1; col <= x2; col++ {
|
|
s.SetContent(col, y1, tcell.RuneHLine, nil, style)
|
|
s.SetContent(col, y2, tcell.RuneHLine, nil, style)
|
|
}
|
|
for row := y1 + 1; row < y2; row++ {
|
|
s.SetContent(x1, row, tcell.RuneVLine, nil, style)
|
|
s.SetContent(x2, row, tcell.RuneVLine, nil, style)
|
|
}
|
|
if y1 != y2 && x1 != x2 {
|
|
// Only add corners if we need to
|
|
s.SetContent(x1, y1, tcell.RuneULCorner, nil, style)
|
|
s.SetContent(x2, y1, tcell.RuneURCorner, nil, style)
|
|
s.SetContent(x1, y2, tcell.RuneLLCorner, nil, style)
|
|
s.SetContent(x2, y2, tcell.RuneLRCorner, nil, style)
|
|
}
|
|
for row := y1 + 1; row < y2; row++ {
|
|
for col := x1 + 1; col < x2; col++ {
|
|
s.SetContent(col, row, r, nil, style)
|
|
}
|
|
}
|
|
}
|
|
|
|
func drawSelect(s tcell.Screen, x1, y1, x2, y2 int, sel bool) {
|
|
|
|
if y2 < y1 {
|
|
y1, y2 = y2, y1
|
|
}
|
|
if x2 < x1 {
|
|
x1, x2 = x2, x1
|
|
}
|
|
for row := y1; row <= y2; row++ {
|
|
for col := x1; col <= x2; col++ {
|
|
mainc, combc, style, width := s.GetContent(col, row)
|
|
if style == tcell.StyleDefault {
|
|
style = defStyle
|
|
}
|
|
style = style.Reverse(sel)
|
|
s.SetContent(col, row, mainc, combc, style)
|
|
col += width - 1
|
|
}
|
|
}
|
|
}
|
|
|
|
// This program just shows simple mouse and keyboard events. Press ESC twice to
|
|
// exit.
|
|
func main() {
|
|
|
|
shell := os.Getenv("SHELL")
|
|
if shell == "" {
|
|
if runtime.GOOS == "windows" {
|
|
shell = "CMD.EXE"
|
|
} else {
|
|
shell = "/bin/sh"
|
|
}
|
|
}
|
|
|
|
encoding.Register()
|
|
|
|
s, e := tcell.NewScreen()
|
|
if e != nil {
|
|
fmt.Fprintf(os.Stderr, "%v\n", e)
|
|
os.Exit(1)
|
|
}
|
|
if e := s.Init(); e != nil {
|
|
fmt.Fprintf(os.Stderr, "%v\n", e)
|
|
os.Exit(1)
|
|
}
|
|
defStyle = tcell.StyleDefault.
|
|
Background(tcell.ColorReset).
|
|
Foreground(tcell.ColorReset)
|
|
s.SetStyle(defStyle)
|
|
s.EnableMouse()
|
|
s.EnablePaste()
|
|
s.Clear()
|
|
|
|
posfmt := "Mouse: %d, %d "
|
|
btnfmt := "Buttons: %s"
|
|
keyfmt := "Keys: %s"
|
|
pastefmt := "Paste: [%d] %s"
|
|
white := tcell.StyleDefault.
|
|
Foreground(tcell.ColorWhite).Background(tcell.ColorRed)
|
|
|
|
mx, my := -1, -1
|
|
ox, oy := -1, -1
|
|
bx, by := -1, -1
|
|
w, h := s.Size()
|
|
lchar := '*'
|
|
bstr := ""
|
|
lks := ""
|
|
pstr := ""
|
|
ecnt := 0
|
|
pasting := false
|
|
|
|
for {
|
|
drawBox(s, 1, 1, 42, 7, white, ' ')
|
|
emitStr(s, 2, 2, white, "Press ESC twice to exit, C to clear.")
|
|
emitStr(s, 2, 3, white, fmt.Sprintf(posfmt, mx, my))
|
|
emitStr(s, 2, 4, white, fmt.Sprintf(btnfmt, bstr))
|
|
emitStr(s, 2, 5, white, fmt.Sprintf(keyfmt, lks))
|
|
|
|
ps := pstr
|
|
if len(ps) > 26 {
|
|
ps = "..." + ps[len(ps)-24:]
|
|
}
|
|
emitStr(s, 2, 6, white, fmt.Sprintf(pastefmt, len(pstr), ps))
|
|
|
|
s.Show()
|
|
bstr = ""
|
|
ev := s.PollEvent()
|
|
st := tcell.StyleDefault.Background(tcell.ColorRed)
|
|
up := tcell.StyleDefault.
|
|
Background(tcell.ColorBlue).
|
|
Foreground(tcell.ColorBlack)
|
|
w, h = s.Size()
|
|
|
|
// always clear any old selection box
|
|
if ox >= 0 && oy >= 0 && bx >= 0 {
|
|
drawSelect(s, ox, oy, bx, by, false)
|
|
}
|
|
|
|
switch ev := ev.(type) {
|
|
case *tcell.EventResize:
|
|
s.Sync()
|
|
s.SetContent(w-1, h-1, 'R', nil, st)
|
|
case *tcell.EventKey:
|
|
s.SetContent(w-2, h-2, ev.Rune(), nil, st)
|
|
if pasting {
|
|
s.SetContent(w-1, h-1, 'P', nil, st)
|
|
if ev.Key() == tcell.KeyRune {
|
|
pstr = pstr + string(ev.Rune())
|
|
} else {
|
|
pstr = pstr + "\ufffd" // replacement for now
|
|
}
|
|
lks = ""
|
|
continue
|
|
}
|
|
pstr = ""
|
|
s.SetContent(w-1, h-1, 'K', nil, st)
|
|
if ev.Key() == tcell.KeyEscape {
|
|
ecnt++
|
|
if ecnt > 1 {
|
|
s.Fini()
|
|
os.Exit(0)
|
|
}
|
|
} else if ev.Key() == tcell.KeyCtrlL {
|
|
s.Sync()
|
|
} else if ev.Key() == tcell.KeyCtrlZ {
|
|
// CtrlZ doesn't really suspend the process, but we use it to execute a subshell.
|
|
if err := s.Suspend(); err == nil {
|
|
fmt.Printf("Executing shell (%s -l)...\n", shell)
|
|
fmt.Printf("Exit the shell to return to the demo.\n")
|
|
c := exec.Command(shell, "-l") // NB: -l works for cmd.exe too (ignored)
|
|
c.Stdin = os.Stdin
|
|
c.Stdout = os.Stdout
|
|
c.Stderr = os.Stderr
|
|
c.Run()
|
|
if err := s.Resume(); err != nil {
|
|
panic("failed to resume: " + err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
ecnt = 0
|
|
if ev.Rune() == 'C' || ev.Rune() == 'c' {
|
|
s.Clear()
|
|
}
|
|
}
|
|
lks = ev.Name()
|
|
case *tcell.EventPaste:
|
|
pasting = ev.Start()
|
|
if pasting {
|
|
pstr = ""
|
|
}
|
|
case *tcell.EventMouse:
|
|
x, y := ev.Position()
|
|
button := ev.Buttons()
|
|
mods := ev.Modifiers()
|
|
if mods&tcell.ModShift != 0 {
|
|
bstr += " Shift"
|
|
}
|
|
if mods&tcell.ModCtrl != 0 {
|
|
bstr += " Ctrl"
|
|
}
|
|
if mods&tcell.ModAlt != 0 {
|
|
bstr += " Alt"
|
|
}
|
|
if mods&tcell.ModMeta != 0 {
|
|
bstr += " Meta"
|
|
}
|
|
for i := uint(0); i < 8; i++ {
|
|
if int(button)&(1<<i) != 0 {
|
|
bstr += fmt.Sprintf(" Button%d", i+1)
|
|
}
|
|
}
|
|
if button&tcell.WheelUp != 0 {
|
|
bstr += " WheelUp"
|
|
}
|
|
if button&tcell.WheelDown != 0 {
|
|
bstr += " WheelDown"
|
|
}
|
|
if button&tcell.WheelLeft != 0 {
|
|
bstr += " WheelLeft"
|
|
}
|
|
if button&tcell.WheelRight != 0 {
|
|
bstr += " WheelRight"
|
|
}
|
|
// Only buttons, not wheel events
|
|
button &= tcell.ButtonMask(0xff)
|
|
ch := '*'
|
|
|
|
if button != tcell.ButtonNone && ox < 0 {
|
|
ox, oy = x, y
|
|
}
|
|
switch ev.Buttons() {
|
|
case tcell.ButtonNone:
|
|
if ox >= 0 {
|
|
bg := tcell.Color((lchar-'0')*2) | tcell.ColorValid
|
|
drawBox(s, ox, oy, x, y,
|
|
up.Background(bg),
|
|
lchar)
|
|
ox, oy = -1, -1
|
|
bx, by = -1, -1
|
|
}
|
|
case tcell.Button1:
|
|
ch = '1'
|
|
case tcell.Button2:
|
|
ch = '2'
|
|
case tcell.Button3:
|
|
ch = '3'
|
|
case tcell.Button4:
|
|
ch = '4'
|
|
case tcell.Button5:
|
|
ch = '5'
|
|
case tcell.Button6:
|
|
ch = '6'
|
|
case tcell.Button7:
|
|
ch = '7'
|
|
case tcell.Button8:
|
|
ch = '8'
|
|
default:
|
|
ch = '*'
|
|
|
|
}
|
|
if button != tcell.ButtonNone {
|
|
bx, by = x, y
|
|
}
|
|
lchar = ch
|
|
s.SetContent(w-1, h-1, 'M', nil, st)
|
|
mx, my = x, y
|
|
default:
|
|
s.SetContent(w-1, h-1, 'X', nil, st)
|
|
}
|
|
|
|
if ox >= 0 && bx >= 0 {
|
|
drawSelect(s, ox, oy, bx, by, true)
|
|
}
|
|
}
|
|
}
|