1 /**
2 A widget to place at the bottom-right of your UI. It resizes the plugin, based upon valid sizes given by `SizeContstraints`.
3
4 This is also an usage example for dplug:canvas.
5
6 Copyright: Guillaume Piolat 2021.
7 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 */
9 module dplug.flatwidgets.windowresizer;
10
11 // THIS IS A WORK IN PROGRESS, DOESN'T WORK YET.
12
13 import dplug.gui.element;
14 import dplug.canvas;
15
16 ///
17 class UIWindowResizer : UIElement
18 {
19 public:
20 nothrow:
21 @nogc:
22
23 @ScriptProperty
24 {
25 /// Color of the stripes in nominal state.
26 RGBA color = RGBA(255, 255, 255, 96);
27
28 /// Color of the stripes when hovered by the mouse.
29 RGBA colorHovered = RGBA(255, 255, 255, 150);
30
31 /// Color of the stripes when dragged / resizing.
32 RGBA colorDragged = RGBA(255, 255, 128, 200);
33
34 /// Color of the stripes when a resize operation failed.
35 RGBA colorCannotResize = RGBA(255, 96, 96, 200);
36
37 /// Time in seconds spent indicating failure to resize.
38 float failureDisplayTime = 1.2f;
39 }
40
41 /// Construct a new `UIWindowResizer`.
42 /// Recommended size is around 20x20 whatever the UI size, and on the bottom-right.
43 this(UIContext context)
44 {
45 super(context, flagRaw | flagAnimated);
46 setCursorWhenMouseOver(MouseCursor.diagonalResize);
47 setCursorWhenDragged(MouseCursor.diagonalResize);
48 }
49
50 override Click onMouseClick(int x, int y, int button, bool isDoubleClick, MouseState mstate)
51 {
52 // Initiate drag
53 setDirtyWhole();
54
55 _sizeBeforeDrag = context.getUISizeInPixelsLogical();
56 _accumX = 0;
57 _accumY = 0;
58
59 return Click.startDrag;
60 }
61
62 override void onMouseDrag(int x, int y, int dx, int dy, MouseState mstate)
63 {
64 vec2i size = _sizeBeforeDrag;
65
66 // Cumulative mouse movement since dragging started
67 _accumX += dx; // TODO: divide that by user scale
68 _accumY += dy;
69 size.x += _accumX;
70 size.y += _accumY;
71
72 // Find nearest valid _logical_ size.
73 context.getUINearestValidSize(&size.x, &size.y);
74
75 // Attempt to resize window with that size.
76 bool success = context.requestUIResize(size.x, size.y);
77
78 if (!success)
79 {
80 _timeDisplayError = failureDisplayTime;
81 }
82 }
83
84 override void onAnimate(double dt, double time) nothrow @nogc
85 {
86 if (_timeDisplayError > 0.0f)
87 {
88 _timeDisplayError = _timeDisplayError - dt;
89 if (_timeDisplayError < 0) _timeDisplayError = 0;
90 setDirtyWhole();
91 }
92 }
93
94 override void onDrawRaw(ImageRef!RGBA rawMap, box2i[] dirtyRects)
95 {
96 float w = position.width;
97 float h = position.height;
98
99 foreach(dirtyRect; dirtyRects)
100 {
101 auto cRaw = rawMap.cropImageRef(dirtyRect);
102 canvas.initialize(cRaw);
103 canvas.translate(-dirtyRect.min.x, -dirtyRect.min.y);
104
105 // Makes a 3 lines hint like in JUCE or iZotope plugins.
106 // This seems to be easiest to understand for users.
107
108 RGBA c = color;
109 if (isMouseOver)
110 c = colorHovered;
111 if (isDragged)
112 c = colorDragged;
113 if (_timeDisplayError > 0)
114 c = colorCannotResize;
115
116 canvas.fillStyle = c;
117
118 canvas.beginPath;
119 canvas.moveTo(w*0/5, h*5/5);
120 canvas.lineTo(w*5/5, h*0/5);
121 canvas.lineTo(w*5/5, h*1/5);
122 canvas.lineTo(w*1/5, h*5/5);
123 canvas.lineTo(w*0/5, h*5/5);
124
125 canvas.moveTo(w*2/5, h*5/5);
126 canvas.lineTo(w*5/5, h*2/5);
127 canvas.lineTo(w*5/5, h*3/5);
128 canvas.lineTo(w*3/5, h*5/5);
129 canvas.lineTo(w*2/5, h*5/5);
130
131 canvas.moveTo(w*4/5, h*5/5);
132 canvas.lineTo(w*5/5, h*4/5);
133 canvas.lineTo(w*5/5, h*5/5);
134 canvas.lineTo(w*4/5, h*5/5);
135
136 canvas.fill();
137 }
138 }
139
140 override bool contains(int x, int y)
141 {
142 if (!context.isUIResizable())
143 return false; // not clickable if UI not resizeable
144
145 return super.contains(x, y);
146 }
147
148 // Account for color changes
149
150 override void onBeginDrag()
151 {
152 setDirtyWhole();
153 }
154
155 override void onStopDrag()
156 {
157 setDirtyWhole();
158 }
159
160 override void onMouseEnter()
161 {
162 setDirtyWhole();
163 }
164
165 override void onMouseExit()
166 {
167 setDirtyWhole();
168 }
169
170 private:
171 Canvas canvas;
172 vec2i _sizeBeforeDrag;
173 int _accumX;
174 int _accumY;
175
176 float _timeDisplayError = 0.0f;
177 }