Browse Source

Merge pull request #281 from DISTRHO/rework-core-resize

Reworked resize handling
pull/282/head
Filipe Coelho GitHub 4 years ago
parent
commit
d7382324ed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 976 additions and 515 deletions
  1. +9
    -9
      Makefile
  2. +21
    -0
      dgl/TopLevelWidget.hpp
  3. +12
    -1
      dgl/Window.hpp
  4. +1
    -2
      dgl/src/SubWidget.cpp
  5. +20
    -0
      dgl/src/TopLevelWidget.cpp
  6. +2
    -3
      dgl/src/TopLevelWidgetPrivateData.cpp
  7. +2
    -0
      dgl/src/Widget.cpp
  8. +34
    -6
      dgl/src/Window.cpp
  9. +81
    -37
      dgl/src/WindowPrivateData.cpp
  10. +6
    -3
      dgl/src/WindowPrivateData.hpp
  11. +37
    -3
      dgl/src/pugl.cpp
  12. +5
    -1
      dgl/src/pugl.hpp
  13. +49
    -11
      distrho/DistrhoUI.hpp
  14. +3
    -0
      distrho/extra/ScopedPointer.hpp
  15. +5
    -16
      distrho/src/DistrhoPluginJack.cpp
  16. +4
    -11
      distrho/src/DistrhoPluginVST.cpp
  17. +39
    -67
      distrho/src/DistrhoUI.cpp
  18. +24
    -33
      distrho/src/DistrhoUIDSSI.cpp
  19. +112
    -255
      distrho/src/DistrhoUIInternal.hpp
  20. +15
    -29
      distrho/src/DistrhoUILV2.cpp
  21. +147
    -23
      distrho/src/DistrhoUIPrivateData.hpp
  22. +21
    -5
      examples/Info/InfoExampleUI.cpp
  23. +176
    -0
      examples/Info/ResizeHandle.hpp
  24. +151
    -0
      tests/Demo.cpp

+ 9
- 9
Makefile View File

@@ -32,15 +32,15 @@ examples: dgl
ifeq ($(HAVE_CAIRO),true) ifeq ($(HAVE_CAIRO),true)
$(MAKE) all -C examples/CairoUI $(MAKE) all -C examples/CairoUI
endif endif
ifneq ($(MACOS_OR_WINDOWS),true)
# ExternalUI is WIP
$(MAKE) all -C examples/ExternalUI
install -d bin/d_extui-dssi
install -d bin/d_extui.lv2
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.sh
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui-dssi/d_extui.sh
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.lv2/d_extui.sh
endif
# ifneq ($(MACOS_OR_WINDOWS),true)
# # ExternalUI is WIP
# $(MAKE) all -C examples/ExternalUI
# install -d bin/d_extui-dssi
# install -d bin/d_extui.lv2
# install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.sh
# install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui-dssi/d_extui.sh
# install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.lv2/d_extui.sh
# endif


ifeq ($(CAN_GENERATE_TTL),true) ifeq ($(CAN_GENERATE_TTL),true)
gen: examples utils/lv2_ttl_generator gen: examples utils/lv2_ttl_generator


+ 21
- 0
dgl/TopLevelWidget.hpp View File

@@ -66,6 +66,27 @@ public:
*/ */
Window& getWindow() const noexcept; Window& getWindow() const noexcept;


/**
Set width of this widget's window.
@note This will not change the widget's size right away, but be pending on OS resizing the window
*/
void setWidth(uint width);

/**
Set height of this widget's window.
*/
void setHeight(uint height);

/**
Set size of this widget's window, using @a width and @a height values.
*/
void setSize(uint width, uint height);

/**
Set size of this widget's window.
*/
void setSize(const Size<uint>& size);

// TODO group stuff after here, convenience functions present in Window class // TODO group stuff after here, convenience functions present in Window class
bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0);
bool removeIdleCallback(IdleCallback* callback); bool removeIdleCallback(IdleCallback* callback);


+ 12
- 1
dgl/Window.hpp View File

@@ -185,7 +185,7 @@ public:
void close(); void close();


/** /**
Check if this window is resizable.
Check if this window is resizable (by the user or window manager).
@see setResizable @see setResizable
*/ */
bool isResizable() const noexcept; bool isResizable() const noexcept;
@@ -367,6 +367,10 @@ protected:
A function called when the window is attempted to be closed. A function called when the window is attempted to be closed.
Returning true closes the window, which is the default behaviour. Returning true closes the window, which is the default behaviour.
Override this method and return false to prevent the window from being closed by the user. Override this method and return false to prevent the window from being closed by the user.

This method is not used for embed windows, and not even made available in DISTRHO_NAMESPACE::UI.
For embed windows, closing is handled by the host/parent process and we have no control over it.
As such, a close action on embed windows will always succeed and cannot be cancelled.
*/ */
virtual bool onClose(); virtual bool onClose();


@@ -383,6 +387,13 @@ protected:
*/ */
virtual void onReshape(uint width, uint height); virtual void onReshape(uint width, uint height);


/**
A function called when scale factor requested for this window changes.
The default implementation does nothing.
WARNING function needs a proper name
*/
virtual void onScaleFactorChanged(double scaleFactor);

#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
/** /**
A function called when a path is selected by the user, as triggered by openFileBrowser(). A function called when a path is selected by the user, as triggered by openFileBrowser().


+ 1
- 2
dgl/src/SubWidget.cpp View File

@@ -96,8 +96,7 @@ void SubWidget::setAbsolutePos(const Point<int>& pos) noexcept
pData->absolutePos = pos; pData->absolutePos = pos;
onPositionChanged(ev); onPositionChanged(ev);


// repaint the bounds of parent
pData->parentWidget->repaint();
repaint();
} }


Widget* SubWidget::getParentWidget() const noexcept Widget* SubWidget::getParentWidget() const noexcept


+ 20
- 0
dgl/src/TopLevelWidget.cpp View File

@@ -40,6 +40,26 @@ Window& TopLevelWidget::getWindow() const noexcept
return pData->window; return pData->window;
} }


void TopLevelWidget::setWidth(const uint width)
{
pData->window.setWidth(width);
}

void TopLevelWidget::setHeight(const uint height)
{
pData->window.setHeight(height);
}

void TopLevelWidget::setSize(const uint width, const uint height)
{
pData->window.setSize(width, height);
}

void TopLevelWidget::setSize(const Size<uint>& size)
{
pData->window.setSize(size);
}

bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs)
{ {
return pData->window.addIdleCallback(callback, timerFrequencyInMs); return pData->window.addIdleCallback(callback, timerFrequencyInMs);


+ 2
- 3
dgl/src/TopLevelWidgetPrivateData.cpp View File

@@ -28,13 +28,12 @@ TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w)
selfw(s), selfw(s),
window(w) window(w)
{ {
window.pData->topLevelWidget = self;
window.pData->topLevelWidgets.push_back(self);
} }


TopLevelWidget::PrivateData::~PrivateData() TopLevelWidget::PrivateData::~PrivateData()
{ {
// FIXME?
window.pData->topLevelWidget = nullptr;
window.pData->topLevelWidgets.remove(self);
} }


bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev)


+ 2
- 0
dgl/src/Widget.cpp View File

@@ -46,6 +46,8 @@ void Widget::setVisible(bool visible)


pData->visible = visible; pData->visible = visible;
repaint(); repaint();

// FIXME check case of hiding a previously visible widget, does it trigger a repaint?
} }


void Widget::show() void Widget::show()


+ 34
- 6
dgl/src/Window.cpp View File

@@ -136,13 +136,37 @@ void Window::setHeight(const uint height)
setSize(getWidth(), height); setSize(getWidth(), height);
} }


void Window::setSize(const uint width, const uint height)
void Window::setSize(uint width, uint height)
{ {
DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,); DISTRHO_SAFE_ASSERT_UINT2_RETURN(width > 1 && height > 1, width, height,);


// FIXME add default and min props for this
if (pData->minWidth == 0 && pData->minHeight == 0)
puglSetDefaultSize(pData->view, static_cast<int>(width), static_cast<int>(height));
if (pData->isEmbed)
{
// handle geometry constraints here
if (width < pData->minWidth)
width = pData->minWidth;

if (height < pData->minHeight)
height = pData->minHeight;

if (pData->keepAspectRatio)
{
const double ratio = static_cast<double>(pData->minWidth)
/ static_cast<double>(pData->minHeight);
const double reqRatio = static_cast<double>(width)
/ static_cast<double>(height);

if (d_isNotEqual(ratio, reqRatio))
{
// fix width
if (reqRatio > ratio)
width = height * ratio;
// fix height
else
height = width / ratio;
}
}
}


puglSetWindowSize(pData->view, width, height); puglSetWindowSize(pData->view, width, height);
} }
@@ -250,8 +274,7 @@ void Window::setGeometryConstraints(const uint minimumWidth,
DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,); DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,);


if (pData->isEmbed) { if (pData->isEmbed) {
// Did you forget to set DISTRHO_UI_USER_RESIZABLE ?
DISTRHO_SAFE_ASSERT_RETURN(isResizable(),);
// nothing to do here
} else if (! isResizable()) { } else if (! isResizable()) {
setResizable(true); setResizable(true);
} }
@@ -259,6 +282,7 @@ void Window::setGeometryConstraints(const uint minimumWidth,
pData->minWidth = minimumWidth; pData->minWidth = minimumWidth;
pData->minHeight = minimumHeight; pData->minHeight = minimumHeight;
pData->autoScaling = automaticallyScale; pData->autoScaling = automaticallyScale;
pData->keepAspectRatio = keepAspectRatio;


const double scaleFactor = pData->scaleFactor; const double scaleFactor = pData->scaleFactor;


@@ -290,6 +314,10 @@ void Window::onReshape(uint, uint)
puglFallbackOnResize(pData->view); puglFallbackOnResize(pData->view);
} }


void Window::onScaleFactorChanged(double)
{
}

#ifndef DGL_FILE_BROWSER_DISABLED #ifndef DGL_FILE_BROWSER_DISABLED
void Window::onFileSelected(const char*) void Window::onFileSelected(const char*)
{ {


+ 81
- 37
dgl/src/WindowPrivateData.cpp View File

@@ -51,6 +51,12 @@ START_NAMESPACE_DGL
#define DEFAULT_WIDTH 640 #define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480 #define DEFAULT_HEIGHT 480


#define FOR_EACH_TOP_LEVEL_WIDGET(it) \
for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it)

#define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \
for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit)

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
@@ -73,7 +79,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s)
appData(a.pData), appData(a.pData),
self(s), self(s),
view(puglNewView(appData->world)), view(puglNewView(appData->world)),
topLevelWidget(nullptr),
topLevelWidgets(),
isClosed(true), isClosed(true),
isVisible(false), isVisible(false),
isEmbed(false), isEmbed(false),
@@ -82,6 +88,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s)
autoScaleFactor(1.0), autoScaleFactor(1.0),
minWidth(0), minWidth(0),
minHeight(0), minHeight(0),
keepAspectRatio(false),
#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr), win32SelectedFile(nullptr),
#endif #endif
@@ -95,7 +102,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
appData(a.pData), appData(a.pData),
self(s), self(s),
view(puglNewView(appData->world)), view(puglNewView(appData->world)),
topLevelWidget(nullptr),
topLevelWidgets(),
isClosed(true), isClosed(true),
isVisible(false), isVisible(false),
isEmbed(false), isEmbed(false),
@@ -104,6 +111,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
autoScaleFactor(1.0), autoScaleFactor(1.0),
minWidth(0), minWidth(0),
minHeight(0), minHeight(0),
keepAspectRatio(false),
#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr), win32SelectedFile(nullptr),
#endif #endif
@@ -121,7 +129,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
appData(a.pData), appData(a.pData),
self(s), self(s),
view(puglNewView(appData->world)), view(puglNewView(appData->world)),
topLevelWidget(nullptr),
topLevelWidgets(),
isClosed(parentWindowHandle == 0), isClosed(parentWindowHandle == 0),
isVisible(parentWindowHandle != 0), isVisible(parentWindowHandle != 0),
isEmbed(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0),
@@ -130,16 +138,14 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
autoScaleFactor(1.0), autoScaleFactor(1.0),
minWidth(0), minWidth(0),
minHeight(0), minHeight(0),
keepAspectRatio(false),
#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr), win32SelectedFile(nullptr),
#endif #endif
modal() modal()
{ {
if (isEmbed) if (isEmbed)
{
// puglSetDefaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, height);
puglSetParentWindow(view, parentWindowHandle); puglSetParentWindow(view, parentWindowHandle);
}


initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable);
} }
@@ -152,7 +158,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
appData(a.pData), appData(a.pData),
self(s), self(s),
view(puglNewView(appData->world)), view(puglNewView(appData->world)),
topLevelWidget(nullptr),
topLevelWidgets(),
isClosed(parentWindowHandle == 0), isClosed(parentWindowHandle == 0),
isVisible(parentWindowHandle != 0), isVisible(parentWindowHandle != 0),
isEmbed(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0),
@@ -161,16 +167,14 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
autoScaleFactor(1.0), autoScaleFactor(1.0),
minWidth(0), minWidth(0),
minHeight(0), minHeight(0),
keepAspectRatio(false),
#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
win32SelectedFile(nullptr), win32SelectedFile(nullptr),
#endif #endif
modal() modal()
{ {
if (isEmbed) if (isEmbed)
{
puglSetDefaultSize(view, static_cast<int>(width), static_cast<int>(height));
puglSetParentWindow(view, parentWindowHandle); puglSetParentWindow(view, parentWindowHandle);
}


initPre(width, height, resizable); initPre(width, height, resizable);
} }
@@ -216,6 +220,9 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo


puglSetMatchingBackendForCurrentBuild(view); puglSetMatchingBackendForCurrentBuild(view);


puglClearMinSize(view);
puglSetWindowSize(view, width, height);

puglSetHandle(view, this); puglSetHandle(view, this);
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);
puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE);
@@ -223,11 +230,6 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo
puglSetViewHint(view, PUGL_STENCIL_BITS, 8); puglSetViewHint(view, PUGL_STENCIL_BITS, 8);
// PUGL_SAMPLES ?? // PUGL_SAMPLES ??
puglSetEventFunc(view, puglEventCallback); puglSetEventFunc(view, puglEventCallback);

PuglRect rect = puglGetFrame(view);
rect.width = width;
rect.height = height;
puglSetFrame(view, rect);
} }


void Window::PrivateData::initPost() void Window::PrivateData::initPost()
@@ -293,7 +295,6 @@ void Window::PrivateData::show()


// FIXME // FIXME
PuglRect rect = puglGetFrame(view); PuglRect rect = puglGetFrame(view);
puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));
puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));


#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
@@ -552,10 +553,6 @@ void Window::PrivateData::startModal()
// make parent give focus to us // make parent give focus to us
modal.parent->modal.child = this; modal.parent->modal.child = this;


// FIXME?
PuglRect rect = puglGetFrame(view);
puglSetDefaultSize(view, static_cast<int>(rect.width), static_cast<int>(rect.height));

// make sure both parent and ourselves are visible // make sure both parent and ourselves are visible
modal.parent->show(); modal.parent->show();
show(); show();
@@ -642,8 +639,20 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh
self->onReshape(uwidth, uheight); self->onReshape(uwidth, uheight);


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->setSize(uwidth, uheight);
FOR_EACH_TOP_LEVEL_WIDGET(it)
{
TopLevelWidget* const widget(*it);

/* Some special care here, we call Widget::setSize instead of the TopLevelWidget one.
* This is because we want TopLevelWidget::setSize to handle both window and widget size,
* but we dont want to change window size here, because we are the window..
* So we just call the Widget specific method manually.
*
* Alternatively, we could expose a resize function on the pData, like done with the display function.
* But there is nothing extra we need to do in there, so this works fine.
*/
((Widget*)widget)->setSize(uwidth, uheight);
}
#endif #endif


// always repaint after a resize // always repaint after a resize
@@ -652,13 +661,18 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh


void Window::PrivateData::onPuglExpose() void Window::PrivateData::onPuglExpose()
{ {
DGL_DBGp("PUGL: onPuglExpose : %p\n", topLevelWidget);
DGL_DBGp("PUGL: onPuglExpose\n");


puglOnDisplayPrepare(view); puglOnDisplayPrepare(view);


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->display();
FOR_EACH_TOP_LEVEL_WIDGET(it)
{
TopLevelWidget* const widget(*it);

if (widget->isVisible())
widget->pData->display();
}
#endif #endif
} }


@@ -711,8 +725,13 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->keyboardEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->keyboardEvent(ev))
break;
}
#endif #endif
} }


@@ -724,8 +743,13 @@ void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->specialEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->specialEvent(ev))
break;
}
#endif #endif
} }


@@ -737,8 +761,13 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->characterInputEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->characterInputEvent(ev))
break;
}
#endif #endif
} }


@@ -750,8 +779,13 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->mouseEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->mouseEvent(ev))
break;
}
#endif #endif
} }


@@ -763,8 +797,13 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->motionEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->motionEvent(ev))
break;
}
#endif #endif
} }


@@ -776,8 +815,13 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev)
return modal.child->focus(); return modal.child->focus();


#ifndef DPF_TEST_WINDOW_CPP #ifndef DPF_TEST_WINDOW_CPP
if (topLevelWidget != nullptr)
topLevelWidget->pData->scrollEvent(ev);
FOR_EACH_TOP_LEVEL_WIDGET_INV(rit)
{
TopLevelWidget* const widget(*rit);

if (widget->isVisible() && widget->pData->scrollEvent(ev))
break;
}
#endif #endif
} }




+ 6
- 3
dgl/src/WindowPrivateData.hpp View File

@@ -23,6 +23,8 @@


#include "pugl.hpp" #include "pugl.hpp"


#include <list>

START_NAMESPACE_DGL START_NAMESPACE_DGL


class TopLevelWidget; class TopLevelWidget;
@@ -45,8 +47,8 @@ struct Window::PrivateData : IdleCallback {
/** Reserved space for graphics context. */ /** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)]; mutable uint8_t graphicsContext[sizeof(void*)];


/** The top-level widget associated with this Window. */
TopLevelWidget* topLevelWidget;
/** The top-level widgets associated with this Window. */
std::list<TopLevelWidget*> topLevelWidgets;


/** Whether this Window is closed (not visible or counted in the Application it is tied to). /** Whether this Window is closed (not visible or counted in the Application it is tied to).
Defaults to true unless embed (embed windows are never closed). */ Defaults to true unless embed (embed windows are never closed). */
@@ -65,8 +67,9 @@ struct Window::PrivateData : IdleCallback {
bool autoScaling; bool autoScaling;
double autoScaleFactor; double autoScaleFactor;


/** Pugl minWidth, minHeight access. */
/** Pugl geometry constraints access. */
uint minWidth, minHeight; uint minWidth, minHeight;
bool keepAspectRatio;


#ifdef DISTRHO_OS_WINDOWS #ifdef DISTRHO_OS_WINDOWS
/** Selected file for openFileBrowser on windows, stored for fake async operation. */ /** Selected file for openFileBrowser on windows, stored for fake async operation. */


+ 37
- 3
dgl/src/pugl.cpp View File

@@ -152,11 +152,20 @@ START_NAMESPACE_DGL
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// expose backend enter // expose backend enter


void puglBackendEnter(PuglView* view)
void puglBackendEnter(PuglView* const view)
{ {
view->backend->enter(view, NULL); view->backend->enter(view, NULL);
} }


// --------------------------------------------------------------------------------------------------------------------
// clear minimum size to 0

void puglClearMinSize(PuglView* const view)
{
view->minWidth = 0;
view->minHeight = 0;
}

// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// missing in pugl, directly returns title char* pointer // missing in pugl, directly returns title char* pointer


@@ -237,10 +246,13 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co
} }


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
// set window size without changing frame x/y position
// set window size with default size and without changing frame x/y position


PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height) PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint height)
{ {
view->defaultWidth = width;
view->defaultHeight = height;

#if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC)
// keep upstream behaviour // keep upstream behaviour
const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height }; const PuglRect frame = { view->frame.x, view->frame.y, (double)width, (double)height };
@@ -271,8 +283,30 @@ PuglStatus puglSetWindowSize(PuglView* const view, const uint width, const uint
// matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow // matches upstream pugl, except we use XResizeWindow instead of XMoveResizeWindow
if (view->impl->win) if (view->impl->win)
{ {
if (! XResizeWindow(view->world->impl->display, view->impl->win, width, height))
Display* const display = view->world->impl->display;

if (! XResizeWindow(display, view->impl->win, width, height))
return PUGL_UNKNOWN_ERROR; return PUGL_UNKNOWN_ERROR;

#if 0
// custom handling for embed non-resizable windows
if (view->parent != 0 && ! view->hints[PUGL_RESIZABLE])
{
XSizeHints sizeHints = {};
sizeHints.flags = PSize | PBaseSize | PMinSize | PMaxSize;
sizeHints.width = static_cast<int>(width);
sizeHints.height = static_cast<int>(height);
sizeHints.base_width = width;
sizeHints.base_height = height;
sizeHints.min_width = width;
sizeHints.min_height = height;
sizeHints.max_width = width;
sizeHints.max_height = height;
XSetNormalHints(display, view->impl->win, &sizeHints);
}
#endif

updateSizeHints(view);
} }
#endif #endif




+ 5
- 1
dgl/src/pugl.hpp View File

@@ -46,6 +46,10 @@ PUGL_BEGIN_DECLS
PUGL_API void PUGL_API void
puglBackendEnter(PuglView* view); puglBackendEnter(PuglView* view);


// clear minimum size to 0
PUGL_API void
puglClearMinSize(PuglView* view);

// missing in pugl, directly returns title char* pointer // missing in pugl, directly returns title char* pointer
PUGL_API const char* PUGL_API const char*
puglGetWindowTitle(const PuglView* view); puglGetWindowTitle(const PuglView* view);
@@ -62,7 +66,7 @@ puglSetMatchingBackendForCurrentBuild(PuglView* view);
PUGL_API PuglStatus PUGL_API PuglStatus
puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); puglSetGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect);


// set window size without changing frame x/y position
// set window size with default size and without changing frame x/y position
PUGL_API PuglStatus PUGL_API PuglStatus
puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height);




+ 49
- 11
distrho/DistrhoUI.hpp View File

@@ -81,6 +81,15 @@ public:
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Host state */ * Host state */


/**
Check if this UI window is resizable (by the user or window manager).
There are situations where an UI supports resizing but the plugin host does not, so this could return false.

You might want to add a resize handle for such cases, so the user is still allowed to resize the window.
(programatically resizing a window is always possible, but the same is not true for the window manager)
*/
bool isResizable() const noexcept;

/** /**
Get the color used for UI background (i.e. window color) in RGBA format. Get the color used for UI background (i.e. window color) in RGBA format.
Returns 0 by default, in case of error or lack of host support. Returns 0 by default, in case of error or lack of host support.
@@ -237,26 +246,55 @@ protected:
* UI Callbacks (optional) */ * UI Callbacks (optional) */


/** /**
uiIdle.
@TODO Document this.
UI idle function, called to give idle time to the plugin UI directly from the host.
This is called right after OS event handling and Window idle events (within the same cycle).
There are no guarantees in terms of timing.
@see addIdleCallback(IdleCallback*, uint).
*/ */
virtual void uiIdle() {} virtual void uiIdle() {}


# ifndef DGL_FILE_BROWSER_DISABLED
/** /**
File browser selected function.
@see Window::onFileSelected(const char*)
Windows focus function, called when the window gains or loses the keyboard focus.
This function is for plugin UIs to be able to override Window::onFocus(bool, CrossingMode).

The default implementation does nothing.
*/ */
virtual void uiFileBrowserSelected(const char* filename);
# endif
virtual void uiFocus(bool focus, CrossingMode mode);


/** /**
OpenGL window reshape function, called when parent window is resized.
You can reimplement this function for a custom OpenGL state.
@see Window::onReshape(uint,uint)
Window reshape function, called when the window is resized.
This function is for plugin UIs to be able to override Window::onReshape(uint, uint).

The plugin UI size will be set right after this function.
The default implementation sets up drawing context where necessary.

You should almost never need to override this function.
The most common exception is custom OpenGL setup, but only really needed for custom OpenGL drawing code.
*/ */
virtual void uiReshape(uint width, uint height); virtual void uiReshape(uint width, uint height);


/**
Window scale factor function, called when the scale factor changes.
This function is for plugin UIs to be able to override Window::onScaleFactorChanged(double).

The default implementation does nothing.
WARNING function needs a proper name
*/
virtual void uiScaleFactorChanged(double scaleFactor);

# ifndef DGL_FILE_BROWSER_DISABLED
/**
Window file selected function, called when a path is selected by the user, as triggered by openFileBrowser().
This function is for plugin UIs to be able to override Window::onFileSelected(const char*).

This action happens after the user confirms the action, so the file browser dialog will be closed at this point.
The default implementation does nothing.

If you need to use files as plugin state, please setup and use DISTRHO_PLUGIN_WANT_STATEFILES instead.
*/
virtual void uiFileBrowserSelected(const char* filename);
# endif

/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */ * UI Resize Handling, internal */


@@ -273,8 +311,8 @@ protected:
private: private:
struct PrivateData; struct PrivateData;
PrivateData* const uiData; PrivateData* const uiData;
friend class PluginWindow;
friend class UIExporter; friend class UIExporter;
friend class UIExporterWindow;


DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI)
}; };


+ 3
- 0
distrho/extra/ScopedPointer.hpp View File

@@ -145,6 +145,9 @@ public:
/** Returns the object that this ScopedPointer refers to. */ /** Returns the object that this ScopedPointer refers to. */
ObjectType* get() const noexcept { return object; } ObjectType* get() const noexcept { return object; }


/** Returns the object that this ScopedPointer refers to. */
ObjectType& getObject() const noexcept { return *object; }

/** Returns the object that this ScopedPointer refers to. */ /** Returns the object that this ScopedPointer refers to. */
ObjectType& operator*() const noexcept { return *object; } ObjectType& operator*() const noexcept { return *object; }




+ 5
- 16
distrho/src/DistrhoPluginJack.cpp View File

@@ -17,7 +17,6 @@
#include "DistrhoPluginInternal.hpp" #include "DistrhoPluginInternal.hpp"


#if DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_UI_IS_STANDALONE true
# include "DistrhoUIInternal.hpp" # include "DistrhoUIInternal.hpp"
# include "../extra/RingBuffer.hpp" # include "../extra/RingBuffer.hpp"
#else #else
@@ -110,12 +109,14 @@ public:
PluginJack(jack_client_t* const client) PluginJack(jack_client_t* const client)
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
#if DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_HAS_UI
fUI(this, 0,
fUI(this,
0, // winId
d_lastSampleRate,
nullptr, // edit param nullptr, // edit param
setParameterValueCallback, setParameterValueCallback,
setStateCallback, setStateCallback,
sendNoteCallback, sendNoteCallback,
setSizeCallback,
nullptr, // window size
nullptr, // file request nullptr, // file request
nullptr, // bundle nullptr, // bundle
fPlugin.getInstancePointer(), fPlugin.getInstancePointer(),
@@ -494,11 +495,6 @@ protected:
fPlugin.setParameterValue(index, value); fPlugin.setParameterValue(index, value);
} }


void setSize(const uint width, const uint height)
{
fUI.setWindowSize(width, height);
}

# if DISTRHO_PLUGIN_WANT_MIDI_INPUT # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{ {
@@ -680,11 +676,6 @@ private:
thisPtr->setParameterValue(index, value); thisPtr->setParameterValue(index, value);
} }


static void setSizeCallback(void* ptr, uint width, uint height)
{
thisPtr->setSize(width, height);
}

# if DISTRHO_PLUGIN_WANT_MIDI_INPUT # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
{ {
@@ -797,9 +788,7 @@ int main()


d_lastBufferSize = jack_get_buffer_size(client); d_lastBufferSize = jack_get_buffer_size(client);
d_lastSampleRate = jack_get_sample_rate(client); d_lastSampleRate = jack_get_sample_rate(client);
#if DISTRHO_PLUGIN_HAS_UI
d_lastUiSampleRate = d_lastSampleRate;
#endif
d_lastCanRequestParameterValueChanges = true;


const PluginJack p(client); const PluginJack p(client);




+ 4
- 11
distrho/src/DistrhoPluginVST.cpp View File

@@ -23,9 +23,6 @@
#endif #endif


#if DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_HAS_UI
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
# define DISTRHO_UI_IS_STANDALONE false
# include "DistrhoUIInternal.hpp" # include "DistrhoUIInternal.hpp"
# include "../extra/RingBuffer.hpp" # include "../extra/RingBuffer.hpp"
#endif #endif
@@ -178,7 +175,7 @@ public:
fEffect(effect), fEffect(effect),
fUiHelper(uiHelper), fUiHelper(uiHelper),
fPlugin(plugin), fPlugin(plugin),
fUI(this, winId,
fUI(this, winId, plugin->getSampleRate(),
editParameterCallback, editParameterCallback,
setParameterCallback, setParameterCallback,
setStateCallback, setStateCallback,
@@ -213,7 +210,7 @@ public:
} }
} }


fUI.idle();
fUI.plugin_idle();
} }


int16_t getWidth() const int16_t getWidth() const
@@ -387,7 +384,7 @@ protected:


void setSize(const uint width, const uint height) void setSize(const uint width, const uint height)
{ {
fUI.setWindowSize(width, height);
// fUI.setWindowSize(width, height);
hostCallback(audioMasterSizeWindow, width, height); hostCallback(audioMasterSizeWindow, width, height);
} }


@@ -671,9 +668,7 @@ public:
} }
else else
{ {
d_lastUiSampleRate = fPlugin.getSampleRate();

UIExporter tmpUI(nullptr, 0,
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
fPlugin.getInstancePointer(), fLastScaleFactor); fPlugin.getInstancePointer(), fLastScaleFactor);
fVstRect.right = tmpUI.getWidth(); fVstRect.right = tmpUI.getWidth();
@@ -694,8 +689,6 @@ public:
return 0; return 0;
} }
# endif # endif
d_lastUiSampleRate = fPlugin.getSampleRate();

fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor); fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor);


# if DISTRHO_PLUGIN_WANT_FULL_STATE # if DISTRHO_PLUGIN_WANT_FULL_STATE


+ 39
- 67
distrho/src/DistrhoUI.cpp View File

@@ -20,76 +20,52 @@
# include "src/TopLevelWidgetPrivateData.hpp" # include "src/TopLevelWidgetPrivateData.hpp"
#endif #endif


#include "NanoVG.hpp"

START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* Static data, see DistrhoUIInternal.hpp and DistrhoUIPrivateData.hpp */
* Static data, see DistrhoUIInternal.hpp */


double d_lastUiSampleRate = 0.0;
void* d_lastUiDspPtr = nullptr;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
const char* g_nextBundlePath = nullptr;
double g_nextScaleFactor = 1.0;
uintptr_t g_nextWindowId = 0;
#else
Window* d_lastUiWindow = nullptr;
uintptr_t g_nextWindowId = 0;
double g_nextScaleFactor = 1.0;
const char* g_nextBundlePath = nullptr;
#endif #endif


// -----------------------------------------------------------------------------------------------------------
/* ------------------------------------------------------------------------------------------------------------
* UI::PrivateData special handling */


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextScaleFactor = scaleFactor;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextScaleFactor = 1.0;
g_nextBundlePath = nullptr;
return ret;
}
#else
UI* createUiWrapper(void* const dspPtr, Window* const window)
UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;

PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height)
{ {
d_lastUiDspPtr = dspPtr;
d_lastUiWindow = window;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
d_lastUiWindow = nullptr;
return ret;
UI::PrivateData* const pData = s_nextPrivateData;
pData->window = new PluginWindow(ui, pData, width, height);
return pData->window.getObject();
} }
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* UI */ * UI */


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI::UI(uint width, uint height)
: UIWidget(width, height),
uiData(new PrivateData()) {}
#else
UI::UI(uint width, uint height)
: UIWidget(*d_lastUiWindow),
uiData(new PrivateData())
{
if (width > 0 && height > 0)
setSize(width, height);
}
#endif
UI::UI(const uint width, const uint height)
: UIWidget(UI::PrivateData::createNextWindow(this, width, height)),
uiData(UI::PrivateData::s_nextPrivateData) {}


UI::~UI() UI::~UI()
{ {
delete uiData;
} }


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* Host state */ * Host state */


bool UI::isResizable() const noexcept
{
#if DISTRHO_UI_USER_RESIZABLE
return uiData->window->isResizable();
#else
return false;
#endif
}

uint UI::getBackgroundColor() const noexcept uint UI::getBackgroundColor() const noexcept
{ {
return uiData->bgColor; return uiData->bgColor;
@@ -166,22 +142,22 @@ uintptr_t UI::getNextWindowId() noexcept
return g_nextWindowId; return g_nextWindowId;
} }
# endif # endif
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#endif


/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* DSP/Plugin Callbacks (optional) */ * DSP/Plugin Callbacks (optional) */


void UI::sampleRateChanged(double) {}
void UI::sampleRateChanged(double)
{
}


#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */ * UI Callbacks (optional) */


# ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*)
void UI::uiFocus(bool, CrossingMode)
{ {
} }
# endif


void UI::uiReshape(uint, uint) void UI::uiReshape(uint, uint)
{ {
@@ -189,22 +165,25 @@ void UI::uiReshape(uint, uint)
pData->fallbackOnResize(); pData->fallbackOnResize();
} }


void UI::uiScaleFactorChanged(double)
{
}

# ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*)
{
}
# endif

/* ------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------
* UI Resize Handling, internal */ * UI Resize Handling, internal */


void UI::onResize(const ResizeEvent& ev) void UI::onResize(const ResizeEvent& ev)
{ {
if (uiData->resizeInProgress)
return;

UIWidget::onResize(ev); UIWidget::onResize(ev);


const uint width = ev.size.getWidth(); const uint width = ev.size.getWidth();
const uint height = ev.size.getHeight(); const uint height = ev.size.getHeight();

/*
pData->window.setSize(width, height);
*/
uiData->setSizeCallback(width, height); uiData->setSizeCallback(width, height);
} }
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
@@ -212,10 +191,3 @@ void UI::onResize(const ResizeEvent& ev)
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------


END_NAMESPACE_DISTRHO END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// Possible template data types

// template class NanoBaseWidget<SubWidget>;
// template class NanoBaseWidget<TopLevelWidget>;
// template class NanoBaseWidget<StandaloneWindow>;

+ 24
- 33
distrho/src/DistrhoUIDSSI.cpp View File

@@ -14,7 +14,6 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#define DISTRHO_UI_IS_STANDALONE true
#include "DistrhoUIInternal.hpp" #include "DistrhoUIInternal.hpp"


#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
@@ -94,11 +93,12 @@ struct OscData {


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


class UIDssi
class UIDssi : public IdleCallback
{ {
public: public:
UIDssi(const OscData& oscData, const char* const uiTitle)
: fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, nullptr),
UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate)
: fUI(this, 0, sampleRate, nullptr,
setParameterCallback, setStateCallback, sendNoteCallback, nullptr, nullptr),
fHostClosed(false), fHostClosed(false),
fOscData(oscData) fOscData(oscData)
{ {
@@ -111,17 +111,19 @@ public:
fOscData.send_exiting(); fOscData.send_exiting();
} }


void exec()
void exec_start()
{ {
for (;;)
{
fOscData.idle();
fUI.exec(this);
}

void idleCallback() override
{
fOscData.idle();


if (fHostClosed || ! fUI.idle())
break;
if (fHostClosed)
return;


d_msleep(30);
}
fUI.exec_idle();
} }


// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -206,11 +208,6 @@ protected:
} }
#endif #endif


void setSize(const uint width, const uint height)
{
fUI.setWindowSize(width, height);
}

private: private:
UIExporter fUI; UIExporter fUI;
bool fHostClosed; bool fHostClosed;
@@ -239,11 +236,6 @@ private:
} }
#endif #endif


static void setSizeCallback(void* ptr, uint width, uint height)
{
uiPtr->setSize(width, height);
}

#undef uiPtr #undef uiPtr
}; };


@@ -252,16 +244,17 @@ private:
static OscData gOscData; static OscData gOscData;
static const char* gUiTitle = nullptr; static const char* gUiTitle = nullptr;
static UIDssi* globalUI = nullptr; static UIDssi* globalUI = nullptr;
static double sampleRate = 0.0;


static void initUiIfNeeded() static void initUiIfNeeded()
{ {
if (globalUI != nullptr) if (globalUI != nullptr)
return; return;


if (d_lastUiSampleRate == 0.0)
d_lastUiSampleRate = 44100.0;
if (sampleRate == 0.0)
sampleRate = 44100.0;


globalUI = new UIDssi(gOscData, gUiTitle);
globalUI = new UIDssi(gOscData, gUiTitle, sampleRate);
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -337,10 +330,8 @@ int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message


int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*)
{ {
const int32_t sampleRate = argv[0]->i;
d_debug("osc_sample_rate_handler(%i)", sampleRate);

d_lastUiSampleRate = sampleRate;
sampleRate = argv[0]->i;
d_debug("osc_sample_rate_handler(%f)", sampleRate);


if (globalUI != nullptr) if (globalUI != nullptr)
globalUI->dssiui_samplerate(sampleRate); globalUI->dssiui_samplerate(sampleRate);
@@ -394,7 +385,7 @@ int main(int argc, char* argv[])


initUiIfNeeded(); initUiIfNeeded();
globalUI->dssiui_show(true); globalUI->dssiui_show(true);
globalUI->exec();
globalUI->exec_start();


delete globalUI; delete globalUI;
globalUI = nullptr; globalUI = nullptr;
@@ -481,7 +472,7 @@ int main(int argc, char* argv[])
{ {
lo_server_recv(oscServer); lo_server_recv(oscServer);


if (d_lastUiSampleRate != 0.0 || globalUI != nullptr)
if (sampleRate != 0.0 || globalUI != nullptr)
break; break;


d_msleep(50); d_msleep(50);
@@ -489,11 +480,11 @@ int main(int argc, char* argv[])


int ret = 1; int ret = 1;


if (d_lastUiSampleRate != 0.0 || globalUI != nullptr)
if (sampleRate != 0.0 || globalUI != nullptr)
{ {
initUiIfNeeded(); initUiIfNeeded();


globalUI->exec();
globalUI->exec_start();


delete globalUI; delete globalUI;
globalUI = nullptr; globalUI = nullptr;


+ 112
- 255
distrho/src/DistrhoUIInternal.hpp View File

@@ -21,13 +21,6 @@


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../extra/Sleep.hpp" # include "../extra/Sleep.hpp"
using DGL_NAMESPACE::IdleCallback;
#else
# include "../../dgl/Application.hpp"
# include "../../dgl/Window.hpp"
using DGL_NAMESPACE::Application;
using DGL_NAMESPACE::IdleCallback;
using DGL_NAMESPACE::Window;
#endif #endif


START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
@@ -35,121 +28,32 @@ START_NAMESPACE_DISTRHO
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Static data, see DistrhoUI.cpp // Static data, see DistrhoUI.cpp


extern double d_lastUiSampleRate;
extern void* d_lastUiDspPtr;
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
extern const char* g_nextBundlePath;
extern double g_nextScaleFactor;
extern uintptr_t g_nextWindowId; extern uintptr_t g_nextWindowId;
#else
extern Window* d_lastUiWindow;
#endif

// -----------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath);
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI* createUiWrapper(void* const dspPtr, Window* const window);
#endif

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public Application
{
public:
PluginApplication()
: Application(DISTRHO_UI_IS_STANDALONE)
{
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
extern double g_nextScaleFactor;
extern const char* g_nextBundlePath;
#endif #endif
"-" DISTRHO_PLUGIN_NAME
);
setClassName(className);
}
};


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Plugin Window, needed to take care of resize properly
// UI exporter class


class UIExporterWindow : public Window
class UIExporter
{ {
public:
UIExporterWindow(PluginApplication& app, const intptr_t winId, const double scaleFactor, void* const dspPtr)
: Window(app, winId, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
fUI(createUiWrapper(dspPtr, this)),
fIsReady(false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI->uiData != nullptr,);

setSize(fUI->getWidth(), fUI->getHeight());
}

~UIExporterWindow()
{
delete fUI;
}

UI* getUI() const noexcept
{
return fUI;
}

bool isReady() const noexcept
{
return fIsReady;
}

protected:
// custom window reshape
void onReshape(uint width, uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

UI::PrivateData* const uiData = fUI->uiData;
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,);

/*
uiData->resizeInProgress = true;
fUI->setSize(width, height);
uiData->resizeInProgress = false;
*/

fUI->uiReshape(width, height);
fIsReady = true;
}

# ifndef DGL_FILE_BROWSER_DISABLED
// custom file-browser selected
void onFileSelected(const char* const filename) override
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
// -------------------------------------------------------------------
// UI Widget and its private data


fUI->uiFileBrowserSelected(filename);
}
# endif
UI* ui;
UI::PrivateData* uiData;


private:
UI* const fUI;
bool fIsReady;
};
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// prevent resize recursion
bool changingSizeRecursionCheck;


// -----------------------------------------------------------------------
// UI exporter class
// -------------------------------------------------------------------


class UIExporter
{
public: public:
UIExporter(void* const callbacksPtr, UIExporter(void* const callbacksPtr,
const intptr_t winId,
const uintptr_t winId,
const double sampleRate,
const editParamFunc editParamCall, const editParamFunc editParamCall,
const setParamFunc setParamCall, const setParamFunc setParamCall,
const setStateFunc setStateCall, const setStateFunc setStateCall,
@@ -161,139 +65,130 @@ public:
const double scaleFactor = 1.0, const double scaleFactor = 1.0,
const uint32_t bgColor = 0, const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff) const uint32_t fgColor = 0xffffffff)
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
: fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)),
#else
: glApp(),
glWindow(glApp, winId, scaleFactor, dspPtr),
fChangingSize(false),
fUI(glWindow.getUI()),
#endif
fData((fUI != nullptr) ? fUI->uiData : nullptr)
: ui(nullptr),
uiData(new UI::PrivateData()),
changingSizeRecursionCheck(false)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);

fData->bgColor = bgColor;
fData->fgColor = fgColor;

fData->callbacksPtr = callbacksPtr;
fData->editParamCallbackFunc = editParamCall;
fData->setParamCallbackFunc = setParamCall;
fData->setStateCallbackFunc = setStateCall;
fData->sendNoteCallbackFunc = sendNoteCall;
fData->setSizeCallbackFunc = setSizeCall;
fData->fileRequestCallbackFunc = fileRequestCall;
uiData->sampleRate = sampleRate;
uiData->dspPtr = dspPtr;


uiData->bgColor = bgColor;
uiData->fgColor = fgColor;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused
return; (void)bundlePath;
uiData->scaleFactor = scaleFactor;
uiData->winId = winId;
#endif #endif
}

uiData->callbacksPtr = callbacksPtr;
uiData->editParamCallbackFunc = editParamCall;
uiData->setParamCallbackFunc = setParamCall;
uiData->setStateCallbackFunc = setStateCall;
uiData->sendNoteCallbackFunc = sendNoteCall;
uiData->setSizeCallbackFunc = setSizeCall;
uiData->fileRequestCallbackFunc = fileRequestCall;


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
~UIExporter()
{
delete fUI;
}
g_nextWindowId = winId;
g_nextScaleFactor = scaleFactor;
g_nextBundlePath = bundlePath;
#endif #endif
UI::PrivateData::s_nextPrivateData = uiData;


// -------------------------------------------------------------------
UI* const uiPtr = createUI();


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uint getWidth() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getWidth();
}
g_nextWindowId = 0;
g_nextScaleFactor = 0.0;
g_nextBundlePath = nullptr;
#endif
UI::PrivateData::s_nextPrivateData = nullptr;


uint getHeight() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getHeight();
}
DISTRHO_SAFE_ASSERT_RETURN(uiPtr != nullptr,);
ui = uiPtr;


bool isVisible() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
return fUI->isRunning();
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused
(void)bundlePath;
#endif
} }


uintptr_t getNativeWindowHandle() const noexcept
~UIExporter()
{ {
return 0;
delete ui;
delete uiData;
} }
#else

// -------------------------------------------------------------------

uint getWidth() const noexcept uint getWidth() const noexcept
{ {
return glWindow.getWidth();
return uiData->window->getWidth();
} }


uint getHeight() const noexcept uint getHeight() const noexcept
{ {
return glWindow.getHeight();
return uiData->window->getHeight();
} }


bool isVisible() const noexcept bool isVisible() const noexcept
{ {
return glWindow.isVisible();
return uiData->window->isVisible();
} }


uintptr_t getNativeWindowHandle() const noexcept uintptr_t getNativeWindowHandle() const noexcept
{ {
return glWindow.getNativeWindowHandle();
return uiData->window->getNativeWindowHandle();
} }
#endif


uint getBackgroundColor() const noexcept uint getBackgroundColor() const noexcept
{ {
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0);


return fData->bgColor;
return uiData->bgColor;
} }


uint getForegroundColor() const noexcept uint getForegroundColor() const noexcept
{ {
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0xffffffff);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0xffffffff);


return fData->fgColor;
return uiData->fgColor;
} }


// ------------------------------------------------------------------- // -------------------------------------------------------------------


uint32_t getParameterOffset() const noexcept uint32_t getParameterOffset() const noexcept
{ {
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr, 0);


return fData->parameterOffset;
return uiData->parameterOffset;
} }


// ------------------------------------------------------------------- // -------------------------------------------------------------------


void parameterChanged(const uint32_t index, const float value) void parameterChanged(const uint32_t index, const float value)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->parameterChanged(index, value);
ui->parameterChanged(index, value);
} }


#if DISTRHO_PLUGIN_WANT_PROGRAMS #if DISTRHO_PLUGIN_WANT_PROGRAMS
void programLoaded(const uint32_t index) void programLoaded(const uint32_t index)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->programLoaded(index);
ui->programLoaded(index);
} }
#endif #endif


#if DISTRHO_PLUGIN_WANT_STATE #if DISTRHO_PLUGIN_WANT_STATE
void stateChanged(const char* const key, const char* const value) void stateChanged(const char* const key, const char* const value)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,);


fUI->stateChanged(key, value);
ui->stateChanged(key, value);
} }
#endif #endif


@@ -303,22 +198,18 @@ public:
void exec(IdleCallback* const cb) void exec(IdleCallback* const cb)
{ {
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->setVisible(true);
ui->setVisible(true);
cb->idleCallback(); cb->idleCallback();


while (fUI->isRunning())
while (ui->isRunning())
{ {
d_msleep(10); d_msleep(10);
cb->idleCallback(); cb->idleCallback();
} }
} }


void exec_idle()
{
}

bool idle() bool idle()
{ {
return true; return true;
@@ -330,103 +221,87 @@ public:


void quit() void quit()
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->setVisible(false);
fUI->terminateAndWaitForProcess();
ui->setVisible(false);
ui->terminateAndWaitForProcess();
} }
#else #else
# if DISTRHO_UI_IS_STANDALONE
void exec(IdleCallback* const cb) void exec(IdleCallback* const cb)
{ {
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);


glWindow.setVisible(true);
glApp.addIdleCallback(cb);
glApp.exec();
uiData->window->show();
uiData->app.addIdleCallback(cb);
uiData->app.exec();
} }


void exec_idle() void exec_idle()
{ {
if (glWindow.isReady())
fUI->uiIdle();
}
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, );


void focus()
{
glWindow.focus();
ui->uiIdle();
} }
bool idle()
# else
bool plugin_idle()
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);

glApp.idle();
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false);


if (glWindow.isReady())
fUI->uiIdle();
uiData->app.idle();
ui->uiIdle();
return ! uiData->app.isQuiting();
}
# endif


return ! glApp.isQuiting();
void focus()
{
uiData->window->focus();
} }


void quit() void quit()
{ {
glWindow.close();
glApp.quit();
uiData->window->close();
uiData->app.quit();
} }

#endif #endif

// ------------------------------------------------------------------- // -------------------------------------------------------------------


#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void setWindowTitle(const char* const uiTitle) void setWindowTitle(const char* const uiTitle)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->setTitle(uiTitle);
ui->setTitle(uiTitle);
} }


void setWindowSize(const uint width, const uint height, const bool = false)
void setWindowSize(const uint width, const uint height)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->setSize(width, height);
ui->setSize(width, height);
} }


void setWindowTransientWinId(const uintptr_t winId) void setWindowTransientWinId(const uintptr_t winId)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);


fUI->setTransientWinId(winId);
ui->setTransientWinId(winId);
} }


bool setWindowVisible(const bool yesNo) bool setWindowVisible(const bool yesNo)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false);


fUI->setVisible(yesNo);
ui->setVisible(yesNo);


return fUI->isRunning();
return ui->isRunning();
} }
#else #else
void setWindowTitle(const char* const uiTitle) void setWindowTitle(const char* const uiTitle)
{ {
glWindow.setTitle(uiTitle);
}

void setWindowSize(const uint width, const uint height, const bool updateUI = false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,);

fChangingSize = true;

if (updateUI)
fUI->setSize(width, height);

glWindow.setSize(width, height);

fChangingSize = false;
uiData->window->setTitle(uiTitle);
} }


void setWindowTransientWinId(const uintptr_t /*winId*/) void setWindowTransientWinId(const uintptr_t /*winId*/)
@@ -438,9 +313,9 @@ public:


bool setWindowVisible(const bool yesNo) bool setWindowVisible(const bool yesNo)
{ {
glWindow.setVisible(yesNo);
uiData->window->setVisible(yesNo);


return ! glApp.isQuiting();
return ! uiData->app.isQuiting();
} }


bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/) bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/)
@@ -464,37 +339,19 @@ public:


void setSampleRate(const double sampleRate, const bool doCallback = false) void setSampleRate(const double sampleRate, const bool doCallback = false)
{ {
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(uiData != nullptr,);
DISTRHO_SAFE_ASSERT(sampleRate > 0.0); DISTRHO_SAFE_ASSERT(sampleRate > 0.0);


if (d_isEqual(fData->sampleRate, sampleRate))
if (d_isEqual(uiData->sampleRate, sampleRate))
return; return;


fData->sampleRate = sampleRate;
uiData->sampleRate = sampleRate;


if (doCallback) if (doCallback)
fUI->sampleRateChanged(sampleRate);
ui->sampleRateChanged(sampleRate);
} }


private:
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// -------------------------------------------------------------------
// DGL Application and Window for this widget

PluginApplication glApp;
UIExporterWindow glWindow;

// prevent recursion
bool fChangingSize;
#endif

// -------------------------------------------------------------------
// Widget and DistrhoUI data

UI* const fUI;
UI::PrivateData* const fData;

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter) DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter)
}; };




+ 15
- 29
distrho/src/DistrhoUILV2.cpp View File

@@ -14,7 +14,6 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#define DISTRHO_UI_IS_STANDALONE false
#include "DistrhoUIInternal.hpp" #include "DistrhoUIInternal.hpp"


#include "../extra/String.hpp" #include "../extra/String.hpp"
@@ -73,10 +72,11 @@ public:
const LV2UI_Write_Function writeFunc, const LV2UI_Write_Function writeFunc,
LV2UI_Widget* const widget, LV2UI_Widget* const widget,
void* const dspPtr, void* const dspPtr,
const float sampleRate,
const float scaleFactor, const float scaleFactor,
const uint32_t bgColor, const uint32_t bgColor,
const uint32_t fgColor) const uint32_t fgColor)
: fUI(this, winId,
: fUI(this, winId, sampleRate,
editParameterCallback, editParameterCallback,
setParameterCallback, setParameterCallback,
setStateCallback, setStateCallback,
@@ -97,8 +97,12 @@ public:
fURIDs(uridMap), fURIDs(uridMap),
fWinIdWasNull(winId == 0) fWinIdWasNull(winId == 0)
{ {
#if ! DISTRHO_UI_USER_RESIZABLE
// this is not needed, hosts can query child window size
// it is best for them to do so anyway, since properties other than current-size are important (like ratio)
if (fUiResize != nullptr && winId != 0) if (fUiResize != nullptr && winId != 0)
fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight());
#endif


if (widget != nullptr) if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); *widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
@@ -191,9 +195,9 @@ public:
int lv2ui_idle() int lv2ui_idle()
{ {
if (fWinIdWasNull) if (fWinIdWasNull)
return (fUI.idle() && fUI.isVisible()) ? 0 : 1;
return (fUI.plugin_idle() && fUI.isVisible()) ? 0 : 1;


return fUI.idle() ? 0 : 1;
return fUI.plugin_idle() ? 0 : 1;
} }


int lv2ui_show() int lv2ui_show()
@@ -206,12 +210,6 @@ public:
return fUI.setWindowVisible(false) ? 0 : 1; return fUI.setWindowVisible(false) ? 0 : 1;
} }


int lv2ui_resize(uint width, uint height)
{
fUI.setWindowSize(width, height, true);
return 0;
}

// ------------------------------------------------------------------- // -------------------------------------------------------------------


uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) uint32_t lv2_get_options(LV2_Options_Option* const /*options*/)
@@ -333,8 +331,8 @@ protected:


void setSize(const uint width, const uint height) void setSize(const uint width, const uint height)
{ {
fUI.setWindowSize(width, height);
// report window size change to host.
// at the moment no lv2 hosts automatically adapt to child window size changes, so this is still needed
if (fUiResize != nullptr && ! fWinIdWasNull) if (fUiResize != nullptr && ! fWinIdWasNull)
fUiResize->ui_resize(fUiResize->handle, width, height); fUiResize->ui_resize(fUiResize->handle, width, height);
} }
@@ -526,6 +524,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
#endif #endif


const intptr_t winId = (intptr_t)parentId; const intptr_t winId = (intptr_t)parentId;
float sampleRate = 0.0f;
float scaleFactor = 1.0f; float scaleFactor = 1.0f;
uint32_t bgColor = 0; uint32_t bgColor = 0;
uint32_t fgColor = 0xffffffff; uint32_t fgColor = 0xffffffff;
@@ -544,7 +543,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
/**/ if (options[i].key == uridSampleRate) /**/ if (options[i].key == uridSampleRate)
{ {
if (options[i].type == uridAtomFloat) if (options[i].type == uridAtomFloat)
d_lastUiSampleRate = *(const float*)options[i].value;
sampleRate = *(const float*)options[i].value;
else else
d_stderr("Host provides UI sample-rate but has wrong value type"); d_stderr("Host provides UI sample-rate but has wrong value type");
} }
@@ -572,15 +571,15 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
} }
} }


if (d_lastUiSampleRate < 1.0)
if (sampleRate < 1.0)
{ {
d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)"); d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)");
d_lastUiSampleRate = 44100.0;
sampleRate = 44100.0;
} }


return new UiLv2(bundlePath, winId, options, uridMap, features, return new UiLv2(bundlePath, winId, options, uridMap, features,
controller, writeFunction, widget, instance, controller, writeFunction, widget, instance,
scaleFactor, bgColor, fgColor);
sampleRate, scaleFactor, bgColor, fgColor);
} }


#define uiPtr ((UiLv2*)ui) #define uiPtr ((UiLv2*)ui)
@@ -612,16 +611,6 @@ static int lv2ui_hide(LV2UI_Handle ui)
return uiPtr->lv2ui_hide(); return uiPtr->lv2ui_hide();
} }


static int lv2ui_resize(LV2UI_Handle ui, int width, int height)
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 1);
DISTRHO_SAFE_ASSERT_RETURN(width > 0, 1);
DISTRHO_SAFE_ASSERT_RETURN(height > 0, 1);

return 1; // This needs more testing
//return uiPtr->lv2ui_resize(width, height);
}

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options) static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
@@ -650,7 +639,6 @@ static const void* lv2ui_extension_data(const char* uri)
static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle }; static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle };
static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide }; static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide };
static const LV2UI_Resize uiResz = { nullptr, lv2ui_resize };


if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
return &options; return &options;
@@ -658,8 +646,6 @@ static const void* lv2ui_extension_data(const char* uri)
return &uiIdle; return &uiIdle;
if (std::strcmp(uri, LV2_UI__showInterface) == 0) if (std::strcmp(uri, LV2_UI__showInterface) == 0)
return &uiShow; return &uiShow;
if (std::strcmp(uri, LV2_UI__resize) == 0)
return &uiResz;


#if DISTRHO_PLUGIN_WANT_PROGRAMS #if DISTRHO_PLUGIN_WANT_PROGRAMS
static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program }; static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program };


+ 147
- 23
distrho/src/DistrhoUIPrivateData.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 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
@@ -18,14 +18,27 @@
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED #define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED


#include "../DistrhoUI.hpp" #include "../DistrhoUI.hpp"
#include "../../dgl/Application.hpp"


START_NAMESPACE_DISTRHO
#ifndef DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../../dgl/Window.hpp"
#endif


// -----------------------------------------------------------------------
// Static data, see DistrhoUI.cpp
#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
# define DISTRHO_UI_IS_STANDALONE 0
#endif

#if defined(DISTRHO_PLUGIN_TARGET_VST)
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

START_NAMESPACE_DISTRHO


extern double d_lastUiSampleRate;
extern void* d_lastUiDspPtr;
using DGL_NAMESPACE::Application;
using DGL_NAMESPACE::Window;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// UI callbacks // UI callbacks
@@ -37,24 +50,51 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8
typedef void (*setSizeFunc) (void* ptr, uint width, uint height); typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key); typedef bool (*fileRequestFunc) (void* ptr, const char* key);


// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public Application
{
public:
explicit PluginApplication()
: Application(DISTRHO_UI_IS_STANDALONE)
{
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
setClassName(className);
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

class PluginWindow;

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// UI private data // UI private data


struct UI::PrivateData { struct UI::PrivateData {
// DGL
PluginApplication app;
ScopedPointer<PluginWindow> window;

// DSP // DSP
double sampleRate; double sampleRate;
uint32_t parameterOffset; uint32_t parameterOffset;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* dspPtr; void* dspPtr;
#endif


// UI // UI
bool automaticallyScale;
bool resizeInProgress;
uint minWidth;
uint minHeight;
uint bgColor; uint bgColor;
uint fgColor; uint fgColor;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
double scaleFactor;
uintptr_t winId;
#endif


// Callbacks // Callbacks
void* callbacksPtr; void* callbacksPtr;
@@ -66,17 +106,19 @@ struct UI::PrivateData {
fileRequestFunc fileRequestCallbackFunc; fileRequestFunc fileRequestCallbackFunc;


PrivateData() noexcept PrivateData() noexcept
: sampleRate(d_lastUiSampleRate),
parameterOffset(0),
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
dspPtr(d_lastUiDspPtr),
: app(),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
window(nullptr),
#endif #endif
automaticallyScale(false),
resizeInProgress(false),
minWidth(0),
minHeight(0),
sampleRate(0),
parameterOffset(0),
dspPtr(nullptr),
bgColor(0), bgColor(0),
fgColor(0),
fgColor(0xffffffff),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
scaleFactor(1.0),
winId(0),
#endif
callbacksPtr(nullptr), callbacksPtr(nullptr),
editParamCallbackFunc(nullptr), editParamCallbackFunc(nullptr),
setParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr),
@@ -85,8 +127,6 @@ struct UI::PrivateData {
setSizeCallbackFunc(nullptr), setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr) fileRequestCallbackFunc(nullptr)
{ {
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));

#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY # if DISTRHO_PLUGIN_WANT_LATENCY
@@ -143,7 +183,91 @@ struct UI::PrivateData {


return false; return false;
} }

static UI::PrivateData* s_nextPrivateData;
static PluginWindow& createNextWindow(UI* ui, uint width, uint height);
};

// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO external ui stuff
class PluginWindow
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height)
: Window(pData->app, pData->winId, pData->scaleFactor, width, height, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr) {}

uint getWidth() const noexcept
{
return ui->getWidth();
}

uint getHeight() const noexcept
{
return ui->getHeight();
}

bool isVisible() const noexcept
{
return ui->isRunning();
}

uintptr_t getNativeWindowHandle() const noexcept
{
return 0;
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else
class PluginWindow : public Window
{
UI* const ui;

public:
explicit PluginWindow(UI* const uiPtr, UI::PrivateData* const pData, const uint width, const uint height)
: Window(pData->app, pData->winId, width, height, pData->scaleFactor, DISTRHO_UI_USER_RESIZABLE),
ui(uiPtr) {}

protected:
void onFocus(const bool focus, const CrossingMode mode) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiFocus(focus, mode);
}

void onReshape(const uint width, const uint height) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiReshape(width, height);
}

void onScaleFactorChanged(const double scaleFactor) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiScaleFactorChanged(scaleFactor);
}

# ifndef DGL_FILE_BROWSER_DISABLED
void onFileSelected(const char* const filename) override
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

ui->uiFileBrowserSelected(filename);
}
# endif

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
}; };
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------




+ 21
- 5
examples/Info/InfoExampleUI.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2021 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
@@ -14,11 +14,9 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */


#include "DistrhoPluginInfo.h"

#include "DistrhoUI.hpp" #include "DistrhoUI.hpp"


#include "Window.hpp"
#include "ResizeHandle.hpp"


START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO


@@ -33,7 +31,9 @@ public:
InfoExampleUI() InfoExampleUI()
: UI(kInitialWidth, kInitialHeight), : UI(kInitialWidth, kInitialHeight),
fSampleRate(getSampleRate()), fSampleRate(getSampleRate()),
fScale(1.0f)
fResizable(isResizable()),
fScale(1.0f),
fResizeHandle(this)
{ {
std::memset(fParameters, 0, sizeof(float)*kParameterCount); std::memset(fParameters, 0, sizeof(float)*kParameterCount);
std::memset(fStrBuf, 0, sizeof(char)*(0xff+1)); std::memset(fStrBuf, 0, sizeof(char)*(0xff+1));
@@ -45,6 +45,10 @@ public:
#endif #endif


setGeometryConstraints(kInitialWidth, kInitialHeight, true); setGeometryConstraints(kInitialWidth, kInitialHeight, true);

// no need to show resize handle if window is user-resizable
if (fResizable)
fResizeHandle.hide();
} }


protected: protected:
@@ -57,6 +61,11 @@ protected:
*/ */
void parameterChanged(uint32_t index, float value) override void parameterChanged(uint32_t index, float value) override
{ {
// some hosts send parameter change events for output parameters even when nothing changed
// we catch that here in order to prevent excessive repaints
if (d_isEqual(fParameters[index], value))
return;

fParameters[index] = value; fParameters[index] = value;
repaint(); repaint();
} }
@@ -123,6 +132,11 @@ protected:
drawRight(x, y, (fParameters[kParameterCanRequestParameterValueChanges] > 0.5f) ? "Yes" : "No", 40); drawRight(x, y, (fParameters[kParameterCanRequestParameterValueChanges] > 0.5f) ? "Yes" : "No", 40);
y+=lineHeight; y+=lineHeight;


// resizable
drawLeft(x, y, "Host resizable:", 20);
drawRight(x, y, fResizable ? "Yes" : "No", 40);
y+=lineHeight;

// BBT // BBT
x = 200.0f * fScale; x = 200.0f * fScale;
y = 15.0f * fScale; y = 15.0f * fScale;
@@ -184,7 +198,9 @@ private:
double fSampleRate; double fSampleRate;


// UI stuff // UI stuff
bool fResizable;
float fScale; float fScale;
ResizeHandle fResizeHandle;


// temp buf for text // temp buf for text
char fStrBuf[0xff+1]; char fStrBuf[0xff+1];


+ 176
- 0
examples/Info/ResizeHandle.hpp View File

@@ -0,0 +1,176 @@
/*
* Resize handle for DPF
* Copyright (C) 2021 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 "TopLevelWidget.hpp"
#include "Color.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),
resizing(false)
{
resetArea();
}

/** Overloaded constructor, will fetch the window from an existing top-level widget. */
explicit ResizeHandle(TopLevelWidget* const tlw)
: TopLevelWidget(tlw->getWindow()),
handleSize(16),
resizing(false)
{
resetArea();
}

/** Set the handle size, minimum 16. */
void setHandleSize(const uint size)
{
handleSize = std::max(16u, size);
resetArea();
}

protected:
void onDisplay() override
{
const GraphicsContext& context(getGraphicsContext());
const double lineWidth = 1.0 * getScaleFactor();

// 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);
}

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:
Rectangle<uint> area;
Line<double> l1, l2, l3;
uint handleSize;

// event handling state
bool resizing;
Point<double> lastResizePoint;
Size<double> resizingSize;

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

+ 151
- 0
tests/Demo.cpp View File

@@ -234,6 +234,155 @@ 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


@@ -263,6 +412,7 @@ public:
wText(this), wText(this),
#endif #endif
wLeft(this, this), wLeft(this, this),
resizer(this),
curWidget(nullptr) curWidget(nullptr)
{ {
wColor.hide(); wColor.hide();
@@ -350,6 +500,7 @@ private:
ExampleTextSubWidget wText; ExampleTextSubWidget wText;
#endif #endif
LeftSideWidget wLeft; LeftSideWidget wLeft;
ResizeHandle resizer;


Widget* curWidget; Widget* curWidget;
}; };


Loading…
Cancel
Save