vertical_layout.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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/wrap/vertical_layout.h"
  8. #include "ui/ui_utility.h"
  9. namespace Ui {
  10. QMargins VerticalLayout::getMargins() const {
  11. auto result = QMargins();
  12. if (!_rows.empty()) {
  13. auto &top = _rows.front();
  14. auto topMargin = top.widget->getMargins().top();
  15. result.setTop(
  16. qMax(topMargin - top.margin.top(), 0));
  17. auto &bottom = _rows.back();
  18. auto bottomMargin = bottom.widget->getMargins().bottom();
  19. result.setBottom(
  20. qMax(bottomMargin - bottom.margin.bottom(), 0));
  21. for (auto &row : _rows) {
  22. auto margins = row.widget->getMargins();
  23. result.setLeft(qMax(
  24. margins.left() - row.margin.left(),
  25. result.left()));
  26. result.setRight(qMax(
  27. margins.right() - row.margin.right(),
  28. result.right()));
  29. }
  30. }
  31. return result;
  32. }
  33. int VerticalLayout::naturalWidth() const {
  34. auto result = 0;
  35. for (auto &row : _rows) {
  36. const auto natural = row.widget->naturalWidth();
  37. if (natural < 0) {
  38. return natural;
  39. }
  40. accumulate_max(
  41. result,
  42. row.margin.left() + natural + row.margin.right());
  43. }
  44. return result;
  45. }
  46. void VerticalLayout::setVerticalShift(int index, int shift) {
  47. Expects(index >= 0 && index < _rows.size());
  48. auto &row = _rows[index];
  49. if (const auto delta = shift - row.verticalShift) {
  50. row.verticalShift = shift;
  51. row.widget->move(row.widget->x(), row.widget->y() + delta);
  52. row.widget->update();
  53. }
  54. }
  55. void VerticalLayout::reorderRows(int oldIndex, int newIndex) {
  56. Expects(oldIndex >= 0 && oldIndex < _rows.size());
  57. Expects(newIndex >= 0 && newIndex < _rows.size());
  58. Expects(!_inResize);
  59. base::reorder(_rows, oldIndex, newIndex);
  60. resizeToWidth(width());
  61. }
  62. int VerticalLayout::resizeGetHeight(int newWidth) {
  63. _inResize = true;
  64. auto guard = gsl::finally([&] { _inResize = false; });
  65. auto margins = getMargins();
  66. auto result = 0;
  67. for (auto &row : _rows) {
  68. updateChildGeometry(
  69. margins,
  70. row.widget,
  71. row.margin,
  72. newWidth,
  73. result + row.verticalShift);
  74. result += row.margin.top()
  75. + row.widget->heightNoMargins()
  76. + row.margin.bottom();
  77. }
  78. return result;
  79. }
  80. void VerticalLayout::visibleTopBottomUpdated(
  81. int visibleTop,
  82. int visibleBottom) {
  83. for (auto &row : _rows) {
  84. setChildVisibleTopBottom(
  85. row.widget,
  86. visibleTop,
  87. visibleBottom);
  88. }
  89. }
  90. void VerticalLayout::updateChildGeometry(
  91. const style::margins &margins,
  92. RpWidget *child,
  93. const style::margins &margin,
  94. int width,
  95. int top) const {
  96. auto availRowWidth = width
  97. - margin.left()
  98. - margin.right();
  99. child->resizeToNaturalWidth(availRowWidth);
  100. child->moveToLeft(
  101. margins.left() + margin.left(),
  102. margins.top() + margin.top() + top,
  103. width);
  104. }
  105. RpWidget *VerticalLayout::insertChild(
  106. int atPosition,
  107. object_ptr<RpWidget> child,
  108. const style::margins &margin) {
  109. Expects(atPosition >= 0 && atPosition <= _rows.size());
  110. Expects(!_inResize);
  111. if (const auto weak = AttachParentChild(this, child)) {
  112. _rows.insert(
  113. begin(_rows) + atPosition,
  114. { std::move(child), margin });
  115. auto availRowWidth = widthNoMargins()
  116. - margin.left()
  117. - margin.right();
  118. if (availRowWidth > 0) {
  119. weak->resizeToNaturalWidth(availRowWidth);
  120. }
  121. weak->heightValue(
  122. ) | rpl::start_with_next_done([=] {
  123. if (!_inResize) {
  124. childHeightUpdated(weak);
  125. }
  126. }, [=] {
  127. removeChild(weak);
  128. }, _rowsLifetime);
  129. return weak;
  130. }
  131. return nullptr;
  132. }
  133. void VerticalLayout::childHeightUpdated(RpWidget *child) {
  134. auto it = ranges::find_if(_rows, [child](const Row &row) {
  135. return (row.widget == child);
  136. });
  137. auto margins = getMargins();
  138. auto top = [&] {
  139. if (it == _rows.begin()) {
  140. return margins.top();
  141. }
  142. auto prev = it - 1;
  143. return prev->widget->bottomNoMargins() + prev->margin.bottom();
  144. }() - margins.top();
  145. for (auto end = _rows.end(); it != end; ++it) {
  146. auto &row = *it;
  147. auto margin = row.margin;
  148. auto widget = row.widget.data();
  149. widget->moveToLeft(
  150. margins.left() + margin.left(),
  151. margins.top() + top + margin.top());
  152. top += margin.top()
  153. + widget->heightNoMargins()
  154. + margin.bottom();
  155. }
  156. resize(width(), margins.top() + top + margins.bottom());
  157. }
  158. void VerticalLayout::removeChild(RpWidget *child) {
  159. auto it = ranges::find_if(_rows, [child](const Row &row) {
  160. return (row.widget == child);
  161. });
  162. auto end = _rows.end();
  163. Assert(it != end);
  164. auto margins = getMargins();
  165. auto top = [&] {
  166. if (it == _rows.begin()) {
  167. return margins.top();
  168. }
  169. auto prev = it - 1;
  170. return prev->widget->bottomNoMargins() + prev->margin.bottom();
  171. }() - margins.top();
  172. for (auto next = it + 1; next != end; ++next) {
  173. auto &row = *next;
  174. auto margin = row.margin;
  175. auto widget = row.widget.data();
  176. widget->moveToLeft(
  177. margins.left() + margin.left(),
  178. margins.top() + top + margin.top());
  179. top += margin.top()
  180. + widget->heightNoMargins()
  181. + margin.bottom();
  182. }
  183. it->widget = nullptr;
  184. _rows.erase(it);
  185. resize(width(), margins.top() + top + margins.bottom());
  186. }
  187. void VerticalLayout::clear() {
  188. while (!_rows.empty()) {
  189. removeChild(_rows.front().widget.data());
  190. }
  191. }
  192. } // namespace Ui