Add --border option to draw horizontal lines above and below the finder

Goes well with --height
This commit is contained in:
Junegunn Choi 2017-02-04 21:51:22 +09:00
parent fe83589ade
commit 4b700192c1
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
10 changed files with 128 additions and 61 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
0.16.4
------
- Added `--border` option to draw border above and below the finder
- Bug fixes and improvements
0.16.3
------
- Fixed a bug where fzf incorrectly display the lines when straddling tab

View File

@ -141,10 +141,10 @@ vim $(fzf --height 40% --reverse)
```
You can add these options to `$FZF_DEFAULT_OPTS` so that they're applied by
default.
default. For example,
```sh
export FZF_DEFAULT_OPTS='--height 40% --reverse'
export FZF_DEFAULT_OPTS='--height 40% --reverse --border'
```
#### Search syntax

View File

@ -156,6 +156,9 @@ Ignored when \fB--height\fR is not specified.
.B "--reverse"
Reverse orientation
.TP
.B "--border"
Draw border above and below the finder
.TP
.BI "--margin=" MARGIN
Comma-separated expression for margins around the finder.
.br

View File

@ -54,6 +54,7 @@ const usage = `usage: fzf [options]
--min-height=HEIGHT Minimum height when --height is given in percent
(default: 10)
--reverse Reverse orientation
--border Draw border above and below the finder
--margin=MARGIN Screen margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
--inline-info Display finder info inline with the query
--prompt=STR Input prompt (default: '> ')
@ -183,6 +184,7 @@ type Options struct {
Header []string
HeaderLines int
Margin [4]sizeSpec
Bordered bool
Tabstop int
Version bool
}
@ -1086,6 +1088,10 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Height = sizeSpec{}
case "--no-margin":
opts.Margin = defaultMargin()
case "--no-border":
opts.Bordered = false
case "--border":
opts.Bordered = true
case "--margin":
opts.Margin = parseMargin(
nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))

View File

@ -83,8 +83,10 @@ type Terminal struct {
tabstop int
margin [4]sizeSpec
strong tui.Attr
bordered bool
border tui.Window
window tui.Window
bwindow tui.Window
pborder tui.Window
pwindow tui.Window
count int
progress int
@ -295,15 +297,22 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
maxHeightFunc := func(termHeight int) int {
var maxHeight int
if opts.Height.percent {
maxHeight = util.Min(termHeight,
util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight))
maxHeight = util.Max(int(opts.Height.size*float64(termHeight)/100.0), opts.MinHeight)
} else {
maxHeight = util.Min(termHeight, int(opts.Height.size))
maxHeight = int(opts.Height.size)
}
effectiveMinHeight := minHeight
if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
effectiveMinHeight *= 2
}
if opts.InlineInfo {
return util.Max(maxHeight, minHeight-1)
effectiveMinHeight -= 1
}
return util.Max(maxHeight, minHeight)
if opts.Bordered {
effectiveMinHeight += 2
}
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
}
renderer = tui.NewLightRenderer(opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, maxHeightFunc)
} else if tui.HasFullscreenRenderer() {
@ -343,6 +352,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
printQuery: opts.PrintQuery,
history: opts.History,
margin: opts.Margin,
bordered: opts.Bordered,
strong: strongAttr,
cycle: opts.Cycle,
header: header,
@ -499,6 +509,9 @@ func (t *Terminal) resizeWindows() {
} else {
marginInt[idx] = int(sizeSpec.size)
}
if t.bordered && idx%2 == 0 {
marginInt[idx] += 1
}
}
adjust := func(idx1 int, idx2 int, max int, min int) {
if max >= min {
@ -524,19 +537,29 @@ func (t *Terminal) resizeWindows() {
}
adjust(1, 3, screenWidth, minAreaWidth)
adjust(0, 2, screenHeight, minAreaHeight)
if t.border != nil {
t.border.Close()
}
if t.window != nil {
t.window.Close()
}
if t.bwindow != nil {
t.bwindow.Close()
if t.pborder != nil {
t.pborder.Close()
t.pwindow.Close()
}
width := screenWidth - marginInt[1] - marginInt[3]
height := screenHeight - marginInt[0] - marginInt[2]
if t.bordered {
t.border = t.tui.NewWindow(
marginInt[0]-1,
marginInt[3],
width,
height+2, tui.BorderHorizontal)
}
if previewVisible {
createPreviewWindow := func(y int, x int, w int, h int) {
t.bwindow = t.tui.NewWindow(y, x, w, h, true)
t.pborder = t.tui.NewWindow(y, x, w, h, tui.BorderAround)
pwidth := w - 4
// ncurses auto-wraps the line when the cursor reaches the right-end of
// the window. To prevent unintended line-wraps, we use the width one
@ -544,28 +567,28 @@ func (t *Terminal) resizeWindows() {
if !t.preview.wrap && t.tui.DoesAutoWrap() {
pwidth += 1
}
t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, false)
t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, tui.BorderNone)
}
switch t.preview.position {
case posUp:
pheight := calculateSize(height, t.preview.size, minHeight, 3)
t.window = t.tui.NewWindow(
marginInt[0]+pheight, marginInt[3], width, height-pheight, false)
marginInt[0]+pheight, marginInt[3], width, height-pheight, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3], width, pheight)
case posDown:
pheight := calculateSize(height, t.preview.size, minHeight, 3)
t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width, height-pheight, false)
marginInt[0], marginInt[3], width, height-pheight, tui.BorderNone)
createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight)
case posLeft:
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
t.window = t.tui.NewWindow(
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, false)
marginInt[0], marginInt[3]+pwidth, width-pwidth, height, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3], pwidth, height)
case posRight:
pwidth := calculateSize(width, t.preview.size, minWidth, 5)
t.window = t.tui.NewWindow(
marginInt[0], marginInt[3], width-pwidth, height, false)
marginInt[0], marginInt[3], width-pwidth, height, tui.BorderNone)
createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height)
}
} else {
@ -573,7 +596,7 @@ func (t *Terminal) resizeWindows() {
marginInt[0],
marginInt[3],
width,
height, false)
height, tui.BorderNone)
}
if !t.tui.IsOptimized() && t.theme != nil && t.theme.HasBg() {
for i := 0; i < t.window.Height(); i++ {
@ -978,11 +1001,15 @@ func (t *Terminal) printAll() {
func (t *Terminal) refresh() {
if !t.suppress {
if t.hasPreviewWindow() {
t.tui.RefreshWindows([]tui.Window{t.bwindow, t.pwindow, t.window})
} else {
t.tui.RefreshWindows([]tui.Window{t.window})
windows := make([]tui.Window, 0, 4)
if t.bordered {
windows = append(windows, t.border)
}
if t.hasPreviewWindow() {
windows = append(windows, t.pborder, t.pwindow)
}
windows = append(windows, t.window)
t.tui.RefreshWindows(windows)
}
}

View File

@ -40,6 +40,6 @@ func (r *FullscreenRenderer) MaxY() int { return 0 }
func (r *FullscreenRenderer) RefreshWindows(windows []Window) {}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window {
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
return nil
}

View File

@ -95,7 +95,7 @@ type LightRenderer struct {
type LightWindow struct {
renderer *LightRenderer
colored bool
border bool
border BorderStyle
top int
left int
width int
@ -600,11 +600,11 @@ func (r *LightRenderer) IsOptimized() bool {
return false
}
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, border bool) Window {
func (r *LightRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
w := &LightWindow{
renderer: r,
colored: r.theme != nil,
border: border,
border: borderStyle,
top: top,
left: left,
width: width,
@ -614,13 +614,27 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, bord
if r.theme != nil {
w.bg = r.theme.Bg
}
if w.border {
w.drawBorder()
}
w.drawBorder()
return w
}
func (w *LightWindow) drawBorder() {
switch w.border {
case BorderAround:
w.drawBorderAround()
case BorderHorizontal:
w.drawBorderHorizontal()
}
}
func (w *LightWindow) drawBorderHorizontal() {
w.Move(0, 0)
w.CPrint(ColBorder, AttrRegular, repeat("─", w.width))
w.Move(w.height-1, 0)
w.CPrint(ColBorder, AttrRegular, repeat("─", w.width))
}
func (w *LightWindow) drawBorderAround() {
w.Move(0, 0)
w.CPrint(ColBorder, AttrRegular, "┌"+repeat("─", w.width-2)+"┐")
for y := 1; y < w.height-1; y++ {
@ -854,9 +868,7 @@ func (w *LightWindow) FinishFill() {
}
func (w *LightWindow) Erase() {
if w.border {
w.drawBorder()
}
w.drawBorder()
// We don't erase the window here to avoid flickering during scroll
w.Move(0, 0)
}

View File

@ -189,12 +189,13 @@ func (r *FullscreenRenderer) Close() {
C.delscreen(_screen)
}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window {
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
win := C.newwin(C.int(height), C.int(width), C.int(top), C.int(left))
if r.theme != nil {
C.wbkgd(win, C.chtype(C.COLOR_PAIR(C.int(ColNormal.index()))))
}
if border {
// FIXME Does not implement BorderHorizontal
if borderStyle != BorderNone {
pair, attr := _colorFn(ColBorder, 0)
C.wcolor_set(win, pair, nil)
C.wattron(win, attr)

View File

@ -27,15 +27,15 @@ func (p ColorPair) style() tcell.Style {
type Attr tcell.Style
type TcellWindow struct {
color bool
top int
left int
width int
height int
lastX int
lastY int
moveCursor bool
border bool
color bool
top int
left int
width int
height int
lastX int
lastY int
moveCursor bool
borderStyle BorderStyle
}
func (w *TcellWindow) Top() int {
@ -61,8 +61,11 @@ func (w *TcellWindow) Refresh() {
}
w.lastX = 0
w.lastY = 0
if w.border {
w.drawBorder()
switch w.borderStyle {
case BorderAround:
w.drawBorder(true)
case BorderHorizontal:
w.drawBorder(false)
}
}
@ -377,15 +380,15 @@ func (r *FullscreenRenderer) RefreshWindows(windows []Window) {
_screen.Show()
}
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, border bool) Window {
func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window {
// TODO
return &TcellWindow{
color: r.theme != nil,
top: top,
left: left,
width: width,
height: height,
border: border}
color: r.theme != nil,
top: top,
left: left,
width: width,
height: height,
borderStyle: borderStyle}
}
func (w *TcellWindow) Close() {
@ -536,7 +539,7 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
return w.fillString(str, ColorPair{fg, bg, -1}, a)
}
func (w *TcellWindow) drawBorder() {
func (w *TcellWindow) drawBorder(around bool) {
left := w.left
right := left + w.width
top := w.top
@ -554,13 +557,15 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(x, bot-1, tcell.RuneHLine, nil, style)
}
for y := top; y < bot; y++ {
_screen.SetContent(left, y, tcell.RuneVLine, nil, style)
_screen.SetContent(right-1, y, tcell.RuneVLine, nil, style)
}
if around {
for y := top; y < bot; y++ {
_screen.SetContent(left, y, tcell.RuneVLine, nil, style)
_screen.SetContent(right-1, y, tcell.RuneVLine, nil, style)
}
_screen.SetContent(left, top, tcell.RuneULCorner, nil, style)
_screen.SetContent(right-1, top, tcell.RuneURCorner, nil, style)
_screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style)
_screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style)
_screen.SetContent(left, top, tcell.RuneULCorner, nil, style)
_screen.SetContent(right-1, top, tcell.RuneURCorner, nil, style)
_screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style)
_screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style)
}
}

View File

@ -195,6 +195,14 @@ type MouseEvent struct {
Mod bool
}
type BorderStyle int
const (
BorderNone BorderStyle = iota
BorderAround
BorderHorizontal
)
type Renderer interface {
Init()
Pause()
@ -211,7 +219,7 @@ type Renderer interface {
DoesAutoWrap() bool
IsOptimized() bool
NewWindow(top int, left int, width int, height int, border bool) Window
NewWindow(top int, left int, width int, height int, borderStyle BorderStyle) Window
}
type Window interface {