1// +build ignore 2 3// Copyright 2015 The TCell Authors 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use file except in compliance with the License. 7// You may obtain a copy of the license at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17// mouse displays a text box and tests mouse interaction. As you click 18// and drag, boxes are displayed on screen. Other events are reported in 19// the box. Press ESC twice to exit the program. 20package main 21 22import ( 23 "fmt" 24 "os" 25 "os/exec" 26 "runtime" 27 28 "github.com/gdamore/tcell/v2" 29 "github.com/gdamore/tcell/v2/encoding" 30 31 "github.com/mattn/go-runewidth" 32) 33 34var defStyle tcell.Style 35 36func emitStr(s tcell.Screen, x, y int, style tcell.Style, str string) { 37 for _, c := range str { 38 var comb []rune 39 w := runewidth.RuneWidth(c) 40 if w == 0 { 41 comb = []rune{c} 42 c = ' ' 43 w = 1 44 } 45 s.SetContent(x, y, c, comb, style) 46 x += w 47 } 48} 49 50func drawBox(s tcell.Screen, x1, y1, x2, y2 int, style tcell.Style, r rune) { 51 if y2 < y1 { 52 y1, y2 = y2, y1 53 } 54 if x2 < x1 { 55 x1, x2 = x2, x1 56 } 57 58 for col := x1; col <= x2; col++ { 59 s.SetContent(col, y1, tcell.RuneHLine, nil, style) 60 s.SetContent(col, y2, tcell.RuneHLine, nil, style) 61 } 62 for row := y1 + 1; row < y2; row++ { 63 s.SetContent(x1, row, tcell.RuneVLine, nil, style) 64 s.SetContent(x2, row, tcell.RuneVLine, nil, style) 65 } 66 if y1 != y2 && x1 != x2 { 67 // Only add corners if we need to 68 s.SetContent(x1, y1, tcell.RuneULCorner, nil, style) 69 s.SetContent(x2, y1, tcell.RuneURCorner, nil, style) 70 s.SetContent(x1, y2, tcell.RuneLLCorner, nil, style) 71 s.SetContent(x2, y2, tcell.RuneLRCorner, nil, style) 72 } 73 for row := y1 + 1; row < y2; row++ { 74 for col := x1 + 1; col < x2; col++ { 75 s.SetContent(col, row, r, nil, style) 76 } 77 } 78} 79 80func drawSelect(s tcell.Screen, x1, y1, x2, y2 int, sel bool) { 81 82 if y2 < y1 { 83 y1, y2 = y2, y1 84 } 85 if x2 < x1 { 86 x1, x2 = x2, x1 87 } 88 for row := y1; row <= y2; row++ { 89 for col := x1; col <= x2; col++ { 90 mainc, combc, style, width := s.GetContent(col, row) 91 if style == tcell.StyleDefault { 92 style = defStyle 93 } 94 style = style.Reverse(sel) 95 s.SetContent(col, row, mainc, combc, style) 96 col += width - 1 97 } 98 } 99} 100 101// This program just shows simple mouse and keyboard events. Press ESC twice to 102// exit. 103func main() { 104 105 shell := os.Getenv("SHELL") 106 if shell == "" { 107 if runtime.GOOS == "windows" { 108 shell = "CMD.EXE" 109 } else { 110 shell = "/bin/sh" 111 } 112 } 113 114 encoding.Register() 115 116 s, e := tcell.NewScreen() 117 if e != nil { 118 fmt.Fprintf(os.Stderr, "%v\n", e) 119 os.Exit(1) 120 } 121 if e := s.Init(); e != nil { 122 fmt.Fprintf(os.Stderr, "%v\n", e) 123 os.Exit(1) 124 } 125 defStyle = tcell.StyleDefault. 126 Background(tcell.ColorReset). 127 Foreground(tcell.ColorReset) 128 s.SetStyle(defStyle) 129 s.EnableMouse() 130 s.EnablePaste() 131 s.Clear() 132 133 posfmt := "Mouse: %d, %d " 134 btnfmt := "Buttons: %s" 135 keyfmt := "Keys: %s" 136 pastefmt := "Paste: [%d] %s" 137 white := tcell.StyleDefault. 138 Foreground(tcell.ColorWhite).Background(tcell.ColorRed) 139 140 mx, my := -1, -1 141 ox, oy := -1, -1 142 bx, by := -1, -1 143 w, h := s.Size() 144 lchar := '*' 145 bstr := "" 146 lks := "" 147 pstr := "" 148 ecnt := 0 149 pasting := false 150 151 for { 152 drawBox(s, 1, 1, 42, 7, white, ' ') 153 emitStr(s, 2, 2, white, "Press ESC twice to exit, C to clear.") 154 emitStr(s, 2, 3, white, fmt.Sprintf(posfmt, mx, my)) 155 emitStr(s, 2, 4, white, fmt.Sprintf(btnfmt, bstr)) 156 emitStr(s, 2, 5, white, fmt.Sprintf(keyfmt, lks)) 157 158 ps := pstr 159 if len(ps) > 26 { 160 ps = "..." + ps[len(ps)-24:] 161 } 162 emitStr(s, 2, 6, white, fmt.Sprintf(pastefmt, len(pstr), ps)) 163 164 s.Show() 165 bstr = "" 166 ev := s.PollEvent() 167 st := tcell.StyleDefault.Background(tcell.ColorRed) 168 up := tcell.StyleDefault. 169 Background(tcell.ColorBlue). 170 Foreground(tcell.ColorBlack) 171 w, h = s.Size() 172 173 // always clear any old selection box 174 if ox >= 0 && oy >= 0 && bx >= 0 { 175 drawSelect(s, ox, oy, bx, by, false) 176 } 177 178 switch ev := ev.(type) { 179 case *tcell.EventResize: 180 s.Sync() 181 s.SetContent(w-1, h-1, 'R', nil, st) 182 case *tcell.EventKey: 183 s.SetContent(w-2, h-2, ev.Rune(), nil, st) 184 if pasting { 185 s.SetContent(w-1, h-1, 'P', nil, st) 186 if ev.Key() == tcell.KeyRune { 187 pstr = pstr + string(ev.Rune()) 188 } else { 189 pstr = pstr + "\ufffd" // replacement for now 190 } 191 lks = "" 192 continue 193 } 194 pstr = "" 195 s.SetContent(w-1, h-1, 'K', nil, st) 196 if ev.Key() == tcell.KeyEscape { 197 ecnt++ 198 if ecnt > 1 { 199 s.Fini() 200 os.Exit(0) 201 } 202 } else if ev.Key() == tcell.KeyCtrlL { 203 s.Sync() 204 } else if ev.Key() == tcell.KeyCtrlZ { 205 // CtrlZ doesn't really suspend the process, but we use it to execute a subshell. 206 if err := s.Suspend(); err == nil { 207 fmt.Printf("Executing shell (%s -l)...\n", shell) 208 fmt.Printf("Exit the shell to return to the demo.\n") 209 c := exec.Command(shell, "-l" ) // NB: -l works for cmd.exe too (ignored) 210 c.Stdin = os.Stdin 211 c.Stdout = os.Stdout 212 c.Stderr = os.Stderr 213 c.Run() 214 if err := s.Resume(); err != nil { 215 panic("failed to resume: " + err.Error()) 216 } 217 } 218 } else { 219 ecnt = 0 220 if ev.Rune() == 'C' || ev.Rune() == 'c' { 221 s.Clear() 222 } 223 } 224 lks = ev.Name() 225 case *tcell.EventPaste: 226 pasting = ev.Start() 227 if pasting { 228 pstr = "" 229 } 230 case *tcell.EventMouse: 231 x, y := ev.Position() 232 button := ev.Buttons() 233 for i := uint(0); i < 8; i++ { 234 if int(button)&(1<<i) != 0 { 235 bstr += fmt.Sprintf(" Button%d", i+1) 236 } 237 } 238 if button&tcell.WheelUp != 0 { 239 bstr += " WheelUp" 240 } 241 if button&tcell.WheelDown != 0 { 242 bstr += " WheelDown" 243 } 244 if button&tcell.WheelLeft != 0 { 245 bstr += " WheelLeft" 246 } 247 if button&tcell.WheelRight != 0 { 248 bstr += " WheelRight" 249 } 250 // Only buttons, not wheel events 251 button &= tcell.ButtonMask(0xff) 252 ch := '*' 253 254 if button != tcell.ButtonNone && ox < 0 { 255 ox, oy = x, y 256 } 257 switch ev.Buttons() { 258 case tcell.ButtonNone: 259 if ox >= 0 { 260 bg := tcell.Color((lchar-'0')*2) | tcell.ColorValid 261 drawBox(s, ox, oy, x, y, 262 up.Background(bg), 263 lchar) 264 ox, oy = -1, -1 265 bx, by = -1, -1 266 } 267 case tcell.Button1: 268 ch = '1' 269 case tcell.Button2: 270 ch = '2' 271 case tcell.Button3: 272 ch = '3' 273 case tcell.Button4: 274 ch = '4' 275 case tcell.Button5: 276 ch = '5' 277 case tcell.Button6: 278 ch = '6' 279 case tcell.Button7: 280 ch = '7' 281 case tcell.Button8: 282 ch = '8' 283 default: 284 ch = '*' 285 286 } 287 if button != tcell.ButtonNone { 288 bx, by = x, y 289 } 290 lchar = ch 291 s.SetContent(w-1, h-1, 'M', nil, st) 292 mx, my = x, y 293 default: 294 s.SetContent(w-1, h-1, 'X', nil, st) 295 } 296 297 if ox >= 0 && bx >= 0 { 298 drawSelect(s, ox, oy, bx, by, true) 299 } 300 } 301} 302