rp_widget.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. #include "ui/rp_widget.h"
  8. #include "base/qt_signal_producer.h"
  9. #include "ui/gl/gl_detection.h"
  10. #include <QtGui/QWindow>
  11. #include <QtGui/QtEvents>
  12. #include <QtGui/QColorSpace>
  13. #include <QtWidgets/QApplication>
  14. TWidget::TWidget(QWidget *parent)
  15. : TWidgetHelper<QWidget>(parent) {
  16. [[maybe_unused]] static const auto Once = [] {
  17. auto format = QSurfaceFormat::defaultFormat();
  18. format.setSwapInterval(0);
  19. #ifdef DESKTOP_APP_USE_ANGLE
  20. format.setRedBufferSize(8);
  21. format.setGreenBufferSize(8);
  22. format.setBlueBufferSize(8);
  23. #endif // DESKTOP_APP_USE_ANGLE
  24. #ifdef Q_OS_MAC
  25. format.setColorSpace(QColorSpace::SRgb);
  26. #endif // Q_OS_MAC
  27. QSurfaceFormat::setDefaultFormat(format);
  28. return true;
  29. }();
  30. }
  31. namespace Ui {
  32. namespace {
  33. [[nodiscard]] std::vector<QPointer<QWidget>> GetChildWidgets(
  34. not_null<QWidget*> widget) {
  35. const auto &children = widget->children();
  36. auto result = std::vector<QPointer<QWidget>>();
  37. result.reserve(children.size());
  38. for (const auto child : children) {
  39. if (child && child->isWidgetType()) {
  40. result.push_back(static_cast<QWidget*>(child));
  41. }
  42. }
  43. return result;
  44. }
  45. } // namespace
  46. void ToggleChildrenVisibility(not_null<QWidget*> widget, bool visible) {
  47. for (const auto &child : GetChildWidgets(widget)) {
  48. if (child) {
  49. child->setVisible(visible);
  50. }
  51. }
  52. }
  53. void ResizeFitChild(
  54. not_null<RpWidget*> parent,
  55. not_null<RpWidget*> child,
  56. int heightMin) {
  57. parent->widthValue(
  58. ) | rpl::start_with_next([=](int width) {
  59. child->resizeToWidth(width);
  60. }, child->lifetime());
  61. child->heightValue(
  62. ) | rpl::start_with_next([=](int height) {
  63. parent->resize(parent->width(), std::max(height, heightMin));
  64. }, child->lifetime());
  65. }
  66. rpl::producer<not_null<QEvent*>> RpWidgetWrap::events() const {
  67. auto &stream = eventStreams().events;
  68. return stream.events();
  69. }
  70. rpl::producer<QRect> RpWidgetWrap::geometryValue() const {
  71. auto &stream = eventStreams().geometry;
  72. return stream.events_starting_with_copy(rpWidget()->geometry());
  73. }
  74. rpl::producer<QSize> RpWidgetWrap::sizeValue() const {
  75. return geometryValue()
  76. | rpl::map([](QRect &&value) { return value.size(); })
  77. | rpl::distinct_until_changed();
  78. }
  79. rpl::producer<int> RpWidgetWrap::heightValue() const {
  80. return geometryValue()
  81. | rpl::map([](QRect &&value) { return value.height(); })
  82. | rpl::distinct_until_changed();
  83. }
  84. rpl::producer<int> RpWidgetWrap::widthValue() const {
  85. return geometryValue()
  86. | rpl::map([](QRect &&value) { return value.width(); })
  87. | rpl::distinct_until_changed();
  88. }
  89. rpl::producer<QPoint> RpWidgetWrap::positionValue() const {
  90. return geometryValue()
  91. | rpl::map([](QRect &&value) { return value.topLeft(); })
  92. | rpl::distinct_until_changed();
  93. }
  94. rpl::producer<int> RpWidgetWrap::leftValue() const {
  95. return geometryValue()
  96. | rpl::map([](QRect &&value) { return value.left(); })
  97. | rpl::distinct_until_changed();
  98. }
  99. rpl::producer<int> RpWidgetWrap::topValue() const {
  100. return geometryValue()
  101. | rpl::map([](QRect &&value) { return value.top(); })
  102. | rpl::distinct_until_changed();
  103. }
  104. rpl::producer<int> RpWidgetWrap::desiredHeightValue() const {
  105. return heightValue();
  106. }
  107. rpl::producer<bool> RpWidgetWrap::shownValue() const {
  108. auto &stream = eventStreams().shown;
  109. return stream.events_starting_with(!rpWidget()->isHidden())
  110. | rpl::distinct_until_changed();
  111. }
  112. rpl::producer<not_null<QScreen*>> RpWidgetWrap::screenValue() const {
  113. auto &stream = eventStreams().screen;
  114. return stream.events_starting_with(rpWidget()->screen());
  115. }
  116. rpl::producer<bool> RpWidgetWrap::windowActiveValue() const {
  117. auto &stream = eventStreams().windowActive;
  118. return stream.events_starting_with(
  119. QApplication::activeWindow() == rpWidget()->window()
  120. ) | rpl::distinct_until_changed();
  121. }
  122. rpl::producer<QRect> RpWidgetWrap::paintRequest() const {
  123. return eventStreams().paint.events();
  124. }
  125. rpl::producer<> RpWidgetWrap::alive() const {
  126. return eventStreams().alive.events();
  127. }
  128. rpl::producer<> RpWidgetWrap::death() const {
  129. return alive() | rpl::then(rpl::single(rpl::empty));
  130. }
  131. rpl::producer<> RpWidgetWrap::macWindowDeactivateEvents() const {
  132. #ifdef Q_OS_MAC
  133. return windowActiveValue()
  134. | rpl::skip(1)
  135. | rpl::filter(!rpl::mappers::_1)
  136. | rpl::to_empty;
  137. #else // Q_OS_MAC
  138. return rpl::never<rpl::empty_value>();
  139. #endif // Q_OS_MAC
  140. }
  141. rpl::producer<WId> RpWidgetWrap::winIdValue() const {
  142. auto &stream = eventStreams().winId;
  143. return stream.events_starting_with(rpWidget()->internalWinId());
  144. }
  145. rpl::lifetime &RpWidgetWrap::lifetime() {
  146. return _lifetime;
  147. }
  148. bool RpWidgetWrap::handleEvent(QEvent *event) {
  149. Expects(event != nullptr);
  150. auto streams = _eventStreams.get();
  151. if (!streams) {
  152. return eventHook(event);
  153. }
  154. auto that = QPointer<QWidget>();
  155. const auto allAreObserved = streams->events.has_consumers();
  156. if (allAreObserved) {
  157. that = rpWidget();
  158. streams->events.fire_copy(event);
  159. if (!that) {
  160. return true;
  161. }
  162. }
  163. switch (event->type()) {
  164. case QEvent::Show:
  165. case QEvent::Hide:
  166. if (rpWidget()->isWindow() && streams->shown.has_consumers()) {
  167. if (!allAreObserved) {
  168. that = rpWidget();
  169. }
  170. streams->shown.fire_copy(!rpWidget()->isHidden());
  171. if (!that) {
  172. return true;
  173. }
  174. }
  175. break;
  176. case QEvent::WindowActivate:
  177. case QEvent::WindowDeactivate:
  178. if (streams->windowActive.has_consumers()) {
  179. if (!allAreObserved) {
  180. that = rpWidget();
  181. }
  182. streams->windowActive.fire_copy(
  183. QApplication::activeWindow() == rpWidget()->window());
  184. if (!that) {
  185. return true;
  186. }
  187. }
  188. break;
  189. case QEvent::Move:
  190. case QEvent::Resize:
  191. if (streams->geometry.has_consumers()) {
  192. if (!allAreObserved) {
  193. that = rpWidget();
  194. }
  195. streams->geometry.fire_copy(rpWidget()->geometry());
  196. if (!that) {
  197. return true;
  198. }
  199. }
  200. break;
  201. case QEvent::ScreenChangeInternal:
  202. if (streams->screen.has_consumers()) {
  203. if (!allAreObserved) {
  204. that = rpWidget();
  205. }
  206. streams->screen.fire_copy(rpWidget()->screen());
  207. if (!that) {
  208. return true;
  209. }
  210. }
  211. break;
  212. case QEvent::Paint:
  213. if (streams->paint.has_consumers()) {
  214. if (!allAreObserved) {
  215. that = rpWidget();
  216. }
  217. const auto rect = static_cast<QPaintEvent*>(event)->rect();
  218. streams->paint.fire_copy(rect);
  219. if (!that) {
  220. return true;
  221. }
  222. }
  223. break;
  224. case QEvent::WinIdChange:
  225. if (streams->winId.has_consumers()) {
  226. if (!allAreObserved) {
  227. that = rpWidget();
  228. }
  229. streams->winId.fire_copy(rpWidget()->internalWinId());
  230. if (!that) {
  231. return true;
  232. }
  233. }
  234. }
  235. return eventHook(event);
  236. }
  237. RpWidgetWrap::Initer::Initer(QWidget *parent, bool setZeroGeometry) {
  238. if (setZeroGeometry) {
  239. parent->setGeometry(0, 0, 0, 0);
  240. }
  241. }
  242. void RpWidgetWrap::visibilityChangedHook(bool wasVisible, bool nowVisible) {
  243. if (nowVisible != wasVisible) {
  244. if (auto streams = _eventStreams.get()) {
  245. streams->shown.fire_copy(nowVisible);
  246. }
  247. }
  248. }
  249. auto RpWidgetWrap::eventStreams() const -> EventStreams& {
  250. if (!_eventStreams) {
  251. _eventStreams = std::make_unique<EventStreams>();
  252. }
  253. return *_eventStreams;
  254. }
  255. } // namespace Ui