|
|
@@ -0,0 +1,207 @@ |
|
|
|
/* |
|
|
|
* Resize handle for DPF |
|
|
|
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> |
|
|
|
* |
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any purpose with |
|
|
|
* or without fee is hereby granted, provided that the above copyright notice and this |
|
|
|
* permission notice appear in all copies. |
|
|
|
* |
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
|
|
|
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN |
|
|
|
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
|
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
|
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
|
|
*/ |
|
|
|
|
|
|
|
#pragma once |
|
|
|
|
|
|
|
#include "../../dgl/Color.hpp" |
|
|
|
#include "../../dgl/TopLevelWidget.hpp" |
|
|
|
|
|
|
|
START_NAMESPACE_DGL |
|
|
|
|
|
|
|
/** Resize handle for DPF windows, will sit on bottom-right. */ |
|
|
|
class ResizeHandle : public TopLevelWidget |
|
|
|
{ |
|
|
|
public: |
|
|
|
/** Constructor for placing this handle on top of a window. */ |
|
|
|
explicit ResizeHandle(Window& window) |
|
|
|
: TopLevelWidget(window), |
|
|
|
handleSize(16), |
|
|
|
hasCursor(false), |
|
|
|
isResizing(false) |
|
|
|
{ |
|
|
|
resetArea(); |
|
|
|
} |
|
|
|
|
|
|
|
/** Overloaded constructor, will fetch the window from an existing top-level widget. */ |
|
|
|
explicit ResizeHandle(TopLevelWidget* const tlw) |
|
|
|
: TopLevelWidget(tlw->getWindow()), |
|
|
|
handleSize(16), |
|
|
|
hasCursor(false), |
|
|
|
isResizing(false) |
|
|
|
{ |
|
|
|
resetArea(); |
|
|
|
} |
|
|
|
|
|
|
|
/** Set the handle size, minimum 16. |
|
|
|
* Scale factor is automatically applied on top of this size as needed */ |
|
|
|
void setHandleSize(const uint size) |
|
|
|
{ |
|
|
|
handleSize = std::max(16u, size); |
|
|
|
resetArea(); |
|
|
|
} |
|
|
|
|
|
|
|
protected: |
|
|
|
void onDisplay() override |
|
|
|
{ |
|
|
|
// TODO implement gl3 stuff in DPF |
|
|
|
#ifndef DGL_USE_OPENGL3 |
|
|
|
const GraphicsContext& context(getGraphicsContext()); |
|
|
|
const double lineWidth = 1.0 * getScaleFactor(); |
|
|
|
|
|
|
|
#if defined(DGL_OPENGL) && !defined(DGL_USE_OPENGL3) |
|
|
|
glMatrixMode(GL_MODELVIEW); |
|
|
|
#endif |
|
|
|
|
|
|
|
// draw white lines, 1px wide |
|
|
|
Color(1.0f, 1.0f, 1.0f).setFor(context); |
|
|
|
l1.draw(context, lineWidth); |
|
|
|
l2.draw(context, lineWidth); |
|
|
|
l3.draw(context, lineWidth); |
|
|
|
|
|
|
|
// draw black lines, offset by 1px and 1px wide |
|
|
|
Color(0.0f, 0.0f, 0.0f).setFor(context); |
|
|
|
Line<double> l1b(l1), l2b(l2), l3b(l3); |
|
|
|
l1b.moveBy(lineWidth, lineWidth); |
|
|
|
l2b.moveBy(lineWidth, lineWidth); |
|
|
|
l3b.moveBy(lineWidth, lineWidth); |
|
|
|
l1b.draw(context, lineWidth); |
|
|
|
l2b.draw(context, lineWidth); |
|
|
|
l3b.draw(context, lineWidth); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
bool onMouse(const MouseEvent& ev) override |
|
|
|
{ |
|
|
|
if (ev.button != 1) |
|
|
|
return false; |
|
|
|
|
|
|
|
if (ev.press && area.contains(ev.pos)) |
|
|
|
{ |
|
|
|
isResizing = true; |
|
|
|
resizingSize = Size<double>(getWidth(), getHeight()); |
|
|
|
lastResizePoint = ev.pos; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
if (isResizing && ! ev.press) |
|
|
|
{ |
|
|
|
isResizing = false; |
|
|
|
recheckCursor(ev.pos); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bool onMotion(const MotionEvent& ev) override |
|
|
|
{ |
|
|
|
if (! isResizing) |
|
|
|
{ |
|
|
|
recheckCursor(ev.pos); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(), |
|
|
|
ev.pos.getY() - lastResizePoint.getY()); |
|
|
|
|
|
|
|
resizingSize += offset; |
|
|
|
lastResizePoint = ev.pos; |
|
|
|
|
|
|
|
// TODO keepAspectRatio |
|
|
|
bool keepAspectRatio; |
|
|
|
const Size<uint> minSize(getWindow().getGeometryConstraints(keepAspectRatio)); |
|
|
|
const uint minWidth = minSize.getWidth(); |
|
|
|
const uint minHeight = minSize.getHeight(); |
|
|
|
|
|
|
|
if (resizingSize.getWidth() < minWidth) |
|
|
|
resizingSize.setWidth(minWidth); |
|
|
|
if (resizingSize.getWidth() > 16384) |
|
|
|
resizingSize.setWidth(16384); |
|
|
|
if (resizingSize.getHeight() < minHeight) |
|
|
|
resizingSize.setHeight(minHeight); |
|
|
|
if (resizingSize.getHeight() > 16384) |
|
|
|
resizingSize.setHeight(16384); |
|
|
|
|
|
|
|
setSize(resizingSize.getWidth(), resizingSize.getHeight()); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void onResize(const ResizeEvent& ev) override |
|
|
|
{ |
|
|
|
TopLevelWidget::onResize(ev); |
|
|
|
resetArea(); |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
Rectangle<uint> area; |
|
|
|
Line<double> l1, l2, l3; |
|
|
|
uint handleSize; |
|
|
|
|
|
|
|
// event handling state |
|
|
|
bool hasCursor, isResizing; |
|
|
|
Point<double> lastResizePoint; |
|
|
|
Size<double> resizingSize; |
|
|
|
|
|
|
|
void recheckCursor(const Point<double>& pos) |
|
|
|
{ |
|
|
|
const bool shouldHaveCursor = area.contains(pos); |
|
|
|
|
|
|
|
if (shouldHaveCursor == hasCursor) |
|
|
|
return; |
|
|
|
|
|
|
|
hasCursor = shouldHaveCursor; |
|
|
|
setCursor(shouldHaveCursor ? kMouseCursorDiagonal : kMouseCursorArrow); |
|
|
|
} |
|
|
|
|
|
|
|
void resetArea() |
|
|
|
{ |
|
|
|
const double scaleFactor = getScaleFactor(); |
|
|
|
const uint margin = 0.0 * scaleFactor; |
|
|
|
const uint size = handleSize * scaleFactor; |
|
|
|
|
|
|
|
area = Rectangle<uint>(getWidth() - size - margin, |
|
|
|
getHeight() - size - margin, |
|
|
|
size, size); |
|
|
|
|
|
|
|
recreateLines(area.getX(), area.getY(), size); |
|
|
|
} |
|
|
|
|
|
|
|
void recreateLines(const uint x, const uint y, const uint size) |
|
|
|
{ |
|
|
|
uint linesize = size; |
|
|
|
uint offset = 0; |
|
|
|
|
|
|
|
// 1st line, full diagonal size |
|
|
|
l1.setStartPos(x + size, y); |
|
|
|
l1.setEndPos(x, y + size); |
|
|
|
|
|
|
|
// 2nd line, bit more to the right and down, cropped |
|
|
|
offset += size / 3; |
|
|
|
linesize -= size / 3; |
|
|
|
l2.setStartPos(x + linesize + offset, y + offset); |
|
|
|
l2.setEndPos(x + offset, y + linesize + offset); |
|
|
|
|
|
|
|
// 3rd line, even more right and down |
|
|
|
offset += size / 3; |
|
|
|
linesize -= size / 3; |
|
|
|
l3.setStartPos(x + linesize + offset, y + offset); |
|
|
|
l3.setEndPos(x + offset, y + linesize + offset); |
|
|
|
} |
|
|
|
|
|
|
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) |
|
|
|
}; |
|
|
|
|
|
|
|
END_NAMESPACE_DGL |