diff --git a/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp b/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp index 36fcbf343de0..b835c270e361 100644 --- a/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp +++ b/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.cpp @@ -125,6 +125,10 @@ void ResizableBorderComponent::mouseDown (const MouseEvent& e) originalBounds = component->getBounds(); + if (auto* peer = component->getPeer()) + if (&peer->getComponent() == component) + peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), mouseZone); + if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h b/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h index bdab8b985e82..fc3c989c0ec2 100644 --- a/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h +++ b/modules/juce_gui_basics/layout/juce_ResizableBorderComponent.h @@ -125,7 +125,7 @@ class JUCE_API ResizableBorderComponent : public Component /** Returns an appropriate mouse-cursor for this resize zone. */ MouseCursor getMouseCursor() const noexcept; - /** Returns true if dragging this zone will move the enire object without resizing it. */ + /** Returns true if dragging this zone will move the entire object without resizing it. */ bool isDraggingWholeObject() const noexcept { return zone == centre; } /** Returns true if dragging this zone will move the object's left edge. */ bool isDraggingLeftEdge() const noexcept { return (zone & left) != 0; } diff --git a/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp b/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp index 83535a078336..8bcd024d3d15 100644 --- a/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp +++ b/modules/juce_gui_basics/layout/juce_ResizableCornerComponent.cpp @@ -45,7 +45,7 @@ void ResizableCornerComponent::paint (Graphics& g) isMouseButtonDown()); } -void ResizableCornerComponent::mouseDown (const MouseEvent&) +void ResizableCornerComponent::mouseDown (const MouseEvent& e) { if (component == nullptr) { @@ -55,6 +55,13 @@ void ResizableCornerComponent::mouseDown (const MouseEvent&) originalBounds = component->getBounds(); + using Zone = ResizableBorderComponent::Zone; + const Zone zone { Zone::bottom | Zone::right }; + + if (auto* peer = component->getPeer()) + if (&peer->getComponent() == component) + peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone); + if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp b/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp index 8629e5fc1fb2..85f164048078 100644 --- a/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp +++ b/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.cpp @@ -52,7 +52,7 @@ void ResizableEdgeComponent::paint (Graphics& g) isMouseOver(), isMouseButtonDown()); } -void ResizableEdgeComponent::mouseDown (const MouseEvent&) +void ResizableEdgeComponent::mouseDown (const MouseEvent& e) { if (component == nullptr) { @@ -62,6 +62,25 @@ void ResizableEdgeComponent::mouseDown (const MouseEvent&) originalBounds = component->getBounds(); + using Zone = ResizableBorderComponent::Zone; + + const Zone zone { [&] + { + switch (edge) + { + case Edge::leftEdge: return Zone::left; + case Edge::rightEdge: return Zone::right; + case Edge::topEdge: return Zone::top; + case Edge::bottomEdge: return Zone::bottom; + } + + return Zone::centre; + }() }; + + if (auto* peer = component->getPeer()) + if (&peer->getComponent() == component) + peer->startHostManagedResize (peer->globalToLocal (localPointToGlobal (e.getPosition())), zone); + if (constrainer != nullptr) constrainer->resizeStart(); } diff --git a/modules/juce_gui_basics/native/juce_Windowing_linux.cpp b/modules/juce_gui_basics/native/juce_Windowing_linux.cpp index 06880521a157..8e6f707dfe9c 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_linux.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_linux.cpp @@ -399,6 +399,12 @@ class LinuxComponentPeer : public ComponentPeer, void clearWindowAssociation() { association = {}; } + void startHostManagedResize (Point mouseDownPosition, + ResizableBorderComponent::Zone zone) override + { + XWindowSystem::getInstance()->startHostManagedResize (windowH, mouseDownPosition, zone); + } + //============================================================================== static bool isActiveApplication; bool focused = false; diff --git a/modules/juce_gui_basics/native/juce_XWindowSystem_linux.cpp b/modules/juce_gui_basics/native/juce_XWindowSystem_linux.cpp index d75a6f8f391f..bd81771ea805 100644 --- a/modules/juce_gui_basics/native/juce_XWindowSystem_linux.cpp +++ b/modules/juce_gui_basics/native/juce_XWindowSystem_linux.cpp @@ -1789,6 +1789,68 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle newBounds, bool } } +void XWindowSystem::startHostManagedResize (::Window windowH, + Point mouseDown, + ResizableBorderComponent::Zone zone) +{ + const auto moveResize = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_MOVERESIZE"); + + if (moveResize == None) + return; + + XWindowSystemUtilities::ScopedXLock xLock; + + X11Symbols::getInstance()->xUngrabPointer (display, CurrentTime); + + const auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display)); + + XClientMessageEvent clientMsg; + clientMsg.display = display; + clientMsg.window = windowH; + clientMsg.type = ClientMessage; + clientMsg.format = 32; + clientMsg.message_type = moveResize; + clientMsg.data.l[0] = mouseDown.getX(); + clientMsg.data.l[1] = mouseDown.getY(); + clientMsg.data.l[2] = [&] + { + // It's unclear which header is supposed to contain these + static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOP = 1; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6; + static constexpr auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7; + static constexpr auto _NET_WM_MOVERESIZE_MOVE = 8; + + using F = ResizableBorderComponent::Zone::Zones; + + switch (zone.getZoneFlags()) + { + case F::top | F::left: return _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + case F::top: return _NET_WM_MOVERESIZE_SIZE_TOP; + case F::top | F::right: return _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + case F::right: return _NET_WM_MOVERESIZE_SIZE_RIGHT; + case F::bottom | F::right: return _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + case F::bottom: return _NET_WM_MOVERESIZE_SIZE_BOTTOM; + case F::bottom | F::left: return _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + case F::left: return _NET_WM_MOVERESIZE_SIZE_LEFT; + } + + return _NET_WM_MOVERESIZE_MOVE; + }(); + clientMsg.data.l[3] = 0; + clientMsg.data.l[4] = 1; + + X11Symbols::getInstance()->xSendEvent (display, + root, + false, + SubstructureRedirectMask | SubstructureNotifyMask, + unalignedPointerCast (&clientMsg)); +} + void XWindowSystem::updateConstraints (::Window windowH) const { if (auto* peer = getPeerFor (windowH)) diff --git a/modules/juce_gui_basics/native/juce_XWindowSystem_linux.h b/modules/juce_gui_basics/native/juce_XWindowSystem_linux.h index 4c906a73ee0c..30af0747a6da 100644 --- a/modules/juce_gui_basics/native/juce_XWindowSystem_linux.h +++ b/modules/juce_gui_basics/native/juce_XWindowSystem_linux.h @@ -241,6 +241,10 @@ class XWindowSystem : public DeletedAtShutdown bool isX11Available() const noexcept { return xIsAvailable; } + void startHostManagedResize (::Window window, + Point mouseDown, + ResizableBorderComponent::Zone zone); + static String getWindowScalingFactorSettingName() { return "Gdk/WindowScalingFactor"; } static String getThemeNameSettingName() { return "Net/ThemeName"; } diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/modules/juce_gui_basics/windows/juce_ComponentPeer.h index fd1c4d09e8c2..0fc704e3e605 100644 --- a/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -258,6 +258,16 @@ class JUCE_API ComponentPeer : private FocusChangeListener */ void setConstrainer (ComponentBoundsConstrainer* newConstrainer) noexcept; + /** Asks the window-manager to begin resizing this window, on platforms where this is useful + (currently just Linux/X11). + + @param mouseDownPosition The position of the mouse event that started the resize in + unscaled peer coordinates + @param zone The edges of the window that may be moved during the resize + */ + virtual void startHostManagedResize ([[maybe_unused]] Point mouseDownPosition, + [[maybe_unused]] ResizableBorderComponent::Zone zone) {} + /** Returns the current constrainer, if one has been set. */ ComponentBoundsConstrainer* getConstrainer() const noexcept { return constrainer; }