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.Box 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.SetLayout(layouts.ContractVertical) if !menu.torn { menu.tearLine = tomo.NewBox() menu.tearLine.SetRole(tomo.R("objects", "TearLine", "")) tomo.Apply(menu.tearLine) menu.tearLine.SetFocusable(true) menu.tearLine.OnKeyUp(func (key input.Key, numberPad bool) { if key != input.KeyEnter && key != input.Key(' ') { return } menu.TearOff() }) menu.tearLine.OnMouseUp(func (button input.Button) { if button != input.ButtonLeft { return } if menu.tearLine.MousePosition().In(menu.tearLine.Bounds()) { menu.TearOff() } }) 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")) tomo.Apply(menu.rootContainer) 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 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) }