205 lines
7.3 KiB
C++
205 lines
7.3 KiB
C++
#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);
|
|
}
|
|
};
|