popup_menu.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package widget
  2. import (
  3. "fyne.io/fyne/v2"
  4. "fyne.io/fyne/v2/internal/widget"
  5. )
  6. var _ fyne.Widget = (*PopUpMenu)(nil)
  7. var _ fyne.Focusable = (*PopUpMenu)(nil)
  8. // PopUpMenu is a Menu which displays itself in an OverlayContainer.
  9. type PopUpMenu struct {
  10. *Menu
  11. canvas fyne.Canvas
  12. overlay *widget.OverlayContainer
  13. }
  14. // NewPopUpMenu creates a new, reusable popup menu. You can show it using ShowAtPosition.
  15. //
  16. // Since: 2.0
  17. func NewPopUpMenu(menu *fyne.Menu, c fyne.Canvas) *PopUpMenu {
  18. m := &Menu{}
  19. m.setMenu(menu)
  20. p := &PopUpMenu{Menu: m, canvas: c}
  21. p.ExtendBaseWidget(p)
  22. p.Menu.Resize(p.Menu.MinSize())
  23. p.Menu.customSized = true
  24. o := widget.NewOverlayContainer(p, c, p.Dismiss)
  25. o.Resize(o.MinSize())
  26. p.overlay = o
  27. p.OnDismiss = func() {
  28. p.Hide()
  29. }
  30. return p
  31. }
  32. // ShowPopUpMenuAtPosition creates a PopUp menu populated with items from the passed menu structure.
  33. // It will automatically be positioned at the provided location and shown as an overlay on the specified canvas.
  34. func ShowPopUpMenuAtPosition(menu *fyne.Menu, c fyne.Canvas, pos fyne.Position) {
  35. m := NewPopUpMenu(menu, c)
  36. m.ShowAtPosition(pos)
  37. }
  38. // FocusGained is triggered when the object gained focus. For the pop-up menu it does nothing.
  39. //
  40. // Implements: fyne.Focusable
  41. func (p *PopUpMenu) FocusGained() {}
  42. // FocusLost is triggered when the object lost focus. For the pop-up menu it does nothing.
  43. //
  44. // Implements: fyne.Focusable
  45. func (p *PopUpMenu) FocusLost() {}
  46. // Hide hides the pop-up menu.
  47. //
  48. // Implements: fyne.Widget
  49. func (p *PopUpMenu) Hide() {
  50. p.overlay.Hide()
  51. p.Menu.Hide()
  52. }
  53. // Move moves the pop-up menu.
  54. // The position is absolute because pop-up menus are shown in an overlay which covers the whole canvas.
  55. //
  56. // Implements: fyne.Widget
  57. func (p *PopUpMenu) Move(pos fyne.Position) {
  58. p.BaseWidget.Move(p.adjustedPosition(pos, p.Size()))
  59. }
  60. // Resize changes the size of the pop-up menu.
  61. //
  62. // Implements: fyne.Widget
  63. func (p *PopUpMenu) Resize(size fyne.Size) {
  64. p.BaseWidget.Move(p.adjustedPosition(p.Position(), size))
  65. p.Menu.Resize(size)
  66. }
  67. // Show makes the pop-up menu visible.
  68. //
  69. // Implements: fyne.Widget
  70. func (p *PopUpMenu) Show() {
  71. p.Menu.alignment = p.alignment
  72. p.Menu.Refresh()
  73. p.overlay.Show()
  74. p.Menu.Show()
  75. if !fyne.CurrentDevice().IsMobile() {
  76. p.canvas.Focus(p)
  77. }
  78. }
  79. // ShowAtPosition shows the pop-up menu at the specified position.
  80. func (p *PopUpMenu) ShowAtPosition(pos fyne.Position) {
  81. p.Move(pos)
  82. p.Show()
  83. }
  84. // TypedKey handles key events. It allows keyboard control of the pop-up menu.
  85. //
  86. // Implements: fyne.Focusable
  87. func (p *PopUpMenu) TypedKey(e *fyne.KeyEvent) {
  88. switch e.Name {
  89. case fyne.KeyDown:
  90. p.ActivateNext()
  91. case fyne.KeyEnter, fyne.KeyReturn, fyne.KeySpace:
  92. p.TriggerLast()
  93. case fyne.KeyEscape:
  94. p.Dismiss()
  95. case fyne.KeyLeft:
  96. p.DeactivateLastSubmenu()
  97. case fyne.KeyRight:
  98. p.ActivateLastSubmenu()
  99. case fyne.KeyUp:
  100. p.ActivatePrevious()
  101. }
  102. }
  103. // TypedRune handles text events. For pop-up menus this does nothing.
  104. //
  105. // Implements: fyne.Focusable
  106. func (p *PopUpMenu) TypedRune(rune) {}
  107. func (p *PopUpMenu) adjustedPosition(pos fyne.Position, size fyne.Size) fyne.Position {
  108. x := pos.X
  109. y := pos.Y
  110. if x+size.Width > p.canvas.Size().Width {
  111. x = p.canvas.Size().Width - size.Width
  112. if x < 0 {
  113. x = 0 // TODO here we may need a scroller as it's wider than our canvas
  114. }
  115. }
  116. if y+size.Height > p.canvas.Size().Height {
  117. y = p.canvas.Size().Height - size.Height
  118. if y < 0 {
  119. y = 0 // TODO here we may need a scroller as it's longer than our canvas
  120. }
  121. }
  122. return fyne.NewPos(x, y)
  123. }