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
|
#include "toggle_button.hpp"
#include "graph/graph.hpp"
#include "ui/animations/animation.hpp"
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);
}
StringView ToggleButton::GetControlType() const
{
return control_type;
}
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));
// ReSharper disable once CppExpressionWithoutSideEffects
AnimationBuilder(Format(L"ToggleButton {}", reinterpret_cast<size_t>(this)), time)
.AddStepHandler([=](auto, const double percentage)
{
current_circle_position_ = static_cast<float>(previous_position + delta * percentage);
Repaint();
})
.Start();
RaiseToggleEvent(state);
Repaint();
}
}
void ToggleButton::Toggle()
{
SetState(!GetState());
}
void ToggleButton::OnToggle(events::ToggleEventArgs& args)
{
}
void ToggleButton::OnDrawContent(ID2D1DeviceContext* device_context)
{
Control::OnDrawContent(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::OnMeasureContent(const Size& available_size)
{
const Size result_size(
half_width * 2 + stroke_width,
half_height * 2 + stroke_width
);
return result_size;
}
void ToggleButton::RaiseToggleEvent(bool new_state)
{
events::ToggleEventArgs args(this, this, new_state);
OnToggle(args);
toggle_event.Raise(args);
}
}
|