1 /*
2 ** Surge Synthesizer is Free and Open Source Software
3 **
4 ** Surge is made available under the Gnu General Public License, v3.0
5 ** https://www.gnu.org/licenses/gpl-3.0.en.html
6 **
7 ** Copyright 2004-2020 by various individuals as described by the Git transaction log
8 **
9 ** All source at: https://github.com/surge-synthesizer/surge.git
10 **
11 ** Surge was a commercial product from 2004-2018, with Copyright and ownership
12 ** in that period held by Claes Johanson at Vember Audio. Claes made Surge
13 ** open source in September 2018.
14 */
15
16 #include "SurgeGUIEditor.h"
17 #include "CSurgeSlider.h"
18 #include "guihelpers.h"
19 #include "resource.h"
20 #include "DspUtilities.h"
21 #include "MouseCursorControl.h"
22 #include "CScalableBitmap.h"
23 #include "SurgeBitmaps.h"
24 #include "SurgeStorage.h"
25 #include "SkinColors.h"
26 #include "StringOps.h"
27 #include <UserDefaults.h>
28 #include <iostream>
29
30 using namespace VSTGUI;
31 using namespace std;
32
33 extern CFontRef displayFont;
34
35 enum
36 {
37 cs_none = 0,
38 cs_drag = 1,
39 };
40
41 class SurgeStorage;
42
43 CSurgeSlider::MoveRateState CSurgeSlider::sliderMoveRateState = kUnInitialized;
44
CSurgeSlider(const CPoint & loc,long stylee,IControlListener * listener,long tag,bool is_mod,std::shared_ptr<SurgeBitmaps> bitmapStore,SurgeStorage * storage)45 CSurgeSlider::CSurgeSlider(const CPoint &loc, long stylee, IControlListener *listener, long tag,
46 bool is_mod, std::shared_ptr<SurgeBitmaps> bitmapStore,
47 SurgeStorage *storage)
48 : CControl(CRect(loc, CPoint(1, 1)), listener, tag, 0),
49 Surge::UI::CursorControlAdapterWithMouseDelta<CSurgeSlider>(storage)
50 {
51 this->style = stylee;
52 this->is_mod = is_mod;
53
54 this->storage = storage;
55
56 labfont = displayFont;
57 #if !TARGET_JUCE_UI
58 labfont->remember();
59 #endif
60
61 modmode = 0;
62 disabled = false;
63
64 label[0] = 0;
65 pModHandle = 0;
66
67 typex = 0;
68 typey = 0;
69 typehx = 0;
70 typehy = 0;
71 qdvalue = value;
72
73 edit_value = 0;
74 drawcount_debug = 0;
75
76 controlstate = cs_none;
77
78 has_modulation = false;
79 has_modulation_current = false;
80
81 restvalue = 0.0f;
82 restmodval = 0.0f;
83
84 CRect size;
85
86 if (style & CSlider::kHorizontal)
87 {
88 if (style & kWhite)
89 typehy = 1;
90
91 range = 112;
92 size = CRect(0, 0, 140, 26);
93 size.offset(loc.x, max(0, (int)loc.y));
94 handle_rect_orig = CRect(1, 0, 20, 14);
95 offsetHandle = CPoint(9, 5);
96 }
97 else
98 {
99 if (!(style & CSlider::kTop))
100 style |= CSlider::kBottom; // CSlider::kBottom by default
101
102 if (style & kWhite)
103 typehy = 0;
104 else
105 typehy = 1;
106
107 if (style & kMini)
108 range = 39;
109 else
110 range = 56;
111 size = CRect(0, 0, 22, 84);
112 size.offset(loc.x, max(0, (int)loc.y - 3));
113 handle_rect_orig = CRect(1, 0, 15, 19);
114 offsetHandle = CPoint(7, 9);
115 }
116
117 setViewSize(size);
118 setMouseableArea(size);
119 }
120
setModValue(float val)121 void CSurgeSlider::setModValue(float val)
122 {
123 this->modval = val;
124 invalid();
125 }
126
~CSurgeSlider()127 CSurgeSlider::~CSurgeSlider() { labfont->forget(); }
128
setLabel(const char * txt)129 void CSurgeSlider::setLabel(const char *txt)
130 {
131 int rp = 0;
132 for (int i = 0; i < 255; i++)
133 {
134 label[i] = txt[rp++];
135 }
136 label[255] = 0;
137 setDirty();
138 }
139
draw(CDrawContext * dc)140 void CSurgeSlider::draw(CDrawContext *dc)
141 {
142 #if 0
143 CColor c;
144 c.red = rand()&255;
145 c.green = rand()&255;
146 c.blue = rand()&255;
147 dc->setFillColor(c);
148 dc->fillRect(size);
149 #endif
150
151 bool drawLabel = true;
152
153 if (skin && skinControl &&
154 skin->propertyValue(skinControl, Surge::Skin::Component::HIDE_SLIDER_LABEL, "") == "true")
155 {
156 drawLabel = false;
157 }
158
159 labfont->setStyle(font_style);
160 labfont->setSize(font_size);
161
162 CRect size = getViewSize();
163
164 if (style & CSlider::kHorizontal)
165 {
166 if (style & kSemitone)
167 typey = 2;
168 else if (isBipolFn())
169 typey = 1;
170 else
171 typey = 0;
172 if (style & kWhite)
173 typey += 3;
174 }
175 else
176 {
177 if (style & kMini)
178 typey = 2;
179 else if (isBipolFn())
180 typey = 1;
181 else
182 typey = 0;
183 }
184
185 typex = has_modulation ? 1 : 0;
186 if (has_modulation_current)
187 typex = 2;
188
189 float slider_alpha = (disabled || isDeactivatedFn()) ? 0.35 : 1.0;
190 bool showHandle = true;
191 #if LINUX && !TARGET_JUCE_UI
192 // linux transparency is a bit broken (i have it patched to ignored) as described in
193 // #2053. For now just disable handles on linux transparent sliders, which is a bummer
194 // but better than a mispaint, then come back to this after 1.7.0
195 if (disabled || isDeactivatedFn())
196 {
197 showHandle = false;
198 slider_alpha = 1.0;
199 }
200 #endif
201
202 if (pTray)
203 {
204 // CRect trect(0,0,pTray->getWidth(),pTray->getHeight());
205 CRect trect;
206 if (style & CSlider::kHorizontal)
207 trect = CRect(0, 0, 133, 14);
208 else
209 trect = CRect(0, 0, 16, 75);
210
211 trect.offset(size.left, size.top);
212
213 if (style & CSlider::kHorizontal)
214 trect.offset(2, 5);
215 else
216 trect.offset(2, 2);
217
218 if (style & CSlider::kHorizontal)
219 pTray->draw(dc, trect, CPoint(133 * typex, 14 * typey), slider_alpha);
220 else
221 pTray->draw(dc, trect, CPoint(16 * typex, 75 * typey), slider_alpha);
222 }
223
224 CRect headrect;
225 if (style & CSlider::kHorizontal)
226 headrect = CRect(0, 0, 28, 24);
227 else
228 headrect = CRect(0, 0, 24, 28);
229
230 if ((hasLabFn || label[0]) && (style & CSlider::kHorizontal))
231 {
232 if (drawLabel)
233 {
234 CRect trect(0, 0, 111, 13);
235 trect.offset(size.left, size.top);
236 trect.offset(13, 12);
237 trect.inset(2, 0);
238
239 // if (label_id >= 0) pLabels->draw(dc,trect,CPoint(0,8*label_id),0xff);
240 // int a = 'a' + (rand()&31);
241 // label[1] = a;
242 // sprintf(label,"%i",drawcount_debug++);
243
244 if (style & kWhite)
245 dc->setFontColor(skin->getColor(Colors::Slider::Label::Light));
246 else
247 dc->setFontColor(skin->getColor(Colors::Slider::Label::Dark));
248 dc->setFont(labfont);
249 dc->setFrameColor(kRedCColor);
250
251 // final x, y offset set from the skin
252 trect.offset(text_hoffset, text_voffset);
253 if (hasLabFn)
254 {
255 auto tl = labfn();
256 dc->drawString(tl.c_str(), trect, text_align, true);
257 }
258 else
259 {
260 dc->drawString(label, trect, text_align, true);
261 }
262 }
263 }
264
265 CRect hrect(headrect);
266 handle_rect = handle_rect_orig;
267 hrect.offset(size.left, size.top);
268 if (style & CSlider::kHorizontal)
269 hrect.offset(0, 3);
270
271 float dispv = limit_range(qdvalue, 0.f, 1.f);
272 if (style & CSlider::kRight || style & CSlider::kBottom)
273 dispv = 1 - dispv;
274 dispv *= range;
275
276 if (style & CSlider::kHorizontal)
277 {
278 hrect.offset(dispv + 1, 0);
279 handle_rect.offset(dispv + 1, 0);
280 }
281 else
282 {
283 hrect.offset(1, dispv);
284 handle_rect.offset(1, dispv);
285 }
286
287 if (modmode)
288 {
289 CRect trect = hrect;
290 CRect trect2 = hrect;
291
292 CColor ColBar = skin->getColor(Colors::Slider::Modulation::Positive);
293 CColor ColBarNeg = skin->getColor(Colors::Slider::Modulation::Negative);
294
295 ColBar.alpha = (int)(slider_alpha * 255.f);
296 ColBarNeg.alpha = (int)(slider_alpha * 255.f);
297
298 // We want modval + value to be bound by -1 and 1. So:
299 float modup = modval;
300 float moddn = modval;
301 bool overtop = false;
302 bool overbot = false;
303 bool overotherside = false;
304
305 if (modup + value > 1.f)
306 {
307 overtop = true;
308 modup = 1.f - value;
309 }
310 if (modup + value < 0.f)
311 {
312 overbot = true;
313 overotherside = true;
314 modup = 0.f - value;
315 }
316 if (value - moddn < 0.f)
317 {
318 overbot = true;
319 moddn = value + 0.f;
320 }
321 if (value - moddn > 1.f)
322 {
323 overtop = true;
324 overotherside = true;
325 moddn = value - 1.f;
326 }
327
328 // at some point in the future draw something special with overtop and overbot
329 modup *= range;
330 moddn *= range;
331
332 std::vector<CRect> drawThese;
333 std::vector<CRect> drawTheseToo;
334
335 if (style & CSlider::kHorizontal)
336 {
337 trect.top += 8;
338 trect.bottom = trect.top + 2;
339
340 trect2.top += 8;
341 trect2.bottom = trect2.top + 2;
342
343 if (!modulation_is_bipolar)
344 {
345 trect.left += 11;
346 trect.right = trect.left + modup;
347 }
348 else
349 {
350 trect.left += 11;
351 trect.right = trect.left + modup;
352
353 trect2.right -= 17;
354 trect2.left = trect2.right - moddn;
355 if (!overotherside && overbot)
356 trect2.left += 1;
357 }
358
359 if (trect.left > trect.right)
360 std::swap(trect.left, trect.right);
361
362 if (trect2.left > trect2.right)
363 std::swap(trect2.left, trect2.right);
364
365 drawThese.push_back(trect);
366 drawTheseToo.push_back(trect2);
367
368 if (overtop)
369 {
370 CRect topr;
371
372 if (overotherside)
373 {
374 topr.top = trect2.top;
375 topr.bottom = trect2.bottom;
376 topr.left = trect2.right + 1;
377 topr.right = topr.left + 4;
378
379 drawTheseToo.push_back(topr);
380 }
381 else
382 {
383 topr.top = trect.top;
384 topr.bottom = trect.bottom;
385 topr.left = trect.right + 1;
386 topr.right = topr.left + 4;
387
388 drawThese.push_back(topr);
389 }
390 }
391
392 if (overbot)
393 {
394 CRect topr;
395
396 if (overotherside)
397 {
398 topr.top = trect.top;
399 topr.bottom = trect.bottom;
400 topr.left = trect.left - 3;
401 topr.right = topr.left + 1;
402
403 drawThese.push_back(topr);
404 }
405 else
406 {
407 topr.top = trect2.top;
408 topr.bottom = trect2.bottom;
409 topr.left = trect2.left - 5;
410 topr.right = topr.left + 4;
411
412 drawTheseToo.push_back(topr);
413 }
414 }
415 }
416 else
417 {
418 trect.left += 8;
419 trect.right = trect.left + 2;
420
421 trect2.left += 8;
422 trect2.right = trect2.left + 2;
423
424 if (!modulation_is_bipolar)
425 {
426 trect.top += 11;
427 trect.bottom = trect.top - modup;
428 }
429 else
430 {
431 trect.top += 11;
432 trect.bottom = trect.top - modup;
433
434 trect2.bottom -= 17;
435 trect2.top = trect2.bottom + moddn;
436 if (overotherside && overtop)
437 trect2.top += 1;
438 }
439
440 if (trect.top < trect.bottom)
441 std::swap(trect.top, trect.bottom);
442
443 if (trect2.top < trect2.bottom)
444 std::swap(trect2.top, trect2.bottom);
445
446 if (overbot)
447 {
448 CRect topr;
449
450 if (overotherside)
451 {
452 topr.left = trect.left;
453 topr.right = trect.right;
454 topr.bottom = trect.top + 1;
455 topr.top = topr.bottom + 4;
456
457 drawThese.push_back(topr);
458 }
459 else
460 {
461 topr.left = trect2.left;
462 topr.right = trect2.right;
463 topr.bottom = trect2.top + 1;
464 topr.top = topr.bottom + 4;
465
466 drawTheseToo.push_back(topr);
467 }
468 }
469
470 if (overtop)
471 {
472 CRect topr;
473
474 if (overotherside)
475 {
476 topr.left = trect2.left;
477 topr.right = trect2.right;
478 topr.bottom = trect2.bottom - 1;
479 topr.top = topr.bottom - 4;
480
481 drawTheseToo.push_back(topr);
482 }
483 else
484 {
485 topr.left = trect.left;
486 topr.right = trect.right;
487 topr.bottom = trect.bottom - 1;
488 topr.top = topr.bottom - 4;
489
490 drawThese.push_back(topr);
491 }
492 }
493
494 drawThese.push_back(trect);
495 drawTheseToo.push_back(trect2);
496 }
497
498 for (auto r : drawThese)
499 {
500 dc->setFillColor(ColBar);
501 dc->drawRect(r, VSTGUI::kDrawFilled);
502 }
503
504 if (modulation_is_bipolar)
505 {
506 for (auto r : drawTheseToo)
507 {
508 dc->setFillColor(ColBarNeg);
509 dc->drawRect(r, VSTGUI::kDrawFilled);
510 }
511 }
512 }
513
514 if (pHandle && showHandle && (modmode != 2) && (!isDeactivatedFn() || !disabled))
515 {
516 draghandlecenter = hrect.getCenter();
517
518 if (style & CSlider::kHorizontal)
519 {
520 pHandle->draw(dc, hrect, CPoint(0, 24 * typehy), modmode ? slider_alpha : slider_alpha);
521 if (pHandleHover && in_hover && (!modmode))
522 pHandleHover->draw(dc, hrect, CPoint(0, 24 * typehy),
523 modmode ? slider_alpha : slider_alpha);
524
525 if (is_temposync)
526 {
527 if (in_hover && pTempoSyncHoverHandle && (!modmode))
528 {
529 pTempoSyncHoverHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
530 }
531 else if (pTempoSyncHandle)
532 {
533 pTempoSyncHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
534 }
535 else
536 {
537 dc->setFont(labfont);
538 dc->setFontColor(CColor(80, 80, 100));
539 auto newRect = hrect;
540 newRect.top += 1;
541 newRect.left += 1;
542 newRect.bottom = newRect.top + 15;
543 newRect.right = newRect.left + 21;
544
545 auto tRect = newRect;
546 tRect.right = tRect.left + 11;
547 tRect.left += 2;
548
549 auto sRect = newRect;
550 sRect.left += 11;
551 sRect.right -= 2;
552 dc->drawString("T", tRect, kCenterText, true);
553 dc->drawString("S", sRect, kCenterText, true);
554 }
555 }
556 }
557 else if ((!isDeactivatedFn() || !disabled))
558 {
559 pHandle->draw(
560 dc, hrect, CPoint(0, 28 * typehy),
561 modmode ? slider_alpha
562 : slider_alpha); // used to be 0x80 which was solid - lets keep taht for now
563 if (pHandleHover && in_hover && (!modmode))
564 pHandleHover->draw(
565 dc, hrect, CPoint(0, 28 * typehy),
566 modmode
567 ? slider_alpha
568 : slider_alpha); // used to be 0x80 which was solid - lets keep taht for now
569
570 if (is_temposync)
571 {
572 if (in_hover && pTempoSyncHoverHandle && (!modmode))
573 {
574 pTempoSyncHoverHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
575 }
576 else if (pTempoSyncHandle)
577 {
578 pTempoSyncHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
579 }
580 else
581 {
582 auto newRect = hrect;
583 newRect.top += 1;
584 newRect.left += 1;
585 newRect.bottom = newRect.top + 20;
586 newRect.right = newRect.left + 16;
587
588 dc->setFont(labfont);
589 dc->setFontColor(CColor(80, 80, 100));
590
591 auto tRect = newRect;
592 tRect.bottom = tRect.top + 11;
593 tRect.top += 2;
594
595 auto sRect = newRect;
596 sRect.top += 11;
597 sRect.bottom -= 2;
598
599 dc->drawString("T", tRect, kCenterText, true);
600 dc->drawString("S", sRect, kCenterText, true);
601 }
602 }
603 }
604 }
605
606 // draw mod-fader
607 if (pHandle && showHandle && modmode && (!isDeactivatedFn() || !disabled))
608 {
609 CRect hrect(headrect);
610 handle_rect = handle_rect_orig;
611 hrect.offset(size.left, size.top);
612 if (style & CSlider::kHorizontal)
613 hrect.offset(0, 3);
614
615 float dispv;
616 if (modmode == 2)
617 dispv = limit_range(0.5f + 0.5f * modval, 0.f, 1.f);
618 else
619 dispv = limit_range(modval + value, 0.f, 1.f);
620
621 if (style & CSlider::kRight || style & CSlider::kBottom)
622 dispv = 1 - dispv;
623 dispv *= range;
624
625 if (style & CSlider::kHorizontal)
626 {
627 hrect.offset(dispv + 1, 0);
628 handle_rect.offset(dispv + 1, 0);
629 }
630 else
631 {
632 hrect.offset(1, dispv);
633 handle_rect.offset(1, dispv);
634 }
635
636 if (style & CSlider::kHorizontal)
637 {
638 pHandle->draw(dc, hrect, CPoint(28, 24 * typehy), slider_alpha);
639 if (pHandleHover && in_hover)
640 pHandleHover->draw(dc, hrect, CPoint(28, 24 * typehy), slider_alpha);
641 }
642 else
643 {
644 pHandle->draw(dc, hrect, CPoint(24, 28 * typehy), slider_alpha);
645 if (pHandleHover && in_hover)
646 pHandleHover->draw(dc, hrect, CPoint(24, 28 * typehy), slider_alpha);
647 }
648 draghandlecenter = hrect.getCenter();
649 }
650
651 setDirty(false);
652 }
653
bounceValue(const bool keeprest)654 void CSurgeSlider::bounceValue(const bool keeprest)
655 {
656 if (keeprest)
657 {
658 if (restvalue != 0.0f)
659 {
660 value += restvalue;
661 restvalue = 0.0f;
662 }
663 if (restmodval != 0.0f)
664 {
665 modval += restmodval;
666 restmodval = 0.0f;
667 }
668 }
669
670 if (value > vmax)
671 {
672 restvalue = value - vmax;
673 value = vmax;
674 }
675 else if (value < vmin)
676 {
677 restvalue = value - vmin;
678 value = vmin;
679 }
680
681 if (modval > 1.f)
682 {
683 restmodval = modval - 1.f;
684 modval = 1.f;
685 }
686 if (modval < -1.f)
687 {
688 restmodval = modval - (-1.f);
689 modval = -1.f;
690 }
691 }
692
isInMouseInteraction()693 bool CSurgeSlider::isInMouseInteraction() { return controlstate == cs_drag; }
694
onMouseDown(CPoint & where,const CButtonState & buttons)695 CMouseEventResult CSurgeSlider::onMouseDown(CPoint &where, const CButtonState &buttons)
696 {
697 startPosition = where;
698 currentPosition = where;
699
700 {
701 auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
702 if (sge)
703 {
704 sge->clear_infoview_peridle = 0;
705 }
706 }
707 if (storage)
708 this->hideCursor = !Surge::UI::showCursor(storage);
709
710 hasBeenDraggedDuringMouseGesture = false;
711 if (wheelInitiatedEdit)
712 while (editing)
713 endEdit();
714 wheelInitiatedEdit = false;
715
716 if (listener && buttons & (kAlt | kRButton | kMButton | kButton4 | kButton5 | kShift |
717 kControl | kApple | kDoubleClick))
718 {
719 unenqueueCursorHideIfMoved();
720
721 if (listener->controlModifierClicked(this, buttons) != 0)
722 {
723 return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
724 }
725 }
726
727 onMouseDownCursorHelper(where);
728
729 if (controlstate)
730 {
731 return kMouseEventHandled;
732 }
733
734 if ((buttons & kLButton) && !controlstate)
735 {
736 beginEdit();
737 controlstate = cs_drag;
738 // getFrame()->setCursor( VSTGUI::kCursorHand );
739
740 edit_value = modmode ? &modval : &value;
741 oldVal = *edit_value;
742
743 restvalue = 0.f;
744 restmodval = 0.f;
745
746 // Show the infowindow. Bit heavy handed
747 bounceValue();
748 if (listener)
749 listener->valueChanged(this);
750
751 // detachCursor(where);
752 enqueueCursorHideIfMoved(where);
753 return kMouseEventHandled;
754 }
755 return kMouseEventHandled;
756 }
757
onMouseUp(CPoint & where,const CButtonState & buttons)758 CMouseEventResult CSurgeSlider::onMouseUp(CPoint &where, const CButtonState &buttons)
759 {
760 unenqueueCursorHideIfMoved();
761 {
762 auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
763 if (sge)
764 {
765 sge->clear_infoview_peridle = -1;
766 }
767 }
768
769 bool resetPosition = hasBeenDraggedDuringMouseGesture;
770
771 // "elastic edit" - resets to the value before the drag started if Alt is held
772 if (buttons & kAlt)
773 {
774 hasBeenDraggedDuringMouseGesture = false;
775 resetPosition = false;
776 *edit_value = oldVal;
777 setDirty();
778 if (isDirty() && listener)
779 listener->valueChanged(this);
780 }
781
782 if (controlstate)
783 {
784 #if MAC
785 /*if(buttons & kRButton)
786 {
787 statezoom = 1.0f;
788 return kMouseEventHandled;
789 }*/
790 #endif
791 endEdit();
792 controlstate = cs_none;
793 // getFrame()->setCursor( VSTGUI::kCursorDefault );
794
795 edit_value = nullptr;
796
797 if (resetPosition && startPosition != currentPosition)
798 {
799 endCursorHide(draghandlecenter);
800 }
801 else
802 {
803 endCursorHide();
804 }
805
806 // attachCursor();
807 }
808
809 return kMouseEventHandled;
810 }
811
onMouseEntered(VSTGUI::CPoint & where,const VSTGUI::CButtonState & buttons)812 VSTGUI::CMouseEventResult CSurgeSlider::onMouseEntered(VSTGUI::CPoint &where,
813 const VSTGUI::CButtonState &buttons)
814 {
815 in_hover = true;
816
817 {
818 auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
819 if (sge)
820 {
821 sge->sliderHoverStart(getTag());
822 }
823 }
824
825 invalid();
826 return kMouseEventHandled;
827 }
828
onMouseExited(VSTGUI::CPoint & where,const VSTGUI::CButtonState & buttons)829 VSTGUI::CMouseEventResult CSurgeSlider::onMouseExited(VSTGUI::CPoint &where,
830 const VSTGUI::CButtonState &buttons)
831 {
832 in_hover = false;
833
834 {
835 auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
836 if (sge)
837 {
838 sge->sliderHoverEnd(getTag());
839 }
840 }
841
842 if (wheelInitiatedEdit)
843 while (editing)
844 endEdit();
845 wheelInitiatedEdit = false;
846 invalid();
847 return kMouseEventHandled;
848 }
849
getMouseDeltaScaling(const CPoint & where,const CButtonState & buttons)850 double CSurgeSlider::getMouseDeltaScaling(const CPoint &where, const CButtonState &buttons)
851 {
852 double rate;
853
854 switch (CSurgeSlider::sliderMoveRateState)
855 {
856 case kSlow:
857 rate = 0.3;
858 break;
859 case kMedium:
860 rate = 0.7;
861 break;
862 case kExact:
863 rate = 1.0;
864 break;
865 case kLegacy:
866 default:
867 rate = 0.3 * moverate;
868 break;
869 }
870
871 if (buttons.isTouch())
872 rate = 1.0;
873
874 if (buttons & kRButton)
875 rate *= 0.1;
876 if (buttons & kShift)
877 rate *= 0.1;
878
879 return rate;
880 }
881
onMouseMoveDelta(const CPoint & where,const CButtonState & buttons,double dx,double dy)882 void CSurgeSlider::onMouseMoveDelta(const CPoint &where, const CButtonState &buttons, double dx,
883 double dy)
884 {
885 currentPosition = where;
886 if (controlstate != cs_drag)
887 {
888 // FIXME - deal with modulation
889 auto handle_rect = handle_rect_orig;
890 auto size = getViewSize();
891 handle_rect.offset(size.left, size.top);
892
893 float dispv = limit_range(qdvalue, 0.f, 1.f);
894 if (style & CSlider::kRight || style & CSlider::kBottom)
895 dispv = 1 - dispv;
896 dispv *= range;
897
898 if (modmode)
899 {
900 if (modmode == 2)
901 dispv = limit_range(0.5f + 0.5f * modval, 0.f, 1.f);
902 else
903 dispv = limit_range(modval + value, 0.f, 1.f);
904
905 if (style & CSlider::kRight || style & CSlider::kBottom)
906 dispv = 1 - dispv;
907 dispv *= range;
908 }
909
910 if (style & CSlider::kHorizontal)
911 {
912 handle_rect.offset(dispv + 1, 3);
913 }
914 else
915 {
916 handle_rect.offset(1, dispv);
917 }
918
919 /*
920 if( handle_rect.pointInside(where) )
921 getFrame()->setCursor( VSTGUI::kCursorHand );
922 else
923 getFrame()->setCursor( VSTGUI::kCursorDefault );
924 */
925 }
926
927 if ((controlstate == cs_drag) && (buttons & kLButton))
928 {
929 hasBeenDraggedDuringMouseGesture = true;
930 if (!edit_value)
931 return;
932 CPoint p;
933
934 double diff;
935 if (style & CSlider::kHorizontal)
936 diff = dx;
937 else
938 diff = dy;
939
940 if (style & CSlider::kRight || style & CSlider::kBottom)
941 diff = -diff;
942
943 *edit_value += diff / (float)range;
944
945 // this "retain" means if you turnaround an overshoot you have to use it all up first, vs
946 // starting the turnaround right away. That behavior only makes sense in exact mode.
947 bounceValue(sliderMoveRateState == MoveRateState::kExact);
948
949 setDirty();
950
951 if (isDirty() && listener)
952 listener->valueChanged(this);
953
954 /*if (isDirty ())
955 invalid ();*/
956 }
957 }
958
SetQuantitizedDispValue(float f)959 void CSurgeSlider::SetQuantitizedDispValue(float f)
960 {
961 qdvalue = f;
962
963 // if (isDirty ())
964 invalid();
965 }
966
setValue(float val)967 void CSurgeSlider::setValue(float val)
968 {
969 if ((controlstate != cs_drag))
970 {
971 value = val;
972 qdvalue = val;
973 }
974 }
975
onWheel(const VSTGUI::CPoint & where,const float & distance,const VSTGUI::CButtonState & buttons)976 bool CSurgeSlider::onWheel(const VSTGUI::CPoint &where, const float &distance,
977 const VSTGUI::CButtonState &buttons)
978 {
979 // shift + scrollwheel for fine control
980 double rate = 0.1 * moverate;
981 if (buttons & kShift)
982 rate *= 0.05;
983
984 edit_value = modmode ? &modval : &value;
985 oldVal = *edit_value;
986
987 if (editing == 0)
988 {
989 wheelInitiatedEdit = true;
990 beginEdit();
991 }
992
993 if (intRange && isStepped && !(buttons & kControl))
994 {
995 *edit_value += distance / (intRange);
996 }
997 else
998 {
999 *edit_value += rate * distance;
1000 }
1001
1002 bounceValue();
1003 if (modmode)
1004 {
1005 setModValue(*edit_value);
1006 }
1007 else
1008 {
1009 setValue(value);
1010 }
1011 setDirty();
1012 if (isDirty() && listener)
1013 listener->valueChanged(this);
1014
1015 /*
1016 ** If we call endEdit it will dismiss the infowindow so instead
1017 ** use the state management where I have a lignering begin
1018 ** which I clear when we click or leave.
1019 */
1020 edit_value = nullptr;
1021 return true;
1022 }
1023
onSkinChanged()1024 void CSurgeSlider::onSkinChanged()
1025 {
1026 if (style & CSlider::kHorizontal)
1027 {
1028 pTray = associatedBitmapStore->getBitmap(IDB_SLIDER_HORIZ_BG);
1029 pHandle = associatedBitmapStore->getBitmap(IDB_SLIDER_HORIZ_HANDLE);
1030 pTempoSyncHandle =
1031 associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_HORIZONTAL_OVERLAY");
1032 pTempoSyncHoverHandle =
1033 associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_HORIZONTAL_HOVER_OVERLAY");
1034 }
1035 else
1036 {
1037 pTray = associatedBitmapStore->getBitmap(IDB_SLIDER_VERT_BG);
1038 pHandle = associatedBitmapStore->getBitmap(IDB_SLIDER_VERT_HANDLE);
1039 pTempoSyncHandle = associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_VERTICAL_OVERLAY");
1040 pTempoSyncHoverHandle =
1041 associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_VERTICAL_HOVER_OVERLAY");
1042 }
1043
1044 if (skinControl.get())
1045 {
1046 auto htr = skin->propertyValue(skinControl, Surge::Skin::Component::SLIDER_TRAY);
1047 if (htr.isJust())
1048 {
1049 pTray = associatedBitmapStore->getBitmapByStringID(htr.fromJust());
1050 }
1051 auto hi = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_IMAGE);
1052 if (hi.isJust())
1053 {
1054 pHandle = associatedBitmapStore->getBitmapByStringID(hi.fromJust());
1055 }
1056 auto ho = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_HOVER_IMAGE);
1057 if (ho.isJust())
1058 {
1059 pHandleHover = associatedBitmapStore->getBitmapByStringID(ho.fromJust());
1060 }
1061 auto ht = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_TEMPOSYNC_IMAGE);
1062 if (ht.isJust())
1063 {
1064 pTempoSyncHandle = associatedBitmapStore->getBitmapByStringID(ht.fromJust());
1065 }
1066
1067 auto hth =
1068 skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_TEMPOSYNC_HOVER_IMAGE);
1069 if (hth.isJust())
1070 {
1071 pTempoSyncHoverHandle = associatedBitmapStore->getBitmapByStringID(hth.fromJust());
1072 }
1073 }
1074
1075 if (!pHandleHover)
1076 pHandleHover = skin->hoverBitmapOverlayForBackgroundBitmap(
1077 skinControl, dynamic_cast<CScalableBitmap *>(pHandle), associatedBitmapStore,
1078 Surge::UI::Skin::HoverType::HOVER);
1079 }
1080