first commit
This commit is contained in:
204
modern-qt/widget/switch.impl.hh
Normal file
204
modern-qt/widget/switch.impl.hh
Normal file
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
#include "switch.hh"
|
||||
|
||||
#include <qpainter.h>
|
||||
|
||||
#include "modern-qt/utility/animation/animatable.hh"
|
||||
#include "modern-qt/utility/animation/state/pid.hh"
|
||||
#include "modern-qt/utility/animation/state/spring.hh"
|
||||
#include "modern-qt/utility/animation/transition.hh"
|
||||
#include "modern-qt/utility/painter/helper.hh"
|
||||
|
||||
using namespace creeper::_switch::internal;
|
||||
|
||||
struct Switch::Impl {
|
||||
|
||||
bool checked = false;
|
||||
bool disabled = false;
|
||||
bool hovered = false;
|
||||
|
||||
QColor track_unchecked;
|
||||
QColor track_checked;
|
||||
QColor track_unchecked_disabled;
|
||||
QColor track_checked_disabled;
|
||||
|
||||
QColor handle_unchecked;
|
||||
QColor handle_checked;
|
||||
QColor handle_unchecked_disabled;
|
||||
QColor handle_checked_disabled;
|
||||
|
||||
QColor outline_unchecked;
|
||||
QColor outline_checked;
|
||||
QColor outline_unchecked_disabled;
|
||||
QColor outline_checked_disabled;
|
||||
|
||||
QColor hover_unchecked;
|
||||
QColor hover_checked;
|
||||
|
||||
static constexpr double position_unchecked = 0.0;
|
||||
static constexpr double position_checked = 1.0;
|
||||
|
||||
Animatable animatable;
|
||||
|
||||
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> track;
|
||||
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> handle;
|
||||
std::unique_ptr<TransitionValue<PidState<Eigen::Vector4d>>> outline;
|
||||
|
||||
std::unique_ptr<TransitionValue<SpringState<double>>> position;
|
||||
|
||||
explicit Impl(Switch& self) noexcept
|
||||
: animatable(self) {
|
||||
|
||||
// @TODO: 适配进 MotionScheme
|
||||
constexpr double kp = 15.0, ki = 0.0, kd = 0.0, hz = 90;
|
||||
constexpr double k = 400, d = 22;
|
||||
{
|
||||
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;
|
||||
};
|
||||
track = make_transition(animatable, make_state());
|
||||
handle = make_transition(animatable, make_state());
|
||||
outline = make_transition(animatable, make_state());
|
||||
}
|
||||
{
|
||||
auto state = std::make_shared<SpringState<double>>();
|
||||
|
||||
state->config.k = k;
|
||||
state->config.d = d;
|
||||
|
||||
position = make_transition(animatable, std::move(state));
|
||||
}
|
||||
|
||||
QObject::connect(&self, &Switch::clicked, [this, &self] { set_checked(self, !checked); });
|
||||
}
|
||||
|
||||
void set_color_scheme(Switch& self, const ColorScheme& scheme) {
|
||||
track_unchecked = scheme.surface_variant;
|
||||
track_checked = scheme.primary;
|
||||
track_unchecked_disabled = scheme.surface_variant;
|
||||
track_checked_disabled = scheme.on_surface;
|
||||
|
||||
handle_unchecked = scheme.outline;
|
||||
handle_checked = scheme.on_primary;
|
||||
handle_unchecked_disabled = scheme.on_surface;
|
||||
handle_checked_disabled = scheme.surface;
|
||||
|
||||
outline_unchecked = scheme.outline;
|
||||
outline_checked = scheme.primary;
|
||||
outline_unchecked_disabled = scheme.on_surface;
|
||||
outline_checked_disabled = Qt::transparent;
|
||||
|
||||
hover_checked = scheme.primary;
|
||||
hover_unchecked = scheme.on_surface;
|
||||
|
||||
constexpr auto disabled_track_opacity = 0.12;
|
||||
track_unchecked_disabled.setAlphaF(disabled_track_opacity);
|
||||
track_checked_disabled.setAlphaF(disabled_track_opacity);
|
||||
outline_unchecked_disabled.setAlphaF(disabled_track_opacity);
|
||||
|
||||
constexpr auto disabled_handle_opacity = 0.38;
|
||||
handle_unchecked_disabled.setAlphaF(disabled_handle_opacity);
|
||||
handle_checked_disabled.setAlphaF(disabled_handle_opacity);
|
||||
|
||||
constexpr auto hover_opacity = 0.08;
|
||||
hover_checked.setAlphaF(hover_opacity);
|
||||
hover_unchecked.setAlphaF(hover_opacity);
|
||||
|
||||
update_switch_ui(self, checked, disabled);
|
||||
}
|
||||
|
||||
void set_disabled(Switch& self, bool on) {
|
||||
if (disabled == on) return;
|
||||
|
||||
disabled = on;
|
||||
update_switch_ui(self, checked, on);
|
||||
}
|
||||
|
||||
void set_checked(Switch& self, bool on) {
|
||||
if (disabled || checked == on) return;
|
||||
|
||||
checked = on;
|
||||
update_switch_ui(self, on);
|
||||
}
|
||||
|
||||
void enter_event(Switch& self, const QEvent& event) {
|
||||
if (!disabled) self.setCursor(Qt::PointingHandCursor);
|
||||
hovered = true;
|
||||
}
|
||||
|
||||
void leave_event(Switch& self, const QEvent& event) { hovered = false; }
|
||||
|
||||
void paint_event(Switch& self, const QPaintEvent& event) {
|
||||
const auto rect = extract_rect(self.rect(), 13, 8);
|
||||
|
||||
// 外轮廓相关变量计算
|
||||
const auto hover_radius = std::min<double>(rect.width(), rect.height()) / 2;
|
||||
|
||||
const auto outline_radius = hover_radius * 4 / 5;
|
||||
const auto outline_width = outline_radius / 8;
|
||||
const auto outline_error = hover_radius - outline_radius;
|
||||
const auto outline_rect = rect.adjusted( //
|
||||
outline_error, outline_error, -outline_error, -outline_error);
|
||||
|
||||
// 计算 handle 半径
|
||||
const auto handle_radius_checked = outline_radius / 4 * 3;
|
||||
const auto handle_radius_unchecked = outline_radius / 2 * 1;
|
||||
|
||||
const auto handle_radius =
|
||||
handle_radius_unchecked + *position * (handle_radius_checked - handle_radius_unchecked);
|
||||
|
||||
// 计算 handle 坐标
|
||||
const auto handle_point_begin = rect.topLeft() + QPointF { hover_radius, hover_radius };
|
||||
const auto handle_point_end =
|
||||
rect.topLeft() + QPointF { rect.width() - hover_radius, rect.height() - hover_radius };
|
||||
|
||||
const auto handle_point =
|
||||
handle_point_begin + *position * (handle_point_end - handle_point_begin);
|
||||
|
||||
// 选择 hover 颜色
|
||||
const auto hover_color =
|
||||
(!disabled && hovered) ? (checked ? hover_checked : hover_unchecked) : Qt::transparent;
|
||||
|
||||
auto painter = QPainter { &self };
|
||||
util::PainterHelper { painter }
|
||||
.set_render_hint(QPainter::RenderHint::Antialiasing)
|
||||
.rounded_rectangle(from_vector4(*track), from_vector4(*outline), outline_width,
|
||||
outline_rect, outline_radius, outline_radius)
|
||||
.ellipse(from_vector4(*handle), Qt::transparent, 0, handle_point, handle_radius,
|
||||
handle_radius)
|
||||
.ellipse(hover_color, Qt::transparent, 0, handle_point, hover_radius, hover_radius)
|
||||
.done();
|
||||
}
|
||||
|
||||
private:
|
||||
auto update_switch_ui(Switch&, bool checked, bool disabled = false) -> void {
|
||||
|
||||
// 添加 Switch 轨道颜色动画
|
||||
const auto track_target = disabled
|
||||
? (checked ? track_checked_disabled : track_unchecked_disabled)
|
||||
: (checked ? track_checked : track_unchecked);
|
||||
track->transition_to(from_color(track_target));
|
||||
|
||||
// 添加 Switch 指示器颜色动画
|
||||
const auto handle_target = disabled
|
||||
? (checked ? handle_checked_disabled : handle_unchecked_disabled)
|
||||
: (checked ? handle_checked : handle_unchecked);
|
||||
handle->transition_to(from_color(handle_target));
|
||||
|
||||
// 添加 Switch 外边框颜色动画
|
||||
const auto outline_target = disabled
|
||||
? (checked ? outline_checked_disabled : outline_unchecked_disabled)
|
||||
: (checked ? outline_checked : outline_unchecked);
|
||||
outline->transition_to(from_color(outline_target));
|
||||
|
||||
// 添加 Switch 运动动画
|
||||
const auto position_target = checked ? position_checked : position_unchecked;
|
||||
position->transition_to(position_target);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user