Signed-off-by: falkTX <falktx@falktx.com>pull/272/head
| @@ -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<uint>& 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); | |||
| @@ -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<uint>& 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); | |||
| @@ -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) | |||
| @@ -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); | |||