jesseduffield.lazygit/pkg/gui/context/view_trait.go
Stefan Haller ec3f681ebf Derive the selection highlight from the context stack
A view drew a selection because something told it to, from four places on
three different schedules: a context being focused, a context losing focus,
a context being activated over another one, and a list being re-rendered.
Whether the flags ended up describing the state of the app depended on
which of those had run last, and the last one to run was often none of
them: a refresh only re-focuses the view that has the focus, so a list
whose contents changed underneath an unfocused panel kept whichever
highlight it happened to have.

Derive both flags instead, in one place, from the two things they mean: a
view shows a selection while its context is on the stack and has something
to select, and the context the user is in shows an active one where the
ones behind it show inactive ones. Nothing else needs to say anything about
highlighting, so nothing else can leave a view saying something untrue
about where the focus is.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 21:19:04 +02:00

96 lines
2.2 KiB
Go

package context
import (
"github.com/jesseduffield/lazygit/pkg/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
const HORIZONTAL_SCROLL_FACTOR = 3
type ViewTrait struct {
view *gocui.View
}
var _ types.IViewTrait = &ViewTrait{}
func NewViewTrait(view *gocui.View) *ViewTrait {
return &ViewTrait{view: view}
}
func (self *ViewTrait) FocusPoint(yIdx int, scrollIntoView bool) {
self.view.FocusPoint(self.view.OriginX(), yIdx, scrollIntoView)
}
func (self *ViewTrait) SetRangeSelectStart(yIdx int) {
self.view.SetRangeSelectStart(yIdx)
}
func (self *ViewTrait) CancelRangeSelect() {
self.view.CancelRangeSelect()
}
func (self *ViewTrait) SetViewPortContent(content string) {
_, y := self.view.Origin()
self.view.OverwriteLines(y, content)
}
func (self *ViewTrait) SetViewPortContentAndClearEverythingElse(lineCount int, content string) {
_, y := self.view.Origin()
self.view.OverwriteLinesAndClearEverythingElse(lineCount, y, content)
}
func (self *ViewTrait) SetContent(content string) {
self.view.SetContent(content)
}
func (self *ViewTrait) SetFooter(value string) {
self.view.Footer = value
}
func (self *ViewTrait) SetOriginX(value int) {
self.view.SetOriginX(value)
}
// tells us the start of line indexes shown in the view currently as well as the capacity of lines shown in the viewport.
func (self *ViewTrait) ViewPortYBounds() (int, int) {
_, start := self.view.Origin()
length := self.view.InnerHeight()
return start, length
}
func (self *ViewTrait) ScrollLeft() {
self.view.ScrollLeft(self.horizontalScrollAmount())
}
func (self *ViewTrait) ScrollRight() {
self.view.ScrollRight(self.horizontalScrollAmount())
}
func (self *ViewTrait) horizontalScrollAmount() int {
return self.view.InnerWidth() / HORIZONTAL_SCROLL_FACTOR
}
func (self *ViewTrait) ScrollUp(value int) {
self.view.ScrollUp(value)
}
func (self *ViewTrait) ScrollDown(value int) {
self.view.ScrollDown(value)
}
// this returns the amount we'll scroll if we want to scroll by a page.
func (self *ViewTrait) PageDelta() int {
height := self.view.InnerHeight()
delta := height - 1
if delta == 0 {
return 1
}
return delta
}
func (self *ViewTrait) SelectedLineIdx() int {
return self.view.SelectedLineIdx()
}