X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Ftab.go;h=b03736705d684809185a8d16ec4d77b2bd91c8cb;hb=ea6a87d41a9fcaa9fff81c1a5310726b1e15c548;hp=370a5ed0e7cc57f22be2be99b99e06ef353e93b6;hpb=1295b877e307b1928d0ec9a8c5a8f31db289c60a;p=micro.git diff --git a/cmd/micro/tab.go b/cmd/micro/tab.go index 370a5ed0..b0373670 100644 --- a/cmd/micro/tab.go +++ b/cmd/micro/tab.go @@ -6,36 +6,94 @@ import ( "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 - // Generally this is the name of the current view's buffer - name string + 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] + 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) @@ -45,19 +103,27 @@ func TabbarString() (string, map[int]int) { } else { str += " " } - str += t.views[t.curView].Buf.Name + buf := t.views[t.CurView].Buf + str += buf.GetName() + if buf.Modified() { + str += " +" + } if i == curTab { str += "]" } else { str += " " } - indicies[len(str)-1] = i + 1 + indicies[Count(str)-1] = i + 1 str += " " } 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 } @@ -65,13 +131,14 @@ func TabbarHandleMouseEvent(event tcell.Event) bool { 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 >= len(str) { + if x+tabBarOffset >= len(str) { return false } var tabnum int @@ -81,7 +148,7 @@ func TabbarHandleMouseEvent(event tcell.Event) bool { } sort.Ints(keys) for _, k := range keys { - if x <= k { + if x+tabBarOffset <= k { tabnum = indicies[k] - 1 break } @@ -94,12 +161,13 @@ func TabbarHandleMouseEvent(event tcell.Event) bool { 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, _ := TabbarString() + str, indicies := TabbarString() tabBarStyle := defStyle.Reverse(true) if style, ok := colorscheme["tabbar"]; ok { @@ -109,6 +177,98 @@ func DisplayTabs() { // 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)