summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/wasm/qwasmwindowstack.inc
diff options
context:
space:
mode:
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 */