package objects import "image" import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo/input" // import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/objects/layouts" // Menu is a menu window. type Menu struct { tomo.Window parent tomo.Window bounds image.Rectangle rootContainer tomo.ContainerBox tearLine tomo.Object torn bool } // NewMenu creates a new menu with the specified items. The menu will appear // directly under the anchor Object. If the anchor is nil, it will appear // directly under the mouse pointer instead. func NewMenu (anchor tomo.Object, items ...tomo.Object) (*Menu, error) { menu := &Menu { } if anchor == nil { // TODO: *actually* put it under the mouse window, err := tomo.NewWindow(menu.bounds) if err != nil { return nil, err } menu.Window = window } else { menu.bounds = menuBoundsFromAnchor(anchor) menu.parent = anchor.GetBox().Window() window, err := menu.parent.NewMenu(menu.bounds) if err != nil { return nil, err } menu.Window = window } menu.rootContainer = tomo.NewContainerBox() menu.rootContainer.SetAttr(tomo.ALayout(layouts.ContractVertical)) if !menu.torn { menu.tearLine = menu.newTearLine() menu.rootContainer.Add(menu.tearLine) } for _, item := range items { menu.rootContainer.Add(item) if item, ok := item.(*MenuItem); ok { item.OnClick(func () { if !menu.torn { menu.Close() } }) } } menu.rootContainer.SetRole(tomo.R("objects", "Container")) menu.rootContainer.SetTag("menu", true) menu.Window.SetRoot(menu.rootContainer) return menu, nil } // TearOff converts this menu into a tear-off menu. func (this *Menu) TearOff () { if this.torn { return } if this.parent == nil { return } this.torn = true window, err := this.parent.NewChild(this.bounds) if err != nil { return } visible := this.Window.Visible() this.Window.SetRoot(nil) this.Window.Close() this.rootContainer.Remove(this.tearLine) this.Window = window this.Window.SetRoot(this.rootContainer) this.Window.SetVisible(visible) } func (this *Menu) newTearLine () tomo.Object { tearLine := tomo.NewBox() tearLine.SetRole(tomo.R("objects", "TearLine")) tearLine.SetFocusable(true) tearLine.OnKeyDown(func (key input.Key, numberPad bool) bool { if key != input.KeyEnter && key != input.Key(' ') { return false } return true }) tearLine.OnKeyUp(func (key input.Key, numberPad bool) bool { if key != input.KeyEnter && key != input.Key(' ') { return false } this.TearOff() return true }) tearLine.OnButtonDown(func (button input.Button) bool { if button != input.ButtonLeft { return false } return true }) tearLine.OnButtonUp(func (button input.Button) bool { if button != input.ButtonLeft { return false } if tearLine.Window().MousePosition().In(tearLine.Bounds()) { this.TearOff() } return true }) return tearLine } func menuBoundsFromAnchor (anchor tomo.Object) image.Rectangle { bounds := anchor.GetBox().Bounds() return image.Rect ( bounds.Min.X, bounds.Max.Y, bounds.Max.X, bounds.Max.Y)//.Add(windowBounds.Min) }