From 2d6cbf8a0bb81bfe55a7d4e04d53a704f93c6b2e Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 9 Oct 2019 18:02:19 +0200 Subject: Make FileBrowser window always-on-top and try to position it inside the plugin gui window rectangle. --- plugingui/drumkitframecontent.cc | 14 +++++++ plugingui/nativewindow.h | 11 +++++- plugingui/nativewindow_cocoa.h | 2 + plugingui/nativewindow_cocoa.mm | 39 ++++++++++++++++++- plugingui/nativewindow_win32.cc | 23 +++++++++-- plugingui/nativewindow_win32.h | 3 ++ plugingui/nativewindow_x11.cc | 84 +++++++++++++++++++++++++++++++++++++++- plugingui/nativewindow_x11.h | 2 + plugingui/widget.cc | 5 +++ plugingui/widget.h | 14 +++++++ plugingui/window.cc | 16 ++++++++ plugingui/window.h | 6 ++- 12 files changed, 210 insertions(+), 9 deletions(-) diff --git a/plugingui/drumkitframecontent.cc b/plugingui/drumkitframecontent.cc index 1da7201..0850ac1 100644 --- a/plugingui/drumkitframecontent.cc +++ b/plugingui/drumkitframecontent.cc @@ -168,6 +168,13 @@ void DrumkitframeContent::kitBrowseClick() CONNECT(&file_browser, fileSelectNotifier, this, &DrumkitframeContent::selectKitFile); file_browser.show(); + Point p{ window()->x() + (int)window()->width() / 2, + window()->y() + (int)window()->height() / 2 }; + auto p0 = window()->translateToScreen(p); + auto sz = file_browser.window()->getNativeSize(); + file_browser.move(p0.x - sz.width / 2, + p0.y - sz.height / 2); + file_browser.setAlwaysOnTop(true); } void DrumkitframeContent::midimapBrowseClick() @@ -189,6 +196,13 @@ void DrumkitframeContent::midimapBrowseClick() CONNECT(&file_browser, fileSelectNotifier, this, &DrumkitframeContent::selectMapFile); file_browser.show(); + Point p{ window()->x() + (int)window()->width() / 2, + window()->y() + (int)window()->height() / 2 }; + auto p0 = window()->translateToScreen(p); + auto sz = file_browser.window()->getNativeSize(); + file_browser.move(p0.x - sz.width / 2, + p0.y - sz.height / 2); + file_browser.setAlwaysOnTop(true); } void DrumkitframeContent::defaultPathChanged(const std::string& path) diff --git a/plugingui/nativewindow.h b/plugingui/nativewindow.h index 3995d43..400ff57 100644 --- a/plugingui/nativewindow.h +++ b/plugingui/nativewindow.h @@ -37,6 +37,8 @@ namespace GUI { +struct Point; + //! Interface class for native window implementations. class NativeWindow { @@ -48,6 +50,9 @@ public: //! It resizes the window and disallows user resizing. virtual void setFixedSize(std::size_t width, std::size_t height) = 0; + //! Force window to stay on top of other windows + virtual void setAlwaysOnTop(bool always_on_top) = 0; + //! Set a new size of the window. virtual void resize(std::size_t width, std::size_t height) = 0; @@ -84,8 +89,12 @@ public: //! \return A queue of shared pointers to events. virtual EventQueue getEvents() = 0; - // \returns the native window handle, it HWND on Win32 or Window id on X11 + //! \returns the native window handle, it HWND on Win32 or Window id on X11 virtual void* getNativeWindowHandle() const = 0; + + //! Translate a the local native window coordinate to a global screen + //! coordinate. + virtual Point translateToScreen(const Point& point) = 0; }; } // GUI:: diff --git a/plugingui/nativewindow_cocoa.h b/plugingui/nativewindow_cocoa.h index 3f0518a..74eb2f4 100644 --- a/plugingui/nativewindow_cocoa.h +++ b/plugingui/nativewindow_cocoa.h @@ -43,6 +43,7 @@ public: // From NativeWindow: virtual void setFixedSize(std::size_t width, std::size_t height) override; + virtual void setAlwaysOnTop(bool always_on_top) override; virtual void resize(std::size_t width, std::size_t height) override; virtual std::pair getSize() const override; virtual void move(int x, int y) override; @@ -55,6 +56,7 @@ public: virtual void grabMouse(bool grab) override; virtual EventQueue getEvents() override; virtual void* getNativeWindowHandle() const override; + virtual Point translateToScreen(const Point& point) override; // Expose friend members of Window to ObjC++ implementation. class Window& getWindow(); diff --git a/plugingui/nativewindow_cocoa.mm b/plugingui/nativewindow_cocoa.mm index aaacd0e..d8e889a 100644 --- a/plugingui/nativewindow_cocoa.mm +++ b/plugingui/nativewindow_cocoa.mm @@ -576,6 +576,18 @@ void NativeWindowCocoa::setFixedSize(std::size_t width, std::size_t height) [priv->window setMaxSize:NSMakeSize(width, height + 22)]; } +void NativeWindowCocoa::setAlwaysOnTop(bool always_on_top) +{ + if(always_on_top) + { + [priv->window setLevel: NSStatusWindowLevel]; + } + else + { + [priv->window setLevel: NSNormalWindowLevel]; + } +} + void NativeWindowCocoa::resize(std::size_t width, std::size_t height) { [priv->window setContentSize:NSMakeSize(width, height)]; @@ -597,13 +609,15 @@ std::pair NativeWindowCocoa::getSize() const void NativeWindowCocoa::move(int x, int y) { - [priv->window setFrameTopLeftPoint:NSMakePoint(x, y)]; + NSRect screen = [[NSScreen mainScreen] frame]; + [priv->window setFrameTopLeftPoint:NSMakePoint(x, screen.size.height - y)]; } std::pair NativeWindowCocoa::getPosition() const { + NSRect screen = [[NSScreen mainScreen] frame]; NSPoint pos = [[priv->window contentView] frame].origin; - return {pos.x, pos.y}; + return {pos.x, screen.size.height - pos.y}; } void NativeWindowCocoa::show() @@ -757,6 +771,27 @@ void* NativeWindowCocoa::getNativeWindowHandle() const } } +Point NativeWindowCocoa::translateToScreen(const Point& point) +{ + NSRect e = [[NSScreen mainScreen] frame]; + NSRect frame; + if(native_window) + { + frame = [priv->parent_view frame]; + } + else + { + frame = [priv->view frame]; + } + + NSRect rect { { point.x + frame.origin.x, + frame.size.height - point.y + frame.origin.y}, + {0.0, 0.0} }; + rect = [priv->window convertRectToScreen:rect]; + + return { (int)rect.origin.x, (int)(e.size.height - rect.origin.y) }; +} + Window& NativeWindowCocoa::getWindow() { return window; diff --git a/plugingui/nativewindow_win32.cc b/plugingui/nativewindow_win32.cc index 55f7c69..4b31130 100644 --- a/plugingui/nativewindow_win32.cc +++ b/plugingui/nativewindow_win32.cc @@ -452,6 +452,13 @@ void NativeWindowWin32::setFixedSize(std::size_t width, std::size_t height) SetWindowLong(m_hwnd, GWL_STYLE, style); } +void NativeWindowWin32::setAlwaysOnTop(bool always_on_top) +{ + this->always_on_top = always_on_top; + SetWindowPos(m_hwnd, always_on_top ? HWND_TOPMOST : nullptr, + 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); +} + void NativeWindowWin32::resize(std::size_t width, std::size_t height) { auto hwnd = m_hwnd; @@ -461,7 +468,8 @@ void NativeWindowWin32::resize(std::size_t width, std::size_t height) //} // Set requested size on the window (or parent) - SetWindowPos(hwnd, nullptr, -1, -1, (int)width, (int)height, SWP_NOMOVE); + SetWindowPos(hwnd, always_on_top ? HWND_TOPMOST : nullptr, + -1, -1, (int)width, (int)height, SWP_NOMOVE); // Ask the client window what size it actually got RECT rect; @@ -470,7 +478,8 @@ void NativeWindowWin32::resize(std::size_t width, std::size_t height) int h = height - rect.bottom; // Set the compensated size on the window (or parent) - SetWindowPos(hwnd, nullptr, -1, -1, width + w, height + h, SWP_NOMOVE); + SetWindowPos(hwnd, always_on_top ? HWND_TOPMOST : nullptr, + -1, -1, width + w, height + h, SWP_NOMOVE); } std::pair NativeWindowWin32::getSize() const @@ -482,7 +491,8 @@ std::pair NativeWindowWin32::getSize() const void NativeWindowWin32::move(int x, int y) { - SetWindowPos(m_hwnd, nullptr, (int)x, (int)y, -1, -1, SWP_NOSIZE); + SetWindowPos(m_hwnd, always_on_top ? HWND_TOPMOST : nullptr, + (int)x, (int)y, -1, -1, SWP_NOSIZE); } std::pair NativeWindowWin32::getPosition() const @@ -564,4 +574,11 @@ void* NativeWindowWin32::getNativeWindowHandle() const return (void*)m_hwnd; } +Point NativeWindowWin32::translateToScreen(const Point& point) +{ + POINT p{ point.x, point.y }; + ClientToScreen(m_hwnd, &p); + return { p.x, p.y }; +} + } // GUI:: diff --git a/plugingui/nativewindow_win32.h b/plugingui/nativewindow_win32.h index d547dc0..046b38a 100644 --- a/plugingui/nativewindow_win32.h +++ b/plugingui/nativewindow_win32.h @@ -45,6 +45,7 @@ public: ~NativeWindowWin32(); void setFixedSize(std::size_t width, std::size_t height) override; + void setAlwaysOnTop(bool always_on_top) override; void resize(std::size_t width, std::size_t height) override; std::pair getSize() const override; void move(int x, int y) override; @@ -57,6 +58,7 @@ public: void grabMouse(bool grab) override; EventQueue getEvents() override; void* getNativeWindowHandle() const override; + Point translateToScreen(const Point& point) override; private: static LRESULT CALLBACK dialogProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); @@ -70,6 +72,7 @@ private: std::pair last_mouse_position{0, 0}; char* m_className = nullptr; EventQueue event_queue; + bool always_on_top{false}; }; } // GUI:: diff --git a/plugingui/nativewindow_x11.cc b/plugingui/nativewindow_x11.cc index e96f26a..de215c2 100644 --- a/plugingui/nativewindow_x11.cc +++ b/plugingui/nativewindow_x11.cc @@ -44,6 +44,72 @@ namespace GUI { +#define _NET_WM_STATE_REMOVE 0 // remove/unset property +#define _NET_WM_STATE_ADD 1 // add/set property + +void setWindowFront(Display *disp, ::Window wind, bool enable) +{ + Atom wm_state, wm_state_above; + XEvent event; + + if((wm_state = XInternAtom(disp, "_NET_WM_STATE", False)) == None) + { + return; + } + + if((wm_state_above = XInternAtom(disp, "_NET_WM_STATE_ABOVE", False)) == None) + { + return; + } + // + //window = the respective client window + //message_type = _NET_WM_STATE + //format = 32 + //data.l[0] = the action, as listed below + //data.l[1] = first property to alter + //data.l[2] = second property to alter + //data.l[3] = source indication (0-unk,1-normal app,2-pager) + //other data.l[] elements = 0 + // + + // sending a ClientMessage + event.xclient.type = ClientMessage; + + // value unimportant in this case + event.xclient.serial = 0; + + // coming from a SendEvent request, so True + event.xclient.send_event = True; + + // the event originates from disp + event.xclient.display = disp; + + // the window whose state will be modified + event.xclient.window = wind; + + // the component Atom being modified in the window + event.xclient.message_type = wm_state; + + // specifies that data.l will be used + event.xclient.format = 32; + + // 0 is _NET_WM_STATE_REMOVE, 1 is _NET_WM_STATE_ADD + event.xclient.data.l[0] = + enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + + // the atom being added + event.xclient.data.l[1] = wm_state_above; + + // unused + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + // actually send the event + XSendEvent(disp, DefaultRootWindow(disp), False, + SubstructureRedirectMask | SubstructureNotifyMask, &event); +} + NativeWindowX11::NativeWindowX11(void* native_window, Window& window) : window(window) { @@ -101,8 +167,8 @@ NativeWindowX11::NativeWindowX11(void* native_window, Window& window) wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false); Atom protocols[] = { wmDeleteMessage }; - int count = sizeof(protocols)/sizeof(Atom); - XSetWMProtocols(display, xwindow, protocols, count); + XSetWMProtocols(display, xwindow, protocols, + sizeof(protocols) / sizeof(*protocols)); // Create a "Graphics Context" gc = XCreateGC(display, xwindow, 0, nullptr); @@ -142,6 +208,11 @@ void NativeWindowX11::setFixedSize(std::size_t width, std::size_t height) XSetNormalHints(display, xwindow, &size_hints); } +void NativeWindowX11::setAlwaysOnTop(bool always_on_top) +{ + setWindowFront(display, xwindow, always_on_top); +} + void NativeWindowX11::resize(std::size_t width, std::size_t height) { if(display == nullptr) @@ -287,6 +358,15 @@ void* NativeWindowX11::getNativeWindowHandle() const return (void*)xwindow; } +Point NativeWindowX11::translateToScreen(const Point& point) +{ + ::Window child_window; + Point p; + XTranslateCoordinates(display, xwindow, DefaultRootWindow(display), + point.x, point.y, &p.x, &p.y, &child_window); + return p; +} + void NativeWindowX11::translateXMessage(XEvent& xevent) { switch(xevent.type) diff --git a/plugingui/nativewindow_x11.h b/plugingui/nativewindow_x11.h index eb88b6c..cb56fbc 100644 --- a/plugingui/nativewindow_x11.h +++ b/plugingui/nativewindow_x11.h @@ -47,6 +47,7 @@ public: // From NativeWindow: void setFixedSize(std::size_t width, std::size_t height) override; + void setAlwaysOnTop(bool always_on_top) override; void resize(std::size_t width, std::size_t height) override; std::pair getSize() const override; void move(int x, int y) override; @@ -59,6 +60,7 @@ public: void grabMouse(bool grab) override; EventQueue getEvents() override; void* getNativeWindowHandle() const override; + Point translateToScreen(const Point& point) override; private: void translateXMessage(XEvent& xevent); diff --git a/plugingui/widget.cc b/plugingui/widget.cc index c148219..5a76af9 100644 --- a/plugingui/widget.cc +++ b/plugingui/widget.cc @@ -196,6 +196,11 @@ std::size_t Widget::height() const return _height; } +Point Widget::position() const +{ + return { _x, _y }; +} + PixelBufferAlpha& Widget::GetPixelBuffer() { return pixbuf; diff --git a/plugingui/widget.h b/plugingui/widget.h index 8595ef7..fbf3f5b 100644 --- a/plugingui/widget.h +++ b/plugingui/widget.h @@ -37,6 +37,18 @@ namespace GUI { +struct Point +{ + int x; + int y; +}; + +struct Size +{ + std::size_t width; + std::size_t height; +}; + class ImageCache; class Window; @@ -66,6 +78,8 @@ public: virtual std::size_t width() const override; virtual std::size_t height() const override; + Point position() const; + // From Canvas PixelBufferAlpha& GetPixelBuffer() override; diff --git a/plugingui/window.cc b/plugingui/window.cc index 60ecf02..a9deed5 100644 --- a/plugingui/window.cc +++ b/plugingui/window.cc @@ -86,6 +86,11 @@ void Window::setFixedSize(int w, int h) native->setFixedSize(w, h); } +void Window::setAlwaysOnTop(bool always_on_top) +{ + native->setAlwaysOnTop(always_on_top); +} + void Window::setCaption(const std::string& caption) { native->setCaption(caption); @@ -125,6 +130,12 @@ Window* Window::window() return this; } +Size Window::getNativeSize() +{ + auto sz = native->getSize(); + return {sz.first, sz.second}; +} + ImageCache& Window::getImageCache() { return image_cache; @@ -188,6 +199,11 @@ void* Window::getNativeWindowHandle() const return native->getNativeWindowHandle(); } +Point Window::translateToScreen(const Point& point) +{ + return native->translateToScreen(point); +} + std::size_t Window::translateToWindowX() { return 0; diff --git a/plugingui/window.h b/plugingui/window.h index e5bc496..6031500 100644 --- a/plugingui/window.h +++ b/plugingui/window.h @@ -45,6 +45,7 @@ public: ~Window(); void setFixedSize(int width, int height); + void setAlwaysOnTop(bool always_on_top); void setCaption(const std::string& caption); // From Widget: @@ -53,7 +54,7 @@ public: void show() override; void hide() override; Window* window() override; - + Size getNativeSize(); ImageCache& getImageCache() override; EventHandler* eventHandler(); @@ -73,6 +74,9 @@ public: // \returns the native window handle, it HWND on Win32 or Window id on X11 void* getNativeWindowHandle() const; + //! Translate a local window coordinate to a global screen coordinate. + Point translateToScreen(const Point& point); + protected: // For the EventHandler friend class EventHandler; -- cgit v1.2.3