From 635e5cede4756bd22fe75dab76758e47778072f2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 16 May 2021 14:03:27 +0100 Subject: [PATCH] Add back modal windows related functionality Signed-off-by: falkTX --- dgl/Window.hpp | 19 +++- dgl/src/Window.cpp | 23 ++--- dgl/src/WindowPrivateData.cpp | 163 +++++++++++++++++++++++++++------- dgl/src/WindowPrivateData.hpp | 138 ++++++++-------------------- 4 files changed, 190 insertions(+), 153 deletions(-) diff --git a/dgl/Window.hpp b/dgl/Window.hpp index 9142712a..8351b019 100644 --- a/dgl/Window.hpp +++ b/dgl/Window.hpp @@ -55,6 +55,12 @@ public: */ explicit Window(Application& app); + /** + Constructor for a modal window, by having another window as its parent. + The Application instance must be the same between the 2 windows. + */ + explicit Window(Application& app, Window& parent); + /** Constructor for an embed Window without known size, typically used in modules or plugins that run inside another host. @@ -224,6 +230,13 @@ public: */ void repaint(const Rectangle& rect) noexcept; + /** + Run this window as a modal, blocking input events from the parent. + Only valid for windows that have been created with another window as parent (as passed in the constructor). + Can optionally block-wait, but such option is only available if the application is running as standalone. + */ + void runAsModal(bool blockWait = false); + /** Set geometry constraints for the Window when resized by the user, and optionally scale contents automatically. */ @@ -239,6 +252,7 @@ public: // TODO deprecated inline bool getIgnoringKeyRepeat() const noexcept { return isIgnoringKeyRepeat(); } inline double getScaling() const noexcept { return getScaling(); } + inline void exec(bool blockWait = false) { runAsModal(blockWait); } protected: /** @@ -257,6 +271,7 @@ protected: /** A function called when the window is resized. If there is a top-level widget associated with this window, its size will be set right after this function. + TODO this seems wrong, top-level widget should be resized here */ virtual void onReshape(uint width, uint height); @@ -318,10 +333,6 @@ END_NAMESPACE_DGL }; #endif // DGL_FILE_BROWSER_DISABLED - static Window& withTransientParentWindow(Window& transientParentWindow); - - void exec(bool lockWait = false); - void addIdleCallback(IdleCallback* const callback); void removeIdleCallback(IdleCallback* const callback); diff --git a/dgl/src/Window.cpp b/dgl/src/Window.cpp index fe3f78cd..b68302ef 100644 --- a/dgl/src/Window.cpp +++ b/dgl/src/Window.cpp @@ -23,12 +23,12 @@ START_NAMESPACE_DGL // ----------------------------------------------------------------------- // Window -// Window::Window(Window& transientParentWindow) -// : pData(new PrivateData(transientParentWindow.pData->fAppData, this, transientParentWindow)) {} - Window::Window(Application& app) : pData(new PrivateData(app, this)) {} +Window::Window(Application& app, Window& parent) + : pData(new PrivateData(app, this, parent.pData)) {} + Window::Window(Application& app, const uintptr_t parentWindowHandle, const double scaleFactor, @@ -170,10 +170,7 @@ double Window::getScaleFactor() const noexcept void Window::focus() { - if (! pData->isEmbed) - puglRaiseWindow(pData->view); - - puglGrabFocus(pData->view); + pData->focus(); } void Window::repaint() noexcept @@ -192,6 +189,11 @@ void Window::repaint(const Rectangle& rect) noexcept puglPostRedisplayRect(pData->view, prect); } +void Window::runAsModal(bool blockWait) +{ + pData->runAsModal(blockWait); +} + void Window::setGeometryConstraints(const uint minimumWidth, const uint minimumHeight, const bool keepAspectRatio, @@ -242,13 +244,6 @@ void Window::onReshape(uint, uint) } #if 0 -#if 0 -void Window::exec(bool lockWait) -{ - pData->exec(lockWait); -} -#endif - void Window::setTransientWinId(const uintptr_t winId) { puglSetTransientFor(pData->fView, winId); diff --git a/dgl/src/WindowPrivateData.cpp b/dgl/src/WindowPrivateData.cpp index f3ca1922..90aa11b7 100644 --- a/dgl/src/WindowPrivateData.cpp +++ b/dgl/src/WindowPrivateData.cpp @@ -63,12 +63,13 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); } -Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transientWindow) +Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData) : app(a), appData(a.pData), self(s), @@ -77,15 +78,16 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, Window& transi isClosed(true), isVisible(false), isEmbed(false), - scaleFactor(getDesktopScaleFactor()), + scaleFactor(ppData->scaleFactor), autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this, ppData) { init(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); - puglSetTransientFor(view, transientWindow.getNativeWindowHandle()); + puglSetTransientFor(view, puglGetNativeWindow(ppData->view)); } Window::PrivateData::PrivateData(Application& a, Window* const s, @@ -103,7 +105,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { if (isEmbed) { @@ -136,7 +139,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, autoScaling(false), autoScaleFactor(1.0), minWidth(0), - minHeight(0) + minHeight(0), + modal(this) { if (isEmbed) { @@ -279,16 +283,26 @@ void Window::PrivateData::hide() // } #endif - puglHide(view); + if (modal.enabled) + stopModal(); -// if (fModal.enabled) -// exec_fini(); + puglHide(view); isVisible = false; } // ----------------------------------------------------------------------- +void Window::PrivateData::focus() +{ + if (! isEmbed) + puglRaiseWindow(view); + + puglGrabFocus(view); +} + +// ----------------------------------------------------------------------- + void Window::PrivateData::close() { DGL_DBG("Window close\n"); @@ -327,13 +341,91 @@ void Window::PrivateData::idleCallback() // std::free(buffer); // } // #endif -// -// if (fModal.enabled && fModal.parent != nullptr) -// fModal.parent->windowSpecificIdle(); -// self->repaint(); + +// if (modal.enabled && modal.parent != nullptr) +// modal.parent->idleCallback(); } // ----------------------------------------------------------------------- +// modal handling + +void Window::PrivateData::startModal() +{ + DGL_DBG("Window modal loop starting..."); DGL_DBGF; + DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show()); + + // activate modal mode for this window + modal.enabled = true; + + // make parent give focus to us + modal.parent->modal.child = this; + + // make sure both parent and ourselves are visible + modal.parent->show(); + show(); + + DGL_DBG("Ok\n"); +} + +void Window::PrivateData::stopModal() +{ + DGL_DBG("Window modal loop stopping..."); DGL_DBGF; + + // deactivate modal mode + modal.enabled = false; + + // safety checks, make sure we have a parent and we are currently active as the child to give focus to + if (modal.parent == nullptr) + return; + if (modal.parent->modal.child != this) + return; + + // stop parent from giving focus to us, so it behaves like normal + modal.parent->modal.child = nullptr; + + // the mouse position probably changed since the modal appeared, + // so send a mouse motion event to the modal's parent window +#if 0 +#if defined(DISTRHO_OS_HAIKU) + // TODO +#elif defined(DISTRHO_OS_MAC) + // TODO +#elif defined(DISTRHO_OS_WINDOWS) + // TODO +#else + int i, wx, wy; + uint u; + ::Window w; + if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True) + fModal.parent->onPuglMotion(wx, wy); +#endif +#endif + + DGL_DBG("Ok\n"); +} + +void Window::PrivateData::runAsModal(const bool blockWait) +{ + DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait); + startModal(); + + if (blockWait) + { + DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,); + + while (isVisible && modal.enabled) + appData->idle(10); + + stopModal(); + } + else + { + appData->idle(0); + } +} + +// ----------------------------------------------------------------------- +// pugl events void Window::PrivateData::onPuglConfigure(const int width, const int height) { @@ -372,14 +464,19 @@ void Window::PrivateData::onPuglClose() { DGL_DBG("PUGL: onClose\n"); -// if (fModal.enabled) -// exec_fini(); - if (! self->onClose()) return; -// if (fModal.childFocus != nullptr) -// fModal.childFocus->fSelf->onClose(); + if (modal.enabled) + stopModal(); + + if (modal.child != nullptr) + { + if (modal.child->modal.enabled) + modal.child->stopModal(); + + modal.child->close(); + } close(); } @@ -388,8 +485,8 @@ void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) { DGL_DBGp("onPuglFocus : %i %i\n", focus, mode); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP self->onFocus(focus, mode); @@ -400,8 +497,8 @@ void Window::PrivateData::onPuglKey(const Events::KeyboardEvent& ev) { DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -413,8 +510,8 @@ void Window::PrivateData::onPuglSpecial(const Events::SpecialEvent& ev) { DGL_DBGp("onPuglSpecial : %i %u\n", ev.press, ev.key); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -426,8 +523,8 @@ void Window::PrivateData::onPuglText(const Events::CharacterInputEvent& ev) { DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -439,8 +536,8 @@ void Window::PrivateData::onPuglMouse(const Events::MouseEvent& ev) { DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -452,8 +549,8 @@ void Window::PrivateData::onPuglMotion(const Events::MotionEvent& ev) { DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) @@ -465,8 +562,8 @@ void Window::PrivateData::onPuglScroll(const Events::ScrollEvent& ev) { DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); -// if (fModal.childFocus != nullptr) -// return fModal.childFocus->focus(); + if (modal.child != nullptr) + return modal.child->focus(); #ifndef DPF_TEST_WINDOW_CPP if (topLevelWidget != nullptr) diff --git a/dgl/src/WindowPrivateData.hpp b/dgl/src/WindowPrivateData.hpp index f128f3d5..859992c9 100644 --- a/dgl/src/WindowPrivateData.hpp +++ b/dgl/src/WindowPrivateData.hpp @@ -68,11 +68,40 @@ struct Window::PrivateData : IdleCallback { /** Pugl minWidth, minHeight access. */ uint minWidth, minHeight; + /** Modal window setup. */ + struct Modal { +// PrivateData* self; // pointer to PrivateData this Modal class belongs to + PrivateData* parent; // parent of this window (so we can become modal) + PrivateData* child; // child window to give focus to when modal mode is enabled + bool enabled; // wherever modal mode is enabled (only possible if parent != null) + + /** Constructor for a non-modal window. */ + Modal(PrivateData* const s) noexcept + : parent(nullptr), + child(nullptr), + enabled(false) {} + + /** Constructor for a modal window (with a parent). */ + Modal(PrivateData* const s, PrivateData* const p) noexcept + : parent(p), + child(nullptr), + enabled(false) {} + + /** Destructor. */ + ~Modal() noexcept + { + DISTRHO_SAFE_ASSERT(! enabled); + } + } modal; + /** Constructor for a regular, standalone window. */ explicit PrivateData(Application& app, Window* self); + /** Constructor for a modal window. */ + explicit PrivateData(Application& app, Window* self, PrivateData* ppData); + /** Constructor for a regular, standalone window with a transient parent. */ - explicit PrivateData(Application& app, Window* self, Window& transientWindow); +// explicit PrivateData(Application& app, Window* self, Window& transientWindow); /** Constructor for an embed Window, with a few extra hints from the host side. */ explicit PrivateData(Application& app, Window* self, uintptr_t parentWindowHandle, double scaling, bool resizable); @@ -89,6 +118,7 @@ struct Window::PrivateData : IdleCallback { void show(); void hide(); + void focus(); /** Hide window and notify application of a window close event. * Does nothing if window is embed (that is, not standalone). @@ -105,6 +135,11 @@ struct Window::PrivateData : IdleCallback { void idleCallback() override; + // modal handling + void startModal(); + void stopModal(); + void runAsModal(bool blockWait); + // pugl events void onPuglConfigure(int width, int height); void onPuglExpose(); @@ -128,39 +163,6 @@ struct Window::PrivateData : IdleCallback { END_NAMESPACE_DGL #if 0 - // this one depends on build type - // GraphicsContext fContext; - - bool fFirstInit; - bool fVisible; - bool fUsingEmbed; - double fScaling; - double fAutoScaling; - - struct Modal { - bool enabled; - PrivateData* parent; - PrivateData* childFocus; - - Modal() - : enabled(false), - parent(nullptr), - childFocus(nullptr) {} - - Modal(PrivateData* const p) - : enabled(false), - parent(p), - childFocus(nullptr) {} - - ~Modal() - { - DISTRHO_SAFE_ASSERT(! enabled); - DISTRHO_SAFE_ASSERT(childFocus == nullptr); - } - - DISTRHO_DECLARE_NON_COPY_STRUCT(Modal) - } fModal; - // #if defined(DISTRHO_OS_HAIKU) // BApplication* bApplication; // BView* bView; @@ -189,74 +191,6 @@ END_NAMESPACE_DGL struct Window::PrivateData { // ------------------------------------------------------------------- - void exec(const bool lockWait) - { - DBG("Window exec\n"); - exec_init(); - - if (lockWait) - { - for (; fVisible && fModal.enabled;) - { - idle(); - d_msleep(10); - } - - exec_fini(); - } - else - { - idle(); - } - } - - // ------------------------------------------------------------------- - - void exec_init() - { - DBG("Window modal loop starting..."); DBGF; - DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true)); - - fModal.enabled = true; - fModal.parent->fModal.childFocus = this; - - fModal.parent->setVisible(true); - setVisible(true); - - DBG("Ok\n"); - } - - void exec_fini() - { - DBG("Window modal loop stopping..."); DBGF; - fModal.enabled = false; - - if (fModal.parent != nullptr) - { - fModal.parent->fModal.childFocus = nullptr; - - // the mouse position probably changed since the modal appeared, - // so send a mouse motion event to the modal's parent window -#if defined(DISTRHO_OS_HAIKU) - // TODO -#elif defined(DISTRHO_OS_MAC) - // TODO -#elif defined(DISTRHO_OS_WINDOWS) - // TODO -#else - int i, wx, wy; - uint u; - ::Window w; - if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True) - fModal.parent->onPuglMotion(wx, wy); -#endif - } - - DBG("Ok\n"); - } - - // ------------------------------------------------------------------- - bool handlePluginSpecial(const bool press, const Key key) { DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);