157 lines
4.2 KiB
Go
157 lines
4.2 KiB
Go
|
package widget
|
||
|
|
||
|
import (
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/internal/widget"
|
||
|
)
|
||
|
|
||
|
var _ fyne.Widget = (*PopUpMenu)(nil)
|
||
|
var _ fyne.Focusable = (*PopUpMenu)(nil)
|
||
|
|
||
|
// PopUpMenu is a Menu which displays itself in an OverlayContainer.
|
||
|
type PopUpMenu struct {
|
||
|
*Menu
|
||
|
canvas fyne.Canvas
|
||
|
overlay *widget.OverlayContainer
|
||
|
}
|
||
|
|
||
|
// NewPopUpMenu creates a new, reusable popup menu. You can show it using ShowAtPosition.
|
||
|
//
|
||
|
// Since: 2.0
|
||
|
func NewPopUpMenu(menu *fyne.Menu, c fyne.Canvas) *PopUpMenu {
|
||
|
m := &Menu{}
|
||
|
m.setMenu(menu)
|
||
|
p := &PopUpMenu{Menu: m, canvas: c}
|
||
|
p.ExtendBaseWidget(p)
|
||
|
p.Menu.Resize(p.Menu.MinSize())
|
||
|
p.Menu.customSized = true
|
||
|
o := widget.NewOverlayContainer(p, c, p.Dismiss)
|
||
|
o.Resize(o.MinSize())
|
||
|
p.overlay = o
|
||
|
p.OnDismiss = func() {
|
||
|
p.Hide()
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
// ShowPopUpMenuAtPosition creates a PopUp menu populated with items from the passed menu structure.
|
||
|
// It will automatically be positioned at the provided location and shown as an overlay on the specified canvas.
|
||
|
func ShowPopUpMenuAtPosition(menu *fyne.Menu, c fyne.Canvas, pos fyne.Position) {
|
||
|
m := NewPopUpMenu(menu, c)
|
||
|
m.ShowAtPosition(pos)
|
||
|
}
|
||
|
|
||
|
// ShowPopUpMenuAtRelativePosition creates a PopUp menu populated with meny items from the passed menu structure.
|
||
|
// It will automatically be positioned at the given position relative to stated object and shown as an overlay on the specified canvas.
|
||
|
//
|
||
|
// Since 2.4
|
||
|
func ShowPopUpMenuAtRelativePosition(menu *fyne.Menu, c fyne.Canvas, rel fyne.Position, to fyne.CanvasObject) {
|
||
|
withRelativePosition(rel, to, func(pos fyne.Position) {
|
||
|
ShowPopUpMenuAtPosition(menu, c, pos)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// FocusGained is triggered when the object gained focus. For the pop-up menu it does nothing.
|
||
|
//
|
||
|
// Implements: fyne.Focusable
|
||
|
func (p *PopUpMenu) FocusGained() {}
|
||
|
|
||
|
// FocusLost is triggered when the object lost focus. For the pop-up menu it does nothing.
|
||
|
//
|
||
|
// Implements: fyne.Focusable
|
||
|
func (p *PopUpMenu) FocusLost() {}
|
||
|
|
||
|
// Hide hides the pop-up menu.
|
||
|
//
|
||
|
// Implements: fyne.Widget
|
||
|
func (p *PopUpMenu) Hide() {
|
||
|
p.overlay.Hide()
|
||
|
p.Menu.Hide()
|
||
|
}
|
||
|
|
||
|
// Move moves the pop-up menu.
|
||
|
// The position is absolute because pop-up menus are shown in an overlay which covers the whole canvas.
|
||
|
//
|
||
|
// Implements: fyne.Widget
|
||
|
func (p *PopUpMenu) Move(pos fyne.Position) {
|
||
|
p.BaseWidget.Move(p.adjustedPosition(pos, p.Size()))
|
||
|
}
|
||
|
|
||
|
// Resize changes the size of the pop-up menu.
|
||
|
//
|
||
|
// Implements: fyne.Widget
|
||
|
func (p *PopUpMenu) Resize(size fyne.Size) {
|
||
|
p.BaseWidget.Move(p.adjustedPosition(p.Position(), size))
|
||
|
p.Menu.Resize(size)
|
||
|
}
|
||
|
|
||
|
// Show makes the pop-up menu visible.
|
||
|
//
|
||
|
// Implements: fyne.Widget
|
||
|
func (p *PopUpMenu) Show() {
|
||
|
p.Menu.alignment = p.alignment
|
||
|
p.Menu.Refresh()
|
||
|
|
||
|
p.overlay.Show()
|
||
|
p.Menu.Show()
|
||
|
if !fyne.CurrentDevice().IsMobile() {
|
||
|
p.canvas.Focus(p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ShowAtPosition shows the pop-up menu at the specified position.
|
||
|
func (p *PopUpMenu) ShowAtPosition(pos fyne.Position) {
|
||
|
p.Move(pos)
|
||
|
p.Show()
|
||
|
}
|
||
|
|
||
|
// ShowAtRelativePosition shows the pop-up menu at the position relative to given object.
|
||
|
//
|
||
|
// Since 2.4
|
||
|
func (p *PopUpMenu) ShowAtRelativePosition(rel fyne.Position, to fyne.CanvasObject) {
|
||
|
withRelativePosition(rel, to, p.ShowAtPosition)
|
||
|
}
|
||
|
|
||
|
// TypedKey handles key events. It allows keyboard control of the pop-up menu.
|
||
|
//
|
||
|
// Implements: fyne.Focusable
|
||
|
func (p *PopUpMenu) TypedKey(e *fyne.KeyEvent) {
|
||
|
switch e.Name {
|
||
|
case fyne.KeyDown:
|
||
|
p.ActivateNext()
|
||
|
case fyne.KeyEnter, fyne.KeyReturn, fyne.KeySpace:
|
||
|
p.TriggerLast()
|
||
|
case fyne.KeyEscape:
|
||
|
p.Dismiss()
|
||
|
case fyne.KeyLeft:
|
||
|
p.DeactivateLastSubmenu()
|
||
|
case fyne.KeyRight:
|
||
|
p.ActivateLastSubmenu()
|
||
|
case fyne.KeyUp:
|
||
|
p.ActivatePrevious()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TypedRune handles text events. For pop-up menus this does nothing.
|
||
|
//
|
||
|
// Implements: fyne.Focusable
|
||
|
func (p *PopUpMenu) TypedRune(rune) {}
|
||
|
|
||
|
func (p *PopUpMenu) adjustedPosition(pos fyne.Position, size fyne.Size) fyne.Position {
|
||
|
x := pos.X
|
||
|
y := pos.Y
|
||
|
if x+size.Width > p.canvas.Size().Width {
|
||
|
x = p.canvas.Size().Width - size.Width
|
||
|
if x < 0 {
|
||
|
x = 0 // TODO here we may need a scroller as it's wider than our canvas
|
||
|
}
|
||
|
}
|
||
|
if y+size.Height > p.canvas.Size().Height {
|
||
|
y = p.canvas.Size().Height - size.Height
|
||
|
if y < 0 {
|
||
|
y = 0 // TODO here we may need a scroller as it's longer than our canvas
|
||
|
}
|
||
|
}
|
||
|
return fyne.NewPos(x, y)
|
||
|
}
|