mirror of
https://github.com/junegunn/fzf.git
synced 2026-05-26 02:08:50 +08:00
Add inline header/header-lines/footer borders inside the list frame
Adds a new BorderShape, BorderInline, accepted as a value for
--header-border, --header-lines-border, and --footer-border. When the
surrounding --list-border has both top and bottom horizontals (rounded,
sharp, bold, double, horizontal), the corresponding section is rendered
inside the list frame separated from the list content by a horizontal
line whose endpoints join the list border as T-junctions. Without a
compatible list border, the shape falls back to BorderLine.
Supports:
- All three layouts (default, reverse, reverse-list).
- Any combination of the three inline sections, producing stacked
separators.
- --header-label and --footer-label rendered on their separator row
(and redrawn on reqRedrawHeaderLabel / reqRedrawFooterLabel).
- Section-specific border colors on the separator line, with the
T-junction characters painted in the list-border color so the outer
frame stays visually continuous.
Rejects the combinations that do not make sense:
- --input-border=inline / --list-border=inline / --preview-border=inline
- --header-first + (--header-border=inline | --header-lines-border=inline)
- --header-border=inline with a non-inline --header-lines-border
(inline has to propagate inward toward the list content).
This commit is contained in:
@@ -1,6 +1,25 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.72.0
|
||||||
|
------
|
||||||
|
- `--header-border`, `--header-lines-border`, and `--footer-border` now accept
|
||||||
|
a new `inline` style that embeds the section inside the list frame,
|
||||||
|
separated from the list content by a horizontal line whose endpoints join
|
||||||
|
the surrounding list border as T-junctions.
|
||||||
|
- Requires `--list-border` with a line-drawing shape (rounded / sharp /
|
||||||
|
bold / double / horizontal); falls back to `line` otherwise.
|
||||||
|
- Works in every layout and supports stacking, e.g.
|
||||||
|
`--header-border=inline --header-lines-border=inline --footer-border=inline`
|
||||||
|
produces up to three internal separators inside the list frame.
|
||||||
|
- `--header-label` and `--footer-label` render on their respective
|
||||||
|
separator row.
|
||||||
|
- The separator inherits `--color list-border` when the section's own
|
||||||
|
border color is not explicitly set.
|
||||||
|
- `--header-first` is not compatible with `--header-border=inline` or
|
||||||
|
`--header-lines-border=inline`; `--header-border=inline` requires
|
||||||
|
`--header-lines-border` to be `inline` or unset.
|
||||||
|
|
||||||
0.71.0
|
0.71.0
|
||||||
------
|
------
|
||||||
_Release highlights: https://junegunn.github.io/fzf/releases/0.71.0/_
|
_Release highlights: https://junegunn.github.io/fzf/releases/0.71.0/_
|
||||||
|
|||||||
+14
-2
@@ -1100,7 +1100,13 @@ Print header before the prompt line. When both normal header and header lines
|
|||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-border" [=STYLE]
|
.BI "\-\-header\-border" [=STYLE]
|
||||||
Draw border around the header section. \fBline\fR style draws a single
|
Draw border around the header section. \fBline\fR style draws a single
|
||||||
separator line between the header window and the list section.
|
separator line between the header window and the list section. \fBinline\fR
|
||||||
|
style embeds the header inside the list border frame, joined to the list
|
||||||
|
section by a horizontal separator with T-junctions; it requires a
|
||||||
|
line-drawing \fB\-\-list\-border\fR (rounded / sharp / bold / double /
|
||||||
|
horizontal) and falls back to \fBline\fR otherwise. Not compatible with
|
||||||
|
\fB\-\-header\-first\fR, and when \fB\-\-header\-lines\fR is also set
|
||||||
|
\fB\-\-header\-lines\-border\fR must also be \fBinline\fR.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-label" [=LABEL]
|
.BI "\-\-header\-label" [=LABEL]
|
||||||
@@ -1116,6 +1122,9 @@ Display header from \fB--header\-lines\fR with a separate border. Pass
|
|||||||
\fBnone\fR to still separate the header lines but without a border. To combine
|
\fBnone\fR to still separate the header lines but without a border. To combine
|
||||||
two headers, use \fB\-\-no\-header\-lines\-border\fR. \fBline\fR style draws
|
two headers, use \fB\-\-no\-header\-lines\-border\fR. \fBline\fR style draws
|
||||||
a single separator line between the header lines and the list section.
|
a single separator line between the header lines and the list section.
|
||||||
|
\fBinline\fR style embeds the header lines inside the list border frame
|
||||||
|
with a T-junction separator; it requires a line-drawing
|
||||||
|
\fB\-\-list\-border\fR and is not compatible with \fB\-\-header\-first\fR.
|
||||||
|
|
||||||
.SS FOOTER
|
.SS FOOTER
|
||||||
|
|
||||||
@@ -1129,7 +1138,10 @@ are not affected by \fB\-\-with\-nth\fR. ANSI color codes are processed even whe
|
|||||||
.TP
|
.TP
|
||||||
.BI "\-\-footer\-border" [=STYLE]
|
.BI "\-\-footer\-border" [=STYLE]
|
||||||
Draw border around the footer section. \fBline\fR style draws a single
|
Draw border around the footer section. \fBline\fR style draws a single
|
||||||
separator line between the footer and the list section.
|
separator line between the footer and the list section. \fBinline\fR style
|
||||||
|
embeds the footer inside the list border frame with a T-junction separator;
|
||||||
|
it requires a line-drawing \fB\-\-list\-border\fR and falls back to
|
||||||
|
\fBline\fR otherwise.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-footer\-label" [=LABEL]
|
.BI "\-\-footer\-label" [=LABEL]
|
||||||
|
|||||||
+19
-1
@@ -953,6 +953,8 @@ func parseBorder(str string, optional bool) (tui.BorderShape, error) {
|
|||||||
switch str {
|
switch str {
|
||||||
case "line":
|
case "line":
|
||||||
return tui.BorderLine, nil
|
return tui.BorderLine, nil
|
||||||
|
case "inline":
|
||||||
|
return tui.BorderInline, nil
|
||||||
case "rounded":
|
case "rounded":
|
||||||
return tui.BorderRounded, nil
|
return tui.BorderRounded, nil
|
||||||
case "sharp":
|
case "sharp":
|
||||||
@@ -983,7 +985,7 @@ func parseBorder(str string, optional bool) (tui.BorderShape, error) {
|
|||||||
if optional && str == "" {
|
if optional && str == "" {
|
||||||
return defaultBorderShape, nil
|
return defaultBorderShape, nil
|
||||||
}
|
}
|
||||||
return tui.BorderNone, errors.New("invalid border style (expected: rounded|sharp|bold|block|thinblock|double|horizontal|vertical|top|bottom|left|right|none)")
|
return tui.BorderNone, errors.New("invalid border style (expected: rounded|sharp|bold|block|thinblock|double|horizontal|vertical|top|bottom|left|right|line|inline|none)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseKeyChords(str string, message string) (map[tui.Event]string, []tui.Event, error) {
|
func parseKeyChords(str string, message string) (map[tui.Event]string, []tui.Event, error) {
|
||||||
@@ -3610,6 +3612,22 @@ func validateOptions(opts *Options) error {
|
|||||||
return errors.New("only ANSI attributes are allowed for 'nth' (regular, bold, underline, reverse, dim, italic, strikethrough)")
|
return errors.New("only ANSI attributes are allowed for 'nth' (regular, bold, underline, reverse, dim, italic, strikethrough)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.BorderShape == tui.BorderInline ||
|
||||||
|
opts.ListBorderShape == tui.BorderInline ||
|
||||||
|
opts.InputBorderShape == tui.BorderInline ||
|
||||||
|
opts.Preview.border == tui.BorderInline {
|
||||||
|
return errors.New("inline border is only supported for --header-border, --header-lines-border, and --footer-border")
|
||||||
|
}
|
||||||
|
if opts.HeaderFirst && (opts.HeaderBorderShape == tui.BorderInline || opts.HeaderLinesShape == tui.BorderInline) {
|
||||||
|
return errors.New("--header-first is not compatible with --header-border=inline or --header-lines-border=inline")
|
||||||
|
}
|
||||||
|
if opts.HeaderBorderShape == tui.BorderInline &&
|
||||||
|
opts.HeaderLinesShape != tui.BorderInline &&
|
||||||
|
opts.HeaderLinesShape != tui.BorderUndefined &&
|
||||||
|
opts.HeaderLinesShape != tui.BorderNone {
|
||||||
|
return errors.New("--header-border=inline requires --header-lines-border to be inline or unset")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+185
-10
@@ -456,6 +456,11 @@ type Terminal struct {
|
|||||||
proxyScript string
|
proxyScript string
|
||||||
numLinesCache map[int32]numLinesCacheValue
|
numLinesCache map[int32]numLinesCacheValue
|
||||||
raw bool
|
raw bool
|
||||||
|
|
||||||
|
// Separator rows (relative to wborder.Top()) for inline sections. -1 when not inline.
|
||||||
|
inlineHeaderSepRow int
|
||||||
|
inlineHeaderLinesSepRow int
|
||||||
|
inlineFooterSepRow int
|
||||||
}
|
}
|
||||||
|
|
||||||
type numLinesCacheValue struct {
|
type numLinesCacheValue struct {
|
||||||
@@ -1227,6 +1232,22 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inline borders are embedded between the list's top and bottom horizontals.
|
||||||
|
// Shapes missing either one (none/phantom/line/single-sided) fall back to a plain
|
||||||
|
// horizontal separator (same as BorderLine).
|
||||||
|
inlineSupported := t.listBorderShape.HasTop() && t.listBorderShape.HasBottom()
|
||||||
|
if !inlineSupported {
|
||||||
|
if t.headerBorderShape == tui.BorderInline {
|
||||||
|
t.headerBorderShape = tui.BorderLine
|
||||||
|
}
|
||||||
|
if t.headerLinesShape == tui.BorderInline {
|
||||||
|
t.headerLinesShape = tui.BorderLine
|
||||||
|
}
|
||||||
|
if t.footerBorderShape == tui.BorderInline {
|
||||||
|
t.footerBorderShape = tui.BorderLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Determine header border shape
|
// Determine header border shape
|
||||||
if t.headerBorderShape == tui.BorderLine {
|
if t.headerBorderShape == tui.BorderLine {
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
@@ -2353,6 +2374,36 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inline borders live inside the list frame instead of consuming shift/shrink/availableLines.
|
||||||
|
// Tracked here and applied when positioning t.window and the component windows.
|
||||||
|
const (
|
||||||
|
inlineRoleHeader = 1
|
||||||
|
inlineRoleHeaderLines = 2
|
||||||
|
inlineRoleFooter = 3
|
||||||
|
)
|
||||||
|
type inlineSlot struct {
|
||||||
|
role int
|
||||||
|
windowType tui.WindowType
|
||||||
|
contentLines int
|
||||||
|
}
|
||||||
|
var inlineTop []inlineSlot // ordered outer-to-inner (index 0 = closest to top border of wborder)
|
||||||
|
var inlineBottom []inlineSlot // ordered outer-to-inner (index 0 = closest to bottom border of wborder)
|
||||||
|
|
||||||
|
// Helper: reserve space inside wborder for a component. isInner indicates whether the section
|
||||||
|
// is adjacent to the list content (true) or to the outer list border (false).
|
||||||
|
addInline := func(onTop bool, contentLines int, windowType tui.WindowType, role int, isInner bool) {
|
||||||
|
slot := inlineSlot{role: role, windowType: windowType, contentLines: contentLines}
|
||||||
|
target := &inlineTop
|
||||||
|
if !onTop {
|
||||||
|
target = &inlineBottom
|
||||||
|
}
|
||||||
|
if isInner {
|
||||||
|
*target = append(*target, slot)
|
||||||
|
} else {
|
||||||
|
*target = append([]inlineSlot{slot}, *target...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Adjust position and size of the list window if header border is set
|
// Adjust position and size of the list window if header border is set
|
||||||
headerBorderHeight := 0
|
headerBorderHeight := 0
|
||||||
if hasHeaderWindow {
|
if hasHeaderWindow {
|
||||||
@@ -2360,6 +2411,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow {
|
||||||
headerWindowHeight -= t.headerLines
|
headerWindowHeight -= t.headerLines
|
||||||
}
|
}
|
||||||
|
if t.headerBorderShape == tui.BorderInline {
|
||||||
|
// Reverse: header on top of list frame. Default/reverseList: header below.
|
||||||
|
// Header is the outer section (furthest from list) when paired with header-lines.
|
||||||
|
onTop := t.layout == layoutReverse
|
||||||
|
addInline(onTop, headerWindowHeight, tui.WindowHeader, inlineRoleHeader, false)
|
||||||
|
} else {
|
||||||
headerBorderHeight = util.Constrain(borderLines(t.headerBorderShape)+headerWindowHeight, 0, availableLines)
|
headerBorderHeight = util.Constrain(borderLines(t.headerBorderShape)+headerWindowHeight, 0, availableLines)
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
shift += headerBorderHeight
|
shift += headerBorderHeight
|
||||||
@@ -2369,9 +2426,16 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
availableLines -= headerBorderHeight
|
availableLines -= headerBorderHeight
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
headerLinesHeight := 0
|
headerLinesHeight := 0
|
||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow {
|
||||||
|
if headerLinesShape == tui.BorderInline {
|
||||||
|
// Header-lines always sits adjacent to list content (inner).
|
||||||
|
// Reverse/reverseList: above list; default: below.
|
||||||
|
onTop := t.layout != layoutDefault
|
||||||
|
addInline(onTop, t.headerLines, tui.WindowHeader, inlineRoleHeaderLines, true)
|
||||||
|
} else {
|
||||||
headerLinesHeight = util.Constrain(borderLines(headerLinesShape)+t.headerLines, 0, availableLines)
|
headerLinesHeight = util.Constrain(borderLines(headerLinesShape)+t.headerLines, 0, availableLines)
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
shift += headerLinesHeight
|
shift += headerLinesHeight
|
||||||
@@ -2381,9 +2445,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
availableLines -= headerLinesHeight
|
availableLines -= headerLinesHeight
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
footerBorderHeight := 0
|
footerBorderHeight := 0
|
||||||
if hasFooterWindow {
|
if hasFooterWindow {
|
||||||
|
if t.footerBorderShape == tui.BorderInline {
|
||||||
|
// Reverse: footer below list (alone, inner). Default: footer above list (alone, inner).
|
||||||
|
// ReverseList: footer above list, outer of header-lines when header-lines is also inline.
|
||||||
|
onTop := t.layout != layoutReverse
|
||||||
|
isInner := t.layout != layoutReverseList
|
||||||
|
addInline(onTop, len(t.footer), tui.WindowFooter, inlineRoleFooter, isInner)
|
||||||
|
} else {
|
||||||
// Footer lines should not take all available lines
|
// Footer lines should not take all available lines
|
||||||
footerBorderHeight = util.Constrain(borderLines(t.footerBorderShape)+len(t.footer), 0, availableLines)
|
footerBorderHeight = util.Constrain(borderLines(t.footerBorderShape)+len(t.footer), 0, availableLines)
|
||||||
shrink += footerBorderHeight
|
shrink += footerBorderHeight
|
||||||
@@ -2392,6 +2464,17 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
availableLines -= footerBorderHeight
|
availableLines -= footerBorderHeight
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute total rows consumed inside wborder for inline sections (content + 1 separator per section).
|
||||||
|
inlineTopLines := 0
|
||||||
|
for _, s := range inlineTop {
|
||||||
|
inlineTopLines += s.contentLines + 1
|
||||||
|
}
|
||||||
|
inlineBottomLines := 0
|
||||||
|
for _, s := range inlineBottom {
|
||||||
|
inlineBottomLines += s.contentLines + 1
|
||||||
|
}
|
||||||
|
|
||||||
// Set up list border
|
// Set up list border
|
||||||
hasListBorder := t.listBorderShape.Visible()
|
hasListBorder := t.listBorderShape.Visible()
|
||||||
@@ -2501,12 +2584,12 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
if previewOpts.position == posUp {
|
if previewOpts.position == posUp {
|
||||||
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
|
innerBorderFn(marginInt[0]+pheight, marginInt[3], width, height-pheight)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
innerMarginInt[0]+pheight+shift, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink, tui.WindowList, noBorder, true)
|
innerMarginInt[0]+pheight+shift+inlineTopLines, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink-inlineTopLines-inlineBottomLines, tui.WindowList, noBorder, true)
|
||||||
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
|
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
|
||||||
} else {
|
} else {
|
||||||
innerBorderFn(marginInt[0], marginInt[3], width, height-pheight)
|
innerBorderFn(marginInt[0], marginInt[3], width, height-pheight)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
innerMarginInt[0]+shift, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink, tui.WindowList, noBorder, true)
|
innerMarginInt[0]+shift+inlineTopLines, innerMarginInt[3], innerWidth, innerHeight-pheight-shrink-inlineTopLines-inlineBottomLines, tui.WindowList, noBorder, true)
|
||||||
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
|
||||||
}
|
}
|
||||||
case posLeft, posRight:
|
case posLeft, posRight:
|
||||||
@@ -2545,7 +2628,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
m = 1
|
m = 1
|
||||||
}
|
}
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
innerMarginInt[0]+shift, innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight-shrink, tui.WindowList, noBorder, true)
|
innerMarginInt[0]+shift+inlineTopLines, innerMarginInt[3]+pwidth+m, innerWidth-pwidth-m, innerHeight-shrink-inlineTopLines-inlineBottomLines, tui.WindowList, noBorder, true)
|
||||||
|
|
||||||
// Clear characters on the margin
|
// Clear characters on the margin
|
||||||
// fzf --bind 'space:toggle-preview' --preview ':' --preview-window left,1
|
// fzf --bind 'space:toggle-preview' --preview ':' --preview-window left,1
|
||||||
@@ -2577,7 +2660,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
innerBorderFn(marginInt[0], marginInt[3], width-pwidth, height)
|
innerBorderFn(marginInt[0], marginInt[3], width-pwidth, height)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
innerMarginInt[0]+shift, innerMarginInt[3], innerWidth-pwidth, innerHeight-shrink, tui.WindowList, noBorder, true)
|
innerMarginInt[0]+shift+inlineTopLines, innerMarginInt[3], innerWidth-pwidth, innerHeight-shrink-inlineTopLines-inlineBottomLines, tui.WindowList, noBorder, true)
|
||||||
x := marginInt[3] + width - pwidth
|
x := marginInt[3] + width - pwidth
|
||||||
createPreviewWindow(marginInt[0], x, pwidth, height)
|
createPreviewWindow(marginInt[0], x, pwidth, height)
|
||||||
}
|
}
|
||||||
@@ -2615,10 +2698,66 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
innerBorderFn(marginInt[0], marginInt[3], width, height)
|
innerBorderFn(marginInt[0], marginInt[3], width, height)
|
||||||
t.window = t.tui.NewWindow(
|
t.window = t.tui.NewWindow(
|
||||||
innerMarginInt[0]+shift,
|
innerMarginInt[0]+shift+inlineTopLines,
|
||||||
innerMarginInt[3],
|
innerMarginInt[3],
|
||||||
innerWidth,
|
innerWidth,
|
||||||
innerHeight-shrink, tui.WindowList, noBorder, true)
|
innerHeight-shrink-inlineTopLines-inlineBottomLines, tui.WindowList, noBorder, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place inline section windows and draw T-junction separators across wborder.
|
||||||
|
// Also record each separator's wborder-relative row so labels can be printed on it.
|
||||||
|
t.inlineHeaderSepRow = -1
|
||||||
|
t.inlineHeaderLinesSepRow = -1
|
||||||
|
t.inlineFooterSepRow = -1
|
||||||
|
recordSep := func(role int, sepRow int) {
|
||||||
|
switch role {
|
||||||
|
case inlineRoleHeader:
|
||||||
|
t.inlineHeaderSepRow = sepRow
|
||||||
|
case inlineRoleHeaderLines:
|
||||||
|
t.inlineHeaderLinesSepRow = sepRow
|
||||||
|
case inlineRoleFooter:
|
||||||
|
t.inlineFooterSepRow = sepRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(inlineTop)+len(inlineBottom) > 0 && t.wborder != nil {
|
||||||
|
// Inline sub-windows align with t.window horizontally (preview narrows t.window too)
|
||||||
|
// and stack immediately above or below it inside wborder.
|
||||||
|
subLeft := t.window.Left()
|
||||||
|
subWidth := t.window.Width()
|
||||||
|
cursor := t.window.Top() - inlineTopLines
|
||||||
|
for _, s := range inlineTop {
|
||||||
|
win := t.tui.NewWindow(cursor, subLeft, subWidth, s.contentLines, s.windowType, noBorder, true)
|
||||||
|
switch s.role {
|
||||||
|
case inlineRoleHeader:
|
||||||
|
t.headerWindow = win
|
||||||
|
case inlineRoleHeaderLines:
|
||||||
|
t.headerLinesWindow = win
|
||||||
|
case inlineRoleFooter:
|
||||||
|
t.footerWindow = win
|
||||||
|
}
|
||||||
|
cursor += s.contentLines
|
||||||
|
sepRow := cursor - t.wborder.Top()
|
||||||
|
t.wborder.DrawHSeparator(sepRow, s.windowType)
|
||||||
|
recordSep(s.role, sepRow)
|
||||||
|
cursor++
|
||||||
|
}
|
||||||
|
cursor = t.window.Top() + t.window.Height() + inlineBottomLines - 1
|
||||||
|
for _, s := range inlineBottom {
|
||||||
|
top := cursor - s.contentLines + 1
|
||||||
|
win := t.tui.NewWindow(top, subLeft, subWidth, s.contentLines, s.windowType, noBorder, true)
|
||||||
|
switch s.role {
|
||||||
|
case inlineRoleHeader:
|
||||||
|
t.headerWindow = win
|
||||||
|
case inlineRoleHeaderLines:
|
||||||
|
t.headerLinesWindow = win
|
||||||
|
case inlineRoleFooter:
|
||||||
|
t.footerWindow = win
|
||||||
|
}
|
||||||
|
sepRow := top - 1 - t.wborder.Top()
|
||||||
|
t.wborder.DrawHSeparator(sepRow, s.windowType)
|
||||||
|
recordSep(s.role, sepRow)
|
||||||
|
cursor = top - 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t.scrollbar) == 0 {
|
if len(t.scrollbar) == 0 {
|
||||||
@@ -2700,7 +2839,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up header border
|
// Set up header border
|
||||||
if hasHeaderWindow {
|
if hasHeaderWindow && t.headerBorderShape != tui.BorderInline {
|
||||||
var btop int
|
var btop int
|
||||||
if hasInputWindow && t.headerFirst {
|
if hasInputWindow && t.headerFirst {
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
@@ -2728,7 +2867,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up header lines border
|
// Set up header lines border
|
||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow && headerLinesShape != tui.BorderInline {
|
||||||
var btop int
|
var btop int
|
||||||
// NOTE: We still have to handle --header-first here in case
|
// NOTE: We still have to handle --header-first here in case
|
||||||
// --header-lines-border is set. Can't we just use header window instead
|
// --header-lines-border is set. Can't we just use header window instead
|
||||||
@@ -2761,7 +2900,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up footer
|
// Set up footer
|
||||||
if hasFooterWindow {
|
if hasFooterWindow && t.footerBorderShape != tui.BorderInline {
|
||||||
var btop int
|
var btop int
|
||||||
if t.layout == layoutReverse {
|
if t.layout == layoutReverse {
|
||||||
btop = w.Top() + w.Height()
|
btop = w.Top() + w.Height()
|
||||||
@@ -2785,6 +2924,31 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, 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)
|
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, false)
|
||||||
t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, false)
|
t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, false)
|
||||||
|
// Labels for inline sections render on the separator row of wborder.
|
||||||
|
if t.wborder != nil {
|
||||||
|
if t.inlineHeaderSepRow >= 0 {
|
||||||
|
t.printInlineLabel(t.wborder, t.inlineHeaderSepRow, t.headerLabel, t.headerLabelOpts, t.headerLabelLen)
|
||||||
|
}
|
||||||
|
if t.inlineFooterSepRow >= 0 {
|
||||||
|
t.printInlineLabel(t.wborder, t.inlineFooterSepRow, t.footerLabel, t.footerLabelOpts, t.footerLabelLen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) printInlineLabel(window tui.Window, row int, render labelPrinter, opts labelOpts, length int) {
|
||||||
|
if window == nil || render == nil || window.Height() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var col int
|
||||||
|
if opts.column == 0 {
|
||||||
|
col = max(0, (window.Width()-length)/2)
|
||||||
|
} else if opts.column < 0 {
|
||||||
|
col = max(0, window.Width()+opts.column+1-length)
|
||||||
|
} else {
|
||||||
|
col = min(opts.column-1, window.Width()-length)
|
||||||
|
}
|
||||||
|
window.Move(row, col)
|
||||||
|
render(window, window.Width())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
|
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
|
||||||
@@ -3277,7 +3441,10 @@ func (t *Terminal) headerIndentImpl(base int, borderShape tui.BorderShape) int {
|
|||||||
if t.listBorderShape.HasLeft() {
|
if t.listBorderShape.HasLeft() {
|
||||||
indentSize += 1 + t.borderWidth
|
indentSize += 1 + t.borderWidth
|
||||||
}
|
}
|
||||||
if borderShape.HasLeft() {
|
// Section borders with their own left side skip past the list border's left column.
|
||||||
|
// Inline sections also skip it, but only when the list border actually has a left,
|
||||||
|
// since otherwise the inline window starts flush with the list window.
|
||||||
|
if borderShape.HasLeft() || (borderShape == tui.BorderInline && t.listBorderShape.HasLeft()) {
|
||||||
indentSize -= 1 + t.borderWidth
|
indentSize -= 1 + t.borderWidth
|
||||||
if indentSize < 0 {
|
if indentSize < 0 {
|
||||||
indentSize = 0
|
indentSize = 0
|
||||||
@@ -5953,8 +6120,16 @@ func (t *Terminal) Loop() error {
|
|||||||
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
|
t.printLabel(t.inputBorder, t.inputLabel, t.inputLabelOpts, t.inputLabelLen, t.inputBorderShape, true)
|
||||||
case reqRedrawHeaderLabel:
|
case reqRedrawHeaderLabel:
|
||||||
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, true)
|
t.printLabel(t.headerBorder, t.headerLabel, t.headerLabelOpts, t.headerLabelLen, t.headerBorderShape, true)
|
||||||
|
if t.wborder != nil && t.inlineHeaderSepRow >= 0 {
|
||||||
|
t.wborder.DrawHSeparator(t.inlineHeaderSepRow, tui.WindowHeader)
|
||||||
|
t.printInlineLabel(t.wborder, t.inlineHeaderSepRow, t.headerLabel, t.headerLabelOpts, t.headerLabelLen)
|
||||||
|
}
|
||||||
case reqRedrawFooterLabel:
|
case reqRedrawFooterLabel:
|
||||||
t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, true)
|
t.printLabel(t.footerBorder, t.footerLabel, t.footerLabelOpts, t.footerLabelLen, t.footerBorderShape, true)
|
||||||
|
if t.wborder != nil && t.inlineFooterSepRow >= 0 {
|
||||||
|
t.wborder.DrawHSeparator(t.inlineFooterSepRow, tui.WindowFooter)
|
||||||
|
t.printInlineLabel(t.wborder, t.inlineFooterSepRow, t.footerLabel, t.footerLabelOpts, t.footerLabelLen)
|
||||||
|
}
|
||||||
case reqRedrawListLabel:
|
case reqRedrawListLabel:
|
||||||
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
|
t.printLabel(t.wborder, t.listLabel, t.listLabelOpts, t.listLabelLen, t.listBorderShape, true)
|
||||||
case reqRedrawBorderLabel:
|
case reqRedrawBorderLabel:
|
||||||
|
|||||||
@@ -1122,6 +1122,49 @@ func (w *LightWindow) DrawHBorder() {
|
|||||||
w.drawBorder(true)
|
w.drawBorder(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *LightWindow) DrawHSeparator(row int, windowType WindowType) {
|
||||||
|
if w.height == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if w.border.shape == BorderNone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
colorFor := func(wt WindowType) ColorPair {
|
||||||
|
switch wt {
|
||||||
|
case WindowList:
|
||||||
|
return ColListBorder
|
||||||
|
case WindowInput:
|
||||||
|
return ColInputBorder
|
||||||
|
case WindowHeader:
|
||||||
|
return ColHeaderBorder
|
||||||
|
case WindowFooter:
|
||||||
|
return ColFooterBorder
|
||||||
|
case WindowPreview:
|
||||||
|
return ColPreviewBorder
|
||||||
|
}
|
||||||
|
return ColBorder
|
||||||
|
}
|
||||||
|
// Section color for the horizontal; list-border color (w.windowType) for the T-junctions.
|
||||||
|
lineColor := colorFor(windowType)
|
||||||
|
junctionColor := colorFor(w.windowType)
|
||||||
|
hw := runeWidth(w.border.top)
|
||||||
|
w.Move(row, 0)
|
||||||
|
if !w.border.shape.HasLeft() && !w.border.shape.HasRight() {
|
||||||
|
// No verticals to join, so draw a continuous horizontal across the full width.
|
||||||
|
full := max(0, w.width/hw)
|
||||||
|
rem := w.width - full*hw
|
||||||
|
w.CPrint(lineColor, repeat(w.border.top, full)+repeat(' ', rem))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lw := runeWidth(w.border.leftMid)
|
||||||
|
rw := runeWidth(w.border.rightMid)
|
||||||
|
inner := max(0, (w.width-lw-rw)/hw)
|
||||||
|
rem := (w.width - lw - rw) - inner*hw
|
||||||
|
w.CPrint(junctionColor, string(w.border.leftMid))
|
||||||
|
w.CPrint(lineColor, repeat(w.border.top, inner)+repeat(' ', rem))
|
||||||
|
w.CPrint(junctionColor, string(w.border.rightMid))
|
||||||
|
}
|
||||||
|
|
||||||
func (w *LightWindow) drawBorder(onlyHorizontal bool) {
|
func (w *LightWindow) drawBorder(onlyHorizontal bool) {
|
||||||
if w.height == 0 {
|
if w.height == 0 {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1017,6 +1017,60 @@ func (w *TcellWindow) DrawHBorder() {
|
|||||||
w.drawBorder(true)
|
w.drawBorder(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *TcellWindow) DrawHSeparator(row int, windowType WindowType) {
|
||||||
|
if w.height == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shape := w.borderStyle.shape
|
||||||
|
if shape == BorderNone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
styleFor := func(wt WindowType) tcell.Style {
|
||||||
|
if !w.color {
|
||||||
|
return w.normal.style()
|
||||||
|
}
|
||||||
|
switch wt {
|
||||||
|
case WindowBase:
|
||||||
|
return ColBorder.style()
|
||||||
|
case WindowList:
|
||||||
|
return ColListBorder.style()
|
||||||
|
case WindowHeader:
|
||||||
|
return ColHeaderBorder.style()
|
||||||
|
case WindowFooter:
|
||||||
|
return ColFooterBorder.style()
|
||||||
|
case WindowInput:
|
||||||
|
return ColInputBorder.style()
|
||||||
|
case WindowPreview:
|
||||||
|
return ColPreviewBorder.style()
|
||||||
|
}
|
||||||
|
return w.normal.style()
|
||||||
|
}
|
||||||
|
// Section color for the horizontal; list-border color (w.windowType) for the T-junctions
|
||||||
|
// so the outer frame's verticals stay visually continuous.
|
||||||
|
lineStyle := styleFor(windowType)
|
||||||
|
junctionStyle := styleFor(w.windowType)
|
||||||
|
y := w.top + row
|
||||||
|
left := w.left
|
||||||
|
right := left + w.width
|
||||||
|
hw := runeWidth(w.borderStyle.top)
|
||||||
|
hasVert := shape.HasLeft() || shape.HasRight()
|
||||||
|
if !hasVert {
|
||||||
|
// No verticals to join, so draw a continuous horizontal across the full width.
|
||||||
|
for x := left; x <= right-hw; x += hw {
|
||||||
|
_screen.SetContent(x, y, w.borderStyle.top, nil, lineStyle)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
leftMidW := runeWidth(w.borderStyle.leftMid)
|
||||||
|
rightMidW := runeWidth(w.borderStyle.rightMid)
|
||||||
|
max := right - leftMidW - rightMidW
|
||||||
|
for x := left + leftMidW; x <= max; x += hw {
|
||||||
|
_screen.SetContent(x, y, w.borderStyle.top, nil, lineStyle)
|
||||||
|
}
|
||||||
|
_screen.SetContent(left, y, w.borderStyle.leftMid, nil, junctionStyle)
|
||||||
|
_screen.SetContent(right-rightMidW, y, w.borderStyle.rightMid, nil, junctionStyle)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
||||||
if w.height == 0 {
|
if w.height == 0 {
|
||||||
return
|
return
|
||||||
|
|||||||
+25
-5
@@ -595,11 +595,12 @@ const (
|
|||||||
BorderBottom
|
BorderBottom
|
||||||
BorderLeft
|
BorderLeft
|
||||||
BorderRight
|
BorderRight
|
||||||
|
BorderInline
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s BorderShape) HasLeft() bool {
|
func (s BorderShape) HasLeft() bool {
|
||||||
switch s {
|
switch s {
|
||||||
case BorderNone, BorderPhantom, BorderLine, BorderRight, BorderTop, BorderBottom, BorderHorizontal: // No Left
|
case BorderNone, BorderPhantom, BorderLine, BorderInline, BorderRight, BorderTop, BorderBottom, BorderHorizontal: // No Left
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -607,7 +608,7 @@ func (s BorderShape) HasLeft() bool {
|
|||||||
|
|
||||||
func (s BorderShape) HasRight() bool {
|
func (s BorderShape) HasRight() bool {
|
||||||
switch s {
|
switch s {
|
||||||
case BorderNone, BorderPhantom, BorderLine, BorderLeft, BorderTop, BorderBottom, BorderHorizontal: // No right
|
case BorderNone, BorderPhantom, BorderLine, BorderInline, BorderLeft, BorderTop, BorderBottom, BorderHorizontal: // No right
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -615,7 +616,7 @@ func (s BorderShape) HasRight() bool {
|
|||||||
|
|
||||||
func (s BorderShape) HasTop() bool {
|
func (s BorderShape) HasTop() bool {
|
||||||
switch s {
|
switch s {
|
||||||
case BorderNone, BorderPhantom, BorderLine, BorderLeft, BorderRight, BorderBottom, BorderVertical: // No top
|
case BorderNone, BorderPhantom, BorderLine, BorderInline, BorderLeft, BorderRight, BorderBottom, BorderVertical: // No top
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -623,7 +624,7 @@ func (s BorderShape) HasTop() bool {
|
|||||||
|
|
||||||
func (s BorderShape) HasBottom() bool {
|
func (s BorderShape) HasBottom() bool {
|
||||||
switch s {
|
switch s {
|
||||||
case BorderNone, BorderPhantom, BorderLine, BorderLeft, BorderRight, BorderTop, BorderVertical: // No bottom
|
case BorderNone, BorderPhantom, BorderLine, BorderInline, BorderLeft, BorderRight, BorderTop, BorderVertical: // No bottom
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -643,6 +644,8 @@ type BorderStyle struct {
|
|||||||
topRight rune
|
topRight rune
|
||||||
bottomLeft rune
|
bottomLeft rune
|
||||||
bottomRight rune
|
bottomRight rune
|
||||||
|
leftMid rune
|
||||||
|
rightMid rune
|
||||||
}
|
}
|
||||||
|
|
||||||
type BorderCharacter int
|
type BorderCharacter int
|
||||||
@@ -658,7 +661,9 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topLeft: ' ',
|
topLeft: ' ',
|
||||||
topRight: ' ',
|
topRight: ' ',
|
||||||
bottomLeft: ' ',
|
bottomLeft: ' ',
|
||||||
bottomRight: ' '}
|
bottomRight: ' ',
|
||||||
|
leftMid: ' ',
|
||||||
|
rightMid: ' '}
|
||||||
}
|
}
|
||||||
if !unicode {
|
if !unicode {
|
||||||
return BorderStyle{
|
return BorderStyle{
|
||||||
@@ -671,6 +676,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '+',
|
topRight: '+',
|
||||||
bottomLeft: '+',
|
bottomLeft: '+',
|
||||||
bottomRight: '+',
|
bottomRight: '+',
|
||||||
|
leftMid: '+',
|
||||||
|
rightMid: '+',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch shape {
|
switch shape {
|
||||||
@@ -685,6 +692,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '┐',
|
topRight: '┐',
|
||||||
bottomLeft: '└',
|
bottomLeft: '└',
|
||||||
bottomRight: '┘',
|
bottomRight: '┘',
|
||||||
|
leftMid: '├',
|
||||||
|
rightMid: '┤',
|
||||||
}
|
}
|
||||||
case BorderBold:
|
case BorderBold:
|
||||||
return BorderStyle{
|
return BorderStyle{
|
||||||
@@ -697,6 +706,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '┓',
|
topRight: '┓',
|
||||||
bottomLeft: '┗',
|
bottomLeft: '┗',
|
||||||
bottomRight: '┛',
|
bottomRight: '┛',
|
||||||
|
leftMid: '┣',
|
||||||
|
rightMid: '┫',
|
||||||
}
|
}
|
||||||
case BorderBlock:
|
case BorderBlock:
|
||||||
// ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
|
// ▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜
|
||||||
@@ -712,6 +723,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '▜',
|
topRight: '▜',
|
||||||
bottomLeft: '▙',
|
bottomLeft: '▙',
|
||||||
bottomRight: '▟',
|
bottomRight: '▟',
|
||||||
|
leftMid: '▌',
|
||||||
|
rightMid: '▐',
|
||||||
}
|
}
|
||||||
|
|
||||||
case BorderThinBlock:
|
case BorderThinBlock:
|
||||||
@@ -728,6 +741,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '🭾',
|
topRight: '🭾',
|
||||||
bottomLeft: '🭼',
|
bottomLeft: '🭼',
|
||||||
bottomRight: '🭿',
|
bottomRight: '🭿',
|
||||||
|
leftMid: '▏',
|
||||||
|
rightMid: '▕',
|
||||||
}
|
}
|
||||||
|
|
||||||
case BorderDouble:
|
case BorderDouble:
|
||||||
@@ -741,6 +756,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '╗',
|
topRight: '╗',
|
||||||
bottomLeft: '╚',
|
bottomLeft: '╚',
|
||||||
bottomRight: '╝',
|
bottomRight: '╝',
|
||||||
|
leftMid: '╠',
|
||||||
|
rightMid: '╣',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return BorderStyle{
|
return BorderStyle{
|
||||||
@@ -753,6 +770,8 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
|||||||
topRight: '╮',
|
topRight: '╮',
|
||||||
bottomLeft: '╰',
|
bottomLeft: '╰',
|
||||||
bottomRight: '╯',
|
bottomRight: '╯',
|
||||||
|
leftMid: '├',
|
||||||
|
rightMid: '┤',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,6 +830,7 @@ type Window interface {
|
|||||||
|
|
||||||
DrawBorder()
|
DrawBorder()
|
||||||
DrawHBorder()
|
DrawHBorder()
|
||||||
|
DrawHSeparator(row int, windowType WindowType)
|
||||||
Refresh()
|
Refresh()
|
||||||
FinishFill()
|
FinishFill()
|
||||||
|
|
||||||
|
|||||||
@@ -1392,5 +1392,37 @@ class TestLayout < TestInteractive
|
|||||||
input: "(printf 'Xaa\\nYbb\\nZcc\\n'; seq 5)",
|
input: "(printf 'Xaa\\nYbb\\nZcc\\n'; seq 5)",
|
||||||
clicks: clicks)
|
clicks: clicks)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Inline header inside a rounded list border.
|
||||||
|
define_method(:"test_click_header_border_inline_#{slug}") do
|
||||||
|
opts = %(--layout=#{layout} --style full --header $'Aaa\\nBbb\\nCcc' )
|
||||||
|
verify_clicks(kind: :header, opts: opts, input: 'seq 5', clicks: HEADER_CLICKS)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inline header inside a horizontal list border (top+bottom only, no T-junctions).
|
||||||
|
define_method(:"test_click_header_border_inline_horizontal_list_#{slug}") do
|
||||||
|
opts = %(--layout=#{layout} --style full --header $'Aaa\\nBbb\\nCcc' )
|
||||||
|
verify_clicks(kind: :header, opts: opts, input: 'seq 5', clicks: HEADER_CLICKS)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inline header-lines inside a rounded list border.
|
||||||
|
define_method(:"test_click_header_lines_border_inline_#{slug}") do
|
||||||
|
clicks_hl = if layout == 'default'
|
||||||
|
[%w[Xaa 3], %w[Ybb 2], %w[Zcc 1]]
|
||||||
|
else
|
||||||
|
[%w[Xaa 1], %w[Ybb 2], %w[Zcc 3]]
|
||||||
|
end
|
||||||
|
opts = %(--layout=#{layout} --style full --header-lines 3 )
|
||||||
|
verify_clicks(kind: :header, opts: opts,
|
||||||
|
input: "(printf 'Xaa\\nYbb\\nZcc\\n'; seq 5)",
|
||||||
|
clicks: clicks_hl)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inline footer inside a rounded list border.
|
||||||
|
define_method(:"test_click_footer_border_inline_#{slug}") do
|
||||||
|
opts = %(--layout=#{layout} --style full --footer $'Foo\\nBar\\nBaz' )
|
||||||
|
verify_clicks(kind: :footer, opts: opts, input: 'seq 5',
|
||||||
|
clicks: [%w[Foo 1], %w[Bar 2], %w[Baz 3]])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user