summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2018-03-28 11:22:09 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-05-22 14:23:33 +0200
commitf48aa008e992429647968a29ef30d412f1ae379a (patch)
tree84115e4f8d3da97865d0d48f57ea6e48dab4b2ae /src/plugins/platforms/windows
parent26a0db4b441a74cb65410635c3e8608a6360c793 (diff)
Windows QPA: Fix RTL window title bars
Add a platform option to the plugin (-platform windows:reverse) that enables reverse mode. It sets WS_EX_LAYOUTRTL on RTL windows, forces normal orientation on all HDCs created for the window, fixes ClientToScreen()/ScreenToClient() accordingly and transforms mouse events. [ChangeLog][Platform Specific Changes][Windows] It is now possible to enable RTL mode by passing the option -platform windows:reverse. Fixes: QTBUG-28463 Change-Id: I4d70818b2fd315d4e8d5627eab11ae912c6e77be Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: André de la Rocha <andre.rocha@qt.io>
Diffstat (limited to 'src/plugins/platforms/windows')
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp70
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h28
7 files changed, 110 insertions, 23 deletions
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 320b5b1a41..f93d196b20 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -713,7 +713,7 @@ static inline bool findPlatformWindowHelper(const POINT &screenPoint, unsigned c
HWND *hwnd, QWindowsWindow **result)
{
POINT point = screenPoint;
- ScreenToClient(*hwnd, &point);
+ screenToClient(*hwnd, &point);
// Returns parent if inside & none matched.
const HWND child = ChildWindowFromPointEx(*hwnd, point, cwexFlags);
if (!child || child == *hwnd)
@@ -1043,7 +1043,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
// For non-client-area messages, these are screen coordinates (as expected
// in the MSG structure), otherwise they are client coordinates.
if (!(et & QtWindows::NonClientEventFlag)) {
- ClientToScreen(msg.hwnd, &msg.pt);
+ clientToScreen(msg.hwnd, &msg.pt);
}
} else {
GetCursorPos(&msg.pt);
@@ -1134,13 +1134,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::QuerySizeHints:
d->m_creationContext->applyToMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
return true;
- case QtWindows::ResizeEvent: {
- const QSize size(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) - d->m_creationContext->menuHeight);
- d->m_creationContext->obtainedGeometry.setSize(size);
- }
+ case QtWindows::ResizeEvent:
+ d->m_creationContext->obtainedSize = QSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return true;
case QtWindows::MoveEvent:
- d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return true;
case QtWindows::NonClientCreate:
if (shouldHaveNonClientDpiScaling(d->m_creationContext->window))
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 4e319c2326..8dd3810463 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -217,6 +217,8 @@ static inline unsigned parseOptions(const QStringList &paramList,
options |= QWindowsIntegration::NoNativeMenus;
} else if (param == QLatin1String("nowmpointer")) {
options |= QWindowsIntegration::DontUseWMPointer;
+ } else if (param == QLatin1String("reverse")) {
+ options |= QWindowsIntegration::RtlEnabled;
} else {
qWarning() << "Unknown option" << param;
}
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index e28b2c2fb3..015cf79b6c 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -69,7 +69,8 @@ public:
AlwaysUseNativeMenus = 0x100,
NoNativeMenus = 0x200,
DontUseWMPointer = 0x400,
- DetectAltGrModifier = 0x800
+ DetectAltGrModifier = 0x800,
+ RtlEnabled = 0x1000
};
explicit QWindowsIntegration(const QStringList &paramList);
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index c4d53855a5..5a52cd3ea8 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -106,7 +106,7 @@ static inline void compressMouseMove(MSG *msg)
// Extract the x,y coordinates from the lParam as we do in the WndProc
msg->pt.x = GET_X_LPARAM(mouseMsg.lParam);
msg->pt.y = GET_Y_LPARAM(mouseMsg.lParam);
- ClientToScreen(msg->hwnd, &(msg->pt));
+ clientToScreen(msg->hwnd, &(msg->pt));
// Remove the mouse move message
PeekMessage(&mouseMsg, msg->hwnd, WM_MOUSEMOVE,
WM_MOUSEMOVE, PM_REMOVE);
@@ -262,7 +262,13 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
if (et == QtWindows::MouseWheelEvent)
return translateMouseWheelEvent(window, hwnd, msg, result);
- const QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
+ QPoint winEventPosition(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
+ if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) {
+ RECT clientArea;
+ GetClientRect(hwnd, &clientArea);
+ winEventPosition.setX(clientArea.right - winEventPosition.x());
+ }
+
QPoint clientPosition;
QPoint globalPosition;
if (et & QtWindows::NonClientEventFlag) {
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index b730dbcf3b..0e686bfb10 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -688,7 +688,13 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
{
*result = 0;
- const QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
+ QPoint eventPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
+ if ((et & QtWindows::NonClientEventFlag) == 0 && QWindowsBaseWindow::isRtlLayout(hwnd)) {
+ RECT clientArea;
+ GetClientRect(hwnd, &clientArea);
+ eventPos.setX(clientArea.right - eventPos.x());
+ }
+
QPoint localPos;
QPoint globalPos;
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index d8273194fa..e700e6cff4 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -134,6 +134,10 @@ static QByteArray debugWinExStyle(DWORD exStyle)
rc += " WS_EX_LAYERED";
if (exStyle & WS_EX_DLGMODALFRAME)
rc += " WS_EX_DLGMODALFRAME";
+ if (exStyle & WS_EX_LAYOUTRTL)
+ rc += " WS_EX_LAYOUTRTL";
+ if (exStyle & WS_EX_NOINHERITLAYOUT)
+ rc += " WS_EX_NOINHERITLAYOUT";
return rc;
}
@@ -307,7 +311,7 @@ static inline QRect frameGeometry(HWND hwnd, bool topLevel)
const int width = rect.right - rect.left;
const int height = rect.bottom - rect.top;
POINT leftTop = { rect.left, rect.top };
- ScreenToClient(parent, &leftTop);
+ screenToClient(parent, &leftTop);
rect.left = leftTop.x;
rect.top = leftTop.y;
rect.right = leftTop.x + width;
@@ -667,6 +671,17 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
if ((flags & Qt::MSWindowsFixedSizeDialogHint))
dialog = true;
+ // This causes the title bar to drawn RTL and the close button
+ // to be left. Note that this causes:
+ // - All DCs created on the Window to have RTL layout (see SetLayout)
+ // - ClientToScreen() and ScreenToClient() to work in reverse as well.
+ // - Mouse event coordinates to be mirrored.
+ // - Positioning of child Windows.
+ if (QGuiApplication::layoutDirection() == Qt::RightToLeft
+ && (QWindowsIntegration::instance()->options() & QWindowsIntegration::RtlEnabled) != 0) {
+ exStyle |= WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT;
+ }
+
// Parent: Use transient parent for top levels.
if (popup) {
flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent.
@@ -772,6 +787,16 @@ QWindowsWindowData
QPoint pos = calcPosition(w, context, invMargins);
+ // Mirror the position when creating on a parent in RTL mode, ditto for the obtained geometry.
+ int mirrorParentWidth = 0;
+ if (!w->isTopLevel() && QWindowsBaseWindow::isRtlLayout(parentHandle)) {
+ RECT rect;
+ GetClientRect(parentHandle, &rect);
+ mirrorParentWidth = rect.right;
+ }
+ if (mirrorParentWidth != 0 && pos.x() != CW_USEDEFAULT && context->frameWidth != CW_USEDEFAULT)
+ pos.setX(mirrorParentWidth - context->frameWidth - pos.x());
+
result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
style,
pos.x(), pos.y(),
@@ -779,14 +804,21 @@ QWindowsWindowData
parentHandle, nullptr, appinst, nullptr);
qCDebug(lcQpaWindows).nospace()
<< "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: "
- << context->obtainedGeometry << ' ' << context->margins;
+ << context->obtainedPos << context->obtainedSize << ' ' << context->margins;
if (!result.hwnd) {
qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__);
return result;
}
- result.geometry = context->obtainedGeometry;
+ if (mirrorParentWidth != 0) {
+ context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width()
+ - context->obtainedPos.x());
+ }
+
+ QRect obtainedGeometry(context->obtainedPos, context->obtainedSize);
+
+ result.geometry = obtainedGeometry;
result.fullFrameMargins = context->margins;
result.embedded = embedded;
result.hasFrame = hasFrame;
@@ -1031,6 +1063,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w)
\ingroup qt-lighthouse-win
*/
+bool QWindowsBaseWindow::isRtlLayout(HWND hwnd)
+{
+ return (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0;
+}
+
QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w)
{
if (w) {
@@ -1193,7 +1230,9 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w,
DWORD style, DWORD exStyle) :
window(w),
requestedGeometryIn(geometryIn),
- requestedGeometry(geometry), obtainedGeometry(geometry),
+ requestedGeometry(geometry),
+ obtainedPos(geometryIn.topLeft()),
+ obtainedSize(geometryIn.size()),
margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)),
customMargins(cm)
{
@@ -1321,11 +1360,12 @@ void QWindowsWindow::initialize()
// will send the message) and screen change signals of QWindow.
if (w->type() != Qt::Desktop) {
const Qt::WindowState state = w->windowState();
+ const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize);
if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
- && creationContext->requestedGeometryIn != creationContext->obtainedGeometry) {
- QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, creationContext->obtainedGeometry);
+ && creationContext->requestedGeometryIn != obtainedGeometry) {
+ QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry);
}
- QPlatformScreen *obtainedScreen = screenForGeometry(creationContext->obtainedGeometry);
+ QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry);
if (obtainedScreen && screen() != obtainedScreen)
QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
}
@@ -1942,7 +1982,16 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
result = SetWindowPlacement(hwnd, &windowPlacement);
} else {
- result = MoveWindow(hwnd, frameGeometry.x(), frameGeometry.y(),
+ int x = frameGeometry.x();
+ if (!window()->isTopLevel()) {
+ const HWND parentHandle = GetParent(hwnd);
+ if (isRtlLayout(parentHandle)) {
+ RECT rect;
+ GetClientRect(parentHandle, &rect);
+ x = rect.right - frameGeometry.width() - x;
+ }
+ }
+ result = MoveWindow(hwnd, x, frameGeometry.y(),
frameGeometry.width(), frameGeometry.height(), true);
}
qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << window()
@@ -1958,8 +2007,11 @@ void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
HDC QWindowsWindow::getDC()
{
- if (!m_hdc)
+ if (!m_hdc) {
m_hdc = GetDC(handle());
+ if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
+ SetLayout(m_hdc, 0); // Clear RTL layout
+ }
return m_hdc;
}
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 958564aa86..1abe1e3531 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -89,7 +89,8 @@ struct QWindowCreationContext
const QWindow *window;
QRect requestedGeometryIn; // QWindow scaled
QRect requestedGeometry; // after QPlatformWindow::initialGeometry()
- QRect obtainedGeometry;
+ QPoint obtainedPos;
+ QSize obtainedSize;
QMargins margins;
QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE
int frameX = CW_USEDEFAULT; // Passed on to CreateWindowEx(), including frame.
@@ -134,6 +135,7 @@ public:
unsigned style() const { return GetWindowLongPtr(handle(), GWL_STYLE); }
unsigned exStyle() const { return GetWindowLongPtr(handle(), GWL_EXSTYLE); }
+ static bool isRtlLayout(HWND hwnd);
static QWindowsBaseWindow *baseWindowOf(const QWindow *w);
static HWND handleOf(const QWindow *w);
@@ -399,18 +401,38 @@ QDebug operator<<(QDebug d, const WINDOWPOS &);
QDebug operator<<(QDebug d, const GUID &guid);
#endif // !QT_NO_DEBUG_STREAM
+static inline void clientToScreen(HWND hwnd, POINT *wP)
+{
+ if (QWindowsBaseWindow::isRtlLayout(hwnd)) {
+ RECT clientArea;
+ GetClientRect(hwnd, &clientArea);
+ wP->x = clientArea.right - wP->x;
+ }
+ ClientToScreen(hwnd, wP);
+}
+
+static inline void screenToClient(HWND hwnd, POINT *wP)
+{
+ ScreenToClient(hwnd, wP);
+ if (QWindowsBaseWindow::isRtlLayout(hwnd)) {
+ RECT clientArea;
+ GetClientRect(hwnd, &clientArea);
+ wP->x = clientArea.right - wP->x;
+ }
+}
+
// ---------- QWindowsGeometryHint inline functions.
QPoint QWindowsGeometryHint::mapToGlobal(HWND hwnd, const QPoint &qp)
{
POINT p = { qp.x(), qp.y() };
- ClientToScreen(hwnd, &p);
+ clientToScreen(hwnd, &p);
return QPoint(p.x, p.y);
}
QPoint QWindowsGeometryHint::mapFromGlobal(const HWND hwnd, const QPoint &qp)
{
POINT p = { qp.x(), qp.y() };
- ScreenToClient(hwnd, &p);
+ screenToClient(hwnd, &p);
return QPoint(p.x, p.y);
}