popup_menu.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. // ShowPopUpMenuAtRelativePosition creates a PopUp menu populated with meny items from the passed menu structure.
  39. // It will automatically be positioned at the given position relative to stated object and shown as an overlay on the specified canvas.
  40. //
  41. // Since 2.4
  42. func ShowPopUpMenuAtRelativePosition(menu *fyne.Menu, c fyne.Canvas, rel fyne.Position, to fyne.CanvasObject) {
  43. withRelativePosition(rel, to, func(pos fyne.Position) {
  44. ShowPopUpMenuAtPosition(menu, c, pos)
  45. })
  46. }
  47. // FocusGained is triggered when the object gained focus. For the pop-up menu it does nothing.
  48. //
  49. // Implements: fyne.Focusable
  50. func (p *PopUpMenu) FocusGained() {}
  51. // FocusLost is triggered when the object lost focus. For the pop-up menu it does nothing.
  52. //
  53. // Implements: fyne.Focusable
  54. func (p *PopUpMenu) FocusLost() {}
  55. // Hide hides the pop-up menu.
  56. //
  57. // Implements: fyne.Widget
  58. func (p *PopUpMenu) Hide() {
  59. p.overlay.Hide()
  60. p.Menu.Hide()
  61. }
  62. // Move moves the pop-up menu.
  63. // The position is absolute because pop-up menus are shown in an overlay which covers the whole canvas.
  64. //
  65. // Implements: fyne.Widget
  66. func (p *PopUpMenu) Move(pos fyne.Position) {
  67. p.BaseWidget.Move(p.adjustedPosition(pos, p.Size()))
  68. }
  69. // Resize changes the size of the pop-up menu.
  70. //
  71. // Implements: fyne.Widget
  72. func (p *PopUpMenu) Resize(size fyne.Size) {
  73. p.BaseWidget.Move(p.adjustedPosition(p.Position(), size))
  74. p.Menu.Resize(size)
  75. }
  76. // Show makes the pop-up menu visible.
  77. //
  78. // Implements: fyne.Widget
  79. func (p *PopUpMenu) Show() {
  80. p.Menu.alignment = p.alignment
  81. p.Menu.Refresh()
  82. p.overlay.Show()
  83. p.Menu.Show()
  84. if !fyne.CurrentDevice().IsMobile() {
  85. p.canvas.Focus(p)
  86. }
  87. }
  88. // ShowAtPosition shows the pop-up menu at the specified position.
  89. func (p *PopUpMenu) ShowAtPosition(pos fyne.Position) {
  90. p.Move(pos)
  91. p.Show()
  92. }
  93. // ShowAtRelativePosition shows the pop-up menu at the position relative to given object.
  94. //
  95. // Since 2.4
  96. func (p *PopUpMenu) ShowAtRelativePosition(rel fyne.Position, to fyne.CanvasObject) {
  97. withRelativePosition(rel, to, p.ShowAtPosition)
  98. }
  99. // TypedKey handles key events. It allows keyboard control of the pop-up menu.
  100. //
  101. // Implements: fyne.Focusable
  102. func (p *PopUpMenu) TypedKey(e *fyne.KeyEvent) {
  103. switch e.Name {
  104. case fyne.KeyDown:
  105. p.ActivateNext()
  106. case fyne.KeyEnter, fyne.KeyReturn, fyne.KeySpace:
  107. p.TriggerLast()
  108. case fyne.KeyEscape:
  109. p.Dismiss()
  110. case fyne.KeyLeft:
  111. p.DeactivateLastSubmenu()
  112. case fyne.KeyRight:
  113. p.ActivateLastSubmenu()
  114. case fyne.KeyUp:
  115. p.ActivatePrevious()
  116. }
  117. }
  118. // TypedRune handles text events. For pop-up menus this does nothing.
  119. //
  120. // Implements: fyne.Focusable
  121. func (p *PopUpMenu) TypedRune(rune) {}
  122. func (p *PopUpMenu) adjustedPosition(pos fyne.Position, size fyne.Size) fyne.Position {
  123. x := pos.X
  124. y := pos.Y
  125. if x+size.Width > p.canvas.Size().Width {
  126. x = p.canvas.Size().Width - size.Width
  127. if x < 0 {
  128. x = 0 // TODO here we may need a scroller as it's wider than our canvas
  129. }
  130. }
  131. if y+size.Height > p.canvas.Size().Height {
  132. y = p.canvas.Size().Height - size.Height
  133. if y < 0 {
  134. y = 0 // TODO here we may need a scroller as it's longer than our canvas
  135. }
  136. }
  137. return fyne.NewPos(x, y)
  138. }