package ui // Where the menu bar puts its panels, and how it paints them. import ( "codeberg.org/turbo-editors/turbo-core/theme" ) // menuX returns the column the title of menu i starts at. func (b *MenuBar) menuX(i int) int { x := b.Bounds().X + 1 for _, m := range b.menus[:i] { x += LabelWidth(m.Label) + 2 } return x } // dropdownBounds returns the rectangle the open drop-down occupies. func (b *MenuBar) dropdownBounds() Rect { menu := b.menus[b.openMenu] width := 0 for _, item := range menu.Items { width = max(width, item.width()) } width += 4 // one cell of frame and one of padding on each side return Rect{ X: b.menuX(b.openMenu) - 1, Y: b.Bounds().Y + 1, W: width, H: len(menu.Items) + 2, } } // Draw paints the bar and, if one is open, the drop-down under it. func (b *MenuBar) Draw(p *Painter, th *theme.Theme) { bar := p.Sub(b.Bounds()) bar.Clear(th.Style(theme.KeyMenuBar)) for i, menu := range b.menus { normal, hot := th.Style(theme.KeyMenuItem), th.Style(theme.KeyMenuShortcut) if i == b.openMenu { normal = th.Style(theme.KeyMenuSelected) hot = normal } x := b.menuX(i) - b.Bounds().X bar.SetCell(x-1, 0, ' ', normal) end := DrawLabel(bar, x, 0, menu.Label, normal, hot) bar.SetCell(end, 0, ' ', normal) } if b.Open() { b.drawDropdown(p, th) } if b.submenuOpen() { b.drawSubmenu(p, th) } } // drawDropdown paints the open menu's frame and items. func (b *MenuBar) drawDropdown(p *Painter, th *theme.Theme) { bounds := b.dropdownBounds() DrawShadow(p, bounds, th.Style(theme.KeyShadow)) panel := p.Sub(bounds) panel.Clear(th.Style(theme.KeyMenuItem)) DrawFrame(panel, panel.Size(), FrameSingle, th.Style(theme.KeyMenuItem)) for i, item := range b.menus[b.openMenu].Items { // The item whose submenu is showing stays highlighted, so the eye can // see which branch the second panel came from. b.drawLine(panel, th, i, item, i == b.highlight || i == b.openItem) } } // drawLine paints one line of a drop-down. func (b *MenuBar) drawLine(p *Painter, th *theme.Theme, i int, item *MenuItem, highlighted bool) { y := i + 1 width := p.Size().W if item.Separator { // The rule joins the frame on both sides, as Turbo Vision drew it. style := th.Style(theme.KeyMenuItem) p.SetCell(0, y, '├', style) p.HLine(1, y, width-2, '─', style) p.SetCell(width-1, y, '┤', style) return } normal, hot := th.Style(theme.KeyMenuItem), th.Style(theme.KeyMenuShortcut) switch { case !item.enabled(): normal, hot = th.Style(theme.KeyMenuDisabled), th.Style(theme.KeyMenuDisabled) case highlighted: normal = th.Style(theme.KeyMenuSelected) hot = normal } p.HLine(1, y, width-2, ' ', normal) DrawLabel(p, 2, y, item.Label, normal, hot) switch { case item.Shortcut != "": p.Text(width-2-len([]rune(item.Shortcut)), y, item.Shortcut, normal) case item.hasSubmenu(): p.Text(width-2-len([]rune(submenuMarker)), y, submenuMarker, normal) } }