/* * SPDX-FileCopyrightText: 2013 Marco Martin * * 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.20 as Kirigami import org.kde.plasma.core as PlasmaCore import org.kde.plasma.configuration 2.0 import org.kde.kitemmodels as KItemModels //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(); } } } KItemModels.KSortFilterProxyModel { id: configDialogFilterModel sourceModel: configDialog.configModel filterRoleName: "visible" filterRowCallback: (sourceRow, sourceParent) => { let value = sourceModel.data(sourceModel.index(sourceRow, 0, sourceParent), filterRole); return value === true; } } //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 = Kirigami.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: Kirigami.Units.smallSpacing } contentHeight: pageColumn.height contentWidth: width ColumnLayout { id: pageColumn spacing: Kirigami.Units.gridUnit / 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, Kirigami.Units.gridUnit * 15) implicitHeight: Math.max(currentItem ? Math.max(currentItem.Layout.minimumHeight, currentItem.Layout.preferredHeight, currentItem.implicitHeight) : 0, Kirigami.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 of plasmoidConfig.keys()) { props["cfg_" + key] = plasmoid.configuration[key] } var newItem = depth == 0 ? push(Qt.resolvedUrl(sourceFile), props) : replace(Qt.resolvedUrl(sourceFile), props) for (var key of plasmoidConfig.keys()) { 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.5 to: 1 duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } XAnimator { from: pageStack.invertAnimations ? -scroll.width/3: scroll.width/3 to: 0 duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } replaceExit: Transition { ParallelAnimation { OpacityAnimator { from: 1 to: 0 duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } XAnimator { from: 0 to: pageStack.invertAnimations ? scroll.width/3 : -scroll.width/3 duration: Kirigami.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: Kirigami.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 }