feat:data slove and update heatmap

This commit is contained in:
2025-10-29 14:09:28 +08:00
parent c50b44efe2
commit c6cef3d89d
200 changed files with 100674 additions and 52814 deletions

View File

@@ -1,156 +1,156 @@
#include "flow.hh"
#include <qstyle.h>
using namespace creeper::flow::internal;
struct Flow::Impl {
QList<Item*> items;
int row_spacing = 10;
int col_spacing = 10;
int row_limit = std::numeric_limits<int>::max();
Flow& self;
explicit Impl(Flow& self) noexcept
: self { self } { }
auto calculate_spacing(QStyle::PixelMetric pm) const -> int {
const auto parent = self.parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
const auto pw = static_cast<QWidget*>(parent);
return pw->style()->pixelMetric(pm, nullptr, pw);
} else {
return static_cast<QLayout*>(parent)->spacing();
}
}
auto horizontal_spacing() const -> int {
if (row_spacing >= 0) {
return row_spacing;
} else {
return calculate_spacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
auto vertical_spacing() const -> int {
if (col_spacing >= 0) {
return col_spacing;
} else {
return calculate_spacing(QStyle::PM_LayoutVerticalSpacing);
}
}
auto update_items_geometry(Item* item, const QPoint& point) const {
// TODO: 显然,这个接口未来可以拓展成带动画的位姿更新
item->setGeometry({ point, item->sizeHint() });
}
// Flow 理应是一个常用的布局,但 Qt 中没有对应的实现,
// https://doc.qt.io/archives/qt-5.15/qtwidgets-layouts-flowlayout-example.html
auto calculate_layout(const QRect& rect, bool apply) const -> int {
int left, top, right, bottom;
self.getContentsMargins(&left, &top, &right, &bottom);
const auto effective_rect = rect.adjusted(+left, +top, -right, -bottom);
auto current_x = effective_rect.x();
auto current_y = effective_rect.y();
auto line_height = int { 0 };
auto line_length = int { 0 };
for (auto item : std::as_const(items)) {
const auto widget = item->widget();
const auto spacing = [widget](Qt::Orientation o) {
return widget->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, o);
};
auto space_x = row_spacing;
if (space_x == -1) space_x = spacing(Qt::Horizontal);
auto space_y = col_spacing;
if (space_y == -1) space_y = spacing(Qt::Vertical);
auto next_x = current_x + item->sizeHint().width() + space_x;
const auto area_flag = next_x - space_x > effective_rect.right();
const auto size_flag = line_length > row_limit;
if ((area_flag || size_flag) && line_height > 0) {
current_x = effective_rect.x();
current_y = current_y + line_height + space_y;
next_x = current_x + item->sizeHint().width() + space_x;
line_height = 0;
line_length = 0;
}
if (apply) {
const auto point = QPoint { current_x, current_y };
update_items_geometry(item, point);
}
current_x = next_x;
line_height = std::max(line_height, item->sizeHint().height());
line_length = line_length + 1;
}
return current_y + line_height - rect.y() + bottom;
}
};
Flow::Flow()
: pimpl { std::make_unique<Impl>(*this) } { }
Flow::~Flow() {
while (auto item = Flow::takeAt(0))
delete item;
}
auto Flow::addItem(Item* item) -> void { pimpl->items.append(item); }
auto Flow::setGeometry(const QRect& rect) -> void {
QLayout::setGeometry(rect);
pimpl->calculate_layout(rect, true);
}
auto Flow::takeAt(int index) -> Item* {
auto& items = pimpl->items;
return (index < 0 || index > items.size() - 1) ? nullptr : items.takeAt(index);
}
auto Flow::expandingDirections() const -> Qt::Orientations { return {}; }
auto Flow::hasHeightForWidth() const -> bool { return true; }
auto Flow::heightForWidth(int width) const -> int {
return pimpl->calculate_layout({ 0, 0, width, 0 }, false);
}
auto Flow::itemAt(int index) const -> Item* { return pimpl->items.value(index); }
auto Flow::count() const -> int { return pimpl->items.size(); }
auto Flow::minimumSize() const -> QSize {
auto result = QSize {};
for (const auto item : std::as_const(pimpl->items))
result = result.expandedTo(item->minimumSize());
const auto margins = contentsMargins();
result += QSize {
margins.left() + margins.right(),
margins.top() + margins.bottom(),
};
return result;
}
auto Flow::sizeHint() const -> QSize { return Flow::minimumSize(); }
auto Flow::set_row_spacing(int spacing) noexcept -> void { pimpl->row_spacing = spacing; }
auto Flow::row_spacing() const noexcept -> int { return pimpl->row_spacing; }
auto Flow::set_col_spacing(int spacing) noexcept -> void { pimpl->col_spacing = spacing; }
auto Flow::col_spacing() const noexcept -> int { return pimpl->col_spacing; }
auto Flow::set_row_limit(int limit) noexcept -> void { pimpl->row_limit = limit; }
auto Flow::row_limit() const noexcept -> int { return pimpl->row_limit; }
#include "flow.hh"
#include <qstyle.h>
using namespace creeper::flow::internal;
struct Flow::Impl {
QList<Item*> items;
int row_spacing = 10;
int col_spacing = 10;
int row_limit = std::numeric_limits<int>::max();
Flow& self;
explicit Impl(Flow& self) noexcept
: self { self } { }
auto calculate_spacing(QStyle::PixelMetric pm) const -> int {
const auto parent = self.parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
const auto pw = static_cast<QWidget*>(parent);
return pw->style()->pixelMetric(pm, nullptr, pw);
} else {
return static_cast<QLayout*>(parent)->spacing();
}
}
auto horizontal_spacing() const -> int {
if (row_spacing >= 0) {
return row_spacing;
} else {
return calculate_spacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
auto vertical_spacing() const -> int {
if (col_spacing >= 0) {
return col_spacing;
} else {
return calculate_spacing(QStyle::PM_LayoutVerticalSpacing);
}
}
auto update_items_geometry(Item* item, const QPoint& point) const {
// TODO: 显然,这个接口未来可以拓展成带动画的位姿更新
item->setGeometry({ point, item->sizeHint() });
}
// Flow 理应是一个常用的布局,但 Qt 中没有对应的实现,
// https://doc.qt.io/archives/qt-5.15/qtwidgets-layouts-flowlayout-example.html
auto calculate_layout(const QRect& rect, bool apply) const -> int {
int left, top, right, bottom;
self.getContentsMargins(&left, &top, &right, &bottom);
const auto effective_rect = rect.adjusted(+left, +top, -right, -bottom);
auto current_x = effective_rect.x();
auto current_y = effective_rect.y();
auto line_height = int { 0 };
auto line_length = int { 0 };
for (auto item : std::as_const(items)) {
const auto widget = item->widget();
const auto spacing = [widget](Qt::Orientation o) {
return widget->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, o);
};
auto space_x = row_spacing;
if (space_x == -1) space_x = spacing(Qt::Horizontal);
auto space_y = col_spacing;
if (space_y == -1) space_y = spacing(Qt::Vertical);
auto next_x = current_x + item->sizeHint().width() + space_x;
const auto area_flag = next_x - space_x > effective_rect.right();
const auto size_flag = line_length > row_limit;
if ((area_flag || size_flag) && line_height > 0) {
current_x = effective_rect.x();
current_y = current_y + line_height + space_y;
next_x = current_x + item->sizeHint().width() + space_x;
line_height = 0;
line_length = 0;
}
if (apply) {
const auto point = QPoint { current_x, current_y };
update_items_geometry(item, point);
}
current_x = next_x;
line_height = std::max(line_height, item->sizeHint().height());
line_length = line_length + 1;
}
return current_y + line_height - rect.y() + bottom;
}
};
Flow::Flow()
: pimpl { std::make_unique<Impl>(*this) } { }
Flow::~Flow() {
while (auto item = Flow::takeAt(0))
delete item;
}
auto Flow::addItem(Item* item) -> void { pimpl->items.append(item); }
auto Flow::setGeometry(const QRect& rect) -> void {
QLayout::setGeometry(rect);
pimpl->calculate_layout(rect, true);
}
auto Flow::takeAt(int index) -> Item* {
auto& items = pimpl->items;
return (index < 0 || index > items.size() - 1) ? nullptr : items.takeAt(index);
}
auto Flow::expandingDirections() const -> Qt::Orientations { return {}; }
auto Flow::hasHeightForWidth() const -> bool { return true; }
auto Flow::heightForWidth(int width) const -> int {
return pimpl->calculate_layout({ 0, 0, width, 0 }, false);
}
auto Flow::itemAt(int index) const -> Item* { return pimpl->items.value(index); }
auto Flow::count() const -> int { return pimpl->items.size(); }
auto Flow::minimumSize() const -> QSize {
auto result = QSize {};
for (const auto item : std::as_const(pimpl->items))
result = result.expandedTo(item->minimumSize());
const auto margins = contentsMargins();
result += QSize {
margins.left() + margins.right(),
margins.top() + margins.bottom(),
};
return result;
}
auto Flow::sizeHint() const -> QSize { return Flow::minimumSize(); }
auto Flow::set_row_spacing(int spacing) noexcept -> void { pimpl->row_spacing = spacing; }
auto Flow::row_spacing() const noexcept -> int { return pimpl->row_spacing; }
auto Flow::set_col_spacing(int spacing) noexcept -> void { pimpl->col_spacing = spacing; }
auto Flow::col_spacing() const noexcept -> int { return pimpl->col_spacing; }
auto Flow::set_row_limit(int limit) noexcept -> void { pimpl->row_limit = limit; }
auto Flow::row_limit() const noexcept -> int { return pimpl->row_limit; }

View File

@@ -1,64 +1,64 @@
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include <qlayout.h>
namespace creeper::flow::internal {
class Flow : public QLayout {
CREEPER_PIMPL_DEFINITION(Flow)
public:
using Item = QLayoutItem;
auto addItem(Item*) -> void override;
auto takeAt(int) -> Item* override;
auto setGeometry(const QRect&) -> void override;
auto expandingDirections() const -> Qt::Orientations override;
auto hasHeightForWidth() const -> bool override;
auto heightForWidth(int) const -> int override;
auto itemAt(int) const -> Item* override;
auto count() const -> int override;
auto minimumSize() const -> QSize override;
auto sizeHint() const -> QSize override;
public:
auto set_row_spacing(int) noexcept -> void;
auto row_spacing() const noexcept -> int;
auto set_col_spacing(int) noexcept -> void;
auto col_spacing() const noexcept -> int;
auto set_row_limit(int) noexcept -> void;
auto row_limit() const noexcept -> int;
};
}
namespace creeper::flow::pro {
using Token = common::Token<internal::Flow>;
using RowSpacing = SetterProp<Token, int, [](auto& self, int v) { self.set_row_spacing(v); }>;
using ColSpacing = SetterProp<Token, int, [](auto& self, int v) { self.set_col_spacing(v); }>;
using RowLimit = SetterProp<Token, int, [](auto& self, int v) { self.set_row_limit(v); }>;
using MainAxisSpacing = RowSpacing;
using CrossAxisSpacing = ColSpacing;
using MaxItemsInEachRow = RowLimit;
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
using Flow = Declarative<flow::internal::Flow, CheckerOr<flow::pro::checker, layout::pro::checker>>;
}
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/pimpl.hh"
#include <qlayout.h>
namespace creeper::flow::internal {
class Flow : public QLayout {
CREEPER_PIMPL_DEFINITION(Flow)
public:
using Item = QLayoutItem;
auto addItem(Item*) -> void override;
auto takeAt(int) -> Item* override;
auto setGeometry(const QRect&) -> void override;
auto expandingDirections() const -> Qt::Orientations override;
auto hasHeightForWidth() const -> bool override;
auto heightForWidth(int) const -> int override;
auto itemAt(int) const -> Item* override;
auto count() const -> int override;
auto minimumSize() const -> QSize override;
auto sizeHint() const -> QSize override;
public:
auto set_row_spacing(int) noexcept -> void;
auto row_spacing() const noexcept -> int;
auto set_col_spacing(int) noexcept -> void;
auto col_spacing() const noexcept -> int;
auto set_row_limit(int) noexcept -> void;
auto row_limit() const noexcept -> int;
};
}
namespace creeper::flow::pro {
using Token = common::Token<internal::Flow>;
using RowSpacing = SetterProp<Token, int, [](auto& self, int v) { self.set_row_spacing(v); }>;
using ColSpacing = SetterProp<Token, int, [](auto& self, int v) { self.set_col_spacing(v); }>;
using RowLimit = SetterProp<Token, int, [](auto& self, int v) { self.set_row_limit(v); }>;
using MainAxisSpacing = RowSpacing;
using CrossAxisSpacing = ColSpacing;
using MaxItemsInEachRow = RowLimit;
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
using Flow = Declarative<flow::internal::Flow, CheckerOr<flow::pro::checker, layout::pro::checker>>;
}

View File

@@ -1,83 +1,83 @@
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qgridlayout.h>
namespace creeper {
namespace grid::internal {
class Grid : public QGridLayout { };
}
namespace grid::pro {
using Token = common::Token<QGridLayout>;
struct RowSpacing : Token { };
struct ColSpacing : Token { };
template <class T>
requires std::is_convertible_v<T*, QWidget*> || std::is_convertible_v<T*, QLayout*>
struct Item : Token {
using Align = Qt::Alignment;
struct LayoutMethod {
int row = 0, row_span = 0;
int col = 0, col_span = 0;
Align align;
explicit LayoutMethod(int row, int col, Align align = {})
: row { row }
, col { col }
, align { align } { }
explicit LayoutMethod(int row, int row_span, int col, int col_span, Align align = {})
: row { row }
, col { col }
, row_span { row_span }
, col_span { col_span }
, align { align } { }
} method;
T* item_pointer = nullptr;
explicit Item(const LayoutMethod& method, auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } }
, method(method) { }
explicit Item(const LayoutMethod& method, T* pointer) noexcept
: item_pointer { pointer }
, method { method } { }
void apply(QGridLayout& layout) const {
if (method.col_span == 0) {
if constexpr (std::is_convertible_v<T*, QWidget*>)
layout.addWidget(item_pointer, method.row, method.col, method.align);
if constexpr (std::is_convertible_v<T*, QLayout*>)
layout.addLayout(item_pointer, method.row, method.col, method.align);
} else {
if constexpr (std::is_convertible_v<T*, QWidget*>)
layout.addWidget(item_pointer, method.row, method.row_span, method.col,
method.col_span, method.align);
if constexpr (std::is_convertible_v<T*, QLayout*>)
layout.addLayout(item_pointer, method.row, method.row_span, method.col,
method.col_span, method.align);
}
}
};
struct Items : Token {
explicit Items() { }
void apply(QGridLayout& self) const { }
};
template <typename T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
}
using Grid = Declarative<grid::internal::Grid, grid::pro::checker>;
}
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qgridlayout.h>
namespace creeper {
namespace grid::internal {
class Grid : public QGridLayout { };
}
namespace grid::pro {
using Token = common::Token<QGridLayout>;
struct RowSpacing : Token { };
struct ColSpacing : Token { };
template <class T>
requires std::is_convertible_v<T*, QWidget*> || std::is_convertible_v<T*, QLayout*>
struct Item : Token {
using Align = Qt::Alignment;
struct LayoutMethod {
int row = 0, row_span = 0;
int col = 0, col_span = 0;
Align align;
explicit LayoutMethod(int row, int col, Align align = {})
: row { row }
, col { col }
, align { align } { }
explicit LayoutMethod(int row, int row_span, int col, int col_span, Align align = {})
: row { row }
, col { col }
, row_span { row_span }
, col_span { col_span }
, align { align } { }
} method;
T* item_pointer = nullptr;
explicit Item(const LayoutMethod& method, auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } }
, method(method) { }
explicit Item(const LayoutMethod& method, T* pointer) noexcept
: item_pointer { pointer }
, method { method } { }
void apply(QGridLayout& layout) const {
if (method.col_span == 0) {
if constexpr (std::is_convertible_v<T*, QWidget*>)
layout.addWidget(item_pointer, method.row, method.col, method.align);
if constexpr (std::is_convertible_v<T*, QLayout*>)
layout.addLayout(item_pointer, method.row, method.col, method.align);
} else {
if constexpr (std::is_convertible_v<T*, QWidget*>)
layout.addWidget(item_pointer, method.row, method.row_span, method.col,
method.col_span, method.align);
if constexpr (std::is_convertible_v<T*, QLayout*>)
layout.addLayout(item_pointer, method.row, method.row_span, method.col,
method.col_span, method.align);
}
}
};
struct Items : Token {
explicit Items() { }
void apply(QGridLayout& self) const { }
};
template <typename T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
}
using Grid = Declarative<grid::internal::Grid, grid::pro::checker>;
}

View File

@@ -1,124 +1,124 @@
#pragma once
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
namespace creeper::group::internal {
template <typename F, typename T>
concept foreach_invoke_item_trait = requires {
{ std::invoke(std::declval<F>(), std::declval<T const&>()) } -> widget_pointer_trait;
};
template <typename F, typename T>
concept foreach_apply_item_trait = requires {
{ std::apply(std::declval<F>(), std::declval<T const&>()) } -> widget_pointer_trait;
};
template <typename F, typename T>
concept foreach_item_trait = foreach_invoke_item_trait<F, T> || foreach_apply_item_trait<F, T>;
template <typename R, typename F>
concept foreach_invoke_ranges_trait = foreach_item_trait<F, std::ranges::range_value_t<R>>;
template <layout_trait T, widget_trait W>
struct Group : public T {
using T::T;
std::vector<W*> widgets;
template <std::ranges::range R, typename F>
requires foreach_invoke_ranges_trait<R, F>
constexpr auto compose(const R& ranges, F&& f, Qt::Alignment a = {}) noexcept -> void {
for (const auto& item : ranges) {
using ItemT = decltype(item);
auto widget_pointer = (W*) {};
if constexpr (foreach_invoke_item_trait<F, ItemT>)
widget_pointer = std::invoke(f, item);
else if constexpr (foreach_apply_item_trait<F, ItemT>)
widget_pointer = std::apply(f, item);
if (widget_pointer != nullptr) {
T::addWidget(widget_pointer, 0, a);
widgets.push_back(widget_pointer);
}
}
}
auto foreach_(this auto&& self, auto&& f) noexcept
requires std::invocable<decltype(f), W&>
{
for (auto widget : self.widgets)
std::invoke(f, *widget);
}
};
};
namespace creeper::group::pro {
using Token = common::Token<internal::Group<QLayout, QWidget>>;
/// @note
/// 一种典型的用法,委托构造时,所传函数只能接受常量引用,
/// 放心使用 auto类型是可以被推导出来的
///
/// group::pro::Compose {
/// std::array {
/// std::tuple(1, "xxxxxx"),
/// ......
/// },
/// [](auto index, auto text) {
/// return new TextButton { ... };
/// },
/// }
///
template <typename R, typename F>
requires internal::foreach_invoke_ranges_trait<R, F>
struct Compose : Token {
const R& ranges;
F method;
Qt::Alignment alignment;
explicit Compose(const R& r, F f, Qt::Alignment a = {}) noexcept
: ranges { r }
, method { std::move(f) }
, alignment { a } { }
auto apply(auto& self) noexcept -> void { //
self.compose(ranges, std::move(method), alignment);
}
};
/// @note
/// 函数参数是组件的引用:
///
/// group::pro::Foreach { [](Widget& button) { ... } },
///
template <typename F>
requires(!std::invocable<F>)
struct Foreach : Token {
F function;
explicit Foreach(F&& f) noexcept
: function { std::forward<F>(f) } { }
auto apply(auto& self) const noexcept {
// 很遗憾Qt 占用了 foreach 这个单词
self.foreach_(std::move(function));
}
};
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait)
};
namespace creeper {
template <layout_trait T, widget_trait W>
using Group =
Declarative<group::internal::Group<T, W>, CheckerOr<group::pro::checker, typename T::Checker>>;
}
#pragma once
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
namespace creeper::group::internal {
template <typename F, typename T>
concept foreach_invoke_item_trait = requires {
{ std::invoke(std::declval<F>(), std::declval<T const&>()) } -> widget_pointer_trait;
};
template <typename F, typename T>
concept foreach_apply_item_trait = requires {
{ std::apply(std::declval<F>(), std::declval<T const&>()) } -> widget_pointer_trait;
};
template <typename F, typename T>
concept foreach_item_trait = foreach_invoke_item_trait<F, T> || foreach_apply_item_trait<F, T>;
template <typename R, typename F>
concept foreach_invoke_ranges_trait = foreach_item_trait<F, std::ranges::range_value_t<R>>;
template <layout_trait T, widget_trait W>
struct Group : public T {
using T::T;
std::vector<W*> widgets;
template <std::ranges::range R, typename F>
requires foreach_invoke_ranges_trait<R, F>
constexpr auto compose(const R& ranges, F&& f, Qt::Alignment a = {}) noexcept -> void {
for (const auto& item : ranges) {
using ItemT = decltype(item);
auto widget_pointer = (W*) {};
if constexpr (foreach_invoke_item_trait<F, ItemT>)
widget_pointer = std::invoke(f, item);
else if constexpr (foreach_apply_item_trait<F, ItemT>)
widget_pointer = std::apply(f, item);
if (widget_pointer != nullptr) {
T::addWidget(widget_pointer, 0, a);
widgets.push_back(widget_pointer);
}
}
}
auto foreach_(this auto&& self, auto&& f) noexcept
requires std::invocable<decltype(f), W&>
{
for (auto widget : self.widgets)
std::invoke(f, *widget);
}
};
};
namespace creeper::group::pro {
using Token = common::Token<internal::Group<QLayout, QWidget>>;
/// @note
/// 一种典型的用法,委托构造时,所传函数只能接受常量引用,
/// 放心使用 auto类型是可以被推导出来的
///
/// group::pro::Compose {
/// std::array {
/// std::tuple(1, "xxxxxx"),
/// ......
/// },
/// [](auto index, auto text) {
/// return new TextButton { ... };
/// },
/// }
///
template <typename R, typename F>
requires internal::foreach_invoke_ranges_trait<R, F>
struct Compose : Token {
const R& ranges;
F method;
Qt::Alignment alignment;
explicit Compose(const R& r, F f, Qt::Alignment a = {}) noexcept
: ranges { r }
, method { std::move(f) }
, alignment { a } { }
auto apply(auto& self) noexcept -> void { //
self.compose(ranges, std::move(method), alignment);
}
};
/// @note
/// 函数参数是组件的引用:
///
/// group::pro::Foreach { [](Widget& button) { ... } },
///
template <typename F>
requires(!std::invocable<F>)
struct Foreach : Token {
F function;
explicit Foreach(F&& f) noexcept
: function { std::forward<F>(f) } { }
auto apply(auto& self) const noexcept {
// 很遗憾Qt 占用了 foreach 这个单词
self.foreach_(std::move(function));
}
};
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait)
};
namespace creeper {
template <layout_trait T, widget_trait W>
using Group =
Declarative<group::internal::Group<T, W>, CheckerOr<group::pro::checker, typename T::Checker>>;
}

View File

@@ -1,108 +1,108 @@
#pragma once
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qboxlayout.h>
namespace creeper::linear::pro {
using Token = common::Token<QBoxLayout>;
struct SpacingItem : Token {
int size;
explicit SpacingItem(int p) { size = p; }
void apply(QBoxLayout& self) const { self.addSpacing(size); }
};
struct Stretch : Token {
int stretch;
explicit Stretch(int p) { stretch = p; }
void apply(QBoxLayout& self) const { self.addStretch(stretch); }
};
struct SpacerItem : Token {
QSpacerItem* spacer_item;
explicit SpacerItem(QSpacerItem* p) { spacer_item = p; }
void apply(QBoxLayout& self) const { self.addSpacerItem(spacer_item); }
};
/// @brief
/// 布局项包装器,用于声明式地将 Widget 或 Layout 添加到布局中
///
/// @tparam T
/// 被包装的组件类型,需满足可转换为 QWidget* 或 QLayout*,不需
/// 要显式指定,由构造参数推倒
///
/// @note
/// Item 提供统一的接口用于在布局中插入控件或子布局,
/// 支持多种构造方式,包括直接传入指针或通过参数构造新对象。
/// 通过 LayoutMethod 可指定拉伸因子和对齐方式,
/// 在布局应用时自动选择 addWidget 或 addLayout
/// 实现非侵入式的布局声明式封装。
///
/// 示例用途:
/// linear::pro::Item<Widget> {
/// { 0, Qt::AlignHCenter } // stretch, and alignment, optional
/// ...
/// };
///
template <item_trait T>
struct Item : Token {
struct LayoutMethod {
int stretch = 0;
Qt::Alignment align = {};
} method;
T* item_pointer = nullptr;
explicit Item(const LayoutMethod& method, T* pointer) noexcept
: item_pointer { pointer }
, method { method } { }
explicit Item(T* pointer) noexcept
: item_pointer { pointer } { }
explicit Item(const LayoutMethod& method, auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } }
, method(method) { }
explicit Item(auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
void apply(linear_trait auto& layout) const {
if constexpr (widget_trait<T>) layout.addWidget(item_pointer, method.stretch, method.align);
if constexpr (layout_trait<T>) layout.addLayout(item_pointer, method.stretch);
}
};
template <typename T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
template <class T>
using BoxLayout = Declarative<T, CheckerOr<linear::pro::checker, layout::pro::checker>>;
using Row = BoxLayout<QHBoxLayout>;
using Col = BoxLayout<QVBoxLayout>;
namespace row = linear;
namespace col = linear;
namespace internal {
inline auto use_the_namespace_alias_to_eliminate_warnings() {
std::ignore = row::pro::Token {};
std::ignore = col::pro::Token {};
}
}
}
#pragma once
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qboxlayout.h>
namespace creeper::linear::pro {
using Token = common::Token<QBoxLayout>;
struct SpacingItem : Token {
int size;
explicit SpacingItem(int p) { size = p; }
void apply(QBoxLayout& self) const { self.addSpacing(size); }
};
struct Stretch : Token {
int stretch;
explicit Stretch(int p) { stretch = p; }
void apply(QBoxLayout& self) const { self.addStretch(stretch); }
};
struct SpacerItem : Token {
QSpacerItem* spacer_item;
explicit SpacerItem(QSpacerItem* p) { spacer_item = p; }
void apply(QBoxLayout& self) const { self.addSpacerItem(spacer_item); }
};
/// @brief
/// 布局项包装器,用于声明式地将 Widget 或 Layout 添加到布局中
///
/// @tparam T
/// 被包装的组件类型,需满足可转换为 QWidget* 或 QLayout*,不需
/// 要显式指定,由构造参数推倒
///
/// @note
/// Item 提供统一的接口用于在布局中插入控件或子布局,
/// 支持多种构造方式,包括直接传入指针或通过参数构造新对象。
/// 通过 LayoutMethod 可指定拉伸因子和对齐方式,
/// 在布局应用时自动选择 addWidget 或 addLayout
/// 实现非侵入式的布局声明式封装。
///
/// 示例用途:
/// linear::pro::Item<Widget> {
/// { 0, Qt::AlignHCenter } // stretch, and alignment, optional
/// ...
/// };
///
template <item_trait T>
struct Item : Token {
struct LayoutMethod {
int stretch = 0;
Qt::Alignment align = {};
} method;
T* item_pointer = nullptr;
explicit Item(const LayoutMethod& method, T* pointer) noexcept
: item_pointer { pointer }
, method { method } { }
explicit Item(T* pointer) noexcept
: item_pointer { pointer } { }
explicit Item(const LayoutMethod& method, auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } }
, method(method) { }
explicit Item(auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
void apply(linear_trait auto& layout) const {
if constexpr (widget_trait<T>) layout.addWidget(item_pointer, method.stretch, method.align);
if constexpr (layout_trait<T>) layout.addLayout(item_pointer, method.stretch);
}
};
template <typename T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
template <class T>
using BoxLayout = Declarative<T, CheckerOr<linear::pro::checker, layout::pro::checker>>;
using Row = BoxLayout<QHBoxLayout>;
using Col = BoxLayout<QVBoxLayout>;
namespace row = linear;
namespace col = linear;
namespace internal {
inline auto use_the_namespace_alias_to_eliminate_warnings() {
std::ignore = row::pro::Token {};
std::ignore = col::pro::Token {};
}
}
}

View File

@@ -1,3 +1,3 @@
#include "mixer.hh"
using namespace creeper::mixer::internal;
#include "mixer.hh"
using namespace creeper::mixer::internal;

View File

@@ -1,112 +1,112 @@
#pragma once
#include <qpainter.h>
#include <qpainterpath.h>
#include "modern-qt/utility/animation/animatable.hh"
#include "modern-qt/utility/animation/state/pid.hh"
#include "modern-qt/utility/animation/transition.hh"
#include "modern-qt/utility/wrapper/widget.hh"
namespace creeper::mixer::internal {
class MixerMask : public QWidget {
public:
explicit MixerMask(auto* widget) noexcept
: QWidget { widget }
, animatable { *this } {
QWidget::setAttribute(Qt::WA_TransparentForMouseEvents);
mask_frame.fill(Qt::transparent);
{
auto state = std::make_shared<PidState<double>>();
state->config.kp = 05.0;
state->config.ki = 00.0;
state->config.kd = 00.0;
state->config.epsilon = 1e-3;
mask_radius = make_transition(animatable, std::move(state));
}
}
auto initiate_animation(QPoint const& point) noexcept {
mask_frame.fill(Qt::transparent);
auto* widget = parentWidget();
if (widget == nullptr) return;
mask_radius->snap_to(0.);
mask_radius->transition_to(1.);
mask_point = point;
mask_frame = widget->grab();
update_animation = true;
QWidget::setFixedSize(widget->size());
}
auto initiate_animation(int x, int y) noexcept {
// Forward Point
initiate_animation(QPoint { x, y });
}
protected:
auto paintEvent(QPaintEvent* e) -> void override {
if (!update_animation) return;
auto const w = QWidget::width();
auto const h = QWidget::height();
auto const x = std::sqrt(w * w + h * h);
auto painter = QPainter { this };
auto const radius = double { *mask_radius * x };
auto const round = [&] {
auto path = QPainterPath {};
path.addRect(QWidget::rect());
auto inner = QPainterPath();
inner.addEllipse(mask_point, radius, radius);
return path.subtracted(inner);
}();
painter.setClipPath(round);
painter.setClipping(true);
painter.drawPixmap(QWidget::rect(), mask_frame);
if (std::abs(*mask_radius - 1.) < 1e-2) {
update_animation = false;
}
}
private:
QPixmap mask_frame;
QPointF mask_point;
bool update_animation = false;
Animatable animatable;
std::unique_ptr<TransitionValue<PidState<double>>> mask_radius;
};
}
namespace creeper::mixer::pro {
struct SetMixerMask : widget::pro::Token {
internal::MixerMask*& mask;
explicit SetMixerMask(auto*& mask)
: mask { mask } { }
auto apply(auto& self) noexcept {
//
mask = new internal::MixerMask { &self };
}
};
}
namespace creeper {
using MixerMask = mixer::internal::MixerMask;
}
#pragma once
#include <qpainter.h>
#include <qpainterpath.h>
#include "modern-qt/utility/animation/animatable.hh"
#include "modern-qt/utility/animation/state/pid.hh"
#include "modern-qt/utility/animation/transition.hh"
#include "modern-qt/utility/wrapper/widget.hh"
namespace creeper::mixer::internal {
class MixerMask : public QWidget {
public:
explicit MixerMask(auto* widget) noexcept
: QWidget { widget }
, animatable { *this } {
QWidget::setAttribute(Qt::WA_TransparentForMouseEvents);
mask_frame.fill(Qt::transparent);
{
auto state = std::make_shared<PidState<double>>();
state->config.kp = 05.0;
state->config.ki = 00.0;
state->config.kd = 00.0;
state->config.epsilon = 1e-3;
mask_radius = make_transition(animatable, std::move(state));
}
}
auto initiate_animation(QPoint const& point) noexcept {
mask_frame.fill(Qt::transparent);
auto* widget = parentWidget();
if (widget == nullptr) return;
mask_radius->snap_to(0.);
mask_radius->transition_to(1.);
mask_point = point;
mask_frame = widget->grab();
update_animation = true;
QWidget::setFixedSize(widget->size());
}
auto initiate_animation(int x, int y) noexcept {
// Forward Point
initiate_animation(QPoint { x, y });
}
protected:
auto paintEvent(QPaintEvent* e) -> void override {
if (!update_animation) return;
auto const w = QWidget::width();
auto const h = QWidget::height();
auto const x = std::sqrt(w * w + h * h);
auto painter = QPainter { this };
auto const radius = double { *mask_radius * x };
auto const round = [&] {
auto path = QPainterPath {};
path.addRect(QWidget::rect());
auto inner = QPainterPath();
inner.addEllipse(mask_point, radius, radius);
return path.subtracted(inner);
}();
painter.setClipPath(round);
painter.setClipping(true);
painter.drawPixmap(QWidget::rect(), mask_frame);
if (std::abs(*mask_radius - 1.) < 1e-2) {
update_animation = false;
}
}
private:
QPixmap mask_frame;
QPointF mask_point;
bool update_animation = false;
Animatable animatable;
std::unique_ptr<TransitionValue<PidState<double>>> mask_radius;
};
}
namespace creeper::mixer::pro {
struct SetMixerMask : widget::pro::Token {
internal::MixerMask*& mask;
explicit SetMixerMask(auto*& mask)
: mask { mask } { }
auto apply(auto& self) noexcept {
//
mask = new internal::MixerMask { &self };
}
};
}
namespace creeper {
using MixerMask = mixer::internal::MixerMask;
}

View File

@@ -1,98 +1,98 @@
#pragma once
#include <modern-qt/layout/group.hh>
#include <modern-qt/utility/wrapper/layout.hh>
#include <ranges>
namespace creeper::mutual_exclusion_group::internal {
template <auto f, typename W>
concept switch_function_trait = std::invocable<decltype(f), W&, bool>;
template <layout_trait T, widget_trait W, auto switch_function>
requires switch_function_trait<switch_function, W>
struct MutualExclusionGroup : public Group<T, W> {
using Group<T, W>::Group;
public:
auto switch_widgets(std::size_t index) const noexcept {
for (auto [index_, w] : std::views::enumerate(this->widgets)) {
switch_function(*w, index_ == index);
}
}
auto switch_widgets(W* widget) const noexcept {
for (auto w_ : this->widgets) {
switch_function(*w_, w_ == widget);
}
}
auto make_signal_injection(auto signal) const noexcept -> void {
for (auto widget : this->widgets) {
QObject::connect(widget, signal, [this, widget] { switch_widgets(widget); });
}
}
};
constexpr inline auto checked_switch_function = [](auto& w, bool on) { w.set_checked(on); };
constexpr inline auto opened_switch_function = [](auto& w, bool on) { w.set_opened(on); };
constexpr inline auto selected_switch_function = [](auto& w, bool on) { w.set_selected(on); };
constexpr inline auto no_action_as_token = [](auto& w, bool on) { };
}
namespace creeper::mutual_exclusion_group::pro {
struct TokenContext { };
using Token = common::Token<TokenContext>;
template <typename Signal>
struct SignalInjection : Token {
Signal signal;
explicit SignalInjection(Signal signal) noexcept
: signal { signal } { }
auto apply(auto& self) const noexcept -> void {
self.make_signal_injection(signal); //
}
};
template <typename T>
concept trait = std::derived_from<T, Token> || group::pro::trait<T>;
CREEPER_DEFINE_CHECKER(trait);
using namespace group::pro;
}
namespace creeper {
template <layout_trait T, widget_trait W, auto switch_function>
requires mutual_exclusion_group::internal::switch_function_trait<switch_function, W>
using MutualExclusionGroup =
mutual_exclusion_group::internal::MutualExclusionGroup<T, W, switch_function>;
template <layout_trait T, widget_trait W>
using CheckGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::checked_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace check_group = mutual_exclusion_group;
template <layout_trait T, widget_trait W>
using OpenGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::opened_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace open_group = mutual_exclusion_group;
template <layout_trait T, widget_trait W>
using SelectGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::selected_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace select_group = mutual_exclusion_group;
namespace internal {
inline auto use_mutual_exclusion_group_namespace() {
std::ignore = check_group::pro::Token {};
std::ignore = open_group::pro::Token {};
std::ignore = select_group::pro::Token {};
}
}
}
#pragma once
#include <modern-qt/layout/group.hh>
#include <modern-qt/utility/wrapper/layout.hh>
#include <ranges>
namespace creeper::mutual_exclusion_group::internal {
template <auto f, typename W>
concept switch_function_trait = std::invocable<decltype(f), W&, bool>;
template <layout_trait T, widget_trait W, auto switch_function>
requires switch_function_trait<switch_function, W>
struct MutualExclusionGroup : public Group<T, W> {
using Group<T, W>::Group;
public:
auto switch_widgets(std::size_t index) const noexcept {
for (auto [index_, w] : std::views::enumerate(this->widgets)) {
switch_function(*w, index_ == index);
}
}
auto switch_widgets(W* widget) const noexcept {
for (auto w_ : this->widgets) {
switch_function(*w_, w_ == widget);
}
}
auto make_signal_injection(auto signal) const noexcept -> void {
for (auto widget : this->widgets) {
QObject::connect(widget, signal, [this, widget] { switch_widgets(widget); });
}
}
};
constexpr inline auto checked_switch_function = [](auto& w, bool on) { w.set_checked(on); };
constexpr inline auto opened_switch_function = [](auto& w, bool on) { w.set_opened(on); };
constexpr inline auto selected_switch_function = [](auto& w, bool on) { w.set_selected(on); };
constexpr inline auto no_action_as_token = [](auto& w, bool on) { };
}
namespace creeper::mutual_exclusion_group::pro {
struct TokenContext { };
using Token = common::Token<TokenContext>;
template <typename Signal>
struct SignalInjection : Token {
Signal signal;
explicit SignalInjection(Signal signal) noexcept
: signal { signal } { }
auto apply(auto& self) const noexcept -> void {
self.make_signal_injection(signal); //
}
};
template <typename T>
concept trait = std::derived_from<T, Token> || group::pro::trait<T>;
CREEPER_DEFINE_CHECKER(trait);
using namespace group::pro;
}
namespace creeper {
template <layout_trait T, widget_trait W, auto switch_function>
requires mutual_exclusion_group::internal::switch_function_trait<switch_function, W>
using MutualExclusionGroup =
mutual_exclusion_group::internal::MutualExclusionGroup<T, W, switch_function>;
template <layout_trait T, widget_trait W>
using CheckGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::checked_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace check_group = mutual_exclusion_group;
template <layout_trait T, widget_trait W>
using OpenGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::opened_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace open_group = mutual_exclusion_group;
template <layout_trait T, widget_trait W>
using SelectGroup = Declarative<
MutualExclusionGroup<T, W, mutual_exclusion_group::internal::selected_switch_function>,
CheckerOr<mutual_exclusion_group::pro::checker, typename T::Checker>>;
namespace select_group = mutual_exclusion_group;
namespace internal {
inline auto use_mutual_exclusion_group_namespace() {
std::ignore = check_group::pro::Token {};
std::ignore = open_group::pro::Token {};
std::ignore = select_group::pro::Token {};
}
}
}

View File

@@ -1,135 +1,135 @@
#pragma once
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "modern-qt/widget/widget.hh"
#include <qscrollarea.h>
#include <qscrollbar.h>
namespace creeper::scroll::internal {
/// NOTE: 先拿 qss 勉强用着吧,找时间完全重构
class ScrollArea : public QScrollArea {
public:
explicit ScrollArea() noexcept {
viewport()->setStyleSheet("background:transparent;");
setStyleSheet("QScrollArea{background:transparent;}");
setWidgetResizable(true);
}
void set_color_scheme(const ColorScheme& scheme) {
constexpr auto q = [](const QColor& c, int a = 255) {
return QString("rgba(%1,%2,%3,%4)").arg(c.red()).arg(c.green()).arg(c.blue()).arg(a);
};
verticalScrollBar()->setStyleSheet(QString {
"QScrollBar:vertical{background:transparent;width:8px;border-radius:4px;}"
"QScrollBar::handle:vertical{background:%1;min-height:20px;border-radius:4px;}"
"QScrollBar::handle:vertical:hover{background:%2;}"
"QScrollBar::handle:vertical:pressed{background:%3;}"
"QScrollBar::add-line:vertical,QScrollBar::sub-line:vertical,"
"QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical{height:0px;}",
}
.arg(q(scheme.primary, 235))
.arg(q(scheme.primary))
.arg(q(scheme.primary.darker(110))));
horizontalScrollBar()->setStyleSheet(QString {
"QScrollBar:horizontal{background:transparent;height:8px;border-radius:4px;}"
"QScrollBar::handle:horizontal{background:%1;min-width:20px;border-radius:4px;}"
"QScrollBar::handle:horizontal:hover{background:%2;}"
"QScrollBar::handle:horizontal:pressed{background:%3;}"
"QScrollBar::add-line:horizontal,QScrollBar::sub-line:horizontal,"
"QScrollBar::add-page:horizontal,QScrollBar::sub-page:horizontal{width:0px;}",
}
.arg(q(scheme.primary, 235))
.arg(q(scheme.primary))
.arg(q(scheme.primary.darker(110))));
}
void load_theme_manager(ThemeManager& manager) {
manager.append_handler(this,
[this](const ThemeManager& manager) { set_color_scheme(manager.color_scheme()); });
}
};
}
namespace creeper::scroll::pro {
using Token = common::Token<internal::ScrollArea>;
struct VerticalScrollBarPolicy : Token {
Qt::ScrollBarPolicy policy;
explicit VerticalScrollBarPolicy(Qt::ScrollBarPolicy policy) noexcept
: policy { policy } { }
auto apply(auto& self) const noexcept -> void { //
self.setVerticalScrollBarPolicy(policy);
}
};
struct HorizontalScrollBarPolicy : Token {
Qt::ScrollBarPolicy policy;
explicit HorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy) noexcept
: policy { policy } { }
auto apply(auto& self) const noexcept -> void { //
self.setHorizontalScrollBarPolicy(policy);
}
};
struct ScrollBarPolicy : Token {
Qt::ScrollBarPolicy v;
Qt::ScrollBarPolicy h;
explicit ScrollBarPolicy(Qt::ScrollBarPolicy v, Qt::ScrollBarPolicy h) noexcept
: v { v }
, h { h } { }
auto apply(auto& self) const noexcept -> void {
self.setVerticalScrollBarPolicy(v);
self.setHorizontalScrollBarPolicy(h);
}
};
template <item_trait T>
struct Item : Token {
T* item_pointer = nullptr;
explicit Item(auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
explicit Item(T* pointer) noexcept
: item_pointer { pointer } { }
auto apply(area_trait auto& layout) const noexcept -> void {
if constexpr (widget_trait<T>) {
layout.setWidget(item_pointer);
}
// NOTE: 这里可能有调整的空间,直接设置 Layout
// 布局 Size 行为是不正确的
else if constexpr (layout_trait<T>) {
const auto content = new Widget {
widget::pro::Layout { item_pointer },
};
layout.setWidget(content);
}
}
};
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
}
namespace creeper {
using ScrollArea = Declarative<scroll::internal::ScrollArea,
CheckerOr<scroll::pro::checker, widget::pro::checker, theme::pro::checker>>;
}
#pragma once
#include "modern-qt/utility/theme/theme.hh"
#include "modern-qt/utility/trait/widget.hh"
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include "modern-qt/widget/widget.hh"
#include <qscrollarea.h>
#include <qscrollbar.h>
namespace creeper::scroll::internal {
/// NOTE: 先拿 qss 勉强用着吧,找时间完全重构
class ScrollArea : public QScrollArea {
public:
explicit ScrollArea() noexcept {
viewport()->setStyleSheet("background:transparent;");
setStyleSheet("QScrollArea{background:transparent;}");
setWidgetResizable(true);
}
void set_color_scheme(const ColorScheme& scheme) {
constexpr auto q = [](const QColor& c, int a = 255) {
return QString("rgba(%1,%2,%3,%4)").arg(c.red()).arg(c.green()).arg(c.blue()).arg(a);
};
verticalScrollBar()->setStyleSheet(QString {
"QScrollBar:vertical{background:transparent;width:8px;border-radius:4px;}"
"QScrollBar::handle:vertical{background:%1;min-height:20px;border-radius:4px;}"
"QScrollBar::handle:vertical:hover{background:%2;}"
"QScrollBar::handle:vertical:pressed{background:%3;}"
"QScrollBar::add-line:vertical,QScrollBar::sub-line:vertical,"
"QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical{height:0px;}",
}
.arg(q(scheme.primary, 235))
.arg(q(scheme.primary))
.arg(q(scheme.primary.darker(110))));
horizontalScrollBar()->setStyleSheet(QString {
"QScrollBar:horizontal{background:transparent;height:8px;border-radius:4px;}"
"QScrollBar::handle:horizontal{background:%1;min-width:20px;border-radius:4px;}"
"QScrollBar::handle:horizontal:hover{background:%2;}"
"QScrollBar::handle:horizontal:pressed{background:%3;}"
"QScrollBar::add-line:horizontal,QScrollBar::sub-line:horizontal,"
"QScrollBar::add-page:horizontal,QScrollBar::sub-page:horizontal{width:0px;}",
}
.arg(q(scheme.primary, 235))
.arg(q(scheme.primary))
.arg(q(scheme.primary.darker(110))));
}
void load_theme_manager(ThemeManager& manager) {
manager.append_handler(this,
[this](const ThemeManager& manager) { set_color_scheme(manager.color_scheme()); });
}
};
}
namespace creeper::scroll::pro {
using Token = common::Token<internal::ScrollArea>;
struct VerticalScrollBarPolicy : Token {
Qt::ScrollBarPolicy policy;
explicit VerticalScrollBarPolicy(Qt::ScrollBarPolicy policy) noexcept
: policy { policy } { }
auto apply(auto& self) const noexcept -> void { //
self.setVerticalScrollBarPolicy(policy);
}
};
struct HorizontalScrollBarPolicy : Token {
Qt::ScrollBarPolicy policy;
explicit HorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy) noexcept
: policy { policy } { }
auto apply(auto& self) const noexcept -> void { //
self.setHorizontalScrollBarPolicy(policy);
}
};
struct ScrollBarPolicy : Token {
Qt::ScrollBarPolicy v;
Qt::ScrollBarPolicy h;
explicit ScrollBarPolicy(Qt::ScrollBarPolicy v, Qt::ScrollBarPolicy h) noexcept
: v { v }
, h { h } { }
auto apply(auto& self) const noexcept -> void {
self.setVerticalScrollBarPolicy(v);
self.setHorizontalScrollBarPolicy(h);
}
};
template <item_trait T>
struct Item : Token {
T* item_pointer = nullptr;
explicit Item(auto&&... args) noexcept
requires std::constructible_from<T, decltype(args)...>
: item_pointer { new T { std::forward<decltype(args)>(args)... } } { }
explicit Item(T* pointer) noexcept
: item_pointer { pointer } { }
auto apply(area_trait auto& layout) const noexcept -> void {
if constexpr (widget_trait<T>) {
layout.setWidget(item_pointer);
}
// NOTE: 这里可能有调整的空间,直接设置 Layout
// 布局 Size 行为是不正确的
else if constexpr (layout_trait<T>) {
const auto content = new Widget {
widget::pro::Layout { item_pointer },
};
layout.setWidget(content);
}
}
};
template <class T>
concept trait = std::derived_from<T, Token>;
CREEPER_DEFINE_CHECKER(trait);
using namespace widget::pro;
using namespace theme::pro;
}
namespace creeper {
using ScrollArea = Declarative<scroll::internal::ScrollArea,
CheckerOr<scroll::pro::checker, widget::pro::checker, theme::pro::checker>>;
}

View File

@@ -1,26 +1,26 @@
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qstackedlayout.h>
namespace creeper::stacked::internal {
class Stacked : public QStackedLayout { };
}
namespace creeper::stacked::pro {
using Token = common::Token<internal::Stacked>;
template <typename T>
concept trait = std::derived_from<T, Token> || layout::pro::trait<T>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
using Stacked = Declarative<stacked::internal::Stacked, stacked::pro::checker>;
using NavHost = Stacked;
}
#pragma once
#include "modern-qt/utility/wrapper/common.hh"
#include "modern-qt/utility/wrapper/layout.hh"
#include "modern-qt/utility/wrapper/property.hh"
#include <qstackedlayout.h>
namespace creeper::stacked::internal {
class Stacked : public QStackedLayout { };
}
namespace creeper::stacked::pro {
using Token = common::Token<internal::Stacked>;
template <typename T>
concept trait = std::derived_from<T, Token> || layout::pro::trait<T>;
CREEPER_DEFINE_CHECKER(trait);
using namespace layout::pro;
}
namespace creeper {
using Stacked = Declarative<stacked::internal::Stacked, stacked::pro::checker>;
using NavHost = Stacked;
}