1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-24 13:48:51 +08:00

fixes #75 Add goreport and fix related issues

This commit is contained in:
Garrett D'Amore 2015-11-04 17:05:24 -08:00
parent b727b9f424
commit 43f9cc0d07
15 changed files with 450 additions and 373 deletions

View File

@ -5,6 +5,7 @@
[![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/tcell/blob/master/LICENSE) [![Apache License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/gdamore/tcell/blob/master/LICENSE)
[![Gitter](https://img.shields.io/badge/gitter-join-brightgreen.svg)](https://gitter.im/gdamore/tcell) [![Gitter](https://img.shields.io/badge/gitter-join-brightgreen.svg)](https://gitter.im/gdamore/tcell)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/tcell) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/tcell)
[![Go Report Card](http://goreportcard.com/badge/gdamore/tcell)](http://goreportcard.com/report/gdamore/tcell)
> _Tcell is a work in progress (Gamma). > _Tcell is a work in progress (Gamma).
> Please use with caution; interfaces may change in before final release. > Please use with caution; interfaces may change in before final release.

View File

@ -31,4 +31,4 @@ const (
AttrNone AttrMask = 0 AttrNone AttrMask = 0
) )
const AttrMaskAll = AttrBold | AttrBlink | AttrReverse | AttrUnderline | AttrDim const attrAll = AttrBold | AttrBlink | AttrReverse | AttrUnderline | AttrDim

View File

@ -16,6 +16,6 @@
package tcell package tcell
func GetCharset() string { func getCharset() string {
return "" return ""
} }

View File

@ -21,7 +21,7 @@ import (
"strings" "strings"
) )
func GetCharset() string { func getCharset() string {
// Determine the character set. This can help us later. // Determine the character set. This can help us later.
// Per POSIX, we search for LC_ALL first, then LC_CTYPE, and // Per POSIX, we search for LC_ALL first, then LC_CTYPE, and
// finally LANG. First one set wins. // finally LANG. First one set wins.

View File

@ -16,6 +16,6 @@
package tcell package tcell
func GetCharset() string { func getCharset() string {
return "UTF-16" return "UTF-16"
} }

View File

@ -964,6 +964,9 @@ var colorNames = map[string]Color{
"slategrey": ColorSlateGray, "slategrey": ColorSlateGray,
} }
// Hex returns the color's hexadecimal RGB 24-bit value with each component
// consisting of a single byte, ala R << 16 | G << 8 | B. If the color
// is unknown or unset, -1 is returned.
func (c Color) Hex() int32 { func (c Color) Hex() int32 {
if c&ColorIsRGB != 0 { if c&ColorIsRGB != 0 {
return (int32(c) & 0xffffff) return (int32(c) & 0xffffff)
@ -974,6 +977,9 @@ func (c Color) Hex() int32 {
return -1 return -1
} }
// RGB returns the red, green, and blue components of the color, with
// each component represented as a value 0-255. In the event that the
// color cannot be broken up (not set usually), -1 is returned for each value.
func (c Color) RGB() (int32, int32, int32) { func (c Color) RGB() (int32, int32, int32) {
v := c.Hex() v := c.Hex()
if v < 0 { if v < 0 {
@ -982,15 +988,18 @@ func (c Color) RGB() (int32, int32, int32) {
return (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff return (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff
} }
// NewRGBColor returns a new color with the given red, green, and blue values.
// Each value must be represented in the range 0-255.
func NewRGBColor(r, g, b int32) Color { func NewRGBColor(r, g, b int32) Color {
return NewHexColor(((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)) return NewHexColor(((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))
} }
// NewHexColor returns a color using the given 24-bit RGB value.
func NewHexColor(v int32) Color { func NewHexColor(v int32) Color {
return ColorIsRGB | Color(v) return ColorIsRGB | Color(v)
} }
// Given a color name (W3C name), return the actual color. A hex value may // GetColor creates a Color from a color name (W3C name). A hex value may
// be supplied as a string in the format "#ffffff". // be supplied as a string in the format "#ffffff".
func GetColor(name string) Color { func GetColor(name string) Color {
if c, ok := colorNames[name]; ok { if c, ok := colorNames[name]; ok {

View File

@ -26,7 +26,6 @@ import (
type cScreen struct { type cScreen struct {
in syscall.Handle in syscall.Handle
out syscall.Handle out syscall.Handle
mbtns uint32 // debounce mouse buttons
evch chan Event evch chan Event
quit chan struct{} quit chan struct{}
curx int curx int
@ -372,6 +371,52 @@ const (
vkF24 = 0x87 vkF24 = 0x87
) )
var vkKeys = map[uint16]Key{
vkCancel: KeyCancel,
vkBack: KeyBackspace,
vkTab: KeyTab,
vkClear: KeyClear,
vkPause: KeyPause,
vkPrint: KeyPrint,
vkPrtScr: KeyPrint,
vkPrior: KeyPgUp,
vkNext: KeyPgDn,
vkReturn: KeyEnter,
vkEnd: KeyEnd,
vkHome: KeyHome,
vkLeft: KeyLeft,
vkUp: KeyUp,
vkRight: KeyRight,
vkDown: KeyDown,
vkInsert: KeyInsert,
vkDelete: KeyDelete,
vkHelp: KeyHelp,
vkF1: KeyF1,
vkF2: KeyF2,
vkF3: KeyF3,
vkF4: KeyF4,
vkF5: KeyF5,
vkF6: KeyF6,
vkF7: KeyF7,
vkF8: KeyF8,
vkF9: KeyF9,
vkF10: KeyF10,
vkF11: KeyF11,
vkF12: KeyF12,
vkF13: KeyF13,
vkF14: KeyF14,
vkF15: KeyF15,
vkF16: KeyF16,
vkF17: KeyF17,
vkF18: KeyF18,
vkF19: KeyF19,
vkF20: KeyF20,
vkF21: KeyF21,
vkF22: KeyF22,
vkF23: KeyF23,
vkF24: KeyF24,
}
// NB: All Windows platforms are little endian. We assume this // NB: All Windows platforms are little endian. We assume this
// never, ever change. The following code is endian safe. and does // never, ever change. The following code is endian safe. and does
// not use unsafe pointers. // not use unsafe pointers.
@ -406,6 +451,50 @@ func mod2mask(cks uint32) ModMask {
return mm return mm
} }
func mrec2btns(mbtns, flags uint32) ButtonMask {
btns := ButtonNone
if mbtns&0x1 != 0 {
btns |= Button1
}
if mbtns&0x2 != 0 {
btns |= Button2
}
if mbtns&0x4 != 0 {
btns |= Button3
}
if mbtns&0x8 != 0 {
btns |= Button4
}
if mbtns&0x10 != 0 {
btns |= Button5
}
if mbtns&0x20 != 0 {
btns |= Button6
}
if mbtns&0x40 != 0 {
btns |= Button7
}
if mbtns&0x80 != 0 {
btns |= Button8
}
if flags&mouseVWheeled != 0 {
if mbtns&0x80000000 == 0 {
btns |= WheelUp
} else {
btns |= WheelDown
}
}
if flags&mouseHWheeled != 0 {
if mbtns&0x80000000 == 0 {
btns |= WheelRight
} else {
btns |= WheelLeft
}
}
return btns
}
func (s *cScreen) getConsoleInput() error { func (s *cScreen) getConsoleInput() error {
rec := &inputRecord{} rec := &inputRecord{}
var nrec int32 var nrec int32
@ -444,92 +533,8 @@ func (s *cScreen) getConsoleInput() error {
return nil return nil
} }
key := KeyNUL // impossible on Windows key := KeyNUL // impossible on Windows
switch krec.kcode { ok := false
case vkCancel: if key, ok = vkKeys[krec.kcode]; !ok {
key = KeyCancel
case vkBack:
key = KeyBackspace
case vkTab:
key = KeyTab
case vkClear:
key = KeyClear
case vkPause:
key = KeyPause
case vkPrint, vkPrtScr:
key = KeyPrint
case vkPrior:
key = KeyPgUp
case vkNext:
key = KeyPgDn
case vkReturn:
key = KeyEnter
case vkEnd:
key = KeyEnd
case vkHome:
key = KeyHome
case vkLeft:
key = KeyLeft
case vkUp:
key = KeyUp
case vkRight:
key = KeyRight
case vkDown:
key = KeyDown
case vkInsert:
key = KeyInsert
case vkDelete:
key = KeyDelete
case vkHelp:
key = KeyHelp
case vkF1:
key = KeyF1
case vkF2:
key = KeyF2
case vkF3:
key = KeyF3
case vkF4:
key = KeyF4
case vkF5:
key = KeyF5
case vkF6:
key = KeyF6
case vkF7:
key = KeyF7
case vkF8:
key = KeyF8
case vkF9:
key = KeyF9
case vkF10:
key = KeyF10
case vkF11:
key = KeyF11
case vkF12:
key = KeyF12
case vkF13:
key = KeyF13
case vkF14:
key = KeyF14
case vkF15:
key = KeyF15
case vkF16:
key = KeyF16
case vkF17:
key = KeyF17
case vkF18:
key = KeyF18
case vkF19:
key = KeyF19
case vkF20:
key = KeyF20
case vkF21:
key = KeyF21
case vkF22:
key = KeyF22
case vkF23:
key = KeyF23
case vkF24:
key = KeyF24
default:
return nil return nil
} }
for krec.repeat > 0 { for krec.repeat > 0 {
@ -544,49 +549,8 @@ func (s *cScreen) getConsoleInput() error {
mrec.y = geti16(rec.data[2:]) mrec.y = geti16(rec.data[2:])
mrec.btns = getu32(rec.data[4:]) mrec.btns = getu32(rec.data[4:])
mrec.mod = getu32(rec.data[8:]) mrec.mod = getu32(rec.data[8:])
mrec.flags = getu32(rec.data[12:]) // not using yet mrec.flags = getu32(rec.data[12:])
btns := ButtonNone btns := mrec2btns(mrec.btns, mrec.flags)
s.mbtns = mrec.btns
if mrec.btns&0x1 != 0 {
btns |= Button1
}
if mrec.btns&0x2 != 0 {
btns |= Button2
}
if mrec.btns&0x4 != 0 {
btns |= Button3
}
if mrec.btns&0x8 != 0 {
btns |= Button4
}
if mrec.btns&0x10 != 0 {
btns |= Button5
}
if mrec.btns&0x20 != 0 {
btns |= Button6
}
if mrec.btns&0x40 != 0 {
btns |= Button7
}
if mrec.btns&0x80 != 0 {
btns |= Button8
}
if mrec.flags&mouseVWheeled != 0 {
if mrec.btns&0x80000000 == 0 {
btns |= WheelUp
} else {
btns |= WheelDown
}
}
if mrec.flags&mouseHWheeled != 0 {
if mrec.btns&0x80000000 == 0 {
btns |= WheelRight
} else {
btns |= WheelLeft
}
}
// we ignore double click, events are delivered normally // we ignore double click, events are delivered normally
s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns, s.PostEvent(NewEventMouse(int(mrec.x), int(mrec.y), btns,
mod2mask(mrec.mod))) mod2mask(mrec.mod)))
@ -615,6 +579,25 @@ func (s *cScreen) Colors() int {
return 16 return 16
} }
var vgaColors = map[Color]uint16{
ColorBlack: 0,
ColorMaroon: 0x4,
ColorGreen: 0x2,
ColorNavy: 0x1,
ColorOlive: 0x6,
ColorPurple: 0x5,
ColorTeal: 0x3,
ColorSilver: 0x7,
ColorGrey: 0x8,
ColorRed: 0xc,
ColorLime: 0xa,
ColorBlue: 0x9,
ColorYellow: 0xe,
ColorFuchsia: 0xd,
ColorAqua: 0xb,
ColorWhite: 0xf,
}
// Windows uses RGB signals // Windows uses RGB signals
func mapColor2RGB(c Color) uint16 { func mapColor2RGB(c Color) uint16 {
@ -628,41 +611,8 @@ func mapColor2RGB(c Color) uint16 {
} }
winLock.Unlock() winLock.Unlock()
switch c { if vc, ok := vgaColors[c]; ok {
case ColorBlack: return vc
return 0
// primaries
case ColorMaroon:
return 0x4
case ColorGreen:
return 0x2
case ColorNavy:
return 0x1
case ColorOlive:
return 0x6
case ColorPurple:
return 0x5
case ColorTeal:
return 0x3
case ColorSilver:
return 0x7
// bright variants
case ColorGrey:
return 0x8
case ColorRed:
return 0xc
case ColorLime:
return 0xa
case ColorBlue:
return 0x9
case ColorYellow:
return 0xe
case ColorFuchsia:
return 0xd
case ColorAqua:
return 0xb
case ColorWhite:
return 0xf
} }
return 0 return 0
} }

View File

@ -31,14 +31,17 @@ type EventTime struct {
when time.Time when time.Time
} }
// When returns the time stamp when the event occurred.
func (e *EventTime) When() time.Time { func (e *EventTime) When() time.Time {
return e.when return e.when
} }
// SetEventTime sets the time of occurrence for the event.
func (e *EventTime) SetEventTime(t time.Time) { func (e *EventTime) SetEventTime(t time.Time) {
e.when = t e.when = t
} }
// SetEventNow sets the time of occurrence for the event to the current time.
func (e *EventTime) SetEventNow() { func (e *EventTime) SetEventNow() {
e.SetEventTime(time.Now()) e.SetEventTime(time.Now())
} }

198
key.go
View File

@ -78,6 +78,128 @@ func (ev *EventKey) Modifiers() ModMask {
return ev.mod return ev.mod
} }
var keyNames = map[Key]string{
KeySpace: "Space",
KeyEnter: "Enter",
KeyBackspace: "Backspace",
KeyTab: "Tab",
KeyBacktab: "Backtab",
KeyEsc: "Esc",
KeyBackspace2: "Backspace2",
KeyDelete: "Delete",
KeyInsert: "Insert",
KeyUp: "Up",
KeyDown: "Down",
KeyLeft: "Left",
KeyRight: "Right",
KeyHome: "Home",
KeyEnd: "End",
KeyUpLeft: "UpLeft",
KeyUpRight: "UpRight",
KeyDownLeft: "DownLeft",
KeyDownRight: "DownRight",
KeyCenter: "Center",
KeyPgDn: "PgDn",
KeyPgUp: "PgUp",
KeyClear: "Clear",
KeyExit: "Exit",
KeyCancel: "Cancel",
KeyPause: "Pause",
KeyPrint: "Print",
KeyF1: "F1",
KeyF2: "F2",
KeyF3: "F3",
KeyF4: "F4",
KeyF5: "F5",
KeyF6: "F6",
KeyF7: "F7",
KeyF8: "F8",
KeyF9: "F9",
KeyF10: "F10",
KeyF11: "F11",
KeyF12: "F12",
KeyF13: "F13",
KeyF14: "F14",
KeyF15: "F15",
KeyF16: "F16",
KeyF17: "F17",
KeyF18: "F18",
KeyF19: "F19",
KeyF20: "F20",
KeyF21: "F21",
KeyF22: "F22",
KeyF23: "F23",
KeyF24: "F24",
KeyF25: "F25",
KeyF26: "F26",
KeyF27: "F27",
KeyF28: "F28",
KeyF29: "F29",
KeyF30: "F30",
KeyF31: "F31",
KeyF32: "F32",
KeyF33: "F33",
KeyF34: "F34",
KeyF35: "F35",
KeyF36: "F36",
KeyF37: "F37",
KeyF38: "F38",
KeyF39: "F39",
KeyF40: "F40",
KeyF41: "F41",
KeyF42: "F42",
KeyF43: "F43",
KeyF44: "F44",
KeyF45: "F45",
KeyF46: "F46",
KeyF47: "F47",
KeyF48: "F48",
KeyF49: "F49",
KeyF50: "F50",
KeyF51: "F51",
KeyF52: "F52",
KeyF53: "F53",
KeyF54: "F54",
KeyF55: "F55",
KeyF56: "F56",
KeyF57: "F57",
KeyF58: "F58",
KeyF59: "F59",
KeyF60: "F60",
KeyF61: "F61",
KeyF62: "F62",
KeyF63: "F63",
KeyF64: "F64",
KeyCtrlA: "Ctrl-A",
KeyCtrlB: "Ctrl-B",
KeyCtrlC: "Ctrl-C",
KeyCtrlD: "Ctrl-D",
KeyCtrlE: "Ctrl-E",
KeyCtrlF: "Ctrl-F",
KeyCtrlG: "Ctrl-G",
KeyCtrlJ: "Ctrl-J",
KeyCtrlK: "Ctrl-K",
KeyCtrlL: "Ctrl-L",
KeyCtrlN: "Ctrl-N",
KeyCtrlO: "Ctrl-O",
KeyCtrlP: "Ctrl-P",
KeyCtrlQ: "Ctrl-Q",
KeyCtrlR: "Ctrl-R",
KeyCtrlS: "Ctrl-S",
KeyCtrlT: "Ctrl-T",
KeyCtrlU: "Ctrl-U",
KeyCtrlV: "Ctrl-V",
KeyCtrlW: "Ctrl-W",
KeyCtrlX: "Ctrl-X",
KeyCtrlY: "Ctrl-Y",
KeyCtrlZ: "Ctrl-Z",
KeyCtrlSpace: "Ctrl-Space",
KeyCtrlUnderscore: "Ctrl-_",
KeyCtrlRightSq: "Ctrl-]",
KeyCtrlBackslash: "Ctrl-\\",
KeyCtrlCarat: "Ctrl-^",
}
// Name returns a printable value or the key stroke. This can be used // Name returns a printable value or the key stroke. This can be used
// when printing the event, for example. // when printing the event, for example.
func (ev *EventKey) Name() string { func (ev *EventKey) Name() string {
@ -96,84 +218,14 @@ func (ev *EventKey) Name() string {
m = append(m, "Ctrl") m = append(m, "Ctrl")
} }
switch ev.key { ok := false
case KeyRune: if s, ok = keyNames[ev.key]; !ok {
if ev.key == KeyRune {
s = "Rune[" + string(ev.ch) + "]" s = "Rune[" + string(ev.ch) + "]"
case KeySpace:
s = "Space"
case KeyEnter:
s = "Enter"
case KeyBackspace:
s = "Backspace"
case KeyTab:
s = "Tab"
case KeyBacktab:
s = "Backtab"
case KeyEsc:
s = "Esc"
case KeyBackspace2:
s = "Backspace2"
case KeyDelete:
s = "Delete"
case KeyInsert:
s = "Insert"
case KeyUp:
s = "Up"
case KeyDown:
s = "Down"
case KeyLeft:
s = "Left"
case KeyRight:
s = "Right"
case KeyHome:
s = "Home"
case KeyEnd:
s = "End"
case KeyUpLeft:
s = "UpLeft"
case KeyUpRight:
s = "UpRight"
case KeyDownLeft:
s = "DownLeft"
case KeyDownRight:
s = "DownRight"
case KeyCenter:
s = "Center"
case KeyPgDn:
s = "PgDn"
case KeyPgUp:
s = "PgUp"
case KeyClear:
s = "Clear"
case KeyExit:
s = "Exit"
case KeyCancel:
s = "Cancel"
case KeyPause:
s = "Pause"
case KeyPrint:
s = "Print"
case KeyCtrlSpace:
s = "Ctrl-Space"
case KeyCtrlUnderscore:
s = "Ctrl-_"
case KeyCtrlRightSq:
s = "Ctrl-]"
case KeyCtrlBackslash:
s = "Ctrl-\\"
case KeyCtrlCarat:
s = "Ctrl-^"
default:
if ev.key >= KeyF1 && ev.key <= KeyF64 {
s = fmt.Sprintf("F%d", int(ev.key-KeyF1)+1)
} else if ev.key >= KeyCtrlA && ev.key <= KeyCtrlZ {
s = fmt.Sprintf("Ctrl-%c",
rune(ev.key-KeyCtrlA)+'A')
} else { } else {
s = fmt.Sprintf("Key[%d,%d]", ev.key, int(ev.ch)) s = fmt.Sprintf("Key[%d,%d]", ev.key, int(ev.ch))
} }
} }
if len(m) != 0 { if len(m) != 0 {
if ev.mod&ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") { if ev.mod&ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") {
s = s[5:] s = s[5:]

View File

@ -226,11 +226,7 @@ func (s *simscreen) drawCell(x, y int) int {
l := utf8.EncodeRune(ubuf, r) l := utf8.EncodeRune(ubuf, r)
if enc := s.encoder; enc != nil { nout, _, _ = s.encoder.Transform(lbuf, ubuf[:l], true)
nout, _, _ = enc.Transform(lbuf, ubuf[:l], true)
} else {
nout = 0
}
if nout == 0 || lbuf[0] == '\x1a' { if nout == 0 || lbuf[0] == '\x1a' {
@ -398,46 +394,17 @@ outer:
continue continue
} }
switch s.charset {
case "UTF-8":
r, l := utf8.DecodeRune(b)
if r == utf8.RuneError && (l == 0 || l == 1) {
failed = true
// yank off one byte
b = b[1:]
} else {
b = b[l:]
ev := NewEventKey(KeyRune, r, ModNone)
s.PostEvent(ev)
continue
}
case "US-ASCII":
// ASCII cannot generate this, so most likely it was
// entered as an Alt sequence
ev := NewEventKey(KeyRune, rune(b[0]-128), ModAlt)
s.PostEvent(ev)
b = b[1:]
continue
default:
utfb := make([]byte, len(b)*4) // worst case utfb := make([]byte, len(b)*4) // worst case
dec := s.decoder
if dec == nil {
failed = true
b = b[1:]
continue
}
// take care to consume at *most* a single rune
for l := 1; l < len(b); l++ { for l := 1; l < len(b); l++ {
dec.Reset() s.decoder.Reset()
nout, nin, _ := dec.Transform(utfb, b[:l], true) nout, nin, _ := s.decoder.Transform(utfb, b[:l], true)
if nout != 0 { if nout != 0 {
r, _ := utf8.DecodeRune(utfb[:nout]) r, _ := utf8.DecodeRune(utfb[:nout])
if r != utf8.RuneError {
ev := NewEventKey(KeyRune, r, ModNone) ev := NewEventKey(KeyRune, r, ModNone)
s.PostEvent(ev) s.PostEvent(ev)
}
b = b[nin:] b = b[nin:]
continue outer continue outer
} }
@ -446,7 +413,6 @@ outer:
b = b[1:] b = b[1:]
continue continue
} }
}
return failed == false return failed == false
} }

View File

@ -77,7 +77,7 @@ func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) {
} else { } else {
bg = ColorDefault bg = ColorDefault
} }
attr = AttrMask(s) & AttrMaskAll attr = AttrMask(s) & attrAll
return fg, bg, attr return fg, bg, attr
} }
@ -91,7 +91,7 @@ func (s Style) setAttrs(attrs Style, on bool) Style {
// Normal returns the style with all attributes disabled. // Normal returns the style with all attributes disabled.
func (s Style) Normal() Style { func (s Style) Normal() Style {
return s &^ Style(AttrMaskAll) return s &^ Style(attrAll)
} }
// Bold returns a new style based on s, with the bold attribute set // Bold returns a new style based on s, with the bold attribute set

View File

@ -25,6 +25,7 @@ import (
var screen tcell.Screen var screen tcell.Screen
var outMode OutputMode var outMode OutputMode
// Init initializes the screen for use.
func Init() error { func Init() error {
outMode = OutputNormal outMode = OutputNormal
if s, e := tcell.NewScreen(); e != nil { if s, e := tcell.NewScreen(); e != nil {
@ -37,23 +38,28 @@ func Init() error {
} }
} }
// Close cleans up the terminal, restoring terminal modes, etc.
func Close() { func Close() {
screen.Fini() screen.Fini()
} }
// Flush updates the screen.
func Flush() error { func Flush() error {
screen.Show() screen.Show()
return nil return nil
} }
// SetCursor displays the terminal cursor at the given location.
func SetCursor(x, y int) { func SetCursor(x, y int) {
screen.ShowCursor(x, y) screen.ShowCursor(x, y)
} }
// HideCursor hides the terminal cursor.
func HideCursor() { func HideCursor() {
SetCursor(-1, -1) SetCursor(-1, -1)
} }
// Size returns the screen size as width, height in character cells.
func Size() (int, int) { func Size() (int, int) {
return screen.Size() return screen.Size()
} }
@ -122,18 +128,19 @@ func mkStyle(fg, bg Attribute) tcell.Style {
b = tcell.ColorDefault b = tcell.ColorDefault
} }
st = st.Foreground(f).Background(b) st = st.Foreground(f).Background(b)
if (fg&AttrBold != 0) || (bg&AttrBold != 0) { if (fg|bg)&AttrBold != 0 {
st = st.Bold(true) st = st.Bold(true)
} }
if (fg&AttrUnderline != 0) || (bg&AttrUnderline != 0) { if (fg|bg)&AttrUnderline != 0 {
st = st.Underline(true) st = st.Underline(true)
} }
if (fg&AttrReverse != 0) || (bg&AttrReverse != 0) { if (fg|bg)&AttrReverse != 0 {
st = st.Reverse(true) st = st.Reverse(true)
} }
return st return st
} }
// Clear clears the screen with the given attributes.
func Clear(fg, bg Attribute) { func Clear(fg, bg Attribute) {
st := mkStyle(fg, bg) st := mkStyle(fg, bg)
w, h := screen.Size() w, h := screen.Size()
@ -144,6 +151,7 @@ func Clear(fg, bg Attribute) {
} }
} }
// InputMode is not used.
type InputMode int type InputMode int
const ( const (
@ -153,11 +161,14 @@ const (
InputMouse InputMouse
) )
// SetInputMode does not do anything in this version.
func SetInputMode(mode InputMode) InputMode { func SetInputMode(mode InputMode) InputMode {
// We don't do anything else right now // We don't do anything else right now
return InputEsc return InputEsc
} }
// OutputMode represents an output mode, which determines how colors
// are used. See the termbox documentation for an explanation.
type OutputMode int type OutputMode int
const ( const (
@ -168,6 +179,7 @@ const (
OutputGrayscale OutputGrayscale
) )
// SetOutputMode is used to set the color palette used.
func SetOutputMode(mode OutputMode) OutputMode { func SetOutputMode(mode OutputMode) OutputMode {
if screen.Colors() < 256 { if screen.Colors() < 256 {
mode = OutputNormal mode = OutputNormal
@ -183,20 +195,29 @@ func SetOutputMode(mode OutputMode) OutputMode {
} }
} }
// Sync forces a resync of the screen.
func Sync() error { func Sync() error {
screen.Sync() screen.Sync()
return nil return nil
} }
// SetCell sets the character cell at a given location to the given
// content (rune) and attributes.
func SetCell(x, y int, ch rune, fg, bg Attribute) { func SetCell(x, y int, ch rune, fg, bg Attribute) {
st := mkStyle(fg, bg) st := mkStyle(fg, bg)
screen.SetContent(x, y, ch, nil, st) screen.SetContent(x, y, ch, nil, st)
} }
// EventType represents the type of event.
type EventType uint8 type EventType uint8
// Modifier represents the possible modifier keys.
type Modifier tcell.ModMask type Modifier tcell.ModMask
// Key is a key press.
type Key tcell.Key type Key tcell.Key
// Event represents an event like a key press, mouse action, or window resize.
type Event struct { type Event struct {
Type EventType Type EventType
Mod Modifier Mod Modifier
@ -309,25 +330,30 @@ func makeEvent(tev tcell.Event) Event {
} }
} }
// ParseEvent is not supported.
func ParseEvent(data []byte) Event { func ParseEvent(data []byte) Event {
// Not supported // Not supported
return Event{Type: EventError, Err: errors.New("no raw events")} return Event{Type: EventError, Err: errors.New("no raw events")}
} }
// PollRawEvent is not supported.
func PollRawEvent(data []byte) Event { func PollRawEvent(data []byte) Event {
// Not supported // Not supported
return Event{Type: EventError, Err: errors.New("no raw events")} return Event{Type: EventError, Err: errors.New("no raw events")}
} }
// PollEvent blocks until an event is ready, and then returns it.
func PollEvent() Event { func PollEvent() Event {
ev := screen.PollEvent() ev := screen.PollEvent()
return makeEvent(ev) return makeEvent(ev)
} }
// Interrupt posts an interrupt event.
func Interrupt() { func Interrupt() {
screen.PostEvent(tcell.NewEventInterrupt(nil)) screen.PostEvent(tcell.NewEventInterrupt(nil))
} }
// Cell represents a single character cell on screen.
type Cell struct { type Cell struct {
Ch rune Ch rune
Fg Attribute Fg Attribute

View File

@ -97,7 +97,7 @@ func (t *tScreen) Init() error {
t.indoneq = make(chan struct{}) t.indoneq = make(chan struct{})
t.charset = "UTF-8" t.charset = "UTF-8"
t.charset = GetCharset() t.charset = getCharset()
if enc := GetEncoding(t.charset); enc != nil { if enc := GetEncoding(t.charset); enc != nil {
t.encoder = enc.NewEncoder() t.encoder = enc.NewEncoder()
t.decoder = enc.NewDecoder() t.decoder = enc.NewDecoder()
@ -679,6 +679,23 @@ func (t *tScreen) PostEvent(ev Event) error {
} }
} }
func (t *tScreen) clip(x, y int) (int, int) {
w, h := t.cells.Size()
if x < 0 {
x = 0
}
if y < 0 {
y = 0
}
if x > w-1 {
x = w - 1
}
if y > h-1 {
y = h - 1
}
return x, y
}
func (t *tScreen) postMouseEvent(x, y, btn int) { func (t *tScreen) postMouseEvent(x, y, btn int) {
// XTerm mouse events only report at most one button at a time, // XTerm mouse events only report at most one button at a time,
@ -733,19 +750,8 @@ func (t *tScreen) postMouseEvent(x, y, btn int) {
// Some terminals will report mouse coordinates outside the // Some terminals will report mouse coordinates outside the
// screen, especially with click-drag events. Clip the coordinates // screen, especially with click-drag events. Clip the coordinates
// to the screen in that case. // to the screen in that case.
if x < 0 { x, y = t.clip(x, y)
x = 0
}
if y < 0 {
y = 0
}
w, h := t.cells.Size()
if x > w-1 {
x = w - 1
}
if y > h-1 {
y = h - 1
}
ev := NewEventMouse(x, y, button, mod) ev := NewEventMouse(x, y, button, mod)
t.PostEvent(ev) t.PostEvent(ev)
} }

View File

@ -40,37 +40,22 @@ type boxLayoutCell struct {
view *ViewPort view *ViewPort
} }
func (b *BoxLayout) layout() { func (b *BoxLayout) hLayout() {
if b.view == nil {
return
}
w, h := b.view.Size() w, h := b.view.Size()
minx, miny, totx, toty := 0, 0, 0, 0
totf := 0.0 totf := 0.0
for _, c := range b.cells { for _, c := range b.cells {
x, y := c.widget.Size() x, y := c.widget.Size()
totx += x
toty += y
totf += c.fill totf += c.fill
if x > minx { b.width += x
minx = x if y > b.height {
} b.height = y
if y > miny {
miny = y
} }
c.pad = 0
c.frac = 0
} }
extra := 0 extra := w - b.width
if b.orient == Horizontal {
extra = w - totx
b.width = totx
b.height = miny
} else {
extra = h - toty
b.width = minx
b.height = toty
}
if extra < 0 { if extra < 0 {
extra = 0 extra = 0
} }
@ -85,9 +70,6 @@ func (b *BoxLayout) layout() {
c.pad = int(c.frac) c.pad = int(c.frac)
c.frac -= float64(c.pad) c.frac -= float64(c.pad)
resid -= c.pad resid -= c.pad
} else {
c.pad = 0
c.frac = 0
} }
} }
@ -109,28 +91,93 @@ func (b *BoxLayout) layout() {
resid-- resid--
} }
x, y, xinc, yinc := 0, 0, 0, 0 x, y, xinc := 0, 0, 0
for _, c := range b.cells { for _, c := range b.cells {
cw, ch := c.widget.Size() cw, _ := c.widget.Size()
switch b.orient {
case Horizontal:
xinc = cw + c.pad xinc = cw + c.pad
cw += c.pad cw += c.pad
ch = h
case Vertical: c.view.Resize(x, y, cw, h)
x += xinc
}
}
func (b *BoxLayout) vLayout() {
w, h := b.view.Size()
totf := 0.0
for _, c := range b.cells {
x, y := c.widget.Size()
b.height += y
totf += c.fill
if x > b.width {
b.width = x
}
c.pad = 0
c.frac = 0
}
extra := h - b.height
if extra < 0 {
extra = 0
}
resid := extra
if totf == 0 {
resid = 0
}
for _, c := range b.cells {
if c.fill > 0 {
c.frac = float64(extra) * c.fill / totf
c.pad = int(c.frac)
c.frac -= float64(c.pad)
resid -= c.pad
}
}
// Distribute any left over padding. We try to give it to the
// the cells with the highest residual fraction. It should be
// the case that no single cell gets more than one more cell.
for resid > 0 {
var best *boxLayoutCell
for _, c := range b.cells {
if c.fill == 0 {
continue
}
if best == nil || c.frac > best.frac {
best = c
}
}
best.pad++
best.frac = 0
resid--
}
x, y, yinc := 0, 0, 0
for _, c := range b.cells {
_, ch := c.widget.Size()
yinc = ch + c.pad yinc = ch + c.pad
ch += c.pad ch += c.pad
cw = w c.view.Resize(x, y, w, ch)
y += yinc
}
}
func (b *BoxLayout) layout() {
if b.view == nil {
return
}
b.width, b.height = 0, 0
switch b.orient {
case Horizontal:
b.hLayout()
case Vertical:
b.vLayout()
default: default:
panic("Bad orientation") panic("Bad orientation")
}
c.view.Resize(x, y, cw, ch)
x += xinc
y += yinc
} }
b.changed = false b.changed = false
} }

View File

@ -36,40 +36,63 @@ type Text struct {
WidgetWatchers WidgetWatchers
} }
func (t *Text) clear() {
v := t.view
w, h := v.Size()
v.Clear()
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
v.SetContent(x, y, ' ', nil, t.style)
}
}
}
// calcY figures the initial Y offset. Alignment is top by default.
func (t *Text) calcY(height int) int {
if t.align&VAlignCenter != 0 {
return (height - len(t.lengths)) / 2
}
if t.align&VAlignBottom != 0 {
return height - len(t.lengths)
}
return 0
}
// calcX figures the initial X offset for the given line.
// Alignment is left by default.
func (t *Text) calcX(width, line int) int {
if t.align&HAlignCenter != 0 {
return (width - t.lengths[line]) / 2
}
if t.align&HAlignRight != 0 {
return width - t.lengths[line]
}
return 0
}
// Draw draws the Text. // Draw draws the Text.
func (t *Text) Draw() { func (t *Text) Draw() {
v := t.view v := t.view
if v == nil { if v == nil {
return return
} }
var x, y int
width, height := v.Size() width, height := v.Size()
if width == 0 || height == 0 { if width == 0 || height == 0 {
return return
} }
v.Clear() t.clear()
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
v.SetContent(x, y, ' ', nil, t.style)
}
}
// Note that we might wind up with a negative X if the width // Note that we might wind up with a negative X if the width
// is larger than the length. That's OK, and correct even. // is larger than the length. That's OK, and correct even.
// The view will clip it properly in that case. // The view will clip it properly in that case.
// We align to the left & top by default. // We align to the left & top by default.
if t.align&VAlignCenter != 0 { y := t.calcY(height)
y = (height - 1) / 2
} else if t.align&VAlignBottom != 0 {
y = height - 1
} else {
y = 0
}
r := rune(0) r := rune(0)
w := 0 w := 0
x := 0
var styl tcell.Style var styl tcell.Style
var comb []rune var comb []rune
line := 0 line := 0
@ -77,13 +100,7 @@ func (t *Text) Draw() {
for i, l := range t.text { for i, l := range t.text {
if newline { if newline {
if t.align&HAlignCenter != 0 { x = t.calcX(width, line)
x = (width - t.lengths[line]) / 2
} else if t.align&HAlignRight != 0 {
x = width - t.lengths[line]
} else {
x = 0
}
newline = false newline = false
} }
if l == '\n' { if l == '\n' {