box_content.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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 "base/flags.h"
  10. #include "ui/dragging_scroll_manager.h"
  11. #include "ui/wrap/padding_wrap.h"
  12. #include "ui/widgets/labels.h"
  13. #include "ui/layers/layer_widget.h"
  14. #include "ui/layers/show.h"
  15. #include "ui/effects/animations.h"
  16. #include "ui/effects/animation_value.h"
  17. #include "ui/text/text_entity.h"
  18. #include "ui/rp_widget.h"
  19. enum class RectPart;
  20. using RectParts = base::flags<RectPart>;
  21. namespace base {
  22. class Timer;
  23. } // namespace base
  24. namespace style {
  25. struct RoundButton;
  26. struct IconButton;
  27. struct ScrollArea;
  28. struct Box;
  29. } // namespace style
  30. namespace st {
  31. extern const style::ScrollArea &boxScroll;
  32. } // namespace st
  33. namespace Ui::Toast {
  34. struct Config;
  35. class Instance;
  36. } // namespace Ui::Toast
  37. namespace Ui {
  38. class GenericBox;
  39. } // namespace Ui
  40. template <typename BoxType = Ui::GenericBox, typename ...Args>
  41. inline object_ptr<BoxType> Box(Args &&...args) {
  42. const auto parent = static_cast<QWidget*>(nullptr);
  43. return object_ptr<BoxType>(parent, std::forward<Args>(args)...);
  44. }
  45. namespace Ui {
  46. class AbstractButton;
  47. class RoundButton;
  48. class IconButton;
  49. class ScrollArea;
  50. class FlatLabel;
  51. class FadeShadow;
  52. class BoxContent;
  53. struct ScrollToRequest;
  54. class BoxContentDelegate {
  55. public:
  56. virtual void setLayerType(bool layerType) = 0;
  57. virtual void setStyle(const style::Box &st) = 0;
  58. virtual const style::Box &style() = 0;
  59. virtual void setTitle(rpl::producer<TextWithEntities> title) = 0;
  60. virtual void setAdditionalTitle(rpl::producer<QString> additional) = 0;
  61. virtual void setCloseByOutsideClick(bool close) = 0;
  62. virtual void setCustomCornersFilling(RectParts corners) = 0;
  63. virtual void clearButtons() = 0;
  64. virtual void addButton(object_ptr<AbstractButton> button) = 0;
  65. virtual void addLeftButton(object_ptr<AbstractButton> button) = 0;
  66. virtual void addTopButton(object_ptr<AbstractButton> button) = 0;
  67. virtual void showLoading(bool show) = 0;
  68. virtual void updateButtonsPositions() = 0;
  69. virtual void showBox(
  70. object_ptr<BoxContent> box,
  71. LayerOptions options,
  72. anim::type animated) = 0;
  73. virtual void setDimensions(
  74. int newWidth,
  75. int maxHeight,
  76. bool forceCenterPosition = false) = 0;
  77. virtual void setNoContentMargin(bool noContentMargin) = 0;
  78. virtual bool isBoxShown() const = 0;
  79. virtual void closeBox() = 0;
  80. virtual void hideLayer() = 0;
  81. virtual void triggerButton(int index) = 0;
  82. template <typename BoxType>
  83. QPointer<BoxType> show(
  84. object_ptr<BoxType> content,
  85. LayerOptions options = LayerOption::KeepOther,
  86. anim::type animated = anim::type::normal) {
  87. auto result = QPointer<BoxType>(content.data());
  88. showBox(std::move(content), options, animated);
  89. return result;
  90. }
  91. virtual ShowFactory showFactory() = 0;
  92. virtual QPointer<QWidget> outerContainer() = 0;
  93. };
  94. class BoxContent : public RpWidget {
  95. public:
  96. BoxContent() {
  97. setAttribute(Qt::WA_OpaquePaintEvent);
  98. }
  99. bool isBoxShown() const {
  100. return getDelegate()->isBoxShown();
  101. }
  102. void closeBox() {
  103. getDelegate()->closeBox();
  104. }
  105. void triggerButton(int index) {
  106. getDelegate()->triggerButton(index);
  107. }
  108. void setTitle(rpl::producer<QString> title);
  109. void setTitle(rpl::producer<TextWithEntities> title) {
  110. getDelegate()->setTitle(std::move(title));
  111. }
  112. void setAdditionalTitle(rpl::producer<QString> additional) {
  113. getDelegate()->setAdditionalTitle(std::move(additional));
  114. }
  115. void setCloseByEscape(bool close) {
  116. _closeByEscape = close;
  117. }
  118. void setCloseByOutsideClick(bool close) {
  119. getDelegate()->setCloseByOutsideClick(close);
  120. }
  121. void scrollToWidget(not_null<QWidget*> widget);
  122. virtual void showFinished() {
  123. }
  124. void setCustomCornersFilling(RectParts corners) {
  125. getDelegate()->setCustomCornersFilling(corners);
  126. }
  127. void clearButtons() {
  128. getDelegate()->clearButtons();
  129. }
  130. QPointer<AbstractButton> addButton(object_ptr<AbstractButton> button);
  131. QPointer<RoundButton> addButton(
  132. rpl::producer<QString> text,
  133. Fn<void()> clickCallback = nullptr);
  134. QPointer<RoundButton> addButton(
  135. rpl::producer<QString> text,
  136. const style::RoundButton &st);
  137. QPointer<RoundButton> addButton(
  138. rpl::producer<QString> text,
  139. Fn<void()> clickCallback,
  140. const style::RoundButton &st);
  141. QPointer<AbstractButton> addLeftButton(
  142. object_ptr<AbstractButton> button);
  143. QPointer<RoundButton> addLeftButton(
  144. rpl::producer<QString> text,
  145. Fn<void()> clickCallback = nullptr);
  146. QPointer<RoundButton> addLeftButton(
  147. rpl::producer<QString> text,
  148. Fn<void()> clickCallback,
  149. const style::RoundButton& st);
  150. QPointer<AbstractButton> addTopButton(
  151. object_ptr<AbstractButton> button);
  152. QPointer<IconButton> addTopButton(
  153. const style::IconButton &st,
  154. Fn<void()> clickCallback = nullptr);
  155. void showLoading(bool show) {
  156. getDelegate()->showLoading(show);
  157. }
  158. void updateButtonsGeometry() {
  159. getDelegate()->updateButtonsPositions();
  160. }
  161. void setStyle(const style::Box &st) {
  162. getDelegate()->setStyle(st);
  163. }
  164. virtual void setInnerFocus() {
  165. setFocus();
  166. }
  167. [[nodiscard]] rpl::producer<> boxClosing() const {
  168. return _boxClosingStream.events();
  169. }
  170. void notifyBoxClosing() {
  171. _boxClosingStream.fire({});
  172. }
  173. void setDelegate(not_null<BoxContentDelegate*> newDelegate) {
  174. _delegate = newDelegate;
  175. _preparing = true;
  176. prepare();
  177. finishPrepare();
  178. }
  179. [[nodiscard]] bool hasDelegate() const {
  180. return _delegate != nullptr;
  181. }
  182. [[nodiscard]] not_null<BoxContentDelegate*> getDelegate() const {
  183. return _delegate;
  184. }
  185. void setNoContentMargin(bool noContentMargin) {
  186. if (_noContentMargin != noContentMargin) {
  187. _noContentMargin = noContentMargin;
  188. setAttribute(Qt::WA_OpaquePaintEvent, !_noContentMargin);
  189. }
  190. getDelegate()->setNoContentMargin(noContentMargin);
  191. }
  192. void scrollByDraggingDelta(int delta);
  193. void scrollToY(int top, int bottom = -1);
  194. void scrollTo(
  195. ScrollToRequest request,
  196. anim::type animated = anim::type::instant);
  197. void sendScrollViewportEvent(not_null<QEvent*> event);
  198. [[nodiscard]] rpl::producer<> scrolls() const;
  199. [[nodiscard]] int scrollTop() const;
  200. [[nodiscard]] int scrollHeight() const;
  201. base::weak_ptr<Toast::Instance> showToast(Toast::Config &&config);
  202. base::weak_ptr<Toast::Instance> showToast(
  203. TextWithEntities &&text,
  204. crl::time duration = 0);
  205. base::weak_ptr<Toast::Instance> showToast(
  206. const QString &text,
  207. crl::time duration = 0);
  208. [[nodiscard]] std::shared_ptr<Show> uiShow();
  209. protected:
  210. virtual void prepare() = 0;
  211. void setLayerType(bool layerType) {
  212. getDelegate()->setLayerType(layerType);
  213. }
  214. void setDimensions(
  215. int newWidth,
  216. int maxHeight,
  217. bool forceCenterPosition = false) {
  218. getDelegate()->setDimensions(
  219. newWidth,
  220. maxHeight,
  221. forceCenterPosition);
  222. }
  223. void setDimensionsToContent(
  224. int newWidth,
  225. not_null<RpWidget*> content);
  226. void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false);
  227. void setInnerBottomSkip(int bottomSkip);
  228. template <typename Widget>
  229. QPointer<Widget> setInnerWidget(
  230. object_ptr<Widget> inner,
  231. const style::ScrollArea &st,
  232. int topSkip = 0,
  233. int bottomSkip = 0) {
  234. auto result = QPointer<Widget>(inner.data());
  235. setInnerTopSkip(topSkip);
  236. setInnerBottomSkip(bottomSkip);
  237. setInner(std::move(inner), st);
  238. return result;
  239. }
  240. template <typename Widget>
  241. QPointer<Widget> setInnerWidget(
  242. object_ptr<Widget> inner,
  243. int topSkip = 0,
  244. int bottomSkip = 0) {
  245. return setInnerWidget(
  246. std::move(inner),
  247. st::boxScroll,
  248. topSkip,
  249. bottomSkip);
  250. }
  251. template <typename Widget>
  252. object_ptr<Widget> takeInnerWidget() {
  253. return object_ptr<Widget>::fromRaw(
  254. static_cast<Widget*>(doTakeInnerWidget().release()));
  255. }
  256. void setInnerVisible(bool scrollAreaVisible);
  257. QPixmap grabInnerCache();
  258. void resizeEvent(QResizeEvent *e) override;
  259. void paintEvent(QPaintEvent *e) override;
  260. void keyPressEvent(QKeyEvent *e) override;
  261. private:
  262. void finishPrepare();
  263. void finishScrollCreate();
  264. void setInner(object_ptr<TWidget> inner, const style::ScrollArea &st);
  265. void updateScrollAreaGeometry();
  266. void updateInnerVisibleTopBottom();
  267. void updateShadowsVisibility(anim::type animated = anim::type::normal);
  268. object_ptr<TWidget> doTakeInnerWidget();
  269. BoxContentDelegate *_delegate = nullptr;
  270. bool _preparing = false;
  271. bool _noContentMargin = false;
  272. bool _closeByEscape = true;
  273. int _innerTopSkip = 0;
  274. int _innerBottomSkip = 0;
  275. object_ptr<ScrollArea> _scroll = { nullptr };
  276. object_ptr<FadeShadow> _topShadow = { nullptr };
  277. object_ptr<FadeShadow> _bottomShadow = { nullptr };
  278. Ui::DraggingScrollManager _draggingScroll;
  279. Ui::Animations::Simple _scrollAnimation;
  280. rpl::event_stream<> _boxClosingStream;
  281. };
  282. class BoxPointer {
  283. public:
  284. BoxPointer() = default;
  285. BoxPointer(const BoxPointer &other) = default;
  286. BoxPointer(BoxPointer &&other) : _value(base::take(other._value)) {
  287. }
  288. BoxPointer(BoxContent *value) : _value(value) {
  289. }
  290. BoxPointer &operator=(const BoxPointer &other) {
  291. if (_value != other._value) {
  292. destroy();
  293. _value = other._value;
  294. }
  295. return *this;
  296. }
  297. BoxPointer &operator=(BoxPointer &&other) {
  298. if (_value != other._value) {
  299. destroy();
  300. _value = base::take(other._value);
  301. }
  302. return *this;
  303. }
  304. BoxPointer &operator=(BoxContent *other) {
  305. if (_value != other) {
  306. destroy();
  307. _value = other;
  308. }
  309. return *this;
  310. }
  311. ~BoxPointer() {
  312. destroy();
  313. }
  314. BoxContent *get() const {
  315. return _value.data();
  316. }
  317. operator BoxContent*() const {
  318. return get();
  319. }
  320. explicit operator bool() const {
  321. return get();
  322. }
  323. BoxContent *operator->() const {
  324. return get();
  325. }
  326. private:
  327. void destroy() {
  328. if (const auto value = base::take(_value)) {
  329. value->closeBox();
  330. }
  331. }
  332. QPointer<BoxContent> _value;
  333. };
  334. } // namespace Ui