popup_menu.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #pragma once
  8. #include "styles/style_widgets.h"
  9. #include "ui/widgets/menu/menu.h"
  10. #include "ui/effects/animations.h"
  11. #include "ui/effects/panel_animation.h"
  12. #include "ui/round_rect.h"
  13. #include "ui/rp_widget.h"
  14. #include "base/object_ptr.h"
  15. #include "base/unique_qptr.h"
  16. namespace style {
  17. struct MenuSeparator;
  18. } // namespace style
  19. namespace Ui {
  20. class ScrollArea;
  21. class PopupMenu : public RpWidget {
  22. public:
  23. enum class VerticalOrigin {
  24. Top,
  25. Bottom,
  26. };
  27. enum class AnimatePhase {
  28. Hidden,
  29. StartShow,
  30. Shown,
  31. StartHide,
  32. };
  33. PopupMenu(QWidget *parent, const style::PopupMenu &st = st::defaultPopupMenu);
  34. PopupMenu(QWidget *parent, QMenu *menu, const style::PopupMenu &st = st::defaultPopupMenu);
  35. ~PopupMenu();
  36. [[nodiscard]] const style::PopupMenu &st() const {
  37. return _st;
  38. }
  39. [[nodiscard]] QRect inner() const {
  40. return _inner;
  41. }
  42. [[nodiscard]] rpl::producer<AnimatePhase> animatePhaseValue() const {
  43. return _animatePhase.value();
  44. }
  45. not_null<QAction*> addAction(base::unique_qptr<Menu::ItemBase> widget);
  46. not_null<QAction*> addAction(
  47. const QString &text,
  48. Fn<void()> callback,
  49. const style::icon *icon = nullptr,
  50. const style::icon *iconOver = nullptr);
  51. not_null<QAction*> addAction(
  52. const QString &text,
  53. std::unique_ptr<PopupMenu> submenu,
  54. const style::icon *icon = nullptr,
  55. const style::icon *iconOver = nullptr);
  56. not_null<QAction*> addSeparator(
  57. const style::MenuSeparator *st = nullptr);
  58. not_null<QAction*> insertAction(
  59. int position,
  60. base::unique_qptr<Menu::ItemBase> widget);
  61. void removeAction(int position);
  62. void clearActions();
  63. [[nodiscard]] const std::vector<not_null<QAction*>> &actions() const;
  64. [[nodiscard]] not_null<PopupMenu*> ensureSubmenu(
  65. not_null<QAction*> action,
  66. const style::PopupMenu &st);
  67. void removeSubmenu(not_null<QAction*> action);
  68. void checkSubmenuShow();
  69. bool empty() const;
  70. void deleteOnHide(bool del);
  71. void popup(const QPoint &p);
  72. bool prepareGeometryFor(const QPoint &p);
  73. void popupPrepared();
  74. void hideMenu(bool fast = false);
  75. void setTopShift(int topShift);
  76. void setForceWidth(int forceWidth);
  77. void setForcedOrigin(PanelAnimation::Origin origin);
  78. void setForcedVerticalOrigin(VerticalOrigin origin);
  79. void setAdditionalMenuPadding(QMargins padding, QMargins margins);
  80. [[nodiscard]] PanelAnimation::Origin preparedOrigin() const;
  81. [[nodiscard]] QMargins preparedPadding() const;
  82. [[nodiscard]] QMargins preparedMargins() const;
  83. [[nodiscard]] bool useTransparency() const;
  84. [[nodiscard]] int scrollTop() const;
  85. [[nodiscard]] rpl::producer<int> scrollTopValue() const;
  86. void setDestroyedCallback(Fn<void()> callback) {
  87. _destroyedCallback = std::move(callback);
  88. }
  89. void discardParentReActivate() {
  90. _reactivateParent = false;
  91. }
  92. [[nodiscard]] not_null<Menu::Menu*> menu() const {
  93. return _menu;
  94. }
  95. struct ShowState {
  96. float64 opacity = 1.;
  97. float64 widthProgress = 1.;
  98. float64 heightProgress = 1.;
  99. int appearingWidth = 0;
  100. int appearingHeight = 0;
  101. bool appearing = false;
  102. bool toggling = false;
  103. };
  104. [[nodiscard]] rpl::producer<ShowState> showStateValue() const;
  105. void setClearLastSeparator(bool clear);
  106. protected:
  107. void paintEvent(QPaintEvent *e) override;
  108. void focusOutEvent(QFocusEvent *e) override;
  109. void hideEvent(QHideEvent *e) override;
  110. void keyPressEvent(QKeyEvent *e) override;
  111. void mouseMoveEvent(QMouseEvent *e) override;
  112. void mousePressEvent(QMouseEvent *e) override;
  113. bool eventFilter(QObject *o, QEvent *e) override;
  114. private:
  115. void paintBg(QPainter &p);
  116. void hideFast();
  117. void setOrigin(PanelAnimation::Origin origin);
  118. void showAnimated(PanelAnimation::Origin origin);
  119. void hideAnimated();
  120. QImage grabForPanelAnimation();
  121. void startShowAnimation();
  122. void startOpacityAnimation(bool hiding);
  123. void prepareCache();
  124. void childHiding(PopupMenu *child);
  125. void showAnimationCallback();
  126. void opacityAnimationCallback();
  127. void init();
  128. void hideFinished();
  129. void showStarted();
  130. void fireCurrentShowState();
  131. using TriggeredSource = Menu::TriggeredSource;
  132. void validateCompositingSupport();
  133. void handleMenuResize();
  134. void handleActivated(const Menu::CallbackData &data);
  135. void handleTriggered(const Menu::CallbackData &data);
  136. void forwardKeyPress(not_null<QKeyEvent*> e);
  137. bool handleKeyPress(int key);
  138. void forwardMouseMove(QPoint globalPosition) {
  139. _menu->handleMouseMove(globalPosition);
  140. }
  141. void handleMouseMove(QPoint globalPosition);
  142. void forwardMousePress(QPoint globalPosition) {
  143. _menu->handleMousePress(globalPosition);
  144. }
  145. void handleMousePress(QPoint globalPosition);
  146. void forwardMouseRelease(QPoint globalPosition) {
  147. _menu->handleMouseRelease(globalPosition);
  148. }
  149. void handleMouseRelease(QPoint globalPosition);
  150. bool popupSubmenuFromAction(const Menu::CallbackData &data);
  151. void popupSubmenu(
  152. not_null<QAction*> action,
  153. not_null<PopupMenu*> submenu,
  154. int actionTop,
  155. TriggeredSource source);
  156. bool prepareGeometryFor(const QPoint &p, PopupMenu *parent);
  157. void showPrepared(TriggeredSource source);
  158. void updateRoundingOverlay();
  159. const style::PopupMenu &_st;
  160. RoundRect _roundRect;
  161. object_ptr<ScrollArea> _scroll;
  162. not_null<Menu::Menu*> _menu;
  163. object_ptr<RpWidget> _roundingOverlay = { nullptr };
  164. base::flat_map<
  165. not_null<QAction*>,
  166. base::unique_qptr<PopupMenu>> _submenus;
  167. PopupMenu *_parent = nullptr;
  168. QRect _inner;
  169. QMargins _padding;
  170. QMargins _margins;
  171. QMargins _additionalMenuPadding;
  172. QMargins _additionalMenuMargins;
  173. QPointer<PopupMenu> _activeSubmenu;
  174. std::optional<VerticalOrigin> _forcedVerticalOrigin;
  175. PanelAnimation::Origin _origin = PanelAnimation::Origin::TopLeft;
  176. std::optional<PanelAnimation::Origin> _forcedOrigin;
  177. std::unique_ptr<PanelAnimation> _showAnimation;
  178. Animations::Simple _a_show;
  179. rpl::event_stream<ShowState> _showStateChanges;
  180. rpl::variable<AnimatePhase> _animatePhase = AnimatePhase::Hidden;
  181. bool _useTransparency = true;
  182. bool _hiding = false;
  183. QPixmap _cache;
  184. Animations::Simple _a_opacity;
  185. bool _deleteOnHide = true;
  186. bool _triggering = false;
  187. bool _deleteLater = false;
  188. bool _reactivateParent = true;
  189. bool _grabbingForPanelAnimation = false;
  190. int _topShift = 0;
  191. bool _clearLastSeparator = true;
  192. Fn<void()> _destroyedCallback;
  193. };
  194. } // namespace Ui