support actual mouse support and focusing the main view

This commit is contained in:
Jesse Duffield 2019-06-29 13:28:09 +10:00
parent 405bb85ed0
commit e220bedc79
14 changed files with 216 additions and 72 deletions

4
Gopkg.lock generated
View file

@ -131,11 +131,11 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:697738bf0065442016622def39cf22041c6f40df99d5315d845b0d528f96377f" digest = "1:f38f5b4c589578819e4316ece29b7e3516907f2662c68ab92442c5fcc6b41781"
name = "github.com/jesseduffield/gocui" name = "github.com/jesseduffield/gocui"
packages = ["."] packages = ["."]
pruneopts = "NUT" pruneopts = "NUT"
revision = "32197598edc52af42d76b8cba78ed788074b4647" revision = "117e4fb79d29e3908cb7a7bed5e991a321fde7fd"
[[projects]] [[projects]]
branch = "master" branch = "master"

View file

@ -42,10 +42,6 @@ func (gui *Gui) handleContainersClick(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
container, err := gui.getSelectedContainer() container, err := gui.getSelectedContainer()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoContainers { if err != gui.Errors.ErrNoContainers {
@ -314,7 +310,7 @@ func (gui *Gui) refreshContainersAndServices() error {
} }
func (gui *Gui) handleContainersNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleContainersNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }
@ -325,7 +321,7 @@ func (gui *Gui) handleContainersNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleContainersPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleContainersPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }

View file

@ -41,10 +41,6 @@ func (gui *Gui) handleImagesClick(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleImageSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleImageSelect(g *gocui.Gui, v *gocui.View) error {
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
Image, err := gui.getSelectedImage(g) Image, err := gui.getSelectedImage(g)
if err != nil { if err != nil {
if err != gui.Errors.ErrNoImages { if err != gui.Errors.ErrNoImages {
@ -151,7 +147,7 @@ func (gui *Gui) refreshStateImages() error {
} }
func (gui *Gui) handleImagesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleImagesNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }
@ -162,7 +158,7 @@ func (gui *Gui) handleImagesNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleImagesPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleImagesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }

View file

@ -153,6 +153,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleStatusNextContext, Handler: gui.handleStatusNextContext,
Description: gui.Tr.NextContext, Description: gui.Tr.NextContext,
}, },
{
ViewName: "status",
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleStatusClick,
Description: gui.Tr.NextContext,
},
{ {
ViewName: "status", ViewName: "status",
Key: 'm', Key: 'm',
@ -366,12 +373,66 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handlePruneVolumes, Handler: gui.handlePruneVolumes,
Description: gui.Tr.PruneVolumes, Description: gui.Tr.PruneVolumes,
}, },
{
ViewName: "main",
Key: gocui.KeyEsc,
Modifier: gocui.ModNone,
Handler: gui.handleExitMain,
Description: gui.Tr.Return,
},
{
ViewName: "main",
Key: gocui.KeyArrowLeft,
Modifier: gocui.ModNone,
Handler: gui.scrollLeftMain,
},
{
ViewName: "main",
Key: gocui.KeyArrowRight,
Modifier: gocui.ModNone,
Handler: gui.scrollRightMain,
},
{
ViewName: "main",
Key: 'h',
Modifier: gocui.ModNone,
Handler: gui.scrollLeftMain,
},
{
ViewName: "main",
Key: 'l',
Modifier: gocui.ModNone,
Handler: gui.scrollRightMain,
},
{
ViewName: "",
Key: 'J',
Modifier: gocui.ModNone,
Handler: gui.scrollDownMain,
},
{
ViewName: "",
Key: 'K',
Modifier: gocui.ModNone,
Handler: gui.scrollUpMain,
},
{
ViewName: "",
Key: 'H',
Modifier: gocui.ModNone,
Handler: gui.scrollLeftMain,
},
{
ViewName: "",
Key: 'L',
Modifier: gocui.ModNone,
Handler: gui.scrollRightMain,
},
} }
// TODO: add more views here // TODO: add more views here
for _, viewName := range []string{"status", "services", "containers", "images", "volumes", "menu"} { for _, viewName := range []string{"status", "services", "containers", "images", "volumes", "menu"} {
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView}, {ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView}, {ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView}, {ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
@ -379,30 +440,41 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
}...) }...)
} }
listPanelMap := map[string]struct { panelMap := map[string]struct {
prevLine func(*gocui.Gui, *gocui.View) error onKeyUpPress func(*gocui.Gui, *gocui.View) error
nextLine func(*gocui.Gui, *gocui.View) error onKeyDownPress func(*gocui.Gui, *gocui.View) error
focus func(*gocui.Gui, *gocui.View) error onClick func(*gocui.Gui, *gocui.View) error
}{ }{
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine, focus: gui.handleMenuClick}, "menu": {onKeyUpPress: gui.handleMenuPrevLine, onKeyDownPress: gui.handleMenuNextLine, onClick: gui.handleMenuClick},
"services": {prevLine: gui.handleServicesPrevLine, nextLine: gui.handleServicesNextLine, focus: gui.handleServicesClick}, "services": {onKeyUpPress: gui.handleServicesPrevLine, onKeyDownPress: gui.handleServicesNextLine, onClick: gui.handleServicesClick},
"containers": {prevLine: gui.handleContainersPrevLine, nextLine: gui.handleContainersNextLine, focus: gui.handleContainersClick}, "containers": {onKeyUpPress: gui.handleContainersPrevLine, onKeyDownPress: gui.handleContainersNextLine, onClick: gui.handleContainersClick},
"images": {prevLine: gui.handleImagesPrevLine, nextLine: gui.handleImagesNextLine, focus: gui.handleImagesClick}, "images": {onKeyUpPress: gui.handleImagesPrevLine, onKeyDownPress: gui.handleImagesNextLine, onClick: gui.handleImagesClick},
"volumes": {prevLine: gui.handleVolumesPrevLine, nextLine: gui.handleVolumesNextLine, focus: gui.handleVolumesClick}, "volumes": {onKeyUpPress: gui.handleVolumesPrevLine, onKeyDownPress: gui.handleVolumesNextLine, onClick: gui.handleVolumesClick},
"main": {onKeyUpPress: gui.scrollUpMain, onKeyDownPress: gui.scrollDownMain, onClick: gui.handleMainClick},
} }
for viewName, functions := range listPanelMap { for viewName, functions := range panelMap {
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.prevLine}, {ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.onKeyUpPress},
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.prevLine}, {ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.onKeyUpPress},
{ViewName: viewName, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: functions.prevLine}, {ViewName: viewName, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: functions.onKeyUpPress},
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.nextLine}, {ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.onKeyDownPress},
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.nextLine}, {ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.onKeyDownPress},
{ViewName: viewName, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: functions.nextLine}, {ViewName: viewName, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: functions.onKeyDownPress},
{ViewName: viewName, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: functions.focus}, {ViewName: viewName, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: functions.onClick},
}...) }...)
} }
for _, viewName := range []string{"status", "services", "containers", "images", "volumes"} {
bindings = append(bindings, &Binding{
ViewName: viewName,
Key: gocui.KeyEnter,
Modifier: gocui.ModNone,
Handler: gui.handleEnterMain,
Description: gui.Tr.FocusMain,
})
}
return bindings return bindings
} }

View file

@ -41,6 +41,10 @@ func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
return nil return nil
} }
if !gui.isPopupPanel(newView.Name()) {
v.ParentView = nil
}
// refocusing because in responsive mode (when the window is very short) we want to ensure that after the view size changes we can still see the last selected item // refocusing because in responsive mode (when the window is very short) we want to ensure that after the view size changes we can still see the last selected item
if err := gui.focusPointInView(v); err != nil { if err := gui.focusPointInView(v); err != nil {
return err return err

View file

@ -7,7 +7,7 @@ import (
) )
func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main") mainView := gui.getMainView()
mainView.Autoscroll = false mainView.Autoscroll = false
ox, oy := mainView.Origin() ox, oy := mainView.Origin()
newOy := int(math.Max(0, float64(oy-gui.Config.UserConfig.Gui.ScrollHeight))) newOy := int(math.Max(0, float64(oy-gui.Config.UserConfig.Gui.ScrollHeight)))
@ -15,7 +15,7 @@ func (gui *Gui) scrollUpMain(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error {
mainView, _ := g.View("main") mainView := gui.getMainView()
ox, oy := mainView.Origin() ox, oy := mainView.Origin()
y := oy y := oy
if !gui.Config.UserConfig.Gui.ScrollPastBottom { if !gui.Config.UserConfig.Gui.ScrollPastBottom {
@ -27,6 +27,21 @@ func (gui *Gui) scrollDownMain(g *gocui.Gui, v *gocui.View) error {
return mainView.SetOrigin(ox, oy+gui.Config.UserConfig.Gui.ScrollHeight) return mainView.SetOrigin(ox, oy+gui.Config.UserConfig.Gui.ScrollHeight)
} }
func (gui *Gui) scrollLeftMain(g *gocui.Gui, v *gocui.View) error {
mainView := gui.getMainView()
ox, oy := mainView.Origin()
newOx := int(math.Max(0, float64(ox-gui.Config.UserConfig.Gui.ScrollHeight)))
return mainView.SetOrigin(newOx, oy)
}
func (gui *Gui) scrollRightMain(g *gocui.Gui, v *gocui.View) error {
mainView := gui.getMainView()
ox, oy := mainView.Origin()
return mainView.SetOrigin(ox+gui.Config.UserConfig.Gui.ScrollHeight, oy)
}
func (gui *Gui) autoScrollMain(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) autoScrollMain(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Autoscroll = true gui.getMainView().Autoscroll = true
return nil return nil
@ -37,6 +52,11 @@ func (gui *Gui) onMainTabClick(tabIndex int) error {
viewName := gui.currentViewName() viewName := gui.currentViewName()
mainView := gui.getMainView()
if viewName == "main" && mainView.ParentView != nil {
viewName = mainView.ParentView.Name()
}
switch viewName { switch viewName {
case "services": case "services":
gui.State.Panels.Services.ContextIndex = tabIndex gui.State.Panels.Services.ContextIndex = tabIndex
@ -57,3 +77,23 @@ func (gui *Gui) onMainTabClick(tabIndex int) error {
return nil return nil
} }
func (gui *Gui) handleEnterMain(g *gocui.Gui, v *gocui.View) error {
mainView := gui.getMainView()
mainView.ParentView = v
return gui.switchFocus(gui.g, v, mainView)
}
func (gui *Gui) handleExitMain(g *gocui.Gui, v *gocui.View) error {
v.ParentView = nil
return gui.returnFocus(gui.g, v)
}
func (gui *Gui) handleMainClick(g *gocui.Gui, v *gocui.View) error {
currentView := gui.g.CurrentView()
v.ParentView = currentView
return gui.switchFocus(gui.g, currentView, v)
}

View file

@ -26,6 +26,28 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding {
} }
} }
// check if we have any keybindings from our parent view to add
if v.ParentView != nil {
L:
for _, binding := range bindings {
if binding.GetKey() != "" && binding.Description != "" {
if binding.ViewName == v.ParentView.Name() {
// if we haven't got a conflict we will display the binding
for _, ownBinding := range bindingsPanel {
if ownBinding.GetKey() == binding.GetKey() {
continue L
}
}
bindingsPanel = append(bindingsPanel, binding)
}
}
}
}
// } else if v.ParentView != nil && binding.ViewName == v.ParentView.Name() {
// // only add this if we don't have our own matching binding
// bindingsPanel = append(bindingsPanel, binding)
// append dummy element to have a separator between // append dummy element to have a separator between
// panel and global keybindings // panel and global keybindings
bindingsPanel = append(bindingsPanel, &Binding{}) bindingsPanel = append(bindingsPanel, &Binding{})

View file

@ -39,10 +39,6 @@ func (gui *Gui) handleServicesClick(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error {
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
service, err := gui.getSelectedService() service, err := gui.getSelectedService()
if err != nil { if err != nil {
return nil return nil
@ -133,7 +129,7 @@ func (gui *Gui) renderServiceLogs(service *commands.Service) error {
} }
func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }
@ -144,7 +140,7 @@ func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleServicesPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleServicesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }

View file

@ -39,7 +39,7 @@ func (gui *Gui) refreshStatus() error {
return nil return nil
} }
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStatusClick(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() {
return nil return nil
} }
@ -48,6 +48,14 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
return gui.handleStatusSelect(g, v)
}
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
key := gui.getStatusContexts()[gui.State.Panels.Status.ContextIndex] key := gui.getStatusContexts()[gui.State.Panels.Status.ContextIndex]
if !gui.shouldRefresh(key) { if !gui.shouldRefresh(key) {
return nil return nil

View file

@ -301,22 +301,6 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
// generalFocusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
func (gui *Gui) generalFocusLine(lineNumber int, bottomLine int, v *gocui.View) error {
_, height := v.Size()
overScroll := bottomLine - height + 1
if overScroll < 0 {
overScroll = 0
}
if err := v.SetOrigin(0, overScroll); err != nil {
return err
}
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
return err
}
return nil
}
func (gui *Gui) changeSelectedLine(line *int, total int, up bool) { func (gui *Gui) changeSelectedLine(line *int, total int, up bool) {
if up { if up {
if *line == -1 || *line == 0 { if *line == -1 || *line == 0 {
@ -389,6 +373,10 @@ func (gui *Gui) handleClick(v *gocui.View, itemCount int, selectedLine *int, han
return nil return nil
} }
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
_, cy := v.Cursor() _, cy := v.Cursor()
_, oy := v.Origin() _, oy := v.Origin()

View file

@ -38,10 +38,6 @@ func (gui *Gui) handleVolumesClick(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleVolumeSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleVolumeSelect(g *gocui.Gui, v *gocui.View) error {
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
volume, err := gui.getSelectedVolume() volume, err := gui.getSelectedVolume()
if err != nil { if err != nil {
if err != gui.Errors.ErrNoVolumes { if err != gui.Errors.ErrNoVolumes {
@ -144,7 +140,7 @@ func (gui *Gui) refreshVolumes() error {
} }
func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }
@ -155,7 +151,7 @@ func (gui *Gui) handleVolumesNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleVolumesPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleVolumesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() { if gui.popupPanelFocused() || gui.g.CurrentView() != v {
return nil return nil
} }

View file

@ -31,6 +31,8 @@ type TranslationSet struct {
RemoveWithVolumes string RemoveWithVolumes string
MustForceToRemoveContainer string MustForceToRemoveContainer string
Confirm string Confirm string
Return string
FocusMain string
StopContainer string StopContainer string
RestartingStatus string RestartingStatus string
StoppingStatus string StoppingStatus string
@ -94,6 +96,8 @@ func englishSet() TranslationSet {
Donate: "Donate", Donate: "Donate",
Confirm: "Confirm", Confirm: "Confirm",
Return: "return",
FocusMain: "focus main panel",
Navigate: "navigate", Navigate: "navigate",
Execute: "execute", Execute: "execute",
Close: "close", Close: "close",

View file

@ -394,6 +394,7 @@ func (g *Gui) SetManager(managers ...Manager) {
g.currentView = nil g.currentView = nil
g.views = nil g.views = nil
g.keybindings = nil g.keybindings = nil
g.tabClickBindings = nil
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }() go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
} }
@ -509,14 +510,7 @@ func (g *Gui) flush() error {
continue continue
} }
if v.Frame { if v.Frame {
var fgColor, bgColor Attribute fgColor, bgColor := g.viewColors(v)
if g.Highlight && v == g.currentView {
fgColor = g.SelFgColor
bgColor = g.SelBgColor
} else {
fgColor = g.FgColor
bgColor = g.BgColor
}
if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil { if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
return err return err
@ -543,6 +537,13 @@ func (g *Gui) flush() error {
return nil 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. // drawFrameEdges draws the horizontal and vertical edges of a view.
func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
runeH, runeV := '─', '│' runeH, runeV := '─', '│'
@ -668,10 +669,20 @@ func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
} else if x > v.x1-2 || x >= g.maxX { } else if x > v.x1-2 || x >= g.maxX {
break break
} }
currentFgColor := fgColor currentFgColor := fgColor
currentBgColor := bgColor 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 { if i >= currentTabStart && i <= currentTabEnd {
currentFgColor = v.SelFgColor - AttrBold currentFgColor = v.SelFgColor
if v != g.currentView {
currentFgColor -= AttrBold
}
currentBgColor = v.SelBgColor currentBgColor = v.SelBgColor
} }
if err := g.SetRune(x, v.y0, ch, currentFgColor, currentBgColor); err != nil { if err := g.SetRune(x, v.y0, ch, currentFgColor, currentBgColor); err != nil {
@ -787,6 +798,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. // 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) { func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
var globalKb *keybinding var globalKb *keybinding
var matchingParentViewKb *keybinding
for _, kb := range g.keybindings { for _, kb := range g.keybindings {
if kb.handler == nil { if kb.handler == nil {
continue continue
@ -797,10 +810,16 @@ func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err err
if kb.matchView(v) { if kb.matchView(v) {
return g.execKeybinding(v, kb) 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)) { if kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) {
globalKb = kb globalKb = kb
} }
} }
if matchingParentViewKb != nil {
return g.execKeybinding(v.ParentView, matchingParentViewKb)
}
if globalKb != nil { if globalKb != nil {
return g.execKeybinding(v, globalKb) return g.execKeybinding(v, globalKb)
} }

View file

@ -100,6 +100,9 @@ type View struct {
// IgnoreCarriageReturns tells us whether to ignore '\r' characters // IgnoreCarriageReturns tells us whether to ignore '\r' characters
IgnoreCarriageReturns bool 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 { type viewLine struct {