ResizeHandle.hpp (6327B)
1 /* 2 * Resize handle for DPF 3 * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any purpose with 6 * or without fee is hereby granted, provided that the above copyright notice and this 7 * permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN 11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #pragma once 18 19 #include "TopLevelWidget.hpp" 20 #include "Color.hpp" 21 22 #if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) 23 #include "OpenGL-include.hpp" 24 #endif 25 26 START_NAMESPACE_DGL 27 28 /** Resize handle for DPF windows, will sit on bottom-right. */ 29 class ResizeHandle : public TopLevelWidget 30 { 31 public: 32 /** Constructor for placing this handle on top of a window. */ 33 explicit ResizeHandle(Window& window) 34 : TopLevelWidget(window), 35 handleSize(16), 36 hasCursor(false), 37 isResizing(false) 38 { 39 resetArea(); 40 } 41 42 /** Overloaded constructor, will fetch the window from an existing top-level widget. */ 43 explicit ResizeHandle(TopLevelWidget* const tlw) 44 : TopLevelWidget(tlw->getWindow()), 45 handleSize(16), 46 hasCursor(false), 47 isResizing(false) 48 { 49 resetArea(); 50 } 51 52 /** Set the handle size, minimum 16. 53 * Scale factor is automatically applied on top of this size as needed */ 54 void setHandleSize(const uint size) 55 { 56 handleSize = std::max(16u, size); 57 resetArea(); 58 } 59 60 protected: 61 void onDisplay() override 62 { 63 // TODO implement gl3 stuff in DPF 64 #ifndef DGL_USE_OPENGL3 65 const GraphicsContext& context(getGraphicsContext()); 66 const double lineWidth = 1.0 * getScaleFactor(); 67 68 #if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) 69 glMatrixMode(GL_MODELVIEW); 70 #endif 71 72 // draw white lines, 1px wide 73 Color(1.0f, 1.0f, 1.0f).setFor(context); 74 l1.draw(context, lineWidth); 75 l2.draw(context, lineWidth); 76 l3.draw(context, lineWidth); 77 78 // draw black lines, offset by 1px and 1px wide 79 Color(0.0f, 0.0f, 0.0f).setFor(context); 80 Line<double> l1b(l1), l2b(l2), l3b(l3); 81 l1b.moveBy(lineWidth, lineWidth); 82 l2b.moveBy(lineWidth, lineWidth); 83 l3b.moveBy(lineWidth, lineWidth); 84 l1b.draw(context, lineWidth); 85 l2b.draw(context, lineWidth); 86 l3b.draw(context, lineWidth); 87 #endif 88 } 89 90 bool onMouse(const MouseEvent& ev) override 91 { 92 if (ev.button != 1) 93 return false; 94 95 if (ev.press && area.contains(ev.pos)) 96 { 97 isResizing = true; 98 resizingSize = Size<double>(getWidth(), getHeight()); 99 lastResizePoint = ev.pos; 100 return true; 101 } 102 103 if (isResizing && ! ev.press) 104 { 105 isResizing = false; 106 recheckCursor(ev.pos); 107 return true; 108 } 109 110 return false; 111 } 112 113 bool onMotion(const MotionEvent& ev) override 114 { 115 if (! isResizing) 116 { 117 recheckCursor(ev.pos); 118 return false; 119 } 120 121 const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(), 122 ev.pos.getY() - lastResizePoint.getY()); 123 124 resizingSize += offset; 125 lastResizePoint = ev.pos; 126 127 // TODO keepAspectRatio 128 bool keepAspectRatio; 129 const Size<uint> minSize(getWindow().getGeometryConstraints(keepAspectRatio)); 130 const uint minWidth = minSize.getWidth(); 131 const uint minHeight = minSize.getHeight(); 132 133 if (resizingSize.getWidth() < minWidth) 134 resizingSize.setWidth(minWidth); 135 if (resizingSize.getWidth() > 16384) 136 resizingSize.setWidth(16384); 137 if (resizingSize.getHeight() < minHeight) 138 resizingSize.setHeight(minHeight); 139 if (resizingSize.getHeight() > 16384) 140 resizingSize.setHeight(16384); 141 142 setSize(resizingSize.getWidth(), resizingSize.getHeight()); 143 return true; 144 } 145 146 void onResize(const ResizeEvent& ev) override 147 { 148 TopLevelWidget::onResize(ev); 149 resetArea(); 150 } 151 152 private: 153 Rectangle<uint> area; 154 Line<double> l1, l2, l3; 155 uint handleSize; 156 157 // event handling state 158 bool hasCursor, isResizing; 159 Point<double> lastResizePoint; 160 Size<double> resizingSize; 161 162 void recheckCursor(const Point<double>& pos) 163 { 164 const bool shouldHaveCursor = area.contains(pos); 165 166 if (shouldHaveCursor == hasCursor) 167 return; 168 169 hasCursor = shouldHaveCursor; 170 setCursor(shouldHaveCursor ? kMouseCursorUpLeftDownRight : kMouseCursorArrow); 171 } 172 173 void resetArea() 174 { 175 const double scaleFactor = getScaleFactor(); 176 const uint margin = 0.0 * scaleFactor; 177 const uint size = handleSize * scaleFactor; 178 179 area = Rectangle<uint>(getWidth() - size - margin, 180 getHeight() - size - margin, 181 size, size); 182 183 recreateLines(area.getX(), area.getY(), size); 184 } 185 186 void recreateLines(const uint x, const uint y, const uint size) 187 { 188 uint linesize = size; 189 uint offset = 0; 190 191 // 1st line, full diagonal size 192 l1.setStartPos(x + size, y); 193 l1.setEndPos(x, y + size); 194 195 // 2nd line, bit more to the right and down, cropped 196 offset += size / 3; 197 linesize -= size / 3; 198 l2.setStartPos(x + linesize + offset, y + offset); 199 l2.setEndPos(x + offset, y + linesize + offset); 200 201 // 3rd line, even more right and down 202 offset += size / 3; 203 linesize -= size / 3; 204 l3.setStartPos(x + linesize + offset, y + offset); 205 l3.setEndPos(x + offset, y + linesize + offset); 206 } 207 208 DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) 209 }; 210 211 END_NAMESPACE_DGL