Files
tdesktop/Telegram/lib_ui/ui/wrap/vertical_layout.cpp
allhaileris afb81b8278
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
init
2026-02-16 15:50:16 +03:00

282 lines
7.0 KiB
C++

// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/wrap/vertical_layout.h"
#include "ui/ui_utility.h"
namespace Ui {
QMargins VerticalLayout::getMargins() const {
auto result = QMargins();
if (!_rows.empty()) {
auto &top = _rows.front();
auto topMargin = top.widget->getMargins().top();
result.setTop(
qMax(topMargin - top.margin.top(), 0));
auto &bottom = _rows.back();
auto bottomMargin = bottom.widget->getMargins().bottom();
result.setBottom(
qMax(bottomMargin - bottom.margin.bottom(), 0));
for (auto &row : _rows) {
auto margins = row.widget->getMargins();
result.setLeft(qMax(
margins.left() - row.margin.left(),
result.left()));
result.setRight(qMax(
margins.right() - row.margin.right(),
result.right()));
}
}
return result;
}
void VerticalLayout::setVerticalShift(int index, int shift) {
Expects(index >= 0 && index < _rows.size());
auto &row = _rows[index];
if (const auto delta = shift - row.verticalShift) {
row.verticalShift = shift;
row.widget->move(row.widget->x(), row.widget->y() + delta);
row.widget->update();
}
}
void VerticalLayout::reorderRows(int oldIndex, int newIndex) {
Expects(oldIndex >= 0 && oldIndex < _rows.size());
Expects(newIndex >= 0 && newIndex < _rows.size());
Expects(!_inResize);
base::reorder(_rows, oldIndex, newIndex);
resizeToWidth(width());
}
int VerticalLayout::resizeGetHeight(int newWidth) {
if (newWidth <= 0) {
return 0;
}
_inResize = true;
auto guard = gsl::finally([&] { _inResize = false; });
const auto margins = getMargins();
const auto outerWidth = margins.left() + newWidth + margins.right();
auto result = margins.top();
for (auto &row : _rows) {
const auto widget = row.widget.data();
const auto &margin = row.margin;
const auto available = newWidth - margin.left() - margin.right();
if (available > 0) {
if (row.align == kAlignJustify) {
widget->resizeToWidth(available);
} else {
widget->resizeToNaturalWidth(available);
}
}
result += moveChildGetSkip(row, result, outerWidth, margins);
}
return result - margins.top();
}
void VerticalLayout::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
for (auto &row : _rows) {
setChildVisibleTopBottom(
row.widget,
visibleTop,
visibleBottom);
}
}
int VerticalLayout::moveChildGetSkip(
const Row &row,
int top,
int outerWidth,
const style::margins &margins) const {
const auto align = row.align;
const auto widget = row.widget.data();
const auto wmargins = widget->getMargins();
const auto &margin = row.margin;
const auto full = margins + margin;
top += margin.top() + row.verticalShift;
if (align == kAlignLeft || align == kAlignJustify) {
widget->moveToLeft(full.left(), top, outerWidth);
} else if (align == kAlignCenter) {
const auto available = outerWidth - full.left() - full.right();
const auto free = available
- widget->width()
+ wmargins.left()
+ wmargins.right();
widget->moveToLeft(full.left() + (free / 2), top, outerWidth);
} else if (align == kAlignRight) {
widget->moveToRight(full.right(), top, outerWidth);
}
return margin.top()
- wmargins.top()
+ widget->height()
- wmargins.bottom()
+ margin.bottom();
}
RpWidget *VerticalLayout::insertChild(
int atPosition,
object_ptr<RpWidget> child,
const style::margins &margin,
style::align align) {
Expects(atPosition >= 0 && atPosition <= _rows.size());
Expects(!_inResize);
if (const auto weak = AttachParentChild(this, child)) {
const auto converted = (align == style::al_justify)
? kAlignJustify
: (align & style::al_left)
? kAlignLeft
: (align & style::al_right)
? kAlignRight
: kAlignCenter;
_rows.insert(
begin(_rows) + atPosition,
{ std::move(child), margin, 0, converted });
if (converted != kAlignJustify) {
subscribeToWidth(weak, margin);
}
weak->heightValue(
) | rpl::on_next_done([=] {
if (!_inResize) {
childHeightUpdated(weak);
}
}, [=] {
removeChild(weak);
}, _rowsLifetime);
return weak;
}
return nullptr;
}
void VerticalLayout::subscribeToWidth(
not_null<RpWidget*> child,
const style::margins &margin) {
child->naturalWidthValue(
) | rpl::on_next([=](int naturalWidth) {
setNaturalWidth([&] {
if (naturalWidth < 0) {
return -1;
}
auto result = -1;
for (const auto &row : _rows) {
if (row.align == kAlignJustify) {
return -1;
}
const auto natural = row.widget->naturalWidth();
if (natural < 0) {
return -1;
}
accumulate_max(
result,
row.margin.left() + natural + row.margin.right());
}
return result;
}());
const auto available = widthNoMargins()
- margin.left()
- margin.right();
if (available > 0) {
child->resizeToWidth((naturalWidth >= 0)
? std::min(naturalWidth, available)
: available);
}
}, _rowsLifetime);
const auto taken = std::exchange(_inResize, true);
child->widthValue(
) | rpl::on_next([=] {
if (!_inResize) {
childWidthUpdated(child);
}
}, _rowsLifetime);
_inResize = taken;
}
void VerticalLayout::childWidthUpdated(RpWidget *child) {
const auto it = ranges::find_if(_rows, [child](const Row &row) {
return (row.widget == child);
});
const auto &row = *it;
const auto margins = getMargins();
const auto top = child->y()
+ child->getMargins().top()
- row.margin.top()
- row.verticalShift;
moveChildGetSkip(row, top, width(), margins);
}
void VerticalLayout::childHeightUpdated(RpWidget *child) {
auto it = ranges::find_if(_rows, [child](const Row &row) {
return (row.widget == child);
});
const auto width = this->width();
const auto margins = getMargins();
auto top = [&] {
if (it == _rows.begin()) {
return margins.top();
}
auto prev = it - 1;
const auto widget = prev->widget.data();
return widget->y()
+ widget->height()
- widget->getMargins().bottom()
+ prev->margin.bottom();
}();
for (auto end = _rows.end(); it != end; ++it) {
const auto &row = *it;
top += moveChildGetSkip(row, top, width, margins);
}
resize(width, top + margins.bottom());
}
void VerticalLayout::removeChild(RpWidget *child) {
auto it = ranges::find_if(_rows, [child](const Row &row) {
return (row.widget == child);
});
auto end = _rows.end();
Assert(it != end);
const auto width = this->width();
const auto margins = getMargins();
auto top = [&] {
if (it == _rows.begin()) {
return margins.top();
}
auto prev = it - 1;
const auto widget = prev->widget.data();
return widget->y()
+ widget->height()
- widget->getMargins().bottom()
+ prev->margin.bottom();
}();
for (auto next = it + 1; next != end; ++next) {
const auto &row = *next;
top += moveChildGetSkip(row, top, width, margins);
}
it->widget = nullptr;
_rows.erase(it);
resize(width, top + margins.bottom());
}
void VerticalLayout::clear() {
while (!_rows.empty()) {
removeChild(_rows.front().widget.data());
}
}
} // namespace Ui