Signed-off-by: falkTX <falktx@falktx.com>pull/321/head
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -23,6 +23,7 @@ | |||||
#ifdef DGL_OPENGL | #ifdef DGL_OPENGL | ||||
#include "widgets/ExampleTextWidget.hpp" | #include "widgets/ExampleTextWidget.hpp" | ||||
#endif | #endif | ||||
#include "widgets/ResizeHandle.hpp" | |||||
#include "demo_res/DemoArtwork.cpp" | #include "demo_res/DemoArtwork.cpp" | ||||
#include "images_res/CatPics.cpp" | #include "images_res/CatPics.cpp" | ||||
@@ -250,155 +251,6 @@ private: | |||||
#endif | #endif | ||||
}; | }; | ||||
// -------------------------------------------------------------------------------------------------------------------- | |||||
// Resize handle widget | |||||
class ResizeHandle : public TopLevelWidget | |||||
{ | |||||
Rectangle<uint> area; | |||||
Line<double> l1, l2, l3; | |||||
uint baseSize; | |||||
// event handling state | |||||
bool resizing; | |||||
Point<double> lastResizePoint; | |||||
Size<double> resizingSize; | |||||
public: | |||||
explicit ResizeHandle(TopLevelWidget* const tlw) | |||||
: TopLevelWidget(tlw->getWindow()), | |||||
baseSize(16), | |||||
resizing(false) | |||||
{ | |||||
resetArea(); | |||||
} | |||||
explicit ResizeHandle(Window& window) | |||||
: TopLevelWidget(window), | |||||
baseSize(16), | |||||
resizing(false) | |||||
{ | |||||
resetArea(); | |||||
} | |||||
void setBaseSize(const uint size) | |||||
{ | |||||
baseSize = std::max(16u, size); | |||||
resetArea(); | |||||
} | |||||
protected: | |||||
void onDisplay() override | |||||
{ | |||||
const GraphicsContext& context(getGraphicsContext()); | |||||
const double offset = getScaleFactor(); | |||||
// draw white lines, 1px wide | |||||
Color(1.0f, 1.0f, 1.0f).setFor(context); | |||||
l1.draw(context, 1); | |||||
l2.draw(context, 1); | |||||
l3.draw(context, 1); | |||||
// draw black lines, offset by 1px and 2px wide | |||||
Color(0.0f, 0.0f, 0.0f).setFor(context); | |||||
Line<double> l1b(l1), l2b(l2), l3b(l3); | |||||
l1b.moveBy(offset, offset); | |||||
l2b.moveBy(offset, offset); | |||||
l3b.moveBy(offset, offset); | |||||
l1b.draw(context, 1); | |||||
l2b.draw(context, 1); | |||||
l3b.draw(context, 1); | |||||
} | |||||
bool onMouse(const MouseEvent& ev) override | |||||
{ | |||||
if (ev.button != 1) | |||||
return false; | |||||
if (ev.press && area.contains(ev.pos)) | |||||
{ | |||||
resizing = true; | |||||
resizingSize = Size<double>(getWidth(), getHeight()); | |||||
lastResizePoint = ev.pos; | |||||
return true; | |||||
} | |||||
if (resizing && ! ev.press) | |||||
{ | |||||
resizing = false; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool onMotion(const MotionEvent& ev) override | |||||
{ | |||||
if (! resizing) | |||||
return false; | |||||
const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(), | |||||
ev.pos.getY() - lastResizePoint.getY()); | |||||
resizingSize += offset; | |||||
lastResizePoint = ev.pos; | |||||
// TODO min width, min height | |||||
const uint minWidth = 16; | |||||
const uint minHeight = 16; | |||||
if (resizingSize.getWidth() < minWidth) | |||||
resizingSize.setWidth(minWidth); | |||||
if (resizingSize.getHeight() < minHeight) | |||||
resizingSize.setHeight(minHeight); | |||||
setSize(resizingSize.getWidth(), resizingSize.getHeight()); | |||||
return true; | |||||
} | |||||
void onResize(const ResizeEvent& ev) override | |||||
{ | |||||
TopLevelWidget::onResize(ev); | |||||
resetArea(); | |||||
} | |||||
private: | |||||
void resetArea() | |||||
{ | |||||
const double scaleFactor = getScaleFactor(); | |||||
const uint margin = 0.0 * scaleFactor; | |||||
const uint size = baseSize * 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); | |||||
} | |||||
}; | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
// Main Demo Window, having a left-side tab-like widget and main area for current widget | // Main Demo Window, having a left-side tab-like widget and main area for current widget | ||||
@@ -535,6 +387,7 @@ void createAndShowExampleWidgetStandaloneWindow(Application& app) | |||||
{ | { | ||||
ExampleWidgetStandaloneWindow swin(app); | ExampleWidgetStandaloneWindow swin(app); | ||||
const double scaleFactor = swin.getScaleFactor(); | const double scaleFactor = swin.getScaleFactor(); | ||||
swin.setGeometryConstraints(128 * scaleFactor, 128 * scaleFactor); | |||||
swin.setResizable(true); | swin.setResizable(true); | ||||
swin.setSize(600 * scaleFactor, 500 * scaleFactor); | swin.setSize(600 * scaleFactor, 500 * scaleFactor); | ||||
swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); | swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); | ||||
@@ -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 |