Files
ts-qt/modern-qt/utility/painter/container.hh
2025-10-20 00:32:01 +08:00

348 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include "modern-qt/utility/painter/common.hh"
namespace creeper::painter {
// 核心容器结构体,现在继承自 Impl使其满足 drawable_trait (假设 Impl 继承了所需的属性)
template <class Impl, drawable_trait... Ts>
struct Container : public Impl {
std::tuple<std::decay_t<Ts>...> drawable;
// 唯一构造函数:接受 Impl 实例和可变参数包
constexpr explicit Container(const Impl& impl, Ts&&... drawable)
: Impl { impl }
, drawable { std::make_tuple(std::forward<Ts>(drawable)...) } { }
auto operator()(qt::painter& painter)
requires(std::invocable<Ts, qt::painter&> && ...)
{
render(painter);
}
auto render(qt::painter& painter) noexcept
requires(std::invocable<Ts, qt::painter&> && ...)
{
constexpr auto has_unique = requires { //
static_cast<Impl&>(*this).unique_render(painter, drawable);
};
if constexpr (has_unique) {
static_cast<Impl&>(*this).unique_render(painter, drawable);
} else {
Impl::make_layout(drawable);
auto f = [&](auto&... d) { (d(painter), ...); };
std::apply(std::move(f), drawable);
}
}
};
// ----------------------------------------------------------------------
// 布局实现基类
// ----------------------------------------------------------------------
struct MakeLayoutFunction {
template <drawable_trait... Ts>
auto make_layout(this auto& self, std::tuple<Ts...>& drawable) {
std::apply([&self](auto&... d) { ((self.make(d)), ...); }, drawable);
}
};
// ----------------------------------------------------------------------
// SurfaceImpl (仅平移)
// ----------------------------------------------------------------------
struct SurfaceImpl : public MakeLayoutFunction, ContainerProps {
constexpr explicit SurfaceImpl(const qt::size& size, const qt::point& origin = {})
: ContainerProps {
.size = size,
.origin = origin,
} { }
auto make(drawable_trait auto& drawable) {
const auto& container_origin = origin;
auto& drawable_origin = drawable.origin;
drawable_origin.setX(container_origin.x() + drawable_origin.x());
drawable_origin.setY(container_origin.y() + drawable_origin.y());
};
};
// ----------------------------------------------------------------------
// BufferImpl
// ----------------------------------------------------------------------
struct BufferImpl : public MakeLayoutFunction, ContainerProps {
mutable QPixmap buffer;
constexpr explicit BufferImpl(const qt::size& size, const qt::point& origin = { 0, 0 })
: ContainerProps {
.size = size,
.origin = origin,
} { }
auto make(drawable_trait auto& drawable) {
const auto& container_origin = origin;
drawable.origin.setX(container_origin.x() + drawable.origin.x());
drawable.origin.setY(container_origin.y() + drawable.origin.y());
};
template <drawable_trait... Ts>
auto unique_render(qt::painter& main_painter, std::tuple<Ts...>& drawable) noexcept {
make_layout(drawable);
if (buffer.size() != size || buffer.isNull()) {
buffer = QPixmap(size.width(), size.height());
buffer.fill(Qt::transparent);
}
buffer.fill(Qt::transparent);
auto buffer_painter = qt::painter { &buffer };
buffer_painter.translate(-origin.x(), -origin.y());
const auto f = [&](auto&... args) {
(
[&]() {
buffer_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
args(buffer_painter);
}(),
...);
};
std::apply(std::move(f), drawable);
buffer_painter.end();
main_painter.drawPixmap(origin, buffer);
}
};
// ----------------------------------------------------------------------
// BoxImpl (居中对齐)
// ----------------------------------------------------------------------
struct BoxImpl : public MakeLayoutFunction, ContainerProps {
constexpr explicit BoxImpl(
const qt::size& size, const qt::align& align, const qt::point& origin = {})
: ContainerProps {
.size = size,
.align = align,
.origin = origin,
} { }
auto make(drawable_trait auto& drawable) {
const auto container_align = align;
const auto container_size = size;
const auto container_origin = origin;
auto& drawable_origin = drawable.origin;
auto& drawable_size = drawable.size;
const auto container_w = container_size.width();
const auto container_h = container_size.height();
if (container_align & Qt::AlignRight) {
drawable_origin.setX(container_origin.x() + container_w - drawable_size.width());
} else if (container_align & Qt::AlignHCenter) {
const auto dx = (container_w - drawable_size.width()) / 2;
drawable_origin.setX(container_origin.x() + dx);
} else {
drawable_origin.setX(container_origin.x());
}
if (container_align & Qt::AlignBottom) {
drawable_origin.setY(container_origin.y() + container_h - drawable_size.height());
} else if (container_align & Qt::AlignVCenter) {
const auto dy = (container_h - drawable_size.height()) / 2;
drawable_origin.setY(container_origin.y() + dy);
} else {
drawable_origin.setY(container_origin.y());
}
};
};
// ----------------------------------------------------------------------
// RowImpl (横向流式布局)
// ----------------------------------------------------------------------
struct RowImpl : public MakeLayoutFunction, ContainerProps {
// 主轴对齐 (Horizontal)
const qt::align main_align;
constexpr explicit RowImpl(
const qt::size& size,
const qt::align& main_align = Qt::AlignLeft, // 主轴对齐AlignLeft/AlignRight/AlignHCenter
const qt::align& cross_align = Qt::AlignVCenter, // 非主轴对齐AlignTop/AlignBottom/AlignVCenter
const qt::point& origin = {})
: ContainerProps {
.size = size,
.align = cross_align, // ContainerProps::align 存储非主轴对齐
.origin = origin,
}
, main_align(main_align) // 存储主轴对齐
{ }
mutable int current_x = 0;
template <drawable_trait... Ts>
auto make_layout(this auto& self, std::tuple<Ts...>& drawable) {
// 1. 计算主轴总尺寸 (Total Width)
int total_width = 0;
std::apply(
[&total_width](auto&... d) { ((total_width += d.size.width()), ...); }, drawable);
// 2. 计算主轴偏移 (Main Axis Offset)
int initial_x_offset = 0;
const int remaining_space = self.size.width() - total_width;
if (remaining_space > 0) {
if (self.main_align & Qt::AlignRight) {
initial_x_offset = remaining_space;
} else if (self.main_align & Qt::AlignHCenter) {
initial_x_offset = remaining_space / 2;
}
}
// 3. 设置起始 X 坐标
self.current_x = self.origin.x() + initial_x_offset;
// 4. 应用布局到每个元素
std::apply([&self](auto&... d) { ((self.make(d)), ...); }, drawable);
}
auto make(drawable_trait auto& drawable) {
const auto container_cross_align = align; // 非主轴对齐 (垂直)
const auto container_size = size;
const auto container_origin = origin;
auto& drawable_origin = drawable.origin;
const auto drawable_h = drawable.size.height();
// 1. 主轴布局 (X 坐标累加)
drawable_origin.setX(current_x);
current_x += drawable.size.width();
// 2. 非主轴对齐 (垂直对齐)
if (container_cross_align & Qt::AlignBottom) {
drawable_origin.setY(container_origin.y() + container_size.height() - drawable_h);
} else if (container_cross_align & Qt::AlignVCenter) {
const auto dy = (container_size.height() - drawable_h) / 2;
drawable_origin.setY(container_origin.y() + dy);
} else { // 默认 AlignTop
drawable_origin.setY(container_origin.y());
}
};
};
// ----------------------------------------------------------------------
// ColImpl (垂直流式布局)
// ----------------------------------------------------------------------
struct ColImpl : public MakeLayoutFunction, ContainerProps {
// 主轴对齐 (Vertical)
const qt::align main_align;
constexpr explicit ColImpl(
const qt::size& size,
const qt::align& main_align = Qt::AlignTop, // 主轴对齐AlignTop/AlignBottom/AlignVCenter
const qt::align& cross_align = Qt::AlignHCenter, // 非主轴对齐AlignLeft/AlignRight/AlignHCenter
const qt::point& origin = {})
: ContainerProps {
.size = size,
.align = cross_align, // ContainerProps::align 存储非主轴对齐
.origin = origin,
}
, main_align(main_align) // 存储主轴对齐
{ }
mutable int current_y = 0;
template <drawable_trait... Ts>
auto make_layout(this auto& self, std::tuple<Ts...>& drawable) {
// 1. 计算主轴总尺寸 (Total Height)
int total_height = 0;
std::apply(
[&total_height](auto&... d) { ((total_height += d.size.height()), ...); }, drawable);
// 2. 计算主轴偏移 (Main Axis Offset)
int initial_y_offset = 0;
const int remaining_space = self.size.height() - total_height;
if (remaining_space > 0) {
if (self.main_align & Qt::AlignBottom) {
initial_y_offset = remaining_space;
} else if (self.main_align & Qt::AlignVCenter) {
initial_y_offset = remaining_space / 2;
}
}
// 3. 设置起始 Y 坐标
self.current_y = self.origin.y() + initial_y_offset;
// 4. 应用布局到每个元素
std::apply([&self](auto&... d) { ((self.make(d)), ...); }, drawable);
}
auto make(drawable_trait auto& drawable) {
const auto container_cross_align = align; // 非主轴对齐 (水平)
const auto container_size = size;
const auto container_origin = origin;
auto& drawable_origin = drawable.origin;
const auto drawable_w = drawable.size.width();
// 1. 主轴布局 (Y 坐标累加)
drawable_origin.setY(current_y);
current_y += drawable.size.height();
// 2. 非主轴对齐 (水平对齐)
if (container_cross_align & Qt::AlignRight) {
drawable_origin.setX(container_origin.x() + container_size.width() - drawable_w);
} else if (container_cross_align & Qt::AlignHCenter) {
const auto dx = (container_size.width() - drawable_w) / 2;
drawable_origin.setX(container_origin.x() + dx);
} else { // 默认 AlignLeft
drawable_origin.setX(container_origin.x());
}
};
};
// ----------------------------------------------------------------------
// 通用 Container 推导指引 (用于简化用户代码)
// ----------------------------------------------------------------------
template <typename... Ts>
Container(const SurfaceImpl& impl, Ts&&... args) -> Container<SurfaceImpl, Ts...>;
template <typename... Ts>
Container(const BufferImpl& impl, Ts&&... args) -> Container<BufferImpl, Ts...>;
template <typename... Ts>
Container(const BoxImpl& impl, Ts&&... args) -> Container<BoxImpl, Ts...>;
template <typename... Ts>
Container(const RowImpl& impl, Ts&&... args) -> Container<RowImpl, Ts...>;
template <typename... Ts>
Container(const ColImpl& impl, Ts&&... args) -> Container<ColImpl, Ts...>;
// ----------------------------------------------------------------------
// Paint 类型导出
// ----------------------------------------------------------------------
namespace Paint {
template <drawable_trait... Ts>
using Surface = Container<SurfaceImpl, Ts...>;
template <drawable_trait... Ts>
using Buffer = Container<BufferImpl, Ts...>;
template <drawable_trait... Ts>
using Box = Container<BoxImpl, Ts...>;
template <drawable_trait... Ts>
using Row = Container<RowImpl, Ts...>;
template <drawable_trait... Ts>
using Col = Container<ColImpl, Ts...>;
}
} // namespace creeper::painter