Hackathon anims
[SugarCubes.git] / _UIFramework.pde
1 /**
2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
3 *
4 * //\\ //\\ //\\ //\\
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
7 * \\// \\// \\// \\//
8 *
9 * EXPERTS ONLY!! EXPERTS ONLY!!
10 *
11 * Little UI framework in progress to handle mouse events, layout,
12 * redrawing, etc.
13 */
14
15 final color lightGreen = #669966;
16 final color lightBlue = #666699;
17 final color bgGray = #444444;
18 final color defaultTextColor = #999999;
19 final PFont defaultItemFont = createFont("Lucida Grande", 11);
20 final PFont defaultTitleFont = createFont("Myriad Pro", 10);
21
22 public abstract class UIObject {
23
24 protected final static int DOUBLE_CLICK_THRESHOLD = 300;
25
26 protected final List<UIObject> children = new ArrayList<UIObject>();
27
28 protected boolean needsRedraw = true;
29 protected boolean childNeedsRedraw = true;
30
31 protected float x=0, y=0, w=0, h=0;
32
33 public UIContainer parent = null;
34
35 protected boolean visible = true;
36
37 public UIObject() {}
38
39 public UIObject(float x, float y, float w, float h) {
40 this.x = x;
41 this.y = y;
42 this.w = w;
43 this.h = h;
44 }
45
46 public boolean isVisible() {
47 return visible;
48 }
49
50 public UIObject setVisible(boolean visible) {
51 if (visible != this.visible) {
52 this.visible = visible;
53 redraw();
54 }
55 return this;
56 }
57
58 public final UIObject setPosition(float x, float y) {
59 this.x = x;
60 this.y = y;
61 redraw();
62 return this;
63 }
64
65 public final UIObject setSize(float w, float h) {
66 this.w = w;
67 this.h = h;
68 redraw();
69 return this;
70 }
71
72 public final UIObject addToContainer(UIContainer c) {
73 c.children.add(this);
74 this.parent = c;
75 return this;
76 }
77
78 public final UIObject removeFromContainer(UIContainer c) {
79 c.children.remove(this);
80 this.parent = null;
81 return this;
82 }
83
84 public final UIObject redraw() {
85 _redraw();
86 UIObject p = this.parent;
87 while (p != null) {
88 p.childNeedsRedraw = true;
89 p = p.parent;
90 }
91 return this;
92 }
93
94 private final void _redraw() {
95 needsRedraw = true;
96 for (UIObject child : children) {
97 childNeedsRedraw = true;
98 child._redraw();
99 }
100 }
101
102 public final void draw(PGraphics pg) {
103 if (!visible) {
104 return;
105 }
106 if (needsRedraw) {
107 needsRedraw = false;
108 onDraw(pg);
109 }
110 if (childNeedsRedraw) {
111 childNeedsRedraw = false;
112 for (UIObject child : children) {
113 if (needsRedraw || child.needsRedraw || child.childNeedsRedraw) {
114 pg.pushMatrix();
115 pg.translate(child.x, child.y);
116 child.draw(pg);
117 pg.popMatrix();
118 }
119 }
120 }
121 }
122
123 public final boolean contains(float x, float y) {
124 return
125 (x >= this.x && x < (this.x + this.w)) &&
126 (y >= this.y && y < (this.y + this.h));
127 }
128
129 protected void onDraw(PGraphics pg) {}
130 protected void onMousePressed(float mx, float my) {}
131 protected void onMouseReleased(float mx, float my) {}
132 protected void onMouseDragged(float mx, float my, float dx, float dy) {}
133 protected void onMouseWheel(float mx, float my, float dx) {}
134 }
135
136 public class UIContainer extends UIObject {
137
138 private UIObject focusedChild = null;
139
140 public UIContainer() {}
141
142 public UIContainer(float x, float y, float w, float h) {
143 super(x, y, w, h);
144 }
145
146 public UIContainer(UIObject[] children) {
147 for (UIObject child : children) {
148 child.addToContainer(this);
149 }
150 }
151
152 protected void onMousePressed(float mx, float my) {
153 for (int i = children.size() - 1; i >= 0; --i) {
154 UIObject child = children.get(i);
155 if (child.contains(mx, my)) {
156 child.onMousePressed(mx - child.x, my - child.y);
157 focusedChild = child;
158 break;
159 }
160 }
161 }
162
163 protected void onMouseReleased(float mx, float my) {
164 if (focusedChild != null) {
165 focusedChild.onMouseReleased(mx - focusedChild.x, my - focusedChild.y);
166 }
167 focusedChild = null;
168 }
169
170 protected void onMouseDragged(float mx, float my, float dx, float dy) {
171 if (focusedChild != null) {
172 focusedChild.onMouseDragged(mx - focusedChild.x, my - focusedChild.y, dx, dy);
173 }
174 }
175
176 protected void onMouseWheel(float mx, float my, float delta) {
177 for (UIObject child : children) {
178 if (child.contains(mx, my)) {
179 child.onMouseWheel(mx - child.x, mx - child.y, delta);
180 }
181 }
182 }
183
184 }
185
186 public class UIContext extends UIContainer {
187
188 final public PGraphics pg;
189
190 UIContext(float x, float y, float w, float h) {
191 super(x, y, w, h);
192 pg = createGraphics((int)w, (int)h, JAVA2D);
193 pg.smooth();
194 }
195
196 public void draw() {
197 if (!visible) {
198 return;
199 }
200 if (needsRedraw || childNeedsRedraw) {
201 pg.beginDraw();
202 draw(pg);
203 pg.endDraw();
204 }
205 image(pg, x, y);
206 }
207
208 private float px, py;
209 private boolean dragging = false;
210
211 public boolean mousePressed(float mx, float my) {
212 if (!visible) {
213 return false;
214 }
215 if (contains(mx, my)) {
216 dragging = true;
217 px = mx;
218 py = my;
219 onMousePressed(mx - x, my - y);
220 return true;
221 }
222 return false;
223 }
224
225 public boolean mouseReleased(float mx, float my) {
226 if (!visible) {
227 return false;
228 }
229 dragging = false;
230 onMouseReleased(mx - x, my - y);
231 return true;
232 }
233
234 public boolean mouseDragged(float mx, float my) {
235 if (!visible) {
236 return false;
237 }
238 if (dragging) {
239 float dx = mx - px;
240 float dy = my - py;
241 onMouseDragged(mx - x, my - y, dx, dy);
242 px = mx;
243 py = my;
244 return true;
245 }
246 return false;
247 }
248
249 public boolean mouseWheel(float mx, float my, float delta) {
250 if (!visible) {
251 return false;
252 }
253 if (contains(mx, my)) {
254 onMouseWheel(mx - x, my - y, delta);
255 return true;
256 }
257 return false;
258 }
259 }
260
261 public class UIWindow extends UIContext {
262
263 protected final static int titleHeight = 24;
264
265 public UIWindow(String label, float x, float y, float w, float h) {
266 super(x, y, w, h);
267 new UILabel(6, 8, w-6, titleHeight-8) {
268 protected void onMouseDragged(float mx, float my, float dx, float dy) {
269 parent.x = constrain(parent.x + dx, 0, width - w);
270 parent.y = constrain(parent.y + dy, 0, height - h);
271 }
272 }.setLabel(label).setFont(defaultTitleFont).addToContainer(this);
273 }
274
275 protected void onDraw(PGraphics pg) {
276 pg.noStroke();
277 pg.fill(#444444);
278 pg.stroke(#292929);
279 pg.rect(0, 0, w-1, h-1);
280 }
281 }
282
283 public class UILabel extends UIObject {
284
285 private PFont font = defaultTitleFont;
286 private color fontColor = #CCCCCC;
287 private String label = "";
288
289 public UILabel(float x, float y, float w, float h) {
290 super(x, y, w, h);
291 }
292
293 protected void onDraw(PGraphics pg) {
294 pg.textAlign(LEFT, TOP);
295 pg.textFont(font);
296 pg.fill(fontColor);
297 pg.text(label, 0, 0);
298 }
299
300 public UILabel setFont(PFont font) {
301 this.font = font;
302 redraw();
303 return this;
304 }
305
306 public UILabel setFontColor(color fontColor) {
307 this.fontColor = fontColor;
308 redraw();
309 return this;
310 }
311
312 public UILabel setLabel(String label) {
313 this.label = label;
314 redraw();
315 return this;
316 }
317 }
318
319 public class UICheckbox extends UIButton {
320
321 private boolean firstDraw = true;
322
323 public UICheckbox(float x, float y, float w, float h) {
324 super(x, y, w, h);
325 setMomentary(false);
326 }
327
328 public void onDraw(PGraphics pg) {
329 pg.stroke(borderColor);
330 pg.fill(active ? activeColor : inactiveColor);
331 pg.rect(0, 0, h, h);
332 if (firstDraw) {
333 pg.fill(labelColor);
334 pg.textFont(defaultItemFont);
335 pg.textAlign(LEFT, CENTER);
336 pg.text(label, h + 4, h/2);
337 firstDraw = false;
338 }
339 }
340
341 }
342
343 public class UIButton extends UIObject {
344
345 protected boolean active = false;
346 protected boolean isMomentary = false;
347 protected color borderColor = #666666;
348 protected color inactiveColor = #222222;
349 protected color activeColor = #669966;
350 protected color labelColor = #999999;
351 protected String label = "";
352
353 public UIButton(float x, float y, float w, float h) {
354 super(x, y, w, h);
355 }
356
357 public UIButton setMomentary(boolean momentary) {
358 isMomentary = momentary;
359 return this;
360 }
361
362 protected void onDraw(PGraphics pg) {
363 pg.stroke(borderColor);
364 pg.fill(active ? activeColor : inactiveColor);
365 pg.rect(0, 0, w, h);
366 if (label != null && label.length() > 0) {
367 pg.fill(active ? #FFFFFF : labelColor);
368 pg.textFont(defaultItemFont);
369 pg.textAlign(CENTER);
370 pg.text(label, w/2, h-5);
371 }
372 }
373
374 protected void onMousePressed(float mx, float my) {
375 if (isMomentary) {
376 setActive(true);
377 } else {
378 setActive(!active);
379 }
380 }
381
382 protected void onMouseReleased(float mx, float my) {
383 if (isMomentary) {
384 setActive(false);
385 }
386 }
387
388 public boolean isActive() {
389 return active;
390 }
391
392 public UIButton setActive(boolean active) {
393 this.active = active;
394 onToggle(active);
395 redraw();
396 return this;
397 }
398
399 public UIButton toggle() {
400 return setActive(!active);
401 }
402
403 protected void onToggle(boolean active) {}
404
405 public UIButton setBorderColor(color borderColor) {
406 if (this.borderColor != borderColor) {
407 this.borderColor = borderColor;
408 redraw();
409 }
410 return this;
411 }
412
413 public UIButton setActiveColor(color activeColor) {
414 if (this.activeColor != activeColor) {
415 this.activeColor = activeColor;
416 if (active) {
417 redraw();
418 }
419 }
420 return this;
421 }
422
423 public UIButton setInactiveColor(color inactiveColor) {
424 if (this.inactiveColor != inactiveColor) {
425 this.inactiveColor = inactiveColor;
426 if (!active) {
427 redraw();
428 }
429 }
430 return this;
431 }
432
433 public UIButton setLabelColor(color labelColor) {
434 if (this.labelColor != labelColor) {
435 this.labelColor = labelColor;
436 redraw();
437 }
438 return this;
439 }
440
441 public UIButton setLabel(String label) {
442 if (!this.label.equals(label)) {
443 this.label = label;
444 redraw();
445 }
446 return this;
447 }
448
449 public void onMousePressed() {
450 setActive(!active);
451 }
452 }
453
454 public class UIToggleSet extends UIObject {
455
456 private String[] options;
457 private int[] boundaries;
458 private String value;
459
460 public UIToggleSet(float x, float y, float w, float h) {
461 super(x, y, w, h);
462 }
463
464 public UIToggleSet setOptions(String[] options) {
465 this.options = options;
466 boundaries = new int[options.length];
467 int totalLength = 0;
468 for (String s : options) {
469 totalLength += s.length();
470 }
471 int lengthSoFar = 0;
472 for (int i = 0; i < options.length; ++i) {
473 lengthSoFar += options[i].length();
474 boundaries[i] = (int) (lengthSoFar * w / totalLength);
475 }
476 value = options[0];
477 redraw();
478 return this;
479 }
480
481 public String getValue() {
482 return value;
483 }
484
485 public UIToggleSet setValue(String option) {
486 value = option;
487 onToggle(value);
488 redraw();
489 return this;
490 }
491
492 public void onDraw(PGraphics pg) {
493 pg.stroke(#666666);
494 pg.fill(#222222);
495 pg.rect(0, 0, w, h);
496 for (int b : boundaries) {
497 pg.line(b, 1, b, h-1);
498 }
499 pg.noStroke();
500 pg.textAlign(CENTER);
501 pg.textFont(defaultItemFont);
502 int leftBoundary = 0;
503
504 for (int i = 0; i < options.length; ++i) {
505 boolean isActive = options[i] == value;
506 if (isActive) {
507 pg.fill(lightGreen);
508 pg.rect(leftBoundary + 1, 1, boundaries[i] - leftBoundary - 1, h-1);
509 }
510 pg.fill(isActive ? #FFFFFF : #999999);
511 pg.text(options[i], (leftBoundary + boundaries[i]) / 2., h-6);
512 leftBoundary = boundaries[i];
513 }
514 }
515
516 public void onMousePressed(float mx, float my) {
517 for (int i = 0; i < boundaries.length; ++i) {
518 if (mx < boundaries[i]) {
519 setValue(options[i]);
520 break;
521 }
522 }
523 }
524
525 protected void onToggle(String option) {}
526
527 }
528
529
530 public abstract class UIParameterControl extends UIObject implements LXParameterListener {
531 protected LXParameter parameter = null;
532
533 protected UIParameterControl(float x, float y, float w, float h) {
534 super(x, y, w, h);
535 }
536
537 public void onParameterChanged(LXParameter parameter) {
538 redraw();
539 }
540
541 protected float getNormalized() {
542 if (parameter != null) {
543 if (parameter instanceof BasicParameter) {
544 return ((BasicParameter)parameter).getNormalizedf();
545 }
546 return parameter.getValuef();
547 }
548 return 0;
549 }
550
551 protected UIParameterControl setNormalized(float value) {
552 if (parameter != null) {
553 if (parameter instanceof BasicParameter) {
554 ((BasicParameter)parameter).setNormalized(value);
555 } else {
556 parameter.setValue(value);
557 }
558 }
559 return this;
560 }
561
562 public UIParameterControl setParameter(LXParameter parameter) {
563 if (this.parameter != null) {
564 if (this.parameter instanceof LXListenableParameter) {
565 ((LXListenableParameter)this.parameter).removeListener(this);
566 }
567 }
568 this.parameter = parameter;
569 if (this.parameter != null) {
570 if (this.parameter instanceof LXListenableParameter) {
571 ((LXListenableParameter)this.parameter).addListener(this);
572 }
573 }
574 redraw();
575 return this;
576 }
577 }
578
579 public class UIParameterKnob extends UIParameterControl {
580 private int knobSize = 28;
581 private final float knobIndent = .4;
582 private final int knobLabelHeight = 14;
583 private boolean showValue = false;
584
585 public UIParameterKnob(float x, float y) {
586 this(x, y, 0, 0);
587 setSize(knobSize, knobSize + knobLabelHeight);
588 }
589
590 public UIParameterKnob(float x, float y, float w, float h) {
591 super(x, y, w, h);
592 }
593
594 protected void onDraw(PGraphics pg) {
595 float knobValue = getNormalized();
596
597 pg.ellipseMode(CENTER);
598 pg.noStroke();
599
600 pg.fill(bgGray);
601 pg.rect(0, 0, knobSize, knobSize);
602
603 // Full outer dark ring
604 pg.fill(#222222);
605 pg.arc(knobSize/2, knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, HALF_PI + knobIndent + (TWO_PI-2*knobIndent));
606
607 // Light ring indicating value
608 pg.fill(lightGreen);
609 pg.arc(knobSize/2, knobSize/2, knobSize, knobSize, HALF_PI + knobIndent, HALF_PI + knobIndent + knobValue*(TWO_PI-2*knobIndent));
610
611 // Center circle of knob
612 pg.fill(#333333);
613 pg.ellipse(knobSize/2, knobSize/2, knobSize/2, knobSize/2);
614
615 String knobLabel;
616 if (showValue) {
617 knobLabel = (parameter != null) ? ("" + parameter.getValue()) : null;
618 } else {
619 knobLabel = (parameter != null) ? parameter.getLabel() : null;
620 }
621 if (knobLabel == null) {
622 knobLabel = "-";
623 } else if (knobLabel.length() > 4) {
624 knobLabel = knobLabel.substring(0, 4);
625 }
626 pg.fill(#000000);
627 pg.rect(0, knobSize + 2, knobSize, knobLabelHeight - 2);
628 pg.fill(#999999);
629 pg.textAlign(CENTER);
630 pg.textFont(defaultTitleFont);
631 pg.text(knobLabel, knobSize/2, knobSize + knobLabelHeight - 2);
632 }
633
634 private long lastMousePress = 0;
635 public void onMousePressed(float mx, float my) {
636 super.onMousePressed(mx, my);
637 long now = millis();
638 if (now - lastMousePress < DOUBLE_CLICK_THRESHOLD) {
639 parameter.reset();
640 lastMousePress = 0;
641 } else {
642 lastMousePress = now;
643 }
644 showValue = true;
645 redraw();
646 }
647
648 public void onMouseReleased(float mx, float my) {
649 showValue = false;
650 redraw();
651 }
652
653 public void onMouseDragged(float mx, float my, float dx, float dy) {
654 float value = constrain(getNormalized() - dy / 100., 0, 1);
655 setNormalized(value);
656 }
657 }
658
659 public class UIParameterSlider extends UIParameterControl {
660
661 private static final float handleWidth = 12;
662
663 UIParameterSlider(float x, float y, float w, float h) {
664 super(x, y, w, h);
665 }
666
667 protected void onDraw(PGraphics pg) {
668 pg.noStroke();
669 pg.fill(#333333);
670 pg.rect(0, 0, w, h);
671 pg.fill(#222222);
672 pg.rect(4, h/2-2, w-8, 4);
673 pg.fill(#666666);
674 pg.stroke(#222222);
675 pg.rect((int) (4 + parameter.getValuef() * (w-8-handleWidth)), 4, handleWidth, h-8);
676 }
677
678 private boolean editing = false;
679 private long lastClick = 0;
680 private float doubleClickMode = 0;
681 private float doubleClickX = 0;
682 protected void onMousePressed(float mx, float my) {
683 long now = millis();
684 float handleLeft = 4 + getNormalized() * (w-8-handleWidth);
685 if (mx >= handleLeft && mx < handleLeft + handleWidth) {
686 editing = true;
687 } else {
688 if ((now - lastClick) < DOUBLE_CLICK_THRESHOLD && abs(mx - doubleClickX) < 3) {
689 setNormalized(doubleClickMode);
690 }
691 doubleClickX = mx;
692 if (mx < w*.25) {
693 doubleClickMode = 0;
694 } else if (mx > w*.75) {
695 doubleClickMode = 1;
696 } else {
697 doubleClickMode = 0.5;
698 }
699 }
700 lastClick = now;
701 }
702
703 protected void onMouseReleased(float mx, float my) {
704 editing = false;
705 }
706
707 protected void onMouseDragged(float mx, float my, float dx, float dy) {
708 if (editing) {
709 setNormalized(constrain((mx - handleWidth/2. - 4) / (w-8-handleWidth), 0, 1));
710 }
711 }
712 }
713
714 public class UIScrollList extends UIObject {
715
716 private List<ScrollItem> items = new ArrayList<ScrollItem>();
717
718 private PFont itemFont = defaultItemFont;
719 private int itemHeight = 20;
720 private color selectedColor = lightGreen;
721 private color pendingColor = lightBlue;
722 private int scrollOffset = 0;
723 private int numVisibleItems = 0;
724
725 private boolean hasScroll;
726 private float scrollYStart;
727 private float scrollYHeight;
728
729 public UIScrollList(float x, float y, float w, float h) {
730 super(x, y, w, h);
731 }
732
733 protected void onDraw(PGraphics pg) {
734 int yp = 0;
735 boolean even = true;
736 for (int i = 0; i < numVisibleItems; ++i) {
737 if (i + scrollOffset >= items.size()) {
738 break;
739 }
740 ScrollItem item = items.get(i + scrollOffset);
741 color itemColor;
742 color labelColor = #FFFFFF;
743 if (item.isSelected()) {
744 itemColor = selectedColor;
745 } else if (item.isPending()) {
746 itemColor = pendingColor;
747 } else {
748 labelColor = #000000;
749 itemColor = #707070;
750 }
751 float factor = even ? .92 : 1.08;
752 itemColor = lx.scaleBrightness(itemColor, factor);
753
754 pg.noStroke();
755 pg.fill(itemColor);
756 pg.rect(0, yp, w, itemHeight);
757 pg.fill(labelColor);
758 pg.textFont(itemFont);
759 pg.textAlign(LEFT, TOP);
760 pg.text(item.getLabel(), 6, yp+4);
761
762 yp += itemHeight;
763 even = !even;
764 }
765 if (hasScroll) {
766 pg.noStroke();
767 pg.fill(0x26ffffff);
768 pg.rect(w-12, 0, 12, h);
769 pg.fill(#333333);
770 pg.rect(w-12, scrollYStart, 12, scrollYHeight);
771 }
772
773 }
774
775 private boolean scrolling = false;
776 private ScrollItem pressedItem = null;
777
778 public void onMousePressed(float mx, float my) {
779 pressedItem = null;
780 if (hasScroll && mx >= w-12) {
781 if (my >= scrollYStart && my < (scrollYStart + scrollYHeight)) {
782 scrolling = true;
783 dAccum = 0;
784 }
785 } else {
786 int index = (int) my / itemHeight;
787 if (scrollOffset + index < items.size()) {
788 pressedItem = items.get(scrollOffset + index);
789 pressedItem.onMousePressed();
790 redraw();
791 }
792 }
793 }
794
795 public void onMouseReleased(float mx, float my) {
796 scrolling = false;
797 if (pressedItem != null) {
798 pressedItem.onMouseReleased();
799 redraw();
800 }
801 }
802
803 private float dAccum = 0;
804 public void onMouseDragged(float mx, float my, float dx, float dy) {
805 if (scrolling) {
806 dAccum += dy;
807 float scrollOne = h / items.size();
808 int offset = (int) (dAccum / scrollOne);
809 if (offset != 0) {
810 dAccum -= offset * scrollOne;
811 setScrollOffset(scrollOffset + offset);
812 }
813 }
814 }
815
816 private float wAccum = 0;
817 public void onMouseWheel(float mx, float my, float delta) {
818 wAccum += delta;
819 int offset = (int) (wAccum / 5);
820 if (offset != 0) {
821 wAccum -= offset * 5;
822 setScrollOffset(scrollOffset + offset);
823 }
824 }
825
826 public void setScrollOffset(int offset) {
827 scrollOffset = constrain(offset, 0, max(0, items.size() - numVisibleItems));
828 scrollYStart = round(scrollOffset * h / items.size());
829 scrollYHeight = round(numVisibleItems * h / items.size());
830 redraw();
831 }
832
833 public UIScrollList setItems(List<ScrollItem> items) {
834 this.items = items;
835 numVisibleItems = (int) (h / itemHeight);
836 hasScroll = items.size() > numVisibleItems;
837 setScrollOffset(0);
838 redraw();
839 return this;
840 }
841 }
842
843 public interface ScrollItem {
844 public boolean isSelected();
845 public boolean isPending();
846 public String getLabel();
847 public void onMousePressed();
848 public void onMouseReleased();
849 }
850
851 public abstract class AbstractScrollItem implements ScrollItem {
852 public boolean isPending() {
853 return false;
854 }
855 public void select() {}
856 public void onMousePressed() {}
857 public void onMouseReleased() {}
858 }
859
860 public class UIIntegerBox extends UIObject {
861
862 private int minValue = 0;
863 private int maxValue = MAX_INT;
864 private int value = 0;
865
866 UIIntegerBox(float x, float y, float w, float h) {
867 super(x, y, w, h);
868 }
869
870 public UIIntegerBox setRange(int minValue, int maxValue) {
871 this.minValue = minValue;
872 this.maxValue = maxValue;
873 setValue(constrain(value, minValue, maxValue));
874 return this;
875 }
876
877 protected void onDraw(PGraphics pg) {
878 pg.stroke(#666666);
879 pg.fill(#222222);
880 pg.rect(0, 0, w, h);
881 pg.textAlign(CENTER, CENTER);
882 pg.textFont(defaultItemFont);
883 pg.fill(#999999);
884 pg.text("" + value, w/2, h/2);
885 }
886
887 protected void onValueChange(int value) {}
888
889 float dAccum = 0;
890 protected void onMousePressed(float mx, float my) {
891 dAccum = 0;
892 }
893
894 protected void onMouseDragged(float mx, float my, float dx, float dy) {
895 dAccum -= dy;
896 int offset = (int) (dAccum / 5);
897 dAccum = dAccum - (offset * 5);
898 setValue(value + offset);
899 }
900
901 public int getValue() {
902 return value;
903 }
904
905 public UIIntegerBox setValue(int value) {
906 if (this.value != value) {
907 int range = (maxValue - minValue + 1);
908 while (value < minValue) {
909 value += range;
910 }
911 this.value = minValue + (value - minValue) % range;
912 this.onValueChange(this.value);
913 redraw();
914 }
915 return this;
916 }
917 }