summaryrefslogtreecommitdiff
path: root/src/tui
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2024-12-31 17:03:18 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2024-12-31 17:05:14 +0900
commit9a2b7f559c88067c25a65a6a34c8e172bdf830dc (patch)
tree56bf93324f9096fc6772e64063c5f9ebc803e90a /src/tui
parentb8d2b0df7e1241c09eff21dbb63ebcc841efc824 (diff)
downloadfzf-9a2b7f559c88067c25a65a6a34c8e172bdf830dc.tar.gz
Add --list-border for additional border around the list section
Close #4148
Diffstat (limited to 'src/tui')
-rw-r--r--src/tui/dummy.go3
-rw-r--r--src/tui/light.go81
-rw-r--r--src/tui/light_unix.go2
-rw-r--r--src/tui/light_windows.go2
-rw-r--r--src/tui/tcell.go53
-rw-r--r--src/tui/tui.go83
6 files changed, 149 insertions, 75 deletions
diff --git a/src/tui/dummy.go b/src/tui/dummy.go
index 1a761460..a49677c6 100644
--- a/src/tui/dummy.go
+++ b/src/tui/dummy.go
@@ -30,6 +30,7 @@ const (
)
func (r *FullscreenRenderer) Init() error { return nil }
+func (r *FullscreenRenderer) DefaultTheme() *ColorTheme { return nil }
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) Pause(bool) {}
func (r *FullscreenRenderer) Resume(bool, bool) {}
@@ -48,6 +49,6 @@ func (r *FullscreenRenderer) MaxY() int { return 0 }
func (r *FullscreenRenderer) RefreshWindows(windows []Window) {}
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
+func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
return nil
}
diff --git a/src/tui/light.go b/src/tui/light.go
index 95984542..f0bb2fdf 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -116,19 +116,19 @@ type LightRenderer struct {
}
type LightWindow struct {
- renderer *LightRenderer
- colored bool
- preview bool
- border BorderStyle
- top int
- left int
- width int
- height int
- posx int
- posy int
- tabstop int
- fg Color
- bg Color
+ renderer *LightRenderer
+ colored bool
+ windowType WindowType
+ border BorderStyle
+ top int
+ left int
+ width int
+ height int
+ posx int
+ posy int
+ tabstop int
+ fg Color
+ bg Color
}
func NewLightRenderer(ttyin *os.File, theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) {
@@ -174,7 +174,6 @@ func (r *LightRenderer) Init() error {
return err
}
r.updateTerminalSize()
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
if r.fullscreen {
r.smcup()
@@ -780,27 +779,32 @@ func (r *LightRenderer) MaxY() int {
return r.height
}
-func (r *LightRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
+func (r *LightRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
w := &LightWindow{
- renderer: r,
- colored: r.theme.Colored,
- preview: preview,
- border: borderStyle,
- top: top,
- left: left,
- width: width,
- height: height,
- tabstop: r.tabstop,
- fg: colDefault,
- bg: colDefault}
- if preview {
- w.fg = r.theme.PreviewFg.Color
- w.bg = r.theme.PreviewBg.Color
- } else {
+ renderer: r,
+ colored: r.theme.Colored,
+ windowType: windowType,
+ border: borderStyle,
+ top: top,
+ left: left,
+ width: width,
+ height: height,
+ tabstop: r.tabstop,
+ fg: colDefault,
+ bg: colDefault}
+ switch windowType {
+ case WindowBase:
w.fg = r.theme.Fg.Color
w.bg = r.theme.Bg.Color
+ case WindowList:
+ w.fg = r.theme.ListFg.Color
+ w.bg = r.theme.ListBg.Color
+ case WindowPreview:
+ w.fg = r.theme.PreviewFg.Color
+ w.bg = r.theme.PreviewBg.Color
}
- if !w.bg.IsDefault() && w.border.shape != BorderNone {
+ if erase && !w.bg.IsDefault() && w.border.shape != BorderNone {
+ // fzf --color bg:blue --border --padding 1,2
w.Erase()
}
w.drawBorder(false)
@@ -845,7 +849,10 @@ func (w *LightWindow) drawBorder(onlyHorizontal bool) {
func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
hw := runeWidth(w.border.top)
@@ -863,7 +870,10 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
func (w *LightWindow) drawBorderVertical(left, right bool) {
vw := runeWidth(w.border.left)
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
for y := 0; y < w.height; y++ {
@@ -883,7 +893,10 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
w.Move(0, 0)
color := ColBorder
- if w.preview {
+ switch w.windowType {
+ case WindowList:
+ color = ColListBorder
+ case WindowPreview:
color = ColPreviewBorder
}
hw := runeWidth(w.border.top)
diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go
index c2d5612a..76aac2eb 100644
--- a/src/tui/light_unix.go
+++ b/src/tui/light_unix.go
@@ -18,7 +18,7 @@ func IsLightRendererSupported() bool {
return true
}
-func (r *LightRenderer) defaultTheme() *ColorTheme {
+func (r *LightRenderer) DefaultTheme() *ColorTheme {
if strings.Contains(os.Getenv("TERM"), "256") {
return Dark256
}
diff --git a/src/tui/light_windows.go b/src/tui/light_windows.go
index e2a2bbfe..cf5126ab 100644
--- a/src/tui/light_windows.go
+++ b/src/tui/light_windows.go
@@ -39,7 +39,7 @@ func IsLightRendererSupported() bool {
return canSetVt100
}
-func (r *LightRenderer) defaultTheme() *ColorTheme {
+func (r *LightRenderer) DefaultTheme() *ColorTheme {
// the getenv check is borrowed from here: https://github.com/gdamore/tcell/commit/0c473b86d82f68226a142e96cc5a34c5a29b3690#diff-b008fcd5e6934bf31bc3d33bf49f47d8R178:
if !IsLightRendererSupported() || os.Getenv("ConEmuPID") != "" || os.Getenv("TCELL_TRUECOLOR") == "disable" {
return Default16
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index a3ce2cb1..92336cd0 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -40,7 +40,7 @@ type Attr int32
type TcellWindow struct {
color bool
- preview bool
+ windowType WindowType
top int
left int
width int
@@ -106,8 +106,12 @@ func (r *FullscreenRenderer) PassThrough(str string) {
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
-func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
- if _screen.Colors() >= 256 {
+func (r *FullscreenRenderer) DefaultTheme() *ColorTheme {
+ s, e := r.getScreen()
+ if e != nil {
+ return Default16
+ }
+ if s.Colors() >= 256 {
return Dark256
}
return Default16
@@ -148,8 +152,19 @@ var (
_initialResize bool = true
)
+func (r *FullscreenRenderer) getScreen() (tcell.Screen, error) {
+ if _screen == nil {
+ s, e := tcell.NewScreen()
+ if e != nil {
+ return nil, e
+ }
+ _screen = s
+ }
+ return _screen, nil
+}
+
func (r *FullscreenRenderer) initScreen() error {
- s, e := tcell.NewScreen()
+ s, e := r.getScreen()
if e != nil {
return e
}
@@ -161,7 +176,6 @@ func (r *FullscreenRenderer) initScreen() error {
} else {
s.DisableMouse()
}
- _screen = s
return nil
}
@@ -174,7 +188,6 @@ func (r *FullscreenRenderer) Init() error {
if err := r.initScreen(); err != nil {
return err
}
- initTheme(r.theme, r.defaultTheme(), r.forceBlack)
return nil
}
@@ -537,14 +550,17 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
_screen.Show()
}
-func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window {
- normal := ColNormal
- if preview {
- normal = ColPreview
+func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window {
+ normal := ColBorder
+ switch windowType {
+ case WindowList:
+ normal = ColListBorder
+ case WindowPreview:
+ normal = ColPreviewBorder
}
w := &TcellWindow{
color: r.theme.Colored,
- preview: preview,
+ windowType: windowType,
top: top,
left: left,
width: width,
@@ -564,11 +580,7 @@ func fill(x, y, w, h int, n ColorPair, r rune) {
}
func (w *TcellWindow) Erase() {
- if w.borderStyle.shape.HasLeft() {
- fill(w.left-1, w.top, w.width, w.height-1, w.normal, ' ')
- } else {
- fill(w.left, w.top, w.width-1, w.height-1, w.normal, ' ')
- }
+ fill(w.left, w.top, w.width-1, w.height-1, w.normal, ' ')
w.drawBorder(false)
}
@@ -768,10 +780,13 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
var style tcell.Style
if w.color {
- if w.preview {
- style = ColPreviewBorder.style()
- } else {
+ switch w.windowType {
+ case WindowBase:
style = ColBorder.style()
+ case WindowList:
+ style = ColListBorder.style()
+ case WindowPreview:
+ style = ColPreviewBorder.style()
}
} else {
style = w.normal.style()
diff --git a/src/tui/tui.go b/src/tui/tui.go
index 902d13fe..e2a891d0 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -303,6 +303,8 @@ type ColorTheme struct {
Disabled ColorAttr
Fg ColorAttr
Bg ColorAttr
+ ListFg ColorAttr
+ ListBg ColorAttr
SelectedFg ColorAttr
SelectedBg ColorAttr
SelectedMatch ColorAttr
@@ -323,9 +325,11 @@ type ColorTheme struct {
Scrollbar ColorAttr
Border ColorAttr
PreviewBorder ColorAttr
+ PreviewLabel ColorAttr
PreviewScrollbar ColorAttr
BorderLabel ColorAttr
- PreviewLabel ColorAttr
+ ListLabel ColorAttr
+ ListBorder ColorAttr
}
type Event struct {
@@ -395,6 +399,10 @@ func (s BorderShape) HasBottom() bool {
return true
}
+func (s BorderShape) Visible() bool {
+ return s != BorderNone
+}
+
type BorderStyle struct {
shape BorderShape
top rune
@@ -525,7 +533,16 @@ type TermSize struct {
PxHeight int
}
+type WindowType int
+
+const (
+ WindowBase WindowType = iota
+ WindowList
+ WindowPreview
+)
+
type Renderer interface {
+ DefaultTheme() *ColorTheme
Init() error
Resize(maxHeightFunc func(int) int)
Pause(clear bool)
@@ -546,7 +563,7 @@ type Renderer interface {
Size() TermSize
- NewWindow(top int, left int, width int, height int, preview bool, borderStyle BorderStyle) Window
+ NewWindow(top int, left int, width int, height int, windowType WindowType, borderStyle BorderStyle, erase bool) Window
}
type Window interface {
@@ -627,6 +644,8 @@ var (
ColPreviewLabel ColorPair
ColPreviewScrollbar ColorPair
ColPreviewSpinner ColorPair
+ ColListBorder ColorPair
+ ColListLabel ColorPair
)
func EmptyTheme() *ColorTheme {
@@ -635,6 +654,8 @@ func EmptyTheme() *ColorTheme {
Input: ColorAttr{colUndefined, AttrUndefined},
Fg: ColorAttr{colUndefined, AttrUndefined},
Bg: ColorAttr{colUndefined, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -650,6 +671,8 @@ func EmptyTheme() *ColorTheme {
Header: ColorAttr{colUndefined, AttrUndefined},
Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: ColorAttr{colUndefined, AttrUndefined},
@@ -668,6 +691,8 @@ func NoColorTheme() *ColorTheme {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colDefault, AttrUndefined},
+ ListBg: ColorAttr{colDefault, AttrUndefined},
SelectedFg: ColorAttr{colDefault, AttrUndefined},
SelectedBg: ColorAttr{colDefault, AttrUndefined},
SelectedMatch: ColorAttr{colDefault, AttrUndefined},
@@ -690,6 +715,8 @@ func NoColorTheme() *ColorTheme {
PreviewBorder: ColorAttr{colDefault, AttrUndefined},
PreviewScrollbar: ColorAttr{colDefault, AttrUndefined},
PreviewLabel: ColorAttr{colDefault, AttrUndefined},
+ ListLabel: ColorAttr{colDefault, AttrUndefined},
+ ListBorder: ColorAttr{colDefault, AttrUndefined},
Separator: ColorAttr{colDefault, AttrUndefined},
Scrollbar: ColorAttr{colDefault, AttrUndefined},
}
@@ -701,6 +728,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -723,6 +752,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
}
@@ -731,6 +762,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -753,6 +786,8 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
}
@@ -761,6 +796,8 @@ func init() {
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
+ ListFg: ColorAttr{colUndefined, AttrUndefined},
+ ListBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
@@ -783,12 +820,14 @@ func init() {
PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListLabel: ColorAttr{colUndefined, AttrUndefined},
+ ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
}
}
-func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
+func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
if forceBlack {
theme.Bg = ColorAttr{colBlack, AttrUndefined}
}
@@ -820,8 +859,10 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
// These colors are not defined in the base themes
- theme.SelectedFg = o(theme.Fg, theme.SelectedFg)
- theme.SelectedBg = o(theme.Bg, theme.SelectedBg)
+ theme.ListFg = o(theme.Fg, theme.ListFg)
+ theme.ListBg = o(theme.Bg, theme.ListBg)
+ theme.SelectedFg = o(theme.ListFg, theme.SelectedFg)
+ theme.SelectedBg = o(theme.ListBg, theme.SelectedBg)
theme.SelectedMatch = o(theme.Match, theme.SelectedMatch)
theme.Disabled = o(theme.Input, theme.Disabled)
theme.Gutter = o(theme.DarkBg, theme.Gutter)
@@ -829,8 +870,10 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.PreviewBg = o(theme.Bg, theme.PreviewBg)
theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel)
theme.PreviewBorder = o(theme.Border, theme.PreviewBorder)
- theme.Separator = o(theme.Border, theme.Separator)
- theme.Scrollbar = o(theme.Border, theme.Scrollbar)
+ theme.ListLabel = o(theme.BorderLabel, theme.ListLabel)
+ theme.ListBorder = o(theme.Border, theme.ListBorder)
+ theme.Separator = o(theme.ListBorder, theme.Separator)
+ theme.Scrollbar = o(theme.ListBorder, theme.Scrollbar)
theme.PreviewScrollbar = o(theme.PreviewBorder, theme.PreviewScrollbar)
initPalette(theme)
@@ -843,19 +886,19 @@ func initPalette(theme *ColorTheme) {
}
return ColorPair{fg.Color, bg.Color, fg.Attr}
}
- blank := theme.Fg
+ blank := theme.ListFg
blank.Attr = AttrRegular
- ColPrompt = pair(theme.Prompt, theme.Bg)
- ColNormal = pair(theme.Fg, theme.Bg)
+ ColPrompt = pair(theme.Prompt, theme.ListBg)
+ ColNormal = pair(theme.ListFg, theme.ListBg)
ColSelected = pair(theme.SelectedFg, theme.SelectedBg)
- ColInput = pair(theme.Input, theme.Bg)
- ColDisabled = pair(theme.Disabled, theme.Bg)
- ColMatch = pair(theme.Match, theme.Bg)
+ ColInput = pair(theme.Input, theme.ListBg)
+ ColDisabled = pair(theme.Disabled, theme.ListBg)
+ ColMatch = pair(theme.Match, theme.ListBg)
ColSelectedMatch = pair(theme.SelectedMatch, theme.SelectedBg)
ColCursor = pair(theme.Cursor, theme.Gutter)
ColCursorEmpty = pair(blank, theme.Gutter)
- if theme.SelectedBg.Color != theme.Bg.Color {
+ if theme.SelectedBg.Color != theme.ListBg.Color {
ColMarker = pair(theme.Marker, theme.SelectedBg)
} else {
ColMarker = pair(theme.Marker, theme.Gutter)
@@ -866,11 +909,11 @@ func initPalette(theme *ColorTheme) {
ColCurrentCursorEmpty = pair(blank, theme.DarkBg)
ColCurrentMarker = pair(theme.Marker, theme.DarkBg)
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
- ColSpinner = pair(theme.Spinner, theme.Bg)
- ColInfo = pair(theme.Info, theme.Bg)
- ColHeader = pair(theme.Header, theme.Bg)
- ColSeparator = pair(theme.Separator, theme.Bg)
- ColScrollbar = pair(theme.Scrollbar, theme.Bg)
+ ColSpinner = pair(theme.Spinner, theme.ListBg)
+ ColInfo = pair(theme.Info, theme.ListBg)
+ ColHeader = pair(theme.Header, theme.ListBg)
+ ColSeparator = pair(theme.Separator, theme.ListBg)
+ ColScrollbar = pair(theme.Scrollbar, theme.ListBg)
ColBorder = pair(theme.Border, theme.Bg)
ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
ColPreviewLabel = pair(theme.PreviewLabel, theme.PreviewBg)
@@ -878,6 +921,8 @@ func initPalette(theme *ColorTheme) {
ColPreviewBorder = pair(theme.PreviewBorder, theme.PreviewBg)
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
+ ColListLabel = pair(theme.ListLabel, theme.ListBg)
+ ColListBorder = pair(theme.ListBorder, theme.ListBg)
}
func runeWidth(r rune) int {