Reworked resize handlingpull/282/head
| @@ -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 | ||||
| @@ -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); | ||||
| @@ -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(). | ||||
| @@ -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 | ||||
| @@ -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); | ||||
| @@ -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) | ||||
| @@ -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() | ||||
| @@ -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*) | ||||
| { | { | ||||
| @@ -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 | ||||
| } | } | ||||
| @@ -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. */ | ||||
| @@ -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 | ||||
| @@ -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); | ||||
| @@ -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) | ||||
| }; | }; | ||||
| @@ -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; } | ||||
| @@ -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); | ||||
| @@ -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 | ||||
| @@ -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>; | |||||
| @@ -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; | ||||
| @@ -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) | ||||
| }; | }; | ||||
| @@ -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 }; | ||||
| @@ -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 | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -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]; | ||||
| @@ -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 | |||||
| @@ -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; | ||||
| }; | }; | ||||