shift-shell/shell/contents/configuration/AppletConfiguration.qml
Devin Lin dee476e773 shell: Import applet/containment configuration dialogs
Import from plasma-nano so that we can better use it from plasma-mobile.
2022-06-22 14:59:34 -04:00

382 lines
15 KiB
QML

/*
* SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.6
import QtQuick.Controls 2.3 as QtControls
import QtQuick.Layouts 1.0
import QtQuick.Window 2.2
import org.kde.kirigami 2.5 as Kirigami
import org.kde.plasma.core 2.1 as PlasmaCore
import org.kde.plasma.configuration 2.0
//TODO: all of this will be done with desktop components
Rectangle {
id: root
// Layout.minimumWidth: plasmoid.availableScreenRect.width
// Layout.minimumHeight: plasmoid.availableScreenRect.height
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
color: "transparent"
//BEGIN properties
property bool isContainment: false
property alias internalDialog: dialogContents
//END properties
//BEGIN model
property ConfigModel globalConfigModel: globalAppletConfigModel
ConfigModel {
id: globalAppletConfigModel
}
Connections {
target: root.Window.window
function onVisibleChanged() {
if (root.Window.window.visible) {
root.Window.window.showMaximized();
}
}
}
PlasmaCore.SortFilterModel {
id: configDialogFilterModel
sourceModel: configDialog.configModel
filterRole: "visible"
filterCallback: function(source_row, value) { return value; }
}
//END model
//BEGIN functions
function saveConfig() {
if (pageStack.currentItem.saveConfig) {
pageStack.currentItem.saveConfig()
}
for (var key in plasmoid.configuration) {
if (pageStack.currentItem["cfg_"+key] !== undefined) {
plasmoid.configuration[key] = pageStack.currentItem["cfg_"+key]
}
}
}
function configurationHasChanged() {
for (var key in plasmoid.configuration) {
if (pageStack.currentItem["cfg_"+key] !== undefined) {
//for objects == doesn't work
if (typeof plasmoid.configuration[key] == 'object') {
for (var i in plasmoid.configuration[key]) {
if (plasmoid.configuration[key][i] != pageStack.currentItem["cfg_"+key][i]) {
return true;
}
}
return false;
} else if (pageStack.currentItem["cfg_"+key] != plasmoid.configuration[key]) {
return true;
}
}
}
return false;
}
function settingValueChanged() {
if (pageStack.currentItem.saveConfig !== undefined) {
pageStack.currentItem.saveConfig();
} else {
root.saveConfig();
}
}
//END functions
//BEGIN connections
Component.onCompleted: {
if (!isContainment && configDialog.configModel && configDialog.configModel.count > 0) {
if (configDialog.configModel.get(0).source) {
pageStack.sourceFile = configDialog.configModel.get(0).source
} else if (configDialog.configModel.get(0).kcm) {
pageStack.sourceFile = Qt.resolvedUrl("ConfigurationKcmPage.qml");
pageStack.currentItem.kcm = configDialog.configModel.get(0).kcm;
} else {
pageStack.sourceFile = "";
}
pageStack.title = configDialog.configModel.get(0).name
} else {
pageStack.sourceFile = globalConfigModel.get(0).source
pageStack.title = globalConfigModel.get(0).name
}
// root.width = dialogRootItem.implicitWidth
// root.height = dialogRootItem.implicitHeight
}
onVisibleChanged: {
if (visible) {
dialogContents.visible = true;
}
}
//END connections
//BEGIN UI components
Rectangle {
id: dialogContents
visible: true
anchors.fill: parent
color: Kirigami.Theme.backgroundColor
ColumnLayout {
id: dialogRootItem
anchors.fill: parent
spacing: 0
implicitWidth: scroll.implicitWidth
QtControls.ScrollView {
id: scroll
activeFocusOnTab: false
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: pageColumn.implicitWidth
implicitHeight: pageColumn.implicitHeight
property Item flickableItem: pageFlickable
// this horrible code below ensures the control with active focus stays visible in the window
// by scrolling the view up or down as needed when tabbing through the window
Window.onActiveFocusItemChanged: {
var flickable = scroll.flickableItem;
var item = Window.activeFocusItem;
if (!item) {
return;
}
// when an item within ScrollView has active focus the ScrollView,
// as FocusScope, also has it, so we only scroll in this case
if (!scroll.activeFocus) {
return;
}
var padding = PlasmaCore.Units.gridUnit * 2 // some padding to the top/bottom when we scroll
var yPos = item.mapToItem(scroll.contentItem, 0, 0).y;
if (yPos < flickable.contentY) {
flickable.contentY = Math.max(0, yPos - padding);
// The "Math.min(padding, item.height)" ensures that we only scroll the item into view
// when it's barely visible. The logic was mostly meant for keyboard navigating through
// a list of CheckBoxes, so this check keeps us from trying to scroll an inner ScrollView
// into view when it implicitly gains focus (like plasma-pa config dialog has).
} else if (yPos + Math.min(padding, item.height) > flickable.contentY + flickable.height) {
flickable.contentY = Math.min(flickable.contentHeight - flickable.height,
yPos - flickable.height + item.height + padding);
}
}
Flickable {
id: pageFlickable
anchors {
fill: parent
margins: PlasmaCore.Units.smallSpacing
}
contentHeight: pageColumn.height
contentWidth: width
ColumnLayout {
id: pageColumn
spacing: PlasmaCore.Units.largeSpacing / 2
width: pageFlickable.width
height: Math.max(implicitHeight, pageFlickable.height)
Kirigami.Heading {
id: pageTitle
Layout.fillWidth: true
level: 1
text: pageStack.title
}
QtControls.StackView {
id: pageStack
property string title: ""
property bool invertAnimations: false
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: Math.max(currentItem ? Math.max(currentItem.Layout.minimumWidth, currentItem.Layout.preferredWidth, currentItem.implicitWidth) : 0, PlasmaCore.Units.gridUnit * 15)
implicitHeight: Math.max(currentItem ? Math.max(currentItem.Layout.minimumHeight, currentItem.Layout.preferredHeight, currentItem.implicitHeight) : 0, PlasmaCore.Units.gridUnit * 15)
property string sourceFile
onSourceFileChanged: {
if (!sourceFile) {
return;
}
//in a StackView pages need to be initialized with stackviews size, or have none
var props = {"width": width, "height": height}
var plasmoidConfig = plasmoid.configuration
for (var key in plasmoidConfig) {
props["cfg_" + key] = plasmoid.configuration[key]
}
var newItem = replace(Qt.resolvedUrl(sourceFile), props)
for (var key in plasmoidConfig) {
var changedSignal = newItem["cfg_" + key + "Changed"]
if (changedSignal) {
changedSignal.connect(root.settingValueChanged)
}
}
var configurationChangedSignal = newItem.configurationChanged
if (configurationChangedSignal) {
configurationChangedSignal.connect(root.settingValueChanged)
}
scroll.flickableItem.contentY = 0
/*
for (var prop in currentItem) {
if (prop.indexOf("cfg_") === 0) {
currentItem[prop+"Changed"].connect(root.pageChanged)
}
}*/
}
replaceEnter: Transition {
ParallelAnimation {
//OpacityAnimator when starting from 0 is buggy (it shows one frame with opacity 1)
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
XAnimator {
from: pageStack.invertAnimations ? -scroll.width/3: scroll.width/3
to: 0
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
}
}
replaceExit: Transition {
ParallelAnimation {
OpacityAnimator {
from: 1
to: 0
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
XAnimator {
from: 0
to: pageStack.invertAnimations ? scroll.width/3 : -scroll.width/3
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
}
}
}
}
}
}
Rectangle {
id: separator
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Kirigami.Theme.highlightColor
visible: categoriesScroll.visible
Behavior on color {
ColorAnimation {
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
}
}
}
QtControls.ScrollView {
id: categoriesScroll
Layout.fillWidth: true
Layout.preferredHeight: categories.implicitHeight
visible: (configDialog.configModel ? configDialog.configModel.count : 0) + globalConfigModel.count > 1
Keys.onLeftPressed: {
var buttons = categories.children
var foundPrevious = false
for (var i = buttons.length - 1; i >= 0; --i) {
var button = buttons[i];
if (!button.hasOwnProperty("current")) {
// not a ConfigCategoryDelegate
continue;
}
if (foundPrevious) {
button.openCategory()
return
} else if (button.current) {
foundPrevious = true
}
}
}
Keys.onRightPressed: {
var buttons = categories.children
var foundNext = false
for (var i = 0, length = buttons.length; i < length; ++i) {
var button = buttons[i];
console.log(button)
if (!button.hasOwnProperty("current")) {
continue;
}
if (foundNext) {
button.openCategory()
return
} else if (button.current) {
foundNext = true
}
}
}
RowLayout {
id: categories
spacing: 0
width: categoriesScroll.width
height: implicitHeight
property Item currentItem: children[1]
Repeater {
model: root.isContainment ? globalConfigModel : undefined
delegate: ConfigCategoryDelegate {}
}
Repeater {
model: configDialogFilterModel
delegate: ConfigCategoryDelegate {}
}
Repeater {
model: !root.isContainment ? globalConfigModel : undefined
delegate: ConfigCategoryDelegate {}
}
}
}
}
}
//END UI components
}