summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2025-01-05 23:02:52 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2025-01-05 23:02:52 +0900
commita5beb08ed72215e8b015851ad70bf3beca526429 (patch)
tree8b469e7210224defe03290878f5473f2cb8e3ce1 /src
parent45fc7b903d1aab0871f03cbe1c4fbec71b932d9f (diff)
downloadfzf-a5beb08ed72215e8b015851ad70bf3beca526429.tar.gz
Border around the header section
Close #4159
Diffstat (limited to 'src')
-rw-r--r--src/actiontype_string.go204
-rw-r--r--src/options.go245
-rw-r--r--src/terminal.go254
-rw-r--r--src/tui/light.go20
-rw-r--r--src/tui/tcell.go15
-rw-r--r--src/tui/tui.go38
6 files changed, 504 insertions, 272 deletions
diff --git a/src/actiontype_string.go b/src/actiontype_string.go
index 2996ce72..12162080 100644
--- a/src/actiontype_string.go
+++ b/src/actiontype_string.go
@@ -28,110 +28,112 @@ func _() {
_ = x[actChangeListLabel-17]
_ = x[actChangeInputLabel-18]
_ = x[actChangeHeader-19]
- _ = x[actChangeMulti-20]
- _ = x[actChangePreviewLabel-21]
- _ = x[actChangePrompt-22]
- _ = x[actChangeQuery-23]
- _ = x[actClearScreen-24]
- _ = x[actClearQuery-25]
- _ = x[actClearSelection-26]
- _ = x[actClose-27]
- _ = x[actDeleteChar-28]
- _ = x[actDeleteCharEof-29]
- _ = x[actEndOfLine-30]
- _ = x[actFatal-31]
- _ = x[actForwardChar-32]
- _ = x[actForwardWord-33]
- _ = x[actKillLine-34]
- _ = x[actKillWord-35]
- _ = x[actUnixLineDiscard-36]
- _ = x[actUnixWordRubout-37]
- _ = x[actYank-38]
- _ = x[actBackwardKillWord-39]
- _ = x[actSelectAll-40]
- _ = x[actDeselectAll-41]
- _ = x[actToggle-42]
- _ = x[actToggleSearch-43]
- _ = x[actToggleAll-44]
- _ = x[actToggleDown-45]
- _ = x[actToggleUp-46]
- _ = x[actToggleIn-47]
- _ = x[actToggleOut-48]
- _ = x[actToggleTrack-49]
- _ = x[actToggleTrackCurrent-50]
- _ = x[actToggleHeader-51]
- _ = x[actToggleWrap-52]
- _ = x[actToggleMultiLine-53]
- _ = x[actToggleHscroll-54]
- _ = x[actTrackCurrent-55]
- _ = x[actUntrackCurrent-56]
- _ = x[actDown-57]
- _ = x[actUp-58]
- _ = x[actPageUp-59]
- _ = x[actPageDown-60]
- _ = x[actPosition-61]
- _ = x[actHalfPageUp-62]
- _ = x[actHalfPageDown-63]
- _ = x[actOffsetUp-64]
- _ = x[actOffsetDown-65]
- _ = x[actOffsetMiddle-66]
- _ = x[actJump-67]
- _ = x[actJumpAccept-68]
- _ = x[actPrintQuery-69]
- _ = x[actRefreshPreview-70]
- _ = x[actReplaceQuery-71]
- _ = x[actToggleSort-72]
- _ = x[actShowPreview-73]
- _ = x[actHidePreview-74]
- _ = x[actTogglePreview-75]
- _ = x[actTogglePreviewWrap-76]
- _ = x[actTransform-77]
- _ = x[actTransformBorderLabel-78]
- _ = x[actTransformListLabel-79]
- _ = x[actTransformInputLabel-80]
- _ = x[actTransformHeader-81]
- _ = x[actTransformPreviewLabel-82]
- _ = x[actTransformPrompt-83]
- _ = x[actTransformQuery-84]
- _ = x[actPreview-85]
- _ = x[actChangePreview-86]
- _ = x[actChangePreviewWindow-87]
- _ = x[actPreviewTop-88]
- _ = x[actPreviewBottom-89]
- _ = x[actPreviewUp-90]
- _ = x[actPreviewDown-91]
- _ = x[actPreviewPageUp-92]
- _ = x[actPreviewPageDown-93]
- _ = x[actPreviewHalfPageUp-94]
- _ = x[actPreviewHalfPageDown-95]
- _ = x[actPrevHistory-96]
- _ = x[actPrevSelected-97]
- _ = x[actPrint-98]
- _ = x[actPut-99]
- _ = x[actNextHistory-100]
- _ = x[actNextSelected-101]
- _ = x[actExecute-102]
- _ = x[actExecuteSilent-103]
- _ = x[actExecuteMulti-104]
- _ = x[actSigStop-105]
- _ = x[actFirst-106]
- _ = x[actLast-107]
- _ = x[actReload-108]
- _ = x[actReloadSync-109]
- _ = x[actDisableSearch-110]
- _ = x[actEnableSearch-111]
- _ = x[actSelect-112]
- _ = x[actDeselect-113]
- _ = x[actUnbind-114]
- _ = x[actRebind-115]
- _ = x[actBecome-116]
- _ = x[actShowHeader-117]
- _ = x[actHideHeader-118]
+ _ = x[actChangeHeaderLabel-20]
+ _ = x[actChangeMulti-21]
+ _ = x[actChangePreviewLabel-22]
+ _ = x[actChangePrompt-23]
+ _ = x[actChangeQuery-24]
+ _ = x[actClearScreen-25]
+ _ = x[actClearQuery-26]
+ _ = x[actClearSelection-27]
+ _ = x[actClose-28]
+ _ = x[actDeleteChar-29]
+ _ = x[actDeleteCharEof-30]
+ _ = x[actEndOfLine-31]
+ _ = x[actFatal-32]
+ _ = x[actForwardChar-33]
+ _ = x[actForwardWord-34]
+ _ = x[actKillLine-35]
+ _ = x[actKillWord-36]
+ _ = x[actUnixLineDiscard-37]
+ _ = x[actUnixWordRubout-38]
+ _ = x[actYank-39]
+ _ = x[actBackwardKillWord-40]
+ _ = x[actSelectAll-41]
+ _ = x[actDeselectAll-42]
+ _ = x[actToggle-43]
+ _ = x[actToggleSearch-44]
+ _ = x[actToggleAll-45]
+ _ = x[actToggleDown-46]
+ _ = x[actToggleUp-47]
+ _ = x[actToggleIn-48]
+ _ = x[actToggleOut-49]
+ _ = x[actToggleTrack-50]
+ _ = x[actToggleTrackCurrent-51]
+ _ = x[actToggleHeader-52]
+ _ = x[actToggleWrap-53]
+ _ = x[actToggleMultiLine-54]
+ _ = x[actToggleHscroll-55]
+ _ = x[actTrackCurrent-56]
+ _ = x[actUntrackCurrent-57]
+ _ = x[actDown-58]
+ _ = x[actUp-59]
+ _ = x[actPageUp-60]
+ _ = x[actPageDown-61]
+ _ = x[actPosition-62]
+ _ = x[actHalfPageUp-63]
+ _ = x[actHalfPageDown-64]
+ _ = x[actOffsetUp-65]
+ _ = x[actOffsetDown-66]
+ _ = x[actOffsetMiddle-67]
+ _ = x[actJump-68]
+ _ = x[actJumpAccept-69]
+ _ = x[actPrintQuery-70]
+ _ = x[actRefreshPreview-71]
+ _ = x[actReplaceQuery-72]
+ _ = x[actToggleSort-73]
+ _ = x[actShowPreview-74]
+ _ = x[actHidePreview-75]
+ _ = x[actTogglePreview-76]
+ _ = x[actTogglePreviewWrap-77]
+ _ = x[actTransform-78]
+ _ = x[actTransformBorderLabel-79]
+ _ = x[actTransformListLabel-80]
+ _ = x[actTransformInputLabel-81]
+ _ = x[actTransformHeader-82]
+ _ = x[actTransformHeaderLabel-83]
+ _ = x[actTransformPreviewLabel-84]
+ _ = x[actTransformPrompt-85]
+ _ = x[actTransformQuery-86]
+ _ = x[actPreview-87]
+ _ = x[actChangePreview-88]
+ _ = x[actChangePreviewWindow-89]
+ _ = x[actPreviewTop-90]
+ _ = x[actPreviewBottom-91]
+ _ = x[actPreviewUp-92]
+ _ = x[actPreviewDown-93]
+ _ = x[actPreviewPageUp-94]
+ _ = x[actPreviewPageDown-95]
+ _ = x[actPreviewHalfPageUp-96]
+ _ = x[actPreviewHalfPageDown-97]
+ _ = x[actPrevHistory-98]
+ _ = x[actPrevSelected-99]
+ _ = x[actPrint-100]
+ _ = x[actPut-101]
+ _ = x[actNextHistory-102]
+ _ = x[actNextSelected-103]
+ _ = x[actExecute-104]
+ _ = x[actExecuteSilent-105]
+ _ = x[actExecuteMulti-106]
+ _ = x[actSigStop-107]
+ _ = x[actFirst-108]
+ _ = x[actLast-109]
+ _ = x[actReload-110]
+ _ = x[actReloadSync-111]
+ _ = x[actDisableSearch-112]
+ _ = x[actEnableSearch-113]
+ _ = x[actSelect-114]
+ _ = x[actDeselect-115]
+ _ = x[actUnbind-116]
+ _ = x[actRebind-117]
+ _ = x[actBecome-118]
+ _ = x[actShowHeader-119]
+ _ = x[actHideHeader-120]
}
-const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
+const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeListLabelactChangeInputLabelactChangeHeaderactChangeHeaderLabelactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformListLabelactTransformInputLabelactTransformHeaderactTransformHeaderLabelactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
-var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 293, 314, 329, 343, 357, 370, 387, 395, 408, 424, 436, 444, 458, 472, 483, 494, 512, 529, 536, 555, 567, 581, 590, 605, 617, 630, 641, 652, 664, 678, 699, 714, 727, 745, 761, 776, 793, 800, 805, 814, 825, 836, 849, 864, 875, 888, 903, 910, 923, 936, 953, 968, 981, 995, 1009, 1025, 1045, 1057, 1080, 1101, 1123, 1141, 1165, 1183, 1200, 1210, 1226, 1248, 1261, 1277, 1289, 1303, 1319, 1337, 1357, 1379, 1393, 1408, 1416, 1422, 1436, 1451, 1461, 1477, 1492, 1502, 1510, 1517, 1526, 1539, 1555, 1570, 1579, 1590, 1599, 1608, 1617, 1630, 1643}
+var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 245, 264, 279, 299, 313, 334, 349, 363, 377, 390, 407, 415, 428, 444, 456, 464, 478, 492, 503, 514, 532, 549, 556, 575, 587, 601, 610, 625, 637, 650, 661, 672, 684, 698, 719, 734, 747, 765, 781, 796, 813, 820, 825, 834, 845, 856, 869, 884, 895, 908, 923, 930, 943, 956, 973, 988, 1001, 1015, 1029, 1045, 1065, 1077, 1100, 1121, 1143, 1161, 1184, 1208, 1226, 1243, 1253, 1269, 1291, 1304, 1320, 1332, 1346, 1362, 1380, 1400, 1422, 1436, 1451, 1459, 1465, 1479, 1494, 1504, 1520, 1535, 1545, 1553, 1560, 1569, 1582, 1598, 1613, 1622, 1633, 1642, 1651, 1660, 1673, 1686}
func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) {
diff --git a/src/options.go b/src/options.go
index b21396a5..f6b7245f 100644
--- a/src/options.go
+++ b/src/options.go
@@ -103,6 +103,14 @@ Usage: fzf [options]
[POSITIVE_INTEGER: columns from left|
NEGATIVE_INTEGER: columns from right][:bottom]
(default: 0 or center)
+ --header-border[=STYLE] Draw border around the header section
+ [rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
+ top|bottom|left|right|none] (default: rounded)
+ --header-label=LABEL Label to print on the header border
+ --header-label-pos=COL Position of the header label
+ [POSITIVE_INTEGER: columns from left|
+ NEGATIVE_INTEGER: columns from right][:bottom]
+ (default: 0 or center)
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--info=STYLE Finder info style
@@ -467,104 +475,106 @@ type walkerOpts struct {
// Options stores the values of command-line options
type Options struct {
- Input chan string
- Output chan string
- NoWinpty bool
- Tmux *tmuxOptions
- ForceTtyIn bool
- ProxyScript string
- Bash bool
- Zsh bool
- Fish bool
- Man bool
- Fuzzy bool
- FuzzyAlgo algo.Algo
- Scheme string
- Extended bool
- Phony bool
- Case Case
- Normalize bool
- Nth []Range
- WithNth []Range
- Delimiter Delimiter
- Sort int
- Track trackOption
- Tac bool
- Tail int
- Criteria []criterion
- Multi int
- Ansi bool
- Mouse bool
- Theme *tui.ColorTheme
- Black bool
- Bold bool
- Height heightSpec
- MinHeight int
- Layout layoutType
- Cycle bool
- Wrap bool
- WrapSign *string
- MultiLine bool
- CursorLine bool
- KeepRight bool
- Hscroll bool
- HscrollOff int
- ScrollOff int
- FileWord bool
- InfoStyle infoStyle
- InfoPrefix string
- InfoCommand string
- Separator *string
- JumpLabels string
- Prompt string
- Pointer *string
- Marker *string
- MarkerMulti *[3]string
- Query string
- Select1 bool
- Exit0 bool
- Filter *string
- ToggleSort bool
- Expect map[tui.Event]string
- Keymap map[tui.Event][]*action
- Preview previewOpts
- PrintQuery bool
- ReadZero bool
- Printer func(string)
- PrintSep string
- Sync bool
- History *History
- Header []string
- HeaderLines int
- HeaderFirst bool
- Gap int
- Ellipsis *string
- Scrollbar *string
- Margin [4]sizeSpec
- Padding [4]sizeSpec
- BorderShape tui.BorderShape
- ListBorderShape tui.BorderShape
- InputBorderShape tui.BorderShape
- InputLabel labelOpts
- BorderLabel labelOpts
- ListLabel labelOpts
- PreviewLabel labelOpts
- Unicode bool
- Ambidouble bool
- Tabstop int
- WithShell string
- ListenAddr *listenAddress
- Unsafe bool
- ClearOnExit bool
- WalkerOpts walkerOpts
- WalkerRoot []string
- WalkerSkip []string
- Version bool
- Help bool
- CPUProfile string
- MEMProfile string
- BlockProfile string
- MutexProfile string
+ Input chan string
+ Output chan string
+ NoWinpty bool
+ Tmux *tmuxOptions
+ ForceTtyIn bool
+ ProxyScript string
+ Bash bool
+ Zsh bool
+ Fish bool
+ Man bool
+ Fuzzy bool
+ FuzzyAlgo algo.Algo
+ Scheme string
+ Extended bool
+ Phony bool
+ Case Case
+ Normalize bool
+ Nth []Range
+ WithNth []Range
+ Delimiter Delimiter
+ Sort int
+ Track trackOption
+ Tac bool
+ Tail int
+ Criteria []criterion
+ Multi int
+ Ansi bool
+ Mouse bool
+ Theme *tui.ColorTheme
+ Black bool
+ Bold bool
+ Height heightSpec
+ MinHeight int
+ Layout layoutType
+ Cycle bool
+ Wrap bool
+ WrapSign *string
+ MultiLine bool
+ CursorLine bool
+ KeepRight bool
+ Hscroll bool
+ HscrollOff int
+ ScrollOff int
+ FileWord bool
+ InfoStyle infoStyle
+ InfoPrefix string
+ InfoCommand string
+ Separator *string
+ JumpLabels string
+ Prompt string
+ Pointer *string
+ Marker *string
+ MarkerMulti *[3]string
+ Query string
+ Select1 bool
+ Exit0 bool
+ Filter *string
+ ToggleSort bool
+ Expect map[tui.Event]string
+ Keymap map[tui.Event][]*action
+ Preview previewOpts
+ PrintQuery bool
+ ReadZero bool
+ Printer func(string)
+ PrintSep string
+ Sync bool
+ History *History
+ Header []string
+ HeaderLines int
+ HeaderFirst bool
+ Gap int
+ Ellipsis *string
+ Scrollbar *string
+ Margin [4]sizeSpec
+ Padding [4]sizeSpec
+ BorderShape tui.BorderShape
+ ListBorderShape tui.BorderShape
+ InputBorderShape tui.BorderShape
+ HeaderBorderShape tui.BorderShape
+ InputLabel labelOpts
+ HeaderLabel labelOpts
+ BorderLabel labelOpts
+ ListLabel labelOpts
+ PreviewLabel labelOpts
+ Unicode bool
+ Ambidouble bool
+ Tabstop int
+ WithShell string
+ ListenAddr *listenAddress
+ Unsafe bool
+ ClearOnExit bool
+ WalkerOpts walkerOpts
+ WalkerRoot []string
+ WalkerSkip []string
+ Version bool
+ Help bool
+ CPUProfile string
+ MEMProfile string
+ BlockProfile string
+ MutexProfile string
}
func filterNonEmpty(input []string) []string {
@@ -1251,6 +1261,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.InputBorder)
case "input-label":
mergeAttr(&theme.InputLabel)
+ case "header-border":
+ mergeAttr(&theme.HeaderBorder)
+ case "header-label":
+ mergeAttr(&theme.HeaderLabel)
case "spinner":
mergeAttr(&theme.Spinner)
case "info":
@@ -1259,8 +1273,10 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro
mergeAttr(&theme.Cursor)
case "marker":
mergeAttr(&theme.Marker)
- case "header":
+ case "header", "header-fg":
mergeAttr(&theme.Header)
+ case "header-bg":
+ mergeAttr(&theme.HeaderBg)
default:
fail()
}
@@ -1314,7 +1330,7 @@ const (
func init() {
executeRegexp = regexp.MustCompile(
- `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|list-label|preview-label|input-label)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`)
+ `(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|(?:border|list|preview|input|header)-label|header)|transform|change-(?:preview-window|preview|multi)|(?:re|un)bind|pos|put|print)`)
splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
}
@@ -1680,6 +1696,8 @@ func isExecuteAction(str string) actionType {
return actChangePreviewLabel
case "change-input-label":
return actChangeInputLabel
+ case "change-header-label":
+ return actChangeHeaderLabel
case "change-preview-window":
return actChangePreviewWindow
case "change-preview":
@@ -1712,6 +1730,8 @@ func isExecuteAction(str string) actionType {
return actTransformPreviewLabel
case "transform-input-label":
return actTransformInputLabel
+ case "transform-header-label":
+ return actTransformHeaderLabel
case "transform-header":
return actTransformHeader
case "transform-prompt":
@@ -2542,6 +2562,27 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err := parseLabelPosition(&opts.ListLabel, pos); err != nil {
return err
}
+ case "--no-header-border":
+ opts.HeaderBorderShape = tui.BorderNone
+ case "--header-border":
+ hasArg, arg := optionalNextString(allArgs, &i)
+ if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg); err != nil {
+ return err
+ }
+ case "--no-header-label":
+ opts.HeaderLabel.label = ""
+ case "--header-label":
+ if opts.HeaderLabel.label, err = nextString(allArgs, &i, "header label required"); err != nil {
+ return err
+ }
+ case "--header-label-pos":
+ pos, err := nextString(allArgs, &i, "header label position required (positive or negative integer or 'center')")
+ if err != nil {
+ return err
+ }
+ if err := parseLabelPosition(&opts.HeaderLabel, pos); err != nil {
+ return err
+ }
case "--no-input-border":
opts.InputBorderShape = tui.BorderNone
case "--input-border":
@@ -3003,6 +3044,10 @@ func postProcessOptions(opts *Options) error {
opts.InputBorderShape = tui.BorderNone
}
+ if opts.HeaderBorderShape == tui.BorderUndefined {
+ opts.HeaderBorderShape = tui.BorderNone
+ }
+
if opts.Pointer == nil {
defaultPointer := "▌"
if !opts.Unicode {
diff --git a/src/terminal.go b/src/terminal.go
index 8076462e..764284a9 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -180,13 +180,13 @@ type itemLine struct {
}
func (t *Terminal) markEmptyLine(line int) {
- if t.window != t.inputWindow {
+ if t.window != t.inputWindow && t.window != t.headerWindow {
t.prevLines[line] = itemLine{valid: true, firstLine: line, empty: true}
}
}
func (t *Terminal) markOtherLine(line int) {
- if t.window != t.inputWindow {
+ if t.window != t.inputWindow && t.window != t.headerWindow {
t.prevLines[line] = itemLine{valid: true, firstLine: line, other: true}
}
}
@@ -249,6 +249,9 @@ type Terminal struct {
inputLabel labelPrinter
inputLabelLen int
inputLabelOpts labelOpts
+ headerLabel labelPrinter
+ headerLabelLen int
+ headerLabelOpts labelOpts
pointer string
pointerLen int
pointerEmpty string
@@ -307,6 +310,7 @@ type Terminal struct {
borderShape tui.BorderShape
listBorderShape tui.BorderShape
inputBorderShape tui.BorderShape
+ headerBorderShape tui.BorderShape
listLabel labelPrinter
listLabelLen int
listLabelOpts labelOpts
@@ -317,6 +321,8 @@ type Terminal struct {
window tui.Window
inputWindow tui.Window
inputBorder tui.Window
+ headerWindow tui.Window
+ headerBorder tui.Window
wborder tui.Window
pborder tui.Window
pwindow tui.Window
@@ -406,6 +412,7 @@ const (
reqFullRedraw
reqResize
reqRedrawInputLabel
+ reqRedrawHeaderLabel
reqRedrawListLabel
reqRedrawBorderLabel
reqRedrawPreviewLabel
@@ -450,6 +457,7 @@ const (
actChangeListLabel
actChangeInputLabel
actChangeHeader
+ actChangeHeaderLabel
actChangeMulti
actChangePreviewLabel
actChangePrompt
@@ -512,6 +520,7 @@ const (
actTransformListLabel
actTransformInputLabel
actTransformHeader
+ actTransformHeaderLabel
actTransformPreviewLabel
actTransformPrompt
actTransformQuery
@@ -847,6 +856,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
borderShape: opts.BorderShape,
listBorderShape: opts.ListBorderShape,
inputBorderShape: opts.InputBorderShape,
+ headerBorderShape: opts.HeaderBorderShape,
borderWidth: 1,
listLabel: nil,
listLabelOpts: opts.ListLabel,
@@ -856,6 +866,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
previewLabelOpts: opts.PreviewLabel,
inputLabel: nil,
inputLabelOpts: opts.InputLabel,
+ headerLabel: nil,
+ headerLabelOpts: opts.HeaderLabel,
cleanExit: opts.ClearOnExit,
executor: executor,
paused: opts.Phony,
@@ -910,7 +922,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
lastFocus: minItem.Index()}
// This should be called before accessing tui.Color*
- tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black)
+ tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible())
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
// Pre-calculated empty pointer and marker signs
@@ -920,6 +932,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(opts.BorderLabel.label, &tui.ColBorderLabel, false)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(opts.PreviewLabel.label, &tui.ColPreviewLabel, false)
t.inputLabel, t.inputLabelLen = t.ansiLabelPrinter(opts.InputLabel.label, &tui.ColInputLabel, false)
+ t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(opts.HeaderLabel.label, &tui.ColHeaderLabel, false)
// Disable separator by default if input border is set
if opts.Separator == nil && !t.inputBorderShape.Visible() || opts.Separator != nil && len(*opts.Separator) > 0 {
@@ -1064,6 +1077,13 @@ func (t *Terminal) visibleHeaderLines() int {
return len(t.header0) + t.headerLines
}
+func (t *Terminal) visibleHeaderLinesInList() int {
+ if t.headerWindow != nil {
+ return 0
+ }
+ return t.visibleHeaderLines()
+}
+
// Extra number of lines needed to display fzf
func (t *Terminal) extraLines() int {
extra := t.visibleHeaderLines() + 1
@@ -1148,10 +1168,10 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
// Temporarily switch 'window' so that we can use the existing windows with
// a different window
-func (t *Terminal) withInputWindow(f func()) {
+func (t *Terminal) withWindow(w tui.Window, f func()) {
prevWindow := t.window
- if t.inputWindow != nil {
- t.window = t.inputWindow
+ if w != nil {
+ t.window = w
}
f()
t.window = prevWindow
@@ -1184,7 +1204,7 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
output := func() {
wrap := t.wrap
t.wrap = false
- t.withInputWindow(func() {
+ t.withWindow(t.inputWindow, func() {
line := t.promptLine()
t.printHighlighted(
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, line, line, true, nil, nil)
@@ -1605,6 +1625,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if t.wborder != nil {
t.wborder = nil
}
+ if t.headerWindow != nil {
+ t.headerWindow = nil
+ }
+ if t.headerBorder != nil {
+ t.headerBorder = nil
+ }
if t.inputWindow != nil {
t.inputWindow = nil
}
@@ -1651,26 +1677,42 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
height -= paddingInt[0] + paddingInt[2]
// Adjust position and size of the list window if input border is set
- inputWindowHeight := 0
inputBorderHeight := 0
+ availableLines := height
shift := 0
shrink := 0
- hasInputWindow := t.inputBorderShape.Visible()
+ hasInputWindow := t.inputBorderShape.Visible() || t.headerBorderShape.Visible()
if hasInputWindow {
- inputWindowHeight = 2
+ inputWindowHeight := 2
if t.noSeparatorLine() {
inputWindowHeight--
}
- inputBorderHeight = borderLines(t.inputBorderShape) + inputWindowHeight
+ inputBorderHeight = util.Min(availableLines, borderLines(t.inputBorderShape)+inputWindowHeight)
if t.layout == layoutReverse {
shift = inputBorderHeight
shrink = inputBorderHeight
} else {
- shift = 0
shrink = inputBorderHeight
}
+ availableLines -= inputBorderHeight
}
+ // Adjust position and size of the list window if header border is set
+ hasHeaderWindow := t.visibleHeaderLines() > 0 && (t.headerBorderShape.Visible() || t.inputBorderShape.Visible())
+ headerBorderHeight := 0
+ if hasHeaderWindow {
+ headerWindowHeight := t.visibleHeaderLines()
+ headerBorderHeight = util.Min(availableLines, borderLines(t.headerBorderShape)+headerWindowHeight)
+ if t.layout == layoutReverse {
+ shift += headerBorderHeight
+ shrink += headerBorderHeight
+ } else {
+ shrink += headerBorderHeight
+ }
+ availableLines -= headerBorderHeight
+ }
+
+ // Set up list border
hasListBorder := t.listBorderShape.Visible()
innerWidth := width
innerHeight := height
@@ -1729,8 +1771,6 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
// Need a column to show scrollbar
pwidth -= 1
}
- pwidth = util.Max(0, pwidth)
- pheight = util.Max(0, pheight)
t.pwindow = t.tui.NewWindow(y, x, pwidth, pheight, tui.WindowPreview, noBorder, true)
if !hadPreviewWindow {
t.pwindow.Erase()
@@ -1760,6 +1800,14 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if previewOpts.hidden {
return
}
+
+ maxPreviewLines := availableLines
+ if t.wborder != nil {
+ maxPreviewLines -= t.wborder.Height()
+ } else {
+ maxPreviewLines -= util.Max(0, innerHeight-pheight-shrink)
+ }
+ pheight = util.Min(pheight, maxPreviewLines)
if previewOpts.position == posUp {
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
t.window = t.tui.NewWindow(
@@ -1873,43 +1921,73 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
innerHeight-shrink, tui.WindowList, noBorder, true)
}
- // Set up input border
- if hasInputWindow {
- w := t.wborder
- if t.wborder == nil {
- w = t.window
- }
- if t.layout == layoutReverse {
- t.inputBorder = t.tui.NewWindow(
- w.Top()-shrink,
- w.Left(),
- w.Width(),
- util.Min(shrink, screenHeight), tui.WindowInput, tui.MakeBorderStyle(t.inputBorderShape, t.unicode), true)
- } else {
- t.inputBorder = t.tui.NewWindow(
- w.Top()+w.Height(),
- w.Left(),
- w.Width(),
- util.Min(shrink, screenHeight), tui.WindowInput, tui.MakeBorderStyle(t.inputBorderShape, t.unicode), true)
- }
- top := t.inputBorder.Top()
- left := t.inputBorder.Left()
- if t.inputBorderShape.HasTop() {
+ createInnerWindow := func(b tui.Window, shape tui.BorderShape, windowType tui.WindowType) tui.Window {
+ top := b.Top()
+ left := b.Left()
+ if shape.HasTop() {
top++
}
- if t.inputBorderShape.HasLeft() {
+ if shape.HasLeft() {
left += t.borderWidth + 1
}
- width := t.inputBorder.Width() - borderColumns(t.inputBorderShape, t.borderWidth)
- if t.inputBorderShape.HasRight() {
+ width := b.Width() - borderColumns(shape, t.borderWidth)
+ if shape.HasRight() {
width++
}
- t.inputWindow = t.tui.NewWindow(
- top,
- left,
- width,
- t.inputBorder.Height()-borderLines(t.inputBorderShape),
- tui.WindowInput, noBorder, true)
+ height := b.Height() - borderLines(shape)
+ return t.tui.NewWindow(top, left, width, height, windowType, noBorder, true)
+ }
+
+ // Set up input border
+ w := t.wborder
+ if t.wborder == nil {
+ w = t.window
+ }
+ if hasInputWindow {
+ var btop int
+ if hasHeaderWindow && t.headerFirst {
+ if t.layout == layoutReverse {
+ btop = w.Top() - inputBorderHeight
+ } else {
+ btop = w.Top() + w.Height()
+ }
+ } else {
+ if t.layout == layoutReverse {
+ btop = w.Top() - shrink
+ } else {
+ btop = w.Top() + w.Height() + headerBorderHeight
+ }
+ }
+ t.inputBorder = t.tui.NewWindow(
+ btop,
+ w.Left(),
+ w.Width(),
+ inputBorderHeight, tui.WindowInput, tui.MakeBorderStyle(t.inputBorderShape, t.unicode), true)
+ t.inputWindow = createInnerWindow(t.inputBorder, t.inputBorderShape, tui.WindowInput)
+ }
+
+ // Set up header border
+ if hasHeaderWindow {
+ var btop int
+ if hasInputWindow && t.headerFirst {
+ if t.layout == layoutReverse {
+ btop = w.Top() - shrink
+ } else {
+ btop = w.Top() + w.Height() + inputBorderHeight
+ }
+ } else {
+ if t.layout == layoutReverse {
+ btop = w.Top() - headerBorderHeight
+ } else {
+ btop = w.Top() + w.Height()
+ }
+ }
+ t.headerBorder = t.tui.NewWindow(
+ btop,
+ w.Left(),
+ w.Width(),
+ headerBorderHeight, tui.WindowHeader, tui.MakeBorderStyle(t.headerBorderShape, t.unicode), true)
+ t.headerWindow = createInnerWindow(t.headerBorder, t.headerBorderShape, tui.WindowHeader)
}
// Print border label
@@ -1917,6 +1995,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.activePreviewOpts.border, false)
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, false)
+ t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, false)
}
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
@@ -1960,7 +2039,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
case layoutDefault:
y = h - y - 1
case layoutReverseList:
- n := 2 + t.visibleHeaderLines()
+ n := 2 + t.visibleHeaderLinesInList()
if t.noSeparatorLine() {
n--
}
@@ -2014,7 +2093,7 @@ func (t *Terminal) promptLine() int {
if !t.noSeparatorLine() {
max--
}
- return util.Min(t.visibleHeaderLines(), max)
+ return util.Min(t.visibleHeaderLinesInList(), max)
}
return 0
}
@@ -2059,9 +2138,7 @@ func (t *Terminal) trimMessage(message string, maxWidth int) string {
}
func (t *Terminal) printInfo() {
- t.withInputWindow(func() {
- t.printInfoImpl()
- })
+ t.withWindow(t.inputWindow, func() { t.printInfoImpl() })
}
func (t *Terminal) printInfoImpl() {
@@ -2258,11 +2335,23 @@ func (t *Terminal) printInfoImpl() {
}
func (t *Terminal) printHeader() {
+ headerLines := t.visibleHeaderLines()
+ if t.headerBorderShape.Visible() && (t.headerWindow == nil && headerLines > 0 || t.headerWindow != nil && headerLines != t.headerWindow.Height()) {
+ t.resizeWindows(false, true)
+ t.printList()
+ t.printPrompt()
+ t.printInfo()
+ t.printPreview()
+ }
+ t.withWindow(t.headerWindow, func() { t.printHeaderImpl() })
+}
+
+func (t *Terminal) printHeaderImpl() {
if t.visibleHeaderLines() == 0 {
return
}
max := t.window.Height()
- if t.inputWindow == nil && t.headerFirst {
+ if t.inputWindow == nil && t.headerWindow == nil && t.headerFirst {
max--
if !t.noSeparatorLine() {
max--
@@ -2276,13 +2365,14 @@ func (t *Terminal) printHeader() {
}
// Wrapping is not supported for header
wrap := t.wrap
+ indent := strings.Repeat(" ", t.pointerLen+t.markerLen)
t.wrap = false
for idx, lineStr := range append(append([]string{}, t.header0...), t.header...) {
line := idx
if needReverse && idx < len(t.header0) {
line = len(t.header0) - idx - 1
}
- if t.inputWindow == nil && !t.headerFirst {
+ if t.inputWindow == nil && t.headerWindow == nil && !t.headerFirst {
line++
if !t.noSeparatorLine() {
line++
@@ -2299,7 +2389,7 @@ func (t *Terminal) printHeader() {
t.printHighlighted(Result{item: item},
tui.ColHeader, tui.ColHeader, false, false, line, line, true,
- func(markerClass) { t.window.Print(strings.Repeat(" ", t.pointerLen+t.markerLen)) }, nil)
+ func(markerClass) { t.window.Print(indent) }, nil)
}
t.wrap = wrap
}
@@ -2327,7 +2417,7 @@ func (t *Terminal) printList() {
count := t.merger.Length() - t.offset
// Start line
- startLine := t.promptLines() + t.visibleHeaderLines()
+ startLine := t.promptLines() + t.visibleHeaderLinesInList()
maxy += startLine
barRange := [2]int{startLine + barStart, startLine + barStart + barLength}
@@ -3187,7 +3277,7 @@ func (t *Terminal) printAll() {
func (t *Terminal) flush() {
t.placeCursor()
if !t.suppress {
- windows := make([]tui.Window, 0, 7)
+ windows := make([]tui.Window, 0, 9)
if t.border != nil {
windows = append(windows, t.border)
}
@@ -3203,6 +3293,12 @@ func (t *Terminal) flush() {
if t.window != nil {
windows = append(windows, t.window)
}
+ if t.headerBorder != nil {
+ windows = append(windows, t.headerBorder)
+ }
+ if t.headerWindow != nil {
+ windows = append(windows, t.headerWindow)
+ }
if t.inputBorder != nil {
windows = append(windows, t.inputBorder)
}
@@ -4110,6 +4206,8 @@ func (t *Terminal) Loop() error {
}
case reqRedrawInputLabel:
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
+ case reqRedrawHeaderLabel:
+ t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, true)
case reqRedrawListLabel:
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
case reqRedrawBorderLabel:
@@ -4493,6 +4591,12 @@ func (t *Terminal) Loop() error {
} else {
req(reqHeader)
}
+ case actChangeHeaderLabel:
+ t.headerLabelOpts.label = a.a
+ if t.headerBorder != nil {
+ t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(a.a, &tui.ColHeaderLabel, false)
+ req(reqRedrawHeaderLabel)
+ }
case actChangeInputLabel:
t.inputLabelOpts.label = a.a
if t.inputBorder != nil {
@@ -4522,6 +4626,13 @@ func (t *Terminal) Loop() error {
if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil {
return doActions(actions)
}
+ case actTransformHeaderLabel:
+ label := t.executeCommand(a.a, false, true, true, true, "")
+ t.headerLabelOpts.label = label
+ if t.headerBorder != nil {
+ t.headerLabel, t.headerLabelLen = t.ansiLabelPrinter(label, &tui.ColHeaderLabel, false)
+ req(reqRedrawHeaderLabel)
+ }
case actTransformInputLabel:
label := t.executeCommand(a.a, false, true, true, true, "")
t.inputLabelOpts.label = label
@@ -5028,25 +5139,25 @@ func (t *Terminal) Loop() error {
case posUp:
if t.pborder.Enclose(my, mx) && my == t.pborder.Top()+t.pborder.Height()-1 {
pborderDragging = 0
- } else if t.listBorderShape.HasTop() && t.wborder.Enclose(my, mx) && my == t.wborder.Top() {
+ } else if t.listBorderShape.HasTop() && t.pborder.EncloseX(mx) && my == t.wborder.Top() {
pborderDragging = 1
}
case posDown:
if t.pborder.Enclose(my, mx) && my == t.pborder.Top() {
pborderDragging = 0
- } else if t.listBorderShape.HasBottom() && t.wborder.Enclose(my, mx) && my == t.wborder.Top()+t.wborder.Height()-1 {
+ } else if t.listBorderShape.HasBottom() && t.pborder.EncloseX(mx) && my == t.wborder.Top()+t.wborder.Height()-1 {
pborderDragging = 1
}
case posLeft:
if t.pborder.Enclose(my, mx) && mx == t.pborder.Left()+t.pborder.Width()-1 {
pborderDragging = 0
- } else if t.listBorderShape.HasLeft() && t.wborder.Enclose(my, mx) && mx == t.wborder.Left() {
+ } else if t.listBorderShape.HasLeft() && t.pborder.EncloseY(my) && mx == t.wborder.Left() {
pborderDragging = 1
}
case posRight:
if t.pborder.Enclose(my, mx) && mx == t.pborder.Left() {
pborderDragging = 0
- } else if t.listBorderShape.HasRight() && t.wborder.Enclose(my, mx) && mx == t.wborder.Left()+t.wborder.Width()-1 {
+ } else if t.listBorderShape.HasRight() && t.pborder.EncloseY(my) && mx == t.wborder.Left()+t.wborder.Width()-1 {
pborderDragging = 1
}
}
@@ -5103,6 +5214,20 @@ func (t *Terminal) Loop() error {
break
}
+ // Inside the header window
+ // TODO: Should we trigger this on mouse up instead?
+ // Should we still trigger it when the position has changed from the down event?
+ if t.headerVisible && t.headerWindow != nil && t.headerWindow.Enclose(my, mx) {
+ mx -= t.headerWindow.Left() + t.pointerLen + t.markerLen
+ my -= t.headerWindow.Top()
+ if mx < 0 {
+ break
+ }
+ t.clickHeaderLine = my + 1
+ t.clickHeaderColumn = mx + 1
+ return doActions(actionsFor(tui.ClickHeader))
+ }
+
// Ignored
if !t.window.Enclose(my, mx) && !barDragging {
break
@@ -5111,7 +5236,7 @@ func (t *Terminal) Loop() error {
// Translate coordinates
mx -= t.window.Left()
my -= t.window.Top()
- min := t.promptLines() + t.visibleHeaderLines()
+ min := t.promptLines() + t.visibleHeaderLinesInList()
h := t.window.Height()
switch t.layout {
case layoutDefault:
@@ -5180,9 +5305,10 @@ func (t *Terminal) Loop() error {
}
}
return doActions(actionsFor(evt))
- } else if t.headerVisible {
+ } else if t.headerVisible && t.headerWindow == nil {
// Header
- numLines := t.visibleHeaderLines()
+ // TODO: Should we trigger this on mouse up instead?
+ numLines := t.visibleHeaderLinesInList()
lineOffset := 0
if t.inputWindow == nil && !t.headerFirst {
// offset for info line
@@ -5193,7 +5319,7 @@ func (t *Terminal) Loop() error {
}
}
my -= lineOffset
- mx -= 2 // offset gutter
+ mx -= t.pointerLen + t.markerLen
if my >= 0 && my < numLines && mx >= 0 {
if t.layout == layoutReverse {
t.clickHeaderLine = my + 1
@@ -5525,7 +5651,7 @@ func (t *Terminal) promptLines() int {
// Number of item lines in the list window
func (t *Terminal) maxItems() int {
- max := t.window.Height() - t.visibleHeaderLines() - t.promptLines()
+ max := t.window.Height() - t.visibleHeaderLinesInList() - t.promptLines()
return util.Max(max, 0)
}
diff --git a/src/tui/light.go b/src/tui/light.go
index 3cb1a8bc..48202bce 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -804,6 +804,9 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, wind
case WindowInput:
w.fg = r.theme.Input.Color
w.bg = r.theme.InputBg.Color
+ case WindowHeader:
+ w.fg = r.theme.Header.Color
+ w.bg = r.theme.HeaderBg.Color
case WindowPreview:
w.fg = r.theme.PreviewFg.Color
w.bg = r.theme.PreviewBg.Color
@@ -862,6 +865,8 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
color = ColListBorder
case WindowInput:
color = ColInputBorder
+ case WindowHeader:
+ color = ColHeaderBorder
case WindowPreview:
color = ColPreviewBorder
}
@@ -885,6 +890,8 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
color = ColListBorder
case WindowInput:
color = ColInputBorder
+ case WindowHeader:
+ color = ColHeaderBorder
case WindowPreview:
color = ColPreviewBorder
}
@@ -910,6 +917,8 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
color = ColListBorder
case WindowInput:
color = ColInputBorder
+ case WindowHeader:
+ color = ColHeaderBorder
case WindowPreview:
color = ColPreviewBorder
}
@@ -970,9 +979,16 @@ func (w *LightWindow) Y() int {
return w.posy
}
+func (w *LightWindow) EncloseX(x int) bool {
+ return x >= w.left && x < (w.left+w.width)
+}
+
+func (w *LightWindow) EncloseY(y int) bool {
+ return y >= w.top && y < (w.top+w.height)
+}
+
func (w *LightWindow) Enclose(y int, x int) bool {
- return x >= w.left && x < (w.left+w.width) &&
- y >= w.top && y < (w.top+w.height)
+ return w.EncloseX(x) && w.EncloseY(y)
}
func (w *LightWindow) Move(y int, x int) {
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index becdabcd..991052bd 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -557,6 +557,8 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
switch windowType {
case WindowList:
normal = ColListBorder
+ case WindowHeader:
+ normal = ColHeaderBorder
case WindowInput:
normal = ColInputBorder
case WindowPreview:
@@ -593,9 +595,16 @@ func (w *TcellWindow) EraseMaybe() bool {
return true
}
+func (w *TcellWindow) EncloseX(x int) bool {
+ return x >= w.left && x < (w.left+w.width)
+}
+
+func (w *TcellWindow) EncloseY(y int) bool {
+ return y >= w.top && y < (w.top+w.height)
+}
+
func (w *TcellWindow) Enclose(y int, x int) bool {
- return x >= w.left && x < (w.left+w.width) &&
- y >= w.top && y < (w.top+w.height)
+ return w.EncloseX(x) && w.EncloseY(y)
}
func (w *TcellWindow) Move(y int, x int) {
@@ -792,6 +801,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
style = ColBorder.style()
case WindowList:
style = ColListBorder.style()
+ case WindowHeader:
+ style = ColHeaderBorder.style()
case WindowInput:
style = ColInputBorder.style()
case WindowPreview:
diff --git a/src/tui/tui.go b/src/tui/tui.go
index 832109ce..db846f75 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -324,6 +324,9 @@ type ColorTheme struct {
Cursor ColorAttr
Marker ColorAttr
Header ColorAttr
+ HeaderBg ColorAttr
+ HeaderBorder ColorAttr
+ HeaderLabel ColorAttr
Separator ColorAttr
Scrollbar ColorAttr
Border ColorAttr
@@ -543,6 +546,7 @@ const (
WindowList
WindowPreview
WindowInput
+ WindowHeader
)
type Renderer interface {
@@ -583,6 +587,8 @@ type Window interface {
X() int
Y() int
+ EncloseX(x int) bool
+ EncloseY(y int) bool
Enclose(y int, x int) bool
Move(y int, x int)
@@ -639,6 +645,8 @@ var (
ColSpinner ColorPair
ColInfo ColorPair
ColHeader ColorPair
+ ColHeaderBorder ColorPair
+ ColHeaderLabel ColorPair
ColSeparator ColorPair
ColScrollbar ColorPair
ColBorder ColorPair
@@ -691,6 +699,9 @@ func EmptyTheme() *ColorTheme {
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBg: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
+ HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
}
}
@@ -731,6 +742,9 @@ func NoColorTheme() *ColorTheme {
InputBg: ColorAttr{colDefault, AttrUndefined},
InputBorder: ColorAttr{colDefault, AttrUndefined},
InputLabel: ColorAttr{colDefault, AttrUndefined},
+ HeaderBg: ColorAttr{colDefault, AttrUndefined},
+ HeaderBorder: ColorAttr{colDefault, AttrUndefined},
+ HeaderLabel: ColorAttr{colDefault, AttrUndefined},
}
}
@@ -845,10 +859,13 @@ func init() {
InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBg: ColorAttr{colUndefined, AttrUndefined},
+ HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
+ HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
}
}
-func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
+func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInputWindow bool, hasHeaderWindow bool) {
if forceBlack {
theme.Bg = ColorAttr{colBlack, AttrUndefined}
}
@@ -896,9 +913,22 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
theme.Separator = o(theme.ListBorder, theme.Separator)
theme.Scrollbar = o(theme.ListBorder, theme.Scrollbar)
theme.PreviewScrollbar = o(theme.PreviewBorder, theme.PreviewScrollbar)
- theme.InputBg = o(theme.Bg, o(theme.ListBg, theme.InputBg))
+ if hasInputWindow {
+ theme.InputBg = o(theme.Bg, theme.InputBg)
+ } else {
+ // We shouldn't use input-bg if there's no separate input window
+ // e.g. fzf --color 'list-bg:green,input-bg:red' --no-input-border
+ theme.InputBg = o(theme.Bg, theme.ListBg)
+ }
theme.InputBorder = o(theme.Border, theme.InputBorder)
theme.InputLabel = o(theme.BorderLabel, theme.InputLabel)
+ if hasHeaderWindow {
+ theme.HeaderBg = o(theme.Bg, theme.HeaderBg)
+ } else {
+ theme.HeaderBg = o(theme.Bg, theme.ListBg)
+ }
+ theme.HeaderBorder = o(theme.Border, theme.HeaderBorder)
+ theme.HeaderLabel = o(theme.BorderLabel, theme.HeaderLabel)
initPalette(theme)
}
@@ -935,7 +965,6 @@ func initPalette(theme *ColorTheme) {
ColCurrentSelectedEmpty = pair(blank, theme.DarkBg)
ColSpinner = pair(theme.Spinner, theme.InputBg)
ColInfo = pair(theme.Info, theme.InputBg)
- ColHeader = pair(theme.Header, theme.ListBg)
ColSeparator = pair(theme.Separator, theme.InputBg)
ColScrollbar = pair(theme.Scrollbar, theme.ListBg)
ColBorder = pair(theme.Border, theme.Bg)
@@ -949,6 +978,9 @@ func initPalette(theme *ColorTheme) {
ColListBorder = pair(theme.ListBorder, theme.ListBg)
ColInputBorder = pair(theme.InputBorder, theme.InputBg)
ColInputLabel = pair(theme.InputLabel, theme.InputBg)
+ ColHeader = pair(theme.Header, theme.HeaderBg)
+ ColHeaderBorder = pair(theme.HeaderBorder, theme.HeaderBg)
+ ColHeaderLabel = pair(theme.HeaderLabel, theme.HeaderBg)
}
func runeWidth(r rune) int {