diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmwindowstack.inc')
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmwindowstack.inc | 384 | 
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 */ | 
