DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

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