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 }