diff --git a/main.go b/main.go index f0705df..3397c81 100644 --- a/main.go +++ b/main.go @@ -68,12 +68,12 @@ func main() { var fileSelector *ui.FileSelectorDialog // if nil, we don't draw it - bar := ui.NewMenuBar(nil, &theme) + bar := ui.NewMenuBar(&theme) barFocused := false // TODO: load menus in another function - bar.Menus = append(bar.Menus, ui.NewMenu("File", &theme, []ui.Item{&ui.ItemEntry{Name: "New File", Callback: func() { + bar.AddMenu(ui.NewMenu("File", &theme, []ui.Item{&ui.ItemEntry{Name: "New File", Callback: func() { textEdit := ui.NewTextEdit(&s, "", "", &theme) // No file path, no contents tabContainer.AddTab("noname", textEdit) }}, &ui.ItemEntry{Name: "Open...", Callback: func() { @@ -112,7 +112,7 @@ func main() { changeFocus(fileSelector) }}, &ui.ItemEntry{Name: "Save", Callback: func() { if tabContainer.GetTabCount() > 0 { - tab := tabContainer.GetTab(tabContainer.Selected) + tab := tabContainer.GetTab(tabContainer.GetSelectedTabIdx()) te := tab.Child.(*ui.TextEdit) if len(te.FilePath) > 0 { contents := te.String() @@ -148,11 +148,11 @@ func main() { os.Exit(0) }}})) - bar.Menus = append(bar.Menus, ui.NewMenu("Edit", &theme, []ui.Item{&ui.ItemEntry{Name: "New", Callback: func() { + bar.AddMenu(ui.NewMenu("Edit", &theme, []ui.Item{&ui.ItemEntry{Name: "New", Callback: func() { s.Beep() }}})) - bar.Menus = append(bar.Menus, ui.NewMenu("Search", &theme, []ui.Item{&ui.ItemEntry{Name: "New", Callback: func() { + bar.AddMenu(ui.NewMenu("Search", &theme, []ui.Item{&ui.ItemEntry{Name: "New", Callback: func() { s.Beep() }}})) diff --git a/ui/container.go b/ui/container.go index a8f07c1..e56f3b7 100644 --- a/ui/container.go +++ b/ui/container.go @@ -107,12 +107,11 @@ type Tab struct { // A TabContainer organizes children by showing only one of them at a time. type TabContainer struct { - Selected int - children []Tab x, y int width, height int focused bool + selected int Theme *Theme } @@ -143,6 +142,28 @@ func (c *TabContainer) RemoveTab(idx int) bool { return false } +// FocusTab sets the visible tab to the one at `idx`. FocusTab clamps `idx` +// between 0 and tab_count - 1. If no tabs are present, the function does nothing. +func (c *TabContainer) FocusTab(idx int) { + if len(c.children) < 1 { + return + } + + if idx < 0 { + idx = 0 + } else if idx >= len(c.children) { + idx = len(c.children)-1 + } + + c.children[c.selected].Child.SetFocused(false) // Unfocus old tab + c.children[idx].Child.SetFocused(true) // Focus new tab + c.selected = idx +} + +func (c *TabContainer) GetSelectedTabIdx() int { + return c.selected +} + func (c *TabContainer) GetTabCount() int { return len(c.children) } @@ -166,7 +187,7 @@ func (c *TabContainer) Draw(s tcell.Screen) { col := c.x + c.width/2 - combinedTabLength/2 // Starting column for i, tab := range c.children { var sty tcell.Style - if c.Selected == i { + if c.selected == i { sty = c.Theme.GetOrDefault("TabSelected") } else { sty = c.Theme.GetOrDefault("Tab") @@ -178,16 +199,16 @@ func (c *TabContainer) Draw(s tcell.Screen) { } // Draw selected child in center - if c.Selected < len(c.children) { - c.children[c.Selected].Child.Draw(s) + if c.selected < len(c.children) { + c.children[c.selected].Child.Draw(s) } } // SetFocused calls SetFocused on the visible child Component. func (c *TabContainer) SetFocused(v bool) { c.focused = v - if c.Selected < len(c.children) { - c.children[c.Selected].Child.SetFocused(v) + if c.selected < len(c.children) { + c.children[c.selected].Child.SetFocused(v) } } @@ -211,8 +232,8 @@ func (c *TabContainer) GetPos() (int, int) { // SetPos sets the position of the container and updates the child Component. func (c *TabContainer) SetPos(x, y int) { c.x, c.y = x, y - if c.Selected < len(c.children) { - c.children[c.Selected].Child.SetPos(x+1, y+1) + if c.selected < len(c.children) { + c.children[c.selected].Child.SetPos(x+1, y+1) } } @@ -224,8 +245,8 @@ func (c *TabContainer) GetSize() (int, int) { // SetSize sets the size of the container and updates the size of the child Component. func (c *TabContainer) SetSize(width, height int) { c.width, c.height = width, height - if c.Selected < len(c.children) { - c.children[c.Selected].Child.SetSize(width-2, height-2) + if c.selected < len(c.children) { + c.children[c.selected].Child.SetSize(width-2, height-2) } } @@ -233,24 +254,27 @@ func (c *TabContainer) SetSize(width, height int) { func (c *TabContainer) HandleEvent(event tcell.Event) bool { switch ev := event.(type) { case *tcell.EventKey: - if ev.Key() == tcell.KeyTab { // Ctrl + Tab was pressed - if ev.Modifiers() == tcell.ModCtrl { - c.Selected++ - if c.Selected >= len(c.children) { - c.Selected = 0 - } - } else if ev.Modifiers() == tcell.ModCtrl&tcell.ModShift { // Ctrl + Shift + Tab was pressed - c.Selected-- - if c.Selected < 0 { - c.Selected = len(c.children) - 1 - } + if ev.Key() == tcell.KeyCtrlE { + newIdx := c.selected + 1 + if newIdx >= len(c.children) { + newIdx = 0 } + c.FocusTab(newIdx) + return true + } else if ev.Key() == tcell.KeyCtrlW { + newIdx := c.selected - 1 + if newIdx < 0 { + newIdx = len(c.children) - 1 + } + c.FocusTab(newIdx) + return true } } - if c.Selected < len(c.children) { - return c.children[c.Selected].Child.HandleEvent(event) + if c.selected < len(c.children) { + return c.children[c.selected].Child.HandleEvent(event) } + return false } diff --git a/ui/menu.go b/ui/menu.go index 75f9187..c85b36f 100644 --- a/ui/menu.go +++ b/ui/menu.go @@ -45,7 +45,7 @@ func (m *Menu) GetName() string { // A MenuBar is a horizontal list of menus. type MenuBar struct { - Menus []Menu + Menus []*Menu x, y int width, height int @@ -55,17 +55,20 @@ type MenuBar struct { Theme *Theme } -func NewMenuBar(menus []Menu, theme *Theme) *MenuBar { - if menus == nil { - menus = make([]Menu, 0, 6) - } - +func NewMenuBar(theme *Theme) *MenuBar { return &MenuBar{ - Menus: menus, + Menus: make([]*Menu, 0, 6), Theme: theme, } } +func (b *MenuBar) AddMenu(menu *Menu) { + menu.itemSelectedCallback = func() { + + } + b.Menus = append(b.Menus, menu) +} + // GetMenuXPos returns the X position of the name of Menu at `idx` visually. func (b *MenuBar) GetMenuXPos(idx int) int { x := 1 @@ -143,8 +146,8 @@ func (b *MenuBar) HandleEvent(event tcell.Event) bool { case *tcell.EventKey: if ev.Key() == tcell.KeyEnter && !b.Menus[b.selected].Visible { menu := &b.Menus[b.selected] - menu.SetPos(b.GetMenuXPos(b.selected), b.y+1) - menu.SetFocused(true) // Makes .Visible true for the Menu + (*menu).SetPos(b.GetMenuXPos(b.selected), b.y+1) + (*menu).SetFocused(true) // Makes .Visible true for the Menu } else if ev.Key() == tcell.KeyLeft { if b.selected <= 0 { b.selected = len(b.Menus) - 1 // Wrap to end @@ -179,20 +182,21 @@ type Menu struct { Items []Item Visible bool // True when focused - x, y int - width, height int // Size may not be settable - selected int // Index of selected Item + x, y int + width, height int // Size may not be settable + selected int // Index of selected Item + itemSelectedCallback func() // Used internally to hide menus on selection Theme *Theme } // New creates a new Menu. `items` can be `nil`. -func NewMenu(name string, theme *Theme, items []Item) Menu { +func NewMenu(name string, theme *Theme, items []Item) *Menu { if items == nil { items = make([]Item, 0, 6) } - return Menu{ + return &Menu{ Name: name, Items: items, Theme: theme, diff --git a/ui/textedit.go b/ui/textedit.go index 84ecfcd..e3ca1fe 100644 --- a/ui/textedit.go +++ b/ui/textedit.go @@ -334,7 +334,7 @@ func (t *TextEdit) Draw(s tcell.Screen) { func (t *TextEdit) SetFocused(v bool) { t.focused = v if v { - t.SetLineCol(t.curx, t.cury) + t.SetLineCol(t.cury, t.curx) } else { (*t.screen).HideCursor() } @@ -387,6 +387,10 @@ func (t *TextEdit) HandleEvent(event tcell.Event) bool { t.SetLineCol(t.cury, 0) case tcell.KeyEnd: t.SetLineCol(t.cury, len(t.buffer[t.cury])) + case tcell.KeyPgUp: + t.SetLineCol(t.scrolly - t.height, t.curx) // Go a page up + case tcell.KeyPgDn: + t.SetLineCol(t.scrolly + t.height*2 - 1, t.curx) // Go a page down // Deleting case tcell.KeyBackspace: