258 lines
8.7 KiB
C++
258 lines
8.7 KiB
C++
#pragma once
|
|
#include "creeper-qt/utility/painter/common.hh"
|
|
#include <qicon.h>
|
|
#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();
|
|
}
|
|
};
|
|
|
|
struct Icon : CommonProps {
|
|
using IconSource = qt::icon;
|
|
using FontSource = std::tuple<qt::string, qt::string>;
|
|
using MutiSource = std::variant<FontSource, IconSource>;
|
|
|
|
MutiSource source;
|
|
qt::color color = Qt::black;
|
|
|
|
auto operator()(qt::painter& painter) const noexcept {
|
|
if (size.isEmpty()) return;
|
|
|
|
painter.save();
|
|
|
|
const auto rect = qt::rect { origin, size };
|
|
std::visit(
|
|
[&](auto& value) {
|
|
using T = std::decay_t<decltype(value)>;
|
|
|
|
if constexpr (std::is_same_v<T, FontSource>) {
|
|
const auto& [font_family, code] = value;
|
|
|
|
auto font = qt::font { font_family };
|
|
font.setPointSizeF(std::min(size.height(), size.width()));
|
|
|
|
auto option = qt::text_option {};
|
|
option.setAlignment(Qt::AlignCenter);
|
|
|
|
painter.setFont(font);
|
|
painter.setPen(color);
|
|
painter.setBrush(Qt::NoBrush);
|
|
painter.drawText(rect, code, option);
|
|
} else if constexpr (std::is_same_v<T, IconSource>) {
|
|
const auto& icon_source = value;
|
|
icon_source.paint(&painter, rect.toRect());
|
|
}
|
|
},
|
|
source);
|
|
|
|
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; }>;
|
|
|
|
/// Export Icon
|
|
struct Icon : common::pro::Token {
|
|
using T = internal::Icon;
|
|
T::MutiSource source;
|
|
|
|
constexpr explicit Icon(const qt::string& font, const qt::string& code) noexcept
|
|
: source { T::FontSource { font, code } } { }
|
|
|
|
constexpr explicit Icon(const qt::icon& icon) noexcept
|
|
: source { T::IconSource { icon } } { }
|
|
|
|
auto apply(auto& self) const noexcept { self.source = source; }
|
|
};
|
|
|
|
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>>;
|
|
using Icon = Declarative<internal::Icon, CheckerOr<common::pro::checker>>;
|
|
}
|
|
|
|
}
|