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