1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
#include "toggle_button.h"
#include <fmt/format.h>
#include "graph/graph.h"
#include "ui/animations/animation.h"
namespace cru::ui::controls
{
using graph::CreateSolidBrush;
using animations::AnimationBuilder;
// ui length parameters of toggle button.
constexpr float half_height = 15;
constexpr float half_width = half_height * 2;
constexpr float stroke_width = 3;
constexpr float inner_circle_radius = half_height - stroke_width;
constexpr float inner_circle_x = half_width - half_height;
ToggleButton::ToggleButton() : current_circle_position_(-inner_circle_x)
{
graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(D2D1::RoundedRect(D2D1::RectF(-half_width, -half_height, half_width, half_height), half_height, half_height), &frame_path_);
on_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::DeepSkyBlue));
off_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightGray));
}
inline D2D1_POINT_2F ConvertPoint(const Point& point)
{
return D2D1::Point2F(point.x, point.y);
}
bool ToggleButton::IsPointInside(const Point& point)
{
const auto size = GetSize();
const auto transform = D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2);
BOOL contains;
frame_path_->FillContainsPoint(ConvertPoint(point), transform, &contains);
if (!contains)
frame_path_->StrokeContainsPoint(ConvertPoint(point), stroke_width, nullptr, transform, &contains);
return contains != 0;
}
void ToggleButton::SetState(const bool state)
{
if (state != state_)
{
state_ = state;
float destination_x;
if (state)
destination_x = inner_circle_x;
else
destination_x = -inner_circle_x;
const auto previous_position = current_circle_position_;
const auto delta = destination_x - current_circle_position_;
constexpr auto total_time = FloatSecond(0.2);
const auto time = total_time * (std::abs(delta) / (inner_circle_x * 2));
AnimationBuilder(fmt::format(L"ToggleButton {}", reinterpret_cast<size_t>(this)), time)
.AddStepHandler(CreatePtr<animations::AnimationStepHandlerPtr>([=](animations::AnimationDelegatePtr, const double percentage)
{
current_circle_position_ = static_cast<float>(previous_position + delta * percentage);
Repaint();
})).Start();
OnToggleInternal(state);
Repaint();
}
}
void ToggleButton::Toggle()
{
SetState(!GetState());
}
void ToggleButton::OnToggle(events::ToggleEventArgs& args)
{
}
void ToggleButton::OnDraw(ID2D1DeviceContext* device_context)
{
const auto size = GetSize();
graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context)
{
if (state_)
{
device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width);
device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get());
}
else
{
device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width);
device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get());
}
});
}
void ToggleButton::OnMouseClickCore(events::MouseButtonEventArgs& args)
{
Control::OnMouseClickCore(args);
Toggle();
}
Size ToggleButton::OnMeasure(const Size& available_size)
{
const auto layout_params = GetLayoutParams();
auto&& get_measure_length = [](const MeasureLength& layout_length, const float available_length, const float fix_length) -> float
{
switch (layout_length.mode)
{
case MeasureMode::Exactly:
{
return std::max(std::min(layout_length.length, available_length), fix_length);
}
case MeasureMode::Stretch:
{
return std::max(available_length, fix_length);
}
case MeasureMode::Content:
{
return fix_length;
}
default:
UnreachableCode();
}
};
const Size result_size(
get_measure_length(layout_params->width, available_size.width, half_width * 2 + stroke_width),
get_measure_length(layout_params->height, available_size.height, half_height * 2 + stroke_width)
);
return result_size;
}
void ToggleButton::OnToggleInternal(bool new_state)
{
events::ToggleEventArgs args(this, this, new_state);
OnToggle(args);
toggle_event.Raise(args);
}
}
|