mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-05 14:40:20 +09:00
* Only show basename of file in tabs unless there are mutliple tabs with the same basename (fixes #1079) * Small fix
294 lines
7.3 KiB
Go
294 lines
7.3 KiB
Go
package main
|
|
|
|
import (
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"github.com/zyedidia/tcell"
|
|
)
|
|
|
|
var tabBarOffset int
|
|
|
|
// A Tab holds an array of views and a splitTree to determine how the
|
|
// views should be arranged
|
|
type Tab struct {
|
|
// This contains all the views in this tab
|
|
// There is generally only one view per tab, but you can have
|
|
// multiple views with splits
|
|
Views []*View
|
|
// This is the current view for this tab
|
|
CurView int
|
|
|
|
tree *SplitTree
|
|
}
|
|
|
|
// NewTabFromView creates a new tab and puts the given view in the tab
|
|
func NewTabFromView(v *View) *Tab {
|
|
t := new(Tab)
|
|
t.Views = append(t.Views, v)
|
|
t.Views[0].Num = 0
|
|
|
|
t.tree = new(SplitTree)
|
|
t.tree.kind = VerticalSplit
|
|
t.tree.children = []Node{NewLeafNode(t.Views[0], t.tree)}
|
|
|
|
w, h := screen.Size()
|
|
t.tree.width = w
|
|
t.tree.height = h
|
|
|
|
if globalSettings["infobar"].(bool) {
|
|
t.tree.height--
|
|
}
|
|
if globalSettings["keymenu"].(bool) {
|
|
t.tree.height -= 2
|
|
}
|
|
|
|
t.Resize()
|
|
|
|
return t
|
|
}
|
|
|
|
// SetNum sets all this tab's views to have the correct tab number
|
|
func (t *Tab) SetNum(num int) {
|
|
t.tree.tabNum = num
|
|
for _, v := range t.Views {
|
|
v.TabNum = num
|
|
}
|
|
}
|
|
|
|
// Cleanup cleans up the tree (for example if views have closed)
|
|
func (t *Tab) Cleanup() {
|
|
t.tree.Cleanup()
|
|
}
|
|
|
|
// Resize handles a resize event from the terminal and resizes
|
|
// all child views correctly
|
|
func (t *Tab) Resize() {
|
|
w, h := screen.Size()
|
|
t.tree.width = w
|
|
t.tree.height = h
|
|
|
|
if globalSettings["infobar"].(bool) {
|
|
t.tree.height--
|
|
}
|
|
if globalSettings["keymenu"].(bool) {
|
|
t.tree.height -= 2
|
|
}
|
|
|
|
t.tree.ResizeSplits()
|
|
|
|
for i, v := range t.Views {
|
|
v.Num = i
|
|
if v.Type == vtTerm {
|
|
v.term.Resize(v.Width, v.Height)
|
|
}
|
|
}
|
|
}
|
|
|
|
// CurView returns the current view
|
|
func CurView() *View {
|
|
curTab := tabs[curTab]
|
|
return curTab.Views[curTab.CurView]
|
|
}
|
|
|
|
// TabbarString returns the string that should be displayed in the tabbar
|
|
// It also returns a map containing which indicies correspond to which tab number
|
|
// This is useful when we know that the mouse click has occurred at an x location
|
|
// but need to know which tab that corresponds to to accurately change the tab
|
|
func TabbarString() (string, map[int]int) {
|
|
str := ""
|
|
indicies := make(map[int]int)
|
|
unique := make(map[string]int)
|
|
|
|
for _, t := range tabs {
|
|
unique[filepath.Base(t.Views[t.CurView].Buf.GetName())]++
|
|
}
|
|
|
|
for i, t := range tabs {
|
|
buf := t.Views[t.CurView].Buf
|
|
name := filepath.Base(buf.GetName())
|
|
|
|
if i == curTab {
|
|
str += "["
|
|
} else {
|
|
str += " "
|
|
}
|
|
if unique[name] == 1 {
|
|
str += name
|
|
} else {
|
|
str += buf.GetName()
|
|
}
|
|
if buf.Modified() {
|
|
str += " +"
|
|
}
|
|
if i == curTab {
|
|
str += "]"
|
|
} else {
|
|
str += " "
|
|
}
|
|
str += " "
|
|
|
|
indicies[Count(str)-2] = i + 1
|
|
}
|
|
return str, indicies
|
|
}
|
|
|
|
// TabbarHandleMouseEvent checks the given mouse event if it is clicking on the tabbar
|
|
// If it is it changes the current tab accordingly
|
|
// This function returns true if the tab is changed
|
|
func TabbarHandleMouseEvent(event tcell.Event) bool {
|
|
// There is no tabbar displayed if there are less than 2 tabs
|
|
if len(tabs) <= 1 {
|
|
return false
|
|
}
|
|
|
|
switch e := event.(type) {
|
|
case *tcell.EventMouse:
|
|
button := e.Buttons()
|
|
// Must be a left click
|
|
if button == tcell.Button1 {
|
|
x, y := e.Position()
|
|
if y != 0 {
|
|
return false
|
|
}
|
|
str, indicies := TabbarString()
|
|
if x+tabBarOffset >= len(str) {
|
|
return false
|
|
}
|
|
var tabnum int
|
|
var keys []int
|
|
for k := range indicies {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Ints(keys)
|
|
for _, k := range keys {
|
|
if x+tabBarOffset <= k {
|
|
tabnum = indicies[k] - 1
|
|
break
|
|
}
|
|
}
|
|
curTab = tabnum
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// DisplayTabs displays the tabbar at the top of the editor if there are multiple tabs
|
|
func DisplayTabs() {
|
|
if len(tabs) <= 1 {
|
|
return
|
|
}
|
|
|
|
str, indicies := TabbarString()
|
|
|
|
tabBarStyle := defStyle.Reverse(true)
|
|
if style, ok := colorscheme["tabbar"]; ok {
|
|
tabBarStyle = style
|
|
}
|
|
|
|
// Maybe there is a unicode filename?
|
|
fileRunes := []rune(str)
|
|
w, _ := screen.Size()
|
|
tooWide := (w < len(fileRunes))
|
|
|
|
// if the entire tab-bar is longer than the screen is wide,
|
|
// then it should be truncated appropriately to keep the
|
|
// active tab visible on the UI.
|
|
if tooWide == true {
|
|
// first we have to work out where the selected tab is
|
|
// out of the total length of the tab bar. this is done
|
|
// by extracting the hit-areas from the indicies map
|
|
// that was constructed by `TabbarString()`
|
|
var keys []int
|
|
for offset := range indicies {
|
|
keys = append(keys, offset)
|
|
}
|
|
// sort them to be in ascending order so that values will
|
|
// correctly reflect the displayed ordering of the tabs
|
|
sort.Ints(keys)
|
|
// record the offset of each tab and the previous tab so
|
|
// we can find the position of the tab's hit-box.
|
|
previousTabOffset := 0
|
|
currentTabOffset := 0
|
|
for _, k := range keys {
|
|
tabIndex := indicies[k] - 1
|
|
if tabIndex == curTab {
|
|
currentTabOffset = k
|
|
break
|
|
}
|
|
// this is +2 because there are two padding spaces that aren't accounted
|
|
// for in the display. please note that this is for cosmetic purposes only.
|
|
previousTabOffset = k + 2
|
|
}
|
|
// get the width of the hitbox of the active tab, from there calculate the offsets
|
|
// to the left and right of it to approximately center it on the tab bar display.
|
|
centeringOffset := (w - (currentTabOffset - previousTabOffset))
|
|
leftBuffer := previousTabOffset - (centeringOffset / 2)
|
|
rightBuffer := currentTabOffset + (centeringOffset / 2)
|
|
|
|
// check to make sure we haven't overshot the bounds of the string,
|
|
// if we have, then take that remainder and put it on the left side
|
|
overshotRight := rightBuffer - len(fileRunes)
|
|
if overshotRight > 0 {
|
|
leftBuffer = leftBuffer + overshotRight
|
|
}
|
|
|
|
overshotLeft := leftBuffer - 0
|
|
if overshotLeft < 0 {
|
|
leftBuffer = 0
|
|
rightBuffer = leftBuffer + (w - 1)
|
|
} else {
|
|
rightBuffer = leftBuffer + (w - 2)
|
|
}
|
|
|
|
if rightBuffer > len(fileRunes)-1 {
|
|
rightBuffer = len(fileRunes) - 1
|
|
}
|
|
|
|
// construct a new buffer of text to put the
|
|
// newly formatted tab bar text into.
|
|
var displayText []rune
|
|
|
|
// if the left-side of the tab bar isn't at the start
|
|
// of the constructed tab bar text, then show that are
|
|
// more tabs to the left by displaying a "+"
|
|
if leftBuffer != 0 {
|
|
displayText = append(displayText, '+')
|
|
}
|
|
// copy the runes in from the original tab bar text string
|
|
// into the new display buffer
|
|
for x := leftBuffer; x < rightBuffer; x++ {
|
|
displayText = append(displayText, fileRunes[x])
|
|
}
|
|
// if there is more text to the right of the right-most
|
|
// column in the tab bar text, then indicate there are more
|
|
// tabs to the right by displaying a "+"
|
|
if rightBuffer < len(fileRunes)-1 {
|
|
displayText = append(displayText, '+')
|
|
}
|
|
|
|
// now store the offset from zero of the left-most text
|
|
// that is being displayed. This is to ensure that when
|
|
// clicking on the tab bar, the correct tab gets selected.
|
|
tabBarOffset = leftBuffer
|
|
|
|
// use the constructed buffer as the display buffer to print
|
|
// onscreen.
|
|
fileRunes = displayText
|
|
} else {
|
|
tabBarOffset = 0
|
|
}
|
|
|
|
// iterate over the width of the terminal display and for each column,
|
|
// write a character into the tab display area with the appropriate style.
|
|
for x := 0; x < w; x++ {
|
|
if x < len(fileRunes) {
|
|
screen.SetContent(x, 0, fileRunes[x], nil, tabBarStyle)
|
|
} else {
|
|
screen.SetContent(x, 0, ' ', nil, tabBarStyle)
|
|
}
|
|
}
|
|
}
|