gocui/gui.go

668 lines
16 KiB
Go
Raw Normal View History

// Copyright 2014 The gocui Authors. All rights reserved.
2014-01-14 20:11:12 +01:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2013-12-27 21:36:26 +01:00
package gocui
import (
"errors"
2013-12-27 22:02:56 +01:00
2013-12-31 21:23:28 +01:00
"github.com/nsf/termbox-go"
2013-12-27 21:36:26 +01:00
)
2016-01-26 00:48:12 +01:00
// Handler represents a handler that can be used to update or modify the GUI.
type Handler func(*Gui) error
// userEvent represents an event triggered by the user.
type userEvent struct {
2016-01-26 00:48:12 +01:00
h Handler
}
var (
// ErrQuit is used to decide if the MainLoop finished successfully.
ErrQuit = errors.New("quit")
2014-01-19 17:03:52 +01:00
// ErrUnknownView allows to assert if a View must be initialized.
ErrUnknownView = errors.New("unknown view")
)
2013-12-27 21:36:26 +01:00
2014-01-19 17:03:52 +01:00
// Gui represents the whole User Interface, including the views, layouts
// and keybindings.
2013-12-27 21:36:26 +01:00
type Gui struct {
tbEvents chan termbox.Event
userEvents chan userEvent
2014-01-16 23:01:53 +01:00
views []*View
currentView *View
2016-01-26 00:48:12 +01:00
layout Handler
2014-01-16 23:01:53 +01:00
keybindings []*keybinding
maxX, maxY int
// BgColor and FgColor allow to configure the background and foreground
// colors of the GUI.
BgColor, FgColor Attribute
// SelBgColor and SelFgColor allow to configure the background and foreground
// colors of the current view.
2014-01-10 12:38:08 +01:00
SelBgColor, SelFgColor Attribute
// If Cursor is true then the cursor is enabled.
Cursor bool
2016-01-23 17:18:30 +01:00
// If Mouse is true then mouse events will be enabled.
Mouse bool
2016-08-30 22:15:03 +02:00
// If InputEsc is true, when ESC sequence is in the buffer and it doesn't
// match any known sequence, ESC means KeyEsc.
InputEsc bool
// Editor allows to define the editor that manages the edition mode,
// including keybindings or cursor behaviour. DefaultEditor is used by
// default.
Editor Editor
2013-12-27 21:36:26 +01:00
}
2014-01-19 17:03:52 +01:00
// NewGui returns a new Gui object.
func NewGui() *Gui {
2013-12-27 21:36:26 +01:00
return &Gui{}
}
2014-01-19 17:03:52 +01:00
// Init initializes the library. This function must be called before
// any other functions.
func (g *Gui) Init() error {
if err := termbox.Init(); err != nil {
2013-12-31 21:23:28 +01:00
return err
}
g.tbEvents = make(chan termbox.Event, 20)
g.userEvents = make(chan userEvent, 20)
2013-12-31 21:23:28 +01:00
g.maxX, g.maxY = termbox.Size()
2016-10-18 00:24:16 +02:00
g.BgColor, g.FgColor = ColorBlack, ColorWhite
g.SelBgColor, g.SelFgColor = ColorBlack, ColorWhite
g.Editor = DefaultEditor
2013-12-31 21:23:28 +01:00
return nil
2013-12-27 21:36:26 +01:00
}
2014-01-19 17:03:52 +01:00
// Close finalizes the library. It should be called after a successful
// initialization and when gocui is not needed anymore.
2013-12-27 21:36:26 +01:00
func (g *Gui) Close() {
termbox.Close()
}
2014-01-19 17:03:52 +01:00
// Size returns the terminal's size.
2013-12-27 21:36:26 +01:00
func (g *Gui) Size() (x, y int) {
2013-12-31 21:23:28 +01:00
return g.maxX, g.maxY
2013-12-27 21:36:26 +01:00
}
2014-01-19 17:03:52 +01:00
// SetRune writes a rune at the given point, relative to the top-left
// corner of the terminal. It checks if the position is valid and applies
2016-10-18 00:24:16 +02:00
// the given colors.
func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
2014-01-02 18:34:58 +01:00
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
2014-01-04 02:50:49 +01:00
return errors.New("invalid point")
2014-01-02 18:34:58 +01:00
}
2016-10-18 00:24:16 +02:00
termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
return nil
}
2014-01-19 17:03:52 +01:00
// Rune returns the rune contained in the cell at the given position.
// It checks if the position is valid.
2014-01-16 23:01:53 +01:00
func (g *Gui) Rune(x, y int) (rune, error) {
2014-01-02 18:34:58 +01:00
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
2014-01-20 23:28:08 +01:00
return ' ', errors.New("invalid point")
2014-01-02 18:34:58 +01:00
}
c := termbox.CellBuffer()[y*g.maxX+x]
return c.Ch, nil
}
2014-01-19 17:03:52 +01:00
// SetView creates a new view with its top-left corner at (x0, y0)
// and the bottom-right one at (x1, y1). If a view with the same name
// already exists, its dimensions are updated; otherwise, the error
// ErrUnknownView is returned, which allows to assert if the View must
2014-01-19 17:03:52 +01:00
// be initialized. It checks if the position is valid.
func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) {
2014-01-04 02:50:49 +01:00
if x0 >= x1 || y0 >= y1 {
return nil, errors.New("invalid dimensions")
2013-12-27 21:36:26 +01:00
}
if name == "" {
return nil, errors.New("invalid name")
}
if v, err := g.View(name); err == nil {
2014-01-16 23:01:53 +01:00
v.x0 = x0
v.y0 = y0
v.x1 = x1
v.y1 = y1
v.tainted = true
2014-01-04 02:50:49 +01:00
return v, nil
}
v := newView(name, x0, y0, x1, y1)
2014-05-03 15:20:46 +02:00
v.BgColor, v.FgColor = g.BgColor, g.FgColor
v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor
2013-12-27 21:36:26 +01:00
g.views = append(g.views, v)
return v, ErrUnknownView
2013-12-27 21:36:26 +01:00
}
// SetViewOnTop sets the given view on top of the existing ones.
func (g *Gui) SetViewOnTop(name string) (*View, error) {
for i, v := range g.views {
if v.name == name {
s := append(g.views[:i], g.views[i+1:]...)
g.views = append(s, v)
return v, nil
}
}
return nil, ErrUnknownView
}
// View returns a pointer to the view with the given name, or error
// ErrUnknownView if a view with that name does not exist.
func (g *Gui) View(name string) (*View, error) {
2014-01-04 03:40:45 +01:00
for _, v := range g.views {
2014-01-16 23:01:53 +01:00
if v.name == name {
return v, nil
2014-01-04 02:50:49 +01:00
}
}
return nil, ErrUnknownView
}
// ViewByPosition returns a pointer to a view matching the given position, or
// error ErrUnknownView if a view in that position does not exist.
func (g *Gui) ViewByPosition(x, y int) (*View, error) {
for _, v := range g.views {
2016-01-26 09:40:06 +01:00
if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
return v, nil
}
}
return nil, ErrUnknownView
}
// ViewPosition returns the coordinates of the view with the given name, or
// error ErrUnknownView if a view with that name does not exist.
func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) {
for _, v := range g.views {
if v.name == name {
return v.x0, v.y0, v.x1, v.y1, nil
}
}
return 0, 0, 0, 0, ErrUnknownView
2014-01-04 02:50:49 +01:00
}
2014-01-19 17:03:52 +01:00
// DeleteView deletes a view by name.
func (g *Gui) DeleteView(name string) error {
2014-01-04 03:40:45 +01:00
for i, v := range g.views {
2014-01-16 23:01:53 +01:00
if v.name == name {
2014-01-04 03:40:45 +01:00
g.views = append(g.views[:i], g.views[i+1:]...)
return nil
}
}
return ErrUnknownView
}
2014-01-19 17:03:52 +01:00
// SetCurrentView gives the focus to a given view.
2016-10-18 00:24:16 +02:00
func (g *Gui) SetCurrentView(name string) (*View, error) {
for _, v := range g.views {
2014-01-16 23:01:53 +01:00
if v.name == name {
g.currentView = v
2016-10-18 00:24:16 +02:00
return v, nil
}
}
2016-10-18 00:24:16 +02:00
return nil, ErrUnknownView
}
// CurrentView returns the currently focused view, or nil if no view
// owns the focus.
func (g *Gui) CurrentView() *View {
return g.currentView
}
2014-01-19 17:03:52 +01:00
// SetKeybinding creates a new keybinding. If viewname equals to ""
// (empty string) then the keybinding will apply to all views. key must
// be a rune or a Key.
func (g *Gui) SetKeybinding(viewname string, key interface{}, mod Modifier, h KeybindingHandler) error {
2014-01-11 20:29:16 +01:00
var kb *keybinding
2016-08-30 23:29:55 +02:00
k, ch, err := getKey(key)
if err != nil {
return err
}
2016-08-30 23:29:55 +02:00
kb = newKeybinding(viewname, k, ch, mod, h)
g.keybindings = append(g.keybindings, kb)
return nil
2014-01-04 03:40:45 +01:00
}
2016-08-30 23:10:07 +02:00
// DeleteKeybinding deletes a keybinding.
func (g *Gui) DeleteKeybinding(viewname string, key interface{}, mod Modifier) error {
2016-08-30 23:29:55 +02:00
k, ch, err := getKey(key)
if err != nil {
return err
2016-08-30 23:10:07 +02:00
}
for i, kb := range g.keybindings {
if kb.viewName == viewname && kb.ch == ch && kb.key == k && kb.mod == mod {
g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...)
return nil
}
}
return errors.New("keybinding not found")
}
2016-10-03 11:10:50 +02:00
// DeleteKeybindings deletes all keybindings of view.
func (g *Gui) DeleteKeybindings(viewname string) {
2016-10-03 11:10:50 +02:00
var s []*keybinding
for _, kb := range g.keybindings {
if kb.viewName != viewname {
2016-10-03 11:10:50 +02:00
s = append(s, kb)
}
}
2016-10-03 11:10:50 +02:00
g.keybindings = s
}
2016-08-30 23:29:55 +02:00
// getKey takes an empty interface with a key and returns the corresponding
// typed Key or rune.
func getKey(key interface{}) (Key, rune, error) {
switch t := key.(type) {
case Key:
return t, 0, nil
case rune:
return 0, t, nil
default:
return 0, 0, errors.New("unknown type")
}
}
// Execute executes the given handler. This function can be called safely from
// a goroutine in order to update the GUI. It is important to note that it
// won't be executed immediately, instead it will be added to the user events
// queue.
2016-01-26 00:48:12 +01:00
func (g *Gui) Execute(h Handler) {
go func() { g.userEvents <- userEvent{h: h} }()
}
2014-01-19 17:03:52 +01:00
// SetLayout sets the current layout. A layout is a function that
// will be called every time the gui is redrawn, it must contain
2014-01-19 17:03:52 +01:00
// the base views and its initializations.
2016-01-26 00:48:12 +01:00
func (g *Gui) SetLayout(layout Handler) {
g.layout = layout
g.currentView = nil
g.views = nil
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
}
2014-01-19 17:03:52 +01:00
// MainLoop runs the main loop until an error is returned. A successful
// finish should return ErrQuit.
func (g *Gui) MainLoop() error {
2013-12-27 21:36:26 +01:00
go func() {
for {
g.tbEvents <- termbox.PollEvent()
2013-12-27 21:36:26 +01:00
}
}()
inputMode := termbox.InputAlt
if g.InputEsc {
inputMode = termbox.InputEsc
}
2016-01-23 17:18:30 +01:00
if g.Mouse {
inputMode |= termbox.InputMouse
}
termbox.SetInputMode(inputMode)
if err := g.flush(); err != nil {
2013-12-27 21:36:26 +01:00
return err
}
for {
select {
case ev := <-g.tbEvents:
if err := g.handleEvent(&ev); err != nil {
return err
}
case ev := <-g.userEvents:
if err := ev.h(g); err != nil {
return err
}
2014-01-10 12:38:08 +01:00
}
if err := g.consumeevents(); err != nil {
return err
}
if err := g.flush(); err != nil {
2014-01-10 12:38:08 +01:00
return err
2013-12-27 21:36:26 +01:00
}
}
}
2014-01-19 17:03:52 +01:00
// consumeevents handles the remaining events in the events pool.
func (g *Gui) consumeevents() error {
2013-12-27 21:36:26 +01:00
for {
select {
case ev := <-g.tbEvents:
2013-12-27 21:36:26 +01:00
if err := g.handleEvent(&ev); err != nil {
return err
}
case ev := <-g.userEvents:
if err := ev.h(g); err != nil {
return err
}
2013-12-27 21:36:26 +01:00
default:
return nil
}
}
}
2014-01-19 17:03:52 +01:00
// handleEvent handles an event, based on its type (key-press, error,
// etc.)
func (g *Gui) handleEvent(ev *termbox.Event) error {
2013-12-27 21:36:26 +01:00
switch ev.Type {
case termbox.EventKey, termbox.EventMouse:
2013-12-27 21:36:26 +01:00
return g.onKey(ev)
case termbox.EventError:
return ev.Err
default:
return nil
}
}
// flush updates the gui, re-drawing frames and buffers.
func (g *Gui) flush() error {
if g.layout == nil {
2014-01-04 02:50:49 +01:00
return errors.New("Null layout")
}
2014-01-10 12:38:08 +01:00
termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor))
maxX, maxY := termbox.Size()
// if GUI's size has changed, we need to redraw all views
if maxX != g.maxX || maxY != g.maxY {
for _, v := range g.views {
v.tainted = true
}
}
g.maxX, g.maxY = maxX, maxY
if err := g.layout(g); err != nil {
return err
}
2014-01-18 13:47:08 +01:00
for _, v := range g.views {
2014-10-18 15:47:24 +02:00
if v.Frame {
var fgColor, bgColor Attribute
if v == g.currentView {
fgColor = g.SelFgColor
bgColor = g.SelBgColor
} else {
fgColor = g.FgColor
bgColor = g.BgColor
}
2016-10-18 00:24:16 +02:00
if err := g.drawFrame(v, fgColor, bgColor); err != nil {
2014-10-17 17:22:28 -04:00
return err
}
2016-10-18 00:24:16 +02:00
if err := g.drawCorners(v, fgColor, bgColor); err != nil {
2016-10-04 00:10:07 +02:00
return err
}
if v.Title != "" {
2016-10-18 00:24:16 +02:00
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
return err
}
}
2014-01-18 13:47:08 +01:00
}
if err := g.draw(v); err != nil {
return err
}
}
2013-12-30 14:19:22 +01:00
if err := g.drawIntersections(); err != nil {
return err
}
2014-01-18 13:47:08 +01:00
termbox.Flush()
return nil
}
2014-01-19 17:03:52 +01:00
// drawFrame draws the horizontal and vertical edges of a view.
2016-10-18 00:24:16 +02:00
func (g *Gui) drawFrame(v *View, fgColor, bgColor Attribute) error {
2014-01-18 13:47:08 +01:00
for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ {
if x < 0 {
continue
}
if v.y0 > -1 && v.y0 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(x, v.y0, '─', fgColor, bgColor); err != nil {
2014-01-18 13:47:08 +01:00
return err
}
}
2014-01-18 13:47:08 +01:00
if v.y1 > -1 && v.y1 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(x, v.y1, '─', fgColor, bgColor); err != nil {
2014-01-18 13:47:08 +01:00
return err
}
2014-01-18 13:47:08 +01:00
}
}
for y := v.y0 + 1; y < v.y1 && y < g.maxY; y++ {
if y < 0 {
continue
}
if v.x0 > -1 && v.x0 < g.maxX {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x0, y, '│', fgColor, bgColor); err != nil {
2014-01-18 13:47:08 +01:00
return err
2014-01-02 18:34:58 +01:00
}
2014-01-18 13:47:08 +01:00
}
if v.x1 > -1 && v.x1 < g.maxX {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x1, y, '│', fgColor, bgColor); err != nil {
2014-01-18 13:47:08 +01:00
return err
}
2013-12-27 21:36:26 +01:00
}
}
return nil
}
2016-10-04 00:10:07 +02:00
// drawCorners draws the corners of the view.
2016-10-18 00:24:16 +02:00
func (g *Gui) drawCorners(v *View, fgColor, bgColor Attribute) error {
2016-10-04 00:10:07 +02:00
if v.x0 >= 0 && v.y0 >= 0 && v.x0 < g.maxX && v.y0 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x0, v.y0, '┌', fgColor, bgColor); err != nil {
2016-10-04 00:10:07 +02:00
return err
}
}
if v.x1 >= 0 && v.y0 >= 0 && v.x1 < g.maxX && v.y0 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x1, v.y0, '┐', fgColor, bgColor); err != nil {
2016-10-04 00:10:07 +02:00
return err
}
}
if v.x0 >= 0 && v.y1 >= 0 && v.x0 < g.maxX && v.y1 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x0, v.y1, '└', fgColor, bgColor); err != nil {
2016-10-04 00:10:07 +02:00
return err
}
}
if v.x1 >= 0 && v.y1 >= 0 && v.x1 < g.maxX && v.y1 < g.maxY {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x1, v.y1, '┘', fgColor, bgColor); err != nil {
2016-10-04 00:10:07 +02:00
return err
}
}
return nil
}
// drawTitle draws the title of the view.
2016-10-18 00:24:16 +02:00
func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
if v.y0 < 0 || v.y0 >= g.maxY {
return nil
}
for i, ch := range v.Title {
x := v.x0 + i + 2
if x < 0 {
continue
} else if x > v.x1-2 || x >= g.maxX {
break
}
2016-10-18 00:24:16 +02:00
if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
return err
}
}
return nil
}
2014-01-19 17:03:52 +01:00
// draw manages the cursor and calls the draw function of a view.
func (g *Gui) draw(v *View) error {
if g.Cursor {
2014-01-19 17:03:52 +01:00
if v := g.currentView; v != nil {
vMaxX, vMaxY := v.Size()
if v.cx < 0 {
v.cx = 0
} else if v.cx >= vMaxX {
v.cx = vMaxX - 1
2014-01-19 17:03:52 +01:00
}
if v.cy < 0 {
v.cy = 0
} else if v.cy >= vMaxY {
v.cy = vMaxY - 1
}
gMaxX, gMaxY := g.Size()
cx, cy := v.x0+v.cx+1, v.y0+v.cy+1
if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY {
termbox.SetCursor(cx, cy)
} else {
termbox.HideCursor()
2014-01-19 17:03:52 +01:00
}
}
} else {
termbox.HideCursor()
}
v.clearRunes()
if err := v.draw(); err != nil {
return err
}
return nil
}
// drawIntersections draws the corners of each view, based on the type
// of the edges that converge at these points.
func (g *Gui) drawIntersections() error {
for _, v := range g.views {
2016-10-18 00:24:16 +02:00
if !v.Frame {
continue
}
2016-10-18 00:24:16 +02:00
var fgColor, bgColor Attribute
if v == g.currentView {
fgColor = g.SelFgColor
bgColor = g.SelBgColor
} else {
fgColor = g.FgColor
bgColor = g.BgColor
2016-10-18 00:24:16 +02:00
}
2014-01-16 23:01:53 +01:00
if ch, ok := g.intersectionRune(v.x0, v.y0); ok {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x0, v.y0, ch, fgColor, bgColor); err != nil {
2014-01-02 18:34:58 +01:00
return err
}
}
2014-01-16 23:01:53 +01:00
if ch, ok := g.intersectionRune(v.x0, v.y1); ok {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x0, v.y1, ch, fgColor, bgColor); err != nil {
2014-01-02 18:34:58 +01:00
return err
}
}
2014-01-16 23:01:53 +01:00
if ch, ok := g.intersectionRune(v.x1, v.y0); ok {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x1, v.y0, ch, fgColor, bgColor); err != nil {
2014-01-02 18:34:58 +01:00
return err
}
}
2014-01-16 23:01:53 +01:00
if ch, ok := g.intersectionRune(v.x1, v.y1); ok {
2016-10-18 00:24:16 +02:00
if err := g.SetRune(v.x1, v.y1, ch, fgColor, bgColor); err != nil {
2014-01-02 18:34:58 +01:00
return err
}
}
}
return nil
}
2014-01-19 17:03:52 +01:00
// intersectionRune returns the correct intersection rune at a given
// point.
2014-01-16 23:01:53 +01:00
func (g *Gui) intersectionRune(x, y int) (rune, bool) {
2013-12-31 21:23:28 +01:00
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
2014-01-20 23:28:08 +01:00
return ' ', false
2013-12-30 14:19:22 +01:00
}
2014-01-16 23:01:53 +01:00
chTop, _ := g.Rune(x, y-1)
2013-12-31 01:37:34 +01:00
top := verticalRune(chTop)
2014-01-16 23:01:53 +01:00
chBottom, _ := g.Rune(x, y+1)
2013-12-31 01:37:34 +01:00
bottom := verticalRune(chBottom)
2014-01-16 23:01:53 +01:00
chLeft, _ := g.Rune(x-1, y)
2013-12-31 01:37:34 +01:00
left := horizontalRune(chLeft)
2014-01-16 23:01:53 +01:00
chRight, _ := g.Rune(x+1, y)
2013-12-31 01:37:34 +01:00
right := horizontalRune(chRight)
2013-12-30 14:19:22 +01:00
var ch rune
2013-12-30 14:19:22 +01:00
switch {
2013-12-31 01:37:34 +01:00
case top && bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┼'
2013-12-31 01:37:34 +01:00
case top && bottom && !left && right:
2013-12-30 14:45:02 +01:00
ch = '├'
2013-12-31 01:37:34 +01:00
case top && bottom && left && !right:
2013-12-30 14:45:02 +01:00
ch = '┤'
2013-12-31 01:37:34 +01:00
case !top && bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┬'
2013-12-31 01:37:34 +01:00
case top && !bottom && left && right:
2013-12-30 14:45:02 +01:00
ch = '┴'
2013-12-30 14:19:22 +01:00
default:
2014-01-20 23:28:08 +01:00
return ' ', false
2013-12-30 14:19:22 +01:00
}
return ch, true
}
2014-01-19 17:03:52 +01:00
// verticalRune returns if the given character is a vertical rune.
2013-12-30 14:37:27 +01:00
func verticalRune(ch rune) bool {
2013-12-30 14:45:02 +01:00
if ch == '│' || ch == '┼' || ch == '├' || ch == '┤' {
2013-12-30 14:37:27 +01:00
return true
}
return false
}
2014-01-19 17:03:52 +01:00
// verticalRune returns if the given character is a horizontal rune.
2013-12-30 14:37:27 +01:00
func horizontalRune(ch rune) bool {
2013-12-30 14:45:02 +01:00
if ch == '─' || ch == '┼' || ch == '┬' || ch == '┴' {
2013-12-30 14:37:27 +01:00
return true
}
return false
}
2014-01-19 17:03:52 +01:00
// onKey manages key-press events. A keybinding handler is called when
// a key-press or mouse event satisfies a configured keybinding. Furthermore,
2014-01-20 23:10:16 +01:00
// currentView's internal buffer is modified if currentView.Editable is true.
func (g *Gui) onKey(ev *termbox.Event) error {
switch ev.Type {
case termbox.EventKey:
if err := g.execKeybindings(g.currentView, ev); err != nil {
return err
}
if g.currentView != nil && g.currentView.Editable && g.Editor != nil {
g.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod))
}
case termbox.EventMouse:
2016-01-26 09:40:06 +01:00
mx, my := ev.MouseX, ev.MouseY
v, err := g.ViewByPosition(mx, my)
if err != nil {
break
}
2016-01-26 09:40:06 +01:00
if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil {
return err
}
2016-10-11 08:34:27 +02:00
if err := g.execKeybindings(v, ev); err != nil {
return err
}
2015-02-13 21:02:56 +01:00
}
return nil
}
// execKeybindings executes the keybinding handlers that match the passed view
// and event.
func (g *Gui) execKeybindings(v *View, ev *termbox.Event) error {
for _, kb := range g.keybindings {
2015-02-13 21:02:56 +01:00
if kb.h == nil {
continue
}
if kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) && kb.matchView(v) {
if err := kb.h(g, v); err != nil {
2015-02-13 21:02:56 +01:00
return err
}
}
2013-12-27 21:36:26 +01:00
}
return nil
2013-12-27 21:36:26 +01:00
}