diff --git a/kcms/icons/package/contents/ui/IconSizePopup.qml b/kcms/icons/package/contents/ui/IconSizePopup.qml index 0fb4a5744..d26a227a0 100644 --- a/kcms/icons/package/contents/ui/IconSizePopup.qml +++ b/kcms/icons/package/contents/ui/IconSizePopup.qml @@ -1,168 +1,168 @@ /* * Copyright 2018 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.3 as QtControls import org.kde.kirigami 2.4 as Kirigami QtControls.Popup { id: iconSizePopup width: 400 modal: true onOpened: { // can we do this automatically with "focus: true" somewhere? iconTypeList.forceActiveFocus(); } onVisibleChanged: { if (visible) { iconSizeSlider.sizes = kcm.availableIconSizes(iconTypeList.currentIndex); iconSizeSlider.updateSizes() } } Connections { target: iconTypeList onCurrentIndexChanged: { iconSizeSlider.sizes = kcm.availableIconSizes(iconTypeList.currentIndex); } } RowLayout { anchors.fill: parent ColumnLayout { id: iconSizeColumn Layout.fillWidth: true QtControls.ItemDelegate { // purely for metrics... id: measureDelegate visible: false } QtControls.ScrollView { id: iconTypeScroll Layout.fillWidth: true Layout.fillHeight: true Layout.preferredHeight: iconTypeList.count * measureDelegate.height + 4 activeFocusOnTab: false Component.onCompleted: iconTypeScroll.background.visible = true; ListView { id: iconTypeList activeFocusOnTab: true keyNavigationEnabled: true keyNavigationWraps: true highlightMoveDuration: 0 model: kcm.iconSizeCategoryModel currentIndex: 0 // Initialize with the first item Keys.onLeftPressed: { LayoutMirroring.enabled ? iconSizeSlider.increase() : iconSizeSlider.decrease() iconSizeSlider.moved(); } Keys.onRightPressed: { LayoutMirroring.enabled ? iconSizeSlider.decrease() : iconSizeSlider.increase() iconSizeSlider.moved(); } delegate: QtControls.ItemDelegate { width: ListView.view.width highlighted: ListView.isCurrentItem text: model.display readonly property string configKey: model.configKey onClicked: { ListView.view.currentIndex = index; ListView.view.forceActiveFocus(); } } } } QtControls.Slider { id: iconSizeSlider property var sizes: kcm.availableIconSizes(iconTypeList.currentIndex) Layout.fillWidth: true from: 0 to: sizes.length - 1 stepSize: 1.0 snapMode: QtControls.Slider.SnapAlways - enabled: sizes.length > 0 + enabled: sizes.length > 0 && !kcm.iconsSettings.isImmutable(iconTypeList.currentItem.configKey) onMoved: { kcm.iconsSettings[iconTypeList.currentItem.configKey] = iconSizeSlider.sizes[iconSizeSlider.value] || 0 } function updateSizes() { // since the icon sizes are queried using invokables, always force an update when opening // in case the user clicked Default or something value = Qt.binding(function() { var iconSize = kcm.iconsSettings[iconTypeList.currentItem.configKey] // I have no idea what this code does but it works and is just copied from the old KCM var index = -1; var delta = 1000; for (var i = 0, length = sizes.length; i < length; ++i) { var dw = Math.abs(iconSize - sizes[i]); if (dw < delta) { delta = dw; index = i; } } return index; }); } } } ColumnLayout { Layout.fillHeight: true Layout.minimumWidth: Math.round(parent.width / 2) Layout.maximumWidth: Math.round(parent.width / 2) Item { Layout.fillWidth: true Layout.fillHeight: true clip: true Kirigami.Icon { anchors.centerIn: parent width: kcm.iconsSettings[iconTypeList.currentItem.configKey] height: width source: "folder" } } QtControls.Label { id: iconSizeLabel Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter text: kcm.iconsSettings[iconTypeList.currentItem.configKey] } } } } diff --git a/kcms/icons/package/contents/ui/main.qml b/kcms/icons/package/contents/ui/main.qml index e7ac19521..cebc60969 100644 --- a/kcms/icons/package/contents/ui/main.qml +++ b/kcms/icons/package/contents/ui/main.qml @@ -1,286 +1,290 @@ /* * Copyright 2018 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.7 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.0 as QtDialogs import QtQuick.Controls 2.3 as QtControls import org.kde.kirigami 2.4 as Kirigami import org.kde.kquickcontrolsaddons 2.0 as KQCAddons import org.kde.kconfig 1.0 // for KAuthorized import org.kde.kcm 1.1 as KCM import org.kde.private.kcms.icons 1.0 as Private KCM.GridViewKCM { KCM.ConfigModule.quickHelp: i18n("This module allows you to choose the icons for your desktop.") view.model: kcm.iconsModel view.currentIndex: kcm.pluginIndex(kcm.iconsSettings.theme) enabled: !kcm.downloadingFile + view.enabled: !kcm.iconsSettings.isImmutable("Theme") DropArea { + enabled: !kcm.iconsSettings.isImmutable("Theme") anchors.fill: parent onEntered: { if (!drag.hasUrls) { drag.accepted = false; } } onDropped: kcm.installThemeFromFile(drop.urls[0]) } view.delegate: KCM.GridDelegate { id: delegate text: model.display toolTip: model.description thumbnailAvailable: typeof thumbFlow.previews === "undefined" || thumbFlow.previews.length > 0 thumbnail: MouseArea { id: thumbArea anchors.fill: parent acceptedButtons: Qt.NoButton hoverEnabled: true clip: thumbFlow.y < 0 opacity: model.pendingDeletion ? 0.3 : 1 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration } } Timer { interval: 1000 repeat: true running: thumbArea.containsMouse onRunningChanged: { if (!running) { thumbFlow.currentPage = 0; } } onTriggered: { if (!thumbFlow.allPreviesLoaded) { thumbFlow.loadPreviews(-1 /*no limit*/); thumbFlow.allPreviesLoaded = true; } ++thumbFlow.currentPage; if (thumbFlow.currentPage >= thumbFlow.pageCount) { stop(); } } } Flow { id: thumbFlow // undefined is "didn't load preview yet" // empty array is "no preview available" property var previews // initially we only load 6 and when the animation starts we'll load the rest property bool allPreviesLoaded: false property int currentPage readonly property int pageCount: Math.ceil(thumbRepeater.count / (thumbFlow.columns * thumbFlow.rows)) readonly property int iconWidth: Math.floor(thumbArea.width / thumbFlow.columns) readonly property int iconHeight: Math.round(thumbArea.height / thumbFlow.rows) readonly property int columns: 3 readonly property int rows: 2 function loadPreviews(limit) { previews = kcm.previewIcons(model.themeName, Math.min(thumbFlow.iconWidth, thumbFlow.iconHeight), Screen.devicePixelRatio, limit); } width: parent.width y: -currentPage * iconHeight * rows Behavior on y { NumberAnimation { duration: Kirigami.Units.longDuration } } Repeater { id: thumbRepeater model: thumbFlow.previews Item { width: thumbFlow.iconWidth height: thumbFlow.iconHeight KQCAddons.QPixmapItem { anchors.centerIn: parent width: Math.min(parent.width, nativeWidth) height: Math.min(parent.height, nativeHeight) // load on demand and avoid leaking a tiny corner of the icon pixmap: thumbFlow.y < 0 || index < (thumbFlow.columns * thumbFlow.rows) ? modelData : undefined smooth: true fillMode: KQCAddons.QPixmapItem.PreserveAspectFit } } } Component.onCompleted: { // avoid reloading it when icon sizes or dpr changes on startup Qt.callLater(function() { // We show 6 icons initially (3x2 grid), only load those thumbFlow.loadPreviews(6 /*limit*/); }); } } } actions: [ Kirigami.Action { iconName: "edit-delete" tooltip: i18n("Remove Icon Theme") enabled: model.removable visible: !model.pendingDeletion onTriggered: model.pendingDeletion = true }, Kirigami.Action { iconName: "edit-undo" tooltip: i18n("Restore Icon Theme") visible: model.pendingDeletion onTriggered: model.pendingDeletion = false } ] onClicked: { if (!model.pendingDeletion) { kcm.iconsSettings.theme = model.themeName; } view.forceActiveFocus(); } } footer: ColumnLayout { Kirigami.InlineMessage { id: infoLabel Layout.fillWidth: true showCloseButton: true Connections { target: kcm onShowSuccessMessage: { infoLabel.type = Kirigami.MessageType.Positive; infoLabel.text = message; infoLabel.visible = true; } onShowErrorMessage: { infoLabel.type = Kirigami.MessageType.Error; infoLabel.text = message; infoLabel.visible = true; } } } RowLayout { id: progressRow visible: false QtControls.BusyIndicator { id: progressBusy } QtControls.Label { id: progressLabel Layout.fillWidth: true textFormat: Text.PlainText wrapMode: Text.WordWrap } Connections { target: kcm onShowProgress: { progressLabel.text = message; progressBusy.running = true; progressRow.visible = true; } onHideProgress: { progressBusy.running = false; progressRow.visible = false; } } } RowLayout { Layout.fillWidth: true QtControls.Button { id: iconSizesButton text: i18n("Configure Icon Sizes") icon.name: "transform-scale" // proper icon? checkable: true checked: iconSizePopupLoader.item && iconSizePopupLoader.item.opened onClicked: { iconSizePopupLoader.active = true; iconSizePopupLoader.item.open(); } } Item { Layout.fillWidth: true } QtControls.Button { + enabled: !kcm.iconsSettings.isImmutable("Theme") id: installFromFileButton text: i18n("Install from File...") icon.name: "document-import" onClicked: fileDialogLoader.active = true } QtControls.Button { + enabled: !kcm.iconsSettings.isImmutable("Theme") text: i18n("Get New Icons...") icon.name: "get-hot-new-stuff" onClicked: kcm.getNewStuff(this) visible: KAuthorized.authorize("ghns") } } } Loader { id: iconSizePopupLoader active: false sourceComponent: IconSizePopup { parent: iconSizesButton y: -height } } Loader { id: fileDialogLoader active: false sourceComponent: QtDialogs.FileDialog { title: i18n("Open Theme") folder: shortcuts.home nameFilters: [ i18n("Theme Files (*.tar.gz *.tar.bz2)") ] Component.onCompleted: open() onAccepted: { kcm.installThemeFromFile(fileUrls[0]) fileDialogLoader.active = false } onRejected: { fileDialogLoader.active = false } } } }