| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // 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) {
- }
- 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::start_with_next_done([=] {
- if (!_inResize) {
- childHeightUpdated(wlabel);
- }
- }, [=] {
- removeChild(wlabel);
- }, _rowsLifetime);
- }
- if (wvalue) {
- wvalue->heightValue(
- ) | rpl::start_with_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) {
- 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 (auto next = it + 1; next != end; ++next) {
- auto &row = *next;
- updateRowPosition(row, outer, top);
- top += rowVerticalSkip(row);
- }
- it->label = nullptr;
- it->value = nullptr;
- _rows.erase(it);
- resize(width(), _rows.empty() ? 0 : top);
- }
- 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
|