Files
tdesktop/Telegram/lib_ui/ui/wrap/table_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

314 lines
7.4 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/table_layout.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
#include "styles/style_widgets.h"
#include <QtGui/QPainterPath>
namespace Ui {
TableLayout::TableLayout(QWidget *parent, const style::Table &st)
: RpWidget(parent)
, _st(st) {
}
TableLayout::~TableLayout() {
_rowsLifetime.destroy();
auto taken = std::move(_rows);
for (auto &row : taken) {
row.label.destroy();
row.value.destroy();
}
}
void TableLayout::paintEvent(QPaintEvent *e) {
if (_rows.empty()) {
return;
}
auto p = QPainter(this);
auto hq = PainterHighQualityEnabler(p);
const auto half = _st.border / 2.;
const auto inner = QRectF(rect()).marginsRemoved(
{ half, half, half, half });
auto labels = QRegion();
auto yfrom = half;
auto ytill = height() - half;
for (auto i = 0, count = int(_rows.size()); i != count; ++i) {
const auto &row = _rows[i];
yfrom = row.top + half;
if (!row.value) {
const auto till = (i + 1 == count)
? (inner.y() + inner.height())
: (_rows[i + 1].top - half);
labels += QRect(inner.x(), yfrom, inner.width(), till - yfrom);
} else if (row.label) {
break;
}
}
for (auto i = 0, count = int(_rows.size()); i != count; ++i) {
const auto index = count - i - 1;
const auto &row = _rows[index];
if (!row.value) {
const auto from = row.top + half;
labels += QRect(inner.x(), from, inner.width(), ytill - from);
} else if (row.label) {
break;
}
ytill = row.top - half;
}
if (ytill > yfrom) {
labels += QRect(0, yfrom, _valueLeft, ytill - yfrom);
}
if (!labels.isEmpty()) {
p.setClipRegion(labels);
p.setBrush(_st.headerBg);
p.setPen(Qt::NoPen);
p.drawRoundedRect(inner, _st.radius, _st.radius);
p.setClipping(false);
}
auto path = QPainterPath();
path.addRoundedRect(inner, _st.radius, _st.radius);
for (auto i = 1, count = int(_rows.size()); i != count; ++i) {
const auto y = _rows[i].top - half;
path.moveTo(half, y);
path.lineTo(width() - half, y);
}
if (ytill > yfrom) {
path.moveTo(_valueLeft - half, yfrom);
path.lineTo(_valueLeft - half, ytill);
}
auto pen = _st.borderFg->p;
pen.setWidth(_st.border);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawPath(path);
}
int TableLayout::resizeGetHeight(int newWidth) {
_inResize = true;
auto guard = gsl::finally([&] { _inResize = false; });
auto available = newWidth - 3 * _st.border;
const auto labelMax = int(base::SafeRound(
_st.labelMaxWidth * available));
const auto valueMin = available - labelMax;
if (labelMax <= 0 || valueMin <= 0 || _rows.empty()) {
return 0;
}
auto label = _st.labelMinWidth;
for (auto &row : _rows) {
const auto natural = (row.label && row.value)
? (row.label->naturalWidth()
+ row.labelMargin.left()
+ row.labelMargin.right())
: 0;
if (natural < 0 || natural >= labelMax) {
label = labelMax;
break;
} else if (natural > label) {
label = natural;
}
}
_valueLeft = _st.border * 2 + label;
auto result = _st.border;
for (auto &row : _rows) {
updateRowGeometry(row, newWidth, result);
result += rowVerticalSkip(row);
}
return result;
}
void TableLayout::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
for (auto &row : _rows) {
if (row.label) {
setChildVisibleTopBottom(
row.label,
visibleTop,
visibleBottom);
}
if (row.value) {
setChildVisibleTopBottom(
row.value,
visibleTop,
visibleBottom);
}
}
}
void TableLayout::updateRowGeometry(
const Row &row,
int width,
int top) const {
if (row.label && row.value) {
row.label->resizeToNaturalWidth(_valueLeft
- 2 * _st.border
- row.labelMargin.left()
- row.labelMargin.right());
row.value->resizeToNaturalWidth(width
- _valueLeft
- _st.border
- row.valueMargin.left()
- row.valueMargin.right());
} else if (row.label) {
row.label->resizeToNaturalWidth(width
- 2 * _st.border
- row.labelMargin.left()
- row.valueMargin.right());
} else {
row.value->resizeToNaturalWidth(width
- 2 * _st.border
- row.labelMargin.left()
- row.valueMargin.right());
}
updateRowPosition(row, width, top);
}
void TableLayout::updateRowPosition(
const Row &row,
int width,
int top) const {
row.top = top;
if (row.label && row.value) {
row.label->moveToLeft(
_st.border + row.labelMargin.left(),
top + row.labelMargin.top(),
width);
row.value->moveToLeft(
_valueLeft + row.valueMargin.left(),
top + row.valueMargin.top(),
width);
} else if (row.label) {
row.label->moveToLeft(
_st.border + row.labelMargin.left(),
top + row.valueMargin.top(),
width);
} else {
row.value->moveToLeft(
_st.border + row.labelMargin.left(),
top + row.valueMargin.top(),
width);
}
}
void TableLayout::insertRow(
int atPosition,
object_ptr<RpWidget> &&label,
object_ptr<RpWidget> &&value,
const style::margins &labelMargin,
const style::margins &valueMargin) {
Expects(atPosition >= 0 && atPosition <= _rows.size());
Expects(!_inResize);
const auto wlabel = label ? AttachParentChild(this, label) : nullptr;
const auto wvalue = value ? AttachParentChild(this, value) : nullptr;
if (wlabel || wvalue) {
_rows.insert(begin(_rows) + atPosition, {
std::move(label),
std::move(value),
labelMargin,
valueMargin,
});
if (wlabel) {
wlabel->heightValue(
) | rpl::on_next_done([=] {
if (!_inResize) {
childHeightUpdated(wlabel);
}
}, [=] {
removeChild(wlabel);
}, _rowsLifetime);
}
if (wvalue) {
wvalue->heightValue(
) | rpl::on_next_done([=] {
if (!_inResize) {
childHeightUpdated(wvalue);
}
}, [=] {
removeChild(wvalue);
}, _rowsLifetime);
}
}
}
void TableLayout::childHeightUpdated(RpWidget *child) {
auto it = ranges::find_if(_rows, [child](const Row &row) {
return (row.label == child) || (row.value == child);
});
const auto end = _rows.end();
Assert(it != end);
auto top = it->top;
const auto outer = width();
for (; it != end; ++it) {
const auto &row = *it;
updateRowPosition(row, outer, top);
top += rowVerticalSkip(row);
}
resize(width(), _rows.empty() ? 0 : top);
}
void TableLayout::removeChild(RpWidget *child) {
const auto it = ranges::find_if(_rows, [child](const Row &row) {
return (row.label == child) || (row.value == child);
});
if (auto e = end(_rows); it != e) {
auto top = it->top;
auto removed = std::move(*it);
auto next = _rows.erase(it);
const auto outer = width();
for (e = end(_rows); next != e; ++next) {
auto &row = *next;
updateRowPosition(row, outer, top);
top += rowVerticalSkip(row);
}
resize(width(), _rows.empty() ? 0 : top);
if (removed.label.data() == child) {
removed.value.destroy();
} else {
removed.label.destroy();
}
}
}
int TableLayout::rowVerticalSkip(const Row &row) const {
const auto labelHeight = row.label
? (row.labelMargin.top()
+ row.label->heightNoMargins()
+ row.labelMargin.bottom())
: 0;
const auto valueHeight = row.value
? (row.valueMargin.top()
+ row.value->heightNoMargins()
+ row.valueMargin.bottom())
: 0;
return std::max(labelHeight, valueHeight) + _st.border;
}
void TableLayout::clear() {
while (!_rows.empty()) {
const auto &row = _rows.front();
removeChild(row.value ? row.value.data() : row.label.data());
}
}
} // namespace Ui