summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/wasm/qwasmwindowstack.inc
diff options
context:
space:
mode:
authorEven Oscar Andersen <[email protected]>2025-05-23 12:37:25 +0200
committerEven Oscar Andersen <[email protected]>2025-05-26 13:27:17 +0200
commite48c19449e3856661f4fe2ccd30d94ba9d61301f (patch)
tree91ca8efd1b4072dcdfc55c83eca67095a7b50de5 /src/plugins/platforms/wasm/qwasmwindowstack.inc
parent3ad9d5777fe0771d14e89bb5601d602f2451bd49 (diff)
wasm: Fix stacking order problem for transient parent windows
Windows with a transient parent does not reflect the relationship in the stacking order. Essentially AboveTransientParent is missing as a configuration choice. What makes this slightly convoluted is that the window stack does not depend on the window (for testability). We solve this problem by making the stack and treenode templates, and provide test class as arguments when testing. QWasmWindow and QWasmScreen are not templated as before. There is also a new order type StayAboveTransientParent. Which means that we can no longer use order type to get to the group location (Since StayAboveTransientParent can map to either of the three types). The window stack tests have been updated to handle the StayAboveTransientParent type. Finally, we do not do anything with a normal parent child relationship as this should already work correctly. Fixes: QTBUG-131699 Change-Id: Ie08e18f9e0a2339175c4a09da0a831f031df71e1 Reviewed-by: Lorn Potter <[email protected]>
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmwindowstack.inc')
-rw-r--r--src/plugins/platforms/wasm/qwasmwindowstack.inc384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/plugins/platforms/wasm/qwasmwindowstack.inc b/src/plugins/platforms/wasm/qwasmwindowstack.inc
new file mode 100644
index 00000000000..9f0b5b2d491
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmwindowstack.inc
@@ -0,0 +1,384 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QWASMWINDOWSTACK_INC
+#define QWASMWINDOWSTACK_INC
+
+template <typename Window>
+QWasmWindowStack<Window>::QWasmWindowStack(
+ WindowOrderChangedCallbackType windowOrderChangedCallback)
+ : m_windowOrderChangedCallback(std::move(windowOrderChangedCallback)),
+ m_regularWindowsBegin(m_windowStack.begin()),
+ m_alwaysOnTopWindowsBegin(m_windowStack.begin())
+{
+ invariant();
+}
+
+template <typename Window>
+QWasmWindowStack<Window>::~QWasmWindowStack()
+{
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::invariant()
+{
+ Q_ASSERT(m_regularWindowsBegin >= m_windowStack.begin());
+ Q_ASSERT(m_regularWindowsBegin <= m_alwaysOnTopWindowsBegin);
+ Q_ASSERT(m_alwaysOnTopWindowsBegin <= m_windowStack.end());
+}
+
+/* insert a window at the correct location.
+ *
+ * There are three groups
+ * StayOnBottom
+ * Regular
+ * StayOnTop
+ *
+ * In addition there is StayAboveParent which
+ * can place the window in either of the
+ * three above groups, depending on which
+ * group the transient parent resides in.
+ *
+ * insertAtRegionBegin controls the placement
+ * within each of the groups. Either at the end
+ * or at the beginning.
+ *
+*/
+template <typename Window>
+void QWasmWindowStack<Window>::pushWindow(Window *window, PositionPreference position,
+ bool insertAtRegionBegin, bool callCallbacks)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 0);
+
+ auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
+ auto stayOnTopDistance = std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
+
+ if (position == PositionPreference::StayAboveTransientParent) {
+ Q_ASSERT(window->transientParent());
+ const auto it =
+ std::find(m_windowStack.begin(), m_windowStack.end(), window->transientParent());
+ if (it == m_windowStack.end()) {
+ qWarning() << "QWasmWindowStack<Window>::pushWindow - missing parent"
+ << window->transientParent();
+ pushWindow(window, PositionPreference::Regular, insertAtRegionBegin, callCallbacks);
+ return;
+ } else {
+ if (it >= m_alwaysOnTopWindowsBegin)
+ ;
+ else if (it >= m_regularWindowsBegin)
+ ++stayOnTopDistance;
+ else {
+ ++regularDistance;
+ ++stayOnTopDistance;
+ }
+ m_windowStack.insert(it + 1, window);
+ }
+ } else if (position == PositionPreference::StayOnTop) {
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_alwaysOnTopWindowsBegin, window);
+ else
+ m_windowStack.insert(m_windowStack.end(), window);
+
+ } else if (position == PositionPreference::Regular) {
+ ++stayOnTopDistance;
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_regularWindowsBegin, window);
+ else
+ m_windowStack.insert(m_alwaysOnTopWindowsBegin, window);
+
+ } else {
+ // StayOnBottom
+ ++regularDistance;
+ ++stayOnTopDistance;
+
+ if (insertAtRegionBegin)
+ m_windowStack.insert(m_windowStack.begin(), window);
+ else
+ m_windowStack.insert(m_regularWindowsBegin, window);
+ }
+
+ m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
+ m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance;
+
+ if (callCallbacks)
+ m_windowOrderChangedCallback();
+
+ Q_ASSERT(m_windowStack.count(window) == 1);
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::removeWindow(Window *window, bool callCallbacks)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ auto regularDistance = std::distance(m_windowStack.begin(), m_regularWindowsBegin);
+ auto stayOnTopDistance = std::distance(m_windowStack.begin(), m_alwaysOnTopWindowsBegin);
+
+ auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+
+ Q_ASSERT(it != m_windowStack.end());
+
+ if (it < m_regularWindowsBegin)
+ --regularDistance;
+ if (it < m_alwaysOnTopWindowsBegin)
+ --stayOnTopDistance;
+
+ m_windowStack.erase(it);
+
+ m_regularWindowsBegin = m_windowStack.begin() + regularDistance;
+ m_alwaysOnTopWindowsBegin = m_windowStack.begin() + stayOnTopDistance;
+
+ if (callCallbacks)
+ m_windowOrderChangedCallback();
+
+ Q_ASSERT(m_windowStack.count(window) == 0);
+ invariant();
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::raise(Window *window)
+{
+ if (raiseImpl(window))
+ m_windowOrderChangedCallback();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::raiseImpl(Window *window)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ {
+ const auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto itEnd = ([this, it]() {
+ if (it < m_regularWindowsBegin)
+ return m_regularWindowsBegin;
+ if (it < m_alwaysOnTopWindowsBegin)
+ return m_alwaysOnTopWindowsBegin;
+ return m_windowStack.end();
+ })();
+
+ if (it + 1 == itEnd)
+ return false;
+
+ std::rotate(it, it + 1, itEnd);
+ }
+
+ std::vector<Window *> windowsToRaise;
+ {
+ for (auto trit = m_windowStack.begin(); trit != m_windowStack.end(); ++trit) {
+ const auto w = *trit;
+ if ((w != window) &&
+ (getWindowPositionPreference(trit) == PositionPreference::StayAboveTransientParent) &&
+ (w->transientParent() == window)) {
+ windowsToRaise.push_back(w);
+ }
+ }
+ }
+
+ for (const auto w : windowsToRaise)
+ {
+ raiseImpl(w);
+ }
+ invariant();
+ return true;
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::lower(Window *window)
+{
+ if (lowerImpl(window))
+ m_windowOrderChangedCallback();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::lowerImpl(Window *window)
+{
+ invariant();
+ Q_ASSERT(m_windowStack.count(window) == 1);
+
+ {
+ const auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto itBegin = ([this, it]() {
+ if (it >= m_alwaysOnTopWindowsBegin)
+ return m_alwaysOnTopWindowsBegin;
+ if (it >= m_regularWindowsBegin)
+ return m_regularWindowsBegin;
+ return m_windowStack.begin();
+ })();
+
+ if (itBegin == it)
+ return false;
+
+ std::rotate(itBegin, it, it + 1);
+ }
+
+ std::vector<Window *> windowsToLower;
+ {
+ for (auto trit = m_windowStack.begin(); trit != m_windowStack.end(); ++trit) {
+ const auto w = *trit;
+ if ((w != window) &&
+ (getWindowPositionPreference(trit) == PositionPreference::StayAboveTransientParent) &&
+ (w->transientParent() == window)) {
+ windowsToLower.push_back(w);
+ }
+ }
+ }
+
+ for (const auto w : windowsToLower)
+ {
+ lowerImpl(w);
+ }
+ invariant();
+ return true;
+}
+
+template <typename Window>
+void QWasmWindowStack<Window>::windowPositionPreferenceChanged(Window *window,
+ PositionPreference position)
+{
+ invariant();
+
+ auto it = std::find(m_windowStack.begin(), m_windowStack.end(), window);
+ const auto currentPosition = getWindowPositionPreference(it);
+
+ if (position == currentPosition) {
+ ;
+ } else if (currentPosition == PositionPreference::StayAboveTransientParent) {
+ // Keep position if possible
+ const bool isStayOnBottom ( it < m_regularWindowsBegin);
+ const bool isRegular( !isStayOnBottom && (it < m_alwaysOnTopWindowsBegin));
+ const bool isStayOnTop(!isStayOnBottom && !isRegular);
+
+ if (isStayOnBottom && (position == PositionPreference::StayOnBottom))
+ ;
+ else if (isRegular && (position == PositionPreference::Regular))
+ ;
+ else if (isStayOnTop && (position == PositionPreference::StayOnTop))
+ ;
+ else {
+ auto current = *it;
+ removeWindow(current, false);
+ pushWindow(current, position, false, false);
+ m_windowOrderChangedCallback();
+ }
+ } else {
+ const bool insertAtRegionBegin = (
+ (currentPosition != PositionPreference::StayAboveTransientParent) &&
+ (position != PositionPreference::StayAboveTransientParent) &&
+ ((currentPosition == PositionPreference::StayOnBottom) ||
+ (position == PositionPreference::StayOnTop)));
+
+ auto current = *it;
+ removeWindow(current, false);
+ pushWindow(current, position, insertAtRegionBegin, false);
+ m_windowOrderChangedCallback();
+ }
+ invariant();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::iterator QWasmWindowStack<Window>::begin()
+{
+ return m_windowStack.rbegin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::iterator QWasmWindowStack<Window>::end()
+{
+ return m_windowStack.rend();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_iterator QWasmWindowStack<Window>::begin() const
+{
+ return m_windowStack.rbegin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_iterator QWasmWindowStack<Window>::end() const
+{
+ return m_windowStack.rend();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_reverse_iterator
+QWasmWindowStack<Window>::rbegin() const
+{
+ return m_windowStack.begin();
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::const_reverse_iterator
+QWasmWindowStack<Window>::rend() const
+{
+ return m_windowStack.end();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::empty() const
+{
+ return m_windowStack.empty();
+}
+
+template <typename Window>
+size_t QWasmWindowStack<Window>::size() const
+{
+ return m_windowStack.size();
+}
+
+template <typename Window>
+Window *QWasmWindowStack<Window>::topWindow() const
+{
+ return m_windowStack.empty() ? nullptr : m_windowStack.last();
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::shouldBeAboveTransientParentFlags(Qt::WindowFlags flags) const
+{
+ if (flags.testFlag(Qt::Tool) ||
+ flags.testFlag(Qt::SplashScreen) ||
+ flags.testFlag(Qt::ToolTip) ||
+ flags.testFlag(Qt::Popup))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+template <typename Window>
+bool QWasmWindowStack<Window>::shouldBeAboveTransientParent(const Window *window) const
+{
+ if (!window->transientParent())
+ return false;
+
+ if (window->isModal())
+ return true;
+
+ if (shouldBeAboveTransientParentFlags(window->windowFlags()))
+ return true;
+
+ return false;
+}
+
+template <typename Window>
+typename QWasmWindowStack<Window>::PositionPreference
+QWasmWindowStack<Window>::getWindowPositionPreference(
+ typename StorageType::const_iterator windowIt, bool testStayAbove) const
+{
+ Window *window = *windowIt;
+ if (testStayAbove && shouldBeAboveTransientParent(window))
+ return PositionPreference::StayAboveTransientParent;
+ if (windowIt >= m_alwaysOnTopWindowsBegin)
+ return PositionPreference::StayOnTop;
+ if (windowIt >= m_regularWindowsBegin)
+ return PositionPreference::Regular;
+ return PositionPreference::StayOnBottom;
+}
+
+#endif /* QWASMWINDOWSTACK_INC */