diff options
-rw-r--r-- | src/corelib/global/qnamespace.h | 1 | ||||
-rw-r--r-- | src/corelib/global/qnamespace.qdoc | 4 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 2 | ||||
-rw-r--r-- | src/gui/kernel/qplatformintegration.cpp | 4 | ||||
-rw-r--r-- | src/gui/kernel/qplatformintegration.h | 3 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 37 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qwindowsysteminterface.cpp | 5 | ||||
-rw-r--r-- | src/gui/kernel/qwindowsysteminterface.h | 3 | ||||
-rw-r--r-- | src/gui/kernel/qwindowsysteminterface_p.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 1 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 260 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.h | 9 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.cpp | 14 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication_p.h | 3 | ||||
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 28 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetwindow.cpp | 40 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetwindow_qpa_p.h | 7 |
20 files changed, 410 insertions, 24 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 41bca2a443..a33c50a041 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -284,6 +284,7 @@ public: SplashScreen = ToolTip | Dialog, Desktop = 0x00000010 | Window, SubWindow = 0x00000012, + ForeignWindow = 0x00000020 | Window, WindowType_Mask = 0x000000ff, MSWindowsFixedSizeDialogHint = 0x00000100, diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 8ec206a572..02d00c213b 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -1966,6 +1966,10 @@ \value SubWindow Indicates that this widget is a sub-window, such as a QMdiSubWindow widget. + \value ForeignWindow Indicates that this window object is a handle + representing a native platform window created by + another process or by manually using native code. + There are also a number of flags which you can use to customize the appearance of top-level windows. These have no effect on other windows: diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 7bfc9ccbec..32c52d2fe2 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1605,7 +1605,7 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate } if (QGuiApplicationPrivate::focus_window) { - QFocusEvent focusIn(QEvent::FocusIn); + QFocusEvent focusIn(QEvent::FocusIn, e->reason); QCoreApplication::sendSpontaneousEvent(QGuiApplicationPrivate::focus_window, &focusIn); QObject::connect(QGuiApplicationPrivate::focus_window, SIGNAL(focusObjectChanged(QObject*)), qApp, SLOT(_q_updateFocusObject(QObject*))); diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index fbc7eeb76f..70de75072c 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -206,6 +206,10 @@ QPlatformServices *QPlatformIntegration::services() const state explicitly by using QWindowSystemInterface::handleApplicationStateChanged(). If not set, application state will follow window activation, which is the normal behavior for desktop platforms. + + \value ForeignWindows The platform allows creating QWindows which represent + native windows created by other processes or anyway created by using native + libraries. */ diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index 55517cf600..ddee6f05c8 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -89,7 +89,8 @@ public: BufferQueueingOpenGL, WindowMasks, MultipleWindows, - ApplicationState + ApplicationState, + ForeignWindows }; virtual ~QPlatformIntegration() { } diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 99a54dc847..3d4383301e 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -474,6 +474,10 @@ void QWindow::create() WId QWindow::winId() const { Q_D(const QWindow); + + if (type() == Qt::ForeignWindow) + return WId(property("_q_foreignWinId").value<WId>()); + if(!d->platformWindow) const_cast<QWindow *>(this)->create(); @@ -499,8 +503,11 @@ QWindow *QWindow::parent() const the clip of the window, so it will be clipped to the \a parent window. Setting \a parent to be 0 will make the window become a top level window. -*/ + If \a parent is a window created by fromWinId(), then the current window + will be embedded inside \a parent, if the platform supports it. Window + embedding is currently supported only by the X11 platform plugin. +*/ void QWindow::setParent(QWindow *parent) { Q_D(QWindow); @@ -2104,6 +2111,34 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed() } +/*! + Creates a local representation of a window created by another process or by + using native libraries below Qt. + + Given the handle \a id to a native window, this method creates a QWindow + object which can be used to represent the window when invoking methods like + setParent() and setTransientParent(). + This can be used, on platforms which support it, to embed a window inside a + container or to make a window stick on top of a window created by another + process. + + \sa setParent() + \sa setTransientParent() +*/ +QWindow *QWindow::fromWinId(WId id) +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ForeignWindows)) { + qWarning() << "QWindow::fromWinId(): platform plugin does not support foreign windows."; + return 0; + } + + QWindow *window = new QWindow; + window->setFlags(Qt::ForeignWindow); + window->setProperty("_q_foreignWinId", QVariant::fromValue(id)); + window->create(); + return window; +} + #ifndef QT_NO_CURSOR /*! \brief set the cursor shape for this window diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h index 0842e9ceb6..ca3ffb0709 100644 --- a/src/gui/kernel/qwindow.h +++ b/src/gui/kernel/qwindow.h @@ -257,6 +257,8 @@ public: void unsetCursor(); #endif + static QWindow *fromWinId(WId id); + public Q_SLOTS: void setVisible(bool visible); diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 3609d5dce6..d2add91d66 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -112,9 +112,10 @@ void QWindowSystemInterface::handleEnterLeaveEvent(QWindow *enter, QWindow *leav } } -void QWindowSystemInterface::handleWindowActivated(QWindow *tlw) +void QWindowSystemInterface::handleWindowActivated(QWindow *tlw, Qt::FocusReason r) { - QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); + QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = + new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw, r); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 22e5983a07..212259c113 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -135,7 +135,8 @@ public: static void handleEnterEvent(QWindow *w, const QPointF &local = QPointF(), const QPointF& global = QPointF()); static void handleLeaveEvent(QWindow *w); static void handleEnterLeaveEvent(QWindow *enter, QWindow *leave, const QPointF &local = QPointF(), const QPointF& global = QPointF()); - static void handleWindowActivated(QWindow *w); + static void handleWindowActivated(QWindow *w, Qt::FocusReason r = Qt::OtherFocusReason); + static void handleWindowStateChanged(QWindow *w, Qt::WindowState newState); static void handleApplicationStateChanged(Qt::ApplicationState newState); diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index eca559abfa..f1bc4667f7 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -139,10 +139,11 @@ public: class ActivatedWindowEvent : public WindowSystemEvent { public: - explicit ActivatedWindowEvent(QWindow *activatedWindow) - : WindowSystemEvent(ActivatedWindow), activated(activatedWindow) + explicit ActivatedWindowEvent(QWindow *activatedWindow, Qt::FocusReason r) + : WindowSystemEvent(ActivatedWindow), activated(activatedWindow), reason(r) { } QPointer<QWindow> activated; + Qt::FocusReason reason; }; class WindowStateChangedEvent : public WindowSystemEvent { diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 2467eb6e7a..1504bd99d2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1066,7 +1066,7 @@ void QXcbConnection::processXcbEvents() while (it != m_peekFuncs.end()) { // These callbacks return true if the event is what they were // waiting for, remove them from the list in that case. - if ((*it)(event)) + if ((*it)(this, event)) it = m_peekFuncs.erase(it); else ++it; @@ -1086,7 +1086,7 @@ void QXcbConnection::processXcbEvents() // Indicate with a null event that the event the callbacks are waiting for // is not in the queue currently. Q_FOREACH (PeekFunc f, m_peekFuncs) - f(0); + f(this, 0); m_peekFuncs.clear(); xcb_flush(xcb_connection()); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index b499f75b78..f69a8a9f35 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -364,7 +364,7 @@ public: template<typename T> inline xcb_generic_event_t *checkEvent(T &checker); - typedef bool (*PeekFunc)(xcb_generic_event_t *); + typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *); void addPeekFunc(PeekFunc f); inline xcb_timestamp_t time() const { return m_time; } diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 0a02ea02af..f0cabea43d 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -228,6 +228,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering(); case WindowMasks: return true; case MultipleWindows: return true; + case ForeignWindows: return true; default: return QPlatformIntegration::hasCapability(cap); } } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index abd2f7a147..27cd163c36 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -122,6 +122,36 @@ enum { QT_BEGIN_NAMESPACE +#undef FocusIn + +enum QX11EmbedFocusInDetail { + XEMBED_FOCUS_CURRENT = 0, + XEMBED_FOCUS_FIRST = 1, + XEMBED_FOCUS_LAST = 2 +}; + +enum QX11EmbedInfoFlags { + XEMBED_MAPPED = (1 << 0), +}; + +enum QX11EmbedMessageType { + XEMBED_EMBEDDED_NOTIFY = 0, + XEMBED_WINDOW_ACTIVATE = 1, + XEMBED_WINDOW_DEACTIVATE = 2, + XEMBED_REQUEST_FOCUS = 3, + XEMBED_FOCUS_IN = 4, + XEMBED_FOCUS_OUT = 5, + XEMBED_FOCUS_NEXT = 6, + XEMBED_FOCUS_PREV = 7, + XEMBED_MODALITY_ON = 10, + XEMBED_MODALITY_OFF = 11, + XEMBED_REGISTER_ACCELERATOR = 12, + XEMBED_UNREGISTER_ACCELERATOR = 13, + XEMBED_ACTIVATE_ACCELERATOR = 14 +}; + +static unsigned int XEMBED_VERSION = 0; + // Returns true if we should set WM_TRANSIENT_FOR on \a w static inline bool isTransient(const QWindow *w) { @@ -157,6 +187,7 @@ QXcbWindow::QXcbWindow(QWindow *window) , m_mapped(false) , m_transparent(false) , m_deferredActivation(false) + , m_embedded(false) , m_netWmUserTimeWindow(XCB_NONE) , m_dirtyFrameMargins(false) #if defined(XCB_USE_EGL) @@ -168,7 +199,10 @@ QXcbWindow::QXcbWindow(QWindow *window) setConnection(m_screen->connection()); - create(); + if (window->type() != Qt::ForeignWindow) + create(); + else + m_window = window->winId(); } void QXcbWindow::create() @@ -235,8 +269,10 @@ void QXcbWindow::create() } xcb_window_t xcb_parent_id = m_screen->root(); - if (parent()) + if (parent()) { xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window(); + m_embedded = parent()->window()->type() == Qt::ForeignWindow; + } m_format = window()->requestedFormat(); @@ -368,6 +404,14 @@ void QXcbWindow::create() atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, 1, &leader)); + /* Add XEMBED info; this operation doesn't initiate the embedding. */ + long data[] = { XEMBED_VERSION, XEMBED_MAPPED }; + Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + atom(QXcbAtom::_XEMBED_INFO), + atom(QXcbAtom::_XEMBED_INFO), + 32, 2, (void *)data)); + + #ifdef XCB_USE_XINPUT2_MAEMO if (connection()->isUsingXInput2Maemo()) { XIEventMask xieventmask; @@ -410,7 +454,8 @@ void QXcbWindow::create() QXcbWindow::~QXcbWindow() { - destroy(); + if (window()->type() != Qt::ForeignWindow) + destroy(); } void QXcbWindow::destroy() @@ -574,9 +619,10 @@ void QXcbWindow::show() propagateSizeHints(); // update WM_TRANSIENT_FOR - if (isTransient(window())) { + const QWindow *tp = window()->transientParent(); + if (isTransient(window()) || tp != 0) { xcb_window_t transientXcbParent = 0; - if (const QWindow *tp = window()->transientParent()) + if (tp) transientXcbParent = static_cast<const QXcbWindow *>(tp->handle())->winId(); // Default to client leader if there is no transient parent, else modal dialogs can // be hidden by their parents. @@ -1140,7 +1186,15 @@ void QXcbWindow::setParent(const QPlatformWindow *parent) { QPoint topLeft = geometry().topLeft(); - xcb_window_t xcb_parent_id = parent ? static_cast<const QXcbWindow *>(parent)->xcb_window() : m_screen->root(); + xcb_window_t xcb_parent_id; + if (parent) { + const QXcbWindow *qXcbParent = static_cast<const QXcbWindow *>(parent); + xcb_parent_id = qXcbParent->xcb_window(); + m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow; + } else { + xcb_parent_id = m_screen->root(); + m_embedded = false; + } Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y())); } @@ -1271,6 +1325,13 @@ void QXcbWindow::propagateSizeHints() void QXcbWindow::requestActivateWindow() { + /* Never activate embedded windows; doing that would prevent the container + * to re-gain the keyboard focus later. */ + if (m_embedded) { + QPlatformWindow::requestActivateWindow(); + return; + } + if (!m_mapped) { m_deferredActivation = true; return; @@ -1434,7 +1495,8 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even } else if (event->type == atom(QXcbAtom::XdndDrop)) { connection()->drag()->handleDrop(window(), event); #endif - } else if (event->type == atom(QXcbAtom::_XEMBED)) { // QSystemTrayIcon + } else if (event->type == atom(QXcbAtom::_XEMBED)) { + handleXEmbedMessage(event); } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } @@ -1476,6 +1538,53 @@ bool QXcbWindow::isExposed() const return m_mapped; } +bool QXcbWindow::isEmbedded(const QPlatformWindow *parentWindow) const +{ + if (!m_embedded) + return false; + + return parentWindow ? (parentWindow == parent()) : true; +} + +QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const +{ + if (!m_embedded) + return pos; + + QPoint ret; + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(xcb_connection(), xcb_window(), m_screen->root(), + pos.x(), pos.y()); + xcb_translate_coordinates_reply_t *reply = + xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); + if (reply) { + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); + free(reply); + } + + return ret; +} + +QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const +{ + if (!m_embedded) + return pos; + QPoint ret; + xcb_translate_coordinates_cookie_t cookie = + xcb_translate_coordinates(xcb_connection(), m_screen->root(), xcb_window(), + pos.x(), pos.y()); + xcb_translate_coordinates_reply_t *reply = + xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); + if (reply) { + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); + free(reply); + } + + return ret; +} + void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) { if (event->window == m_window) { @@ -1506,6 +1615,15 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) updateNetWmUserTime(event->time); + if (m_embedded) { + if (window() != QGuiApplication::focusWindow()) { + const QXcbWindow *container = static_cast<const QXcbWindow *>(parent()); + Q_ASSERT(container != 0); + + sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); + } + } + QPoint local(event->event_x, event->event_y); QPoint global(event->root_x, event->root_y); @@ -1661,6 +1779,25 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev QWindowSystemInterface::handleWindowStateChanged(window(), newState); m_lastWindowStateEvent = newState; } + return; + } + + const xcb_atom_t xEmbedInfoAtom = atom(QXcbAtom::_XEMBED_INFO); + if (event->atom == xEmbedInfoAtom) { + const xcb_get_property_cookie_t get_cookie = + xcb_get_property(xcb_connection(), 0, m_window, xEmbedInfoAtom, + XCB_ATOM_ANY, 0, 3); + + xcb_get_property_reply_t *reply = + xcb_get_property_reply(xcb_connection(), get_cookie, NULL); + if (reply && reply->length >= 2) { + const long *data = (const long *)xcb_get_property_value(reply); + if (data[1] & XEMBED_MAPPED) + Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); + else + Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); + } + free(reply); } } @@ -1672,7 +1809,7 @@ void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *) QWindowSystemInterface::handleWindowActivated(w); } -static bool focusInPeeker(xcb_generic_event_t *event) +static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event) { if (!event) { // FocusIn event is not in the queue, proceed with FocusOut normally. @@ -1680,7 +1817,18 @@ static bool focusInPeeker(xcb_generic_event_t *event) return true; } uint response_type = event->response_type & ~0x80; - return response_type == XCB_FOCUS_IN; + if (response_type == XCB_FOCUS_IN) + return true; + + /* We are also interested in XEMBED_FOCUS_IN events */ + if (response_type == XCB_CLIENT_MESSAGE) { + xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event; + if (cme->type == connection->atom(QXcbAtom::_XEMBED) + && cme->data.data32[1] == XEMBED_FOCUS_IN) + return true; + } + + return false; } void QXcbWindow::handleFocusOutEvent(const xcb_focus_out_event_t *) @@ -1744,6 +1892,35 @@ void QXcbWindow::setCursor(xcb_cursor_t cursor) xcb_flush(xcb_connection()); } +void QXcbWindow::windowEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::FocusIn: + if (m_embedded && !event->spontaneous()) { + QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event); + switch (focusEvent->reason()) { + case Qt::TabFocusReason: + case Qt::BacktabFocusReason: + { + const QXcbWindow *container = + static_cast<const QXcbWindow *>(parent()); + sendXEmbedMessage(container->xcb_window(), + focusEvent->reason() == Qt::TabFocusReason ? + XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV); + event->accept(); + } + break; + default: + break; + } + } + break; + default: + break; + } + QPlatformWindow::windowEvent(event); +} + bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) { const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); @@ -1772,6 +1949,71 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) return true; } +// Sends an XEmbed message. +void QXcbWindow::sendXEmbedMessage(xcb_window_t window, long message, + long detail, long data1, long data2) +{ + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.window = window; + event.type = atom(QXcbAtom::_XEMBED); + event.data.data32[0] = connection()->time(); + event.data.data32[1] = message; + event.data.data32[2] = detail; + event.data.data32[3] = data1; + event.data.data32[4] = data2; + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, window, + XCB_EVENT_MASK_NO_EVENT, (const char *)&event)); +} + +static bool activeWindowChangeQueued(const QWindow *window) +{ + /* Check from window system event queue if the next queued activation + * targets a window other than @window. + */ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *systemEvent = + static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *> + (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::ActivatedWindow)); + return systemEvent && systemEvent->activated != window; +} + +void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) +{ + connection()->setTime(event->data.data32[0]); + switch (event->data.data32[1]) { + case XEMBED_WINDOW_ACTIVATE: + case XEMBED_WINDOW_DEACTIVATE: + case XEMBED_EMBEDDED_NOTIFY: + break; + case XEMBED_FOCUS_IN: + Qt::FocusReason reason; + switch (event->data.data32[2]) { + case XEMBED_FOCUS_FIRST: + reason = Qt::TabFocusReason; + break; + case XEMBED_FOCUS_LAST: + reason = Qt::BacktabFocusReason; + break; + case XEMBED_FOCUS_CURRENT: + default: + reason = Qt::OtherFocusReason; + break; + } + connection()->setFocusWindow(static_cast<QXcbWindow*>(window()->handle())); + QWindowSystemInterface::handleWindowActivated(window(), reason); + break; + case XEMBED_FOCUS_OUT: + if (window() == QGuiApplication::focusWindow() + && !activeWindowChangeQueued(window())) { + connection()->setFocusWindow(0); + QWindowSystemInterface::handleWindowActivated(0); + } + break; + } +} + #if !defined(QT_NO_SHAPE) static inline xcb_rectangle_t qRectToXCBRectangle(const QRect &r) diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 3b5404684f..1810a58c7b 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -87,6 +87,9 @@ public: void setParent(const QPlatformWindow *window); bool isExposed() const; + bool isEmbedded(const QPlatformWindow *parentWindow) const; + QPoint mapToGlobal(const QPoint &pos) const; + QPoint mapFromGlobal(const QPoint &pos) const; void setWindowTitle(const QString &title); void setWindowIcon(const QIcon &icon); @@ -107,6 +110,8 @@ public: QSurfaceFormat format() const; + void windowEvent(QEvent *event); + bool startSystemResize(const QPoint &pos, Qt::Corner corner); void setOpacity(qreal level); @@ -158,6 +163,9 @@ private: void updateDoesNotAcceptFocus(bool doesNotAcceptFocus); QRect windowToWmGeometry(QRect r) const; + void sendXEmbedMessage(xcb_window_t window, long message, + long detail = 0, long data1 = 0, long data2 = 0); + void handleXEmbedMessage(const xcb_client_message_event_t *event); void create(); void destroy(); @@ -184,6 +192,7 @@ private: bool m_deferredActivation; bool m_deferredExpose; bool m_configureNotifyPending; + bool m_embedded; xcb_window_t m_netWmUserTimeWindow; QSurfaceFormat m_format; diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index cff75f258e..1e493fe8c6 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -2010,7 +2010,8 @@ void QApplication::setActiveWindow(QWidget* act) * Returns 0 if a new focus widget could not be found. * Shared with QGraphicsProxyWidgetPrivate::findFocusChild() */ -QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next) +QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool next, + bool *wrappingOccurred) { uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus; @@ -2020,18 +2021,29 @@ QWidget *QApplicationPrivate::focusNextPrevChild_helper(QWidget *toplevel, bool QWidget *w = f; QWidget *test = f->d_func()->focus_next; + bool seenWindow = false; + bool focusWidgetAfterWindow = false; while (test && test != f) { + if (test->isWindow()) + seenWindow = true; + if ((test->focusPolicy() & focus_flag) == focus_flag && !(test->d_func()->extra && test->d_func()->extra->focus_proxy) && test->isVisibleTo(toplevel) && test->isEnabled() && !(w->windowType() == Qt::SubWindow && !w->isAncestorOf(test)) && (toplevel->windowType() != Qt::SubWindow || toplevel->isAncestorOf(test))) { w = test; + if (seenWindow) + focusWidgetAfterWindow = true; if (next) break; } test = test->d_func()->focus_next; } + + if (wrappingOccurred != 0) + *wrappingOccurred = next ? focusWidgetAfterWindow : !focusWidgetAfterWindow; + if (w == f) { if (qt_in_tab_key_event) { w->window()->setAttribute(Qt::WA_KeyboardFocusChange); diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index fbd96366fb..85e26fdd49 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -165,7 +165,8 @@ public: void closePopup(QWidget *popup); void openPopup(QWidget *popup); static void setFocusWidget(QWidget *focus, Qt::FocusReason reason); - static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next); + static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next, + bool *wrappingOccurred = 0); #ifndef QT_NO_GRAPHICSVIEW // Maintain a list of all scenes to ensure font and palette propagation to diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index e5df25e972..7840cddd5d 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -6138,10 +6138,34 @@ bool QWidget::focusNextPrevChild(bool next) if (d->extra && d->extra->proxyWidget) return d->extra->proxyWidget->focusNextPrevChild(next); #endif - QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next); + + bool wrappingOccurred = false; + QWidget *w = QApplicationPrivate::focusNextPrevChild_helper(this, next, + &wrappingOccurred); if (!w) return false; - w->setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason; + + /* If we are about to wrap the focus chain, give the platform + * implementation a chance to alter the wrapping behavior. This is + * especially needed when the window is embedded in a window created by + * another process. + */ + if (wrappingOccurred) { + QWindow *window = windowHandle(); + if (window != 0) { + QWindowPrivate *winp = qt_window_private(window); + + if (winp->platformWindow != 0) { + QFocusEvent event(QEvent::FocusIn, reason); + event.ignore(); + winp->platformWindow->windowEvent(&event); + if (event.isAccepted()) return true; + } + } + } + + w->setFocus(reason); return true; } diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index c543303024..183787ccc3 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE +Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets(); + QWidget *qt_button_down = 0; // widget got last button-down static QWidget *qt_tablet_target = 0; @@ -123,6 +125,8 @@ bool QWidgetWindow::event(QEvent *event) // these should not be sent to QWidget, the corresponding events // are sent by QApplicationPrivate::notifyActiveWindowChange() case QEvent::FocusIn: + handleFocusInEvent(static_cast<QFocusEvent *>(event)); + // Fallthrough case QEvent::FocusOut: { #ifndef QT_NO_ACCESSIBILITY QAccessible::State state; @@ -284,6 +288,42 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event) } } +QWidget *QWidgetWindow::getFocusWidget(FocusWidgets fw) +{ + QWidget *tlw = m_widget; + QWidget *w = tlw->nextInFocusChain(); + + QWidget *last = tlw; + + uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus; + + while (w != tlw) + { + if (((w->focusPolicy() & focus_flag) == focus_flag) + && w->isVisibleTo(m_widget) && w->isEnabled()) + { + last = w; + if (fw == FirstFocusWidget) + break; + } + w = w->nextInFocusChain(); + } + + return last; +} + +void QWidgetWindow::handleFocusInEvent(QFocusEvent *e) +{ + QWidget *focusWidget = 0; + if (e->reason() == Qt::BacktabFocusReason) + focusWidget = getFocusWidget(LastFocusWidget); + else if (e->reason() == Qt::TabFocusReason) + focusWidget = getFocusWidget(FirstFocusWidget); + + if (focusWidget != 0) + focusWidget->setFocus(); +} + void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e) { QApplication::sendSpontaneousEvent(m_widget, e); diff --git a/src/widgets/kernel/qwidgetwindow_qpa_p.h b/src/widgets/kernel/qwidgetwindow_qpa_p.h index 84c1b90826..77fdcbeb56 100644 --- a/src/widgets/kernel/qwidgetwindow_qpa_p.h +++ b/src/widgets/kernel/qwidgetwindow_qpa_p.h @@ -70,6 +70,7 @@ protected: void handleCloseEvent(QCloseEvent *); void handleEnterLeaveEvent(QEvent *); + void handleFocusInEvent(QFocusEvent *); void handleKeyEvent(QKeyEvent *); void handleMouseEvent(QMouseEvent *); void handleNonClientAreaMouseEvent(QMouseEvent *); @@ -100,6 +101,12 @@ private slots: private: void updateGeometry(); + enum FocusWidgets { + FirstFocusWidget, + LastFocusWidget + }; + QWidget *getFocusWidget(FocusWidgets fw); + QWidget *m_widget; QPointer<QWidget> m_implicit_mouse_grabber; #ifndef QT_NO_DRAGANDDROP |