添加creeper-qt最新依赖

This commit is contained in:
2025-11-25 15:59:47 +08:00
parent 0ec07218ab
commit fb1a30fc94
89 changed files with 8520 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
#pragma once
#include "creeper-qt/utility/wrapper/common.hh"
#include "creeper-qt/utility/wrapper/property.hh"
namespace creeper::button::pro {
struct Button { };
using Token = common::Token<Button>;
template <class Button>
concept trait = std::derived_from<Button, Token>;
using Text = common::pro::Text<Token>;
using TextColor = common::pro::TextColor<Token>;
using Radius = common::pro::Radius<Token>;
using BorderWidth = common::pro::BorderWidth<Token>;
using BorderColor = common::pro::BorderColor<Token>;
using Background = common::pro::Background<Token>;
using WaterColor = common::pro::WaterColor<Token>;
template <typename Callback>
using Clickable = common::pro::Clickable<Callback, Token>;
CREEPER_DEFINE_CHECKER(trait)
}

View File

@@ -0,0 +1,172 @@
#include "filled-button.hh"
#include "creeper-qt/utility/animation/math.hh"
#include "creeper-qt/utility/animation/state/pid.hh"
#include "creeper-qt/utility/animation/transition.hh"
#include "creeper-qt/utility/animation/water-ripple.hh"
#include "creeper-qt/utility/painter/helper.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include <qevent.h>
namespace creeper::filled_button::internal {
constexpr auto kWaterSpeed = double { 5.0 };
struct FilledButton::Impl {
public:
bool enable_water_ripple = true;
double water_ripple_step = 5.;
double radius = -1;
QColor text_color = Qt::black;
QColor background = Qt::white;
QColor border_color = Qt::red;
double border_width = 0;
QColor water_color = Qt::black;
QColor kHoverColor = QColor { 0, 0, 0, 30 };
bool is_mouse_hover = false;
Animatable animatable;
WaterRippleRenderer water_ripple;
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> hover_color;
public:
explicit Impl(QAbstractButton& self)
: animatable { self }
, water_ripple { animatable, kWaterSpeed } {
{
auto state = std::make_shared<PidState<Eigen::Vector4d>>();
state->config.kp = 20;
state->config.ki = 0;
state->config.kd = 0;
hover_color = make_transition(animatable, std::move(state));
}
}
void paint_event(QAbstractButton& self, QPaintEvent* event) {
const auto radius = this->radius < 0
? std::min<double>(self.rect().height(), self.rect().width()) / 2
: this->radius;
const auto button_path = make_rounded_rectangle_path(self.rect(), radius);
const auto hover_color = from_vector4(*this->hover_color);
auto painter = QPainter { &self };
util::PainterHelper { painter }
.set_render_hint(QPainter::RenderHint::Antialiasing)
.set_opacity(1.)
.rounded_rectangle(background, border_color, border_width, self.rect(), radius, radius)
.apply(water_ripple.renderer(button_path, water_color))
.set_opacity(1.)
.rounded_rectangle(hover_color, Qt::transparent, 0, self.rect(), radius, radius)
.set_opacity(1.)
.simple_text(self.text(), self.font(), text_color, self.rect(), Qt::AlignCenter)
.done();
}
void mouse_release_event(QAbstractButton& self, QMouseEvent* event) {
if (enable_water_ripple) {
const auto center_point = event->pos();
const auto max_distance = std::max<double>(self.width(), self.height());
water_ripple.clicked(center_point, max_distance);
}
}
void enter_event(QAbstractButton& self, qt::EnterEvent* event) {
hover_color->transition_to(from_color(kHoverColor));
is_mouse_hover = true;
}
void leave_event(QAbstractButton& self, QEvent* event) {
hover_color->transition_to(from_color(Qt::transparent));
is_mouse_hover = false;
}
private:
static QPainterPath make_rounded_rectangle_path(const QRectF& rect, double radius) {
auto path = QPainterPath {};
path.addRoundedRect(rect, radius, radius);
return path;
}
};
FilledButton::FilledButton()
: pimpl(std::make_unique<Impl>(*this)) { }
FilledButton::~FilledButton() = default;
void FilledButton::set_color_scheme(const ColorScheme& color_scheme) {
pimpl->background = color_scheme.primary;
pimpl->text_color = color_scheme.on_primary;
if (color_scheme.primary.lightness() > 128) {
pimpl->water_color = color_scheme.primary.darker(130);
pimpl->kHoverColor = QColor { 000, 000, 000, 30 };
} else {
pimpl->water_color = color_scheme.primary.lighter(130);
pimpl->kHoverColor = QColor { 255, 255, 255, 30 };
}
pimpl->water_color.setAlphaF(0.4);
update();
}
void FilledButton::load_theme_manager(ThemeManager& manager) {
manager.append_handler(this, [this](const ThemeManager& manager) {
const auto color_mode = manager.color_mode();
const auto theme_pack = manager.theme_pack();
const auto color_scheme = color_mode == ColorMode::LIGHT //
? theme_pack.light
: theme_pack.dark;
set_color_scheme(color_scheme);
});
}
// 属性设置接口实现
void FilledButton::set_radius(double radius) { pimpl->radius = radius; }
void FilledButton::set_border_width(double border) { pimpl->border_width = border; }
void FilledButton::set_border_color(const QColor& color) { pimpl->border_color = color; }
void FilledButton::set_water_color(const QColor& color) { pimpl->water_color = color; }
void FilledButton::set_text_color(const QColor& color) { pimpl->text_color = color; }
void FilledButton::set_background(const QColor& color) { pimpl->background = color; }
void FilledButton::set_hover_color(const QColor& color) { pimpl->kHoverColor = color; }
void FilledButton::set_water_ripple_status(bool enable) { pimpl->enable_water_ripple = enable; }
void FilledButton::set_water_ripple_step(double step) { pimpl->water_ripple_step = step; }
// Qt 接口重载
void FilledButton::mouseReleaseEvent(QMouseEvent* event) {
pimpl->mouse_release_event(*this, event);
QAbstractButton::mouseReleaseEvent(event);
}
void FilledButton::paintEvent(QPaintEvent* event) {
pimpl->paint_event(*this, event);
/* Disable QAbstractButton::paintEvent */;
}
void FilledButton::enterEvent(qt::EnterEvent* event) {
pimpl->enter_event(*this, event);
QAbstractButton::enterEvent(event);
}
void FilledButton::leaveEvent(QEvent* event) {
pimpl->leave_event(*this, event);
QAbstractButton::leaveEvent(event);
}
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "creeper-qt/utility/qt_wrapper/enter-event.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/utility/wrapper/pimpl.hh"
#include "creeper-qt/utility/wrapper/property.hh"
#include "creeper-qt/utility/wrapper/widget.hh"
#include "creeper-qt/widget/buttons/button.hh"
#include "qabstractbutton.h"
namespace creeper::filled_button::internal {
class FilledButton : public QAbstractButton {
CREEPER_PIMPL_DEFINITION(FilledButton);
public:
void set_color_scheme(const ColorScheme& pack);
void load_theme_manager(ThemeManager& manager);
void set_radius(double radius);
void set_border_width(double border);
void set_water_color(const QColor& color);
void set_border_color(const QColor& color);
void set_text_color(const QColor& color);
void set_background(const QColor& color);
void set_hover_color(const QColor& color);
void set_water_ripple_status(bool enable);
void set_water_ripple_step(double step);
protected:
void mouseReleaseEvent(QMouseEvent* event) override;
void enterEvent(qt::EnterEvent* event) override;
void leaveEvent(QEvent* event) override;
void paintEvent(QPaintEvent* event) override;
};
}
namespace creeper::filled_button::pro {
using namespace button::pro;
using namespace widget::pro;
using namespace theme::pro;
}
namespace creeper {
using FilledButton = Declarative<filled_button::internal::FilledButton,
CheckerOr<button::pro::checker, widget::pro::checker, theme::pro::checker>>;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "filled-button.hh"
namespace creeper::filled_tonal_button::internal {
class FilledTonalButton : public FilledButton {
public:
void set_color_scheme(const ColorScheme& color_scheme) {
set_background(color_scheme.secondary_container);
set_text_color(color_scheme.on_secondary_container);
auto water_color = QColor {};
if (color_scheme.primary.lightness() > 128) {
water_color = color_scheme.primary.darker(130);
set_hover_color(QColor { 0, 0, 0, 30 });
} else {
water_color = color_scheme.primary.lighter(130);
set_hover_color(QColor { 255, 255, 255, 30 });
}
water_color.setAlphaF(0.25);
set_water_color(water_color);
update();
}
void load_theme_manager(ThemeManager& manager) {
manager.append_handler(this,
[this](const ThemeManager& manager) { set_color_scheme(manager.color_scheme()); });
}
};
}
namespace creeper {
namespace filled_tonal_button::pro {
using namespace filled_button::pro;
}
using FilledTonalButton =
Declarative<filled_tonal_button::internal::FilledTonalButton, FilledButton::Checker>;
}

View File

@@ -0,0 +1,44 @@
#include "icon-button.impl.hh"
IconButton::IconButton()
: pimpl(std::make_unique<Impl>(*this)) { }
IconButton::~IconButton() = default;
void IconButton::set_color_scheme(const ColorScheme& scheme) noexcept {
pimpl->set_color_scheme(*this, scheme);
}
void IconButton::load_theme_manager(ThemeManager& manager) noexcept {
pimpl->load_theme_manager(*this, manager);
}
void IconButton::enterEvent(qt::EnterEvent* event) {
pimpl->enter_event(*this, *event);
QAbstractButton::enterEvent(event);
}
void IconButton::leaveEvent(QEvent* event) {
pimpl->leave_event(*this, *event);
QAbstractButton::leaveEvent(event);
}
void IconButton::paintEvent(QPaintEvent* event) { pimpl->paint_event(*this, *event); }
void IconButton::set_icon(const QString& icon) noexcept { pimpl->font_icon = icon; }
void IconButton::set_icon(const QIcon& icon) noexcept { QAbstractButton::setIcon(icon); }
void IconButton::set_types(Types types) noexcept { pimpl->set_types_type(*this, types); }
void IconButton::set_shape(Shape shape) noexcept { pimpl->set_shape_type(*this, shape); }
void IconButton::set_color(Color color) noexcept { pimpl->set_color_type(*this, color); }
void IconButton::set_width(Width width) noexcept { pimpl->set_width_type(*this, width); }
auto IconButton::types_enum() const noexcept -> Types { return pimpl->types; }
auto IconButton::shape_enum() const noexcept -> Shape { return pimpl->shape; }
auto IconButton::color_enum() const noexcept -> Color { return pimpl->color; }
auto IconButton::width_enum() const noexcept -> Width { return pimpl->width; }
auto IconButton::selected() const noexcept -> bool {
return pimpl->types == Types::TOGGLE_SELECTED;
}
auto IconButton::set_selected(bool selected) noexcept -> void {
set_types(selected ? Types::TOGGLE_SELECTED : Types::TOGGLE_UNSELECTED);
};

View File

@@ -0,0 +1,133 @@
#pragma once
#include <qabstractbutton.h>
#include <qpainter.h>
#include "creeper-qt/utility/qt_wrapper/enter-event.hh"
#include "creeper-qt/utility/theme/theme.hh"
#include "creeper-qt/utility/wrapper/common.hh"
#include "creeper-qt/utility/wrapper/pimpl.hh"
#include "creeper-qt/utility/wrapper/property.hh"
#include "creeper-qt/utility/wrapper/widget.hh"
namespace creeper::icon_button::internal {
class IconButton : public QAbstractButton {
CREEPER_PIMPL_DEFINITION(IconButton);
public:
enum class Types { DEFAULT, TOGGLE_UNSELECTED, TOGGLE_SELECTED };
enum class Shape { DEFAULT_ROUND, SQUARE };
enum class Color { DEFAULT_FILLED, TONAL, OUTLINED, STANDARD };
enum class Width { DEFAULT, NARROW, WIDE };
/// @brief
/// 依照文档 https://m3.material.io/components/icon-buttons/specs
/// 给出如下标准容器尺寸,图标尺寸和字体大小
/// @note
/// 该组件支持 Material Symbols只要安装相关字体即可使用下面的
/// FontIcon Size 也是根据字体的大小而定的, utility/material-icon.hh
/// 文件中有一些预定义的字体和图标编码
// Extra Small
static constexpr auto kExtraSmallContainerSize = QSize { 32, 32 };
static constexpr auto kExtraSmallIconSize = QSize { 20, 20 };
static constexpr auto kExtraSmallFontIconSize = int { 15 };
// Small
static constexpr auto kSmallContainerSize = QSize { 40, 40 };
static constexpr auto kSmallIconSize = QSize { 24, 24 };
static constexpr auto kSmallFontIconSize = int { 18 };
// Medium
static constexpr auto kMediumContainerSize = QSize { 56, 56 };
static constexpr auto kMediumIconSize = QSize { 24, 24 };
static constexpr auto kMediumFontIconSize = int { 18 };
// Large
static constexpr auto kLargeContainerSize = QSize { 96, 96 };
static constexpr auto kLargeIconSize = QSize { 32, 32 };
static constexpr auto kLargeFontIconSize = int { 24 };
// Extra Large
static constexpr auto kExtraLargeContainerSize = QSize { 136, 136 };
static constexpr auto kExtraLargeIconSize = QSize { 40, 40 };
static constexpr auto kExtraLargeFontIconSize = int { 32 };
public:
auto set_color_scheme(const ColorScheme&) noexcept -> void;
auto load_theme_manager(ThemeManager&) noexcept -> void;
auto set_icon(const QString&) noexcept -> void;
auto set_icon(const QIcon&) noexcept -> void;
auto set_types(Types) noexcept -> void;
auto set_shape(Shape) noexcept -> void;
auto set_color(Color) noexcept -> void;
auto set_width(Width) noexcept -> void;
auto types_enum() const noexcept -> Types;
auto shape_enum() const noexcept -> Shape;
auto color_enum() const noexcept -> Color;
auto width_enum() const noexcept -> Width;
auto selected() const noexcept -> bool;
auto set_selected(bool) noexcept -> void;
// TODO: 详细的颜色自定义接口有缘再写
protected:
auto enterEvent(qt::EnterEvent*) -> void override;
auto leaveEvent(QEvent*) -> void override;
auto paintEvent(QPaintEvent*) -> void override;
};
}
namespace creeper::icon_button::pro {
using Token = common::Token<internal::IconButton>;
using Icon =
creeper::DerivedProp<Token, QIcon, [](auto& self, const auto& v) { self.set_icon(v); }>;
using FontIcon =
creeper::DerivedProp<Token, QString, [](auto& self, const auto& v) { self.set_icon(v); }>;
using Color = creeper::SetterProp<Token, internal::IconButton::Color,
[](auto& self, const auto& v) { self.set_color(v); }>;
using Shape = creeper::SetterProp<Token, internal::IconButton::Shape,
[](auto& self, const auto& v) { self.set_shape(v); }>;
using Types = creeper::SetterProp<Token, internal::IconButton::Types,
[](auto& self, const auto& v) { self.set_types(v); }>;
using Width = creeper::SetterProp<Token, internal::IconButton::Width,
[](auto& self, const auto& v) { self.set_width(v); }>;
constexpr auto ColorFilled = Color { internal::IconButton::Color::DEFAULT_FILLED };
constexpr auto ColorOutlined = Color { internal::IconButton::Color::OUTLINED };
constexpr auto ColorStandard = Color { internal::IconButton::Color::STANDARD };
constexpr auto ColorTonal = Color { internal::IconButton::Color::TONAL };
constexpr auto ShapeRound = Shape { internal::IconButton::Shape::DEFAULT_ROUND };
constexpr auto ShapeSquare = Shape { internal::IconButton::Shape::SQUARE };
constexpr auto TypesDefault = Types { internal::IconButton::Types::DEFAULT };
constexpr auto TypesToggleSelected = Types { internal::IconButton::Types::TOGGLE_SELECTED };
constexpr auto TypesToggleUnselected = Types { internal::IconButton::Types::TOGGLE_UNSELECTED };
constexpr auto WidthDefault = Width { internal::IconButton::Width::DEFAULT };
constexpr auto WidthNarrow = Width { internal::IconButton::Width::NARROW };
constexpr auto WidthWide = Width { internal::IconButton::Width::WIDE };
template <typename Callback>
using Clickable = common::pro::Clickable<Callback, Token>;
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 IconButton = Declarative<icon_button::internal::IconButton,
CheckerOr<icon_button::pro::checker, widget::pro::checker, theme::pro::checker>>;
}

View File

@@ -0,0 +1,285 @@
#pragma once
#include "icon-button.hh"
#include "creeper-qt/utility/animation/animatable.hh"
#include "creeper-qt/utility/animation/state/pid.hh"
#include "creeper-qt/utility/animation/state/spring.hh"
#include "creeper-qt/utility/animation/transition.hh"
#include "creeper-qt/utility/animation/water-ripple.hh"
#include "creeper-qt/utility/painter/helper.hh"
using namespace creeper::icon_button::internal;
constexpr auto kHoverOpacity = double { 0.1 };
constexpr auto kWaterOpacity = double { 0.4 };
constexpr auto kWidthRatio = double { 1.25 };
constexpr auto kOutlineWidth = double { 1.5 };
constexpr auto kSquareRatio = double { 0.5 };
constexpr double kp = 15.0, ki = 0.0, kd = 0.0;
constexpr auto kSpringK = double { 400.0 };
constexpr auto kSpringD = double { 15.0 };
constexpr auto kThreshold1D = double { 1e-1 };
constexpr auto kWaterSpeed = double { 5.0 };
struct IconButton::Impl {
bool is_hovered = false;
QString font_icon {};
Types types { Types::DEFAULT };
Shape shape { Shape::DEFAULT_ROUND };
Color color { Color::DEFAULT_FILLED };
Width width { Width::DEFAULT };
QColor container_color = Qt::white;
QColor container_color_unselected = Qt::white;
QColor container_color_selected = Qt::white;
QColor outline_color = Qt::gray;
QColor outline_color_unselected = Qt::gray;
QColor outline_color_selected = Qt::gray;
QColor icon_color = Qt::black;
QColor icon_color_unselected = Qt::black;
QColor icon_color_selected = Qt::black;
QColor hover_color = Qt::gray;
QColor hover_color_unselected = Qt::gray;
QColor hover_color_selected = Qt::gray;
QColor water_color = Qt::gray;
Animatable animatable;
WaterRippleRenderer water_ripple;
std::unique_ptr<TransitionValue<SpringState<double>>> now_container_radius;
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> now_color_container;
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> now_color_icon;
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> now_color_outline;
explicit Impl(IconButton& self) noexcept
: animatable { self }
, water_ripple { animatable, kWaterSpeed } {
{
auto state = std::make_shared<SpringState<double>>();
state->config.epsilon = kThreshold1D;
state->config.k = kSpringK;
state->config.d = kSpringD;
now_container_radius = make_transition(animatable, std::move(state));
}
{
constexpr auto make_state = [] {
auto state = std::make_shared<PidState<Eigen::Vector4d>>();
state->config.kp = kp;
state->config.ki = ki;
state->config.kd = kd;
return state;
};
now_color_container = make_transition(animatable, make_state());
now_color_icon = make_transition(animatable, make_state());
now_color_outline = make_transition(animatable, make_state());
}
QObject::connect(&self, &IconButton::clicked, [this, &self] {
if (types == Types::DEFAULT) {
const auto center_point = self.mapFromGlobal(QCursor::pos());
const auto max_distance = std::max(self.width(), self.height());
water_ripple.clicked(center_point, max_distance);
}
toggle_status();
update_animation_status(self);
});
}
auto enter_event(IconButton& self, const QEvent& event) {
self.setCursor(Qt::PointingHandCursor);
is_hovered = true;
}
auto leave_event(IconButton& self, const QEvent& event) { is_hovered = false; }
auto paint_event(IconButton& self, const QPaintEvent& event) {
// TODO: 做计算数据缓存优化,特别是 Resize 相关的计算
const auto icon = self.icon();
const auto color_container = from_vector4(*now_color_container);
const auto color_icon = from_vector4(*now_color_icon);
const auto color_outline = from_vector4(*now_color_outline);
const auto hover_color = is_hovered ? get_hover_color() : Qt::transparent;
const auto container_radius = *now_container_radius;
const auto container_rect = container_rectangle(self);
auto clip_path = QPainterPath {};
clip_path.addRoundedRect(container_rect, container_radius, container_radius);
auto renderer = QPainter { &self };
util::PainterHelper { renderer }
.set_render_hint(QPainter::Antialiasing)
.rounded_rectangle(color_container, color_outline, kOutlineWidth, container_rect,
container_radius, container_radius)
.apply(water_ripple.renderer(clip_path, water_color))
.simple_text(font_icon, self.font(), color_icon, container_rect, Qt::AlignCenter)
.rounded_rectangle(
hover_color, Qt::transparent, 0, container_rect, container_radius, container_radius)
.done();
}
auto set_types_type(IconButton& self, Types types) {
this->types = types;
update_animation_status(self);
}
auto set_color_type(IconButton& self, Color color) {
this->color = color;
update_animation_status(self);
}
auto set_shape_type(IconButton& self, Shape shape) {
this->shape = shape;
update_animation_status(self);
}
auto set_width_type(IconButton& self, Width width) {
this->width = width;
update_animation_status(self);
}
auto set_color_scheme(IconButton& self, const ColorScheme& scheme) {
switch (color) {
case Color::DEFAULT_FILLED:
container_color = scheme.primary;
icon_color = scheme.on_primary;
outline_color = Qt::transparent;
container_color_unselected = scheme.surface_container_high;
icon_color_unselected = scheme.primary;
outline_color_unselected = Qt::transparent;
container_color_selected = scheme.primary;
icon_color_selected = scheme.on_primary;
outline_color_selected = Qt::transparent;
break;
case Color::TONAL:
container_color = scheme.secondary_container;
icon_color = scheme.on_secondary_container;
outline_color = Qt::transparent;
container_color_unselected = scheme.surface_container_high;
icon_color_unselected = scheme.surface_variant;
outline_color_unselected = Qt::transparent;
container_color_selected = scheme.secondary_container;
icon_color_selected = scheme.on_secondary_container;
outline_color_selected = Qt::transparent;
break;
case Color::OUTLINED:
container_color = Qt::transparent;
outline_color = scheme.outline_variant;
icon_color = scheme.on_surface_variant;
container_color_unselected = Qt::transparent;
outline_color_unselected = scheme.outline_variant;
icon_color_unselected = scheme.surface_variant;
container_color_selected = scheme.inverse_surface;
outline_color_selected = scheme.inverse_surface;
icon_color_selected = scheme.inverse_on_surface;
break;
case Color::STANDARD:
container_color = Qt::transparent;
outline_color = Qt::transparent;
icon_color = scheme.on_surface_variant;
container_color_unselected = Qt::transparent;
outline_color_unselected = Qt::transparent;
icon_color_unselected = scheme.on_surface_variant;
container_color_selected = Qt::transparent;
outline_color_selected = Qt::transparent;
icon_color_selected = scheme.primary;
break;
}
hover_color = icon_color;
hover_color.setAlphaF(kHoverOpacity);
hover_color_selected = icon_color_selected;
hover_color_selected.setAlphaF(kHoverOpacity);
hover_color_unselected = icon_color_unselected;
hover_color_unselected.setAlphaF(kHoverOpacity);
water_color = icon_color;
water_color.setAlphaF(kWaterOpacity);
update_animation_status(self);
}
auto load_theme_manager(IconButton& self, ThemeManager& manager) {
manager.append_handler(&self, [this, &self](const ThemeManager& manager) {
set_color_scheme(self, manager.color_scheme());
});
}
private:
auto update_animation_status(IconButton& self) -> void {
const auto container_color_target = (types == Types::DEFAULT) ? container_color : //
(types == Types::TOGGLE_SELECTED) ? container_color_selected
: container_color_unselected;
now_color_container->transition_to(from_color(container_color_target));
const auto icon_color_target = (types == Types::DEFAULT) ? icon_color : //
(types == Types::TOGGLE_SELECTED) ? icon_color_selected
: icon_color_unselected;
now_color_icon->transition_to(from_color(icon_color_target));
const auto outline_color_target //
= (types == Types::DEFAULT) ? outline_color
: (types == Types::TOGGLE_SELECTED) ? outline_color_selected
: outline_color_unselected;
now_color_outline->transition_to(from_color(outline_color_target));
const auto rectangle = container_rectangle(self);
const auto radius_round = std::min<double>(rectangle.width(), rectangle.height()) / 2.;
const auto radius_target = (types == Types::TOGGLE_SELECTED || shape == Shape::SQUARE)
? radius_round * kSquareRatio
: radius_round * 1.0;
now_container_radius->transition_to(radius_target);
}
auto get_hover_color() const noexcept -> QColor {
switch (types) {
case Types::DEFAULT:
return hover_color;
case Types::TOGGLE_UNSELECTED:
return hover_color_unselected;
case Types::TOGGLE_SELECTED:
return hover_color_selected;
}
return { /* 不可能到达的彼岸 */ };
}
auto toggle_status() -> void {
if (types == Types::TOGGLE_UNSELECTED) types = Types::TOGGLE_SELECTED;
else if (types == Types::TOGGLE_SELECTED) types = Types::TOGGLE_UNSELECTED;
}
// 设计指南上的大小全是固定的,十分不自由,故转成比例
auto container_rectangle(IconButton& self) -> QRectF {
return (width == Width::DEFAULT) ? (extract_rect(self.rect(), 1, 1))
: (width == Width::NARROW) ? (extract_rect(self.rect(), 1, kWidthRatio))
: (extract_rect(self.rect(), kWidthRatio, 1));
}
};

View File

@@ -0,0 +1,43 @@
#pragma once
#include "filled-button.hh"
namespace creeper::outlined_button::internal {
class OutlinedButton : public FilledButton {
public:
void set_color_scheme(const ColorScheme& color_scheme) {
set_background(Qt::transparent);
set_border_color(color_scheme.outline);
set_text_color(color_scheme.primary);
auto hover_color = color_scheme.primary;
hover_color.setAlphaF(0.08);
set_hover_color(hover_color);
auto water_color = QColor {};
if (color_scheme.primary.lightness() > 128) {
water_color = color_scheme.primary.darker(130);
} else {
water_color = color_scheme.primary.lighter(130);
}
water_color.setAlphaF(0.25);
set_water_color(water_color);
update();
}
auto load_theme_manager(ThemeManager& manager) noexcept -> void {
set_border_width(1.5);
manager.append_handler(this,
[this](const ThemeManager& manager) { set_color_scheme(manager.color_scheme()); });
}
};
}
namespace creeper {
namespace outlined_button::pro {
using namespace filled_button::pro;
}
using OutlinedButton =
Declarative<outlined_button::internal::OutlinedButton, FilledButton::Checker>;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "filled-button.hh"
namespace creeper::text_button::internal {
class TextButton : public FilledButton {
public:
void set_color_scheme(const ColorScheme& color_scheme) {
set_background(Qt::transparent);
set_text_color(color_scheme.primary);
auto hover_color = color_scheme.primary;
hover_color.setAlphaF(0.08);
set_hover_color(hover_color);
auto water_color = QColor {};
if (color_scheme.primary.lightness() > 128) {
water_color = color_scheme.primary.darker(130);
} else {
water_color = color_scheme.primary.lighter(130);
}
water_color.setAlphaF(0.25);
set_water_color(water_color);
update();
}
void load_theme_manager(ThemeManager& manager) {
manager.append_handler(this,
[this](const ThemeManager& manager) { set_color_scheme(manager.color_scheme()); });
}
};
}
namespace creeper {
namespace text_button::pro {
using namespace filled_button::pro;
}
using TextButton = Declarative<text_button::internal::TextButton, FilledButton::Checker>;
}