PreviewProgramSizeObserver.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /******************************************************************************
  2. Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "PreviewProgramSizeObserver.hpp"
  15. #include <QEvent>
  16. #include <QLayout>
  17. #include <QResizeEvent>
  18. #include <QTimer>
  19. #include <util/base.h>
  20. PreviewProgramSizeObserver::PreviewProgramSizeObserver(QWidget *widgetLeft, QWidget *widgetRight, QObject *parent)
  21. : QObject(parent),
  22. left(widgetLeft),
  23. right(widgetRight)
  24. {
  25. if (!left || !right) {
  26. return;
  27. }
  28. std::pair<QWidget *, QWidget *> siblingParents = findSiblingParents(left, right);
  29. leftContainer = siblingParents.first;
  30. rightContainer = siblingParents.second;
  31. leftOriginalMaxSize = leftContainer->maximumSize();
  32. rightOriginalMaxSize = rightContainer->maximumSize();
  33. QWidget *sharedParent = leftContainer->parentWidget();
  34. if (!sharedParent) {
  35. return;
  36. }
  37. ancestorContainer = sharedParent;
  38. QLayout *ancestorLayout = ancestorContainer->layout();
  39. if (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(ancestorLayout)) {
  40. setOrientation((boxLayout->direction() == QBoxLayout::LeftToRight) ? Qt::Horizontal : Qt::Vertical);
  41. }
  42. leftTargetSize = left->width();
  43. rightTargetSize = right->width();
  44. ancestorContainer->installEventFilter(this);
  45. connect(ancestorContainer, &QWidget::destroyed, this, &QWidget::deleteLater);
  46. connect(left, &QWidget::destroyed, this, &QWidget::deleteLater);
  47. connect(right, &QWidget::destroyed, this, &QWidget::deleteLater);
  48. connect(leftContainer, &QWidget::destroyed, this, &QWidget::deleteLater);
  49. connect(rightContainer, &QWidget::destroyed, this, &QWidget::deleteLater);
  50. }
  51. PreviewProgramSizeObserver::~PreviewProgramSizeObserver()
  52. {
  53. if (ancestorContainer) {
  54. ancestorContainer->removeEventFilter(this);
  55. }
  56. if (leftContainer) {
  57. leftContainer->setMaximumSize(leftOriginalMaxSize);
  58. }
  59. if (rightContainer) {
  60. rightContainer->setMaximumSize(rightOriginalMaxSize);
  61. }
  62. }
  63. void PreviewProgramSizeObserver::setOrientation(Qt::Orientation orientation_)
  64. {
  65. orientation = orientation_;
  66. }
  67. std::pair<QWidget *, QWidget *> PreviewProgramSizeObserver::findSiblingParents(QWidget *a, QWidget *b)
  68. {
  69. // Search through ancestors of two widgets to find the topmost pair that are siblings
  70. QWidget *ancestor1 = a;
  71. QWidget *ancestor2 = b;
  72. while (ancestor1 && ancestor2) {
  73. QWidget *parent1 = ancestor1->parentWidget();
  74. QWidget *parent2 = ancestor2->parentWidget();
  75. if (!parent1 || !parent2) {
  76. break;
  77. }
  78. // Found sibling containers
  79. if (parent1 == parent2) {
  80. return {ancestor1, ancestor2};
  81. }
  82. if (parent1->isAncestorOf(parent2)) {
  83. ancestor2 = parent2;
  84. } else if (parent2->isAncestorOf(parent1)) {
  85. ancestor1 = parent1;
  86. } else {
  87. ancestor1 = parent1;
  88. ancestor2 = parent2;
  89. }
  90. }
  91. return {a, b};
  92. }
  93. void PreviewProgramSizeObserver::syncContainerSizes(int containerSizeDelta)
  94. {
  95. auto setMax = (orientation == Qt::Horizontal) ? [](QWidget* widget, int value) {
  96. widget->setMaximumWidth(value);
  97. } : [](QWidget* widget, int value) {
  98. widget->setMaximumHeight(value);
  99. };
  100. QLayout *ancestorLayout = ancestorContainer->layout();
  101. if (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(ancestorLayout)) {
  102. setOrientation((boxLayout->direction() == QBoxLayout::LeftToRight) ? Qt::Horizontal : Qt::Vertical);
  103. }
  104. if (orientation == Qt::Horizontal) {
  105. leftContainer->setMaximumHeight(leftOriginalMaxSize.height());
  106. rightContainer->setMaximumHeight(rightOriginalMaxSize.height());
  107. } else {
  108. leftContainer->setMaximumWidth(leftOriginalMaxSize.width());
  109. rightContainer->setMaximumWidth(rightOriginalMaxSize.width());
  110. }
  111. int leftInner = (orientation == Qt::Horizontal) ? left->width() : left->height();
  112. int rightInner = (orientation == Qt::Horizontal) ? right->width() : right->height();
  113. int leftOuter = (orientation == Qt::Horizontal) ? leftContainer->width() : leftContainer->height();
  114. int rightOuter = (orientation == Qt::Horizontal) ? rightContainer->width() : rightContainer->height();
  115. int totalOuter = leftOuter + rightOuter;
  116. if (containerSizeDelta >= 0) {
  117. totalOuter += containerSizeDelta;
  118. }
  119. int leftOffset = leftOuter - leftInner;
  120. int rightOffset = rightOuter - rightInner;
  121. int targetInner = (totalOuter - leftOffset - rightOffset) / 2;
  122. leftTargetSize = targetInner + leftOffset;
  123. rightTargetSize = targetInner + rightOffset;
  124. if (containerSizeDelta >= 0) {
  125. setMax(leftContainer, leftTargetSize);
  126. setMax(rightContainer, rightTargetSize);
  127. } else {
  128. // Container shrunk, only set max size on larger widget
  129. if (leftInner > rightInner) {
  130. setMax(leftContainer, leftTargetSize);
  131. setMax(rightContainer, QWIDGETSIZE_MAX);
  132. } else {
  133. setMax(leftContainer, QWIDGETSIZE_MAX);
  134. setMax(rightContainer, rightTargetSize);
  135. }
  136. // Force a second recalculation
  137. QTimer::singleShot(1, this, [&, setMax]() {
  138. if (updating) {
  139. return;
  140. }
  141. updating = true;
  142. setMax(leftContainer, leftTargetSize);
  143. setMax(rightContainer, rightTargetSize);
  144. updating = false;
  145. });
  146. }
  147. }
  148. bool PreviewProgramSizeObserver::eventFilter(QObject *target, QEvent *event)
  149. {
  150. if (event->type() != QEvent::Resize && event->type() != QEvent::LayoutRequest) {
  151. return QObject::eventFilter(target, event);
  152. }
  153. if (!left || !right || !leftContainer || !rightContainer) {
  154. deleteLater();
  155. return QObject::eventFilter(target, event);
  156. }
  157. if (updating == true) {
  158. return QObject::eventFilter(target, event);
  159. }
  160. updating = true;
  161. if (event->type() == QEvent::LayoutRequest) {
  162. syncContainerSizes(0);
  163. } else if (event->type() == QEvent::Resize) {
  164. QResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);
  165. int newSize = (orientation == Qt::Horizontal) ? resizeEvent->size().width()
  166. : resizeEvent->size().height();
  167. int oldSize = (orientation == Qt::Horizontal) ? resizeEvent->oldSize().width()
  168. : resizeEvent->oldSize().height();
  169. if (newSize - oldSize != 0) {
  170. syncContainerSizes(newSize - oldSize);
  171. }
  172. }
  173. updating = false;
  174. return QObject::eventFilter(target, event);
  175. }