mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-15 00:15:32 +02:00
bump gocui
This commit is contained in:
139
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
139
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -6,6 +6,7 @@ package gocui
|
||||
|
||||
import (
|
||||
standardErrors "errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@ -38,18 +39,26 @@ const (
|
||||
Output216 = OutputMode(termbox.Output216)
|
||||
)
|
||||
|
||||
type tabClickHandler func(int) error
|
||||
|
||||
type tabClickBinding struct {
|
||||
viewName string
|
||||
handler tabClickHandler
|
||||
}
|
||||
|
||||
// Gui represents the whole User Interface, including the views, layouts
|
||||
// and keybindings.
|
||||
type Gui struct {
|
||||
tbEvents chan termbox.Event
|
||||
userEvents chan userEvent
|
||||
views []*View
|
||||
currentView *View
|
||||
managers []Manager
|
||||
keybindings []*keybinding
|
||||
maxX, maxY int
|
||||
outputMode OutputMode
|
||||
stop chan struct{}
|
||||
tbEvents chan termbox.Event
|
||||
userEvents chan userEvent
|
||||
views []*View
|
||||
currentView *View
|
||||
managers []Manager
|
||||
keybindings []*keybinding
|
||||
tabClickBindings []*tabClickBinding
|
||||
maxX, maxY int
|
||||
outputMode OutputMode
|
||||
stop chan struct{}
|
||||
|
||||
// BgColor and FgColor allow to configure the background and foreground
|
||||
// colors of the GUI.
|
||||
@ -84,11 +93,16 @@ type Gui struct {
|
||||
|
||||
// NewGui returns a new Gui object with a given output mode.
|
||||
func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) {
|
||||
if err := termbox.Init(); err != nil {
|
||||
g := &Gui{}
|
||||
|
||||
var err error
|
||||
if g.maxX, g.maxY, err = g.getTermWindowSize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g := &Gui{}
|
||||
if err := termbox.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.outputMode = mode
|
||||
termbox.SetOutputMode(termbox.OutputMode(mode))
|
||||
@ -98,8 +112,6 @@ func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) {
|
||||
g.tbEvents = make(chan termbox.Event, 20)
|
||||
g.userEvents = make(chan userEvent, 20)
|
||||
|
||||
g.maxX, g.maxY = termbox.Size()
|
||||
|
||||
g.BgColor, g.FgColor = ColorDefault, ColorDefault
|
||||
g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault
|
||||
|
||||
@ -129,7 +141,8 @@ func (g *Gui) Size() (x, y int) {
|
||||
// the given colors.
|
||||
func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
|
||||
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
|
||||
return errors.New("invalid point")
|
||||
// swallowing error because it's not that big of a deal
|
||||
return nil
|
||||
}
|
||||
termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
|
||||
return nil
|
||||
@ -232,7 +245,11 @@ func (g *Gui) ViewByPosition(x, y int) (*View, error) {
|
||||
// traverse views in reverse order checking top views first
|
||||
for i := len(g.views); i > 0; i-- {
|
||||
v := g.views[i-1]
|
||||
if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
|
||||
frameOffset := 0
|
||||
if v.Frame {
|
||||
frameOffset = 1
|
||||
}
|
||||
if x > v.x0-frameOffset && x < v.x1+frameOffset && y > v.y0-frameOffset && y < v.y1+frameOffset {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
@ -320,6 +337,16 @@ func (g *Gui) DeleteKeybindings(viewname string) {
|
||||
g.keybindings = s
|
||||
}
|
||||
|
||||
// SetTabClickBinding sets a binding for a tab click event
|
||||
func (g *Gui) SetTabClickBinding(viewName string, handler tabClickHandler) error {
|
||||
g.tabClickBindings = append(g.tabClickBindings, &tabClickBinding{
|
||||
viewName: viewName,
|
||||
handler: handler,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getKey takes an empty interface with a key and returns the corresponding
|
||||
// typed Key or rune.
|
||||
func getKey(key interface{}) (Key, rune, error) {
|
||||
@ -371,6 +398,7 @@ func (g *Gui) SetManager(managers ...Manager) {
|
||||
g.currentView = nil
|
||||
g.views = nil
|
||||
g.keybindings = nil
|
||||
g.tabClickBindings = nil
|
||||
|
||||
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
|
||||
}
|
||||
@ -486,14 +514,7 @@ func (g *Gui) flush() error {
|
||||
continue
|
||||
}
|
||||
if v.Frame {
|
||||
var fgColor, bgColor Attribute
|
||||
if g.Highlight && v == g.currentView {
|
||||
fgColor = g.SelFgColor
|
||||
bgColor = g.SelBgColor
|
||||
} else {
|
||||
fgColor = g.FgColor
|
||||
bgColor = g.BgColor
|
||||
}
|
||||
fgColor, bgColor := g.viewColors(v)
|
||||
|
||||
if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
@ -501,7 +522,7 @@ func (g *Gui) flush() error {
|
||||
if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Title != "" {
|
||||
if v.Title != "" || len(v.Tabs) > 0 {
|
||||
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -520,6 +541,13 @@ func (g *Gui) flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gui) viewColors(v *View) (Attribute, Attribute) {
|
||||
if g.Highlight && v == g.currentView {
|
||||
return g.SelFgColor, g.SelBgColor
|
||||
}
|
||||
return g.FgColor, g.BgColor
|
||||
}
|
||||
|
||||
// drawFrameEdges draws the horizontal and vertical edges of a view.
|
||||
func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
|
||||
runeH, runeV := '─', '│'
|
||||
@ -615,17 +643,57 @@ func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, ch := range v.Title {
|
||||
tabs := v.Tabs
|
||||
separator := " - "
|
||||
charIndex := 0
|
||||
currentTabStart := -1
|
||||
currentTabEnd := -1
|
||||
if len(tabs) == 0 {
|
||||
tabs = []string{v.Title}
|
||||
} else {
|
||||
for i, tab := range tabs {
|
||||
if i == v.TabIndex {
|
||||
currentTabStart = charIndex
|
||||
currentTabEnd = charIndex + len(tab)
|
||||
break
|
||||
}
|
||||
charIndex += len(tab)
|
||||
if i < len(tabs)-1 {
|
||||
charIndex += len(separator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str := strings.Join(tabs, separator)
|
||||
|
||||
for i, ch := range str {
|
||||
x := v.x0 + i + 2
|
||||
if x < 0 {
|
||||
continue
|
||||
} else if x > v.x1-2 || x >= g.maxX {
|
||||
break
|
||||
}
|
||||
if err := g.SetRune(x, v.y0, ch, fgColor, bgColor); err != nil {
|
||||
|
||||
currentFgColor := fgColor
|
||||
currentBgColor := bgColor
|
||||
// if you are the current view and you have multiple tabs, de-highlight the non-selected tabs
|
||||
if v == g.currentView && len(v.Tabs) > 0 {
|
||||
currentFgColor = v.FgColor
|
||||
currentBgColor = v.BgColor
|
||||
}
|
||||
|
||||
if i >= currentTabStart && i <= currentTabEnd {
|
||||
currentFgColor = v.SelFgColor
|
||||
if v != g.currentView {
|
||||
currentFgColor -= AttrBold
|
||||
}
|
||||
currentBgColor = v.SelBgColor
|
||||
}
|
||||
if err := g.SetRune(x, v.y0, ch, currentFgColor, currentBgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -708,6 +776,17 @@ func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if v.Frame && my == v.y0 {
|
||||
if len(v.Tabs) > 0 {
|
||||
tabIndex := v.GetClickedTabIndex(mx - v.x0)
|
||||
|
||||
for _, binding := range g.tabClickBindings {
|
||||
if binding.viewName == v.Name() {
|
||||
return binding.handler(tabIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := v.SetCursor(mx-v.x0-1, my-v.y0-1); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -723,6 +802,8 @@ func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
// and event. The value of matched is true if there is a match and no errors.
|
||||
func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
|
||||
var globalKb *keybinding
|
||||
var matchingParentViewKb *keybinding
|
||||
|
||||
for _, kb := range g.keybindings {
|
||||
if kb.handler == nil {
|
||||
continue
|
||||
@ -733,10 +814,16 @@ func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err err
|
||||
if kb.matchView(v) {
|
||||
return g.execKeybinding(v, kb)
|
||||
}
|
||||
if kb.matchView(v.ParentView) {
|
||||
matchingParentViewKb = kb
|
||||
}
|
||||
if kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) {
|
||||
globalKb = kb
|
||||
}
|
||||
}
|
||||
if matchingParentViewKb != nil {
|
||||
return g.execKeybinding(v.ParentView, matchingParentViewKb)
|
||||
}
|
||||
if globalKb != nil {
|
||||
return g.execKeybinding(v, globalKb)
|
||||
}
|
||||
|
61
vendor/github.com/jesseduffield/gocui/gui_notwin.go
generated
vendored
Normal file
61
vendor/github.com/jesseduffield/gocui/gui_notwin.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// +build !windows
|
||||
|
||||
package gocui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
)
|
||||
|
||||
type windowSize struct {
|
||||
rows uint16
|
||||
cols uint16
|
||||
xpixels uint16
|
||||
ypixels uint16
|
||||
}
|
||||
|
||||
// getTermWindowSize is get terminal window size on linux or unix.
|
||||
// When gocui run inside the docker contaienr need to check and get the window size.
|
||||
func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
out, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
signalCh := make(chan os.Signal, 1)
|
||||
signal.Notify(signalCh, syscall.SIGWINCH, syscall.SIGINT)
|
||||
defer signal.Stop(signalCh)
|
||||
|
||||
var sz windowSize
|
||||
|
||||
for {
|
||||
_, _, err = syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
out.Fd(),
|
||||
uintptr(syscall.TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(&sz)),
|
||||
)
|
||||
|
||||
// check terminal window size
|
||||
if sz.cols > 0 && sz.rows > 0 {
|
||||
return int(sz.cols), int(sz.rows), nil
|
||||
}
|
||||
|
||||
select {
|
||||
case signal := <-signalCh:
|
||||
switch signal {
|
||||
// when the terminal window size is changed
|
||||
case syscall.SIGWINCH:
|
||||
continue
|
||||
// ctrl + c to cancel
|
||||
case syscall.SIGINT:
|
||||
return 0, 0, errors.New("There was not enough window space to start the application")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
vendor/github.com/jesseduffield/gocui/gui_win.go
generated
vendored
Normal file
10
vendor/github.com/jesseduffield/gocui/gui_win.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// +build windows
|
||||
|
||||
package gocui
|
||||
|
||||
import "github.com/jesseduffield/termbox-go"
|
||||
|
||||
func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
x, y := termbox.Size()
|
||||
return x, y, nil
|
||||
}
|
41
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
41
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@ -80,6 +80,9 @@ type View struct {
|
||||
// If Frame is true, Title allows to configure a title for the view.
|
||||
Title string
|
||||
|
||||
Tabs []string
|
||||
TabIndex int
|
||||
|
||||
// If Frame is true, Subtitle allows to configure a subtitle for the view.
|
||||
Subtitle string
|
||||
|
||||
@ -94,6 +97,12 @@ type View struct {
|
||||
HasLoader bool
|
||||
|
||||
writeMutex sync.Mutex
|
||||
|
||||
// IgnoreCarriageReturns tells us whether to ignore '\r' characters
|
||||
IgnoreCarriageReturns bool
|
||||
|
||||
// ParentView is the view which catches events bubbled up from the given view if there's no matching handler
|
||||
ParentView *View
|
||||
}
|
||||
|
||||
type viewLine struct {
|
||||
@ -190,7 +199,7 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
|
||||
func (v *View) SetCursor(x, y int) error {
|
||||
maxX, maxY := v.Size()
|
||||
if x < 0 || x >= maxX || y < 0 || y >= maxY {
|
||||
return errors.New("invalid point")
|
||||
return nil
|
||||
}
|
||||
v.cx = x
|
||||
v.cy = y
|
||||
@ -235,6 +244,9 @@ func (v *View) Write(p []byte) (n int, err error) {
|
||||
case '\n':
|
||||
v.lines = append(v.lines, nil)
|
||||
case '\r':
|
||||
if v.IgnoreCarriageReturns {
|
||||
continue
|
||||
}
|
||||
nl := len(v.lines)
|
||||
if nl > 0 {
|
||||
v.lines[nl-1] = nil
|
||||
@ -421,7 +433,11 @@ func (v *View) realPosition(vx, vy int) (x, y int, err error) {
|
||||
|
||||
// Clear empties the view's internal buffer.
|
||||
func (v *View) Clear() {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.tainted = true
|
||||
v.ei.reset()
|
||||
|
||||
v.lines = nil
|
||||
v.viewLines = nil
|
||||
@ -470,10 +486,16 @@ func (v *View) ViewBufferLines() []string {
|
||||
return lines
|
||||
}
|
||||
|
||||
// LinesHeight is the count of view lines (i.e. lines excluding wrapping)
|
||||
func (v *View) LinesHeight() int {
|
||||
return len(v.lines)
|
||||
}
|
||||
|
||||
// ViewLinesHeight is the count of view lines (i.e. lines including wrapping)
|
||||
func (v *View) ViewLinesHeight() int {
|
||||
return len(v.viewLines)
|
||||
}
|
||||
|
||||
// ViewBuffer returns a string with the contents of the view's buffer that is
|
||||
// shown to the user.
|
||||
func (v *View) ViewBuffer() string {
|
||||
@ -614,3 +636,20 @@ func Loader() cell {
|
||||
func (v *View) IsTainted() bool {
|
||||
return v.tainted
|
||||
}
|
||||
|
||||
// GetClickedTabIndex tells us which tab was clicked
|
||||
func (v *View) GetClickedTabIndex(x int) int {
|
||||
if len(v.Tabs) <= 1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
charIndex := 0
|
||||
for i, tab := range v.Tabs {
|
||||
charIndex += len(tab + " - ")
|
||||
if x < charIndex {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
Reference in New Issue
Block a user