Add attribute ActiveColor to configure the color of the current view

This commit is contained in:
Henri Koski 2016-10-17 16:50:03 +03:00 committed by Roi Martin
parent ec14132e67
commit 7779534f95
3 changed files with 179 additions and 19 deletions

112
_examples/active.go Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"log"
"github.com/jroimartin/gocui"
)
const LEN = 4
var (
viewArr = [LEN]string{"v1", "v2", "v3", "v4"}
active = 0
)
func nextView(g *gocui.Gui, v *gocui.View) error {
nextIndex := (active + 1) % LEN
name := viewArr[nextIndex]
out, _ := g.View("v2")
fmt.Fprintln(out, "Going from view "+v.Name()+" to "+name)
if err := g.SetCurrentViewOnTop(name); err != nil {
fmt.Fprintln(out, err)
return err
}
if nextIndex == 0 || nextIndex == 3 {
g.Cursor = true
} else {
g.Cursor = false
}
active = nextIndex
return nil
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("v1", 0, 0, maxX/2-1, maxY/2-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "v1 (editable)"
v.Editable = true
v.Wrap = true
}
if v, err := g.SetView("v2", maxX/2-1, 0, maxX-1, maxY/2-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "v2"
v.Editable = false
v.Wrap = true
v.Autoscroll = true
v.ActiveColor = gocui.ColorRed
}
if v, err := g.SetView("v3", 0, maxY/2-1, maxX/2-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "v3"
v.Write([]byte("Press TAB to change current view"))
v.Wrap = true
v.Editable = false
v.Autoscroll = true
v.ActiveColor = gocui.ColorRed
}
if v, err := g.SetView("v4", maxX/2, maxY/2, maxX-1, maxY-1); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "v4 (editable)"
v.Editable = true
if err = g.SetCurrentViewOnTop("v1"); err != nil {
return err
}
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func main() {
g := gocui.NewGui()
if err := g.Init(); err != nil {
log.Panicln(err)
}
defer g.Close()
g.ActiveColor = gocui.ColorGreen
g.SetLayout(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}
if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
log.Panicln(err)
}
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
}
}

82
gui.go
View File

@ -41,6 +41,9 @@ type Gui struct {
// colors of the GUI.
BgColor, FgColor Attribute
// ActiveColor allow to configure color of currentWiew frame.
ActiveColor Attribute
// SelBgColor and SelFgColor are used to configure the background and
// foreground colors of the selected line, when it is highlighted.
SelBgColor, SelFgColor Attribute
@ -77,6 +80,7 @@ func (g *Gui) Init() error {
g.maxX, g.maxY = termbox.Size()
g.BgColor = ColorBlack
g.FgColor = ColorWhite
g.ActiveColor = ColorWhite
g.Editor = DefaultEditor
return nil
}
@ -103,6 +107,17 @@ func (g *Gui) SetRune(x, y int, ch rune) error {
return nil
}
// SetRuneWithColor writes a rune at the given point, relative to the top-left
// corner of the terminal with given colors. It checks if the position is
// valid and applies the colors.
func (g *Gui) SetRuneWithColor(x, y int, ch rune, fg, bg Attribute) error {
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
return errors.New("invalid point")
}
termbox.SetCell(x, y, ch, termbox.Attribute(fg), termbox.Attribute(bg))
return nil
}
// Rune returns the rune contained in the cell at the given position.
// It checks if the position is valid.
func (g *Gui) Rune(x, y int) (rune, error) {
@ -137,6 +152,7 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int) (*View, error) {
v := newView(name, x0, y0, x1, y1)
v.BgColor, v.FgColor = g.BgColor, g.FgColor
v.ActiveColor = g.ActiveColor
v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor
g.views = append(g.views, v)
return v, ErrUnknownView
@ -209,6 +225,20 @@ func (g *Gui) SetCurrentView(name string) error {
return ErrUnknownView
}
// SetCurrentViewOnTop gives the focus to a given view and puts it on top
// of existing views.
func (g *Gui) SetCurrentViewOnTop(name string) 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)
g.currentView = v
return nil
}
}
return ErrUnknownView
}
// CurrentView returns the currently focused view, or nil if no view
// owns the focus.
func (g *Gui) CurrentView() *View {
@ -382,14 +412,21 @@ func (g *Gui) flush() error {
}
for _, v := range g.views {
if v.Frame {
if err := g.drawFrame(v); err != nil {
// Read colors here to new variables so that frame
// will be always drawn fully with same color
bg := v.BgColor
fg := v.FgColor
if v.name == g.currentView.name {
fg = v.ActiveColor
}
if err := g.drawFrame(v, fg, bg); err != nil {
return err
}
if err := g.drawCorners(v); err != nil {
if err := g.drawCorners(v, fg, bg); err != nil {
return err
}
if v.Title != "" {
if err := g.drawTitle(v); err != nil {
if err := g.drawTitle(v, fg, bg); err != nil {
return err
}
}
@ -408,18 +445,18 @@ func (g *Gui) flush() error {
}
// drawFrame draws the horizontal and vertical edges of a view.
func (g *Gui) drawFrame(v *View) error {
func (g *Gui) drawFrame(v *View, fg, bg Attribute) error {
for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ {
if x < 0 {
continue
}
if v.y0 > -1 && v.y0 < g.maxY {
if err := g.SetRune(x, v.y0, '─'); err != nil {
if err := g.SetRuneWithColor(x, v.y0, '─', fg, bg); err != nil {
return err
}
}
if v.y1 > -1 && v.y1 < g.maxY {
if err := g.SetRune(x, v.y1, '─'); err != nil {
if err := g.SetRuneWithColor(x, v.y1, '─', fg, bg); err != nil {
return err
}
}
@ -429,12 +466,12 @@ func (g *Gui) drawFrame(v *View) error {
continue
}
if v.x0 > -1 && v.x0 < g.maxX {
if err := g.SetRune(v.x0, y, '│'); err != nil {
if err := g.SetRuneWithColor(v.x0, y, '│', fg, bg); err != nil {
return err
}
}
if v.x1 > -1 && v.x1 < g.maxX {
if err := g.SetRune(v.x1, y, '│'); err != nil {
if err := g.SetRuneWithColor(v.x1, y, '│', fg, bg); err != nil {
return err
}
}
@ -443,24 +480,24 @@ func (g *Gui) drawFrame(v *View) error {
}
// drawCorners draws the corners of the view.
func (g *Gui) drawCorners(v *View) error {
func (g *Gui) drawCorners(v *View, fg, bg Attribute) error {
if v.x0 >= 0 && v.y0 >= 0 && v.x0 < g.maxX && v.y0 < g.maxY {
if err := g.SetRune(v.x0, v.y0, '┌'); err != nil {
if err := g.SetRuneWithColor(v.x0, v.y0, '┌', fg, bg); err != nil {
return err
}
}
if v.x1 >= 0 && v.y0 >= 0 && v.x1 < g.maxX && v.y0 < g.maxY {
if err := g.SetRune(v.x1, v.y0, '┐'); err != nil {
if err := g.SetRuneWithColor(v.x1, v.y0, '┐', fg, bg); err != nil {
return err
}
}
if v.x0 >= 0 && v.y1 >= 0 && v.x0 < g.maxX && v.y1 < g.maxY {
if err := g.SetRune(v.x0, v.y1, '└'); err != nil {
if err := g.SetRuneWithColor(v.x0, v.y1, '└', fg, bg); err != nil {
return err
}
}
if v.x1 >= 0 && v.y1 >= 0 && v.x1 < g.maxX && v.y1 < g.maxY {
if err := g.SetRune(v.x1, v.y1, '┘'); err != nil {
if err := g.SetRuneWithColor(v.x1, v.y1, '┘', fg, bg); err != nil {
return err
}
}
@ -468,7 +505,7 @@ func (g *Gui) drawCorners(v *View) error {
}
// drawTitle draws the title of the view.
func (g *Gui) drawTitle(v *View) error {
func (g *Gui) drawTitle(v *View, fg, bg Attribute) error {
if v.y0 < 0 || v.y0 >= g.maxY {
return nil
}
@ -480,7 +517,7 @@ func (g *Gui) drawTitle(v *View) error {
} else if x > v.x1-2 || x >= g.maxX {
break
}
if err := g.SetRune(x, v.y0, ch); err != nil {
if err := g.SetRuneWithColor(x, v.y0, ch, fg, bg); err != nil {
return err
}
}
@ -526,23 +563,30 @@ func (g *Gui) draw(v *View) error {
// of the edges that converge at these points.
func (g *Gui) drawIntersections() error {
for _, v := range g.views {
// Use same active view frame coloring as tmux uses.
// If intersection rune is part of currentView use ActiveColor.
bg := v.BgColor
fg := v.FgColor
if v.name == g.currentView.name {
fg = v.ActiveColor
}
if ch, ok := g.intersectionRune(v.x0, v.y0); ok {
if err := g.SetRune(v.x0, v.y0, ch); err != nil {
if err := g.SetRuneWithColor(v.x0, v.y0, ch, fg, bg); err != nil {
return err
}
}
if ch, ok := g.intersectionRune(v.x0, v.y1); ok {
if err := g.SetRune(v.x0, v.y1, ch); err != nil {
if err := g.SetRuneWithColor(v.x0, v.y1, ch, fg, bg); err != nil {
return err
}
}
if ch, ok := g.intersectionRune(v.x1, v.y0); ok {
if err := g.SetRune(v.x1, v.y0, ch); err != nil {
if err := g.SetRuneWithColor(v.x1, v.y0, ch, fg, bg); err != nil {
return err
}
}
if ch, ok := g.intersectionRune(v.x1, v.y1); ok {
if err := g.SetRune(v.x1, v.y1, ch); err != nil {
if err := g.SetRuneWithColor(v.x1, v.y1, ch, fg, bg); err != nil {
return err
}
}

View File

@ -29,6 +29,10 @@ type View struct {
ei *escapeInterpreter // used to decode ESC sequences on Write
// ActiveColor allow to configure the frame color of the View.
// It will be used when focus is on view.
ActiveColor Attribute
// BgColor and FgColor allow to configure the background and foreground
// colors of the View.
BgColor, FgColor Attribute