From 358da5aa4dc1b11c928a92b543f8b2e568be26bc Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 27 May 2021 23:02:21 +0100 Subject: [PATCH 01/13] Start rework of core UI class, special attention to size Signed-off-by: falkTX --- dgl/Window.hpp | 4 + distrho/DistrhoUI.hpp | 44 +++- distrho/extra/ScopedPointer.hpp | 3 + distrho/src/DistrhoPluginJack.cpp | 10 +- distrho/src/DistrhoPluginVST.cpp | 9 +- distrho/src/DistrhoUI.cpp | 120 ++++----- distrho/src/DistrhoUIDSSI.cpp | 23 +- distrho/src/DistrhoUIInternal.hpp | 353 ++++++++------------------- distrho/src/DistrhoUILV2.cpp | 16 +- distrho/src/DistrhoUIPrivateData.hpp | 158 ++++++++++-- 10 files changed, 350 insertions(+), 390 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index f1f07c85..d0a6b34d 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -367,6 +367,10 @@ protected: A function called when the window is attempted to be closed. 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. + + 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(); diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 6423c630..77e1ae69 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -237,26 +237,46 @@ protected: * 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() {} -# 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); +# 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 */ @@ -265,7 +285,7 @@ protected: This is overriden here so the host knows when the UI is resized by you. @see Widget::onResize(const ResizeEvent&) */ - void onResize(const ResizeEvent& ev) override; +// void onResize(const ResizeEvent& ev) override; #endif // ------------------------------------------------------------------------------------------------------- @@ -273,8 +293,8 @@ protected: private: struct PrivateData; PrivateData* const uiData; + friend class PluginWindow; friend class UIExporter; - friend class UIExporterWindow; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UI) }; diff --git a/distrho/extra/ScopedPointer.hpp b/distrho/extra/ScopedPointer.hpp index b8efbf0a..c756a1a8 100644 --- a/distrho/extra/ScopedPointer.hpp +++ b/distrho/extra/ScopedPointer.hpp @@ -145,6 +145,9 @@ public: /** Returns the object that this ScopedPointer refers to. */ 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. */ ObjectType& operator*() const noexcept { return *object; } diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 495ffb7e..c1e1a9b3 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -17,7 +17,6 @@ #include "DistrhoPluginInternal.hpp" #if DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_UI_IS_STANDALONE true # include "DistrhoUIInternal.hpp" # include "../extra/RingBuffer.hpp" #else @@ -110,7 +109,9 @@ public: PluginJack(jack_client_t* const client) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), #if DISTRHO_PLUGIN_HAS_UI - fUI(this, 0, + fUI(this, + 0, // winId + d_lastSampleRate, nullptr, // edit param setParameterValueCallback, setStateCallback, @@ -301,7 +302,7 @@ protected: } } - fUI.exec_idle(); + fUI.idle(); } #endif @@ -797,9 +798,6 @@ int main() d_lastBufferSize = jack_get_buffer_size(client); d_lastSampleRate = jack_get_sample_rate(client); -#if DISTRHO_PLUGIN_HAS_UI - d_lastUiSampleRate = d_lastSampleRate; -#endif const PluginJack p(client); diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 0725bb9c..8c7c08a8 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -25,7 +25,6 @@ #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 "../extra/RingBuffer.hpp" #endif @@ -178,7 +177,7 @@ public: fEffect(effect), fUiHelper(uiHelper), fPlugin(plugin), - fUI(this, winId, + fUI(this, winId, plugin->getSampleRate(), editParameterCallback, setParameterCallback, setStateCallback, @@ -671,9 +670,7 @@ public: } else { - d_lastUiSampleRate = fPlugin.getSampleRate(); - - UIExporter tmpUI(nullptr, 0, + UIExporter tmpUI(nullptr, 0,fPlugin.getSampleRate(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fPlugin.getInstancePointer(), fLastScaleFactor); fVstRect.right = tmpUI.getWidth(); @@ -694,8 +691,6 @@ public: return 0; } # endif - d_lastUiSampleRate = fPlugin.getSampleRate(); - fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor); # if DISTRHO_PLUGIN_WANT_FULL_STATE diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 652826ff..666b0a1c 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -20,71 +20,38 @@ # include "src/TopLevelWidgetPrivateData.hpp" #endif -#include "NanoVG.hpp" - 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 -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 -// ----------------------------------------------------------------------------------------------------------- +/* ------------------------------------------------------------------------------------------------------------ + * 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) -{ - d_lastUiDspPtr = dspPtr; - d_lastUiWindow = window; - UI* const ret = createUI(); - d_lastUiDspPtr = nullptr; - d_lastUiWindow = nullptr; - return ret; -} -#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI +UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr; + +PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height) +{ + UI::PrivateData* const pData = s_nextPrivateData; + pData->window = new PluginWindow(ui, pData, width, height); + return pData->window.getObject(); +} /* ------------------------------------------------------------------------------------------------------------ * 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() { - delete uiData; } /* ------------------------------------------------------------------------------------------------------------ @@ -166,22 +133,22 @@ uintptr_t UI::getNextWindowId() noexcept return g_nextWindowId; } # endif -#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI +#endif /* ------------------------------------------------------------------------------------------------------------ * DSP/Plugin Callbacks (optional) */ -void UI::sampleRateChanged(double) {} +void UI::sampleRateChanged(double) +{ +} #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI /* ------------------------------------------------------------------------------------------------------------ * UI Callbacks (optional) */ -# ifndef DGL_FILE_BROWSER_DISABLED -void UI::uiFileBrowserSelected(const char*) +void UI::uiFocus(bool, CrossingMode) { } -# endif void UI::uiReshape(uint, uint) { @@ -189,33 +156,32 @@ void UI::uiReshape(uint, uint) pData->fallbackOnResize(); } -/* ------------------------------------------------------------------------------------------------------------ - * UI Resize Handling, internal */ - -void UI::onResize(const ResizeEvent& ev) +# ifndef DGL_FILE_BROWSER_DISABLED +void UI::uiFileBrowserSelected(const char*) { - if (uiData->resizeInProgress) - return; - - UIWidget::onResize(ev); +} +# endif - const uint width = ev.size.getWidth(); - const uint height = ev.size.getHeight(); +/* ------------------------------------------------------------------------------------------------------------ + * UI Resize Handling, internal */ - /* - pData->window.setSize(width, height); - */ - uiData->setSizeCallback(width, height); -} +// void UI::onResize(const ResizeEvent& ev) +// { +// if (uiData->resizeInProgress) +// return; +// +// UIWidget::onResize(ev); +// +// const uint width = ev.size.getWidth(); +// const uint height = ev.size.getHeight(); +// +// /* +// pData->window.setSize(width, height); +// */ +// uiData->setSizeCallback(width, height); +// } #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // ----------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO - -// ----------------------------------------------------------------------- -// Possible template data types - -// template class NanoBaseWidget; -// template class NanoBaseWidget; -// template class NanoBaseWidget; diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index 5207d27f..f636e403 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -14,7 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define DISTRHO_UI_IS_STANDALONE true #include "DistrhoUIInternal.hpp" #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS @@ -97,8 +96,9 @@ struct OscData { class UIDssi { 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, setSizeCallback, nullptr), fHostClosed(false), fOscData(oscData) { @@ -252,16 +252,17 @@ private: static OscData gOscData; static const char* gUiTitle = nullptr; static UIDssi* globalUI = nullptr; +static double sampleRate = 0.0; static void initUiIfNeeded() { if (globalUI != nullptr) 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 +338,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*) { - 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) globalUI->dssiui_samplerate(sampleRate); @@ -481,7 +480,7 @@ int main(int argc, char* argv[]) { lo_server_recv(oscServer); - if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) + if (sampleRate != 0.0 || globalUI != nullptr) break; d_msleep(50); @@ -489,7 +488,7 @@ int main(int argc, char* argv[]) int ret = 1; - if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) + if (sampleRate != 0.0 || globalUI != nullptr) { initUiIfNeeded(); diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 47c5b07a..71a17815 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -21,13 +21,6 @@ #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI # 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 START_NAMESPACE_DISTRHO @@ -35,121 +28,32 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- // Static data, see DistrhoUI.cpp -extern double d_lastUiSampleRate; -extern void* d_lastUiDspPtr; #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI -extern const char* g_nextBundlePath; -extern double g_nextScaleFactor; 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 - "-" 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; - } + // ------------------------------------------------------------------- + // UI Widget and its private data -# ifndef DGL_FILE_BROWSER_DISABLED - // custom file-browser selected - void onFileSelected(const char* const filename) override - { - DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); + UI* ui; + UI::PrivateData* uiData; - fUI->uiFileBrowserSelected(filename); - } -# endif + // prevent resize recursion + bool changingSizeRecursionCheck; -private: - UI* const fUI; - bool fIsReady; -}; -#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI - -// ----------------------------------------------------------------------- -// UI exporter class + // ------------------------------------------------------------------- -class UIExporter -{ public: UIExporter(void* const callbacksPtr, - const intptr_t winId, + const uintptr_t winId, + const double sampleRate, const editParamFunc editParamCall, const setParamFunc setParamCall, const setStateFunc setStateCall, @@ -161,139 +65,130 @@ public: const double scaleFactor = 1.0, const uint32_t bgColor = 0, 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 - // unused - return; (void)bundlePath; + uiData->scaleFactor = scaleFactor; + uiData->winId = winId; #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 - ~UIExporter() - { - delete fUI; - } + g_nextWindowId = winId; + g_nextScaleFactor = scaleFactor; + g_nextBundlePath = bundlePath; #endif + UI::PrivateData::s_nextPrivateData = uiData; - // ------------------------------------------------------------------- + UI* const uiPtr = createUI(); #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 { - return glWindow.getWidth(); + return uiData->window->getWidth(); } uint getHeight() const noexcept { - return glWindow.getHeight(); + return uiData->window->getHeight(); } bool isVisible() const noexcept { - return glWindow.isVisible(); + return uiData->window->isVisible(); } uintptr_t getNativeWindowHandle() const noexcept { - return glWindow.getNativeWindowHandle(); + return uiData->window->getNativeWindowHandle(); } -#endif 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 { - 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 { - 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) { - 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 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 #if DISTRHO_PLUGIN_WANT_STATE 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(value != nullptr,); - fUI->stateChanged(key, value); + ui->stateChanged(key, value); } #endif @@ -303,12 +198,12 @@ public: void exec(IdleCallback* const cb) { 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(); - while (fUI->isRunning()) + while (ui->isRunning()) { d_msleep(10); cb->idleCallback(); @@ -330,49 +225,38 @@ public: 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 void exec(IdleCallback* const cb) { DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); - glWindow.setVisible(true); - glApp.addIdleCallback(cb); - glApp.exec(); - } - - void exec_idle() - { - if (glWindow.isReady()) - fUI->uiIdle(); + uiData->window->show(); + uiData->app.addIdleCallback(cb); + uiData->app.exec(); } void focus() { - glWindow.focus(); + uiData->window->focus(); } bool idle() { - DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false); - glApp.idle(); - - if (glWindow.isReady()) - fUI->uiIdle(); - - return ! glApp.isQuiting(); + ui->uiIdle(); + return ! uiData->app.isQuiting(); } void quit() { - glWindow.close(); - glApp.quit(); + uiData->window->close(); + uiData->app.quit(); } #endif @@ -381,52 +265,46 @@ public: #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI 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) { - DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); - fUI->setTransientWinId(winId); + ui->setTransientWinId(winId); } 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 void setWindowTitle(const char* const uiTitle) { - glWindow.setTitle(uiTitle); +// glWindow.setTitle(uiTitle); } - void setWindowSize(const uint width, const uint height, const bool updateUI = false) + void setWindowSize(const uint width, const uint height) { - DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,); - - fChangingSize = true; + DISTRHO_SAFE_ASSERT_RETURN(! changingSizeRecursionCheck,); - if (updateUI) - fUI->setSize(width, height); - - glWindow.setSize(width, height); - - fChangingSize = false; + changingSizeRecursionCheck = true; +// glWindow.setSize(width, height); + changingSizeRecursionCheck = false; } void setWindowTransientWinId(const uintptr_t /*winId*/) @@ -438,9 +316,10 @@ public: bool setWindowVisible(const bool yesNo) { - glWindow.setVisible(yesNo); +// glWindow.setVisible(yesNo); - return ! glApp.isQuiting(); +// return ! glApp.isQuiting(); + return true; } bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/) @@ -464,37 +343,19 @@ public: 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); - if (d_isEqual(fData->sampleRate, sampleRate)) + if (d_isEqual(uiData->sampleRate, sampleRate)) return; - fData->sampleRate = sampleRate; + uiData->sampleRate = sampleRate; 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) }; diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 67ece68d..1485da29 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -14,7 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define DISTRHO_UI_IS_STANDALONE false #include "DistrhoUIInternal.hpp" #include "../extra/String.hpp" @@ -73,10 +72,11 @@ public: const LV2UI_Write_Function writeFunc, LV2UI_Widget* const widget, void* const dspPtr, + const float sampleRate, const float scaleFactor, const uint32_t bgColor, const uint32_t fgColor) - : fUI(this, winId, + : fUI(this, winId, sampleRate, editParameterCallback, setParameterCallback, setStateCallback, @@ -208,7 +208,8 @@ public: int lv2ui_resize(uint width, uint height) { - fUI.setWindowSize(width, height, true); + // FIXME + // fUI.setWindowSize(width, height, true); return 0; } @@ -526,6 +527,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, #endif const intptr_t winId = (intptr_t)parentId; + float sampleRate = 0.0f; float scaleFactor = 1.0f; uint32_t bgColor = 0; uint32_t fgColor = 0xffffffff; @@ -544,7 +546,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, /**/ if (options[i].key == uridSampleRate) { if (options[i].type == uridAtomFloat) - d_lastUiSampleRate = *(const float*)options[i].value; + sampleRate = *(const float*)options[i].value; else d_stderr("Host provides UI sample-rate but has wrong value type"); } @@ -572,15 +574,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_lastUiSampleRate = 44100.0; + sampleRate = 44100.0; } return new UiLv2(bundlePath, winId, options, uridMap, features, controller, writeFunction, widget, instance, - scaleFactor, bgColor, fgColor); + sampleRate, scaleFactor, bgColor, fgColor); } #define uiPtr ((UiLv2*)ui) diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 7f871e09..42c687ed 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2020 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -18,14 +18,22 @@ #define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED #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 true +#else +# define DISTRHO_UI_IS_STANDALONE false +#endif -extern double d_lastUiSampleRate; -extern void* d_lastUiDspPtr; +START_NAMESPACE_DISTRHO + +using DGL_NAMESPACE::Application; +using DGL_NAMESPACE::Window; // ----------------------------------------------------------------------- // UI callbacks @@ -37,24 +45,51 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8 typedef void (*setSizeFunc) (void* ptr, uint width, uint height); 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 struct UI::PrivateData { + // DGL + PluginApplication app; + ScopedPointer window; + // DSP double sampleRate; uint32_t parameterOffset; -#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS void* dspPtr; -#endif // UI - bool automaticallyScale; - bool resizeInProgress; - uint minWidth; - uint minHeight; uint bgColor; uint fgColor; +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + double scaleFactor; + uintptr_t winId; +#endif // Callbacks void* callbacksPtr; @@ -66,17 +101,19 @@ struct UI::PrivateData { fileRequestFunc fileRequestCallbackFunc; 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 - automaticallyScale(false), - resizeInProgress(false), - minWidth(0), - minHeight(0), + sampleRate(0), + parameterOffset(0), + dspPtr(nullptr), bgColor(0), - fgColor(0), + fgColor(0xffffffff), +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + scaleFactor(1.0), + winId(0), +#endif callbacksPtr(nullptr), editParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr), @@ -85,8 +122,6 @@ struct UI::PrivateData { setSizeCallbackFunc(nullptr), fileRequestCallbackFunc(nullptr) { - DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); - #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; # if DISTRHO_PLUGIN_WANT_LATENCY @@ -143,7 +178,84 @@ struct UI::PrivateData { 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); + } + +# 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 // ----------------------------------------------------------------------- From 3c641b3fe15a65654c3e7653971dff381d9a0507 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 27 May 2021 23:18:20 +0100 Subject: [PATCH 02/13] Skip external ui during rework Signed-off-by: falkTX --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 6454e014..d65c1c8e 100644 --- a/Makefile +++ b/Makefile @@ -32,15 +32,15 @@ examples: dgl ifeq ($(HAVE_CAIRO),true) $(MAKE) all -C examples/CairoUI 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) gen: examples utils/lv2_ttl_generator From 57817027713af9e3b4bea411106c47c668478980 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 27 May 2021 23:19:01 +0100 Subject: [PATCH 03/13] Fix/make clear distinction between standalone and plugin ui idle Signed-off-by: falkTX --- distrho/src/DistrhoPluginJack.cpp | 2 +- distrho/src/DistrhoPluginVST.cpp | 2 +- distrho/src/DistrhoUIDSSI.cpp | 24 +++++++++++++----------- distrho/src/DistrhoUIInternal.hpp | 24 +++++++++++++++--------- distrho/src/DistrhoUILV2.cpp | 4 ++-- distrho/src/DistrhoUIPrivateData.hpp | 4 ++-- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index c1e1a9b3..3707450a 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -302,7 +302,7 @@ protected: } } - fUI.idle(); + fUI.exec_idle(); } #endif diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index 8c7c08a8..a245f237 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -212,7 +212,7 @@ public: } } - fUI.idle(); + fUI.plugin_idle(); } int16_t getWidth() const diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index f636e403..9ea2db31 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -93,7 +93,7 @@ struct OscData { // ----------------------------------------------------------------------- -class UIDssi +class UIDssi : public IdleCallback { public: UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate) @@ -111,17 +111,19 @@ public: 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(); } // ------------------------------------------------------------------- @@ -393,7 +395,7 @@ int main(int argc, char* argv[]) initUiIfNeeded(); globalUI->dssiui_show(true); - globalUI->exec(); + globalUI->exec_start(); delete globalUI; globalUI = nullptr; @@ -492,7 +494,7 @@ int main(int argc, char* argv[]) { initUiIfNeeded(); - globalUI->exec(); + globalUI->exec_start(); delete globalUI; globalUI = nullptr; diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index 71a17815..dbfd0f80 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -210,10 +210,6 @@ public: } } - void exec_idle() - { - } - bool idle() { return true; @@ -231,6 +227,7 @@ public: ui->terminateAndWaitForProcess(); } #else +# if DISTRHO_UI_IS_STANDALONE void exec(IdleCallback* const cb) { DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); @@ -240,26 +237,35 @@ public: uiData->app.exec(); } - void focus() + void exec_idle() { - uiData->window->focus(); - } + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, ); - bool idle() + ui->uiIdle(); + } +# else + bool plugin_idle() { DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false); + uiData->app.idle(); ui->uiIdle(); return ! uiData->app.isQuiting(); } +# endif + + void focus() + { + uiData->window->focus(); + } void quit() { uiData->window->close(); uiData->app.quit(); } - #endif + // ------------------------------------------------------------------- #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 1485da29..85132e65 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -191,9 +191,9 @@ public: int lv2ui_idle() { 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() diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index 42c687ed..afaaa84e 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -25,9 +25,9 @@ #endif #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) -# define DISTRHO_UI_IS_STANDALONE true +# define DISTRHO_UI_IS_STANDALONE 1 #else -# define DISTRHO_UI_IS_STANDALONE false +# define DISTRHO_UI_IS_STANDALONE 0 #endif START_NAMESPACE_DISTRHO From 919f18973ac4ec270bb2f4028517336ae2ec0ce8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 12:05:29 +0100 Subject: [PATCH 04/13] Allow one Window to have many top level widgets Signed-off-by: falkTX --- dgl/src/TopLevelWidgetPrivateData.cpp | 5 +- dgl/src/WindowPrivateData.cpp | 85 ++++++++++++++++++++------- dgl/src/WindowPrivateData.hpp | 6 +- 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/dgl/src/TopLevelWidgetPrivateData.cpp b/dgl/src/TopLevelWidgetPrivateData.cpp index 5adbca95..ef3d54a8 100644 --- a/dgl/src/TopLevelWidgetPrivateData.cpp +++ b/dgl/src/TopLevelWidgetPrivateData.cpp @@ -28,13 +28,12 @@ TopLevelWidget::PrivateData::PrivateData(TopLevelWidget* const s, Window& w) selfw(s), window(w) { - window.pData->topLevelWidget = self; + window.pData->topLevelWidgets.push_back(self); } TopLevelWidget::PrivateData::~PrivateData() { - // FIXME? - window.pData->topLevelWidget = nullptr; + window.pData->topLevelWidgets.remove(self); } bool TopLevelWidget::PrivateData::keyboardEvent(const KeyboardEvent& ev) diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 5eecc74c..57ea9977 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -51,6 +51,12 @@ START_NAMESPACE_DGL #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +#define FOR_EACH_TOP_LEVEL_WIDGET(it) \ + for (std::list::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it) + +#define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \ + for (std::list::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit) + // ----------------------------------------------------------------------- #ifdef DISTRHO_OS_WINDOWS @@ -73,7 +79,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) appData(a.pData), self(s), view(puglNewView(appData->world)), - topLevelWidget(nullptr), + topLevelWidgets(), isClosed(true), isVisible(false), isEmbed(false), @@ -95,7 +101,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c appData(a.pData), self(s), view(puglNewView(appData->world)), - topLevelWidget(nullptr), + topLevelWidgets(), isClosed(true), isVisible(false), isEmbed(false), @@ -121,7 +127,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, appData(a.pData), self(s), view(puglNewView(appData->world)), - topLevelWidget(nullptr), + topLevelWidgets(), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), @@ -152,7 +158,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, appData(a.pData), self(s), view(puglNewView(appData->world)), - topLevelWidget(nullptr), + topLevelWidgets(), isClosed(parentWindowHandle == 0), isVisible(parentWindowHandle != 0), isEmbed(parentWindowHandle != 0), @@ -642,8 +648,12 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh self->onReshape(uwidth, uheight); #ifndef DPF_TEST_WINDOW_CPP - if (topLevelWidget != nullptr) - topLevelWidget->setSize(uwidth, uheight); + FOR_EACH_TOP_LEVEL_WIDGET(it) + { + TopLevelWidget* const widget(*it); + + widget->setSize(uwidth, uheight); + } #endif // always repaint after a resize @@ -657,8 +667,13 @@ void Window::PrivateData::onPuglExpose() puglOnDisplayPrepare(view); #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 } @@ -711,8 +726,13 @@ void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) return modal.child->focus(); #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 } @@ -724,8 +744,13 @@ void Window::PrivateData::onPuglSpecial(const Widget::SpecialEvent& ev) return modal.child->focus(); #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 } @@ -737,8 +762,13 @@ void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) return modal.child->focus(); #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 } @@ -750,8 +780,13 @@ void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) return modal.child->focus(); #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 } @@ -763,8 +798,13 @@ void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) return modal.child->focus(); #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 } @@ -776,8 +816,13 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) return modal.child->focus(); #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 } diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index 99450b22..c62ea50e 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -23,6 +23,8 @@ #include "pugl.hpp" +#include + START_NAMESPACE_DGL class TopLevelWidget; @@ -45,8 +47,8 @@ struct Window::PrivateData : IdleCallback { /** Reserved space for graphics context. */ 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 topLevelWidgets; /** 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). */ From df5bff7440b04ad5b89c3ce82bc4f285f97c97c3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 13:37:20 +0100 Subject: [PATCH 05/13] More special handling for resize Signed-off-by: falkTX --- dgl/TopLevelWidget.hpp | 31 +++++++++++++++++++++++++++++++ dgl/Window.hpp | 2 +- dgl/src/SubWidget.cpp | 3 +-- dgl/src/TopLevelWidget.cpp | 25 +++++++++++++++++++++++++ dgl/src/Widget.cpp | 2 ++ dgl/src/WindowPrivateData.cpp | 10 +++++++++- 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 165f41bd..4b0de992 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -66,6 +66,37 @@ public: */ Window& getWindow() const noexcept; + /** + Check if this top-level widget's window is resizable (by the user or window manager). + For situations where this top-level widget is an embed plugin UI and the plugin host does not support resizing, + this function can return false where it normally returns true. + + 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; + + /** + 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& size); + // TODO group stuff after here, convenience functions present in Window class bool addIdleCallback(IdleCallback* callback, uint timerFrequencyInMs = 0); bool removeIdleCallback(IdleCallback* callback); diff --git a/dgl/Window.hpp b/dgl/Window.hpp index d0a6b34d..bd6c0b29 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -185,7 +185,7 @@ public: void close(); /** - Check if this window is resizable. + Check if this window is resizable (by the user or window manager). @see setResizable */ bool isResizable() const noexcept; diff --git a/dgl/src/SubWidget.cpp b/dgl/src/SubWidget.cpp index 3e0ef59d..40b34a11 100644 --- a/dgl/src/SubWidget.cpp +++ b/dgl/src/SubWidget.cpp @@ -96,8 +96,7 @@ void SubWidget::setAbsolutePos(const Point& pos) noexcept pData->absolutePos = pos; onPositionChanged(ev); - // repaint the bounds of parent - pData->parentWidget->repaint(); + repaint(); } Widget* SubWidget::getParentWidget() const noexcept diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index 9f68360e..ffd227dd 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -40,6 +40,31 @@ Window& TopLevelWidget::getWindow() const noexcept return pData->window; } +bool TopLevelWidget::isResizable() const noexcept +{ + return pData->window.isResizable(); +} + +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& size) +{ + pData->window.setSize(size); +} + bool TopLevelWidget::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) { return pData->window.addIdleCallback(callback, timerFrequencyInMs); diff --git a/dgl/src/Widget.cpp b/dgl/src/Widget.cpp index 31095002..0a393572 100644 --- a/dgl/src/Widget.cpp +++ b/dgl/src/Widget.cpp @@ -46,6 +46,8 @@ void Widget::setVisible(bool visible) pData->visible = visible; repaint(); + + // FIXME check case of hiding a previously visible widget, does it trigger a repaint? } void Widget::show() diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 57ea9977..33fe82dd 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -652,7 +652,15 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh { TopLevelWidget* const widget(*it); - widget->setSize(uwidth, uheight); + /* 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 From 69d1f91edba7fd64722d0b2c8f3d681439e7d477 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 13:55:03 +0100 Subject: [PATCH 06/13] Add a resize handle to test demo Signed-off-by: falkTX --- tests/Demo.cpp | 151 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/tests/Demo.cpp b/tests/Demo.cpp index 751ce106..bb059593 100644 --- a/tests/Demo.cpp +++ b/tests/Demo.cpp @@ -234,6 +234,155 @@ private: #endif }; +// -------------------------------------------------------------------------------------------------------------------- +// Resize handle widget + +class ResizeHandle : public TopLevelWidget +{ + Rectangle area; + Line l1, l2, l3; + uint baseSize; + + // event handling state + bool resizing; + Point lastResizePoint; + Size 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 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(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 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(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 @@ -263,6 +412,7 @@ public: wText(this), #endif wLeft(this, this), + resizer(this), curWidget(nullptr) { wColor.hide(); @@ -350,6 +500,7 @@ private: ExampleTextSubWidget wText; #endif LeftSideWidget wLeft; + ResizeHandle resizer; Widget* curWidget; }; From 8a702163118c693da96dca625573784f9637e729 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 15:05:26 +0100 Subject: [PATCH 07/13] Handle geometry constraints for embed windows; More resize handling Signed-off-by: falkTX --- dgl/Window.hpp | 7 ++++++ dgl/src/Window.cpp | 36 +++++++++++++++++++++++++--- dgl/src/WindowPrivateData.cpp | 4 ++++ dgl/src/WindowPrivateData.hpp | 3 ++- distrho/DistrhoUI.hpp | 11 ++++++++- distrho/src/DistrhoPluginJack.cpp | 12 +--------- distrho/src/DistrhoPluginVST.cpp | 4 +--- distrho/src/DistrhoUI.cpp | 27 ++++++++++----------- distrho/src/DistrhoUIDSSI.cpp | 12 +--------- distrho/src/DistrhoUILV2.cpp | 4 ++-- distrho/src/DistrhoUIPrivateData.hpp | 12 ++++++++++ 11 files changed, 85 insertions(+), 47 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index bd6c0b29..93bf1eb8 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -387,6 +387,13 @@ protected: */ 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 /** A function called when a path is selected by the user, as triggered by openFileBrowser(). diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index adf0f557..fedc5aef 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -136,10 +136,36 @@ void Window::setHeight(const uint 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,); + 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(pData->minWidth) + / static_cast(pData->minHeight); + const double reqRatio = static_cast(width) + / static_cast(height); + + if (d_isNotEqual(ratio, reqRatio)) + { + // fix width + if (reqRatio > ratio) + width = height * ratio; + // fix height + else + height = width / ratio; + } + } + } + // FIXME add default and min props for this if (pData->minWidth == 0 && pData->minHeight == 0) puglSetDefaultSize(pData->view, static_cast(width), static_cast(height)); @@ -250,8 +276,7 @@ void Window::setGeometryConstraints(const uint minimumWidth, DISTRHO_SAFE_ASSERT_RETURN(minimumHeight > 0,); if (pData->isEmbed) { - // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? - DISTRHO_SAFE_ASSERT_RETURN(isResizable(),); + // nothing to do here } else if (! isResizable()) { setResizable(true); } @@ -259,6 +284,7 @@ void Window::setGeometryConstraints(const uint minimumWidth, pData->minWidth = minimumWidth; pData->minHeight = minimumHeight; pData->autoScaling = automaticallyScale; + pData->keepAspectRatio = keepAspectRatio; const double scaleFactor = pData->scaleFactor; @@ -290,6 +316,10 @@ void Window::onReshape(uint, uint) puglFallbackOnResize(pData->view); } +void Window::onScaleFactorChanged(double) +{ +} + #ifndef DGL_FILE_BROWSER_DISABLED void Window::onFileSelected(const char*) { diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index 33fe82dd..a6386683 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -88,6 +88,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaleFactor(1.0), minWidth(0), minHeight(0), + keepAspectRatio(false), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -110,6 +111,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c autoScaleFactor(1.0), minWidth(0), minHeight(0), + keepAspectRatio(false), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -136,6 +138,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), + keepAspectRatio(false), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif @@ -167,6 +170,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaleFactor(1.0), minWidth(0), minHeight(0), + keepAspectRatio(false), #ifdef DISTRHO_OS_WINDOWS win32SelectedFile(nullptr), #endif diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index c62ea50e..b3fd194d 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -67,8 +67,9 @@ struct Window::PrivateData : IdleCallback { bool autoScaling; double autoScaleFactor; - /** Pugl minWidth, minHeight access. */ + /** Pugl geometry constraints access. */ uint minWidth, minHeight; + bool keepAspectRatio; #ifdef DISTRHO_OS_WINDOWS /** Selected file for openFileBrowser on windows, stored for fake async operation. */ diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 77e1ae69..69540bb5 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -264,6 +264,15 @@ protected: */ 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(). @@ -285,7 +294,7 @@ protected: This is overriden here so the host knows when the UI is resized by you. @see Widget::onResize(const ResizeEvent&) */ -// void onResize(const ResizeEvent& ev) override; + void onResize(const ResizeEvent& ev) override; #endif // ------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 3707450a..e58eadef 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -116,7 +116,7 @@ public: setParameterValueCallback, setStateCallback, sendNoteCallback, - setSizeCallback, + nullptr, // window size nullptr, // file request nullptr, // bundle fPlugin.getInstancePointer(), @@ -495,11 +495,6 @@ protected: fPlugin.setParameterValue(index, value); } - void setSize(const uint width, const uint height) - { - fUI.setWindowSize(width, height); - } - # if DISTRHO_PLUGIN_WANT_MIDI_INPUT void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) { @@ -681,11 +676,6 @@ private: thisPtr->setParameterValue(index, value); } - static void setSizeCallback(void* ptr, uint width, uint height) - { - thisPtr->setSize(width, height); - } - # if DISTRHO_PLUGIN_WANT_MIDI_INPUT static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) { diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index a245f237..edc1021d 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -23,8 +23,6 @@ #endif #if DISTRHO_PLUGIN_HAS_UI -# undef DISTRHO_UI_USER_RESIZABLE -# define DISTRHO_UI_USER_RESIZABLE 0 # include "DistrhoUIInternal.hpp" # include "../extra/RingBuffer.hpp" #endif @@ -386,7 +384,7 @@ protected: void setSize(const uint width, const uint height) { - fUI.setWindowSize(width, height); + // fUI.setWindowSize(width, height); hostCallback(audioMasterSizeWindow, width, height); } diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 666b0a1c..fd610f27 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -156,6 +156,10 @@ void UI::uiReshape(uint, uint) pData->fallbackOnResize(); } +void UI::uiScaleFactorChanged(double) +{ +} + # ifndef DGL_FILE_BROWSER_DISABLED void UI::uiFileBrowserSelected(const char*) { @@ -165,21 +169,14 @@ void UI::uiFileBrowserSelected(const char*) /* ------------------------------------------------------------------------------------------------------------ * UI Resize Handling, internal */ -// void UI::onResize(const ResizeEvent& ev) -// { -// if (uiData->resizeInProgress) -// return; -// -// UIWidget::onResize(ev); -// -// const uint width = ev.size.getWidth(); -// const uint height = ev.size.getHeight(); -// -// /* -// pData->window.setSize(width, height); -// */ -// uiData->setSizeCallback(width, height); -// } +void UI::onResize(const ResizeEvent& ev) +{ + UIWidget::onResize(ev); + + const uint width = ev.size.getWidth(); + const uint height = ev.size.getHeight(); + uiData->setSizeCallback(width, height); +} #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI // ----------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index 9ea2db31..13a2395e 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -98,7 +98,7 @@ class UIDssi : public IdleCallback public: UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate) : fUI(this, 0, sampleRate, nullptr, - setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, nullptr), + setParameterCallback, setStateCallback, sendNoteCallback, nullptr, nullptr), fHostClosed(false), fOscData(oscData) { @@ -208,11 +208,6 @@ protected: } #endif - void setSize(const uint width, const uint height) - { - fUI.setWindowSize(width, height); - } - private: UIExporter fUI; bool fHostClosed; @@ -241,11 +236,6 @@ private: } #endif - static void setSizeCallback(void* ptr, uint width, uint height) - { - uiPtr->setSize(width, height); - } - #undef uiPtr }; diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 85132e65..47ba18f1 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -208,7 +208,7 @@ public: int lv2ui_resize(uint width, uint height) { - // FIXME + // this comes from the host // fUI.setWindowSize(width, height, true); return 0; } @@ -334,7 +334,7 @@ protected: void setSize(const uint width, const uint height) { - fUI.setWindowSize(width, height); + // fUI.setWindowSize(width, height); if (fUiResize != nullptr && ! fWinIdWasNull) fUiResize->ui_resize(fUiResize->handle, width, height); diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp index afaaa84e..2301e544 100644 --- a/distrho/src/DistrhoUIPrivateData.hpp +++ b/distrho/src/DistrhoUIPrivateData.hpp @@ -30,6 +30,11 @@ # 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 using DGL_NAMESPACE::Application; @@ -244,6 +249,13 @@ protected: 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 { From 287f43675baa96790997d35cb161e38ef832b3ac Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 15:06:05 +0100 Subject: [PATCH 08/13] Add resize handle for Info example UI, but only visible when needed Signed-off-by: falkTX --- examples/Info/InfoExampleUI.cpp | 21 +++- examples/Info/ResizeHandle.hpp | 176 ++++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 examples/Info/ResizeHandle.hpp diff --git a/examples/Info/InfoExampleUI.cpp b/examples/Info/InfoExampleUI.cpp index 09110e04..e8afc2a5 100644 --- a/examples/Info/InfoExampleUI.cpp +++ b/examples/Info/InfoExampleUI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2019 Filipe Coelho + * Copyright (C) 2012-2021 Filipe Coelho * * 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 @@ -14,11 +14,9 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "DistrhoPluginInfo.h" - #include "DistrhoUI.hpp" -#include "Window.hpp" +#include "ResizeHandle.hpp" START_NAMESPACE_DISTRHO @@ -33,7 +31,9 @@ public: InfoExampleUI() : UI(kInitialWidth, kInitialHeight), fSampleRate(getSampleRate()), - fScale(1.0f) + fResizable(isResizable()), + fScale(1.0f), + fResizeHandle(this) { std::memset(fParameters, 0, sizeof(float)*kParameterCount); std::memset(fStrBuf, 0, sizeof(char)*(0xff+1)); @@ -45,6 +45,10 @@ public: #endif setGeometryConstraints(kInitialWidth, kInitialHeight, true); + + // no need to show resize handle if window is user-resizable + if (fResizable) + fResizeHandle.hide(); } protected: @@ -123,6 +127,11 @@ protected: drawRight(x, y, (fParameters[kParameterCanRequestParameterValueChanges] > 0.5f) ? "Yes" : "No", 40); y+=lineHeight; + // resizable + drawLeft(x, y, "UI resizable:", 20); + drawRight(x, y, fResizable ? "Yes" : "No", 40); + y+=lineHeight; + // BBT x = 200.0f * fScale; y = 15.0f * fScale; @@ -184,7 +193,9 @@ private: double fSampleRate; // UI stuff + bool fResizable; float fScale; + ResizeHandle fResizeHandle; // temp buf for text char fStrBuf[0xff+1]; diff --git a/examples/Info/ResizeHandle.hpp b/examples/Info/ResizeHandle.hpp new file mode 100644 index 00000000..ac66d69a --- /dev/null +++ b/examples/Info/ResizeHandle.hpp @@ -0,0 +1,176 @@ +/* + * Resize handle for DPF + * Copyright (C) 2021 Filipe Coelho + * + * 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 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(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 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 area; + Line l1, l2, l3; + uint handleSize; + + // event handling state + bool resizing; + Point lastResizePoint; + Size resizingSize; + + void resetArea() + { + const double scaleFactor = getScaleFactor(); + const uint margin = 0.0 * scaleFactor; + const uint size = handleSize * scaleFactor; + + area = Rectangle(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 From c2a3737d8bccf5d2787298267e273c5810156ce2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 22:26:45 +0100 Subject: [PATCH 09/13] Cleanup lv2 ui resizing, stick with current best possible approach Signed-off-by: falkTX --- distrho/src/DistrhoUILV2.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/distrho/src/DistrhoUILV2.cpp b/distrho/src/DistrhoUILV2.cpp index 47ba18f1..3573409a 100644 --- a/distrho/src/DistrhoUILV2.cpp +++ b/distrho/src/DistrhoUILV2.cpp @@ -97,8 +97,12 @@ public: fURIDs(uridMap), 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) fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); +#endif if (widget != nullptr) *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); @@ -206,13 +210,6 @@ public: return fUI.setWindowVisible(false) ? 0 : 1; } - int lv2ui_resize(uint width, uint height) - { - // this comes from the host - // fUI.setWindowSize(width, height, true); - return 0; - } - // ------------------------------------------------------------------- uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) @@ -334,8 +331,8 @@ protected: 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) fUiResize->ui_resize(fUiResize->handle, width, height); } @@ -614,16 +611,6 @@ static int lv2ui_hide(LV2UI_Handle ui) 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) @@ -652,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 LV2UI_Idle_Interface uiIdle = { lv2ui_idle }; 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) return &options; @@ -660,8 +646,6 @@ static const void* lv2ui_extension_data(const char* uri) return &uiIdle; if (std::strcmp(uri, LV2_UI__showInterface) == 0) return &uiShow; - if (std::strcmp(uri, LV2_UI__resize) == 0) - return &uiResz; #if DISTRHO_PLUGIN_WANT_PROGRAMS static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program }; From d6c2658a5c1c25494ab931dd923a5fc8d743baee Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 28 May 2021 22:28:05 +0100 Subject: [PATCH 10/13] Move TopLevelWidget::isResizable to UI, where it makes more sense Signed-off-by: falkTX --- dgl/TopLevelWidget.hpp | 10 ---------- dgl/src/TopLevelWidget.cpp | 5 ----- distrho/DistrhoUI.hpp | 9 +++++++++ distrho/src/DistrhoUI.cpp | 9 +++++++++ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/dgl/TopLevelWidget.hpp b/dgl/TopLevelWidget.hpp index 4b0de992..fbbd8812 100644 --- a/dgl/TopLevelWidget.hpp +++ b/dgl/TopLevelWidget.hpp @@ -66,16 +66,6 @@ public: */ Window& getWindow() const noexcept; - /** - Check if this top-level widget's window is resizable (by the user or window manager). - For situations where this top-level widget is an embed plugin UI and the plugin host does not support resizing, - this function can return false where it normally returns true. - - 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; - /** 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 diff --git a/dgl/src/TopLevelWidget.cpp b/dgl/src/TopLevelWidget.cpp index ffd227dd..e05d70a9 100644 --- a/dgl/src/TopLevelWidget.cpp +++ b/dgl/src/TopLevelWidget.cpp @@ -40,11 +40,6 @@ Window& TopLevelWidget::getWindow() const noexcept return pData->window; } -bool TopLevelWidget::isResizable() const noexcept -{ - return pData->window.isResizable(); -} - void TopLevelWidget::setWidth(const uint width) { pData->window.setWidth(width); diff --git a/distrho/DistrhoUI.hpp b/distrho/DistrhoUI.hpp index 69540bb5..62c4ab9e 100644 --- a/distrho/DistrhoUI.hpp +++ b/distrho/DistrhoUI.hpp @@ -81,6 +81,15 @@ public: /* -------------------------------------------------------------------------------------------------------- * 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. Returns 0 by default, in case of error or lack of host support. diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index fd610f27..1cee53de 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -57,6 +57,15 @@ UI::~UI() /* ------------------------------------------------------------------------------------------------------------ * 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 { return uiData->bgColor; From 68c55e0dfd6df1e06362d3a7ae1a46b7925ba5c1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 29 May 2021 00:11:57 +0100 Subject: [PATCH 11/13] Always set default size; Continue resize cleanup Signed-off-by: falkTX --- dgl/src/Window.cpp | 6 ++--- dgl/src/WindowPrivateData.cpp | 23 ++++-------------- dgl/src/pugl.cpp | 40 ++++++++++++++++++++++++++++--- dgl/src/pugl.hpp | 6 ++++- distrho/src/DistrhoPluginVST.cpp | 2 +- distrho/src/DistrhoUIInternal.hpp | 16 +++---------- 6 files changed, 53 insertions(+), 40 deletions(-) diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index fedc5aef..7eefa63b 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -145,8 +145,10 @@ void Window::setSize(uint width, uint height) // 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(pData->minWidth) @@ -166,10 +168,6 @@ void Window::setSize(uint width, uint height) } } - // FIXME add default and min props for this - if (pData->minWidth == 0 && pData->minHeight == 0) - puglSetDefaultSize(pData->view, static_cast(width), static_cast(height)); - puglSetWindowSize(pData->view, width, height); } diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index a6386683..7c7453a4 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -145,10 +145,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, modal() { if (isEmbed) - { - // puglSetDefaultSize(DEFAULT_WIDTH, DEFAULT_HEIGHT, height); puglSetParentWindow(view, parentWindowHandle); - } initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); } @@ -177,10 +174,7 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, modal() { if (isEmbed) - { - puglSetDefaultSize(view, static_cast(width), static_cast(height)); puglSetParentWindow(view, parentWindowHandle); - } initPre(width, height, resizable); } @@ -226,6 +220,9 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo puglSetMatchingBackendForCurrentBuild(view); + puglClearMinSize(view); + puglSetWindowSize(view, width, height); + puglSetHandle(view, this); puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); @@ -233,11 +230,6 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo puglSetViewHint(view, PUGL_STENCIL_BITS, 8); // PUGL_SAMPLES ?? puglSetEventFunc(view, puglEventCallback); - - PuglRect rect = puglGetFrame(view); - rect.width = width; - rect.height = height; - puglSetFrame(view, rect); } void Window::PrivateData::initPost() @@ -303,7 +295,6 @@ void Window::PrivateData::show() // FIXME PuglRect rect = puglGetFrame(view); - puglSetDefaultSize(view, static_cast(rect.width), static_cast(rect.height)); puglSetWindowSize(view, static_cast(rect.width), static_cast(rect.height)); #ifdef DISTRHO_OS_WINDOWS @@ -562,10 +553,6 @@ void Window::PrivateData::startModal() // make parent give focus to us modal.parent->modal.child = this; - // FIXME? - PuglRect rect = puglGetFrame(view); - puglSetDefaultSize(view, static_cast(rect.width), static_cast(rect.height)); - // make sure both parent and ourselves are visible modal.parent->show(); show(); @@ -659,8 +646,8 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh /* 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. */ @@ -674,7 +661,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh void Window::PrivateData::onPuglExpose() { - DGL_DBGp("PUGL: onPuglExpose : %p\n", topLevelWidget); + DGL_DBGp("PUGL: onPuglExpose\n"); puglOnDisplayPrepare(view); diff --git a/dgl/src/pugl.cpp b/dgl/src/pugl.cpp index 5a013bda..7d45cd05 100644 --- a/dgl/src/pugl.cpp +++ b/dgl/src/pugl.cpp @@ -152,11 +152,20 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- // expose backend enter -void puglBackendEnter(PuglView* view) +void puglBackendEnter(PuglView* const view) { 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 @@ -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) { + view->defaultWidth = width; + view->defaultHeight = height; + #if defined(DISTRHO_OS_HAIKU) || defined(DISTRHO_OS_MAC) // keep upstream behaviour 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 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; + +#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(width); + sizeHints.height = static_cast(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 diff --git a/dgl/src/pugl.hpp b/dgl/src/pugl.hpp index f89e2847..6c98d54c 100644 --- a/dgl/src/pugl.hpp +++ b/dgl/src/pugl.hpp @@ -46,6 +46,10 @@ PUGL_BEGIN_DECLS PUGL_API void puglBackendEnter(PuglView* view); +// clear minimum size to 0 +PUGL_API void +puglClearMinSize(PuglView* view); + // missing in pugl, directly returns title char* pointer PUGL_API const char* puglGetWindowTitle(const PuglView* view); @@ -62,7 +66,7 @@ puglSetMatchingBackendForCurrentBuild(PuglView* view); PUGL_API PuglStatus 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 puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index edc1021d..75fc35d0 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -668,7 +668,7 @@ public: } else { - UIExporter tmpUI(nullptr, 0,fPlugin.getSampleRate(), + UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, fPlugin.getInstancePointer(), fLastScaleFactor); fVstRect.right = tmpUI.getWidth(); diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index dbfd0f80..c34299e6 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -301,16 +301,7 @@ public: #else void setWindowTitle(const char* const uiTitle) { -// glWindow.setTitle(uiTitle); - } - - void setWindowSize(const uint width, const uint height) - { - DISTRHO_SAFE_ASSERT_RETURN(! changingSizeRecursionCheck,); - - changingSizeRecursionCheck = true; -// glWindow.setSize(width, height); - changingSizeRecursionCheck = false; + uiData->window->setTitle(uiTitle); } void setWindowTransientWinId(const uintptr_t /*winId*/) @@ -322,10 +313,9 @@ public: bool setWindowVisible(const bool yesNo) { -// glWindow.setVisible(yesNo); + uiData->window->setVisible(yesNo); -// return ! glApp.isQuiting(); - return true; + return ! uiData->app.isQuiting(); } bool handlePluginKeyboard(const bool /*press*/, const uint /*key*/, const uint16_t /*mods*/) From 294c2d266536c0408f30895bd3f5b0e22cba0024 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 29 May 2021 00:15:51 +0100 Subject: [PATCH 12/13] Stop repainting Info example if parameter has not really changed Signed-off-by: falkTX --- examples/Info/InfoExampleUI.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/Info/InfoExampleUI.cpp b/examples/Info/InfoExampleUI.cpp index e8afc2a5..a1638484 100644 --- a/examples/Info/InfoExampleUI.cpp +++ b/examples/Info/InfoExampleUI.cpp @@ -61,6 +61,11 @@ protected: */ 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; repaint(); } @@ -128,7 +133,7 @@ protected: y+=lineHeight; // resizable - drawLeft(x, y, "UI resizable:", 20); + drawLeft(x, y, "Host resizable:", 20); drawRight(x, y, fResizable ? "Yes" : "No", 40); y+=lineHeight; From f2a117c4b52df01f543ec1654c6bdbca0344eed1 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 29 May 2021 00:20:44 +0100 Subject: [PATCH 13/13] Report parameter request changes as supported for JACK standalone Signed-off-by: falkTX --- distrho/src/DistrhoPluginJack.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index e58eadef..3b0e9751 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -788,6 +788,7 @@ int main() d_lastBufferSize = jack_get_buffer_size(client); d_lastSampleRate = jack_get_sample_rate(client); + d_lastCanRequestParameterValueChanges = true; const PluginJack p(client);