1 | // Aseprite UI Library |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2016 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "ui/slider.h" |
13 | |
14 | #include "os/font.h" |
15 | #include "ui/message.h" |
16 | #include "ui/size_hint_event.h" |
17 | #include "ui/system.h" |
18 | #include "ui/theme.h" |
19 | #include "ui/widget.h" |
20 | |
21 | #include <algorithm> |
22 | #include <cstdio> |
23 | #include <cstdlib> |
24 | |
25 | namespace ui { |
26 | |
27 | static int slider_press_x; |
28 | static int slider_press_value; |
29 | static bool slider_press_left; |
30 | |
31 | Slider::Slider(int min, int max, int value, SliderDelegate* delegate) |
32 | : Widget(kSliderWidget) |
33 | , m_min(min) |
34 | , m_max(max) |
35 | , m_value(std::clamp(value, min, max)) |
36 | , m_readOnly(false) |
37 | , m_delegate(delegate) |
38 | { |
39 | setFocusStop(true); |
40 | initTheme(); |
41 | } |
42 | |
43 | void Slider::setRange(int min, int max) |
44 | { |
45 | m_min = min; |
46 | m_max = max; |
47 | m_value = std::clamp(m_value, min, max); |
48 | |
49 | invalidate(); |
50 | } |
51 | |
52 | void Slider::setValue(int value) |
53 | { |
54 | int old_value = m_value; |
55 | |
56 | m_value = std::clamp(value, m_min, m_max); |
57 | |
58 | if (m_value != old_value) |
59 | invalidate(); |
60 | |
61 | // It DOES NOT emit CHANGE signal! to avoid recursive calls. |
62 | } |
63 | |
64 | void Slider::getSliderThemeInfo(int* min, int* max, int* value) const |
65 | { |
66 | if (min) *min = m_min; |
67 | if (max) *max = m_max; |
68 | if (value) *value = m_value; |
69 | } |
70 | |
71 | std::string Slider::convertValueToText(int value) const |
72 | { |
73 | if (m_delegate) |
74 | return m_delegate->onGetTextFromValue(value); |
75 | else { |
76 | char buf[128]; |
77 | std::sprintf(buf, "%d" , value); |
78 | return buf; |
79 | } |
80 | } |
81 | |
82 | int Slider::convertTextToValue(const std::string& text) const |
83 | { |
84 | if (m_delegate) |
85 | return m_delegate->onGetValueFromText(text); |
86 | else { |
87 | return std::strtol(text.c_str(), nullptr, 10); |
88 | } |
89 | } |
90 | |
91 | bool Slider::onProcessMessage(Message* msg) |
92 | { |
93 | switch (msg->type()) { |
94 | |
95 | case kFocusEnterMessage: |
96 | case kFocusLeaveMessage: |
97 | if (isEnabled()) |
98 | invalidate(); |
99 | break; |
100 | |
101 | case kMouseDownMessage: |
102 | if (!isEnabled() || isReadOnly()) |
103 | return true; |
104 | |
105 | setSelected(true); |
106 | captureMouse(); |
107 | |
108 | { |
109 | gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position(); |
110 | slider_press_x = mousePos.x; |
111 | slider_press_value = m_value; |
112 | slider_press_left = static_cast<MouseMessage*>(msg)->left(); |
113 | } |
114 | |
115 | setupSliderCursor(); |
116 | |
117 | [[fallthrough]]; |
118 | |
119 | case kMouseMoveMessage: |
120 | if (hasCapture()) { |
121 | int value, accuracy, range; |
122 | gfx::Rect rc = childrenBounds(); |
123 | gfx::Point mousePos = static_cast<MouseMessage*>(msg)->positionForDisplay(display()); |
124 | |
125 | range = m_max - m_min + 1; |
126 | |
127 | // With left click |
128 | if (slider_press_left) { |
129 | value = m_min + range * (mousePos.x - rc.x) / rc.w; |
130 | } |
131 | // With right click |
132 | else { |
133 | accuracy = std::clamp(rc.w / range, 1, rc.w); |
134 | |
135 | value = slider_press_value + |
136 | (mousePos.x - slider_press_x) / accuracy; |
137 | } |
138 | |
139 | value = std::clamp(value, m_min, m_max); |
140 | if (m_value != value) { |
141 | setValue(value); |
142 | onChange(); |
143 | } |
144 | |
145 | return true; |
146 | } |
147 | break; |
148 | |
149 | case kMouseUpMessage: |
150 | if (hasCapture()) { |
151 | setSelected(false); |
152 | releaseMouse(); |
153 | setupSliderCursor(); |
154 | |
155 | onSliderReleased(); |
156 | } |
157 | break; |
158 | |
159 | case kMouseEnterMessage: |
160 | case kMouseLeaveMessage: |
161 | // TODO theme stuff |
162 | if (isEnabled()) |
163 | invalidate(); |
164 | break; |
165 | |
166 | case kKeyDownMessage: |
167 | if (hasFocus() && !isReadOnly()) { |
168 | int value = m_value; |
169 | |
170 | switch (static_cast<KeyMessage*>(msg)->scancode()) { |
171 | case kKeyLeft: --value; break; |
172 | case kKeyRight: ++value; break; |
173 | case kKeyPageDown: value -= (m_max-m_min+1)/4; break; |
174 | case kKeyPageUp: value += (m_max-m_min+1)/4; break; |
175 | case kKeyHome: value = m_min; break; |
176 | case kKeyEnd: value = m_max; break; |
177 | default: |
178 | goto not_used; |
179 | } |
180 | |
181 | value = std::clamp(value, m_min, m_max); |
182 | if (m_value != value) { |
183 | setValue(value); |
184 | onChange(); |
185 | } |
186 | |
187 | return true; |
188 | } |
189 | break; |
190 | |
191 | case kMouseWheelMessage: |
192 | if (isEnabled() && !isReadOnly()) { |
193 | int value = m_value |
194 | + static_cast<MouseMessage*>(msg)->wheelDelta().x |
195 | - static_cast<MouseMessage*>(msg)->wheelDelta().y; |
196 | |
197 | value = std::clamp(value, m_min, m_max); |
198 | |
199 | if (m_value != value) { |
200 | this->setValue(value); |
201 | onChange(); |
202 | } |
203 | return true; |
204 | } |
205 | break; |
206 | |
207 | case kSetCursorMessage: |
208 | setupSliderCursor(); |
209 | return true; |
210 | } |
211 | |
212 | not_used:; |
213 | return Widget::onProcessMessage(msg); |
214 | } |
215 | |
216 | void Slider::onPaint(PaintEvent& ev) |
217 | { |
218 | theme()->paintSlider(ev); |
219 | } |
220 | |
221 | void Slider::onChange() |
222 | { |
223 | Change(); // Emit Change signal |
224 | } |
225 | |
226 | void Slider::onSliderReleased() |
227 | { |
228 | SliderReleased(); |
229 | } |
230 | |
231 | void Slider::setupSliderCursor() |
232 | { |
233 | if (hasCapture()) { |
234 | if (slider_press_left) |
235 | set_mouse_cursor(kArrowCursor); |
236 | else |
237 | set_mouse_cursor(kSizeWECursor); |
238 | } |
239 | else |
240 | set_mouse_cursor(kArrowCursor); |
241 | } |
242 | |
243 | } // namespace ui |
244 | |