diff --git a/applets/mediacontroller/contents/config/config.qml b/applets/mediacontroller/contents/config/config.qml new file mode 100644 index 000000000..ca0fd0c9e --- /dev/null +++ b/applets/mediacontroller/contents/config/config.qml @@ -0,0 +1,29 @@ +/* + * Copyright 2016 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) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: i18n("General") + icon: "applications-multimedia" + source: "configGeneral.qml" + } +} diff --git a/applets/mediacontroller/contents/config/main.xml b/applets/mediacontroller/contents/config/main.xml new file mode 100644 index 000000000..a266afa37 --- /dev/null +++ b/applets/mediacontroller/contents/config/main.xml @@ -0,0 +1,14 @@ + + + + + + + false + + + + diff --git a/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml b/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml index 91a74adfd..5580c6c45 100644 --- a/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml +++ b/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml @@ -1,391 +1,391 @@ /*************************************************************************** * Copyright 2013 Sebastian Kügler * * Copyright 2014, 2016 Kai Uwe Broulik * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kcoreaddons 1.0 as KCoreAddons Item { id: expandedRepresentation Layout.minimumWidth: Layout.minimumHeight * 1.333 Layout.minimumHeight: units.gridUnit * 10 Layout.preferredWidth: Layout.minimumWidth * 1.5 Layout.preferredHeight: Layout.minimumHeight * 1.5 readonly property int controlSize: Math.min(height, width) / 4 property int position: mpris2Source.currentData.Position || 0 readonly property real rate: mpris2Source.currentData.Rate || 1 - readonly property int length: currentMetadata ? currentMetadata["mpris:length"] || 0 : 0 + readonly property double length: currentMetadata ? currentMetadata["mpris:length"] || 0 : 0 property bool disablePositionUpdate: false property bool keyPressed: false function retrievePosition() { var service = mpris2Source.serviceForSource(mpris2Source.current); var operation = service.operationDescription("GetPosition"); service.startOperationCall(operation); } Connections { target: plasmoid onExpandedChanged: { if (plasmoid.expanded) { retrievePosition(); } } } onPositionChanged: { // we don't want to interrupt the user dragging the slider if (!seekSlider.pressed && !keyPressed) { // we also don't want passive position updates disablePositionUpdate = true seekSlider.value = position disablePositionUpdate = false } } onLengthChanged: { disablePositionUpdate = true // When reducing maximumValue, value is clamped to it, however // when increasing it again it gets its old value back. // To keep us from seeking to the end of the track when moving // to a new track, we'll reset the value to zero and ask for the position again seekSlider.value = 0 seekSlider.maximumValue = length retrievePosition() disablePositionUpdate = false } Keys.onPressed: keyPressed = true Keys.onReleased: { keyPressed = false if (!event.modifiers) { event.accepted = true if (event.key === Qt.Key_Space || event.key === Qt.Key_K) { // K is YouTube's key for "play/pause" :) root.action_playPause() } else if (event.key === Qt.Key_P) { root.action_previous() } else if (event.key === Qt.Key_N) { root.action_next() } else if (event.key === Qt.Key_S) { root.action_stop() } else if (event.key === Qt.Key_Left || event.key === Qt.Key_J) { // TODO ltr languages // seek back 5s seekSlider.value = Math.max(0, seekSlider.value - 5000000) // microseconds } else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) { // seek forward 5s seekSlider.value = Math.min(seekSlider.maximumValue, seekSlider.value + 5000000) } else if (event.key === Qt.Key_Home) { seekSlider.value = 0 } else if (event.key === Qt.Key_End) { seekSlider.value = seekSlider.maximumValue } else if (event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { // jump to percentage, ie. 0 = beginnign, 1 = 10% of total length etc seekSlider.value = seekSlider.maximumValue * (event.key - Qt.Key_0) / 10 } else { event.accepted = false } } } ColumnLayout { id: titleColumn width: parent.width spacing: units.smallSpacing PlasmaComponents.ComboBox { id: playerCombo Layout.fillWidth: true visible: model.length > 2 // more than one player, @multiplex is always there model: { var model = [{ text: i18n("Choose player automatically"), source: mpris2Source.multiplexSource }] var sources = mpris2Source.sources for (var i = 0, length = sources.length; i < length; ++i) { var source = sources[i] if (source === mpris2Source.multiplexSource) { continue } // we could show the pretty player name ("Identity") here but then we // would have to connect all sources just for this model.push({text: source, source: source}) } return model } onModelChanged: { // if model changes, ComboBox resets, so we try to find the current player again... for (var i = 0, length = model.length; i < length; ++i) { if (model[i].source === mpris2Source.current) { currentIndex = i break } } } onActivated: { disablePositionUpdate = true // ComboBox has currentIndex and currentText, why doesn't it have currentItem/currentModelValue? mpris2Source.current = model[index].source disablePositionUpdate = false } } RowLayout { id: titleRow Layout.fillWidth: true Layout.minimumHeight: albumArt.Layout.preferredHeight spacing: units.largeSpacing Image { id: albumArt readonly property int size: Math.round(expandedRepresentation.height / 2 - (playerCombo.count > 2 ? playerCombo.height : 0)) source: root.albumArt asynchronous: true fillMode: Image.PreserveAspectCrop sourceSize: Qt.size(size, size) Layout.preferredHeight: size Layout.preferredWidth: size visible: !!root.track && status === Image.Ready } ColumnLayout { Layout.fillWidth: true spacing: units.smallSpacing / 2 PlasmaExtras.Heading { id: song Layout.fillWidth: true level: 3 opacity: 0.6 maximumLineCount: 3 wrapMode: Text.WrapAtWordBoundaryOrAnywhere elide: Text.ElideRight text: root.track ? root.track : i18n("No media playing") } PlasmaExtras.Heading { id: artist Layout.fillWidth: true level: 4 opacity: 0.4 maximumLineCount: 2 wrapMode: Text.WrapAtWordBoundaryOrAnywhere visible: text !== "" elide: Text.ElideRight text: root.artist || "" } PlasmaExtras.Heading { Layout.fillWidth: true level: 5 opacity: 0.4 wrapMode: Text.NoWrap elide: Text.ElideRight visible: text !== "" text: { var metadata = root.currentMetadata if (!metadata) { return "" } var xesamAlbum = metadata["xesam:album"] if (xesamAlbum) { return xesamAlbum } // if we play a local file without title and artist, show its containing folder instead if (metadata["xesam:title"] || root.artist) { return "" } var xesamUrl = metadata["xesam:url"].toString() if (xesamUrl.indexOf("file:///") !== 0) { // "!startsWith()" return "" } var urlParts = xesamUrl.split("/") if (urlParts.length < 3) { return "" } var lastFolderPath = urlParts[urlParts.length - 2] // last would be filename if (lastFolderPath) { return lastFolderPath } return "" } } } } RowLayout { Layout.fillWidth: true spacing: units.smallSpacing // if there's no "mpris:length" in the metadata, we cannot seek, so hide it in that case enabled: !root.noPlayer && root.track && seekSlider.maximumValue > 0 && mpris2Source.currentData.CanSeek ? true : false opacity: enabled ? 1 : 0 Behavior on opacity { NumberAnimation { duration: units.longDuration } } // ensure the layout doesn't shift as the numbers change and measure roughly the longest text that could occur with the current song TextMetrics { id: timeMetrics text: i18nc("Remaining time for song e.g -5:42", "-%1", KCoreAddons.Format.formatDuration(seekSlider.maximumValue / 1000, KCoreAddons.FormatTypes.FoldHours)) font: theme.smallestFont } PlasmaComponents.Label { Layout.preferredWidth: timeMetrics.width Layout.fillHeight: true verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignRight text: KCoreAddons.Format.formatDuration(seekSlider.value / 1000, KCoreAddons.FormatTypes.FoldHours) opacity: 0.6 font: theme.smallestFont } PlasmaComponents.Slider { id: seekSlider Layout.fillWidth: true z: 999 value: 0 onValueChanged: { if (!disablePositionUpdate) { // delay setting the position to avoid race conditions queuedPositionUpdate.restart() } } Timer { id: seekTimer interval: 1000 / expandedRepresentation.rate repeat: true running: root.state == "playing" && plasmoid.expanded && !keyPressed && interval > 0 onTriggered: { // some players don't continuously update the seek slider position via mpris // add one second; value in microseconds if (!seekSlider.pressed) { disablePositionUpdate = true if (seekSlider.value == seekSlider.maximumValue) { retrievePosition(); } else { seekSlider.value += 1000000 } disablePositionUpdate = false } } } } PlasmaComponents.Label { Layout.preferredWidth: timeMetrics.width Layout.fillHeight: true verticalAlignment: Text.AlignVCenter text: i18nc("Remaining time for song e.g -5:42", "-%1", KCoreAddons.Format.formatDuration((seekSlider.maximumValue - seekSlider.value) / 1000, KCoreAddons.FormatTypes.FoldHours)) opacity: 0.6 font: theme.smallestFont } } } Timer { id: queuedPositionUpdate interval: 100 onTriggered: { if (position == seekSlider.value) { return; } var service = mpris2Source.serviceForSource(mpris2Source.current) var operation = service.operationDescription("SetPosition") operation.microseconds = seekSlider.value service.startOperationCall(operation) } } Item { anchors.bottom: parent.bottom width: parent.width height: playerControls.height Row { id: playerControls property bool enabled: root.canControl property int controlsSize: theme.mSize(theme.defaultFont).height * 3 anchors.horizontalCenter: parent.horizontalCenter spacing: units.largeSpacing PlasmaComponents.ToolButton { anchors.verticalCenter: parent.verticalCenter width: expandedRepresentation.controlSize height: width enabled: playerControls.enabled && root.canGoPrevious iconSource: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" onClicked: { seekSlider.value = 0 // Let the media start from beginning. Bug 362473 root.action_previous() } } PlasmaComponents.ToolButton { width: Math.round(expandedRepresentation.controlSize * 1.5) height: width enabled: playerControls.enabled iconSource: root.state == "playing" ? "media-playback-pause" : "media-playback-start" onClicked: root.action_playPause() } PlasmaComponents.ToolButton { anchors.verticalCenter: parent.verticalCenter width: expandedRepresentation.controlSize height: width enabled: playerControls.enabled && root.canGoNext iconSource: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" onClicked: { seekSlider.value = 0 // Let the media start from beginning. Bug 362473 root.action_next() } } } } } diff --git a/applets/mediacontroller/contents/ui/configGeneral.qml b/applets/mediacontroller/contents/ui/configGeneral.qml new file mode 100644 index 000000000..7ebc744bc --- /dev/null +++ b/applets/mediacontroller/contents/ui/configGeneral.qml @@ -0,0 +1,35 @@ +/* + * Copyright 2016 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) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.1 as Controls +import QtQuick.Layouts 1.1 as Layouts + +Layouts.ColumnLayout { + property alias cfg_pauseWhenScreenLocked: pauseWhenScreenLockedCheckBox.checked + + Controls.CheckBox { + id: pauseWhenScreenLockedCheckBox + Layouts.Layout.fillWidth: true + text: i18n("Pause playback when screen is locked") + } + + Item { // compress layout + Layouts.Layout.fillHeight: true + } +} diff --git a/applets/notifications/lib/notificationsapplet.cpp b/applets/notifications/lib/notificationsapplet.cpp index 7b3115422..4474c8c82 100644 --- a/applets/notifications/lib/notificationsapplet.cpp +++ b/applets/notifications/lib/notificationsapplet.cpp @@ -1,131 +1,137 @@ /* * Copyright 2014 (c) Martin Klapetek * * 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 . * */ #include "notificationsapplet.h" #include #include #include #include #include NotificationsApplet::NotificationsApplet(QObject *parent, const QVariantList &data) : Plasma::Applet(parent, data), m_availableScreenRect(0,0,0,0) { } NotificationsApplet::~NotificationsApplet() { } void NotificationsApplet::init() { m_popupPosition = (NotificationsHelper::PositionOnScreen)configScreenPosition(); connect(this, &Plasma::Applet::locationChanged, this, &NotificationsApplet::onAppletLocationChanged); connect(containment(), &Plasma::Containment::screenChanged, this, &NotificationsApplet::onScreenChanges); Q_ASSERT(containment()); Q_ASSERT(containment()->corona()); connect(containment()->corona(), &Plasma::Corona::availableScreenRectChanged, this, &NotificationsApplet::onScreenChanges); Plasma::Applet::init(); onScreenChanges(); onAppletLocationChanged(); } void NotificationsApplet::onScreenChanges() { + // when removing the panel the applet is in, the containment is being destroyed but its corona is still + // there, rightfully emitting availableScreenRectChanged and then we blow up if we try to access it. + if (!containment()) { + return; + } + m_availableScreenRect = containment()->corona()->availableScreenRect(containment()->screen()); Q_EMIT availableScreenRectChanged(m_availableScreenRect); } QRect NotificationsApplet::availableScreenRect() const { return m_availableScreenRect; } void NotificationsApplet::onAppletLocationChanged() { if (configScreenPosition() == 0) { // If the screenPosition is set to default, // just follow the panel setScreenPositionFromAppletLocation(); } } uint NotificationsApplet::screenPosition() const { return m_popupPosition; } void NotificationsApplet::onScreenPositionChanged(uint position) { KConfigGroup globalGroup = globalConfig(); globalGroup.writeEntry("popupPosition", position); globalGroup.sync(); // If the position is set to default, let the setScreenPositionFromAppletLocation() // figure out the effective position, otherwise just set it to m_popupPosition // and emit the change if (position == NotificationsHelper::Default) { setScreenPositionFromAppletLocation(); } else { m_popupPosition = (NotificationsHelper::PositionOnScreen)position; Q_EMIT screenPositionChanged(m_popupPosition); } } uint NotificationsApplet::configScreenPosition() const { KConfigGroup globalGroup = globalConfig(); return globalGroup.readEntry("popupPosition", 0); //0 is default } void NotificationsApplet::setScreenPositionFromAppletLocation() { if (location() == Plasma::Types::TopEdge) { if (QGuiApplication::isRightToLeft()) { m_popupPosition = NotificationsHelper::TopLeft; } else { m_popupPosition = NotificationsHelper::TopRight; } } else { if (QGuiApplication::isRightToLeft()) { m_popupPosition = NotificationsHelper::BottomLeft; } else { m_popupPosition = NotificationsHelper::BottomRight; } } Q_EMIT screenPositionChanged(m_popupPosition); } K_EXPORT_PLASMA_APPLET_WITH_JSON(notifications, NotificationsApplet, "metadata.json") #include "notificationsapplet.moc" diff --git a/wallpapers/image/imagepackage/contents/ui/config.qml b/wallpapers/image/imagepackage/contents/ui/config.qml index eba31796b..c851f3a58 100644 --- a/wallpapers/image/imagepackage/contents/ui/config.qml +++ b/wallpapers/image/imagepackage/contents/ui/config.qml @@ -1,365 +1,357 @@ /* * Copyright 2013 Marco Martin * Copyright 2014 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) any later version. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.5 import QtQuick.Controls 1.0 as QtControls import QtQuick.Dialogs 1.1 as QtDialogs import QtQuick.Layouts 1.0 import QtQuick.Window 2.0 // for Screen //We need units from it import org.kde.plasma.core 2.0 as Plasmacore import org.kde.plasma.wallpapers.image 2.0 as Wallpaper import org.kde.kquickcontrolsaddons 2.0 ColumnLayout { id: root property alias cfg_Color: colorDialog.color property string cfg_Image property int cfg_FillMode property var cfg_SlidePaths: "" property int cfg_SlideInterval: 0 - signal restoreIndex(int count) function saveConfig() { - root.restoreIndex(imageWallpaper.wallpaperModel.count) imageWallpaper.commitDeletion(); } SystemPalette { id: syspal } Wallpaper.Image { id: imageWallpaper targetSize: { if (typeof plasmoid !== "undefined") { return Qt.size(plasmoid.width, plasmoid.height) } // Lock screen configuration case return Qt.size(Screen.width, Screen.height) } onSlidePathsChanged: cfg_SlidePaths = slidePaths } onCfg_SlidePathsChanged: { imageWallpaper.slidePaths = cfg_SlidePaths } property int hoursIntervalValue: Math.floor(cfg_SlideInterval / 3600) property int minutesIntervalValue: Math.floor(cfg_SlideInterval % 3600) / 60 property int secondsIntervalValue: cfg_SlideInterval % 3600 % 60 //Rectangle { color: "orange"; x: formAlignment; width: formAlignment; height: 20 } TextMetrics { id: textMetrics text: "00" } Row { //x: formAlignment - positionLabel.paintedWidth spacing: units.largeSpacing / 2 QtControls.Label { id: positionLabel width: formAlignment - units.largeSpacing anchors { verticalCenter: resizeComboBox.verticalCenter } text: i18nd("plasma_applet_org.kde.image", "Positioning:") horizontalAlignment: Text.AlignRight } QtControls.ComboBox { id: resizeComboBox property int textLength: 24 width: theme.mSize(theme.defaultFont).width * textLength model: [ { 'label': i18nd("plasma_applet_org.kde.image", "Scaled and Cropped"), 'fillMode': Image.PreserveAspectCrop }, { 'label': i18nd("plasma_applet_org.kde.image","Scaled"), 'fillMode': Image.Stretch }, { 'label': i18nd("plasma_applet_org.kde.image","Scaled, Keep Proportions"), 'fillMode': Image.PreserveAspectFit }, { 'label': i18nd("plasma_applet_org.kde.image", "Centered"), 'fillMode': Image.Pad }, { 'label': i18nd("plasma_applet_org.kde.image","Tiled"), 'fillMode': Image.Tile } ] textRole: "label" onCurrentIndexChanged: cfg_FillMode = model[currentIndex]["fillMode"] Component.onCompleted: setMethod(); function setMethod() { for (var i = 0; i < model.length; i++) { if (model[i]["fillMode"] == wallpaper.configuration.FillMode) { resizeComboBox.currentIndex = i; var tl = model[i]["label"].length; //resizeComboBox.textLength = Math.max(resizeComboBox.textLength, tl+5); } } } } } QtDialogs.ColorDialog { id: colorDialog modality: Qt.WindowModal showAlphaChannel: false title: i18nd("plasma_applet_org.kde.image", "Select Background Color") } Row { id: colorRow spacing: units.largeSpacing / 2 QtControls.Label { width: formAlignment - units.largeSpacing anchors.verticalCenter: colorButton.verticalCenter horizontalAlignment: Text.AlignRight text: i18nd("plasma_applet_org.kde.image", "Background Color:") } QtControls.Button { id: colorButton width: units.gridUnit * 3 text: " " // needed to it gets a proper height... onClicked: colorDialog.open() Rectangle { id: colorRect anchors.centerIn: parent width: parent.width - 2 * units.smallSpacing height: theme.mSize(theme.defaultFont).height color: colorDialog.color } } } Component { id: foldersComponent ColumnLayout { anchors.fill: parent Connections { target: root onHoursIntervalValueChanged: hoursInterval.value = root.hoursIntervalValue onMinutesIntervalValueChanged: minutesInterval.value = root.minutesIntervalValue onSecondsIntervalValueChanged: secondsInterval.value = root.secondsIntervalValue } //FIXME: there should be only one spinbox: QtControls spinboxes are still too limited for it tough RowLayout { Layout.fillWidth: true spacing: units.largeSpacing / 2 QtControls.Label { Layout.minimumWidth: formAlignment - units.largeSpacing anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignRight text: i18nd("plasma_applet_org.kde.image","Change every:") } QtControls.SpinBox { id: hoursInterval anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: textMetrics.width + units.gridUnit width: units.gridUnit * 3 decimals: 0 value: root.hoursIntervalValue minimumValue: 0 maximumValue: 24 onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls.Label { anchors.verticalCenter: parent.verticalCenter text: i18nd("plasma_applet_org.kde.image","Hours") } Item { Layout.preferredWidth: units.gridUnit } QtControls.SpinBox { id: minutesInterval anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: textMetrics.width + units.gridUnit width: units.gridUnit * 3 decimals: 0 value: root.minutesIntervalValue minimumValue: 0 maximumValue: 60 onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls.Label { anchors.verticalCenter: parent.verticalCenter text: i18nd("plasma_applet_org.kde.image","Minutes") } Item { Layout.preferredWidth: units.gridUnit } QtControls.SpinBox { id: secondsInterval anchors.verticalCenter: parent.verticalCenter Layout.minimumWidth: textMetrics.width + units.gridUnit width: units.gridUnit * 3 decimals: 0 value: root.secondsIntervalValue minimumValue: root.hoursIntervalValue === 0 && root.minutesIntervalValue === 0 ? 1 : 0 maximumValue: 60 onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls.Label { anchors.verticalCenter: parent.verticalCenter text: i18nd("plasma_applet_org.kde.image","Seconds") } } QtControls.ScrollView { Layout.fillHeight: true; Layout.fillWidth: true frameVisible: true ListView { id: slidePathsView anchors.margins: 4 model: imageWallpaper.slidePaths delegate: QtControls.Label { text: modelData width: slidePathsView.width height: Math.max(paintedHeight, removeButton.height); QtControls.ToolButton { id: removeButton anchors { verticalCenter: parent.verticalCenter right: parent.right } iconName: "list-remove" onClicked: imageWallpaper.removeSlidePath(modelData); } } } } } } Component { id: thumbnailsComponent QtControls.ScrollView { anchors.fill: parent frameVisible: true highlightOnFocus: true; Component.onCompleted: { //replace the current binding on the scrollbar that makes it visible when content doesn't fit //otherwise we adjust gridSize when we hide the vertical scrollbar and //due to layouting that can make everything adjust which changes the contentWidth/height which //changes our scrollbars and we continue being stuck in a loop //looks better to not have everything resize anyway. //BUG: 336301 __verticalScrollBar.visible = true } GridView { id: wallpapersGrid model: imageWallpaper.wallpaperModel currentIndex: -1 focus: true cellWidth: Math.floor(wallpapersGrid.width / Math.max(Math.floor(wallpapersGrid.width / (units.gridUnit*12)), 3)) cellHeight: Math.round(cellWidth / (imageWallpaper.targetSize.width / imageWallpaper.targetSize.height)) anchors.margins: 4 boundsBehavior: Flickable.StopAtBounds delegate: WallpaperDelegate { color: cfg_Color } onContentHeightChanged: { wallpapersGrid.currentIndex = imageWallpaper.wallpaperModel.indexOf(cfg_Image); wallpapersGrid.positionViewAtIndex(wallpapersGrid.currentIndex, GridView.Visible) } - Connections { - target: root - onRestoreIndex: { - wallpapersGrid.currentIndex = wallpapersGrid.currentIndex - count - } - } Keys.onPressed: { if (count < 1) { return; } if (event.key == Qt.Key_Home) { currentIndex = 0; } else if (event.key == Qt.Key_End) { currentIndex = count - 1; } } Keys.onLeftPressed: moveCurrentIndexLeft() Keys.onRightPressed: moveCurrentIndexRight() Keys.onUpPressed: moveCurrentIndexUp() Keys.onDownPressed: moveCurrentIndexDown() Connections { target: imageWallpaper onCustomWallpaperPicked: { wallpapersGrid.currentIndex = 0 } } } } } Loader { Layout.fillWidth: true Layout.fillHeight: true sourceComponent: (configDialog.currentWallpaper == "org.kde.image") ? thumbnailsComponent : foldersComponent } RowLayout { id: buttonsRow anchors { right: parent.right } QtControls.Button { visible: (configDialog.currentWallpaper == "org.kde.slideshow") iconName: "list-add" text: i18nd("plasma_applet_org.kde.image","Add Folder") onClicked: imageWallpaper.showAddSlidePathsDialog() } QtControls.Button { visible: (configDialog.currentWallpaper == "org.kde.image") iconName: "document-open-folder" text: i18nd("plasma_applet_org.kde.image","Open...") onClicked: imageWallpaper.showFileDialog(); } QtControls.Button { iconName: "get-hot-new-stuff" text: i18nd("plasma_applet_org.kde.image","Get New Wallpapers...") onClicked: imageWallpaper.getNewWallpaper(); } } }