Add simple label->value table layout.
This commit is contained in:
parent
5963a62ff5
commit
dc6a4e976c
4 changed files with 351 additions and 0 deletions
|
|
@ -252,6 +252,8 @@ PRIVATE
|
|||
ui/wrap/padding_wrap.h
|
||||
ui/wrap/slide_wrap.cpp
|
||||
ui/wrap/slide_wrap.h
|
||||
ui/wrap/table_layout.cpp
|
||||
ui/wrap/table_layout.h
|
||||
ui/wrap/vertical_layout.cpp
|
||||
ui/wrap/vertical_layout.h
|
||||
ui/wrap/vertical_layout_reorder.cpp
|
||||
|
|
|
|||
|
|
@ -612,6 +612,16 @@ Toast {
|
|||
durationSlide: int;
|
||||
}
|
||||
|
||||
Table {
|
||||
bg: color;
|
||||
headerBg: color;
|
||||
borderFg: color;
|
||||
border: pixels;
|
||||
radius: pixels;
|
||||
labelMinWidth: pixels;
|
||||
labelMaxWidth: double;
|
||||
}
|
||||
|
||||
defaultLabelSimple: LabelSimple {
|
||||
font: normalFont;
|
||||
maxWidth: 0px;
|
||||
|
|
@ -640,6 +650,15 @@ defaultRippleAnimation: RippleAnimation {
|
|||
hideDuration: 200;
|
||||
}
|
||||
|
||||
defaultTable: Table {
|
||||
bg: windowBg;
|
||||
headerBg: windowBgOver;
|
||||
borderFg: inputBorderFg;
|
||||
border: 1px;
|
||||
radius: 6px;
|
||||
labelMaxWidth: 0.5;
|
||||
}
|
||||
|
||||
emptyRippleAnimation: RippleAnimation {
|
||||
}
|
||||
|
||||
|
|
|
|||
235
ui/wrap/table_layout.cpp
Normal file
235
ui/wrap/table_layout.cpp
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
// 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 });
|
||||
|
||||
p.setClipRect(0, 0, _valueLeft, height());
|
||||
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);
|
||||
auto top = half;
|
||||
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);
|
||||
}
|
||||
path.moveTo(_valueLeft - half, half);
|
||||
path.lineTo(_valueLeft - half, height() - half);
|
||||
|
||||
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->naturalWidth()
|
||||
+ row.labelMargin.left()
|
||||
+ row.labelMargin.right();
|
||||
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) {
|
||||
setChildVisibleTopBottom(
|
||||
row.label,
|
||||
visibleTop,
|
||||
visibleBottom);
|
||||
setChildVisibleTopBottom(
|
||||
row.value,
|
||||
visibleTop,
|
||||
visibleBottom);
|
||||
}
|
||||
}
|
||||
|
||||
void TableLayout::updateRowGeometry(
|
||||
const Row &row,
|
||||
int width,
|
||||
int top) const {
|
||||
row.top = top;
|
||||
row.label->resizeToNaturalWidth(_valueLeft
|
||||
- 2 * _st.border
|
||||
- row.labelMargin.left()
|
||||
- row.labelMargin.right());
|
||||
row.label->moveToLeft(
|
||||
_st.border + row.labelMargin.left(),
|
||||
top + row.labelMargin.top(),
|
||||
width);
|
||||
row.value->resizeToNaturalWidth(width
|
||||
- _valueLeft
|
||||
- _st.border
|
||||
- row.valueMargin.left()
|
||||
- row.valueMargin.right());
|
||||
row.value->moveToLeft(
|
||||
_valueLeft + row.valueMargin.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 = AttachParentChild(this, label);
|
||||
const auto wvalue = AttachParentChild(this, value);
|
||||
if (wlabel && wvalue) {
|
||||
_rows.insert(begin(_rows) + atPosition, {
|
||||
std::move(label),
|
||||
std::move(value),
|
||||
labelMargin,
|
||||
valueMargin,
|
||||
});
|
||||
wlabel->heightValue(
|
||||
) | rpl::start_with_next_done([=] {
|
||||
if (!_inResize) {
|
||||
childHeightUpdated(wlabel);
|
||||
}
|
||||
}, [=] {
|
||||
removeChild(wlabel);
|
||||
}, _rowsLifetime);
|
||||
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);
|
||||
});
|
||||
|
||||
auto top = (it == _rows.begin()) ? _st.border : (it - 1)->top;
|
||||
const auto outer = width();
|
||||
for (auto end = _rows.end(); it != end; ++it) {
|
||||
const auto &row = *it;
|
||||
row.top = top;
|
||||
row.label->moveToLeft(
|
||||
_st.border + row.labelMargin.left(),
|
||||
top + row.labelMargin.top(),
|
||||
outer);
|
||||
row.value->moveToLeft(
|
||||
_valueLeft + row.valueMargin.left(),
|
||||
top + row.valueMargin.top(),
|
||||
outer);
|
||||
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);
|
||||
});
|
||||
auto end = _rows.end();
|
||||
Assert(it != end);
|
||||
|
||||
auto top = (it == _rows.begin()) ? _st.border : (it - 1)->top;
|
||||
const auto outer = width();
|
||||
for (auto next = it + 1; next != end; ++next) {
|
||||
auto &row = *next;
|
||||
row.top = top;
|
||||
row.label->moveToLeft(
|
||||
_st.border + row.labelMargin.left(),
|
||||
top + row.labelMargin.top(),
|
||||
outer);
|
||||
row.value->moveToLeft(
|
||||
_valueLeft + row.valueMargin.left(),
|
||||
top + row.valueMargin.top(),
|
||||
outer);
|
||||
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.labelMargin.top()
|
||||
+ row.label->heightNoMargins()
|
||||
+ row.labelMargin.bottom();
|
||||
const auto valueHeight = row.valueMargin.top()
|
||||
+ row.value->heightNoMargins()
|
||||
+ row.valueMargin.bottom();
|
||||
return std::max(labelHeight, valueHeight) + _st.border;
|
||||
}
|
||||
|
||||
void TableLayout::clear() {
|
||||
while (!_rows.empty()) {
|
||||
removeChild(_rows.front().label.data());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
95
ui/wrap/table_layout.h
Normal file
95
ui/wrap/table_layout.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
namespace style {
|
||||
struct Table;
|
||||
} // namespace style
|
||||
|
||||
namespace st {
|
||||
extern const style::Table &defaultTable;
|
||||
} // namespace st
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class TableLayout : public RpWidget {
|
||||
public:
|
||||
TableLayout(QWidget *parent, const style::Table &st = st::defaultTable);
|
||||
|
||||
[[nodiscard]] int rowsCount() const {
|
||||
return _rows.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<RpWidget*> labelAt(int index) const {
|
||||
Expects(index >= 0 && index < rowsCount());
|
||||
|
||||
return _rows[index].label.data();
|
||||
}
|
||||
[[nodiscard]] not_null<RpWidget*> valueAt(int index) const {
|
||||
Expects(index >= 0 && index < rowsCount());
|
||||
|
||||
return _rows[index].value.data();
|
||||
}
|
||||
|
||||
void insertRow(
|
||||
int atPosition,
|
||||
object_ptr<RpWidget> &&label,
|
||||
object_ptr<RpWidget> &&value,
|
||||
const style::margins &labelMargin = style::margins(),
|
||||
const style::margins &valueMargin = style::margins());
|
||||
|
||||
void addRow(
|
||||
object_ptr<RpWidget> &&label,
|
||||
object_ptr<RpWidget> &&value,
|
||||
const style::margins &labelMargin = style::margins(),
|
||||
const style::margins &valueMargin = style::margins()) {
|
||||
insertRow(
|
||||
rowsCount(),
|
||||
std::move(label),
|
||||
std::move(value),
|
||||
labelMargin,
|
||||
valueMargin);
|
||||
}
|
||||
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
private:
|
||||
struct Row {
|
||||
object_ptr<RpWidget> label;
|
||||
object_ptr<RpWidget> value;
|
||||
style::margins labelMargin;
|
||||
style::margins valueMargin;
|
||||
mutable int top = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] int rowVerticalSkip(const Row &row) const;
|
||||
void childHeightUpdated(RpWidget *child);
|
||||
void removeChild(RpWidget *child);
|
||||
void updateRowGeometry(const Row &row, int width, int top) const;
|
||||
|
||||
const style::Table &_st;
|
||||
|
||||
std::vector<Row> _rows;
|
||||
int _valueLeft = 0;
|
||||
bool _inResize = false;
|
||||
|
||||
rpl::lifetime _rowsLifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
Loading…
Add table
Reference in a new issue