Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
ecm_add_qml_module(kcmutilsqmlplugin URI "org.kde.kcmutils" VERSION 1.0 DEPENDENCIES QtQuick org.kde.kcmutils.private org.kde.config GENERATE_PLUGIN_SOURCE)
|
||||
target_sources(kcmutilsqmlplugin
|
||||
PRIVATE
|
||||
settingstateproxy.cpp
|
||||
kcmlauncher.cpp
|
||||
types.h
|
||||
)
|
||||
target_link_libraries(kcmutilsqmlplugin
|
||||
PRIVATE
|
||||
KF6::KIOGui
|
||||
Qt6::Qml
|
||||
Qt6::Quick
|
||||
kcmutils_proxy_model
|
||||
kcmutils_logging_STATIC
|
||||
KF6KCMUtilsQuick
|
||||
)
|
||||
|
||||
ecm_target_qml_sources(kcmutilsqmlplugin SOURCES
|
||||
components/PluginDelegate.qml
|
||||
components/PluginSelector.qml
|
||||
components/AbstractKCM.qml
|
||||
components/ContextualHelpButton.qml
|
||||
components/GridDelegate.qml
|
||||
components/GridView.qml
|
||||
components/GridViewKCM.qml
|
||||
components/ScrollView.qml
|
||||
components/ScrollViewKCM.qml
|
||||
components/SettingHighlighter.qml
|
||||
components/SettingStateBinding.qml
|
||||
components/SimpleKCM.qml
|
||||
)
|
||||
ecm_target_qml_sources(kcmutilsqmlplugin PATH private SOURCES
|
||||
components/private/AboutPlugin.qml
|
||||
components/private/GridDelegateMenu.qml
|
||||
components/private/GridViewInternal.qml
|
||||
)
|
||||
ecm_finalize_qml_module(kcmutilsqmlplugin DESTINATION ${KDE_INSTALL_QMLDIR})
|
||||
|
||||
ecm_add_qml_module(kcmutilsprivateqmlplugin URI "org.kde.kcmutils.private" DEPENDENCIES QtCore QtQuick GENERATE_PLUGIN_SOURCE)
|
||||
target_sources(kcmutilsprivateqmlplugin PRIVATE private_types.h settinghighlighterprivate.cpp)
|
||||
target_link_libraries(kcmutilsprivateqmlplugin PRIVATE Qt6::Quick kcmutils_proxy_model)
|
||||
ecm_finalize_qml_module(kcmutilsprivateqmlplugin DESTINATION ${KDE_INSTALL_QMLDIR})
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.7 as QQC2
|
||||
import QtQuick.Window 2.15
|
||||
import org.kde.kirigami 2.3 as Kirigami
|
||||
|
||||
@Deprecated { reason: "Use the version in Kirigami instead!" }
|
||||
QQC2.Button {
|
||||
id: root
|
||||
|
||||
icon.name: "help-contextual"
|
||||
flat: true
|
||||
property alias toolTipText: toolTip.text
|
||||
property var toolTipVisible: false
|
||||
|
||||
onReleased: {
|
||||
toolTipVisible ? toolTip.delay = Kirigami.Units.toolTipDelay : toolTip.delay = 0;
|
||||
toolTipVisible = !toolTipVisible;
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
toolTip.delay = Kirigami.Units.toolTipDelay;
|
||||
toolTipVisible = false;
|
||||
}
|
||||
Layout.maximumHeight: parent.height
|
||||
QQC2.ToolTip {
|
||||
id: toolTip
|
||||
implicitWidth: Math.min(21 * Kirigami.Units.gridUnit, root.Window.width) // Wikipedia says anything between 45 and 75 characters per line is acceptable. 21 * Kirigami.Units.gridUnit feels right.
|
||||
visible: parent.hovered || parent.toolTipVisible
|
||||
onVisibleChanged: {
|
||||
if (!visible && parent.toolTipVisible) {
|
||||
parent.toolTipVisible = false;
|
||||
delay = Kirigami.Units.toolTipDelay;
|
||||
}
|
||||
}
|
||||
timeout: -1
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.WhatsThisCursor
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
Accessible.name: i18ndc("kcmutils6", "@action:button", "Show Contextual Help")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Templates as T
|
||||
import org.kde.kirigami as Kirigami
|
||||
import "private" as Private
|
||||
|
||||
/**
|
||||
* Base delegate for KControlmodules based on Grid views of thumbnails
|
||||
* Use the onClicked signal handler for managing the main action when
|
||||
* the user clicks on the thumbnail
|
||||
* @inherits QtQuick.Templates.ItemDelegate
|
||||
*/
|
||||
T.ItemDelegate {
|
||||
id: delegate
|
||||
|
||||
/**
|
||||
* toolTip: string
|
||||
* string for a tooltip for the whole delegate
|
||||
*/
|
||||
property string toolTip
|
||||
|
||||
/**
|
||||
* subtitle: string
|
||||
* optional string for the text to show below the main label
|
||||
*/
|
||||
property string subtitle
|
||||
|
||||
/**
|
||||
* thumbnail: Item
|
||||
* the item actually implementing the thumbnail: the visualization is up to the implementation
|
||||
*/
|
||||
property alias thumbnail: thumbnailArea.data
|
||||
|
||||
/**
|
||||
* thumbnailAvailable: bool
|
||||
* Set it to true when a thumbnail is actually available: when false,
|
||||
* only an icon will be shown instead of the actual thumbnail
|
||||
* ("edit-none" if pluginName is "None", otherwise it uses "view-preview").
|
||||
*/
|
||||
property bool thumbnailAvailable: false
|
||||
|
||||
/**
|
||||
* actions: list<Kirigami.Action>
|
||||
* A list of extra actions for the thumbnails. They will be shown as
|
||||
* icons on the bottom-right corner of the thumbnail on mouse over
|
||||
*/
|
||||
property list<Kirigami.Action> actions
|
||||
|
||||
width: GridView.view.cellWidth
|
||||
height: GridView.view.cellHeight
|
||||
hoverEnabled: !Kirigami.Settings.isMobile
|
||||
|
||||
Accessible.description: {
|
||||
if (toolTip.length === 0) {
|
||||
return subtitle;
|
||||
} else if (subtitle.length === 0) {
|
||||
return toolTip;
|
||||
}
|
||||
return `${subtitle}; ${toolTip}`
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: event => thumbnail.trigger()
|
||||
Keys.onMenuPressed: event => thumbnail.trigger()
|
||||
Keys.onSpacePressed: event => thumbnail.trigger()
|
||||
|
||||
Kirigami.ShadowedRectangle {
|
||||
id: thumbnail
|
||||
anchors {
|
||||
centerIn: parent
|
||||
verticalCenterOffset: Math.ceil(-labelLayout.height / 2)
|
||||
}
|
||||
width: Kirigami.Settings.isMobile ? delegate.width - Kirigami.Units.gridUnit : Math.min(delegate.GridView.view.implicitCellWidth, delegate.width - Kirigami.Units.gridUnit)
|
||||
height: Kirigami.Settings.isMobile ? Math.round((delegate.width - Kirigami.Units.gridUnit) / 1.6)
|
||||
: Math.min(delegate.GridView.view.implicitCellHeight - Kirigami.Units.gridUnit * 3,
|
||||
delegate.height - Kirigami.Units.gridUnit)
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
shadow.xOffset: 0
|
||||
shadow.yOffset: 2
|
||||
shadow.size: 10
|
||||
shadow.color: Qt.rgba(0, 0, 0, 0.3)
|
||||
|
||||
color: {
|
||||
if (delegate.GridView.isCurrentItem) {
|
||||
if (delegate.enabled && delegate.GridView.view.neutralHighlight) {
|
||||
return Kirigami.Theme.neutralTextColor;
|
||||
}
|
||||
return Kirigami.Theme.highlightColor;
|
||||
}
|
||||
if (delegate.enabled && delegate.hovered) {
|
||||
// Match appearance of hovered list items
|
||||
return Qt.alpha(Kirigami.Theme.highlightColor, 0.5);
|
||||
}
|
||||
return Kirigami.Theme.backgroundColor;
|
||||
}
|
||||
|
||||
// The menu is only used for keyboard navigation, so no need to always load
|
||||
// it. This speeds up the compilation of GridDelegate.
|
||||
property Private.GridDelegateMenu menu
|
||||
|
||||
function trigger() {
|
||||
if (!menu) {
|
||||
const component = Qt.createComponent("private/GridDelegateMenu.qml");
|
||||
menu = component.createObject(delegate);
|
||||
component.destroy();
|
||||
}
|
||||
|
||||
menu.trigger();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: thumbnailArea
|
||||
|
||||
radius: Math.round(Kirigami.Units.cornerRadius / 2)
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
// "None/There's nothing here" indicator
|
||||
Kirigami.Icon {
|
||||
visible: !delegate.thumbnailAvailable
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: Kirigami.Units.iconSizes.large
|
||||
source: typeof pluginName === "string" && pluginName === "None" ? "edit-none" : "view-preview"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Kirigami.Units.smallSpacing
|
||||
bottom: parent.bottom
|
||||
bottomMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
// Always show above thumbnail content
|
||||
z: 9999
|
||||
|
||||
visible: delegate.actions.length > 0 && (Kirigami.Settings.isMobile || delegate.hovered || delegate.GridView.isCurrentItem)
|
||||
|
||||
Repeater {
|
||||
model: delegate.actions
|
||||
delegate: QQC2.Button {
|
||||
required property Kirigami.Action modelData
|
||||
|
||||
icon.name: modelData.icon.name
|
||||
text: modelData.text || modelData.tooltip
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
enabled: modelData.enabled
|
||||
visible: modelData.visible
|
||||
|
||||
activeFocusOnTab: false
|
||||
|
||||
onClicked: modelData.trigger()
|
||||
|
||||
QQC2.ToolTip.visible: (Kirigami.Settings.tabletMode ? pressed : hovered) && (QQC2.ToolTip.text !== "")
|
||||
QQC2.ToolTip.delay: Kirigami.Settings.tabletMode ? Qt.styleHints.mousePressAndHoldInterval : Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.text: text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: labelLayout
|
||||
|
||||
spacing: 0
|
||||
height: Kirigami.Units.gridUnit * 2
|
||||
anchors {
|
||||
left: thumbnail.left
|
||||
right: thumbnail.right
|
||||
top: thumbnail.bottom
|
||||
topMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: title
|
||||
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
text: delegate.text
|
||||
color: enabled ? Kirigami.Theme.textColor : Kirigami.Theme.disabledTextColor
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
font.bold: delegate.GridView.isCurrentItem
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
QQC2.Label {
|
||||
id: caption
|
||||
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: delegate.subtitle.length > 0
|
||||
opacity: 0.6
|
||||
text: delegate.subtitle
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
font.bold: delegate.GridView.isCurrentItem
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: Math.max(title.paintedWidth, caption.paintedWidth)
|
||||
Layout.maximumWidth: labelLayout.width // Otherwise labels can overflow
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
color: Kirigami.Theme.highlightColor
|
||||
|
||||
opacity: delegate.visualFocus ? 1 : 0
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true; Layout.fillHeight: true; }
|
||||
}
|
||||
|
||||
QQC2.ToolTip.visible: (Kirigami.Settings.tabletMode ? pressed : hovered) && (QQC2.ToolTip.text !== "")
|
||||
QQC2.ToolTip.delay: Kirigami.Settings.tabletMode ? Qt.styleHints.mousePressAndHoldInterval : Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.text: {
|
||||
const parts = [];
|
||||
if (delegate.toolTip.length > 0) {
|
||||
parts.push(delegate.toolTip);
|
||||
}
|
||||
if (title.truncated) {
|
||||
parts.push(title.text);
|
||||
}
|
||||
if (caption.truncated) {
|
||||
parts.push(caption.text);
|
||||
}
|
||||
return parts.join("\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import org.kde.kirigami as Kirigami
|
||||
import "private" as Private
|
||||
|
||||
/**
|
||||
* A ScrollView containing a GridView, with the default behavior about
|
||||
* sizing and background as recommended by the user interface guidelines
|
||||
* For most KControl modules, it's recommended to use instead the GridViewKCM
|
||||
* component as the root element of your module.
|
||||
* @see GridViewKCM
|
||||
*/
|
||||
QQC2.ScrollView {
|
||||
id: scroll
|
||||
|
||||
/**
|
||||
* view: GridView
|
||||
* Exposes the internal GridView: in order to set a model or a delegate to it,
|
||||
* use the following code:
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
*
|
||||
* KCMUtils.GridView {
|
||||
* view.model: kcm.model
|
||||
* view.delegate: KCMUtils.GridDelegate { }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
property alias view: view
|
||||
property bool framedView: true
|
||||
|
||||
activeFocusOnTab: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
Component.onCompleted: {
|
||||
if (background) {
|
||||
background.visible = Qt.binding(() => framedView);
|
||||
}
|
||||
}
|
||||
|
||||
Private.GridViewInternal {
|
||||
id: view
|
||||
}
|
||||
QQC2.ScrollBar.horizontal.visible: false
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kcmutils as KCMutils
|
||||
|
||||
/**
|
||||
* This component is intended to be used as the root item for KCMs that are based upon
|
||||
* a grid view of thumbnails, such as the theme or wallpaper selectors.
|
||||
* It contains a GridView as its main item.
|
||||
* It is possible to specify a header and footer component.
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMutils
|
||||
*
|
||||
* KCMutils.GridViewKCM {
|
||||
* header: Item { }
|
||||
* view.model: kcm.model
|
||||
* view.delegate: KCMutils.GridDelegate { }
|
||||
* footer: Item { }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
KCMutils.AbstractKCM {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* view: GridView
|
||||
* Exposes the internal GridView: in order to set a model or a delegate to it,
|
||||
* use the following code:
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMutils
|
||||
*
|
||||
* KCMutils.GridViewKCM {
|
||||
* view.model: kcm.model
|
||||
* view.delegate: KCMutils.GridDelegate { }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
property alias view: scroll.view
|
||||
|
||||
/**
|
||||
* framedView: bool
|
||||
* Whether to draw a frame around the KCM's inner scrollable grid view.
|
||||
* Default: false
|
||||
*/
|
||||
framedView: false
|
||||
|
||||
implicitWidth: {
|
||||
let width = 0;
|
||||
|
||||
// Show three columns at once, every column occupies implicitCellWidth + Units.gridUnit
|
||||
width += 3 * (view.implicitCellWidth + Kirigami.Units.gridUnit);
|
||||
|
||||
const scrollBar = scroll.QQC2.ScrollBar.vertical;
|
||||
width += scrollBar.width + scrollBar.leftPadding + scrollBar.rightPadding;
|
||||
|
||||
width += scroll.leftPadding + scroll.rightPadding
|
||||
width += root.leftPadding + root.rightPadding;
|
||||
|
||||
return width;
|
||||
}
|
||||
implicitHeight: view.implicitCellHeight * 3 + (header ? header.height : 0) + (footer ? footer.height : 0) + Kirigami.Units.gridUnit
|
||||
|
||||
flickable: scroll.view
|
||||
|
||||
KCMutils.GridView {
|
||||
id: scroll
|
||||
anchors.fill: parent
|
||||
framedView: root.framedView
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Templates as T
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kcmutils as KCMUtils
|
||||
|
||||
/// @since 6.0, this got renamed from KPluginDelegate to PluginDelegate
|
||||
Kirigami.CheckSubtitleDelegate {
|
||||
id: listItem
|
||||
|
||||
// Note: when PluginDelegate is embedded in a more complex delegate, model
|
||||
// object should be passed down explicitly, but it also means that it may
|
||||
// become null right before delegate's destruction.
|
||||
required property var model
|
||||
|
||||
property list<T.Action> additionalActions
|
||||
|
||||
property alias leading: leadingProxy.target
|
||||
|
||||
readonly property bool enabledByDefault: model?.enabledByDefault ?? false
|
||||
readonly property var metaData: model?.metaData
|
||||
readonly property bool configureVisible: model?.config.isValid ?? false
|
||||
|
||||
signal configTriggered()
|
||||
|
||||
// Let Optional chaining (?.) operator fall back to `undefined` which resets the width to an implicit value.
|
||||
width: ListView.view?.width
|
||||
|
||||
icon.name: model?.icon ?? ""
|
||||
text: model?.name ?? ""
|
||||
subtitle: model?.description ?? ""
|
||||
checked: model?.enabled ?? false
|
||||
|
||||
// TODO: It should be possible to disable this
|
||||
onToggled: model.enabled = checked
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
// Used by CheckSubtitleDelegate through duck-typing
|
||||
readonly property alias truncated: titleSubtitle.truncated
|
||||
|
||||
LayoutItemProxy {
|
||||
id: leadingProxy
|
||||
visible: target !== null
|
||||
}
|
||||
|
||||
Kirigami.IconTitleSubtitle {
|
||||
id: titleSubtitle
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: Math.ceil(implicitWidth)
|
||||
|
||||
icon: icon.fromControlsIcon(listItem.icon)
|
||||
title: listItem.text
|
||||
subtitle: listItem.subtitle
|
||||
reserveSpaceForSubtitle: true
|
||||
}
|
||||
|
||||
Kirigami.ActionToolBar {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
alignment: Qt.AlignRight
|
||||
actions: [infoAction, configureAction, ...listItem.additionalActions]
|
||||
}
|
||||
}
|
||||
|
||||
KCMUtils.SettingHighlighter {
|
||||
target: listItem.indicator
|
||||
highlight: listItem.checked !== listItem.enabledByDefault
|
||||
}
|
||||
|
||||
// Take care of displaying the actions
|
||||
readonly property Kirigami.Action __infoAction: Kirigami.Action {
|
||||
id: infoAction
|
||||
|
||||
icon.name: "help-about-symbolic"
|
||||
text: i18ndc("kcmutils6", "@info:tooltip", "About")
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
onTriggered: {
|
||||
const aboutDialog = (listItem.ListView.view ?? listItem.parent.ListView.view).__aboutDialog;
|
||||
aboutDialog.metaDataInfo = listItem.metaData;
|
||||
aboutDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Kirigami.Action __configureAction: Kirigami.Action {
|
||||
id: configureAction
|
||||
|
||||
visible: listItem.configureVisible
|
||||
enabled: listItem.checked
|
||||
icon.name: "configure-symbolic"
|
||||
text: i18ndc("kcmutils6", "@info:tooltip", "Configure…")
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
onTriggered: listItem.configTriggered()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.kcmutils.private as KCMUtilsPrivate
|
||||
import "private" as Private
|
||||
|
||||
/**
|
||||
* ListView for showing plugins with their info and configuration.
|
||||
* If extra butons should be added, a custom KPluginDelegate with the additionalActions
|
||||
* property should be defined.
|
||||
*
|
||||
* @since 6.0, this got renamed from KPluginSelector to PluginSelector
|
||||
*/
|
||||
ListView {
|
||||
id: pluginSelector
|
||||
// KPluginModel which contains the plugins that should be displayed
|
||||
required property QtObject sourceModel
|
||||
// Query that is typed into the search field. Ideally, this is part of the KCM header
|
||||
property var query
|
||||
// PluginDelegate should be used with this, it contains an ActionToolBar that is incredibly expensive to construct,
|
||||
// make sure to cache delegates a fair amount right out of the box.
|
||||
cacheBuffer: parent.height * 2
|
||||
|
||||
clip: true
|
||||
|
||||
// Don't select anything by default as selection is not used here
|
||||
currentIndex: -1
|
||||
|
||||
model: KCMUtilsPrivate.ProxyModel {
|
||||
id: proxyModel
|
||||
model: pluginSelector.sourceModel
|
||||
query: pluginSelector.query ?? ""
|
||||
}
|
||||
|
||||
delegate: PluginDelegate {
|
||||
}
|
||||
|
||||
section.property: "category"
|
||||
section.delegate: Kirigami.ListSectionHeader {
|
||||
width: pluginSelector.width
|
||||
text: section
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
id: internalAboutDialog
|
||||
parent: pluginSelector.Window.window?.contentItem ?? null
|
||||
property var metaDataInfo
|
||||
|
||||
Loader {
|
||||
active: internalAboutDialog.metaDataInfo !== undefined
|
||||
sourceComponent: ColumnLayout {
|
||||
Private.AboutPlugin {
|
||||
metaData: internalAboutDialog.metaDataInfo
|
||||
Layout.maximumWidth: Math.min(Kirigami.Units.gridUnit * 30, Math.round(pluginSelector.width * 0.8))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only for internal usage in KPluginDelegate!
|
||||
property var __aboutDialog: internalAboutDialog
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Kirigami.Units.gridUnit * 8)
|
||||
active: pluginSelector.count === 0 && !startupTimer.running
|
||||
opacity: active && status === Loader.Ready ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
icon.name: "edit-none"
|
||||
text: pluginSelector.query && pluginSelector.query.length > 0 ? i18nd("kcmutils6", "No matches") : i18nd("kcmutils6", "No plugins found")
|
||||
}
|
||||
}
|
||||
|
||||
// The view can take a bit of time to initialize itself when the KCM first
|
||||
// loads, during which time count is 0, which would cause the placeholder
|
||||
// message to appear for a moment and then disappear. To prevent this, let's
|
||||
// suppress it appearing for a moment after the KCM loads.
|
||||
Timer {
|
||||
id: startupTimer
|
||||
interval: Kirigami.Units.humanMoment
|
||||
running: false
|
||||
}
|
||||
Component.onCompleted: {
|
||||
startupTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 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
|
||||
|
||||
/**
|
||||
* A ScrollView containing a GridView, with the default behavior about
|
||||
* sizing and background as recommended by the user interface guidelines
|
||||
* For most KControl modules, it's recommended to use instead the GridViewKCM
|
||||
* component as the root element of your module.
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
*
|
||||
* KCMUtils.ScrollView {
|
||||
* view: ListView { }
|
||||
* }
|
||||
* @endcode
|
||||
* @see GridViewKCM
|
||||
*/
|
||||
QQC2.ScrollView {
|
||||
id: scroll
|
||||
|
||||
/**
|
||||
* view: GridView
|
||||
* Exposes the internal flickable
|
||||
*/
|
||||
property Flickable view
|
||||
property bool framedView: true
|
||||
|
||||
contentItem: view
|
||||
onViewChanged: {
|
||||
view.parent = scroll;
|
||||
if (!view.KeyNavigation.up) {
|
||||
view.KeyNavigation.up = Qt.binding(() => root.globalToolBarItem);
|
||||
}
|
||||
}
|
||||
|
||||
activeFocusOnTab: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
Component.onCompleted: {
|
||||
if (background) {
|
||||
background.visible = Qt.binding(() => framedView);
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ScrollBar.horizontal.visible: false
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kcmutils as KCMUtils
|
||||
|
||||
/**
|
||||
* This component is intended to be used as the root item for KCMs that are based upon a list view or another vertical flickable.
|
||||
* It contains a ScrollView as its main item.
|
||||
* It is possible to specify a header and footer component.
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
*
|
||||
* KCMUtils.ScrollViewKCM {
|
||||
* header: Item { }
|
||||
* view: ListView { }
|
||||
* footer: Item { }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
KCMUtils.AbstractKCM {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* view: ScrollView
|
||||
* Exposes the internal flickable
|
||||
*/
|
||||
property alias view: scroll.view
|
||||
|
||||
/**
|
||||
* framedView: bool
|
||||
* Whether to draw a frame around the KCM's inner scrollable list view.
|
||||
* Default: false
|
||||
*
|
||||
* @since 5.90
|
||||
*/
|
||||
framedView: false
|
||||
|
||||
onViewChanged: {
|
||||
if (view) {
|
||||
// Deliberately don't take separators into account, because those are opaque anyway
|
||||
view.clip = Qt.binding(() => __headerContentVisible() || __footerContentVisible());
|
||||
}
|
||||
}
|
||||
|
||||
KCMUtils.ScrollView {
|
||||
id: scroll
|
||||
anchors.fill: parent
|
||||
framedView: root.framedView
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
SPDX-FileCopyrightText: 2020 David Redondo <kde@david.redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kcmutils.private as KCMUtilsPrivate
|
||||
|
||||
/**
|
||||
* SettingHighlighter automatically impacts the representation of an item based on
|
||||
* the value of a setting. When you are using this item you need to manually
|
||||
* manage whether the highlighting is enabled or not. For a higher level component
|
||||
* see KCM.SettingStateBinding which will manage the state of the Item
|
||||
* @since 6.0
|
||||
*/
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
active: typeof kcm !== "undefined" && root.target !== null
|
||||
|
||||
/**
|
||||
* target: Item
|
||||
* The graphical element whose appearance will be altered.
|
||||
* If target is not set, it will try to find the visual parent item
|
||||
*/
|
||||
property Item target: root.parent
|
||||
|
||||
/**
|
||||
* highlight: bool
|
||||
* Whether the target will be highlighted.
|
||||
*/
|
||||
property bool highlight: false
|
||||
|
||||
sourceComponent: KCMUtilsPrivate.SettingHighlighterPrivate {
|
||||
id: helper
|
||||
highlight: root.highlight
|
||||
target: root.target
|
||||
defaultIndicatorVisible: kcm.defaultsIndicatorsVisible
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import org.kde.kcmutils as KCMUtils
|
||||
import org.kde.kcmutils.private as KCMUtilsPrivate
|
||||
|
||||
/**
|
||||
* SettingStateBinding automatically impacts the representation
|
||||
* of an item based on the state of a setting. It will disable
|
||||
* the item if the setting is immutable and use a visual indicator
|
||||
* for the state of the setting.
|
||||
*
|
||||
* This is a higher level convenience wrapper for KCMUtils.SettingStateProxy
|
||||
* and KCMUtils.SettingStateIndicator.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
active: typeof kcm !== "undefined" && root.target !== null
|
||||
|
||||
/**
|
||||
* target: Item
|
||||
* The graphical element whose state we want to manage based on a setting
|
||||
* If target is not set, it will try to find the visual parent item
|
||||
*/
|
||||
property Item target: root.parent
|
||||
|
||||
/**
|
||||
* configObject: KCoreConfigSkeleton
|
||||
* The config object which will be monitored for setting state changes
|
||||
*/
|
||||
property alias configObject: settingState.configObject
|
||||
|
||||
/**
|
||||
* settingName: string
|
||||
* The name of the setting in the config object to be monitored
|
||||
*/
|
||||
property alias settingName: settingState.settingName
|
||||
|
||||
/**
|
||||
* extraEnabledConditions: bool
|
||||
* SettingStateBinding will manage the enabled property of the target
|
||||
* based on the immutability state of the setting it represents. But,
|
||||
* sometimes that enabled state needs to bind to other properties as
|
||||
* well to be computed properly. This extra condition will thus be
|
||||
* combined with the immutability state of the setting to determine
|
||||
* the effective enabled state of the target.
|
||||
*/
|
||||
property bool extraEnabledConditions: true
|
||||
|
||||
/**
|
||||
* nonDefaultHighlightVisible: bool
|
||||
* Expose whether the non default highlight is visible.
|
||||
* Allow one to implement highlight with custom items.
|
||||
*/
|
||||
readonly property bool nonDefaultHighlightVisible: root.active && root.item.highlight && kcm.defaultsIndicatorsVisible
|
||||
|
||||
Binding {
|
||||
when: root.active
|
||||
target: root.target
|
||||
property: "enabled"
|
||||
value: extraEnabledConditions && !settingState.immutable
|
||||
}
|
||||
|
||||
KCMUtils.SettingStateProxy {
|
||||
id: settingState
|
||||
}
|
||||
|
||||
sourceComponent: KCMUtilsPrivate.SettingHighlighterPrivate {
|
||||
id: helper
|
||||
defaultIndicatorVisible: kcm.defaultsIndicatorsVisible
|
||||
highlight: !settingState.defaulted
|
||||
target: root.target
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
|
||||
|
||||
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. Often a Kirigami.FormLayout
|
||||
* is used as main element.
|
||||
* It is possible to specify a header and footer component.
|
||||
* @code
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
* import org.kde.kirigami as Kirigami
|
||||
*
|
||||
* KCMUtils.SimpleKCM {
|
||||
* Kirigami.FormLayout {
|
||||
* TextField {
|
||||
* Kirigami.FormData.label: "Label:"
|
||||
* }
|
||||
* TextField {
|
||||
* Kirigami.FormData.label: "Label:"
|
||||
* }
|
||||
* }
|
||||
* footer: Item {...}
|
||||
* }
|
||||
* @endcode
|
||||
* @inherits org.kde.kirigami.ScrollablePage
|
||||
*/
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
readonly property int margins: 6 // Layout_ChildMarginWidth from Breeze
|
||||
|
||||
/**
|
||||
* extraFooterTopPadding: bool
|
||||
* @deprecated unused
|
||||
* 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: false
|
||||
*/
|
||||
property bool headerPaddingEnabled: false
|
||||
|
||||
function __itemVisible(item: Item): bool {
|
||||
return item !== null && item.visible && item.implicitHeight > 0;
|
||||
}
|
||||
|
||||
function __headerContentVisible(): bool {
|
||||
return __itemVisible(headerParent.contentItem);
|
||||
}
|
||||
|
||||
property bool __flickableOverflows: flickable.contentHeight + flickable.topMargin + flickable.bottomMargin > flickable.height
|
||||
|
||||
// Context properties are not reliable
|
||||
title: (typeof kcm !== "undefined") ? kcm.name : ""
|
||||
|
||||
// Make pages fill the whole view by default
|
||||
Kirigami.ColumnView.fillWidth: true
|
||||
|
||||
property bool sidebarMode: false
|
||||
|
||||
topPadding: margins
|
||||
leftPadding: margins
|
||||
rightPadding: margins
|
||||
bottomPadding: margins
|
||||
|
||||
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 header is visible, we need to add a line below to separate
|
||||
// it from the view
|
||||
Kirigami.Separator {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
visible: root.__headerContentVisible()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
__adoptOverlaySheets();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
|
||||
SPDX-FileCopyrightText: 2020 David Redondo <kde@david-redondo.de>
|
||||
SPDX-FileCopyrightText: 2022 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
/**
|
||||
* A copy of Kirigami.AboutPage adapted to KPluginMetadata instead of KAboutData
|
||||
*/
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property var metaData
|
||||
|
||||
// Icon, name, version, and description
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
source: root.metaData.iconName
|
||||
fallback: "application-x-plasma"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: root.metaData.version ? i18ndc("kcmutils6", "Plugin name and plugin version", "%1 %2", root.metaData.name, root.metaData.version) : root.metaData.name
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
level: 2
|
||||
text: root.metaData.description
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
|
||||
// Copyright
|
||||
Kirigami.Heading {
|
||||
text: i18nd("kcmutils6", "Copyright")
|
||||
}
|
||||
QQC2.Label {
|
||||
Layout.leftMargin: Kirigami.Units.gridUnit
|
||||
text: root.metaData.copyrightText
|
||||
visible: text.length > 0
|
||||
}
|
||||
Kirigami.UrlButton {
|
||||
Layout.leftMargin: Kirigami.Units.gridUnit
|
||||
url: root.metaData.website ? root.metaData.website : ""
|
||||
visible: url.length > 0
|
||||
}
|
||||
|
||||
|
||||
// License
|
||||
RowLayout {
|
||||
QQC2.Label {
|
||||
text: i18nd("kcmutils6", "License:")
|
||||
}
|
||||
Kirigami.LinkButton {
|
||||
text: root.metaData.license
|
||||
onClicked: {
|
||||
licenseSheet.text = root.metaData.licenseText
|
||||
licenseSheet.title = root.metaData.license
|
||||
licenseSheet.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Authors, if any
|
||||
Item {
|
||||
implicitHeight: Kirigami.Units.largeSpacing
|
||||
visible: repAuthors.visible
|
||||
}
|
||||
Kirigami.Heading {
|
||||
text: i18nd("kcmutils6", "Authors")
|
||||
visible: repAuthors.visible
|
||||
}
|
||||
Repeater {
|
||||
id: repAuthors
|
||||
visible: count > 0
|
||||
model: root.metaData.authors
|
||||
delegate: personDelegate
|
||||
}
|
||||
|
||||
|
||||
// Credits, if any
|
||||
Item {
|
||||
implicitHeight: Kirigami.Units.largeSpacing
|
||||
visible: repCredits.visible
|
||||
}
|
||||
Kirigami.Heading {
|
||||
text: i18nd("kcmutils6", "Credits")
|
||||
visible: repCredits.visible
|
||||
}
|
||||
Repeater {
|
||||
id: repCredits
|
||||
visible: count > 0
|
||||
model: root.metaData.otherContributors
|
||||
delegate: personDelegate
|
||||
}
|
||||
|
||||
|
||||
// Translators, if any
|
||||
Item {
|
||||
implicitHeight: Kirigami.Units.largeSpacing
|
||||
visible: repTranslators.visible
|
||||
}
|
||||
Kirigami.Heading {
|
||||
text: i18nd("kcmutils6", "Translators")
|
||||
visible: repTranslators.visible
|
||||
}
|
||||
Repeater {
|
||||
id: repTranslators
|
||||
visible: count > 0
|
||||
model: root.metaData.translators
|
||||
delegate: personDelegate
|
||||
}
|
||||
|
||||
|
||||
Component {
|
||||
id: personDelegate
|
||||
|
||||
RowLayout {
|
||||
height: implicitHeight + (Kirigami.Units.largeSpacing)
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: modelData.name
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
visible: modelData.emailAddress
|
||||
icon.name: "mail-sent"
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: i18nd("kcmutils6", "Send an email to %1", modelData.emailAddress)
|
||||
onClicked: Qt.openUrlExternally("mailto:%1".arg(modelData.emailAddress))
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
visible: modelData.webAddress
|
||||
icon.name: "globe"
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: modelData.webAddress
|
||||
onClicked: Qt.openUrlExternally(modelData.webAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Dialog {
|
||||
id: licenseSheet
|
||||
property alias text: licenseLabel.text
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
contentItem: QQC2.ScrollView {
|
||||
id: scroll
|
||||
Component.onCompleted: {
|
||||
if (background) {
|
||||
background.visible = true;
|
||||
}
|
||||
}
|
||||
Flickable {
|
||||
id: flickable
|
||||
contentWidth: width
|
||||
contentHeight: licenseLabel.contentHeight
|
||||
clip: true
|
||||
QQC2.Label {
|
||||
id: licenseLabel
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Templates as T
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
QQC2.Menu {
|
||||
id: menu
|
||||
|
||||
function trigger() {
|
||||
parent.clicked();
|
||||
if (parent.actions.length > 0) {
|
||||
popup(parent, thumbnail.x, thumbnail.y + thumbnail.height);
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: parent.forceActiveFocus()
|
||||
|
||||
Repeater {
|
||||
model: menu.parent.actions
|
||||
delegate: QQC2.MenuItem {
|
||||
required property Kirigami.Action modelData
|
||||
|
||||
text: modelData.text || modelData.tooltip
|
||||
icon.name: modelData.icon.name
|
||||
enabled: modelData.enabled
|
||||
visible: modelData.visible
|
||||
|
||||
onTriggered: modelData.trigger()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
GridView {
|
||||
id: view
|
||||
|
||||
property int implicitCellWidth: Kirigami.Units.gridUnit * 10
|
||||
property int implicitCellHeight: Math.round(implicitCellWidth / 1.6) + Kirigami.Units.gridUnit*3
|
||||
|
||||
/**
|
||||
* Allow to highlight the selected item with Kirigami.Theme.neutralTextColor
|
||||
*/
|
||||
property bool neutralHighlight: false
|
||||
|
||||
onCurrentIndexChanged: positionViewAtIndex(currentIndex, GridView.Contain);
|
||||
|
||||
QtObject {
|
||||
id: internal
|
||||
readonly property int availableWidth: scroll.width - internal.scrollBarSpace - 4
|
||||
readonly property int scrollBarSpace: scroll.QQC2.ScrollBar.vertical.width
|
||||
}
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: 2
|
||||
leftMargin: 2 + (scroll.QQC2.ScrollBar.vertical.visible ? 0 : Math.round(internal.scrollBarSpace / 2))
|
||||
}
|
||||
clip: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
Keys.onTabPressed: event => nextItemInFocusChain().forceActiveFocus(Qt.TabFocusReason)
|
||||
Keys.onBacktabPressed: event => nextItemInFocusChain(false).forceActiveFocus(Qt.TabFocusReason)
|
||||
|
||||
cellWidth: Math.floor(internal.availableWidth / Math.max(Math.floor(internal.availableWidth / (implicitCellWidth + Kirigami.Units.gridUnit)), (Kirigami.Settings.isMobile ? 1 : 2)))
|
||||
cellHeight: Kirigami.Settings.isMobile ? cellWidth/1.6 + Kirigami.Units.gridUnit : implicitCellHeight
|
||||
|
||||
keyNavigationEnabled: true
|
||||
keyNavigationWraps: true
|
||||
highlightMoveDuration: 0
|
||||
|
||||
remove: Transition {
|
||||
ParallelAnimation {
|
||||
NumberAnimation { property: "scale"; to: 0.5; duration: Kirigami.Units.longDuration }
|
||||
NumberAnimation { property: "opacity"; to: 0.0; duration: Kirigami.Units.longDuration }
|
||||
}
|
||||
}
|
||||
|
||||
removeDisplaced: Transition {
|
||||
SequentialAnimation {
|
||||
// wait for the "remove" animation to finish
|
||||
PauseAnimation { duration: Kirigami.Units.longDuration }
|
||||
NumberAnimation { properties: "x,y"; duration: Kirigami.Units.longDuration }
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Kirigami.Units.gridUnit * 8)
|
||||
active: parent.count === 0 && !startupTimer.running
|
||||
opacity: active && status === Loader.Ready ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
anchors.centerIn: parent
|
||||
icon.name: "edit-none"
|
||||
text: i18nd("kcmutils6", "No items found")
|
||||
}
|
||||
}
|
||||
|
||||
// The view can take a bit of time to initialize itself when the KCM first
|
||||
// loads, during which time count is 0, which would cause the placeholder
|
||||
// message to appear for a moment and then disappear. To prevent this, let's
|
||||
// suppress it appearing for a moment after the KCM loads.
|
||||
Timer {
|
||||
id: startupTimer
|
||||
interval: Kirigami.Units.humanMoment
|
||||
running: false
|
||||
}
|
||||
Component.onCompleted: {
|
||||
startupTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcmlauncher_p.h"
|
||||
|
||||
#include <KIO/CommandLauncherJob>
|
||||
#include <KService>
|
||||
|
||||
void KCMLauncher::open(const QStringList &names) const
|
||||
{
|
||||
KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), names);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void KCMLauncher::openSystemSettings(const QString &name, const QStringList &args) const
|
||||
{
|
||||
// The desktop filename is the same as the binary and icon
|
||||
const QString systemSettings = QStringLiteral("systemsettings");
|
||||
KIO::CommandLauncherJob *job = nullptr;
|
||||
|
||||
QStringList cmdline{name};
|
||||
if (!args.isEmpty()) {
|
||||
cmdline.append(QStringLiteral("--args"));
|
||||
cmdline.append(args.join(QLatin1Char(' ')));
|
||||
}
|
||||
|
||||
// Open in System Settings if it's available
|
||||
if (KService::serviceByDesktopName(systemSettings)) {
|
||||
job = new KIO::CommandLauncherJob(systemSettings, cmdline);
|
||||
job->setDesktopName(systemSettings);
|
||||
} else {
|
||||
job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), cmdline);
|
||||
}
|
||||
|
||||
job->start();
|
||||
}
|
||||
|
||||
void KCMLauncher::openInfoCenter(const QString &name) const
|
||||
{
|
||||
const QString infoCenterDesktopFile = QStringLiteral("org.kde.kinfocenter");
|
||||
const QString infoCenterbinary = QStringLiteral("kinfocenter");
|
||||
|
||||
KIO::CommandLauncherJob *job = nullptr;
|
||||
|
||||
// Open in Info Center if it's available
|
||||
if (KService::serviceByDesktopName(infoCenterDesktopFile)) {
|
||||
job = new KIO::CommandLauncherJob(infoCenterbinary, QStringList(name));
|
||||
job->setDesktopName(infoCenterDesktopFile);
|
||||
} else {
|
||||
job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), QStringList(name));
|
||||
}
|
||||
|
||||
job->start();
|
||||
}
|
||||
|
||||
#include "moc_kcmlauncher_p.cpp"
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCMSHELL_H
|
||||
#define KCMSHELL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class KCMLauncher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public Q_SLOTS:
|
||||
void open(const QStringList &names) const;
|
||||
|
||||
/**
|
||||
* Opens the specified module in System Settings. Only a single KCM name may
|
||||
* be provided.
|
||||
*
|
||||
* @code
|
||||
* import QtQuick.Controls as QQC2
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
*
|
||||
* QQC2.Button {
|
||||
* onClicked: KCMUtils.KCMLauncher.openSystemSettings("kcm_kscreen")
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param name A single kcm name to open in System Settings. Opening multiple
|
||||
* KCMs using this function is not supported; to do that, use KCMLauncher.open().
|
||||
* @param args Additional arguments to pass to the module.
|
||||
*
|
||||
* @since 5.71
|
||||
*/
|
||||
void openSystemSettings(const QString &name, const QStringList &args = QStringList()) const;
|
||||
|
||||
/**
|
||||
* Opens the specified module in InfCenter. Only a single KCM name may
|
||||
* be provided.
|
||||
*
|
||||
* @code
|
||||
* import QtQuick.Controls as QQC2
|
||||
* import org.kde.kcmutils as KCMUtils
|
||||
* QQC2.Button {
|
||||
* onClicked: KCMUtils.KCMLauncher.openInfoCenter("kcm_energy")
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param name A single kcm name to open in Info Center. Opening multiple
|
||||
* KCMs using this function is not supported; to do that, use KCMLauncher.open().
|
||||
*
|
||||
* @since 5.71
|
||||
*/
|
||||
void openInfoCenter(const QString &name) const;
|
||||
};
|
||||
|
||||
#endif // KCMSHELL_H
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCMUTILS_PRIVATE_QML_TYPES
|
||||
#define KCMUTILS_PRIVATE_QML_TYPES
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <kpluginproxymodel.h>
|
||||
|
||||
struct PluginProxyModelForeign {
|
||||
Q_GADGET
|
||||
QML_NAMED_ELEMENT(ProxyModel)
|
||||
QML_FOREIGN(KPluginProxyModel)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "settinghighlighterprivate.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlContext>
|
||||
|
||||
namespace
|
||||
{
|
||||
QByteArray itemClassName(QQuickItem *item)
|
||||
{
|
||||
// Split since some exported types will be of the form: Foo_QMLTYPE_XX
|
||||
const auto className = QByteArray(item->metaObject()->className()).split('_').first();
|
||||
return className;
|
||||
}
|
||||
|
||||
QList<QQuickItem *> findDescendantItems(QQuickItem *item)
|
||||
{
|
||||
const auto children = item->childItems();
|
||||
auto result = children;
|
||||
for (auto child : children) {
|
||||
result += findDescendantItems(child);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QQuickItem *findStyleItem(QQuickItem *item)
|
||||
{
|
||||
const auto className = itemClassName(item);
|
||||
|
||||
auto descendant = findDescendantItems(item);
|
||||
for (auto child : std::as_const(descendant)) {
|
||||
if (className.contains("FontWidget") && itemClassName(child).contains("TextField")) {
|
||||
return child->property("background").value<QQuickItem *>();
|
||||
}
|
||||
if (itemClassName(child).contains("GridViewInternal")) {
|
||||
return child;
|
||||
}
|
||||
if (itemClassName(child).contains("GridView")) {
|
||||
return child->property("view").value<QQuickItem *>();
|
||||
}
|
||||
if (itemClassName(child).contains("CheckIndicator") //
|
||||
|| itemClassName(child).contains("KQuickStyleItem")) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QQuickItem *SettingHighlighterPrivate::target() const
|
||||
{
|
||||
return m_target;
|
||||
}
|
||||
|
||||
void SettingHighlighterPrivate::setTarget(QQuickItem *target)
|
||||
{
|
||||
if (m_target == target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_target) {
|
||||
disconnect(m_target, &QQuickItem::childrenChanged, this, &SettingHighlighterPrivate::updateTarget);
|
||||
}
|
||||
|
||||
m_target = target;
|
||||
|
||||
if (m_target) {
|
||||
connect(m_target, &QQuickItem::childrenChanged, this, &SettingHighlighterPrivate::updateTarget);
|
||||
}
|
||||
|
||||
Q_EMIT targetChanged();
|
||||
updateTarget();
|
||||
}
|
||||
|
||||
bool SettingHighlighterPrivate::highlight() const
|
||||
{
|
||||
return m_highlight;
|
||||
}
|
||||
|
||||
void SettingHighlighterPrivate::setHighlight(bool highlight)
|
||||
{
|
||||
if (m_highlight == highlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_highlight = highlight;
|
||||
Q_EMIT highlightChanged();
|
||||
updateTarget();
|
||||
}
|
||||
|
||||
bool SettingHighlighterPrivate::defaultIndicatorVisible() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void SettingHighlighterPrivate::setDefaultIndicatorVisible(bool enabled)
|
||||
{
|
||||
if (m_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_enabled = enabled;
|
||||
Q_EMIT defaultIndicatorVisibleChanged(m_enabled);
|
||||
updateTarget();
|
||||
}
|
||||
|
||||
void SettingHighlighterPrivate::updateTarget()
|
||||
{
|
||||
if (!m_isComponentComplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_styleTarget && m_target) {
|
||||
m_styleTarget = findStyleItem(m_target);
|
||||
}
|
||||
|
||||
if (m_styleTarget) {
|
||||
if (itemClassName(m_styleTarget).contains("GridViewInternal")) {
|
||||
m_styleTarget->setProperty("neutralHighlight", m_highlight && m_enabled);
|
||||
} else {
|
||||
m_styleTarget->setProperty("_kde_highlight_neutral", m_highlight && m_enabled);
|
||||
}
|
||||
m_styleTarget->polish();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingHighlighterPrivate::componentComplete()
|
||||
{
|
||||
m_isComponentComplete = true;
|
||||
|
||||
updateTarget();
|
||||
}
|
||||
|
||||
#include "moc_settinghighlighterprivate.cpp"
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SETTINGSHIGHLIGHTERPRIVATE_H
|
||||
#define SETTINGSHIGHLIGHTERPRIVATE_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QQmlParserStatus>
|
||||
#include <QQuickItem>
|
||||
|
||||
class SettingHighlighterPrivate : public QObject, public QQmlParserStatus
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
|
||||
Q_PROPERTY(bool highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
|
||||
Q_PROPERTY(bool defaultIndicatorVisible READ defaultIndicatorVisible WRITE setDefaultIndicatorVisible NOTIFY defaultIndicatorVisibleChanged)
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
QQuickItem *target() const;
|
||||
void setTarget(QQuickItem *target);
|
||||
|
||||
bool highlight() const;
|
||||
void setHighlight(bool highlight);
|
||||
|
||||
bool defaultIndicatorVisible() const;
|
||||
void setDefaultIndicatorVisible(bool enabled);
|
||||
|
||||
Q_SIGNALS:
|
||||
void targetChanged();
|
||||
void highlightChanged();
|
||||
void defaultIndicatorVisibleChanged(bool enabled);
|
||||
|
||||
private:
|
||||
void updateTarget();
|
||||
|
||||
void classBegin() override
|
||||
{
|
||||
}
|
||||
void componentComplete() override;
|
||||
|
||||
bool m_isComponentComplete = false;
|
||||
|
||||
QPointer<QQuickItem> m_target = nullptr;
|
||||
QPointer<QQuickItem> m_styleTarget = nullptr;
|
||||
bool m_highlight = false;
|
||||
bool m_enabled = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "settingstateproxy.h"
|
||||
#include "kcmutils_debug.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaMethod>
|
||||
|
||||
KCoreConfigSkeleton *SettingStateProxy::configObject() const
|
||||
{
|
||||
return m_configObject;
|
||||
}
|
||||
|
||||
void SettingStateProxy::setConfigObject(KCoreConfigSkeleton *configObject)
|
||||
{
|
||||
if (m_configObject == configObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_configObject) {
|
||||
m_configObject->disconnect(this);
|
||||
}
|
||||
|
||||
m_configObject = configObject;
|
||||
Q_EMIT configObjectChanged();
|
||||
updateState();
|
||||
connectSetting();
|
||||
}
|
||||
|
||||
QString SettingStateProxy::settingName() const
|
||||
{
|
||||
return m_settingName;
|
||||
}
|
||||
|
||||
void SettingStateProxy::setSettingName(const QString &settingName)
|
||||
{
|
||||
if (m_settingName == settingName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_configObject) {
|
||||
m_configObject->disconnect(this);
|
||||
}
|
||||
|
||||
m_settingName = settingName;
|
||||
Q_EMIT settingNameChanged();
|
||||
updateState();
|
||||
connectSetting();
|
||||
}
|
||||
|
||||
bool SettingStateProxy::isImmutable() const
|
||||
{
|
||||
return m_immutable;
|
||||
}
|
||||
|
||||
bool SettingStateProxy::isDefaulted() const
|
||||
{
|
||||
return m_defaulted;
|
||||
}
|
||||
|
||||
void SettingStateProxy::updateState()
|
||||
{
|
||||
const auto item = m_configObject ? m_configObject->findItem(m_settingName) : nullptr;
|
||||
const auto immutable = item ? item->isImmutable() : false;
|
||||
const auto defaulted = item ? item->isDefault() : true;
|
||||
|
||||
if (m_immutable != immutable) {
|
||||
m_immutable = immutable;
|
||||
Q_EMIT immutableChanged();
|
||||
}
|
||||
|
||||
if (m_defaulted != defaulted) {
|
||||
m_defaulted = defaulted;
|
||||
Q_EMIT defaultedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingStateProxy::connectSetting()
|
||||
{
|
||||
const auto item = m_configObject ? m_configObject->findItem(m_settingName) : nullptr;
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto updateStateSlotIndex = metaObject()->indexOfMethod("updateState()");
|
||||
Q_ASSERT(updateStateSlotIndex >= 0);
|
||||
const auto updateStateSlot = metaObject()->method(updateStateSlotIndex);
|
||||
Q_ASSERT(updateStateSlot.isValid());
|
||||
|
||||
const auto itemHasSignals = dynamic_cast<KConfigCompilerSignallingItem *>(item) || dynamic_cast<KPropertySkeletonItem *>(item);
|
||||
if (!itemHasSignals) {
|
||||
qCWarning(KCMUTILS_LOG) << "Attempting to use SettingStateProxy with a non signalling item:" << m_settingName;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto propertyName = [this] {
|
||||
auto name = m_settingName;
|
||||
if (name.at(0).isUpper()) {
|
||||
name[0] = name[0].toLower();
|
||||
}
|
||||
return name.toUtf8();
|
||||
}();
|
||||
|
||||
const auto metaObject = m_configObject->metaObject();
|
||||
const auto propertyIndex = metaObject->indexOfProperty(propertyName.constData());
|
||||
Q_ASSERT(propertyIndex >= 0);
|
||||
const auto property = metaObject->property(propertyIndex);
|
||||
Q_ASSERT(property.isValid());
|
||||
if (!property.hasNotifySignal()) {
|
||||
qCWarning(KCMUTILS_LOG) << "Attempting to use SettingStateProxy with a non notifying property:" << propertyName;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto changedSignal = property.notifySignal();
|
||||
Q_ASSERT(changedSignal.isValid());
|
||||
connect(m_configObject, changedSignal, this, updateStateSlot);
|
||||
connect(m_configObject, &KCoreConfigSkeleton::configChanged, this, &SettingStateProxy::updateState);
|
||||
}
|
||||
|
||||
#include "moc_settingstateproxy.cpp"
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kevin Ottens <kevin.ottens@enioka.com>
|
||||
SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SETTINGSTATEPROXY_H
|
||||
#define SETTINGSTATEPROXY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <KCoreConfigSkeleton>
|
||||
|
||||
/**
|
||||
* This element allows to represent in a declarative way the
|
||||
* state of a particular setting in a config object.
|
||||
*
|
||||
* @since 5.73
|
||||
*/
|
||||
class SettingStateProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* The config object which will be monitored for setting state changes
|
||||
*/
|
||||
Q_PROPERTY(KCoreConfigSkeleton *configObject READ configObject WRITE setConfigObject NOTIFY configObjectChanged)
|
||||
|
||||
/**
|
||||
* The name of the setting in the config object
|
||||
*/
|
||||
Q_PROPERTY(QString settingName READ settingName WRITE setSettingName NOTIFY settingNameChanged)
|
||||
|
||||
/**
|
||||
* Indicates if the setting is marked as immutable
|
||||
*/
|
||||
Q_PROPERTY(bool immutable READ isImmutable NOTIFY immutableChanged)
|
||||
|
||||
/**
|
||||
* Indicates if the setting differs from its default value
|
||||
*/
|
||||
Q_PROPERTY(bool defaulted READ isDefaulted NOTIFY defaultedChanged)
|
||||
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
KCoreConfigSkeleton *configObject() const;
|
||||
void setConfigObject(KCoreConfigSkeleton *configObject);
|
||||
|
||||
QString settingName() const;
|
||||
void setSettingName(const QString &settingName);
|
||||
|
||||
bool isImmutable() const;
|
||||
bool isDefaulted() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void configObjectChanged();
|
||||
void settingNameChanged();
|
||||
|
||||
void immutableChanged();
|
||||
void defaultedChanged();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateState();
|
||||
|
||||
private:
|
||||
void connectSetting();
|
||||
|
||||
QPointer<KCoreConfigSkeleton> m_configObject;
|
||||
QString m_settingName;
|
||||
bool m_immutable = false;
|
||||
bool m_defaulted = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCMUTILS_QML_TYPES
|
||||
#define KCMUTILS_QML_TYPES
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <kquickconfigmodule.h>
|
||||
|
||||
struct ConfigModuleForeign {
|
||||
Q_GADGET
|
||||
QML_NAMED_ELEMENT(ConfigModule)
|
||||
QML_UNCREATABLE("")
|
||||
QML_FOREIGN(KQuickConfigModule)
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user