Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
slider_core.hpp
Go to the documentation of this file.
1#pragma once
2
49
50#include <algorithm>
51#include <cmath>
52#include <cstdint>
53#include <functional>
54#include <utility>
55#include <vector>
56
57namespace pulp::view {
58
60enum class Notify {
64};
65
70
73void queue_async_notification(std::function<void()> fn);
74
75// ── The engine ───────────────────────────────────────────────────────────────
76
78public:
79 // ── Range ────────────────────────────────────────────────────────────
80
85 void set_range(double min_value, double max_value, double interval = 0.0) {
86 minimum_ = min_value;
87 maximum_ = std::max(min_value, max_value);
88 interval_ = std::max(0.0, interval);
89 value_ = constrain(value_);
90 }
91 double minimum() const { return minimum_; }
92 double maximum() const { return maximum_; }
93 double interval() const { return interval_; }
94
95 // ── Response curve ───────────────────────────────────────────────────
96
98 void set_skew(double skew) { skew_ = std::max(1e-4, skew); }
99 double skew() const { return skew_; }
100
102 void set_skew_from_midpoint(double mid_value) {
103 const double n = std::clamp(normalized_for(mid_value), 1e-4, 1.0 - 1e-4);
104 skew_ = std::max(1e-4, std::log(0.5) / std::log(n));
105 }
106
107 // ── Value ────────────────────────────────────────────────────────────
108
109 double value() const { return value_; }
110
114 bool set_value(double v, Notify notify = Notify::sync) {
115 const double next = constrain(v);
116 if (next == value_) return false;
117 value_ = next;
118 notify_value(notify);
119 return true;
120 }
121
123 double proportion() const { return proportion_for(value_); }
124
127 bool set_proportion(double p, Notify notify = Notify::sync) {
128 return set_value(value_for_proportion(p), notify);
129 }
130
135 double normalized() const { return std::clamp(normalized_for(value_), 0.0, 1.0); }
136 double value_for_normalized(double n) const {
137 return minimum_ + std::clamp(n, 0.0, 1.0) * (maximum_ - minimum_);
138 }
139 bool set_normalized(double n, Notify notify = Notify::sync) {
140 return set_value(value_for_normalized(n), notify);
141 }
142
143 double proportion_for(double v) const {
144 const double n = std::clamp(normalized_for(v), 0.0, 1.0);
145 return skew_ == 1.0 ? n : std::pow(n, skew_);
146 }
147 double value_for_proportion(double p) const {
148 p = std::clamp(p, 0.0, 1.0);
149 const double n = skew_ == 1.0 ? p : std::pow(p, 1.0 / skew_);
150 return minimum_ + n * (maximum_ - minimum_);
151 }
152
154 double constrain(double v) const {
155 v = std::clamp(v, minimum_, maximum_);
156 if (interval_ > 0.0) {
157 v = minimum_ + std::round((v - minimum_) / interval_) * interval_;
158 v = std::clamp(v, minimum_, maximum_);
159 }
160 return v;
161 }
162
163 // ── Default ──────────────────────────────────────────────────────────
164
169 void set_default_value(double v) {
170 default_value_ = constrain(v);
171 has_default_ = true;
172 }
173 bool has_default() const { return has_default_; }
174 double default_value() const { return default_value_; }
176 if (!has_default_) return false;
177 return set_value(default_value_, notify);
178 }
179
180 // ── Drag law ─────────────────────────────────────────────────────────
181
184 void set_drag_sensitivity(int pixels) { drag_pixels_ = std::max(1, pixels); }
185 int drag_sensitivity() const { return drag_pixels_; }
186
190 void set_velocity_mode(bool on) { velocity_mode_ = on; }
191 bool velocity_mode() const { return velocity_mode_; }
192 void set_velocity_sensitivity(double s) { velocity_sensitivity_ = std::max(1e-3, s); }
193
199 bool drag_to(double delta_pixels, double proportion_at_press, bool fine,
200 Notify notify = Notify::sync) {
201 double span = static_cast<double>(drag_pixels_);
202 if (fine) span *= fine_divisor_;
203 return set_proportion(proportion_at_press + delta_pixels / span, notify);
204 }
205
211 bool drag_step(double delta_pixels, bool fine, Notify notify = Notify::sync) {
212 double span = static_cast<double>(drag_pixels_);
213 if (fine) span *= fine_divisor_;
214 return set_proportion(proportion() + (delta_pixels / span) * velocity_sensitivity_,
215 notify);
216 }
217
218 void set_fine_divisor(double d) { fine_divisor_ = std::max(1.0, d); }
219 double fine_divisor() const { return fine_divisor_; }
220
221 // ── Gesture bracket ──────────────────────────────────────────────────
222 //
223 // A host records automation between these two. They are edge-triggered:
224 // a second begin without an end is ignored, so a widget that funnels both a
225 // rich event and a legacy pointer callback into the same handler cannot
226 // open two gestures.
227
229 if (gesture_active_) return;
230 gesture_active_ = true;
232 }
233 void end_gesture() {
234 if (!gesture_active_) return;
235 gesture_active_ = false;
237 }
238 bool gesture_active() const { return gesture_active_; }
239
240 // ── Listeners ────────────────────────────────────────────────────────
241
242 std::function<void(double)> on_value_change;
243 std::function<void()> on_gesture_begin;
244 std::function<void()> on_gesture_end;
245
246private:
247 double normalized_for(double v) const {
248 const double span = maximum_ - minimum_;
249 if (span <= 0.0) return 0.0;
250 return (v - minimum_) / span;
251 }
252
253 void notify_value(Notify notify) {
254 if (notify == Notify::none || !on_value_change) return;
255 if (notify == Notify::sync) {
256 on_value_change(value_);
257 return;
258 }
259 // Copy the value, not `this`: an async notification that outlives the
260 // control would otherwise read a dead object. The callback itself is a
261 // copy for the same reason.
262 auto fn = on_value_change;
263 const double v = value_;
264 queue_async_notification([fn, v] { fn(v); });
265 }
266
267 double minimum_ = 0.0;
268 double maximum_ = 1.0;
269 double interval_ = 0.0;
270 double skew_ = 1.0;
271 double value_ = 0.0;
272 double default_value_ = 0.0;
273 bool has_default_ = false;
274 int drag_pixels_ = 250;
275 bool velocity_mode_ = false;
276 double velocity_sensitivity_ = 1.0;
277 double fine_divisor_ = 10.0;
278 bool gesture_active_ = false;
279};
280
281} // namespace pulp::view
Definition slider_core.hpp:77
int drag_sensitivity() const
Definition slider_core.hpp:185
double proportion() const
Travel proportion in [0,1] for the current value, after the curve.
Definition slider_core.hpp:123
std::function< void()> on_gesture_end
Definition slider_core.hpp:244
double value_for_proportion(double p) const
Definition slider_core.hpp:147
bool reset_to_default(Notify notify=Notify::sync)
Definition slider_core.hpp:175
bool set_value(double v, Notify notify=Notify::sync)
Write a real-world value.
Definition slider_core.hpp:114
void set_velocity_sensitivity(double s)
Definition slider_core.hpp:192
double value_for_normalized(double n) const
Definition slider_core.hpp:136
double proportion_for(double v) const
Definition slider_core.hpp:143
void begin_gesture()
Definition slider_core.hpp:228
double maximum() const
Definition slider_core.hpp:92
bool gesture_active() const
Definition slider_core.hpp:238
bool drag_step(double delta_pixels, bool fine, Notify notify=Notify::sync)
VELOCITY drag: delta_pixels is the pointer's travel since the LAST tick, and the control integrates i...
Definition slider_core.hpp:211
bool drag_to(double delta_pixels, double proportion_at_press, bool fine, Notify notify=Notify::sync)
ABSOLUTE drag: delta_pixels is the pointer's total travel SINCE the gesture began (positive = increas...
Definition slider_core.hpp:199
void set_skew_from_midpoint(double mid_value)
Choose the skew that places mid_value (real-world units) at half travel.
Definition slider_core.hpp:102
void set_velocity_mode(bool on)
In velocity mode the value moves by pointer SPEED rather than by absolute pointer travel,...
Definition slider_core.hpp:190
double minimum() const
Definition slider_core.hpp:91
void set_fine_divisor(double d)
Definition slider_core.hpp:218
void set_skew(double skew)
1 = linear. Below 1, the low end of the range gets more travel.
Definition slider_core.hpp:98
double default_value() const
Definition slider_core.hpp:174
double skew() const
Definition slider_core.hpp:99
double interval() const
Definition slider_core.hpp:93
double constrain(double v) const
Clamp to the range and snap to the interval.
Definition slider_core.hpp:154
bool velocity_mode() const
Definition slider_core.hpp:191
void set_drag_sensitivity(int pixels)
Pointer pixels that cross the FULL range in a plain (non-velocity) drag.
Definition slider_core.hpp:184
double normalized() const
LINEAR position of the value in the range, in [0,1], with NO response curve applied.
Definition slider_core.hpp:135
void set_default_value(double v)
The value the control returns to on a reset gesture (conventionally a double-click or alt-click).
Definition slider_core.hpp:169
void end_gesture()
Definition slider_core.hpp:233
bool set_normalized(double n, Notify notify=Notify::sync)
Definition slider_core.hpp:139
std::function< void()> on_gesture_begin
Definition slider_core.hpp:243
double value() const
Definition slider_core.hpp:109
bool set_proportion(double p, Notify notify=Notify::sync)
Write from a travel proportion (what a drag or a painter's inverse hit-test produces).
Definition slider_core.hpp:127
bool has_default() const
Definition slider_core.hpp:173
double fine_divisor() const
Definition slider_core.hpp:219
void set_range(double min_value, double max_value, double interval=0.0)
Set the real-world range and the quantization interval.
Definition slider_core.hpp:85
std::function< void(double)> on_value_change
Definition slider_core.hpp:242
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
void queue_async_notification(std::function< void()> fn)
Queue a callback for the next flush.
@ none
Definition appearance_defects.hpp:53
Notify
Whether a value write notifies listeners, and when.
Definition slider_core.hpp:60
@ async
fire listeners at the next flush_async_notifications()
Definition slider_core.hpp:63
@ none
change the value silently
Definition slider_core.hpp:61
@ sync
fire listeners before returning
Definition slider_core.hpp:62
@ s
Definition input_events.hpp:41
@ n
Definition input_events.hpp:40
@ d
Definition input_events.hpp:39
@ p
Definition input_events.hpp:40
@ v
Definition input_events.hpp:41
@ e
Definition input_events.hpp:39
void flush_async_notifications()
Deferred Notify::async callbacks, oldest first.
@ on
Definition accessibility.hpp:132