#pragma once #include "modern-qt/utility/painter/common.hh" namespace creeper::painter { // 核心容器结构体,现在继承自 Impl,使其满足 drawable_trait (假设 Impl 继承了所需的属性) template struct Container : public Impl { std::tuple...> drawable; // 唯一构造函数:接受 Impl 实例和可变参数包 constexpr explicit Container(const Impl& impl, Ts&&... drawable) : Impl { impl } , drawable { std::make_tuple(std::forward(drawable)...) } { } auto operator()(qt::painter& painter) requires(std::invocable && ...) { render(painter); } auto render(qt::painter& painter) noexcept requires(std::invocable && ...) { constexpr auto has_unique = requires { // static_cast(*this).unique_render(painter, drawable); }; if constexpr (has_unique) { static_cast(*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 auto make_layout(this auto& self, std::tuple& 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 auto unique_render(qt::painter& main_painter, std::tuple& 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 auto make_layout(this auto& self, std::tuple& 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 auto make_layout(this auto& self, std::tuple& 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 Container(const SurfaceImpl& impl, Ts&&... args) -> Container; template Container(const BufferImpl& impl, Ts&&... args) -> Container; template Container(const BoxImpl& impl, Ts&&... args) -> Container; template Container(const RowImpl& impl, Ts&&... args) -> Container; template Container(const ColImpl& impl, Ts&&... args) -> Container; // ---------------------------------------------------------------------- // Paint 类型导出 // ---------------------------------------------------------------------- namespace Paint { template using Surface = Container; template using Buffer = Container; template using Box = Container; template using Row = Container; template using Col = Container; } } // namespace creeper::painter