Use bottom char for thinblock inline separator above list
CodeQL / Analyze (go) (push) Has been cancelled
build / build (push) Has been cancelled
Test fzf on macOS / build (push) Has been cancelled

For border styles where the top and bottom horizontals differ (thinblock
and block), the inline separator now picks the char based on position:
separators above the list content use the `bottom` char so the thin
line hugs the list from above, matching how the list frame's top edge
sits against the first row. Separators below list content continue to
use the `top` char.

Rounded / sharp / bold / double / horizontal / line are unaffected because
their top and bottom chars are identical.
This commit is contained in:
Junegunn Choi
2026-04-18 14:13:08 +09:00
parent 35e8e65b2e
commit 23cb4316ef
4 changed files with 39 additions and 13 deletions
+17 -4
View File
@@ -2739,7 +2739,9 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
cursor += s.contentLines
sepRow := cursor - t.wborder.Top()
t.wborder.DrawHSeparator(sepRow, s.windowType)
// Separator sits above list content; hug the list below by using the
// bottom char (matters for thinblock/block where top and bottom differ).
t.wborder.DrawHSeparator(sepRow, s.windowType, true)
recordSep(s.role, sepRow)
cursor++
}
@@ -2756,7 +2758,8 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
t.footerWindow = win
}
sepRow := top - 1 - t.wborder.Top()
t.wborder.DrawHSeparator(sepRow, s.windowType)
// Separator sits below list content; hug the list above with the top char.
t.wborder.DrawHSeparator(sepRow, s.windowType, false)
recordSep(s.role, sepRow)
cursor = top - 2
}
@@ -2937,6 +2940,16 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
}
}
// isInlineSepAboveList reports whether the recorded wborder-relative separator row sits
// above the list content window, so DrawHSeparator should use the bottom horizontal char
// to visually hug the list from above.
func (t *Terminal) isInlineSepAboveList(sepRow int) bool {
if t.wborder == nil || t.window == nil {
return false
}
return t.wborder.Top()+sepRow < t.window.Top()
}
func (t *Terminal) printInlineLabel(window tui.Window, row int, render labelPrinter, opts labelOpts, length int) {
if window == nil || render == nil || window.Height() == 0 {
return
@@ -6123,13 +6136,13 @@ func (t *Terminal) Loop() error {
case reqRedrawHeaderLabel:
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.wborder.DrawHSeparator(t.inlineHeaderSepRow, tui.WindowHeader, t.isInlineSepAboveList(t.inlineHeaderSepRow))
t.printInlineLabel(t.wborder, t.inlineHeaderSepRow, t.headerLabel, t.headerLabelOpts, t.headerLabelLen)
}
case reqRedrawFooterLabel:
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.wborder.DrawHSeparator(t.inlineFooterSepRow, tui.WindowFooter, t.isInlineSepAboveList(t.inlineFooterSepRow))
t.printInlineLabel(t.wborder, t.inlineFooterSepRow, t.footerLabel, t.footerLabelOpts, t.footerLabelLen)
}
case reqRedrawListLabel:
+8 -4
View File
@@ -1122,7 +1122,7 @@ func (w *LightWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *LightWindow) DrawHSeparator(row int, windowType WindowType) {
func (w *LightWindow) DrawHSeparator(row int, windowType WindowType, useBottom bool) {
if w.height == 0 {
return
}
@@ -1147,13 +1147,17 @@ func (w *LightWindow) DrawHSeparator(row int, windowType WindowType) {
// 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)
lineChar := w.border.top
if useBottom {
lineChar = w.border.bottom
}
hw := runeWidth(lineChar)
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))
w.CPrint(lineColor, repeat(lineChar, full)+repeat(' ', rem))
return
}
lw := runeWidth(w.border.leftMid)
@@ -1161,7 +1165,7 @@ func (w *LightWindow) DrawHSeparator(row int, windowType WindowType) {
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(lineColor, repeat(lineChar, inner)+repeat(' ', rem))
w.CPrint(junctionColor, string(w.border.rightMid))
}
+8 -4
View File
@@ -1017,7 +1017,7 @@ func (w *TcellWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *TcellWindow) DrawHSeparator(row int, windowType WindowType) {
func (w *TcellWindow) DrawHSeparator(row int, windowType WindowType, useBottom bool) {
if w.height == 0 {
return
}
@@ -1049,15 +1049,19 @@ func (w *TcellWindow) DrawHSeparator(row int, windowType WindowType) {
// so the outer frame's verticals stay visually continuous.
lineStyle := styleFor(windowType)
junctionStyle := styleFor(w.windowType)
lineChar := w.borderStyle.top
if useBottom {
lineChar = w.borderStyle.bottom
}
y := w.top + row
left := w.left
right := left + w.width
hw := runeWidth(w.borderStyle.top)
hw := runeWidth(lineChar)
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)
_screen.SetContent(x, y, lineChar, nil, lineStyle)
}
return
}
@@ -1065,7 +1069,7 @@ func (w *TcellWindow) DrawHSeparator(row int, windowType WindowType) {
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(x, y, lineChar, nil, lineStyle)
}
_screen.SetContent(left, y, w.borderStyle.leftMid, nil, junctionStyle)
_screen.SetContent(right-rightMidW, y, w.borderStyle.rightMid, nil, junctionStyle)
+6 -1
View File
@@ -830,7 +830,12 @@ type Window interface {
DrawBorder()
DrawHBorder()
DrawHSeparator(row int, windowType WindowType)
// DrawHSeparator draws an inline horizontal separator at `row` (relative to the
// window's top) using the color for `windowType`. When useBottom is true, the
// `bottom` horizontal char is used instead of `top` — for thinblock/block styles
// where the two characters differ, this keeps the thin line visually bonded to
// the list content that sits on the opposite side of the separator.
DrawHSeparator(row int, windowType WindowType, useBottom bool)
Refresh()
FinishFill()