Introduce QPA API for size grip handling.

- Introduce API to do size grip handling (mouse press
  and move).
- Move Windows code to Windows plugin.
- Move X11 code to XCB plugin and activate it.

Change-Id: I2f61d6ddc1fa07447e668554d41ecc820efca23f
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
This commit is contained in:
Friedemann Kleint 2012-06-12 10:47:42 +02:00 committed by Qt by Nokia
parent 1539e8e310
commit 0137f092af
8 changed files with 123 additions and 72 deletions

View File

@ -117,6 +117,8 @@ public:
virtual void windowEvent(QEvent *event); virtual void windowEvent(QEvent *event);
virtual bool startSystemResize(const QPoint &pos, Qt::Corner corner);
protected: protected:
QScopedPointer<QPlatformWindowPrivate> d_ptr; QScopedPointer<QPlatformWindowPrivate> d_ptr;
private: private:

View File

@ -335,6 +335,23 @@ void QPlatformWindow::windowEvent(QEvent *event)
Q_UNUSED(event); Q_UNUSED(event);
} }
/*!
Reimplement this method to start a system size grip drag
operation if the system supports it and return true to indicate
success.
It is called from the mouse press event handler of the size grip.
The default implementation is empty and does nothing with \a pos
and \a corner.
*/
bool QPlatformWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
{
Q_UNUSED(pos)
Q_UNUSED(corner)
return false;
}
/*! /*!
\class QPlatformWindow \class QPlatformWindow
\since 4.8 \since 4.8

View File

@ -151,6 +151,16 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
return true; return true;
} }
compressMouseMove(&msg); compressMouseMove(&msg);
// Eat mouse move after size grip drag.
if (msg.message == WM_MOUSEMOVE) {
QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
if (platformWindow->testFlag(QWindowsWindow::SizeGripOperation)) {
MSG mouseMsg;
while (PeekMessage(&mouseMsg, platformWindow->handle(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) ;
platformWindow->clearFlag(QWindowsWindow::SizeGripOperation);
return true;
}
}
const QPoint client(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam)); const QPoint client(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
// Enter new window: track to generate leave event. // Enter new window: track to generate leave event.
if (m_windowUnderMouse != window) { if (m_windowUnderMouse != window) {

View File

@ -1402,6 +1402,32 @@ void QWindowsWindow::setMouseGrabEnabled_sys(bool grab)
} }
} }
static inline DWORD cornerToWinOrientation(Qt::Corner corner)
{
switch (corner) {
case Qt::TopLeftCorner:
return 0xf004; // SZ_SIZETOPLEFT;
case Qt::TopRightCorner:
return 0xf005; // SZ_SIZETOPRIGHT
case Qt::BottomLeftCorner:
return 0xf007; // SZ_SIZEBOTTOMLEFT
case Qt::BottomRightCorner:
return 0xf008; // SZ_SIZEBOTTOMRIGHT
}
return 0;
}
bool QWindowsWindow::startSystemResize(const QPoint &, Qt::Corner corner)
{
if (!GetSystemMenu(m_data.hwnd, FALSE))
return false;
ReleaseCapture();
PostMessage(m_data.hwnd, WM_SYSCOMMAND, cornerToWinOrientation(corner), 0);
setFlag(SizeGripOperation);
return true;
}
#ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO
void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{ {

View File

@ -122,7 +122,8 @@ public:
OpenGLSurface = 0x10, OpenGLSurface = 0x10,
OpenGLDoubleBuffered = 0x20, OpenGLDoubleBuffered = 0x20,
OpenGlPixelFormatInitialized = 0x40, OpenGlPixelFormatInitialized = 0x40,
BlockedByModal = 0x80 BlockedByModal = 0x80,
SizeGripOperation = 0x100
}; };
struct WindowData struct WindowData
@ -173,6 +174,8 @@ public:
virtual bool setKeyboardGrabEnabled(bool grab); virtual bool setKeyboardGrabEnabled(bool grab);
virtual bool setMouseGrabEnabled(bool grab); virtual bool setMouseGrabEnabled(bool grab);
virtual bool startSystemResize(const QPoint &pos, Qt::Corner corner);
Qt::WindowState windowState_sys() const; Qt::WindowState windowState_sys() const;
Qt::WindowStates windowStates_sys() const; Qt::WindowStates windowStates_sys() const;

View File

@ -1670,4 +1670,37 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor)
xcb_flush(xcb_connection()); xcb_flush(xcb_connection());
} }
#ifdef XCB_USE_XLIB
bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
{
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
if (!connection()->wmSupport()->isSupportedByWM(moveResize))
return false;
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.message_type = moveResize;
Display *display = (Display *)connection()->xlib_display();
xev.xclient.display = display;
xev.xclient.window = xcb_window();
xev.xclient.format = 32;
const QPoint globalPos = window()->mapToGlobal(pos);
xev.xclient.data.l[0] = globalPos.x();
xev.xclient.data.l[1] = globalPos.y();
const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;
const bool left = corner == Qt::BottomLeftCorner || corner == Qt::TopLeftCorner;
if (bottom)
xev.xclient.data.l[2] = left ? 6 : 4; // bottomleft/bottomright
else
xev.xclient.data.l[2] = left ? 0 : 2; // topleft/topright
xev.xclient.data.l[3] = Button1;
xev.xclient.data.l[4] = 0;
XUngrabPointer(display, CurrentTime);
XSendEvent(display, m_screen->root(), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
return true;
}
#endif // XCB_USE_XLIB
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -107,6 +107,10 @@ public:
QSurfaceFormat format() const; QSurfaceFormat format() const;
#ifdef XCB_USE_XLIB
bool startSystemResize(const QPoint &pos, Qt::Corner corner);
#endif // XCB_USE_XLIB
xcb_window_t xcb_window() const { return m_window; } xcb_window_t xcb_window() const { return m_window; }
uint depth() const { return m_depth; } uint depth() const { return m_depth; }
QImage::Format imageFormat() const { return m_imageFormat; } QImage::Format imageFormat() const { return m_imageFormat; }

View File

@ -46,6 +46,8 @@
#include "qapplication.h" #include "qapplication.h"
#include "qevent.h" #include "qevent.h"
#include "qpainter.h" #include "qpainter.h"
#include "qwindow.h"
#include <qpa/qplatformwindow.h>
#include "qstyle.h" #include "qstyle.h"
#include "qstyleoption.h" #include "qstyleoption.h"
#include "qlayout.h" #include "qlayout.h"
@ -59,23 +61,8 @@
#include <private/qwidget_p.h> #include <private/qwidget_p.h>
#include <QtWidgets/qabstractscrollarea.h> #include <QtWidgets/qabstractscrollarea.h>
#ifdef Q_OS_WIN
# include <QtCore/qt_windows.h>
# include "private/qapplication_p.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
#if defined (Q_OS_WIN)
# define SZ_SIZEBOTTOMRIGHT 0xf008
# define SZ_SIZEBOTTOMLEFT 0xf007
# define SZ_SIZETOPLEFT 0xf004
# define SZ_SIZETOPRIGHT 0xf005
HMENU qt_getWindowsSystemMenu(const QWidget *w);
#endif
static QWidget *qt_sizegrip_topLevelWidget(QWidget* w) static QWidget *qt_sizegrip_topLevelWidget(QWidget* w)
{ {
while (w && !w->isWindow() && w->windowType() != Qt::SubWindow) while (w && !w->isWindow() && w->windowType() != Qt::SubWindow)
@ -87,6 +74,7 @@ class QSizeGripPrivate : public QWidgetPrivate
{ {
Q_DECLARE_PUBLIC(QSizeGrip) Q_DECLARE_PUBLIC(QSizeGrip)
public: public:
QSizeGripPrivate();
void init(); void init();
QPoint p; QPoint p;
QRect r; QRect r;
@ -143,8 +131,19 @@ public:
if (showSizeGrip) if (showSizeGrip)
q->setVisible(true); q->setVisible(true);
} }
bool m_platformSizeGrip;
}; };
QSizeGripPrivate::QSizeGripPrivate()
: dxMax(0)
, dyMax(0)
, gotMousePress(false)
, tlw(0)
, m_platformSizeGrip(false)
{
}
#ifdef Q_WS_MAC #ifdef Q_WS_MAC
void QSizeGripPrivate::updateMacSizer(bool hide) const void QSizeGripPrivate::updateMacSizer(bool hide) const
{ {
@ -226,11 +225,7 @@ QSizeGrip::QSizeGrip(QWidget * parent)
void QSizeGripPrivate::init() void QSizeGripPrivate::init()
{ {
Q_Q(QSizeGrip); Q_Q(QSizeGrip);
dxMax = 0;
dyMax = 0;
tlw = 0;
m_corner = q->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner; m_corner = q->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
gotMousePress = false;
#if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC) #if !defined(QT_NO_CURSOR) && !defined(Q_WS_MAC)
q->setCursor(m_corner == Qt::TopLeftCorner || m_corner == Qt::BottomRightCorner q->setCursor(m_corner == Qt::TopLeftCorner || m_corner == Qt::BottomRightCorner
@ -284,6 +279,7 @@ void QSizeGrip::paintEvent(QPaintEvent *event)
resize operation. The mouse press event is passed in the \a event resize operation. The mouse press event is passed in the \a event
parameter. parameter.
*/ */
void QSizeGrip::mousePressEvent(QMouseEvent * e) void QSizeGrip::mousePressEvent(QMouseEvent * e)
{ {
if (e->button() != Qt::LeftButton) { if (e->button() != Qt::LeftButton) {
@ -297,44 +293,20 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e)
d->gotMousePress = true; d->gotMousePress = true;
d->r = tlw->geometry(); d->r = tlw->geometry();
#ifdef Q_WS_X11 // Does the platform provide size grip support?
// Use a native X11 sizegrip for "real" top-level windows if supported. d->m_platformSizeGrip = false;
if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE)) if (tlw->isWindow()
&& tlw->windowHandle()
&& !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint) && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)
&& !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) { && !tlw->testAttribute(Qt::WA_DontShowOnScreen)
XEvent xev; && !tlw->hasHeightForWidth()) {
xev.xclient.type = ClientMessage; QPlatformWindow *platformWindow = tlw->windowHandle()->handle();
xev.xclient.message_type = ATOM(_NET_WM_MOVERESIZE); const QPoint topLevelPos = mapTo(tlw, e->pos());
xev.xclient.display = X11->display; d->m_platformSizeGrip = platformWindow && platformWindow->startSystemResize(topLevelPos, d->m_corner);
xev.xclient.window = tlw->winId();
xev.xclient.format = 32;
xev.xclient.data.l[0] = e->globalPos().x();
xev.xclient.data.l[1] = e->globalPos().y();
if (d->atBottom())
xev.xclient.data.l[2] = d->atLeft() ? 6 : 4; // bottomleft/bottomright
else
xev.xclient.data.l[2] = d->atLeft() ? 0 : 2; // topleft/topright
xev.xclient.data.l[3] = Button1;
xev.xclient.data.l[4] = 0;
XUngrabPointer(X11->display, X11->time);
XSendEvent(X11->display, QX11Info::appRootWindow(x11Info().screen()), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
return;
} }
#endif // Q_WS_X11
#ifdef Q_OS_WIN
if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) {
uint orientation = 0;
if (d->atBottom())
orientation = d->atLeft() ? SZ_SIZEBOTTOMLEFT : SZ_SIZEBOTTOMRIGHT;
else
orientation = d->atLeft() ? SZ_SIZETOPLEFT : SZ_SIZETOPRIGHT;
ReleaseCapture(); if (d->m_platformSizeGrip)
PostMessage(QApplicationPrivate::getHWNDForWidget(tlw), WM_SYSCOMMAND, orientation, 0);
return; return;
}
#endif // Q_OS_WIN
// Find available desktop/workspace geometry. // Find available desktop/workspace geometry.
QRect availableGeometry; QRect availableGeometry;
@ -400,32 +372,16 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e)
*/ */
void QSizeGrip::mouseMoveEvent(QMouseEvent * e) void QSizeGrip::mouseMoveEvent(QMouseEvent * e)
{ {
if (e->buttons() != Qt::LeftButton) { Q_D(QSizeGrip);
if (e->buttons() != Qt::LeftButton || d->m_platformSizeGrip) {
QWidget::mouseMoveEvent(e); QWidget::mouseMoveEvent(e);
return; return;
} }
Q_D(QSizeGrip);
QWidget* tlw = qt_sizegrip_topLevelWidget(this); QWidget* tlw = qt_sizegrip_topLevelWidget(this);
if (!d->gotMousePress || tlw->testAttribute(Qt::WA_WState_ConfigPending)) if (!d->gotMousePress || tlw->testAttribute(Qt::WA_WState_ConfigPending))
return; return;
#ifdef Q_WS_X11
if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
&& tlw->isTopLevel() && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)
&& !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth())
return;
#endif
#ifdef Q_OS_WIN
if (tlw->isWindow() && qt_getWindowsSystemMenu(tlw) && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) {
if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(tlw)) {
MSG msg;
while (PeekMessage(&msg, hwnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) ;
return;
}
}
#endif
QPoint np(e->globalPos()); QPoint np(e->globalPos());
// Don't extend beyond the available geometry; bound to dyMax and dxMax. // Don't extend beyond the available geometry; bound to dyMax and dxMax.