rp_widget.h 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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 "base/unique_qptr.h"
  9. #include "ui/style/style_core_direction.h"
  10. #include <rpl/event_stream.h>
  11. #include <rpl/map.h>
  12. #include <rpl/distinct_until_changed.h>
  13. #include <QtWidgets/QWidget>
  14. #include <QtCore/QPointer>
  15. #include <QtGui/QtEvents>
  16. namespace Ui {
  17. void ToggleChildrenVisibility(not_null<QWidget*> widget, bool visible);
  18. } // namespace Ui
  19. class TWidget;
  20. template <typename Base>
  21. class TWidgetHelper : public Base {
  22. public:
  23. using Base::Base;
  24. virtual QMargins getMargins() const {
  25. return QMargins();
  26. }
  27. void hideChildren() {
  28. Ui::ToggleChildrenVisibility(this, false);
  29. }
  30. void showChildren() {
  31. Ui::ToggleChildrenVisibility(this, true);
  32. }
  33. void moveToLeft(int x, int y, int outerw = 0) {
  34. auto margins = getMargins();
  35. x -= margins.left();
  36. y -= margins.top();
  37. Base::move(style::RightToLeft() ? ((outerw > 0 ? outerw : Base::parentWidget()->width()) - x - Base::width()) : x, y);
  38. }
  39. void moveToRight(int x, int y, int outerw = 0) {
  40. auto margins = getMargins();
  41. x -= margins.right();
  42. y -= margins.top();
  43. Base::move(style::RightToLeft() ? x : ((outerw > 0 ? outerw : Base::parentWidget()->width()) - x - Base::width()), y);
  44. }
  45. void setGeometryToLeft(const QRect &r, int outerw = 0) {
  46. setGeometryToLeft(r.x(), r.y(), r.width(), r.height(), outerw);
  47. }
  48. void setGeometryToLeft(int x, int y, int w, int h, int outerw = 0) {
  49. auto margins = getMargins();
  50. x -= margins.left();
  51. y -= margins.top();
  52. w -= margins.left() - margins.right();
  53. h -= margins.top() - margins.bottom();
  54. Base::setGeometry(style::RightToLeft() ? ((outerw > 0 ? outerw : Base::parentWidget()->width()) - x - w) : x, y, w, h);
  55. }
  56. void setGeometryToRight(const QRect &r, int outerw = 0) {
  57. setGeometryToRight(r.x(), r.y(), r.width(), r.height(), outerw);
  58. }
  59. void setGeometryToRight(int x, int y, int w, int h, int outerw = 0) {
  60. auto margins = getMargins();
  61. x -= margins.right();
  62. y -= margins.top();
  63. w -= margins.left() - margins.right();
  64. h -= margins.top() - margins.bottom();
  65. Base::setGeometry(style::RightToLeft() ? x : ((outerw > 0 ? outerw : Base::parentWidget()->width()) - x - w), y, w, h);
  66. }
  67. QPoint myrtlpoint(int x, int y) const {
  68. return style::rtlpoint(x, y, Base::width());
  69. }
  70. QPoint myrtlpoint(const QPoint point) const {
  71. return style::rtlpoint(point, Base::width());
  72. }
  73. QRect myrtlrect(int x, int y, int w, int h) const {
  74. return style::rtlrect(x, y, w, h, Base::width());
  75. }
  76. QRect myrtlrect(const QRect &rect) const {
  77. return style::rtlrect(rect, Base::width());
  78. }
  79. void rtlupdate(const QRect &rect) {
  80. Base::update(myrtlrect(rect));
  81. }
  82. void rtlupdate(int x, int y, int w, int h) {
  83. Base::update(myrtlrect(x, y, w, h));
  84. }
  85. QPoint mapFromGlobal(const QPoint &point) const {
  86. return Base::mapFromGlobal(point);
  87. }
  88. QPoint mapToGlobal(const QPoint &point) const {
  89. return Base::mapToGlobal(point);
  90. }
  91. QRect mapFromGlobal(const QRect &rect) const {
  92. return QRect(mapFromGlobal(rect.topLeft()), rect.size());
  93. }
  94. QRect mapToGlobal(const QRect &rect) const {
  95. return QRect(mapToGlobal(rect.topLeft()), rect.size());
  96. }
  97. protected:
  98. #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
  99. void enterEvent(QEnterEvent *e) final override {
  100. if (auto parent = tparent()) {
  101. parent->leaveToChildEvent(e, this);
  102. }
  103. return enterEventHook(e);
  104. }
  105. #else // Qt >= 6.0.0
  106. void enterEvent(QEvent *e) final override {
  107. if (auto parent = tparent()) {
  108. parent->leaveToChildEvent(e, this);
  109. }
  110. return enterEventHook(static_cast<QEnterEvent*>(e));
  111. }
  112. #endif // Qt < 6.0.0
  113. virtual void enterEventHook(QEnterEvent *e) {
  114. return Base::enterEvent(e);
  115. }
  116. void leaveEvent(QEvent *e) final override {
  117. if (auto parent = tparent()) {
  118. parent->enterFromChildEvent(e, this);
  119. }
  120. return leaveEventHook(e);
  121. }
  122. virtual void leaveEventHook(QEvent *e) {
  123. return Base::leaveEvent(e);
  124. }
  125. // e - from enterEvent() of child TWidget
  126. virtual void leaveToChildEvent(QEvent *e, QWidget *child) {
  127. }
  128. // e - from leaveEvent() of child TWidget
  129. virtual void enterFromChildEvent(QEvent *e, QWidget *child) {
  130. }
  131. private:
  132. TWidget *tparent() {
  133. return qobject_cast<TWidget*>(Base::parentWidget());
  134. }
  135. const TWidget *tparent() const {
  136. return qobject_cast<const TWidget*>(Base::parentWidget());
  137. }
  138. template <typename OtherBase>
  139. friend class TWidgetHelper;
  140. };
  141. class TWidget : public TWidgetHelper<QWidget> {
  142. // The Q_OBJECT meta info is used for qobject_cast!
  143. Q_OBJECT
  144. public:
  145. TWidget(QWidget *parent = nullptr);
  146. // Get the size of the widget as it should be.
  147. // Negative return value means no default width.
  148. virtual int naturalWidth() const {
  149. return -1;
  150. }
  151. // Count new height for width=newWidth and resize to it.
  152. void resizeToWidth(int newWidth) {
  153. auto margins = getMargins();
  154. auto fullWidth = margins.left() + newWidth + margins.right();
  155. auto fullHeight = margins.top() + resizeGetHeight(newWidth) + margins.bottom();
  156. auto newSize = QSize(fullWidth, fullHeight);
  157. if (newSize != size()) {
  158. resize(newSize);
  159. update();
  160. }
  161. }
  162. // Resize to minimum of natural width and available width.
  163. void resizeToNaturalWidth(int newWidth) {
  164. const auto natural = naturalWidth();
  165. resizeToWidth((natural >= 0) ? qMin(newWidth, natural) : newWidth);
  166. }
  167. QRect rectNoMargins() const {
  168. return rect().marginsRemoved(getMargins());
  169. }
  170. int widthNoMargins() const {
  171. return rectNoMargins().width();
  172. }
  173. int heightNoMargins() const {
  174. return rectNoMargins().height();
  175. }
  176. int bottomNoMargins() const {
  177. auto rectWithoutMargins = rectNoMargins();
  178. return y() + rectWithoutMargins.y() + rectWithoutMargins.height();
  179. }
  180. QSize sizeNoMargins() const {
  181. return rectNoMargins().size();
  182. }
  183. // Updates the area that is visible inside the scroll container.
  184. void setVisibleTopBottom(int visibleTop, int visibleBottom) {
  185. const auto max = std::max(height(), 0);
  186. visibleTopBottomUpdated(
  187. std::clamp(visibleTop, 0, max),
  188. std::clamp(visibleBottom, 0, max));
  189. }
  190. protected:
  191. void setChildVisibleTopBottom(
  192. TWidget *child,
  193. int visibleTop,
  194. int visibleBottom) {
  195. if (child) {
  196. auto top = child->y();
  197. child->setVisibleTopBottom(
  198. visibleTop - top,
  199. visibleBottom - top);
  200. }
  201. }
  202. // Resizes content and counts natural widget height for the desired width.
  203. virtual int resizeGetHeight(int newWidth) {
  204. return heightNoMargins();
  205. }
  206. virtual void visibleTopBottomUpdated(
  207. int visibleTop,
  208. int visibleBottom) {
  209. }
  210. };
  211. namespace Ui {
  212. class RpWidget;
  213. void ResizeFitChild(
  214. not_null<RpWidget*> parent,
  215. not_null<RpWidget*> child,
  216. int heightMin = 0);
  217. template <typename Widget>
  218. using RpWidgetParent = std::conditional_t<
  219. std::is_same_v<Widget, QWidget>,
  220. TWidget,
  221. TWidgetHelper<Widget>>;
  222. template <typename Widget, typename Traits>
  223. class RpWidgetBase;
  224. class RpWidgetWrap {
  225. public:
  226. virtual QWidget *rpWidget() = 0;
  227. virtual const QWidget *rpWidget() const = 0;
  228. rpl::producer<not_null<QEvent*>> events() const;
  229. rpl::producer<QRect> geometryValue() const;
  230. rpl::producer<QSize> sizeValue() const;
  231. rpl::producer<int> heightValue() const;
  232. rpl::producer<int> widthValue() const;
  233. rpl::producer<QPoint> positionValue() const;
  234. rpl::producer<int> leftValue() const;
  235. rpl::producer<int> topValue() const;
  236. virtual rpl::producer<int> desiredHeightValue() const;
  237. rpl::producer<bool> shownValue() const;
  238. rpl::producer<not_null<QScreen*>> screenValue() const;
  239. rpl::producer<bool> windowActiveValue() const;
  240. rpl::producer<QRect> paintRequest() const;
  241. rpl::producer<> alive() const;
  242. rpl::producer<> death() const;
  243. rpl::producer<> macWindowDeactivateEvents() const;
  244. rpl::producer<WId> winIdValue() const;
  245. template <typename Error, typename Generator>
  246. void showOn(rpl::producer<bool, Error, Generator> &&shown) {
  247. std::move(
  248. shown
  249. ) | rpl::start_with_next([this](bool visible) {
  250. callSetVisible(visible);
  251. }, lifetime());
  252. }
  253. rpl::lifetime &lifetime();
  254. virtual ~RpWidgetWrap() = default;
  255. protected:
  256. bool handleEvent(QEvent *event);
  257. virtual bool eventHook(QEvent *event) = 0;
  258. private:
  259. template <typename Widget, typename Traits>
  260. friend class RpWidgetBase;
  261. struct EventStreams {
  262. rpl::event_stream<not_null<QEvent*>> events;
  263. rpl::event_stream<QRect> geometry;
  264. rpl::event_stream<QRect> paint;
  265. rpl::event_stream<bool> shown;
  266. rpl::event_stream<not_null<QScreen*>> screen;
  267. rpl::event_stream<bool> windowActive;
  268. rpl::event_stream<WId> winId;
  269. rpl::event_stream<> alive;
  270. };
  271. struct Initer {
  272. Initer(QWidget *parent, bool setZeroGeometry);
  273. };
  274. virtual void callSetVisible(bool visible) = 0;
  275. void visibilityChangedHook(bool wasVisible, bool nowVisible);
  276. EventStreams &eventStreams() const;
  277. mutable std::unique_ptr<EventStreams> _eventStreams;
  278. rpl::lifetime _lifetime;
  279. };
  280. struct RpWidgetDefaultTraits {
  281. static constexpr bool kSetZeroGeometry = true;
  282. };
  283. template <typename Widget, typename Traits = RpWidgetDefaultTraits>
  284. class RpWidgetBase
  285. : public RpWidgetParent<Widget>
  286. , public RpWidgetWrap {
  287. using Self = RpWidgetBase<Widget, Traits>;
  288. using Parent = RpWidgetParent<Widget>;
  289. public:
  290. using Parent::Parent;
  291. QWidget *rpWidget() final override {
  292. return this;
  293. }
  294. const QWidget *rpWidget() const final override {
  295. return this;
  296. }
  297. void setVisible(bool visible) final override {
  298. auto wasVisible = !this->isHidden();
  299. setVisibleHook(visible);
  300. visibilityChangedHook(wasVisible, !this->isHidden());
  301. }
  302. ~RpWidgetBase() {
  303. base::take(_lifetime);
  304. base::take(_eventStreams);
  305. }
  306. protected:
  307. bool event(QEvent *event) final override {
  308. return handleEvent(event);
  309. }
  310. bool eventHook(QEvent *event) override {
  311. return Parent::event(event);
  312. }
  313. virtual void setVisibleHook(bool visible) {
  314. Parent::setVisible(visible);
  315. }
  316. private:
  317. void callSetVisible(bool visible) final override {
  318. Self::setVisible(visible); // Save one virtual method invocation.
  319. }
  320. Initer _initer = { this, Traits::kSetZeroGeometry };
  321. };
  322. class RpWidget : public RpWidgetBase<QWidget> {
  323. public:
  324. using RpWidgetBase<QWidget>::RpWidgetBase;
  325. };
  326. } // namespace Ui