diff --git a/internal/config/colorscheme.go b/internal/config/colorscheme.go index 1058ab5286..1109717f2e 100644 --- a/internal/config/colorscheme.go +++ b/internal/config/colorscheme.go @@ -208,6 +208,16 @@ func StringToStyle(str string) tcell.Style { return style } +func ReverseColor(s tcell.Style) tcell.Style { + _, _, attr := s.Decompose() + attr = attr ^ tcell.AttrReverse + if attr&tcell.AttrReverse == tcell.AttrReverse { + return s.Reverse(true) + } else { + return s.Reverse(false) + } +} + // StringToColor returns a tcell color from a string representation of a color // We accept either bright... or light... to mean the brighter version of a color func StringToColor(str string) (tcell.Color, bool) { diff --git a/internal/config/settings.go b/internal/config/settings.go index 2f8158334f..c93adaa2c8 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -129,8 +129,9 @@ var DefaultGlobalOnlySettings = map[string]any{ "savehistory": true, "scrollbarchar": "|", "sucmd": "sudo", - "tabhighlight": false, - "tabreverse": true, + "tabbarchars": "div=│,active= [] ,inactive= ", + "tabhighlight": true, + "tabreverse": false, "xterm": false, } diff --git a/internal/display/tabwindow.go b/internal/display/tabwindow.go index 844f12e922..4baa028201 100644 --- a/internal/display/tabwindow.go +++ b/internal/display/tabwindow.go @@ -2,11 +2,12 @@ package display import ( runewidth "github.com/mattn/go-runewidth" - "github.com/micro-editor/tcell/v2" "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" "github.com/zyedidia/micro/v2/internal/screen" "github.com/zyedidia/micro/v2/internal/util" + "strings" + "unicode/utf8" ) type TabWindow struct { @@ -30,15 +31,26 @@ func (w *TabWindow) Resize(width, height int) { func (w *TabWindow) LocFromVisual(vloc buffer.Loc) int { x := -w.hscroll - + tabactiverunes, tabinactiverunes, tabdivrunes := GetTabRunes() for i, n := range w.Names { - x++ + if i == w.active { + x += len(tabactiverunes) / 2 + } else { + x += len(tabinactiverunes) / 2 + } + s := util.CharacterCountInString(n) if vloc.Y == w.Y && vloc.X < x+s { return i } x += s - x += 3 + + if i == w.active { + x += len(tabactiverunes) - len(tabactiverunes)/2 + } else { + x += len(tabinactiverunes) - len(tabinactiverunes)/2 + } + x += len(tabdivrunes) if x >= w.Width { break } @@ -91,35 +103,81 @@ func (w *TabWindow) SetActive(a int) { } } +func GetTabRunes() ([]rune, []rune, []rune) { + var tabactivechars string + var tabinactivechars string + var tabdivchars string + for _, entry := range strings.Split(config.GetGlobalOption("tabbarchars").(string), ",") { + split := strings.SplitN(entry, "=", 2) + if len(split) < 2 { + continue + } + key, val := split[0], split[1] + switch key { + case "active": + tabactivechars = val + case "inactive": + tabinactivechars = val + case "div": + tabdivchars = val + } + } + + if utf8.RuneCountInString(tabactivechars) < 2 { + tabactivechars = "" + } + if utf8.RuneCountInString(tabinactivechars) < 2 { + tabinactivechars = "" + } + + tabactiverunes := []rune(tabactivechars) + tabinactiverunes := []rune(tabinactivechars) + tabdivrunes := []rune(tabdivchars) + return tabactiverunes, tabinactiverunes, tabdivrunes +} + func (w *TabWindow) Display() { x := -w.hscroll done := false globalTabReverse := config.GetGlobalOption("tabreverse").(bool) globalTabHighlight := config.GetGlobalOption("tabhighlight").(bool) + tabBarStyle := config.DefStyle - // xor of reverse and tab highlight to get tab character (as in filename and surrounding characters) reverse state - tabCharHighlight := (globalTabReverse || globalTabHighlight) && !(globalTabReverse && globalTabHighlight) - - reverseStyles := func(reverse bool) (tcell.Style, tcell.Style) { - tabBarStyle := config.DefStyle.Reverse(reverse) - if style, ok := config.Colorscheme["tabbar"]; ok { - tabBarStyle = style - } - tabBarActiveStyle := tabBarStyle - if style, ok := config.Colorscheme["tabbar.active"]; ok { - tabBarActiveStyle = style - } - return tabBarStyle, tabBarActiveStyle + if style, ok := config.Colorscheme["tabbar"]; ok { + tabBarStyle = style + } + if globalTabReverse { + tabBarStyle = config.ReverseColor(tabBarStyle) + } + tabBarActiveStyle := tabBarStyle + if globalTabHighlight { + tabBarActiveStyle = config.ReverseColor(tabBarStyle) + } + if style, ok := config.Colorscheme["tabbar.active"]; ok { + tabBarActiveStyle = style + } + tabBarInactiveStyle := tabBarStyle + if style, ok := config.Colorscheme["tabbar.inactive"]; ok { + tabBarInactiveStyle = style + } + tabBarDivStyle := tabBarStyle + if style, ok := config.Colorscheme["tabbar.div"]; ok { + tabBarDivStyle = style } - draw := func(r rune, n int, active bool, reversed bool) { - tabBarStyle, tabBarActiveStyle := reverseStyles(reversed) - + draw := func(r rune, n int, active bool, tab bool, div bool) { style := tabBarStyle - if active { - style = tabBarActiveStyle + if tab { + if active { + style = tabBarActiveStyle + } else { + style = tabBarInactiveStyle + } + } else if div { + style = tabBarDivStyle } + for i := 0; i < n; i++ { rw := runewidth.RuneWidth(r) for j := 0; j < rw; j++ { @@ -128,11 +186,11 @@ func (w *TabWindow) Display() { c = ' ' } if x == w.Width-1 && !done { - screen.SetContent(w.Width-1, w.Y, '>', nil, tabBarStyle) + screen.SetContent(w.Width-1, w.Y, '>', nil, style) x++ break } else if x == 0 && w.hscroll > 0 { - screen.SetContent(0, w.Y, '<', nil, tabBarStyle) + screen.SetContent(0, w.Y, '<', nil, style) } else if x >= 0 && x < w.Width { screen.SetContent(x, w.Y, c, nil, style) } @@ -141,15 +199,26 @@ func (w *TabWindow) Display() { } } + tabactiverunes, tabinactiverunes, tabdivrunes := GetTabRunes() + leftactiverunes := tabactiverunes[0 : len(tabactiverunes)/2] + rightactiverunes := tabactiverunes[len(tabactiverunes)/2:] + + leftinactiverunes := tabinactiverunes[0 : len(tabinactiverunes)/2] + rightinactiverunes := tabinactiverunes[len(tabinactiverunes)/2:] + for i, n := range w.Names { if i == w.active { - draw('[', 1, true, tabCharHighlight) + for j := 0; j < len(leftactiverunes); j++ { + draw(leftactiverunes[j], 1, true, true, false) + } } else { - draw(' ', 1, false, tabCharHighlight) + for j := 0; j < len(leftinactiverunes); j++ { + draw(leftinactiverunes[j], 1, false, true, false) + } } for _, c := range n { - draw(c, 1, i == w.active, tabCharHighlight) + draw(c, 1, i == w.active, true, false) } if i == len(w.Names)-1 { @@ -157,11 +226,17 @@ func (w *TabWindow) Display() { } if i == w.active { - draw(']', 1, true, tabCharHighlight) - draw(' ', 2, true, globalTabReverse) + for j := 0; j < len(rightactiverunes); j++ { + draw(rightactiverunes[j], 1, true, true, false) + } } else { - draw(' ', 1, false, tabCharHighlight) - draw(' ', 2, false, globalTabReverse) + for j := 0; j < len(rightinactiverunes); j++ { + draw(rightinactiverunes[j], 1, false, true, false) + } + } + + for j := 0; j < len(tabdivrunes); j++ { + draw(tabdivrunes[j], 1, false, false, true) } if x >= w.Width { @@ -170,6 +245,6 @@ func (w *TabWindow) Display() { } if x < w.Width { - draw(' ', w.Width-x, false, globalTabReverse) + draw(' ', w.Width-x, false, false, false) } } diff --git a/runtime/colorschemes/gotham.micro b/runtime/colorschemes/gotham.micro index d067a81cfa..e4af7ef2d6 100644 --- a/runtime/colorschemes/gotham.micro +++ b/runtime/colorschemes/gotham.micro @@ -25,5 +25,6 @@ color-link cursor-line "#091F2E" color-link color-column "#11151C" color-link symbol "#99D1CE,#0C1014" color-link match-brace "#0C1014,#D26937" +color-link tabbar "#0C1014,#99D1CE" color-link tab-error "#D75F5F" color-link trailingws "#D75F5F" diff --git a/runtime/colorschemes/monokai-dark.micro b/runtime/colorschemes/monokai-dark.micro index 3a1e89c4f3..949535de24 100644 --- a/runtime/colorschemes/monokai-dark.micro +++ b/runtime/colorschemes/monokai-dark.micro @@ -25,4 +25,5 @@ color-link cursor-line "#323232" color-link color-column "#323232" color-link match-brace "#1D0000,#AE81FF" color-link tab-error "#D75F5F" +color-link tabbar "#1D0000,#D5D8D6" color-link trailingws "#D75F5F" diff --git a/runtime/colorschemes/sunny-day.micro b/runtime/colorschemes/sunny-day.micro index 24a3e763ea..e19c623e3b 100644 --- a/runtime/colorschemes/sunny-day.micro +++ b/runtime/colorschemes/sunny-day.micro @@ -26,4 +26,5 @@ color-link cursor-line "229" color-link current-line-number "246" color-link match-brace "230,22" color-link tab-error "210" +color-link tabbar "230,0" color-link trailingws "210" diff --git a/runtime/help/colors.md b/runtime/help/colors.md index c085919a93..747562bdd3 100644 --- a/runtime/help/colors.md +++ b/runtime/help/colors.md @@ -40,7 +40,7 @@ color support comes in three flavors. same regardless of the configured 16-color palette. However, the color range is fairly limited due to the small number of colors available. Default 256-color colorschemes include `monokai`, `twilight`, `zenburn`, - `darcula` and more. + `dracula` and more. * true-color: Some terminals support displaying "true color" with 16 million colors using standard RGB values. This mode will be able to support @@ -179,6 +179,8 @@ Here is a list of the colorscheme groups that you can use: * statusline.suggestions (Color of the autocomplete suggestions menu) * tabbar (Color of the tabbar that lists open files) * tabbar.active (Color of the active tab in the tabbar) +* tabbar.inactive (Color of the inactive tabs in the tabbar) +* tabbar.div (Color of the space/divider between each tab in the tabbar) * indent-char (Color of the character which indicates tabs if the option is enabled) * line-number diff --git a/runtime/help/options.md b/runtime/help/options.md index d4832c5d08..f5f64841e4 100644 --- a/runtime/help/options.md +++ b/runtime/help/options.md @@ -460,10 +460,24 @@ Here are the available options: default value: `true` -* `tabhighlight`: inverts the tab characters' (filename, save indicator, etc) - colors with respect to the tab bar. +* `tabbarchars`: sets what visual characters to be shown for various tabbar options. + This option is specified in the form of `key1=value1,key2=value2,...`. - default value: `false` + Here are the list of keys: + - `active`: the opening and closing tab characters for the current active tab, + where the values are splitted in half for opening and closing characters. + For example, value of `[[]]` will have `[[` as opening characters and + `]]` as closing characters. + - `div`: the characters to be filled between each tab. + - `inactive`: the opening and closing tab characters for the inactive tabs. + where the values are splitted in half for opening and closing characters. + + default value: `div=│,active= [] ,inactive= ` + +* `tabhighlight`: highlighting the current active tab by using the inverted tab bar color. + Has no effect if `tabbar.active` is present in the current colorscheme. + + default value: `true` * `tabmovement`: navigate spaces at the beginning of lines as if they are tabs (e.g. move over 4 spaces at once). This option only does anything if @@ -471,9 +485,9 @@ Here are the available options: default value: `false` -* `tabreverse`: reverses the tab bar colors when active. +* `tabreverse`: reverses the tab bar colors. - default value: `true` + default value: `false` * `tabsize`: the size in spaces that a tab character should be displayed with. @@ -626,7 +640,7 @@ so that you can see what the formatting should look like. "statusline": true, "sucmd": "sudo", "syntax": true, - "tabhighlight": true, + "tabhighlight": false, "tabmovement": false, "tabreverse": false, "tabsize": 4,