0ce04b65创建于 2025年4月1日历史提交
/*
    SPDX-FileCopyrightText: 2020 Marco Martin <mart@kde.org>
    SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

import QtQuick
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami

/**
 * This component is intended to be used as root item for
 * KCMs with arbitrary content. Unlike SimpleKCM this does NOT
 * provide a scrollable view, The developer will have to manage
 * their own scrollviews.
 * Most of the times SimpleKCM should be used instead
 * @code
 * import QtQuick
 * import QtQuick.Controls as QQC2
 * import QtQuick.Layouts
 * import org.kde.kcmutils as KCMUtils
 *
 * KCMUtils.AbstractKCM {
 *     RowLayout {
 *         QQC2.ScrollView { }
 *         QQC2.ScrollView { }
 *     }
 *     footer: QQC2.ToolBar { }
 * }
 * @endcode
 * @inherits org.kde.kirigami.Page
 * @since 6.0
 */
Kirigami.Page {
    id: root

    readonly property int margins: 6 // Layout_ChildMarginWidth from Breeze

    /**
     * framedView: bool
     * Whether to use this component as the base of a "framed" KCM with an
     * inner scrollview that draws its own frame.
     * Default: true
     */
    property bool framedView: true

    /**
     * extraFooterTopPadding: bool
     * Whether the footer should have extra space and a separator line drawn
     * above it. Set this to true in a KCM with a custom footer and a ListView
     * immediately above it.
     * Default: false
     */
    property bool extraFooterTopPadding: false

    /**
     * headerPaddingEnabled: bool
     * Whether the contents of the header will have automatic padding around it.
     * Should be disabled when using an InlineMessage or custom content item in
     * the header that's intended to touch the window edges.
     * Default: true
     */
    property bool headerPaddingEnabled: true

    /**
     * footerPaddingEnabled: bool
     * Whether the contents of the footer will have automatic padding around it.
     * Should be disabled when using an InlineMessage or custom content item in
     * the footer that's intended to touch the window edges.
     * Default: true
     */
    property bool footerPaddingEnabled: true

    property bool sidebarMode: false

    function __itemVisible(item: Item): bool {
        return item !== null && item.visible && item.implicitHeight > 0;
    }

    function __headerContentVisible(): bool {
        return __itemVisible(headerParent.contentItem);
    }
    function __footerContentVisible(): bool {
        return __itemVisible(footerParent.contentItem);
    }

    // Deliberately not checking for __footerContentVisible because
    // we always want the footer line to be visible when the scrollview
    // doesn't have a frame of its own, because System Settings always
    // adds its own footer for the Apply, Help, and Defaults buttons
    function __headerSeparatorVisible(): bool {
        return !framedView && __headerContentVisible();
    }
    function __footerSeparatorVisible(): bool {
        return !framedView && extraFooterTopPadding;
    }

    title: (typeof kcm !== "undefined") ? kcm.name : ""

    // Make pages fill the whole view by default
    Kirigami.ColumnView.fillWidth: sidebarMode
                                   ? Kirigami.ColumnView.view
                                         && (Kirigami.ColumnView.view.width < Kirigami.Units.gridUnit * 36
                                             || Kirigami.ColumnView.index >= Kirigami.ColumnView.view.count - 1)
                                   : true

    padding: 0
    topPadding: framedView && !__headerContentVisible() ? margins : 0
    leftPadding: undefined
    rightPadding: undefined
    bottomPadding: framedView && !__footerContentVisible() ? margins : 0
    verticalPadding: undefined
    horizontalPadding: framedView ? margins : 0

    header: Column {
        Kirigami.Padding {
            id: headerParent

            anchors {
                left: parent.left
                right: parent.right
            }

            height: root.__headerContentVisible() ? undefined : 0
            padding: root.headerPaddingEnabled ? root.margins : 0
        }

        // When the scrollview isn't drawing its own frame, we need to add a
        // line below the header (when visible) to separate it from the view
        Kirigami.Separator {
            anchors {
                left: parent.left
                right: parent.right
            }

            visible: root.__headerSeparatorVisible()
        }
    }

    // View background, shown when the scrollview isn't drawing its own frame
    Rectangle {
        anchors.fill: parent
        visible: !root.framedView
        Kirigami.Theme.colorSet: Kirigami.Theme.View
        Kirigami.Theme.inherit: false
        color: Kirigami.Theme.backgroundColor
    }

    footer: Column {
        // When the scrollview isn't drawing its own frame, we need to add a
        // line above the footer ourselves to separate it from the view
        Kirigami.Separator {
            anchors {
                left: parent.left
                right: parent.right
            }

            visible: root.__footerSeparatorVisible()
        }

        Kirigami.Padding {
            id: footerParent

            anchors {
                left: parent.left
                right: parent.right
            }

            height: root.__footerContentVisible() ? undefined : 0
            padding: root.footerPaddingEnabled ? root.margins : 0
        }
    }

    function __swapContentIntoContainer(property: string, container: Item): void {
        const content = this[property];
        const rootContainer = container.parent;

        if (content && content !== rootContainer) {
            // Revert the effect of repeated onHeaderChanged invocations
            // during initialization in Page super-type.
            content.anchors.top = undefined;

            this[property] = rootContainer;
            container.contentItem = content;
        }
    }

    function __adoptOverlaySheets(): void {
        // Search overlaysheets in contentItem, parent to root if found
        for (const object of contentItem.data) {
            if (object instanceof Kirigami.OverlaySheet) {
                if (object.parent === null) {
                    object.parent = this;
                }
                data.push(object);
            }
        }
    }

    Component.onCompleted: {
        __swapContentIntoContainer("header", headerParent);
        __swapContentIntoContainer("footer", footerParent);
        __adoptOverlaySheets();
    }
}