feat:data slove and update heatmap
This commit is contained in:
@@ -1,98 +1,98 @@
|
||||
#pragma once
|
||||
#include "modern-qt/utility/wrapper/property.hh"
|
||||
|
||||
#include <qpainter.h>
|
||||
|
||||
namespace creeper::qt {
|
||||
using painter = QPainter;
|
||||
using point = QPointF;
|
||||
using size = QSizeF;
|
||||
using rect = QRectF;
|
||||
using color = QColor;
|
||||
using real = qreal;
|
||||
using align = Qt::Alignment;
|
||||
using string = QString;
|
||||
using font = QFont;
|
||||
using text_option = QTextOption;
|
||||
}
|
||||
|
||||
namespace creeper::painter {
|
||||
|
||||
template <class T>
|
||||
concept common_trait = requires(T t) {
|
||||
{ auto { t.origin } } -> std::same_as<qt::point>;
|
||||
{ auto { t.size } } -> std::same_as<qt::size>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept container_trait = requires(T t) {
|
||||
{ auto { t.align } } -> std::same_as<qt::align>;
|
||||
} && common_trait<T>;
|
||||
|
||||
template <class T>
|
||||
concept shape_trait = requires(T t) {
|
||||
{ auto { t.color_container } } -> std::same_as<qt::color>;
|
||||
{ auto { t.color_outline } } -> std::same_as<qt::color>;
|
||||
{ auto { t.thickness_outline } } -> std::same_as<qt::real>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept drawable_trait = common_trait<T> && std::invocable<T, qt::painter&>;
|
||||
|
||||
struct CommonProps {
|
||||
qt::point origin = qt::point { 0, 0 };
|
||||
qt::size size = qt::size { 0, 0 };
|
||||
auto rect() const { return qt::rect { origin, size }; }
|
||||
};
|
||||
|
||||
struct ContainerProps {
|
||||
qt::size size = qt::size { 0, 0 };
|
||||
qt::align align = qt::align {};
|
||||
qt::point origin = qt::point { 0, 0 };
|
||||
auto rect() const { return qt::rect { origin, size }; }
|
||||
};
|
||||
|
||||
struct ShapeProps {
|
||||
qt::color container_color = Qt::transparent;
|
||||
qt::color outline_color = Qt::transparent;
|
||||
qt::real outline_width = 0;
|
||||
};
|
||||
|
||||
}
|
||||
namespace creeper::painter::common::pro {
|
||||
|
||||
struct Token { };
|
||||
|
||||
using Size = DerivedProp<Token, qt::size, [](auto& self, auto const& v) { self.size = v; }>;
|
||||
using Origin = DerivedProp<Token, qt::point, [](auto& self, auto const& v) { self.origin = v; }>;
|
||||
|
||||
using ContainerColor =
|
||||
SetterProp<Token, qt::color, [](auto& self, auto const& v) { self.container_color = v; }>;
|
||||
using OutlineColor =
|
||||
SetterProp<Token, qt::color, [](auto& self, auto const& v) { self.outline_color = v; }>;
|
||||
using OutlineWidth =
|
||||
SetterProp<Token, qt::real, [](auto& self, auto const& v) { self.outline_width = v; }>;
|
||||
|
||||
struct Outline : Token {
|
||||
qt::color color;
|
||||
qt::real width;
|
||||
Outline(const qt::color& color, qt::real width)
|
||||
: color { color }
|
||||
, width { width } { }
|
||||
auto apply(auto& self) {
|
||||
self.outline_color = color;
|
||||
self.outline_width = width;
|
||||
}
|
||||
};
|
||||
|
||||
/// Alias
|
||||
|
||||
using Fill = ContainerColor;
|
||||
|
||||
/// Export
|
||||
|
||||
template <class T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
#pragma once
|
||||
#include "modern-qt/utility/wrapper/property.hh"
|
||||
|
||||
#include <qpainter.h>
|
||||
|
||||
namespace creeper::qt {
|
||||
using painter = QPainter;
|
||||
using point = QPointF;
|
||||
using size = QSizeF;
|
||||
using rect = QRectF;
|
||||
using color = QColor;
|
||||
using real = qreal;
|
||||
using align = Qt::Alignment;
|
||||
using string = QString;
|
||||
using font = QFont;
|
||||
using text_option = QTextOption;
|
||||
}
|
||||
|
||||
namespace creeper::painter {
|
||||
|
||||
template <class T>
|
||||
concept common_trait = requires(T t) {
|
||||
{ auto { t.origin } } -> std::same_as<qt::point>;
|
||||
{ auto { t.size } } -> std::same_as<qt::size>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept container_trait = requires(T t) {
|
||||
{ auto { t.align } } -> std::same_as<qt::align>;
|
||||
} && common_trait<T>;
|
||||
|
||||
template <class T>
|
||||
concept shape_trait = requires(T t) {
|
||||
{ auto { t.color_container } } -> std::same_as<qt::color>;
|
||||
{ auto { t.color_outline } } -> std::same_as<qt::color>;
|
||||
{ auto { t.thickness_outline } } -> std::same_as<qt::real>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept drawable_trait = common_trait<T> && std::invocable<T, qt::painter&>;
|
||||
|
||||
struct CommonProps {
|
||||
qt::point origin = qt::point { 0, 0 };
|
||||
qt::size size = qt::size { 0, 0 };
|
||||
auto rect() const { return qt::rect { origin, size }; }
|
||||
};
|
||||
|
||||
struct ContainerProps {
|
||||
qt::size size = qt::size { 0, 0 };
|
||||
qt::align align = qt::align {};
|
||||
qt::point origin = qt::point { 0, 0 };
|
||||
auto rect() const { return qt::rect { origin, size }; }
|
||||
};
|
||||
|
||||
struct ShapeProps {
|
||||
qt::color container_color = Qt::transparent;
|
||||
qt::color outline_color = Qt::transparent;
|
||||
qt::real outline_width = 0;
|
||||
};
|
||||
|
||||
}
|
||||
namespace creeper::painter::common::pro {
|
||||
|
||||
struct Token { };
|
||||
|
||||
using Size = DerivedProp<Token, qt::size, [](auto& self, auto const& v) { self.size = v; }>;
|
||||
using Origin = DerivedProp<Token, qt::point, [](auto& self, auto const& v) { self.origin = v; }>;
|
||||
|
||||
using ContainerColor =
|
||||
SetterProp<Token, qt::color, [](auto& self, auto const& v) { self.container_color = v; }>;
|
||||
using OutlineColor =
|
||||
SetterProp<Token, qt::color, [](auto& self, auto const& v) { self.outline_color = v; }>;
|
||||
using OutlineWidth =
|
||||
SetterProp<Token, qt::real, [](auto& self, auto const& v) { self.outline_width = v; }>;
|
||||
|
||||
struct Outline : Token {
|
||||
qt::color color;
|
||||
qt::real width;
|
||||
Outline(const qt::color& color, qt::real width)
|
||||
: color { color }
|
||||
, width { width } { }
|
||||
auto apply(auto& self) {
|
||||
self.outline_color = color;
|
||||
self.outline_width = width;
|
||||
}
|
||||
};
|
||||
|
||||
/// Alias
|
||||
|
||||
using Fill = ContainerColor;
|
||||
|
||||
/// Export
|
||||
|
||||
template <class T>
|
||||
concept trait = std::derived_from<T, Token>;
|
||||
|
||||
CREEPER_DEFINE_CHECKER(trait);
|
||||
}
|
||||
|
||||
@@ -1,347 +1,347 @@
|
||||
#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
|
||||
#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
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#include "modern-qt/utility/painter/helper.hh"
|
||||
#include <qdebug.h>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
constexpr auto enable_print_paint_event_count = bool { false };
|
||||
|
||||
auto print_paint_event_count() noexcept -> void {
|
||||
if constexpr (enable_print_paint_event_count) {
|
||||
static auto count = std::size_t { 0 };
|
||||
qDebug() << "[PainterHelper] Paint Event:" << count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#include "modern-qt/utility/painter/helper.hh"
|
||||
#include <qdebug.h>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
constexpr auto enable_print_paint_event_count = bool { false };
|
||||
|
||||
auto print_paint_event_count() noexcept -> void {
|
||||
if constexpr (enable_print_paint_event_count) {
|
||||
static auto count = std::size_t { 0 };
|
||||
qDebug() << "[PainterHelper] Paint Event:" << count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,224 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
auto print_paint_event_count() noexcept -> void;
|
||||
|
||||
/// @brief 隐藏冗杂的细节,解放命令式的绘图调用
|
||||
class PainterHelper {
|
||||
public:
|
||||
using Renderer = std::function<void(QPainter&)>;
|
||||
|
||||
explicit PainterHelper(QPainter& painter)
|
||||
: painter(painter) {
|
||||
print_paint_event_count();
|
||||
}
|
||||
|
||||
inline void done() { }
|
||||
|
||||
inline PainterHelper& apply(const Renderer& renderer) {
|
||||
renderer(painter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& apply(const std::ranges::range auto& renderers)
|
||||
requires std::same_as<std::ranges::range_value_t<decltype(renderers)>, Renderer>
|
||||
{
|
||||
for (const auto& renderer : renderers)
|
||||
renderer(painter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
inline PainterHelper& set_brush(const QBrush& brush) {
|
||||
painter.setBrush(brush);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_pen(const QPen& pen) {
|
||||
painter.setPen(pen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_opacity(double opacity) {
|
||||
painter.setOpacity(opacity);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_render_hint(QPainter::RenderHint hint, bool on = true) {
|
||||
painter.setRenderHint(hint, on);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_render_hints(QPainter::RenderHints hint, bool on = true) {
|
||||
painter.setRenderHints(hint, on);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_clip_path(
|
||||
const QPainterPath& path, Qt::ClipOperation operation = Qt::ReplaceClip) {
|
||||
painter.setClipPath(path, operation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
inline PainterHelper& point(const QColor& color, double radius, const QPointF& point) {
|
||||
pen_only({ color, radius * 2 }).drawPoint(point);
|
||||
return *this;
|
||||
}
|
||||
inline PainterHelper& ellipse(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect) {
|
||||
|
||||
brush_only({ background }).drawEllipse(rect);
|
||||
|
||||
const auto half = border_width / 2;
|
||||
if (border_width != 0)
|
||||
pen_only({ border_color, border_width })
|
||||
.drawEllipse(rect.adjusted(half, half, -half, -half));
|
||||
|
||||
return *this;
|
||||
}
|
||||
inline PainterHelper& ellipse(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QPointF& origin, double radius_x, double radius_y) {
|
||||
|
||||
brush_only({ background }).drawEllipse(origin, radius_x, radius_y);
|
||||
|
||||
if (border_width != 0)
|
||||
pen_only({ border_color, border_width })
|
||||
.drawEllipse(origin, radius_x - border_width / 2, radius_y - border_width / 2);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect) {
|
||||
|
||||
brush_only({ background }).drawRect(rect);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
|
||||
pen_only({ border_color, border_width }).drawRect(inliner_border_rectangle);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rounded_rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect, double radius_x, double radius_y) {
|
||||
|
||||
brush_only({ background }).drawRoundedRect(rect, radius_x, radius_y);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
|
||||
pen_only({ border_color, border_width })
|
||||
.drawRoundedRect(inliner_border_rectangle, std::max(radius_x - border_width / 2, 0.),
|
||||
std::max(radius_y - border_width / 2, 0.));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rounded_rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect, double tl, double tr, double br, double bl) {
|
||||
|
||||
const auto path = make_rounded_rect_path(rect, tl, tr, br, bl);
|
||||
brush_only({ background }).drawPath(path);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
|
||||
const auto inliner = [=](double r) { return std::max(r - border_width / 2, 0.); };
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
const auto inliner_path = make_rounded_rect_path(
|
||||
inliner_border_rectangle, inliner(tl), inliner(tr), inliner(br), inliner(bl));
|
||||
|
||||
pen_only({ border_color, border_width }).drawPath(inliner_path);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Pen 是以路径为中心来绘制图片,有绘出 rect 导致画面被裁切的可能,由于是 path 类型,不好做限制
|
||||
inline PainterHelper& path(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QPainterPath& path) {
|
||||
brush_only({ background }).drawPath(path);
|
||||
if (border_width != 0) pen_only({ border_color, border_width }).drawPath(path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& pixmap(const QPixmap& pixmap, const QRectF& dst, const QRectF& src) {
|
||||
painter.drawPixmap(dst, pixmap, src);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& simple_text(const QString& text, const QFont& font, const QColor& color,
|
||||
const QRectF& rect, Qt::Alignment alignment) {
|
||||
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||
painter.setFont(font);
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen({ color });
|
||||
painter.drawText(rect, alignment, text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
QPainter& painter;
|
||||
|
||||
QPainter& pen_only(const QPen& pen) {
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(pen);
|
||||
return painter;
|
||||
}
|
||||
QPainter& brush_only(const QBrush& brush) {
|
||||
painter.setBrush(brush);
|
||||
painter.setPen(Qt::NoPen);
|
||||
return painter;
|
||||
}
|
||||
|
||||
static auto make_rounded_rect_path(
|
||||
const QRectF& rect, qreal tl, qreal tr, qreal br, qreal bl) noexcept -> QPainterPath {
|
||||
|
||||
auto path = QPainterPath {};
|
||||
|
||||
const auto half_width = rect.width() / 2.0;
|
||||
const auto half_height = rect.height() / 2.0;
|
||||
|
||||
const auto max_radius = std::min(half_width, half_height);
|
||||
|
||||
const auto clamp_radius = [&](qreal r) {
|
||||
return r < 0 ? max_radius : std::min(r, max_radius);
|
||||
};
|
||||
tl = clamp_radius(tl);
|
||||
tr = clamp_radius(tr);
|
||||
br = clamp_radius(br);
|
||||
bl = clamp_radius(bl);
|
||||
|
||||
path.moveTo(rect.topLeft() + QPointF(tl, 0));
|
||||
|
||||
path.lineTo(rect.topRight() - QPointF(tr, 0));
|
||||
path.arcTo(
|
||||
QRectF(rect.topRight().x() - 2 * tr, rect.topRight().y(), 2 * tr, 2 * tr), 90, -90);
|
||||
|
||||
path.lineTo(rect.bottomRight() - QPointF(0, br));
|
||||
path.arcTo(QRectF(rect.bottomRight().x() - 2 * br, rect.bottomRight().y() - 2 * br, 2 * br,
|
||||
2 * br),
|
||||
0, -90);
|
||||
|
||||
path.lineTo(rect.bottomLeft() + QPointF(bl, 0));
|
||||
path.arcTo(QRectF(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * bl, 2 * bl, 2 * bl),
|
||||
270, -90);
|
||||
|
||||
path.lineTo(rect.topLeft() + QPointF(0, tl));
|
||||
path.arcTo(QRectF(rect.topLeft().x(), rect.topLeft().y(), 2 * tl, 2 * tl), 180, -90);
|
||||
|
||||
path.closeSubpath();
|
||||
|
||||
return path;
|
||||
}
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
|
||||
namespace creeper::util {
|
||||
|
||||
auto print_paint_event_count() noexcept -> void;
|
||||
|
||||
/// @brief 隐藏冗杂的细节,解放命令式的绘图调用
|
||||
class PainterHelper {
|
||||
public:
|
||||
using Renderer = std::function<void(QPainter&)>;
|
||||
|
||||
explicit PainterHelper(QPainter& painter)
|
||||
: painter(painter) {
|
||||
print_paint_event_count();
|
||||
}
|
||||
|
||||
inline void done() { }
|
||||
|
||||
inline PainterHelper& apply(const Renderer& renderer) {
|
||||
renderer(painter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& apply(const std::ranges::range auto& renderers)
|
||||
requires std::same_as<std::ranges::range_value_t<decltype(renderers)>, Renderer>
|
||||
{
|
||||
for (const auto& renderer : renderers)
|
||||
renderer(painter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
inline PainterHelper& set_brush(const QBrush& brush) {
|
||||
painter.setBrush(brush);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_pen(const QPen& pen) {
|
||||
painter.setPen(pen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_opacity(double opacity) {
|
||||
painter.setOpacity(opacity);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_render_hint(QPainter::RenderHint hint, bool on = true) {
|
||||
painter.setRenderHint(hint, on);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_render_hints(QPainter::RenderHints hint, bool on = true) {
|
||||
painter.setRenderHints(hint, on);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& set_clip_path(
|
||||
const QPainterPath& path, Qt::ClipOperation operation = Qt::ReplaceClip) {
|
||||
painter.setClipPath(path, operation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
inline PainterHelper& point(const QColor& color, double radius, const QPointF& point) {
|
||||
pen_only({ color, radius * 2 }).drawPoint(point);
|
||||
return *this;
|
||||
}
|
||||
inline PainterHelper& ellipse(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect) {
|
||||
|
||||
brush_only({ background }).drawEllipse(rect);
|
||||
|
||||
const auto half = border_width / 2;
|
||||
if (border_width != 0)
|
||||
pen_only({ border_color, border_width })
|
||||
.drawEllipse(rect.adjusted(half, half, -half, -half));
|
||||
|
||||
return *this;
|
||||
}
|
||||
inline PainterHelper& ellipse(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QPointF& origin, double radius_x, double radius_y) {
|
||||
|
||||
brush_only({ background }).drawEllipse(origin, radius_x, radius_y);
|
||||
|
||||
if (border_width != 0)
|
||||
pen_only({ border_color, border_width })
|
||||
.drawEllipse(origin, radius_x - border_width / 2, radius_y - border_width / 2);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect) {
|
||||
|
||||
brush_only({ background }).drawRect(rect);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
|
||||
pen_only({ border_color, border_width }).drawRect(inliner_border_rectangle);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rounded_rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect, double radius_x, double radius_y) {
|
||||
|
||||
brush_only({ background }).drawRoundedRect(rect, radius_x, radius_y);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
|
||||
pen_only({ border_color, border_width })
|
||||
.drawRoundedRect(inliner_border_rectangle, std::max(radius_x - border_width / 2, 0.),
|
||||
std::max(radius_y - border_width / 2, 0.));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& rounded_rectangle(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QRectF& rect, double tl, double tr, double br, double bl) {
|
||||
|
||||
const auto path = make_rounded_rect_path(rect, tl, tr, br, bl);
|
||||
brush_only({ background }).drawPath(path);
|
||||
|
||||
if (border_width == 0) return *this;
|
||||
|
||||
const auto inliner = [=](double r) { return std::max(r - border_width / 2, 0.); };
|
||||
const auto inliner_border_rectangle =
|
||||
rect.adjusted(border_width / 2, border_width / 2, -border_width / 2, -border_width / 2);
|
||||
const auto inliner_path = make_rounded_rect_path(
|
||||
inliner_border_rectangle, inliner(tl), inliner(tr), inliner(br), inliner(bl));
|
||||
|
||||
pen_only({ border_color, border_width }).drawPath(inliner_path);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Pen 是以路径为中心来绘制图片,有绘出 rect 导致画面被裁切的可能,由于是 path 类型,不好做限制
|
||||
inline PainterHelper& path(const QColor& background, const QColor& border_color,
|
||||
double border_width, const QPainterPath& path) {
|
||||
brush_only({ background }).drawPath(path);
|
||||
if (border_width != 0) pen_only({ border_color, border_width }).drawPath(path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& pixmap(const QPixmap& pixmap, const QRectF& dst, const QRectF& src) {
|
||||
painter.drawPixmap(dst, pixmap, src);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PainterHelper& simple_text(const QString& text, const QFont& font, const QColor& color,
|
||||
const QRectF& rect, Qt::Alignment alignment) {
|
||||
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||
painter.setFont(font);
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen({ color });
|
||||
painter.drawText(rect, alignment, text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
QPainter& painter;
|
||||
|
||||
QPainter& pen_only(const QPen& pen) {
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(pen);
|
||||
return painter;
|
||||
}
|
||||
QPainter& brush_only(const QBrush& brush) {
|
||||
painter.setBrush(brush);
|
||||
painter.setPen(Qt::NoPen);
|
||||
return painter;
|
||||
}
|
||||
|
||||
static auto make_rounded_rect_path(
|
||||
const QRectF& rect, qreal tl, qreal tr, qreal br, qreal bl) noexcept -> QPainterPath {
|
||||
|
||||
auto path = QPainterPath {};
|
||||
|
||||
const auto half_width = rect.width() / 2.0;
|
||||
const auto half_height = rect.height() / 2.0;
|
||||
|
||||
const auto max_radius = std::min(half_width, half_height);
|
||||
|
||||
const auto clamp_radius = [&](qreal r) {
|
||||
return r < 0 ? max_radius : std::min(r, max_radius);
|
||||
};
|
||||
tl = clamp_radius(tl);
|
||||
tr = clamp_radius(tr);
|
||||
br = clamp_radius(br);
|
||||
bl = clamp_radius(bl);
|
||||
|
||||
path.moveTo(rect.topLeft() + QPointF(tl, 0));
|
||||
|
||||
path.lineTo(rect.topRight() - QPointF(tr, 0));
|
||||
path.arcTo(
|
||||
QRectF(rect.topRight().x() - 2 * tr, rect.topRight().y(), 2 * tr, 2 * tr), 90, -90);
|
||||
|
||||
path.lineTo(rect.bottomRight() - QPointF(0, br));
|
||||
path.arcTo(QRectF(rect.bottomRight().x() - 2 * br, rect.bottomRight().y() - 2 * br, 2 * br,
|
||||
2 * br),
|
||||
0, -90);
|
||||
|
||||
path.lineTo(rect.bottomLeft() + QPointF(bl, 0));
|
||||
path.arcTo(QRectF(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * bl, 2 * bl, 2 * bl),
|
||||
270, -90);
|
||||
|
||||
path.lineTo(rect.topLeft() + QPointF(0, tl));
|
||||
path.arcTo(QRectF(rect.topLeft().x(), rect.topLeft().y(), 2 * tl, 2 * tl), 180, -90);
|
||||
|
||||
path.closeSubpath();
|
||||
|
||||
return path;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,200 +1,200 @@
|
||||
#pragma once
|
||||
#include "modern-qt/utility/painter/common.hh"
|
||||
#include <qpainterpath.h>
|
||||
|
||||
namespace creeper::painter::internal {
|
||||
|
||||
struct EraseRectangle : public CommonProps {
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
|
||||
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
||||
|
||||
painter.setBrush(Qt::black);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(rect());
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
struct Rectangle : public CommonProps, ShapeProps {
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
|
||||
const auto rectangle = qt::rect { origin, size };
|
||||
|
||||
painter.setBrush(container_color);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(rectangle);
|
||||
|
||||
if (outline_width > 0) {
|
||||
const auto thickness = outline_width / 2;
|
||||
const auto inliner = rectangle.adjusted(thickness, thickness, -thickness, -thickness);
|
||||
|
||||
painter.setPen({ outline_color, outline_width });
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.drawRect(inliner);
|
||||
}
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
struct RoundedRectangle : public CommonProps, ShapeProps {
|
||||
double radius_tl = 0;
|
||||
double radius_tr = 0;
|
||||
double radius_bl = 0;
|
||||
double radius_br = 0;
|
||||
|
||||
auto set_radiuses(double r) {
|
||||
radius_tl = r;
|
||||
radius_tr = r;
|
||||
radius_bl = r;
|
||||
radius_br = r;
|
||||
}
|
||||
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const auto rect = qt::rect { origin, size };
|
||||
const auto outline_shape =
|
||||
make_rounded_rect_path(rect, radius_tl, radius_tr, radius_br, radius_bl);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(container_color);
|
||||
painter.drawPath(outline_shape);
|
||||
|
||||
if (outline_width > 0) {
|
||||
const auto thickness = outline_width;
|
||||
const auto inliner_f = [=](double r) { return std::max(r - thickness / 2, 0.); };
|
||||
const auto inliner_rect =
|
||||
rect.adjusted(thickness / 2, thickness / 2, -thickness / 2, -thickness / 2);
|
||||
const auto inliner_shape = make_rounded_rect_path(inliner_rect, inliner_f(radius_tl),
|
||||
inliner_f(radius_tr), inliner_f(radius_br), inliner_f(radius_bl));
|
||||
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen({ outline_color, outline_width });
|
||||
painter.drawPath(inliner_shape);
|
||||
}
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
static constexpr auto make_rounded_rect_path(
|
||||
const qt::rect& rect, qreal tl, qreal tr, qreal br, qreal bl) noexcept -> QPainterPath {
|
||||
|
||||
auto path = QPainterPath {};
|
||||
|
||||
const auto max_radius = std::min(rect.width(), rect.height()) / 2.0;
|
||||
const auto clamp = [&](qreal r) -> qreal {
|
||||
return r < 0 ? max_radius : std::min(r, max_radius);
|
||||
};
|
||||
|
||||
tl = clamp(tl);
|
||||
tr = clamp(tr);
|
||||
br = clamp(br);
|
||||
bl = clamp(bl);
|
||||
|
||||
const auto Arc = [](qreal x, qreal y, qreal r,
|
||||
int start_angle) -> std::tuple<qt::rect, int, int> {
|
||||
return { qt::rect(x, y, 2 * r, 2 * r), start_angle, -90 };
|
||||
};
|
||||
|
||||
path.moveTo(rect.topLeft() + qt::point(tl, 0));
|
||||
path.lineTo(rect.topRight() - qt::point(tr, 0));
|
||||
const auto [tr_rect, tr_start, tr_span] =
|
||||
Arc(rect.topRight().x() - 2 * tr, rect.topRight().y(), tr, 90);
|
||||
path.arcTo(tr_rect, tr_start, tr_span);
|
||||
|
||||
path.lineTo(rect.bottomRight() - qt::point(0, br));
|
||||
const auto [br_rect, br_start, br_span] =
|
||||
Arc(rect.bottomRight().x() - 2 * br, rect.bottomRight().y() - 2 * br, br, 0);
|
||||
path.arcTo(br_rect, br_start, br_span);
|
||||
|
||||
path.lineTo(rect.bottomLeft() + qt::point(bl, 0));
|
||||
const auto [bl_rect, bl_start, bl_span] =
|
||||
Arc(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * bl, bl, 270);
|
||||
path.arcTo(bl_rect, bl_start, bl_span);
|
||||
|
||||
path.lineTo(rect.topLeft() + qt::point(0, tl));
|
||||
const auto [tl_rect, tl_start, tl_span] =
|
||||
Arc(rect.topLeft().x(), rect.topLeft().y(), tl, 180);
|
||||
path.arcTo(tl_rect, tl_start, tl_span);
|
||||
|
||||
path.closeSubpath();
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
struct Text : CommonProps {
|
||||
qt::string text;
|
||||
qt::font font;
|
||||
qt::color color = Qt::black;
|
||||
qt::text_option text_option;
|
||||
qt::real scale = 1.;
|
||||
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
painter.scale(scale, scale);
|
||||
|
||||
const auto origin_rect = rect();
|
||||
const auto offset_x = origin_rect.x() * (1.0 - scale);
|
||||
const auto center_y = origin_rect.y() + origin_rect.height() / 2.0;
|
||||
const auto offset_y = center_y * (1.0 - scale);
|
||||
painter.translate(offset_x, offset_y);
|
||||
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(color);
|
||||
painter.setFont(font);
|
||||
|
||||
const auto scaled_rect = qt::rect {
|
||||
origin_rect.x() / scale,
|
||||
origin_rect.y() / scale,
|
||||
origin_rect.width() / scale,
|
||||
origin_rect.height() / scale,
|
||||
};
|
||||
painter.drawText(scaled_rect, text, text_option);
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
namespace creeper::painter {
|
||||
|
||||
/// Export Rounded Rectangle
|
||||
|
||||
using RadiusTL = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_tl = radius; }>;
|
||||
using RadiusTR = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_tr = radius; }>;
|
||||
using RadiusBL = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_bl = radius; }>;
|
||||
using RadiusBR = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_br = radius; }>;
|
||||
using Radiuses = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.set_radiuses(radius); }>;
|
||||
|
||||
/// Export Text
|
||||
using Text = DerivedProp<common::pro::Token, qt::string,
|
||||
[](auto& self, const auto& text) { self.text = text; }>;
|
||||
using Font = DerivedProp<common::pro::Token, qt::font,
|
||||
[](auto& self, const auto& font) { self.font = font; }>;
|
||||
using Color = DerivedProp<common::pro::Token, qt::color,
|
||||
[](auto& self, const auto& color) { self.color = color; }>;
|
||||
using Scale = SetterProp<common::pro::Token, qt::real,
|
||||
[](auto& self, const auto& scale) { self.scale = scale; }>;
|
||||
|
||||
using TextOption = DerivedProp<common::pro::Token, qt::text_option,
|
||||
[](auto& self, const auto& option) { self.text_option = option; }>;
|
||||
|
||||
namespace Paint {
|
||||
using EraseRectangle = Declarative<internal::EraseRectangle, CheckerOr<common::pro::checker>>;
|
||||
using Rectangle = Declarative<internal::Rectangle, CheckerOr<common::pro::checker>>;
|
||||
using RoundedRectangle =
|
||||
Declarative<internal::RoundedRectangle, CheckerOr<common::pro::checker>>;
|
||||
using Text = Declarative<internal::Text, CheckerOr<common::pro::checker>>;
|
||||
}
|
||||
|
||||
}
|
||||
#pragma once
|
||||
#include "modern-qt/utility/painter/common.hh"
|
||||
#include <qpainterpath.h>
|
||||
|
||||
namespace creeper::painter::internal {
|
||||
|
||||
struct EraseRectangle : public CommonProps {
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
|
||||
painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
|
||||
|
||||
painter.setBrush(Qt::black);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(rect());
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
struct Rectangle : public CommonProps, ShapeProps {
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
|
||||
const auto rectangle = qt::rect { origin, size };
|
||||
|
||||
painter.setBrush(container_color);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawRect(rectangle);
|
||||
|
||||
if (outline_width > 0) {
|
||||
const auto thickness = outline_width / 2;
|
||||
const auto inliner = rectangle.adjusted(thickness, thickness, -thickness, -thickness);
|
||||
|
||||
painter.setPen({ outline_color, outline_width });
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.drawRect(inliner);
|
||||
}
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
struct RoundedRectangle : public CommonProps, ShapeProps {
|
||||
double radius_tl = 0;
|
||||
double radius_tr = 0;
|
||||
double radius_bl = 0;
|
||||
double radius_br = 0;
|
||||
|
||||
auto set_radiuses(double r) {
|
||||
radius_tl = r;
|
||||
radius_tr = r;
|
||||
radius_bl = r;
|
||||
radius_br = r;
|
||||
}
|
||||
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const auto rect = qt::rect { origin, size };
|
||||
const auto outline_shape =
|
||||
make_rounded_rect_path(rect, radius_tl, radius_tr, radius_br, radius_bl);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(container_color);
|
||||
painter.drawPath(outline_shape);
|
||||
|
||||
if (outline_width > 0) {
|
||||
const auto thickness = outline_width;
|
||||
const auto inliner_f = [=](double r) { return std::max(r - thickness / 2, 0.); };
|
||||
const auto inliner_rect =
|
||||
rect.adjusted(thickness / 2, thickness / 2, -thickness / 2, -thickness / 2);
|
||||
const auto inliner_shape = make_rounded_rect_path(inliner_rect, inliner_f(radius_tl),
|
||||
inliner_f(radius_tr), inliner_f(radius_br), inliner_f(radius_bl));
|
||||
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen({ outline_color, outline_width });
|
||||
painter.drawPath(inliner_shape);
|
||||
}
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
static constexpr auto make_rounded_rect_path(
|
||||
const qt::rect& rect, qreal tl, qreal tr, qreal br, qreal bl) noexcept -> QPainterPath {
|
||||
|
||||
auto path = QPainterPath {};
|
||||
|
||||
const auto max_radius = std::min(rect.width(), rect.height()) / 2.0;
|
||||
const auto clamp = [&](qreal r) -> qreal {
|
||||
return r < 0 ? max_radius : std::min(r, max_radius);
|
||||
};
|
||||
|
||||
tl = clamp(tl);
|
||||
tr = clamp(tr);
|
||||
br = clamp(br);
|
||||
bl = clamp(bl);
|
||||
|
||||
const auto Arc = [](qreal x, qreal y, qreal r,
|
||||
int start_angle) -> std::tuple<qt::rect, int, int> {
|
||||
return { qt::rect(x, y, 2 * r, 2 * r), start_angle, -90 };
|
||||
};
|
||||
|
||||
path.moveTo(rect.topLeft() + qt::point(tl, 0));
|
||||
path.lineTo(rect.topRight() - qt::point(tr, 0));
|
||||
const auto [tr_rect, tr_start, tr_span] =
|
||||
Arc(rect.topRight().x() - 2 * tr, rect.topRight().y(), tr, 90);
|
||||
path.arcTo(tr_rect, tr_start, tr_span);
|
||||
|
||||
path.lineTo(rect.bottomRight() - qt::point(0, br));
|
||||
const auto [br_rect, br_start, br_span] =
|
||||
Arc(rect.bottomRight().x() - 2 * br, rect.bottomRight().y() - 2 * br, br, 0);
|
||||
path.arcTo(br_rect, br_start, br_span);
|
||||
|
||||
path.lineTo(rect.bottomLeft() + qt::point(bl, 0));
|
||||
const auto [bl_rect, bl_start, bl_span] =
|
||||
Arc(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * bl, bl, 270);
|
||||
path.arcTo(bl_rect, bl_start, bl_span);
|
||||
|
||||
path.lineTo(rect.topLeft() + qt::point(0, tl));
|
||||
const auto [tl_rect, tl_start, tl_span] =
|
||||
Arc(rect.topLeft().x(), rect.topLeft().y(), tl, 180);
|
||||
path.arcTo(tl_rect, tl_start, tl_span);
|
||||
|
||||
path.closeSubpath();
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
struct Text : CommonProps {
|
||||
qt::string text;
|
||||
qt::font font;
|
||||
qt::color color = Qt::black;
|
||||
qt::text_option text_option;
|
||||
qt::real scale = 1.;
|
||||
|
||||
auto operator()(qt::painter& painter) const noexcept {
|
||||
painter.save();
|
||||
painter.scale(scale, scale);
|
||||
|
||||
const auto origin_rect = rect();
|
||||
const auto offset_x = origin_rect.x() * (1.0 - scale);
|
||||
const auto center_y = origin_rect.y() + origin_rect.height() / 2.0;
|
||||
const auto offset_y = center_y * (1.0 - scale);
|
||||
painter.translate(offset_x, offset_y);
|
||||
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setPen(color);
|
||||
painter.setFont(font);
|
||||
|
||||
const auto scaled_rect = qt::rect {
|
||||
origin_rect.x() / scale,
|
||||
origin_rect.y() / scale,
|
||||
origin_rect.width() / scale,
|
||||
origin_rect.height() / scale,
|
||||
};
|
||||
painter.drawText(scaled_rect, text, text_option);
|
||||
|
||||
painter.restore();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
namespace creeper::painter {
|
||||
|
||||
/// Export Rounded Rectangle
|
||||
|
||||
using RadiusTL = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_tl = radius; }>;
|
||||
using RadiusTR = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_tr = radius; }>;
|
||||
using RadiusBL = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_bl = radius; }>;
|
||||
using RadiusBR = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.radius_br = radius; }>;
|
||||
using Radiuses = SetterProp<common::pro::Token, double,
|
||||
[](auto& self, auto radius) { self.set_radiuses(radius); }>;
|
||||
|
||||
/// Export Text
|
||||
using Text = DerivedProp<common::pro::Token, qt::string,
|
||||
[](auto& self, const auto& text) { self.text = text; }>;
|
||||
using Font = DerivedProp<common::pro::Token, qt::font,
|
||||
[](auto& self, const auto& font) { self.font = font; }>;
|
||||
using Color = DerivedProp<common::pro::Token, qt::color,
|
||||
[](auto& self, const auto& color) { self.color = color; }>;
|
||||
using Scale = SetterProp<common::pro::Token, qt::real,
|
||||
[](auto& self, const auto& scale) { self.scale = scale; }>;
|
||||
|
||||
using TextOption = DerivedProp<common::pro::Token, qt::text_option,
|
||||
[](auto& self, const auto& option) { self.text_option = option; }>;
|
||||
|
||||
namespace Paint {
|
||||
using EraseRectangle = Declarative<internal::EraseRectangle, CheckerOr<common::pro::checker>>;
|
||||
using Rectangle = Declarative<internal::Rectangle, CheckerOr<common::pro::checker>>;
|
||||
using RoundedRectangle =
|
||||
Declarative<internal::RoundedRectangle, CheckerOr<common::pro::checker>>;
|
||||
using Text = Declarative<internal::Text, CheckerOr<common::pro::checker>>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user