You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

334 lines
11 KiB

/*
* Copyright 2018 Kai Uwe Broulik <kde@privat.broulik.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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.14 as Kirigami
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
import org.kde.newstuff 1.62 as NewStuff
import org.kde.kcm 1.3 as KCM
import org.kde.private.kcms.icons 1.0 as Private
KCM.GridViewKCM {
id: root
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
KCM.SettingStateBinding {
configObject: kcm.iconsSettings
settingName: "Theme"
}
DropArea {
enabled: view.enabled
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.floor(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();
}
onDoubleClicked: {
kcm.save();
}
}
footer: ColumnLayout {
Kirigami.InlineMessage {
id: infoLabel
Layout.fillWidth: true
showCloseButton: true
Connections {
target: kcm
function onShowSuccessMessage(message) {
infoLabel.type = Kirigami.MessageType.Positive;
infoLabel.text = message;
infoLabel.visible = true;
}
function onShowErrorMessage(message) {
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
// Using a non-flat toolbutton here, so it matches the items in the actiontoolbar
// (a Button is just ever so slightly smaller than a ToolButton, and it would look
// kind of silly if the buttons aren't the same size)
QtControls.ToolButton {
id: iconSizesButton
text: i18n("Configure Icon Sizes")
icon.name: "transform-scale" // proper icon?
display: QtControls.ToolButton.TextBesideIcon
flat: false
checkable: true
checked: iconSizePopupLoader.item && iconSizePopupLoader.item.opened
onClicked: {
iconSizePopupLoader.active = true;
iconSizePopupLoader.item.open();
}
}
Kirigami.ActionToolBar {
flat: false
alignment: Qt.AlignRight
actions: [
Kirigami.Action {
enabled: root.view.enabled
text: i18n("Install from File...")
icon.name: "document-import"
onTriggered: fileDialogLoader.active = true
},
Kirigami.Action {
enabled: root.view.enabled
text: i18n("Get New Icons...")
icon.name: "get-hot-new-stuff"
onTriggered: { newStuffPage.open(); }
}
]
}
}
}
Loader {
id: newStuffPage
// Use this function to open the dialog. It seems roundabout, but this ensures
// that the dialog is not constructed until we want it to be shown the first time,
// since it will initialise itself on the first load (which causes it to phone
// home) and we don't want that until the user explicitly asks for it.
function open() {
if (item) {
item.open();
} else {
active = true;
}
}
onLoaded: {
item.open();
}
active: false
asynchronous: true
sourceComponent: NewStuff.Dialog {
configFile: "icons.knsrc"
viewMode: NewStuff.Page.ViewMode.Preview
Connections {
target: newStuffPage.item.engine.engine
function onSignalEntryEvent(entry, event) {
kcm.ghnsEntriesChanged();
}
}
}
}
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
}
}
}
}