diff --git a/applets/analog-clock/contents/ui/analogclock.qml b/applets/analog-clock/contents/ui/analogclock.qml index bd7e8965f..86028ad36 100644 --- a/applets/analog-clock/contents/ui/analogclock.qml +++ b/applets/analog-clock/contents/ui/analogclock.qml @@ -1,199 +1,199 @@ /* * Copyright 2012 Viranch Mehta * Copyright 2012 Marco Martin * Copyright 2013 David Edmundson * * 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 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.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.calendar 2.0 as PlasmaCalendar import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents Item { id: analogclock width: units.gridUnit * 15 height: units.gridUnit * 15 property int hours property int minutes property int seconds property bool showSecondsHand: plasmoid.configuration.showSecondHand property bool showTimezone: plasmoid.configuration.showTimezoneString property int tzOffset Plasmoid.backgroundHints: "NoBackground"; Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation Plasmoid.toolTipMainText: Qt.formatDate(dataSource.data["Local"]["DateTime"],"dddd") Plasmoid.toolTipSubText: Qt.formatDate(dataSource.data["Local"]["DateTime"], Qt.locale().dateFormat(Locale.LongFormat).replace(/(^dddd.?\s)|(,?\sdddd$)/, "")) PlasmaCore.DataSource { id: dataSource engine: "time" connectedSources: "Local" interval: showSecondsHand ? 1000 : 30000 onDataChanged: { var date = new Date(data["Local"]["DateTime"]); hours = date.getHours(); minutes = date.getMinutes(); seconds = date.getSeconds(); } Component.onCompleted: { onDataChanged(); } } function dateTimeChanged() { //console.log("Date/time changed!"); var currentTZOffset = dataSource.data["Local"]["Offset"] / 60; - if (currentTZOffset != tzOffset) { + if (currentTZOffset !== tzOffset) { tzOffset = currentTZOffset; //console.log("TZ offset changed: " + tzOffset); Date.timeZoneUpdated(); // inform the QML JS engine about TZ change } } Component.onCompleted: { tzOffset = new Date().getTimezoneOffset(); //console.log("Initial TZ offset: " + tzOffset); dataSource.onDataChanged.connect(dateTimeChanged); } Plasmoid.compactRepresentation: Item { id: representation - Layout.minimumWidth: plasmoid.formFactor != PlasmaCore.Types.Vertical ? representation.height : units.gridUnit - Layout.minimumHeight: plasmoid.formFactor == PlasmaCore.Types.Vertical ? representation.width : units.gridUnit + Layout.minimumWidth: plasmoid.formFactor !== PlasmaCore.Types.Vertical ? representation.height : units.gridUnit + Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? representation.width : units.gridUnit MouseArea { anchors.fill: parent onClicked: plasmoid.expanded = !plasmoid.expanded } PlasmaCore.Svg { id: clockSvg imagePath: "widgets/clock" } Item { id: clock width: parent.width anchors { top: parent.top bottom: showTimezone ? timezoneBg.top : parent.bottom } PlasmaCore.SvgItem { id: face anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: Math.min(parent.width, parent.height) svg: clockSvg elementId: "ClockFace" } Hand { anchors.topMargin: -6 elementId: "HourHandShadow" rotation: 180 + hours * 30 + (minutes/2) svgScale: face.width / face.naturalSize.width } Hand { elementId: "HourHand" rotation: 180 + hours * 30 + (minutes/2) svgScale: face.width / face.naturalSize.width } Hand { anchors.topMargin: 3 elementId: "MinuteHandShadow" rotation: 180 + minutes * 6 svgScale: face.width / face.naturalSize.width } Hand { elementId: "MinuteHand" rotation: 180 + minutes * 6 svgScale: face.width / face.naturalSize.width } Hand { anchors.topMargin: 2 elementId: "SecondHandShadow" rotation: 180 + seconds * 6 visible: showSecondsHand svgScale: face.width / face.naturalSize.width } Hand { elementId: "SecondHand" rotation: 180 + seconds * 6 visible: showSecondsHand svgScale: face.width / face.naturalSize.width } PlasmaCore.SvgItem { id: center width: naturalSize.width * face.width / face.naturalSize.width height: naturalSize.height * face.width / face.naturalSize.width anchors.centerIn: clock svg: clockSvg elementId: "HandCenterScrew" z: 1000 } PlasmaCore.SvgItem { anchors.fill: face svg: clockSvg elementId: "Glass" width: naturalSize.width * face.width / face.naturalSize.width height: naturalSize.height * face.width / face.naturalSize.width } } PlasmaCore.FrameSvgItem { id: timezoneBg anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom bottomMargin: 10 } imagePath: "widgets/background" width: childrenRect.width + margins.right + margins.left height: childrenRect.height + margins.top + margins.bottom visible: showTimezone PlasmaComponents.Label { id: timezoneText x: timezoneBg.margins.left y: timezoneBg.margins.top text: dataSource.data["Local"]["Timezone"] } } } Plasmoid.fullRepresentation: PlasmaCalendar.MonthView { Layout.minimumWidth: units.gridUnit * 20 Layout.minimumHeight: units.gridUnit * 20 today: dataSource.data["Local"]["DateTime"] } } diff --git a/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml b/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml index e4bd58377..ee3107434 100644 --- a/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml +++ b/applets/batterymonitor/package/contents/ui/CompactRepresentation.qml @@ -1,80 +1,80 @@ /* * Copyright 2011 Sebastian Kügler * Copyright 2011 Viranch Mehta * Copyright 2013 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 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 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as Components import org.kde.plasma.workspace.components 2.0 MouseArea { id: root Layout.minimumWidth: units.iconSizes.small * view.count Layout.minimumHeight: units.iconSizes.small property real itemSize: Math.min(root.height, root.width/view.count) onClicked: plasmoid.expanded = !plasmoid.expanded - readonly property bool isConstrained: plasmoid.formFactor == PlasmaCore.Types.Vertical || plasmoid.formFactor == PlasmaCore.Types.Horizontal + readonly property bool isConstrained: plasmoid.formFactor === PlasmaCore.Types.Vertical || plasmoid.formFactor === PlasmaCore.Types.Horizontal //Should we consider turning this into a Flow item? Row { Repeater { id: view property bool hasBattery: batterymonitor.pmSource.data["Battery"]["Has Cumulative"] property bool singleBattery: isConstrained || !hasBattery model: singleBattery ? 1 : batterymonitor.batteries Item { id: batteryContainer property bool hasBattery: view.singleBattery ? view.hasBattery : model["Plugged in"] property int percent: view.singleBattery ? pmSource.data["Battery"]["Percent"] : model["Percent"] property bool pluggedIn: pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"] && (view.singleBattery || model["Is Power Supply"]) height: root.itemSize width: root.width/view.count property real iconSize: Math.min(width, height) BatteryIcon { id: batteryIcon anchors.centerIn: parent hasBattery: batteryContainer.hasBattery percent: batteryContainer.percent pluggedIn: batteryContainer.pluggedIn height: isConstrained ? batteryContainer.iconSize : batteryContainer.iconSize - batteryLabel.height width: height } BadgeOverlay { anchors.fill: batteryIcon text: batteryContainer.hasBattery ? i18nc("battery percentage below battery icon", "%1%", percent) : i18nc("short symbol to signal there is no battery curently available", "-") icon: batteryIcon visible: plasmoid.configuration.showPercentage } } } } } diff --git a/applets/batterymonitor/package/contents/ui/batterymonitor.qml b/applets/batterymonitor/package/contents/ui/batterymonitor.qml index 77cc0c13b..fc96da183 100644 --- a/applets/batterymonitor/package/contents/ui/batterymonitor.qml +++ b/applets/batterymonitor/package/contents/ui/batterymonitor.qml @@ -1,244 +1,244 @@ /* * Copyright 2011 Sebastian Kügler * Copyright 2011 Viranch Mehta * Copyright 2013-2015 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 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 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kcoreaddons 1.0 as KCoreAddons import org.kde.kquickcontrolsaddons 2.0 import "logic.js" as Logic Item { id: batterymonitor Plasmoid.switchWidth: units.gridUnit * 10 Plasmoid.switchHeight: units.gridUnit * 10 LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true Plasmoid.status: { if (powermanagementDisabled) { return PlasmaCore.Types.ActiveStatus } if (pmSource.data.Battery["Has Cumulative"]) { if (pmSource.data.Battery.State !== "Charging" && pmSource.data.Battery.Percent <= 5) { return PlasmaCore.Types.NeedsAttentionStatus } else if (pmSource.data["Battery"]["State"] !== "FullyCharged") { return PlasmaCore.Types.ActiveStatus } } return PlasmaCore.Types.PassiveStatus } Plasmoid.toolTipMainText: { if (batteries.count === 0) { return i18n("No Batteries Available"); } else if (!pmSource.data["Battery"]["Has Cumulative"]) { // Bug 362924: Distinguish between no batteries and no power supply batteries // just show the generic applet title in the latter case return i18n("Battery and Brightness") } else if (pmSource.data["Battery"]["State"] === "FullyCharged") { return i18n("Fully Charged"); } else if (pmSource.data["AC Adapter"] && pmSource.data["AC Adapter"]["Plugged in"]) { var percent = pmSource.data.Battery.Percent var state = pmSource.data.Battery.State if (state === "Charging") { return i18n("%1% Charging", percent) } else if (state === "NoCharge") { return i18n("%1% Plugged in, not Charging", percent) } else { return i18n("%1% Plugged in", percent) } } else { if (remainingTime > 0) { return i18nc("%1 is remaining time, %2 is percentage", "%1 Remaining (%2%)", KCoreAddons.Format.formatDuration(remainingTime, KCoreAddons.FormatTypes.HideSeconds), pmSource.data["Battery"]["Percent"]) } else { return i18n("%1% Battery Remaining", pmSource.data["Battery"]["Percent"]); } } } Plasmoid.toolTipSubText: powermanagementDisabled ? i18n("Power management is disabled") : "" Plasmoid.icon: "battery" property bool disableBrightnessUpdate: true property int screenBrightness readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0 property int keyboardBrightness readonly property int maximumKeyboardBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Keyboard Brightness"] || 0 : 0 readonly property int remainingTime: Number(pmSource.data["Battery"]["Remaining msec"]) property bool powermanagementDisabled: false property var inhibitions: [] readonly property var kcms: ["powerdevilprofilesconfig.desktop", "powerdevilactivitiesconfig.desktop", "powerdevilglobalconfig.desktop"] readonly property bool kcmsAuthorized: KCMShell.authorize(batterymonitor.kcms).length > 0 onScreenBrightnessChanged: { if (disableBrightnessUpdate) { return; } var service = pmSource.serviceForSource("PowerDevil"); var operation = service.operationDescription("setBrightness"); operation.brightness = screenBrightness; // show OSD only when the plasmoid isn't expanded since the moving slider is feedback enough operation.silent = plasmoid.expanded service.startOperationCall(operation); } onKeyboardBrightnessChanged: { if (disableBrightnessUpdate) { return; } var service = pmSource.serviceForSource("PowerDevil"); var operation = service.operationDescription("setKeyboardBrightness"); operation.brightness = keyboardBrightness; operation.silent = plasmoid.expanded service.startOperationCall(operation); } function action_powerdevilkcm() { KCMShell.open(batterymonitor.kcms); } Component.onCompleted: { Logic.updateBrightness(batterymonitor, pmSource); Logic.updateInhibitions(batterymonitor, pmSource) if (batterymonitor.kcmsAuthorized) { plasmoid.setAction("powerdevilkcm", i18n("&Configure Power Saving..."), "preferences-system-power-management"); } } Plasmoid.compactRepresentation: CompactRepresentation { property int wheelDelta: 0 onEntered: wheelDelta = 0 onExited: wheelDelta = 0 onWheel: { var delta = wheel.angleDelta.y || wheel.angleDelta.x var maximumBrightness = batterymonitor.maximumScreenBrightness // Don't allow the UI to turn off the screen // Please see https://git.reviewboard.kde.org/r/122505/ for more information var minimumBrightness = (maximumBrightness > 100 ? 1 : 0) var steps = Math.max(1, Math.round(maximumBrightness / 20)) var deltaSteps = delta / 120; batterymonitor.screenBrightness = Math.max(minimumBrightness, Math.min(maximumBrightness, batterymonitor.screenBrightness + deltaSteps * steps)); } } property QtObject pmSource: PlasmaCore.DataSource { id: pmSource engine: "powermanagement" connectedSources: sources onSourceAdded: { disconnectSource(source); connectSource(source); } onSourceRemoved: { disconnectSource(source); } onDataChanged: { Logic.updateBrightness(batterymonitor, pmSource) Logic.updateInhibitions(batterymonitor, pmSource) } } property QtObject batteries: PlasmaCore.SortFilterModel { id: batteries filterRole: "Is Power Supply" sortOrder: Qt.DescendingOrder sourceModel: PlasmaCore.SortFilterModel { sortRole: "Pretty Name" sortOrder: Qt.AscendingOrder sortCaseSensitivity: Qt.CaseInsensitive sourceModel: PlasmaCore.DataModel { dataSource: pmSource sourceFilter: "Battery[0-9]+" } } } Plasmoid.fullRepresentation: PopupDialog { id: dialogItem Layout.minimumWidth: units.iconSizes.medium * 9 Layout.minimumHeight: units.gridUnit * 15 // TODO Probably needs a sensible preferredHeight too model: plasmoid.expanded ? batteries : null anchors.fill: parent focus: true isBrightnessAvailable: pmSource.data["PowerDevil"] && pmSource.data["PowerDevil"]["Screen Brightness Available"] ? true : false isKeyboardBrightnessAvailable: pmSource.data["PowerDevil"] && pmSource.data["PowerDevil"]["Keyboard Brightness Available"] ? true : false - pluggedIn: pmSource.data["AC Adapter"] != undefined && pmSource.data["AC Adapter"]["Plugged in"] + pluggedIn: pmSource.data["AC Adapter"] !== undefined && pmSource.data["AC Adapter"]["Plugged in"] property int cookie1: -1 property int cookie2: -1 onPowermanagementChanged: { var service = pmSource.serviceForSource("PowerDevil"); if (checked) { var op1 = service.operationDescription("stopSuppressingSleep"); op1.cookie = cookie1; var op2 = service.operationDescription("stopSuppressingScreenPowerManagement"); op2.cookie = cookie2; var job1 = service.startOperationCall(op1); job1.finished.connect(function(job) { cookie1 = -1; }); var job2 = service.startOperationCall(op2); job2.finished.connect(function(job) { cookie2 = -1; }); } else { var reason = i18n("The battery applet has enabled system-wide inhibition"); var op1 = service.operationDescription("beginSuppressingSleep"); op1.reason = reason; var op2 = service.operationDescription("beginSuppressingScreenPowerManagement"); op2.reason = reason; var job1 = service.startOperationCall(op1); job1.finished.connect(function(job) { cookie1 = job.result; }); var job2 = service.startOperationCall(op2); job2.finished.connect(function(job) { cookie2 = job.result; }); } batterymonitor.powermanagementDisabled = !checked } } } diff --git a/applets/clipboard/contents/ui/ClipboardPage.qml b/applets/clipboard/contents/ui/ClipboardPage.qml index f57e7880d..2623d0146 100644 --- a/applets/clipboard/contents/ui/ClipboardPage.qml +++ b/applets/clipboard/contents/ui/ClipboardPage.qml @@ -1,126 +1,126 @@ /******************************************************************** This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin Copyright (C) 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, see . *********************************************************************/ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 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 ColumnLayout { Keys.onPressed: { switch(event.key) { case Qt.Key_Up: { clipboardMenu.view.decrementCurrentIndex(); event.accepted = true; break; } case Qt.Key_Down: { clipboardMenu.view.incrementCurrentIndex(); event.accepted = true; break; } case Qt.Key_Enter: case Qt.Key_Return: { if (clipboardMenu.view.currentIndex >= 0) { var uuid = clipboardMenu.model.get(clipboardMenu.view.currentIndex).UuidRole if (uuid) { clipboardSource.service(uuid, "select") clipboardMenu.view.currentIndex = 0 } } break; } case Qt.Key_Escape: { if (filter.text != "") { filter.text = ""; event.accepted = true; } break; } default: { // forward key to filter // filter.text += event.text wil break if the key is backspace - if (event.key == Qt.Key_Backspace && filter.text == "") { + if (event.key === Qt.Key_Backspace && filter.text == "") { return; } - if (event.text != "" && !filter.activeFocus) { + if (event.text !== "" && !filter.activeFocus) { clipboardMenu.view.currentIndex = -1 if (event.matches(StandardKey.Paste)) { filter.paste(); } else { filter.text = ""; filter.text += event.text; } filter.forceActiveFocus(); event.accepted = true; } } } } PlasmaExtras.Heading { id: emptyHint Layout.fillWidth: true level: 3 opacity: 0.6 visible: clipboardMenu.model.count === 0 && filter.length === 0 text: i18n("Clipboard history is empty.") } RowLayout { Layout.fillWidth: true visible: !emptyHint.visible PlasmaComponents.TextField { id: filter placeholderText: i18n("Search...") clearButtonShown: true Layout.fillWidth: true } PlasmaComponents.ToolButton { iconSource: "edit-clear-history" tooltip: i18n("Clear history") onClicked: clipboardSource.service("", "clearHistory") } } Menu { id: clipboardMenu model: PlasmaCore.SortFilterModel { sourceModel: clipboardSource.models.clipboard filterRole: "DisplayRole" filterRegExp: filter.text } supportsBarcodes: clipboardSource.data["clipboard"]["supportsBarcodes"] Layout.fillWidth: true Layout.fillHeight: true onItemSelected: clipboardSource.service(uuid, "select") onRemove: clipboardSource.service(uuid, "remove") onEdit: clipboardSource.edit(uuid) onBarcode: { var page = stack.push(barcodePage); page.show(uuid); } onAction: { clipboardSource.service(uuid, "action") clipboardMenu.view.currentIndex = 0 } } } diff --git a/applets/devicenotifier/package/contents/ui/DeviceItem.qml b/applets/devicenotifier/package/contents/ui/DeviceItem.qml index fb7b179dd..b1cd0962f 100644 --- a/applets/devicenotifier/package/contents/ui/DeviceItem.qml +++ b/applets/devicenotifier/package/contents/ui/DeviceItem.qml @@ -1,342 +1,342 @@ /* * Copyright 2011 Viranch Mehta * Copyright 2012 Jacopo De Simoi * Copyright 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 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 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.0 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.kquickcontrolsaddons 2.0 MouseArea { id: deviceItem property string udi property alias icon: deviceIcon.source property alias deviceName: deviceLabel.text property string emblemIcon property int state property bool mounted property bool isRoot property bool expanded: devicenotifier.expandedDevice == udi property alias percentUsage: freeSpaceBar.value property string freeSpaceText signal actionTriggered property alias actionIcon: actionButton.iconName property alias actionToolTip: actionButton.tooltip property bool actionVisible readonly property bool hasMessage: statusSource.lastUdi == udi && statusSource.data[statusSource.last] ? true : false readonly property var message: hasMessage ? statusSource.data[statusSource.last] || ({}) : ({}) height: row.childrenRect.height + 2 * row.y hoverEnabled: true onHasMessageChanged: { if (hasMessage) { messageHighlight.highlight(this) } } onContainsMouseChanged: { if (containsMouse) { devicenotifier.currentIndex = index } // this is done to hide the highlight if the mouse moves out of the list view // and we are not hovering anything if (deviceItem.ListView.view.highlightItem) { deviceItem.ListView.view.highlightItem.opacity = (containsMouse ? 1 : 0) } } onClicked: { var data = hpSource.data[udi] if (!data) { return } var actions = data.actions if (actions.length === 1) { var service = hpSource.serviceForSource(udi) var operation = service.operationDescription("invokeAction") operation.predicate = actions[0].predicate service.startOperationCall(operation) } else { devicenotifier.expandedDevice = (expanded ? "" : udi) } } Connections { target: unmountAll onClicked: { if (model["Removable"] && mounted) { actionTriggered(); } } } // this keeps the delegate around for 5 seconds after the device has been // removed in case there was a message, such as "you can now safely remove this" ListView.onRemove: { if (devicenotifier.expandedDevice == udi) { devicenotifier.expandedDevice = "" } if (deviceItem.hasMessage) { ListView.delayRemove = true keepDelegateTimer.restart() statusMessage.opacity = 1 // HACK seems the Column animation breaksf freeSpaceBar.visible = false actionButton.visible = false ++devicenotifier.pendingDelegateRemoval // QTBUG-50380 } } Timer { id: keepDelegateTimer interval: 3000 // same interval as the auto hide / passive timer onTriggered: { deviceItem.ListView.delayRemove = false // otherwise the last message will show again when this device reappears statusSource.clearMessage() --devicenotifier.pendingDelegateRemoval // QTBUG-50380 } } Timer { id: updateStorageSpaceTimer interval: 5000 repeat: true running: mounted && plasmoid.expanded triggeredOnStart: true // Update the storage space as soon as we open the plasmoid onTriggered: { var service = sdSource.serviceForSource(udi); var operation = service.operationDescription("updateFreespace"); service.startOperationCall(operation); } } RowLayout { id: row anchors.horizontalCenter: parent.horizontalCenter y: units.smallSpacing width: parent.width - 2 * units.smallSpacing spacing: units.smallSpacing // FIXME: Device item loses focus on mounting/unmounting it, // or specifically, when some UI element changes. PlasmaCore.IconItem { id: deviceIcon Layout.alignment: Qt.AlignTop Layout.preferredWidth: units.iconSizes.medium Layout.preferredHeight: width enabled: deviceItem.state == 0 active: iconToolTip.containsMouse PlasmaCore.IconItem { id: deviceEmblem anchors { left: parent.left bottom: parent.bottom } width: units.iconSizes.small height: width source: { if (deviceItem.hasMessage) { if (deviceItem.message.solidError === 0) { return "emblem-information" } else { return "emblem-error" } } else if (deviceItem.state == 0) { return emblemIcon } else { return "" } } } PlasmaCore.ToolTipArea { id: iconToolTip anchors.fill: parent subText: { - if ((mounted || deviceItem.state != 0) && model["Available Content"] != "Audio") { + if ((mounted || deviceItem.state != 0) && model["Available Content"] !== "Audio") { if (model["Removable"]) { return i18n("It is currently not safe to remove this device: applications may be accessing it. Click the eject button to safely remove this device.") } else { return i18n("This device is currently accessible.") } } else { if (model["Removable"]) { if (model["In Use"]) { return i18n("It is currently not safe to remove this device: applications may be accessing other volumes on this device. Click the eject button on these other volumes to safely remove this device."); } else { return i18n("It is currently safe to remove this device.") } } else { return i18n("This device is not currently accessible.") } } } } } Column { Layout.fillWidth: true move: Transition { NumberAnimation { property: "y"; duration: units.longDuration; easing.type: Easing.InOutQuad } // ensure opacity values return to 1.0 if the add transition animation has been interrupted NumberAnimation { property: "opacity"; to: 1.0 } } add: Transition { NumberAnimation { property: "opacity" from: 0 to: 1 duration: units.longDuration easing.type: Easing.InOutQuad } } PlasmaComponents.Label { id: deviceLabel width: parent.width height: undefined // reset PlasmaComponent.Label's strange default height elide: Text.ElideRight } PlasmaComponents.ProgressBar { id: freeSpaceBar width: parent.width height: units.gridUnit // default is * 1.6 visible: deviceItem.state == 0 && mounted minimumValue: 0 maximumValue: 100 PlasmaCore.ToolTipArea { anchors.fill: parent subText: freeSpaceText != "" ? i18nc("@info:status Free disk space", "%1 free", freeSpaceText) : "" } // ProgressBar eats click events, so we'll forward them manually here... // setting enabled to false will also make the ProgressBar *look* disabled MouseArea { anchors.fill: parent onClicked: deviceItem.clicked(mouse) } } PlasmaComponents.Label { id: actionMessage width: parent.width height: undefined opacity: 0.6 font.pointSize: theme.smallestFont.pointSize visible: deviceItem.state != 0 || (!actionsList.visible && !deviceItem.hasMessage) text: { if (deviceItem.state == 0) { if (!hpSource.data[udi]) { return "" } var actions = hpSource.data[udi].actions if (actions.length > 1) { return i18np("1 action for this device", "%1 actions for this device", actions.length); } else { return actions[0].text } } else if (deviceItem.state == 1) { return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Accessing...") } else { return i18nc("Removing is a less technical word for Unmounting; translation should be short and mean \'Currently unmounting this device\'", "Removing...") } } } PlasmaComponents.Label { id: statusMessage width: parent.width height: undefined font.pointSize: theme.smallestFont.pointSize text: deviceItem.hasMessage ? (deviceItem.message.error || "") : "" wrapMode: Text.WordWrap maximumLineCount: 10 elide: Text.ElideRight visible: deviceItem.hasMessage } Item { // spacer width: 1 height: units.smallSpacing visible: actionsList.visible } ListView { id: actionsList width: parent.width interactive: false model: hpSource.data[udi] ? hpSource.data[udi].actions : null height: deviceItem.expanded ? actionsList.contentHeight : 0 visible: height > 0 cacheBuffer: 50000 // create all items delegate: ActionItem { width: actionsList.width icon: modelData.icon label: modelData.text predicate: modelData.predicate } highlight: PlasmaComponents.Highlight {} highlightMoveDuration: 0 highlightResizeDuration: 0 } } Item { Layout.preferredWidth: units.iconSizes.medium Layout.fillHeight: true PlasmaComponents.ToolButton { id: actionButton visible: !busyIndicator.visible && deviceItem.actionVisible enabled: !isRoot onClicked: actionTriggered() y: mounted ? deviceLabel.height + (freeSpaceBar.height - height - units.smallSpacing) / 2 : (deviceLabel.height + actionMessage.height - height) / 2 } PlasmaComponents.BusyIndicator { id: busyIndicator width: parent.width height: width running: visible visible: deviceItem.state != 0 } } } } diff --git a/applets/devicenotifier/package/contents/ui/FullRepresentation.qml b/applets/devicenotifier/package/contents/ui/FullRepresentation.qml index cee10703a..dcf62e3e5 100644 --- a/applets/devicenotifier/package/contents/ui/FullRepresentation.qml +++ b/applets/devicenotifier/package/contents/ui/FullRepresentation.qml @@ -1,228 +1,228 @@ /* * Copyright 2011 Viranch Mehta * Copyright 2012 Jacopo De Simoi * Copyright 2014 David Edmundson * Copyright 2014 Marco Martin * * * 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 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 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.2 import QtQuick.Window 2.2 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 MouseArea { id: fullRep property bool spontaneousOpen: false hoverEnabled: true Layout.minimumWidth: units.gridUnit * 12 Layout.minimumHeight: units.gridUnit * 12 PlasmaExtras.Heading { width: parent.width level: 3 opacity: 0.6 text: i18n("No Devices Available") visible: notifierDialog.count === 0 && !devicenotifier.pendingDelegateRemoval } PlasmaCore.DataSource { id: userActivitySource engine: "powermanagement" connectedSources: "UserActivity" property int polls: 0 //poll only on plasmoid expanded interval: !fullRep.containsMouse && !fullRep.Window.active && spontaneousOpen && plasmoid.expanded ? 3000 : 0 onIntervalChanged: polls = 0; onDataChanged: { //only do when polling if (interval == 0 || polls++ < 1) { return; } if (userActivitySource.data["UserActivity"]["IdleTime"] < interval) { plasmoid.expanded = false; spontaneousOpen = false; } } } // this item is reparented to a delegate that is showing a message to draw focus to it PlasmaComponents.Highlight { id: messageHighlight visible: false OpacityAnimator { id: messageHighlightAnimator target: messageHighlight from: 1 to: 0 duration: 3000 easing.type: Easing.InOutQuad } Connections { target: statusSource onLastChanged: { if (!statusSource.last) { messageHighlightAnimator.stop() messageHighlight.visible = false } } } function highlight(item) { parent = item width = Qt.binding(function() { return item.width }) height = Qt.binding(function() { return item.height }) opacity = 1 // Animator is threaded so the old opacity might be visible for a frame or two visible = true messageHighlightAnimator.start() } } Connections { target: plasmoid onExpandedChanged: { if (!plasmoid.expanded) { statusSource.clearMessage() } } } Item { anchors.fill: parent PlasmaComponents.ToolButton { id: unmountAll visible: devicenotifier.mountedRemovables > 1; anchors.right: parent.right iconSource: "media-eject" tooltip: i18n("Click to safely remove all devices") text: i18n("Remove all") implicitWidth: minimumWidth } PlasmaExtras.ScrollArea { anchors.fill: parent anchors.top: unmountAll.top ListView { id: notifierDialog focus: true boundsBehavior: Flickable.StopAtBounds model: filterModel delegate: deviceItem highlight: PlasmaComponents.Highlight { } highlightMoveDuration: 0 highlightResizeDuration: 0 spacing: units.smallSpacing currentIndex: devicenotifier.currentIndex //this is needed to make SectionScroller actually work //acceptable since one doesn't have a billion of devices cacheBuffer: 1000 section { property: "Type Description" delegate: Item { height: childrenRect.height width: notifierDialog.width PlasmaExtras.Heading { level: 3 opacity: 0.6 text: section } } } } } } Component { id: deviceItem DeviceItem { width: notifierDialog.width udi: DataEngineSource Binding on icon { when: sdSource.data[udi] !== undefined value: sdSource.data[udi].Icon } Binding on deviceName { when: sdSource.data[udi] !== undefined value: sdSource.data[udi].Description } emblemIcon: Emblems && Emblems[0] ? Emblems[0] : "" state: sdSource.data[udi] ? sdSource.data[udi].State : 0 - isRoot: sdSource.data[udi]["File Path"] == "/" + isRoot: sdSource.data[udi]["File Path"] === "/" percentUsage: { if (!sdSource.data[udi]) { return 0 } var freeSpace = new Number(sdSource.data[udi]["Free Space"]); var size = new Number(sdSource.data[udi]["Size"]); var used = size-freeSpace; return used*100/size; } freeSpaceText: sdSource.data[udi] && sdSource.data[udi]["Free Space Text"] ? sdSource.data[udi]["Free Space Text"] : "" actionIcon: mounted ? "media-eject" : "media-mount" - actionVisible: model["Device Types"].indexOf("Portable Media Player") == -1 + actionVisible: model["Device Types"].indexOf("Portable Media Player") === -1 actionToolTip: { var types = model["Device Types"]; if (!mounted) { return i18n("Click to access this device from other applications.") - } else if (types && types.indexOf("OpticalDisc") != -1) { + } else if (types && types.indexOf("OpticalDisc") !== -1) { return i18n("Click to eject this disc.") } else { return i18n("Click to safely remove this device.") } } mounted: devicenotifier.isMounted(udi) onActionTriggered: { var operationName = mounted ? "unmount" : "mount"; var service = sdSource.serviceForSource(udi); var operation = service.operationDescription(operationName); service.startOperationCall(operation); } property int operationResult: (model["Operation result"]) onOperationResultChanged: { if (operationResult == 1) { devicenotifier.popupIcon = "dialog-ok" popupIconTimer.restart() } else if (operationResult == 2) { devicenotifier.popupIcon = "dialog-error" popupIconTimer.restart() } } Behavior on height { NumberAnimation { duration: units.shortDuration * 3 } } } } } diff --git a/applets/devicenotifier/package/contents/ui/devicenotifier.qml b/applets/devicenotifier/package/contents/ui/devicenotifier.qml index 217c225db..d66226636 100644 --- a/applets/devicenotifier/package/contents/ui/devicenotifier.qml +++ b/applets/devicenotifier/package/contents/ui/devicenotifier.qml @@ -1,303 +1,303 @@ /* * Copyright 2011 Viranch Mehta * Copyright 2012 Jacopo De Simoi * Copyright 2014 David Edmundson * Copyright 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 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 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 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.kquickcontrolsaddons 2.0 Item { id: devicenotifier readonly property string automounterKcmName: "device_automounter_kcm" property string devicesType: { if (plasmoid.configuration.allDevices) { return "all" } else if (plasmoid.configuration.removableDevices) { return "removable" } else { return "nonRemovable" } } property string expandedDevice property string popupIcon: "device-notifier" property bool itemClicked: false property int currentIndex: -1 property var connectedRemovables: [] property int mountedRemovables: 0 // QTBUG-50380: As soon as the item gets removed from the model, all of ListView's // properties (count, contentHeight) pretend the delegate doesn't exist anymore // causing our "No devices" heading to overlap with the remaining device property int pendingDelegateRemoval: 0 Plasmoid.switchWidth: units.gridUnit * 10 Plasmoid.switchHeight: units.gridUnit * 10 Plasmoid.toolTipMainText: filterModel.count > 0 && filterModel.get(0) ? i18n("Most Recent Device") : i18n("No Devices Available") Plasmoid.toolTipSubText: { if (filterModel.count > 0) { var data = filterModel.get(0) if (data && data.Description) { return data.Description } } return "" } Plasmoid.icon: { if (filterModel.count > 0) { var data = filterModel.get(0) if (data && data.Icon) { return data.Icon } } return "device-notifier" } Plasmoid.status: (filterModel.count > 0 || pendingDelegateRemoval > 0) ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus PlasmaCore.DataSource { id: hpSource engine: "hotplug" connectedSources: sources interval: 0 onSourceAdded: { disconnectSource(source); connectSource(source); } onSourceRemoved: { disconnectSource(source); } } Plasmoid.compactRepresentation: PlasmaCore.IconItem { source: devicenotifier.popupIcon width: units.iconSizes.medium; height: units.iconSizes.medium; MouseArea { anchors.fill: parent onClicked: plasmoid.expanded = !plasmoid.expanded } } Plasmoid.fullRepresentation: FullRepresentation {} PlasmaCore.DataSource { id: sdSource engine: "soliddevice" connectedSources: hpSource.sources interval: 0 property string last onSourceAdded: { disconnectSource(source); connectSource(source); last = source; processLastDevice(true); if (data[source].Removable) { devicenotifier.connectedRemovables.push(source); devicenotifier.connectedRemovables = devicenotifier.connectedRemovables; } } onSourceRemoved: { if (expandedDevice == source) { expandedDevice = ""; } disconnectSource(source); var index = devicenotifier.connectedRemovables.indexOf(source); if (index >= 0) { devicenotifier.connectedRemovables.splice(index, 1); devicenotifier.connectedRemovables = devicenotifier.connectedRemovables; } } onDataChanged: { processLastDevice(true); var counter = 0; for (var i = 0; i < devicenotifier.connectedRemovables.length; i++) { if (isMounted(devicenotifier.connectedRemovables[i])) { counter++; } } - if (counter != devicenotifier.mountedRemovables) { + if (counter !== devicenotifier.mountedRemovables) { devicenotifier.mountedRemovables = counter; } } onNewData: { last = sourceName; processLastDevice(false); } function isViableDevice(udi) { if (devicesType === "all") { return true; } var device = data[udi]; if (!device) { return false; } return (devicesType === "removable" && device.Removable) || (devicesType === "nonRemovable" && !device.Removable); } function processLastDevice(expand) { if (last) { if (isViableDevice(last)) { if (expand && hpSource.data[last].added) { expandDevice(last); } last = ""; } } } } PlasmaCore.SortFilterModel { id: filterModel sourceModel: PlasmaCore.DataModel { dataSource: sdSource } filterRole: "Removable" filterRegExp: { if (devicesType === "removable") { return "true" } else if (devicesType === "nonRemovable") { return "false" } else { return "" } } sortRole: "Timestamp" sortOrder: Qt.DescendingOrder } PlasmaCore.DataSource { id: statusSource engine: "devicenotifications" property string last property string lastUdi onSourceAdded: { last = source; disconnectSource(source); connectSource(source); } onSourceRemoved: disconnectSource(source) onDataChanged: { if (last) { lastUdi = data[last].udi if (sdSource.isViableDevice(lastUdi)) { plasmoid.expanded = true plasmoid.fullRepresentationItem.spontaneousOpen = true; } } } function clearMessage() { last = "" lastUdi = "" } } Component.onCompleted: { - if (sdSource.connectedSources.count == 0) { + if (sdSource.connectedSources.count === 0) { Plasmoid.status = PlasmaCore.Types.PassiveStatus; } if (KCMShell.authorize(devicenotifier.automounterKcmName + ".desktop").length > 0) { plasmoid.setAction("openAutomounterKcm", i18nc("Open auto mounter kcm", "Configure Removable Devices"), "drive-removable-media") } } function action_openAutomounterKcm() { KCMShell.open([devicenotifier.automounterKcmName]) } Plasmoid.onExpandedChanged: { popupEventSlot(plasmoid.expanded); } function popupEventSlot(popped) { if (!popped) { // reset the property that lets us remember if an item was clicked // (versus only hovered) for autohide purposes devicenotifier.itemClicked = true; expandedDevice = ""; devicenotifier.currentIndex = -1; } } function expandDevice(udi) { if (hpSource.data[udi]["actions"].length > 1) { expandedDevice = udi } // reset the property that lets us remember if an item was clicked // (versus only hovered) for autohide purposes devicenotifier.itemClicked = false; devicenotifier.popupIcon = "preferences-desktop-notification"; //plasmoid.expanded = true; expandTimer.restart(); popupIconTimer.restart() } function isMounted(udi) { if (!sdSource.data[udi]) { return false; } var types = sdSource.data[udi]["Device Types"]; if (types.indexOf("Storage Access") >= 0) { return sdSource.data[udi]["Accessible"]; } return (types.indexOf("Storage Volume") >= 0 && types.indexOf("OpticalDisc") >= 0) } Timer { id: popupIconTimer interval: 3000 onTriggered: devicenotifier.popupIcon = "device-notifier"; } Timer { id: expandTimer interval: 250 onTriggered: { if (plasmoid.configuration.popupOnNewDevice) { // Bug 351592 plasmoid.expanded = true; plasmoid.fullRepresentationItem.spontaneousOpen = true; } } } } diff --git a/applets/digital-clock/package/contents/ui/CalendarView.qml b/applets/digital-clock/package/contents/ui/CalendarView.qml index 3ebdfd466..383176a5a 100644 --- a/applets/digital-clock/package/contents/ui/CalendarView.qml +++ b/applets/digital-clock/package/contents/ui/CalendarView.qml @@ -1,373 +1,373 @@ /* * Copyright 2013 Sebastian Kügler * Copyright 2015 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) 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, see . */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.calendar 2.0 as PlasmaCalendar import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras Item { id: calendar Layout.minimumWidth: _minimumWidth Layout.minimumHeight: _minimumHeight // The "sensible" values property int _minimumWidth: (showAgenda ? agendaViewWidth : 0) + monthViewWidth property int _minimumHeight: units.gridUnit * 14 Layout.preferredWidth: _minimumWidth Layout.preferredHeight: _minimumHeight * 1.5 readonly property bool showAgenda: PlasmaCalendar.EventPluginsManager.enabledPlugins.length > 0 readonly property int agendaViewWidth: _minimumHeight * 1.5 readonly property int monthViewWidth: monthView.showWeekNumbers ? Math.round(_minimumHeight * 1.75) : Math.round(_minimumHeight * 1.5) property int boxWidth: (agendaViewWidth + monthViewWidth - ((showAgenda ? 3 : 4) * spacing)) / 2 property int spacing: units.largeSpacing property alias borderWidth: monthView.borderWidth property alias monthView: monthView property bool debug: false property bool isExpanded: plasmoid.expanded onIsExpandedChanged: { // clear all the selections when the plasmoid is showing/hiding monthView.resetToToday(); } Item { id: agenda visible: calendar.showAgenda width: boxWidth anchors { top: parent.top left: parent.left bottom: parent.bottom leftMargin: spacing topMargin: spacing bottomMargin: spacing } function dateString(format) { return Qt.formatDate(monthView.currentDate, format); } function formatDateWithoutYear(date) { // Unfortunatelly Qt overrides ECMA's Date.toLocaleDateString(), // which is able to return locale-specific date-and-month-only date // formats, with its dumb version that only supports Qt::DateFormat // enum subset. So to get a day-and-month-only date format string we // must resort to this magic and hope there are no locales that use // other separators... var format = Qt.locale().dateFormat(Locale.ShortFormat).replace(/[./ ]*Y{2,4}[./ ]*/i, ''); return Qt.formatDate(date, format); } Connections { target: monthView onCurrentDateChanged: { // Apparently this is needed because this is a simple QList being // returned and if the list for the current day has 1 event and the // user clicks some other date which also has 1 event, QML sees the // sizes match and does not update the labels with the content. // Resetting the model to null first clears it and then correct data // are displayed. holidaysList.model = null; holidaysList.model = monthView.daysModel.eventsForDate(monthView.currentDate); } } Connections { target: monthView.daysModel onAgendaUpdated: { // Checks if the dates are the same, comparing the date objects // directly won't work and this does a simple integer subtracting // so should be fastest. One of the JS weirdness. if (updatedDate - monthView.currentDate === 0) { holidaysList.model = null; holidaysList.model = monthView.daysModel.eventsForDate(monthView.currentDate); } } } Connections { target: plasmoid.configuration onEnabledCalendarPluginsChanged: { PlasmaCalendar.EventPluginsManager.enabledPlugins = plasmoid.configuration.enabledCalendarPlugins; } } Binding { target: plasmoid property: "hideOnWindowDeactivate" value: !plasmoid.configuration.pin } PlasmaComponents.Label { id: dayLabel anchors.left: parent.left height: dayHeading.height + dateHeading.height width: paintedWidth font.pixelSize: height font.weight: Font.Light text: agenda.dateString("dd") opacity: 0.6 } PlasmaExtras.Heading { id: dayHeading anchors { top: parent.top left: dayLabel.right right: parent.right leftMargin: spacing / 2 } level: 1 elide: Text.ElideRight text: agenda.dateString("dddd") } PlasmaComponents.Label { id: dateHeading anchors { top: dayHeading.bottom left: dayLabel.right right: parent.right leftMargin: spacing / 2 } elide: Text.ElideRight text: Qt.locale().standaloneMonthName(monthView.currentDate.getMonth()) + agenda.dateString(" yyyy") } TextMetrics { id: dateLabelMetrics // Date/time are arbitrary values with all parts being two-digit readonly property string timeString: Qt.formatTime(new Date(2000, 12, 12, 12, 12, 12, 12)) readonly property string dateString: agenda.formatDateWithoutYear(new Date(2000, 12, 12, 12, 12, 12)) font: theme.defaultFont text: timeString.length > dateString.length ? timeString : dateString } PlasmaExtras.ScrollArea { id: holidaysView anchors { top: dateHeading.bottom left: parent.left right: parent.right bottom: parent.bottom } flickableItem.boundsBehavior: Flickable.StopAtBounds ListView { id: holidaysList delegate: PlasmaComponents.ListItem { id: eventItem property bool hasTime: { // Explicitly all-day event if (modelData.isAllDay) { return false; } // Multi-day event which does not start or end today (so // is all-day from today's point of view) if (modelData.startDateTime - monthView.currentDate < 0 && modelData.endDateTime - monthView.currentDate > 86400000) { // 24hrs in ms return false; } // Non-explicit all-day event - var startIsMidnight = modelData.startDateTime.getHours() == 0 - && modelData.startDateTime.getMinutes() == 0; + var startIsMidnight = modelData.startDateTime.getHours() === 0 + && modelData.startDateTime.getMinutes() === 0; - var endIsMidnight = modelData.endDateTime.getHours() == 0 - && modelData.endDateTime.getMinutes() == 0; + var endIsMidnight = modelData.endDateTime.getHours() === 0 + && modelData.endDateTime.getMinutes() === 0; - var sameDay = modelData.startDateTime.getDate() == modelData.endDateTime.getDate() - && modelData.startDateTime.getDay() == modelData.endDateTime.getDay() + var sameDay = modelData.startDateTime.getDate() === modelData.endDateTime.getDate() + && modelData.startDateTime.getDay() === modelData.endDateTime.getDay() if (startIsMidnight && endIsMidnight && sameDay) { return false } return true; } PlasmaCore.ToolTipArea { width: parent.width height: eventGrid.height active: eventTitle.truncated || eventDescription.truncated mainText: active ? eventTitle.text : "" subText: active ? eventDescription.text : "" GridLayout { id: eventGrid columns: 3 rows: 2 rowSpacing: 0 columnSpacing: 2 * units.smallSpacing width: parent.width Rectangle { id: eventColor Layout.row: 0 Layout.column: 0 Layout.rowSpan: 2 Layout.fillHeight: true color: modelData.eventColor width: 5 * units.devicePixelRatio visible: modelData.eventColor !== "" } PlasmaComponents.Label { id: startTimeLabel readonly property bool startsToday: modelData.startDateTime - monthView.currentDate >= 0 readonly property bool startedYesterdayLessThan12HoursAgo: modelData.startDateTime - monthView.currentDate >= -43200000 //12hrs in ms Layout.row: 0 Layout.column: 1 Layout.minimumWidth: dateLabelMetrics.width text: startsToday || startedYesterdayLessThan12HoursAgo ? Qt.formatTime(modelData.startDateTime) : agenda.formatDateWithoutYear(modelData.startDateTime) horizontalAlignment: Qt.AlignRight visible: eventItem.hasTime } PlasmaComponents.Label { id: endTimeLabel readonly property bool endsToday: modelData.endDateTime - monthView.currentDate <= 86400000 // 24hrs in ms readonly property bool endsTomorrowInLessThan12Hours: modelData.endDateTime - monthView.currentDate <= 86400000 + 43200000 // 36hrs in ms Layout.row: 1 Layout.column: 1 Layout.minimumWidth: dateLabelMetrics.width text: endsToday || endsTomorrowInLessThan12Hours ? Qt.formatTime(modelData.endDateTime) : agenda.formatDateWithoutYear(modelData.endDateTime) horizontalAlignment: Qt.AlignRight enabled: false visible: eventItem.hasTime } PlasmaComponents.Label { id: eventTitle readonly property bool wrap: eventDescription.text === "" Layout.row: 0 Layout.rowSpan: wrap ? 2 : 1 Layout.column: 2 Layout.fillWidth: true font.weight: Font.Bold elide: Text.ElideRight text: modelData.title verticalAlignment: Text.AlignVCenter maximumLineCount: 2 wrapMode: wrap ? Text.Wrap : Text.NoWrap } PlasmaComponents.Label { id: eventDescription Layout.row: 1 Layout.column: 2 Layout.fillWidth: true elide: Text.ElideRight text: modelData.description verticalAlignment: Text.AlignVCenter enabled: false visible: text !== "" } } } } section.property: "modelData.eventType" section.delegate: PlasmaExtras.Heading { level: 3 elide: Text.ElideRight text: section } } } PlasmaExtras.Heading { anchors.fill: holidaysView anchors.leftMargin: units.largeSpacing anchors.rightMargin: units.largeSpacing text: monthView.isToday(monthView.currentDate) ? i18n("No events for today") : i18n("No events for this day"); level: 3 opacity: 0.4 visible: holidaysList.count == 0 } } Item { id: cal width: boxWidth anchors { top: parent.top right: parent.right bottom: parent.bottom margins: spacing } PlasmaCalendar.MonthView { id: monthView borderOpacity: 0.25 today: root.tzDate showWeekNumbers: plasmoid.configuration.showWeekNumbers anchors.fill: parent } } // Allows the user to keep the calendar open for reference PlasmaComponents.ToolButton { anchors.right: parent.right width: Math.round(units.gridUnit * 1.25) height: width checkable: true iconSource: "window-pin" checked: plasmoid.configuration.pin onCheckedChanged: plasmoid.configuration.pin = checked tooltip: i18n("Keep Open") } } diff --git a/applets/digital-clock/package/contents/ui/DigitalClock.qml b/applets/digital-clock/package/contents/ui/DigitalClock.qml index f0479ed5c..0c49b7f67 100644 --- a/applets/digital-clock/package/contents/ui/DigitalClock.qml +++ b/applets/digital-clock/package/contents/ui/DigitalClock.qml @@ -1,699 +1,699 @@ /* * Copyright 2013 Heena Mahour * Copyright 2013 Sebastian Kügler * Copyright 2013 Martin Klapetek * Copyright 2014 David Edmundson * * 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, see . */ import QtQuick 2.6 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as Components import org.kde.plasma.private.digitalclock 1.0 Item { id: main property string timeFormat property date currentTime property bool showSeconds: plasmoid.configuration.showSeconds property bool showLocalTimezone: plasmoid.configuration.showLocalTimezone property bool showDate: plasmoid.configuration.showDate property var dateFormat: { if (plasmoid.configuration.dateFormat === "custom") { return plasmoid.configuration.customDateFormat; // str } else if (plasmoid.configuration.dateFormat === "longDate") { return Qt.SystemLocaleLongDate; // int } else if (plasmoid.configuration.dateFormat === "isoDate") { return Qt.ISODate; // int } else { // "shortDate" return Qt.SystemLocaleShortDate; // int } } property string lastSelectedTimezone: plasmoid.configuration.lastSelectedTimezone property bool displayTimezoneAsCode: plasmoid.configuration.displayTimezoneAsCode property int use24hFormat: plasmoid.configuration.use24hFormat property string lastDate: "" property int tzOffset // This is the index in the list of user selected timezones property int tzIndex: 0 // if the date/timezone cannot be fit with the smallest font to its designated space - readonly property bool oneLineMode: plasmoid.formFactor == PlasmaCore.Types.Horizontal && + readonly property bool oneLineMode: plasmoid.formFactor === PlasmaCore.Types.Horizontal && main.height <= 2 * theme.smallestFont.pixelSize && (main.showDate || timezoneLabel.visible) onDateFormatChanged: { setupLabels(); } onDisplayTimezoneAsCodeChanged: { setupLabels(); } onStateChanged: { setupLabels(); } onLastSelectedTimezoneChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } onShowSecondsChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } onShowLocalTimezoneChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } onShowDateChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } onUse24hFormatChanged: { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)) } Connections { target: plasmoid onContextualActionsAboutToShow: { ClipboardMenu.secondsIncluded = main.showSeconds; ClipboardMenu.currentDate = main.currentTime; } } Connections { target: plasmoid.configuration onSelectedTimeZonesChanged: { // If the currently selected timezone was removed, // default to the first one in the list var lastSelectedTimezone = plasmoid.configuration.lastSelectedTimezone; - if (plasmoid.configuration.selectedTimeZones.indexOf(lastSelectedTimezone) == -1) { + if (plasmoid.configuration.selectedTimeZones.indexOf(lastSelectedTimezone) === -1) { plasmoid.configuration.lastSelectedTimezone = plasmoid.configuration.selectedTimeZones[0]; } setupLabels(); setTimezoneIndex(); } } states: [ State { name: "horizontalPanel" - when: plasmoid.formFactor == PlasmaCore.Types.Horizontal && !main.oneLineMode + when: plasmoid.formFactor === PlasmaCore.Types.Horizontal && !main.oneLineMode PropertyChanges { target: main Layout.fillHeight: true Layout.fillWidth: false Layout.minimumWidth: contentItem.width Layout.maximumWidth: Layout.minimumWidth } PropertyChanges { target: contentItem height: timeLabel.height + (main.showDate || timezoneLabel.visible ? 0.8 * timeLabel.height : 0) width: Math.max(labelsGrid.width, timezoneLabel.paintedWidth, dateLabel.paintedWidth) } PropertyChanges { target: labelsGrid rows: main.showDate ? 1 : 2 } AnchorChanges { target: labelsGrid anchors.horizontalCenter: contentItem.horizontalCenter } PropertyChanges { target: timeLabel height: sizehelper.height width: sizehelper.contentWidth font.pixelSize: timeLabel.height } PropertyChanges { target: timezoneLabel height: main.showDate ? 0.7 * timeLabel.height : 0.8 * timeLabel.height width: main.showDate ? timezoneLabel.paintedWidth : timeLabel.width font.pixelSize: timezoneLabel.height } PropertyChanges { target: dateLabel height: 0.8 * timeLabel.height width: dateLabel.paintedWidth font.pixelSize: dateLabel.height } AnchorChanges { target: dateLabel anchors.top: labelsGrid.bottom anchors.horizontalCenter: labelsGrid.horizontalCenter } PropertyChanges { target: sizehelper /* * The value 0.71 was picked by testing to give the clock the right * size (aligned with tray icons). * Value 0.56 seems to be chosen rather arbitrary as well such that * the time label is slightly larger than the date or timezone label * and still fits well into the panel with all the applied margins. */ height: Math.min(main.showDate || timezoneLabel.visible ? main.height * 0.56 : main.height * 0.71, 3 * theme.defaultFont.pixelSize) font.pixelSize: sizehelper.height } }, State { name: "horizontalPanelSmall" - when: plasmoid.formFactor == PlasmaCore.Types.Horizontal && main.oneLineMode + when: plasmoid.formFactor === PlasmaCore.Types.Horizontal && main.oneLineMode PropertyChanges { target: main Layout.fillHeight: true Layout.fillWidth: false Layout.minimumWidth: contentItem.width Layout.maximumWidth: Layout.minimumWidth } PropertyChanges { target: contentItem height: sizehelper.height width: dateLabel.width + dateLabel.anchors.rightMargin + labelsGrid.width } AnchorChanges { target: labelsGrid anchors.right: contentItem.right } PropertyChanges { target: dateLabel height: timeLabel.height width: dateLabel.paintedWidth anchors.rightMargin: labelsGrid.columnSpacing fontSizeMode: Text.VerticalFit } AnchorChanges { target: dateLabel anchors.right: labelsGrid.left anchors.verticalCenter: labelsGrid.verticalCenter } PropertyChanges { target: timeLabel height: sizehelper.height width: sizehelper.contentWidth fontSizeMode: Text.VerticalFit } PropertyChanges { target: timezoneLabel height: 0.7 * timeLabel.height width: timezoneLabel.paintedWidth fontSizeMode: Text.VerticalFit horizontalAlignment: Text.AlignHCenter } PropertyChanges { target: sizehelper height: Math.min(main.height, 3 * theme.defaultFont.pixelSize) fontSizeMode: Text.VerticalFit font.pixelSize: 3 * theme.defaultFont.pixelSize } }, State { name: "verticalPanel" - when: plasmoid.formFactor == PlasmaCore.Types.Vertical + when: plasmoid.formFactor === PlasmaCore.Types.Vertical PropertyChanges { target: main Layout.fillHeight: false Layout.fillWidth: true Layout.maximumHeight: contentItem.height Layout.minimumHeight: Layout.maximumHeight } PropertyChanges { target: contentItem height: main.showDate ? labelsGrid.height + dateLabel.height : labelsGrid.height width: main.width } PropertyChanges { target: labelsGrid rows: 2 } PropertyChanges { target: timeLabel height: sizehelper.contentHeight width: main.width font.pixelSize: Math.min(timeLabel.height, 3 * theme.defaultFont.pixelSize) fontSizeMode: Text.HorizontalFit } PropertyChanges { target: timezoneLabel height: Math.max(0.7 * timeLabel.height, minimumPixelSize) width: main.width fontSizeMode: Text.Fit minimumPixelSize: dateLabel.minimumPixelSize elide: Text.ElideRight } PropertyChanges { target: dateLabel // this can be marginal bigger than contentHeight because of the horizontal fit height: Math.max(0.8 * timeLabel.height, minimumPixelSize) width: main.width fontSizeMode: Text.Fit minimumPixelSize: Math.min(0.7 * theme.smallestFont.pixelSize, timeLabel.height) elide: Text.ElideRight } AnchorChanges { target: dateLabel anchors.top: labelsGrid.bottom anchors.horizontalCenter: labelsGrid.horizontalCenter } PropertyChanges { target: sizehelper width: main.width fontSizeMode: Text.HorizontalFit font.pixelSize: 3 * theme.defaultFont.pixelSize } }, State { name: "other" - when: plasmoid.formFactor != PlasmaCore.Types.Vertical && plasmoid.formFactor != PlasmaCore.Types.Horizontal + when: plasmoid.formFactor !== PlasmaCore.Types.Vertical && plasmoid.formFactor !== PlasmaCore.Types.Horizontal PropertyChanges { target: main Layout.fillHeight: false Layout.fillWidth: false Layout.minimumWidth: units.gridUnit * 3 Layout.minimumHeight: units.gridUnit * 3 } PropertyChanges { target: contentItem height: main.height width: main.width } PropertyChanges { target: labelsGrid rows: 2 } PropertyChanges { target: timeLabel height: sizehelper.height width: main.width fontSizeMode: Text.Fit } PropertyChanges { target: timezoneLabel height: 0.7 * timeLabel.height width: main.width fontSizeMode: Text.Fit minimumPixelSize: 1 } PropertyChanges { target: dateLabel height: 0.8 * timeLabel.height width: Math.max(timeLabel.contentWidth, units.gridUnit * 3) fontSizeMode: Text.Fit minimumPixelSize: 1 } AnchorChanges { target: dateLabel anchors.top: labelsGrid.bottom anchors.horizontalCenter: labelsGrid.horizontalCenter } PropertyChanges { target: sizehelper height: { if (main.showDate) { if (timezoneLabel.visible) { return 0.4 * main.height } return 0.56 * main.height } else if (timezoneLabel.visible) { return 0.59 * main.height } return main.height } width: main.width fontSizeMode: Text.Fit font.pixelSize: 1024 } } ] MouseArea { id: mouseArea property int wheelDelta: 0 anchors.fill: parent onClicked: plasmoid.expanded = !plasmoid.expanded onWheel: { if (!plasmoid.configuration.wheelChangesTimezone) { return; } var delta = wheel.angleDelta.y || wheel.angleDelta.x var newIndex = main.tzIndex; wheelDelta += delta; // magic number 120 for common "one click" // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop while (wheelDelta >= 120) { wheelDelta -= 120; newIndex--; } while (wheelDelta <= -120) { wheelDelta += 120; newIndex++; } if (newIndex >= plasmoid.configuration.selectedTimeZones.length) { newIndex = 0; } else if (newIndex < 0) { newIndex = plasmoid.configuration.selectedTimeZones.length - 1; } - if (newIndex != main.tzIndex) { + if (newIndex !== main.tzIndex) { plasmoid.configuration.lastSelectedTimezone = plasmoid.configuration.selectedTimeZones[newIndex]; main.tzIndex = newIndex; dataSource.dataChanged(); setupLabels(); } } } /* * Visible elements * */ Item { id: contentItem anchors.verticalCenter: main.verticalCenter Grid { id: labelsGrid rows: 1 horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter flow: Grid.TopToBottom columnSpacing: units.smallSpacing Rectangle { height: 0.8 * sizehelper.height width: 1 visible: main.showDate && main.oneLineMode color: theme.textColor opacity: 0.4 } Components.Label { id: timeLabel font { family: plasmoid.configuration.fontFamily || theme.defaultFont.family weight: plasmoid.configuration.boldText ? Font.Bold : theme.defaultFont.weight italic: plasmoid.configuration.italicText pixelSize: 1024 } minimumPixelSize: 1 text: { // get the time for the given timezone from the dataengine var now = dataSource.data[plasmoid.configuration.lastSelectedTimezone]["DateTime"]; // get current UTC time var msUTC = now.getTime() + (now.getTimezoneOffset() * 60000); // add the dataengine TZ offset to it var currentTime = new Date(msUTC + (dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Offset"] * 1000)); main.currentTime = currentTime; return Qt.formatTime(currentTime, main.timeFormat); } verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } Components.Label { id: timezoneLabel font.weight: timeLabel.font.weight font.italic: timeLabel.font.italic font.pixelSize: 1024 minimumPixelSize: 1 visible: text.length > 0 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } Components.Label { id: dateLabel visible: main.showDate font.family: timeLabel.font.family font.weight: timeLabel.font.weight font.italic: timeLabel.font.italic font.pixelSize: 1024 minimumPixelSize: 1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } /* * end: Visible Elements * */ Components.Label { id: sizehelper font.family: timeLabel.font.family font.weight: timeLabel.font.weight font.italic: timeLabel.font.italic minimumPixelSize: 1 visible: false } FontMetrics { id: timeMetrics font.family: timeLabel.font.family font.weight: timeLabel.font.weight font.italic: timeLabel.font.italic } // Qt's QLocale does not offer any modular time creating like Klocale did // eg. no "gimme time with seconds" or "gimme time without seconds and with timezone". // QLocale supports only two formats - Long and Short. Long is unusable in many situations // and Short does not provide seconds. So if seconds are enabled, we need to add it here. // // What happens here is that it looks for the delimiter between "h" and "m", takes it // and appends it after "mm" and then appends "ss" for the seconds. function timeFormatCorrection(timeFormatString) { var regexp = /(hh*)(.+)(mm)/i var match = regexp.exec(timeFormatString); var hours = match[1]; var delimiter = match[2]; var minutes = match[3] var seconds = "ss"; var amPm = "AP"; - var uses24hFormatByDefault = timeFormatString.toLowerCase().indexOf("ap") == -1; + var uses24hFormatByDefault = timeFormatString.toLowerCase().indexOf("ap") === -1; // because QLocale is incredibly stupid and does not convert 12h/24h clock format // when uppercase H is used for hours, needs to be h or hh, so toLowerCase() var result = hours.toLowerCase() + delimiter + minutes; if (main.showSeconds) { result += delimiter + seconds; } // add "AM/PM" either if the setting is the default and locale uses it OR if the user unchecked "use 24h format" if ((main.use24hFormat == Qt.PartiallyChecked && !uses24hFormatByDefault) || main.use24hFormat == Qt.Unchecked) { result += " " + amPm; } main.timeFormat = result; setupLabels(); } function setupLabels() { - var showTimezone = main.showLocalTimezone || (plasmoid.configuration.lastSelectedTimezone != "Local" - && dataSource.data["Local"]["Timezone City"] != dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone City"]); + var showTimezone = main.showLocalTimezone || (plasmoid.configuration.lastSelectedTimezone !== "Local" + && dataSource.data["Local"]["Timezone City"] !== dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone City"]); var timezoneString = ""; if (showTimezone) { timezoneString = plasmoid.configuration.displayTimezoneAsCode ? dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone Abbreviation"] : TimezonesI18n.i18nCity(dataSource.data[plasmoid.configuration.lastSelectedTimezone]["Timezone City"]); - timezoneLabel.text = (main.showDate || main.oneLineMode) && plasmoid.formFactor == PlasmaCore.Types.Horizontal ? "(" + timezoneString + ")" : timezoneString; + timezoneLabel.text = (main.showDate || main.oneLineMode) && plasmoid.formFactor === PlasmaCore.Types.Horizontal ? "(" + timezoneString + ")" : timezoneString; } else { // this clears the label and that makes it hidden timezoneLabel.text = timezoneString; } if (main.showDate) { dateLabel.text = Qt.formatDate(main.currentTime, main.dateFormat); } else { // clear it so it doesn't take space in the layout dateLabel.text = ""; } // find widest character between 0 and 9 var maximumWidthNumber = 0; var maximumAdvanceWidth = 0; for (var i = 0; i <= 9; i++) { var advanceWidth = timeMetrics.advanceWidth(i); if (advanceWidth > maximumAdvanceWidth) { maximumAdvanceWidth = advanceWidth; maximumWidthNumber = i; } } // replace all placeholders with the widest number (two digits) var format = main.timeFormat.replace(/(h+|m+|s+)/g, "" + maximumWidthNumber + maximumWidthNumber); // make sure maximumWidthNumber is formatted as string // build the time string twice, once with an AM time and once with a PM time var date = new Date(2000, 0, 1, 1, 0, 0); var timeAm = Qt.formatTime(date, format); var advanceWidthAm = timeMetrics.advanceWidth(timeAm); date.setHours(13); var timePm = Qt.formatTime(date, format); var advanceWidthPm = timeMetrics.advanceWidth(timePm); // set the sizehelper's text to the widest time string if (advanceWidthAm > advanceWidthPm) { sizehelper.text = timeAm; } else { sizehelper.text = timePm; } } function dateTimeChanged() { var doCorrections = false; if (main.showDate) { // If the date has changed, force size recalculation, because the day name // or the month name can now be longer/shorter, so we need to adjust applet size var currentDate = Qt.formatDateTime(dataSource.data["Local"]["DateTime"], "yyyy-mm-dd"); - if (main.lastDate != currentDate) { + if (main.lastDate !== currentDate) { doCorrections = true; main.lastDate = currentDate } } var currentTZOffset = dataSource.data["Local"]["Offset"] / 60; - if (currentTZOffset != tzOffset) { + if (currentTZOffset !== tzOffset) { doCorrections = true; tzOffset = currentTZOffset; Date.timeZoneUpdated(); // inform the QML JS engine about TZ change } if (doCorrections) { timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)); } } function setTimezoneIndex() { for (var i = 0; i < plasmoid.configuration.selectedTimeZones.length; i++) { - if (plasmoid.configuration.selectedTimeZones[i] == plasmoid.configuration.lastSelectedTimezone) { + if (plasmoid.configuration.selectedTimeZones[i] === plasmoid.configuration.lastSelectedTimezone) { main.tzIndex = i; break; } } } Component.onCompleted: { // Sort the timezones according to their offset // Calling sort() directly on plasmoid.configuration.selectedTimeZones // has no effect, so sort a copy and then assign the copy to it var sortArray = plasmoid.configuration.selectedTimeZones; sortArray.sort(function(a, b) { return dataSource.data[a]["Offset"] - dataSource.data[b]["Offset"]; }); plasmoid.configuration.selectedTimeZones = sortArray; setTimezoneIndex(); tzOffset = -(new Date().getTimezoneOffset()); dateTimeChanged(); timeFormatCorrection(Qt.locale().timeFormat(Locale.ShortFormat)); dataSource.onDataChanged.connect(dateTimeChanged); } } diff --git a/applets/digital-clock/package/contents/ui/Tooltip.qml b/applets/digital-clock/package/contents/ui/Tooltip.qml index 889e03816..d82fc7fc7 100644 --- a/applets/digital-clock/package/contents/ui/Tooltip.qml +++ b/applets/digital-clock/package/contents/ui/Tooltip.qml @@ -1,145 +1,145 @@ /* * Copyright 2015 by Martin Klapetek * * 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, 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 2.010-1301, USA. */ import QtQuick 2.0 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.plasma.private.digitalclock 1.0 Item { id: tooltipContentItem property int preferredTextWidth: units.gridUnit * 20 width: childrenRect.width + units.gridUnit height: childrenRect.height + units.gridUnit LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true function timeForZone(zone) { var compactRepresentationItem = plasmoid.compactRepresentationItem; if (!compactRepresentationItem) { return ""; } // get the time for the given timezone from the dataengine var now = dataSource.data[zone]["DateTime"]; // get current UTC time var msUTC = now.getTime() + (now.getTimezoneOffset() * 60000); // add the dataengine TZ offset to it var dateTime = new Date(msUTC + (dataSource.data[zone]["Offset"] * 1000)); var formattedTime = Qt.formatTime(dateTime, compactRepresentationItem.timeFormat); - if (dateTime.getDay() != dataSource.data["Local"]["DateTime"].getDay()) { + if (dateTime.getDay() !== dataSource.data["Local"]["DateTime"].getDay()) { formattedTime += " (" + Qt.formatDate(dateTime, compactRepresentationItem.dateFormat) + ")"; } return formattedTime; } function nameForZone(zone) { // add the timezone string to the clock var timezoneString = plasmoid.configuration.displayTimezoneAsCode ? dataSource.data[zone]["Timezone Abbreviation"] : TimezonesI18n.i18nCity(dataSource.data[zone]["Timezone City"]); return timezoneString; } RowLayout { anchors { left: parent.left top: parent.top margins: units.gridUnit / 2 } spacing: units.largeSpacing PlasmaCore.IconItem { id: tooltipIcon source: "preferences-system-time" Layout.alignment: Qt.AlignTop visible: true implicitWidth: units.iconSizes.medium Layout.preferredWidth: implicitWidth Layout.preferredHeight: implicitWidth } ColumnLayout { PlasmaExtras.Heading { id: tooltipMaintext level: 3 Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth) Layout.maximumWidth: preferredTextWidth elide: Text.ElideRight text: Qt.formatDate(tzDate,"dddd") } PlasmaComponents.Label { id: tooltipSubtext Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth) Layout.maximumWidth: preferredTextWidth text: Qt.formatDate(tzDate, dateFormatString) opacity: 0.6 } GridLayout { Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth) Layout.maximumWidth: preferredTextWidth Layout.maximumHeight: childrenRect.height columns: 2 visible: plasmoid.configuration.selectedTimeZones.length > 1 Repeater { model: { // The timezones need to be duplicated in the array // because we need their data twice - once for the name // and once for the time and the Repeater delegate cannot // be one Item with two Labels because that wouldn't work // in a grid then var timezones = []; for (var i = 0; i < plasmoid.configuration.selectedTimeZones.length; i++) { timezones.push(plasmoid.configuration.selectedTimeZones[i]); timezones.push(plasmoid.configuration.selectedTimeZones[i]); } return timezones; } PlasmaComponents.Label { id: timezone // Layout.fillWidth is buggy here Layout.alignment: index % 2 === 0 ? Qt.AlignRight : Qt.AlignLeft wrapMode: Text.NoWrap text: index % 2 == 0 ? nameForZone(modelData) : timeForZone(modelData) font.weight: modelData === plasmoid.configuration.lastSelectedTimezone ? Font.Bold : Font.Normal height: paintedHeight elide: Text.ElideNone opacity: 0.6 } } } } } } diff --git a/applets/digital-clock/package/contents/ui/configAppearance.qml b/applets/digital-clock/package/contents/ui/configAppearance.qml index 91f6ff0f3..66bd5c917 100644 --- a/applets/digital-clock/package/contents/ui/configAppearance.qml +++ b/applets/digital-clock/package/contents/ui/configAppearance.qml @@ -1,261 +1,261 @@ /* * Copyright 2013 Bhushan Shah * Copyright 2013 Sebastian Kügler * Copyright 2015 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.0 import QtQuick.Controls 2.3 as QtControls import QtQuick.Layouts 1.0 as QtLayouts import org.kde.plasma.calendar 2.0 as PlasmaCalendar import org.kde.kquickcontrolsaddons 2.0 import org.kde.kirigami 2.5 as Kirigami QtLayouts.ColumnLayout { id: appearancePage width: childrenRect.width height: childrenRect.height signal configurationChanged property string cfg_fontFamily property alias cfg_boldText: boldCheckBox.checked property string cfg_timeFormat: "" property alias cfg_italicText: italicCheckBox.checked property alias cfg_showLocalTimezone: showLocalTimezone.checked property alias cfg_displayTimezoneAsCode: timezoneCodeRadio.checked property alias cfg_showSeconds: showSeconds.checked property alias cfg_showDate: showDate.checked property string cfg_dateFormat: "shortDate" property alias cfg_customDateFormat: customDateFormat.text property alias cfg_use24hFormat: use24hFormat.currentIndex onCfg_fontFamilyChanged: { // HACK by the time we populate our model and/or the ComboBox is finished the value is still undefined if (cfg_fontFamily) { for (var i = 0, j = fontsModel.count; i < j; ++i) { - if (fontsModel.get(i).value == cfg_fontFamily) { + if (fontsModel.get(i).value === cfg_fontFamily) { fontFamilyComboBox.currentIndex = i break } } } } ListModel { id: fontsModel Component.onCompleted: { var arr = [] // use temp array to avoid constant binding stuff arr.push({text: i18nc("Use default font", "Default"), value: ""}) var fonts = Qt.fontFamilies() var foundIndex = 0 for (var i = 0, j = fonts.length; i < j; ++i) { arr.push({text: fonts[i], value: fonts[i]}) } append(arr) } } Kirigami.FormLayout { QtLayouts.Layout.fillWidth: true QtControls.CheckBox { id: showDate Kirigami.FormData.label: i18n("Information:") text: i18n("Show date") } QtControls.CheckBox { id: showSeconds text: i18n("Show seconds") } QtControls.CheckBox { id: showLocalTimezone text: i18n("Show local time zone") } Item { Kirigami.FormData.isSection: true } QtLayouts.ColumnLayout { Kirigami.FormData.label: i18n("Display time zone as:") Kirigami.FormData.buddyFor: timezoneCityRadio QtControls.RadioButton { id: timezoneCityRadio text: i18n("Time zone city") } QtControls.RadioButton { id: timezoneCodeRadio text: i18n("Time zone code") } } Item { Kirigami.FormData.isSection: true } QtLayouts.RowLayout { QtLayouts.Layout.fillWidth: true Kirigami.FormData.label: i18n("Time display:") QtControls.ComboBox { id: use24hFormat model: [ i18n("12-Hour"), i18n("Use Region Defaults"), i18n("24-Hour") ] onCurrentIndexChanged: cfg_use24hFormat = currentIndex } QtControls.Button { visible: KCMShell.authorize("formats.desktop").length > 0 text: i18n("Change Regional Settings...") icon.name: "preferences-desktop-locale" onClicked: KCMShell.open("formats.desktop") } } Item { Kirigami.FormData.isSection: true } QtControls.ComboBox { id: dateFormat Kirigami.FormData.label: i18n("Date format:") enabled: showDate.checked textRole: "label" model: [ { 'label': i18n("Long Date"), 'name': "longDate" }, { 'label': i18n("Short Date"), 'name': "shortDate" }, { 'label': i18n("ISO Date"), 'name': "isoDate" }, { 'label': i18nc("custom date format", "Custom"), 'name': "custom" } ] onCurrentIndexChanged: cfg_dateFormat = model[currentIndex]["name"] Component.onCompleted: { for (var i = 0; i < model.length; i++) { - if (model[i]["name"] == plasmoid.configuration.dateFormat) { + if (model[i]["name"] === plasmoid.configuration.dateFormat) { dateFormat.currentIndex = i; } } } } QtControls.TextField { id: customDateFormat QtLayouts.Layout.fillWidth: true visible: cfg_dateFormat == "custom" } QtControls.Label { text: i18n("Time Format Documentation") visible: cfg_dateFormat == "custom" wrapMode: Text.Wrap QtLayouts.Layout.preferredWidth: QtLayouts.Layout.maximumWidth QtLayouts.Layout.maximumWidth: units.gridUnit * 16 onLinkActivated: Qt.openUrlExternally(link) MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton // We don't want to eat clicks on the Label cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor } } Item { Kirigami.FormData.isSection: true } QtLayouts.RowLayout { QtLayouts.Layout.fillWidth: true Kirigami.FormData.label: i18n("Font style:") QtControls.ComboBox { id: fontFamilyComboBox QtLayouts.Layout.fillWidth: true currentIndex: 0 // ComboBox's sizing is just utterly broken QtLayouts.Layout.minimumWidth: units.gridUnit * 10 model: fontsModel // doesn't autodeduce from model because we manually populate it textRole: "text" onCurrentIndexChanged: { var current = model.get(currentIndex) if (current) { cfg_fontFamily = current.value appearancePage.configurationChanged() } } } QtControls.Button { id: boldCheckBox QtControls.ToolTip { text: i18n("Bold text") } icon.name: "format-text-bold" checkable: true Accessible.name: tooltip } QtControls.Button { id: italicCheckBox QtControls.ToolTip { text: i18n("Italic text") } icon.name: "format-text-italic" checkable: true Accessible.name: tooltip } } } Item { QtLayouts.Layout.fillHeight: true } Component.onCompleted: { if (plasmoid.configuration.displayTimezoneAsCode) { timezoneCodeRadio.checked = true; } else { timezoneCityRadio.checked = true; } } } diff --git a/applets/digital-clock/package/contents/ui/configTimeZones.qml b/applets/digital-clock/package/contents/ui/configTimeZones.qml index 3ac84a7ed..2b20a3220 100644 --- a/applets/digital-clock/package/contents/ui/configTimeZones.qml +++ b/applets/digital-clock/package/contents/ui/configTimeZones.qml @@ -1,209 +1,209 @@ /* * Copyright 2013 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.0 import QtQuick.Controls 1.2 as QtControls import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.1 import org.kde.plasma.private.digitalclock 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents Item { id: timeZonesPage width: parent.width height: parent.height property alias cfg_selectedTimeZones: timeZones.selectedTimeZones property alias cfg_wheelChangesTimezone: enableWheelCheckBox.checked TimeZoneModel { id: timeZones onSelectedTimeZonesChanged: { - if (selectedTimeZones.length == 0) { + if (selectedTimeZones.length === 0) { messageWidget.visible = true; timeZones.selectLocalTimeZone(); } } } // This is just for getting the column width QtControls.CheckBox { id: checkbox visible: false } ColumnLayout { anchors.fill: parent Rectangle { id: messageWidget anchors { left: parent.left right: parent.right margins: 1 } height: 0 //TODO: This is the actual color KMessageWidget uses as its base color but here it gives // a different color, figure out why //property color gradBaseColor: Qt.rgba(0.69, 0.5, 0, 1) gradient: Gradient { GradientStop { position: 0.0; color: "#FFD86D" } //Qt.lighter(messageWidget.gradBaseColor, 1.1) GradientStop { position: 0.1; color: "#EAC360" } // messageWidget.gradBaseColor GradientStop { position: 1.0; color: "#CAB064" } //Qt.darker(messageWidget.gradBaseColor, 1.1) } radius: 5 border.width: 1 border.color: "#79735B" visible: false Behavior on visible { ParallelAnimation { PropertyAnimation { target: messageWidget property: "opacity" to: messageWidget.visible ? 0 : 1.0 easing.type: Easing.Linear } PropertyAnimation { target: messageWidget property: "Layout.minimumHeight" to: messageWidget.visible ? 0 : messageWidgetLabel.height + (2 *units.largeSpacing) easing.type: Easing.Linear } } } RowLayout { anchors.fill: parent anchors.margins: units.largeSpacing anchors.leftMargin: units.smallSpacing anchors.rightMargin: units.smallSpacing spacing: units.smallSpacing PlasmaCore.IconItem { anchors.verticalCenter: parent.verticalCenter height: units.iconSizes.smallMedium width: height source: "dialog-warning" } QtControls.Label { id: messageWidgetLabel anchors.verticalCenter: parent.verticalCenter Layout.fillWidth: true text: i18n("At least one time zone needs to be enabled. 'Local' was enabled automatically.") verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } PlasmaComponents.ToolButton { anchors.verticalCenter: parent.verticalCenter iconName: "dialog-close" flat: true onClicked: { messageWidget.visible = false; } } } } QtControls.TextField { id: filter Layout.fillWidth: true placeholderText: i18n("Search Time Zones") } QtControls.TableView { id: timeZoneView signal toggleCurrent Layout.fillWidth: true Layout.fillHeight: true Keys.onSpacePressed: toggleCurrent() model: TimeZoneFilterProxy { sourceModel: timeZones filterString: filter.text } QtControls.TableViewColumn { role: "checked" width: checkbox.width delegate: QtControls.CheckBox { id: checkBox anchors.centerIn: parent checked: styleData.value activeFocusOnTab: false // only let the TableView as a whole get focus onClicked: { //needed for model's setData to be called model.checked = checked; } Connections { target: timeZoneView onToggleCurrent: { if (styleData.row === timeZoneView.currentRow) { model.checked = !checkBox.checked } } } } resizable: false movable: false } QtControls.TableViewColumn { role: "city" title: i18n("City") } QtControls.TableViewColumn { role: "region" title: i18n("Region") } QtControls.TableViewColumn { role: "comment" title: i18n("Comment") } } RowLayout { Layout.fillWidth: true QtControls.CheckBox { id: enableWheelCheckBox text: i18n("Switch time zone with mouse wheel") } } } } diff --git a/applets/icon/package/contents/ui/main.qml b/applets/icon/package/contents/ui/main.qml index ce488dd57..f9da674d1 100644 --- a/applets/icon/package/contents/ui/main.qml +++ b/applets/icon/package/contents/ui/main.qml @@ -1,168 +1,168 @@ /* * Copyright 2013 Bhushan Shah * 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) 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.0 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as Components import org.kde.kquickcontrolsaddons 2.0 import org.kde.draganddrop 2.0 as DragDrop MouseArea { id: root - readonly property bool inPanel: (plasmoid.location == PlasmaCore.Types.TopEdge - || plasmoid.location == PlasmaCore.Types.RightEdge - || plasmoid.location == PlasmaCore.Types.BottomEdge - || plasmoid.location == PlasmaCore.Types.LeftEdge) + readonly property bool inPanel: (plasmoid.location === PlasmaCore.Types.TopEdge + || plasmoid.location === PlasmaCore.Types.RightEdge + || plasmoid.location === PlasmaCore.Types.BottomEdge + || plasmoid.location === PlasmaCore.Types.LeftEdge) readonly property bool constrained: plasmoid.formFactor === PlasmaCore.Types.Vertical || plasmoid.formFactor === PlasmaCore.Types.Horizontal property bool containsAcceptableDrag: false height: Math.round(units.iconSizes.desktop + 2 * theme.mSize(theme.defaultFont).height) width: Math.round(units.iconSizes.desktop * 1.5) Layout.minimumWidth: plasmoid.formFactor === PlasmaCore.Types.Horizontal ? height : units.iconSizes.small Layout.minimumHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical ? width : (units.iconSizes.small + 2 * theme.mSize(theme.defaultFont).height) Layout.maximumWidth: inPanel ? units.iconSizeHints.panel : -1 Layout.maximumHeight: inPanel ? units.iconSizeHints.panel : -1 hoverEnabled: true onClicked: plasmoid.nativeInterface.run() Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.icon: plasmoid.nativeInterface.iconName Plasmoid.title: plasmoid.nativeInterface.name Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground Plasmoid.onActivated: plasmoid.nativeInterface.run() Plasmoid.onContextualActionsAboutToShow: updateActions() Component.onCompleted: updateActions() function updateActions() { plasmoid.clearActions() plasmoid.removeAction("configure"); if (plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) { plasmoid.setAction("configure", i18n("Properties"), "document-properties"); } } function action_configure() { plasmoid.nativeInterface.configure() } Connections { target: plasmoid onExternalData: plasmoid.nativeInterface.url = data } DragDrop.DropArea { id: dropArea anchors.fill: parent preventStealing: true onDragEnter: { var acceptable = plasmoid.nativeInterface.isAcceptableDrag(event); root.containsAcceptableDrag = acceptable; if (!acceptable) { event.ignore(); } } onDragLeave: root.containsAcceptableDrag = false onDrop: { if (root.containsAcceptableDrag) { plasmoid.nativeInterface.processDrop(event) } else { event.ignore(); } root.containsAcceptableDrag = false } } PlasmaCore.IconItem { id: icon anchors{ left: parent.left right: parent.right top: parent.top bottom: constrained ? parent.bottom : text.top } source: plasmoid.icon enabled: root.enabled active: root.containsMouse || root.containsAcceptableDrag usesPlasmaTheme: false opacity: plasmoid.busy ? 0.6 : 1 } DropShadow { id: textShadow anchors.fill: text visible: !constrained horizontalOffset: units.devicePixelRatio * 2 verticalOffset: horizontalOffset radius: 9.0 samples: 18 spread: 0.15 color: "black" source: constrained ? null : text } Components.Label { id : text text : plasmoid.title anchors { left : parent.left bottom : parent.bottom right : parent.right } height: undefined // unset Label defaults horizontalAlignment : Text.AlignHCenter visible: false // rendered by DropShadow maximumLineCount: 2 color: "white" elide: Text.ElideRight wrapMode: Text.WrapAtWordBoundaryOrAnywhere textFormat: Text.PlainText } PlasmaCore.ToolTipArea { anchors.fill: parent mainText: plasmoid.title subText: plasmoid.nativeInterface.genericName !== mainText ? plasmoid.nativeInterface.genericName :"" icon: plasmoid.icon textFormat: Text.PlainText } } diff --git a/applets/lock_logout/contents/ui/lockout.qml b/applets/lock_logout/contents/ui/lockout.qml index 1f6f64bed..2f04109c8 100644 --- a/applets/lock_logout/contents/ui/lockout.qml +++ b/applets/lock_logout/contents/ui/lockout.qml @@ -1,179 +1,179 @@ /* * Copyright 2011 Viranch Mehta * * 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 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 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.0 import QtQuick.Layouts 1.0 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 import org.kde.kquickcontrolsaddons 2.0 import "data.js" as Data Flow { id: lockout Layout.minimumWidth: { if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { return 0 } else if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { return height < minButtonSize * visibleButtons ? height * visibleButtons : height / visibleButtons - 1; } else { return width > height ? minButtonSize * visibleButtons : minButtonSize } } Layout.minimumHeight: { if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { return width >= minButtonSize * visibleButtons ? width / visibleButtons - 1 : width * visibleButtons } else if (plasmoid.formFactor === PlasmaCore.Types.Horizontal) { return 0 } else { return width > height ? minButtonSize : minButtonSize * visibleButtons } } Layout.preferredWidth: Layout.minimumWidth Layout.preferredHeight: Layout.minimumHeight readonly property int minButtonSize: units.iconSizes.small Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation readonly property int visibleButtons: { var count = 0 for (var i = 0, j = items.count; i < j; ++i) { if (items.itemAt(i).visible) { ++count } } return count } flow: { if ((plasmoid.formFactor === PlasmaCore.Types.Vertical && width >= minButtonSize * visibleButtons) || (plasmoid.formFactor === PlasmaCore.Types.Horizontal && height < minButtonSize * visibleButtons) || (width > height)) { return Flow.LeftToRight // horizontal } else { return Flow.TopToBottom // vertical } } PlasmaCore.DataSource { id: dataEngine engine: "powermanagement" connectedSources: ["PowerDevil", "Sleep States"] } Repeater { id: items property int itemWidth: parent.flow==Flow.LeftToRight ? Math.floor(parent.width/visibleButtons) : parent.width property int itemHeight: parent.flow==Flow.TopToBottom ? Math.floor(parent.height/visibleButtons) : parent.height property int iconSize: Math.min(itemWidth, itemHeight) model: Data.data delegate: Item { id: iconDelegate visible: plasmoid.configuration["show_" + modelData.operation] && (!modelData.hasOwnProperty("requires") || dataEngine.data["Sleep States"][modelData.requires]) width: items.itemWidth height: items.itemHeight PlasmaCore.IconItem { id: iconButton width: items.iconSize height: items.iconSize anchors.centerIn: parent source: modelData.icon scale: mouseArea.pressed ? 0.9 : 1 active: mouseArea.containsMouse MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true onReleased: clickHandler(modelData.operation, this) PlasmaCore.ToolTipArea { anchors.fill: parent mainText: modelData.tooltip_mainText subText: modelData.tooltip_subText icon: modelData.icon } } } } } Component { id: hibernateDialogComponent QueryDialog { titleIcon: "system-suspend-hibernate" titleText: i18n("Hibernate") message: i18n("Do you want to suspend to disk (hibernate)?") location: plasmoid.location acceptButtonText: i18n("Yes") rejectButtonText: i18n("No") onAccepted: performOperation("suspendToDisk") } } property QueryDialog hibernateDialog Component { id: sleepDialogComponent QueryDialog { titleIcon: "system-suspend" titleText: i18nc("Suspend to RAM", "Sleep") message: i18n("Do you want to suspend to RAM (sleep)?") location: plasmoid.location acceptButtonText: i18n("Yes") rejectButtonText: i18n("No") onAccepted: performOperation("suspendToRam") } } property QueryDialog sleepDialog function clickHandler(what, button) { - if (what == "suspendToDisk") { + if (what === "suspendToDisk") { if (!hibernateDialog) { hibernateDialog = hibernateDialogComponent.createObject(lockout); } hibernateDialog.visualParent = button hibernateDialog.open(); - } else if (what == "suspendToRam") { + } else if (what === "suspendToRam") { if (!sleepDialog) { sleepDialog = sleepDialogComponent.createObject(lockout); } sleepDialog.visualParent = button sleepDialog.open(); } else { performOperation(what); } } function performOperation(what) { var service = dataEngine.serviceForSource("PowerDevil"); var operation = service.operationDescription(what); service.startOperationCall(operation); } } diff --git a/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml b/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml index ff445163d..c54fe40d7 100644 --- a/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml +++ b/applets/mediacontroller/contents/ui/ExpandedRepresentation.qml @@ -1,432 +1,432 @@ /*************************************************************************** * 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 3.0 as PlasmaComponents3 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: units.iconSizes.large property double position: mpris2Source.currentData.Position || 0 readonly property real rate: mpris2Source.currentData.Rate || 1 readonly property double length: currentMetadata ? currentMetadata["mpris:length"] || 0 : 0 readonly property bool canSeek: mpris2Source.currentData.CanSeek || false // only show hours (the default for KFormat) when track is actually longer than an hour readonly property int durationFormattingOptions: length >= 60*60*1000*1000 ? 0 : KCoreAddons.FormatTypes.FoldHours 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.to = 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.togglePlaying() } 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 seekSlider.moved(); } else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) { // seek forward 5s seekSlider.value = Math.min(seekSlider.to, seekSlider.value + 5000000) seekSlider.moved(); } else if (event.key === Qt.Key_Home) { seekSlider.value = 0 seekSlider.moved(); } else if (event.key === Qt.Key_End) { seekSlider.value = seekSlider.to seekSlider.moved(); } 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.to * (event.key - Qt.Key_0) / 10 seekSlider.moved(); } else { event.accepted = false } } } PlasmaComponents3.ComboBox { id: playerCombo width: Math.round(0.6 * parent.width) height: visible ? undefined : 0 anchors.horizontalCenter: parent.horizontalCenter textRole: "text" 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 } } Item { anchors { horizontalCenter: parent.horizontalCenter top: playerCombo.bottom bottom: controlCol.top margins: units.smallSpacing } PlasmaCore.IconItem { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } height: Math.round(parent.height / 2) width: height source: mpris2Source.currentData["Desktop Icon Name"] visible: !albumArt.visible usesPlasmaTheme: false } } Image { id: albumArt anchors { horizontalCenter: parent.horizontalCenter top: playerCombo.bottom bottom: controlCol.top margins: units.smallSpacing } source: root.albumArt asynchronous: true fillMode: Image.PreserveAspectFit sourceSize: Qt.size(height, height) visible: !!root.track && status === Image.Ready } Column { id: controlCol width: parent.width anchors.bottom: parent.bottom spacing: units.smallSpacing RowLayout { anchors { left: parent.left right: parent.right margins: units.smallSpacing } 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 && expandedRepresentation.length > 0 ? 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.to / 1000, expandedRepresentation.durationFormattingOptions)) font: theme.smallestFont } PlasmaComponents3.Label { Layout.preferredWidth: timeMetrics.width verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignRight text: KCoreAddons.Format.formatDuration(seekSlider.value / 1000, expandedRepresentation.durationFormattingOptions) opacity: 0.9 font: theme.smallestFont } PlasmaComponents3.Slider { id: seekSlider Layout.fillWidth: true z: 999 value: 0 visible: canSeek onMoved: { 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 && seekSlider.to >= 1000000 + running: root.state === "playing" && plasmoid.expanded && !keyPressed && interval > 0 && seekSlider.to >= 1000000 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.to) { retrievePosition(); } else { seekSlider.value += 1000000 } disablePositionUpdate = false } } } } PlasmaComponents3.ProgressBar { Layout.fillWidth: true value: seekSlider.value from: seekSlider.from to: seekSlider.to visible: !canSeek } PlasmaComponents3.Label { Layout.preferredWidth: timeMetrics.width verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft text: i18nc("Remaining time for song e.g -5:42", "-%1", KCoreAddons.Format.formatDuration((seekSlider.to - seekSlider.value) / 1000, expandedRepresentation.durationFormattingOptions)) opacity: 0.9 font: theme.smallestFont } } Column { width: parent.width PlasmaExtras.Heading { id: song width: parent.width height: undefined level: 4 horizontalAlignment: Text.AlignHCenter maximumLineCount: 1 elide: Text.ElideRight text: { if (!root.track) { return i18n("No media playing") } return root.artist ? i18nc("artist – track", "%1 – %2", root.artist, root.track) : root.track } textFormat: Text.PlainText } PlasmaExtras.Heading { width: parent.width height: undefined level: 5 opacity: 0.6 horizontalAlignment: Text.AlignHCenter 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 "" } textFormat: Text.PlainText } } Item { 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 PlasmaComponents3.ToolButton { anchors.verticalCenter: parent.verticalCenter width: expandedRepresentation.controlSize height: width enabled: playerControls.enabled && root.canGoPrevious icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" onClicked: { seekSlider.value = 0 // Let the media start from beginning. Bug 362473 root.action_previous() } } PlasmaComponents3.ToolButton { width: Math.round(expandedRepresentation.controlSize * 1.5) height: width enabled: root.state == "playing" ? root.canPause : root.canPlay icon.name: root.state == "playing" ? "media-playback-pause" : "media-playback-start" onClicked: root.togglePlaying() } PlasmaComponents3.ToolButton { anchors.verticalCenter: parent.verticalCenter width: expandedRepresentation.controlSize height: width enabled: playerControls.enabled && root.canGoNext icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" onClicked: { seekSlider.value = 0 // Let the media start from beginning. Bug 362473 root.action_next() } } } } } 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) } } } diff --git a/applets/notifications/package/contents/ui/Jobs.qml b/applets/notifications/package/contents/ui/Jobs.qml index a0fd74131..fd27471fd 100644 --- a/applets/notifications/package/contents/ui/Jobs.qml +++ b/applets/notifications/package/contents/ui/Jobs.qml @@ -1,170 +1,170 @@ /* * Copyright 2012 Marco Martin * * 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, 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.0 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.plasma.private.notifications 1.0 Column { id: jobsRoot width: parent.width property alias count: jobs.count ListModel { id: jobs } PlasmaCore.DataSource { id: jobsSource property var runningJobs: ({}) engine: "applicationjobs" interval: 0 onSourceAdded: { connectSource(source) jobs.append({name: source}) } onSourceRemoved: { // remove source from jobs model for (var i = 0, len = jobs.count; i < len; ++i) { if (jobs.get(i).name === source) { jobs.remove(i) break } } if (!notifications) { delete runningJobs[source] return } var error = runningJobs[source]["error"] var errorText = runningJobs[source]["errorText"] // 1 = ERR_USER_CANCELED - don't show any notification at all - if (error == 1) { + if (error === 1) { delete runningJobs[source] return } var message = runningJobs[source]["label1"] ? runningJobs[source]["label1"] : runningJobs[source]["label0"] var infoMessage = runningJobs[source]["infoMessage"] if (!message && !infoMessage) { delete runningJobs[source] return } var summary = infoMessage ? i18nc("the job, which can be anything, has finished", "%1: Finished", infoMessage) : i18n("Job Finished") if (error) { summary = infoMessage ? i18nc("the job, which can be anything, failed to complete", "%1: Failed", infoMessage) : i18n("Job Failed") } // notification body interprets HTML, so we need to manually escape the name var body = (errorText || message || "").replace(/[&<>]/g, function (tag) { return { '&': '&', '<': '<', '>': '>' }[tag] || tag }); var op = { appIcon: runningJobs[source].appIconName, appName: runningJobs[source].appName, summary: summary, body: body, isPersistent: !!error, // we'll assume success to be the note-unworthy default, only be persistent in error case urgency: 0, configurable: false, skipGrouping: true, // Bug 360156 actions: !error && UrlHelper.isUrlValid(message) ? ["jobUrl#" + message, i18n("Open...")] : [] }; // If the actionId contains "jobUrl#", it tries to open the "id" value (which is "message") notifications.createNotification(op); delete runningJobs[source] } onNewData: { runningJobs[sourceName] = data } onDataChanged: { var total = 0 for (var i = 0; i < sources.length; ++i) { if (jobsSource.data[sources[i]] && jobsSource.data[sources[i]]["percentage"]) { total += jobsSource.data[sources[i]]["percentage"] } } total /= sources.length notificationsApplet.globalProgress = total/100 } Component.onCompleted: { connectedSources = sources } } Item { visible: jobs.count > 3 PlasmaComponents.ProgressBar { anchors { verticalCenter: parent.verticalCenter left: parent.left right: parent.right } minimumValue: 0 maximumValue: 100 value: notificationsApplet.globalProgress * 100 } } Repeater { id: jobsRepeater model: jobs delegate: JobDelegate { infoMessageVisible: { if (!infoMessage) { return false; } // hide info message if it's the same as the previous job, while we don't // actively group those jobs, it still improves the situation where you // started copying a couple of different things simultaneously var previousItem = jobsRepeater.itemAt(index - 1); if (!previousItem) { return true; } return previousItem.infoMessage !== infoMessage; } } } } diff --git a/applets/notifications/package/contents/ui/NotificationItem.qml b/applets/notifications/package/contents/ui/NotificationItem.qml index e7b748a5f..7025d7de9 100644 --- a/applets/notifications/package/contents/ui/NotificationItem.qml +++ b/applets/notifications/package/contents/ui/NotificationItem.qml @@ -1,418 +1,418 @@ /* * Copyright 2011 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 Library General Public License as * published by the Free Software Foundation; either version 2, 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.5 import QtQuick.Layouts 1.1 import QtQuick.Controls.Private 1.0 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.kquickcontrolsaddons 2.0 MouseArea { id: notificationItem width: parent.width implicitHeight: bodyText.lineCount > 1 ? mainLayout.height : (appIconItem.valid || imageItem.nativeWidth > 0 ? (Math.max((mainLayout.height + 2 * units.smallSpacing),(units.iconSizes.large + 2 * units.smallSpacing))) : (bottomPart.height != 0 ? (mainLayout.height + 2 * units.smallSpacing) : (mainLayout.height + units.smallSpacing))) // We need to clip here because we support displaying images through // and if we don't clip, they will be painted over the borders of the dialog/item clip: true signal close signal configure signal action(string actionId) signal openUrl(url url) property bool compact: false property alias icon: appIconItem.source property alias image: imageItem.image property alias summary: summaryLabel.text property alias body: bodyText.text property alias configurable: settingsButton.visible property var created property var urls: [] property int maximumTextHeight: -1 property ListModel actions: ListModel { } property bool hasDefaultAction: false property bool hasConfigureAction: false readonly property bool dragging: thumbnailStripLoader.item ? thumbnailStripLoader.item.dragging : false onClicked: { // the MEL would close the notification before the action button // onClicked handler would fire effectively breaking notification actions if (pressedAction()) { return } if (hasDefaultAction) { // the notification was clicked, trigger the default action if set action("default") } } function pressedAction() { for (var i = 0, count = actionRepeater.count; i < count; ++i) { var item = actionRepeater.itemAt(i) if (item.pressed) { return item } } if (thumbnailStripLoader.item) { var item = thumbnailStripLoader.item.pressedAction() if (item) { return item } } if (settingsButton.pressed) { return settingsButton } if (closeButton.pressed) { return closeButton } return null } function updateTimeLabel() { if (!created || created.getTime() <= 0) { timeLabel.text = "" return } var currentTime = new Date().getTime() var createdTime = created.getTime() var d = (currentTime - createdTime) / 1000 if (d < 10) { timeLabel.text = i18nc("notification was just added, keep short", "Just now") } else if (d < 20) { timeLabel.text = i18nc("10 seconds ago, keep short", "10 s ago"); } else if (d < 40) { timeLabel.text = i18nc("30 seconds ago, keep short", "30 s ago"); } else if (d < 60 * 60) { timeLabel.text = i18ncp("minutes ago, keep short", "%1 min ago", "%1 min ago", Math.round(d / 60)) } else if (d <= 60 * 60 * 23) { timeLabel.text = Qt.formatTime(created, Qt.locale().timeFormat(Locale.ShortFormat).replace(/.ss?/i, "")) } else { var yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) // this will wrap yesterday.setHours(0) yesterday.setMinutes(0) yesterday.setSeconds(0) if (createdTime > yesterday.getTime()) { timeLabel.text = i18nc("notification was added yesterday, keep short", "Yesterday"); } else { timeLabel.text = i18ncp("notification was added n days ago, keep short", "%1 day ago", "%1 days ago", Math.round((currentTime - yesterday.getTime()) / 1000 / 3600 / 24)); } } } Timer { interval: 15000 running: plasmoid.expanded repeat: true triggeredOnStart: true onTriggered: updateTimeLabel() } PlasmaCore.IconItem { id: appIconItem width: units.iconSizes.large height: units.iconSizes.large anchors { top: parent.top left: parent.left leftMargin: units.smallSpacing topMargin: units.smallSpacing } - visible: imageItem.nativeWidth == 0 && valid + visible: imageItem.nativeWidth === 0 && valid animated: false } QImageItem { id: imageItem anchors.fill: appIconItem smooth: true visible: nativeWidth > 0 } ColumnLayout { id: mainLayout anchors { top: parent.top topMargin: bodyText.lineCount > 1 ? 0 : units.smallSpacing // Lift up heading if bodyText is long left: appIconItem.valid || imageItem.nativeWidth > 0 ? appIconItem.right : parent.left right: parent.right leftMargin: units.smallSpacing * 2 rightMargin: units.smallSpacing // Equal padding on either side (notification icon margin) } spacing: Math.round(units.smallSpacing / 2) RowLayout { id: titleBar spacing: units.smallSpacing PlasmaExtras.Heading { id: summaryLabel Layout.fillWidth: true Layout.fillHeight: true height: undefined verticalAlignment: Text.AlignVCenter level: 4 elide: Text.ElideRight wrapMode: Text.NoWrap textFormat: Text.PlainText } PlasmaExtras.Heading { id: timeLabel Layout.fillHeight: true level: 5 visible: text !== "" verticalAlignment: Text.AlignVCenter PlasmaCore.ToolTipArea { anchors.fill: parent subText: Qt.formatDateTime(created, Qt.DefaultLocaleLongDate) } } PlasmaComponents.ToolButton { id: settingsButton width: units.iconSizes.smallMedium height: width visible: false iconSource: "configure" onClicked: { if (notificationItem.hasConfigureAction) { notificationItem.action("settings"); } else { configure() } } } PlasmaComponents.ToolButton { id: closeButton width: units.iconSizes.smallMedium height: width flat: compact iconSource: "window-close" onClicked: close() } } RowLayout { id: bottomPart Layout.alignment: Qt.AlignTop spacing: units.smallSpacing // Force the whole thing to collapse if the children are invisible // If there is a big notification followed by a small one, the height // of the popup does not always shrink back, so this forces it to // height=0 when those are invisible. -1 means "default to implicitHeight" Layout.maximumHeight: bodyText.length > 0 || notificationItem.actions.count > 0 ? -1 : 0 PlasmaExtras.ScrollArea { id: bodyTextScrollArea Layout.alignment: Qt.AlignTop Layout.fillWidth: true implicitHeight: maximumTextHeight > 0 ? Math.min(maximumTextHeight, bodyText.paintedHeight) : bodyText.paintedHeight visible: bodyText.length > 0 flickableItem.boundsBehavior: Flickable.StopAtBounds flickableItem.flickableDirection: Flickable.VerticalFlick horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff TextEdit { id: bodyText width: bodyTextScrollArea.width enabled: !Settings.isMobile color: PlasmaCore.ColorScope.textColor selectedTextColor: theme.viewBackgroundColor selectionColor: theme.viewFocusColor font.capitalization: theme.defaultFont.capitalization font.family: theme.defaultFont.family font.italic: theme.defaultFont.italic font.letterSpacing: theme.defaultFont.letterSpacing font.pointSize: theme.defaultFont.pointSize font.strikeout: theme.defaultFont.strikeout font.underline: theme.defaultFont.underline font.weight: theme.defaultFont.weight font.wordSpacing: theme.defaultFont.wordSpacing renderType: Text.NativeRendering selectByMouse: true readOnly: true wrapMode: Text.Wrap textFormat: TextEdit.RichText // ensure selecting text scrolls the view as needed... onCursorRectangleChanged: { var flick = bodyTextScrollArea.flickableItem if (flick.contentY >= cursorRectangle.y) { flick.contentY = cursorRectangle.y } else if (flick.contentY + flick.height <= cursorRectangle.y + cursorRectangle.height) { flick.contentY = cursorRectangle.y + cursorRectangle.height - flick.height } } MouseArea { property int selectionStart property point mouseDownPos: Qt.point(-999, -999); anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton cursorShape: bodyText.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor preventStealing: true // don't let us accidentally drag the Flickable onPressed: { if (mouse.button === Qt.RightButton) { contextMenu.link = bodyText.linkAt(mouse.x, mouse.y); contextMenu.open(mouse.x, mouse.y); return; } mouseDownPos = Qt.point(mouse.x, mouse.y); selectionStart = bodyText.positionAt(mouse.x, mouse.y); var pos = bodyText.positionAt(mouse.x, mouse.y); // deselect() would scroll to the end which we don't want bodyText.select(pos, pos); } onReleased: { // emulate "onClicked" var manhattanLength = Math.abs(mouseDownPos.x - mouse.x) + Math.abs(mouseDownPos.y - mouse.y); if (manhattanLength <= Qt.styleHints.startDragDistance) { var link = bodyText.linkAt(mouse.x, mouse.y); if (link) { Qt.openUrlExternally(link); } else { notificationItem.clicked(null/*mouse*/); } } mouseDownPos = Qt.point(-999, -999); } // HACK to be able to select text whilst still getting all mouse events to the MouseArea onPositionChanged: { if (pressed) { var pos = bodyText.positionAt(mouseX, mouseY); if (selectionStart < pos) { bodyText.select(selectionStart, pos); } else { bodyText.select(pos, selectionStart); } } } Clipboard { id: clipboard } PlasmaComponents.ContextMenu { id: contextMenu property string link PlasmaComponents.MenuItem { text: i18n("Copy Link Address") onClicked: clipboard.content = contextMenu.link visible: contextMenu.link !== "" } PlasmaComponents.MenuItem { separator: true visible: contextMenu.link !== "" } PlasmaComponents.MenuItem { text: i18n("Copy") icon: "edit-copy" enabled: bodyText.selectionStart !== bodyText.selectionEnd onClicked: bodyText.copy() } PlasmaComponents.MenuItem { text: i18n("Select All") onClicked: bodyText.selectAll() } } } } } ColumnLayout { id: actionsColumn Layout.alignment: Qt.AlignTop Layout.maximumWidth: theme.mSize(theme.defaultFont).width * (compact ? 10 : 16) // this is so it never collapses but always follows what the Buttons below want // but also don't let the buttons get too narrow (e.g. "View" or "Open" button) Layout.minimumWidth: Math.max(units.gridUnit * 4, implicitWidth) spacing: units.smallSpacing visible: notificationItem.actions && notificationItem.actions.count > 0 Repeater { id: actionRepeater model: notificationItem.actions PlasmaComponents.Button { Layout.fillWidth: true Layout.preferredWidth: minimumWidth Layout.maximumWidth: actionsColumn.Layout.maximumWidth text: model.text tooltip: width < minimumWidth ? text : "" onClicked: notificationItem.action(model.id) } } } } Loader { id: thumbnailStripLoader Layout.fillWidth: true Layout.preferredHeight: item ? item.implicitHeight : 0 source: "ThumbnailStrip.qml" active: notificationItem.urls.length > 0 } } } diff --git a/applets/notifications/package/contents/ui/Notifications.qml b/applets/notifications/package/contents/ui/Notifications.qml index 23b34a319..9032f8a52 100644 --- a/applets/notifications/package/contents/ui/Notifications.qml +++ b/applets/notifications/package/contents/ui/Notifications.qml @@ -1,328 +1,328 @@ /* * Copyright 2012 Marco Martin * * 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, 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.0 import QtQuick.Layouts 1.2 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.plasma.private.notifications 1.0 Column { id: notificationsRoot anchors { left: parent.left right: parent.right } property alias count: notificationsRepeater.count readonly property int historyCount: historyList.count property bool showHistory: plasmoid.configuration.showHistory signal popupShown(var popup) onShowHistoryChanged: { if(!showHistory) clearHistory() } Component.onCompleted: { // Create the popup components and pass them to the C++ plugin for (var i = 0; i < 3; i++) { var popup = notificationPopupComponent.createObject(); notificationPositioner.addNotificationPopup(popup); } } function addNotification(notification) { // Do not show duplicated notifications for (var i = 0; i < notificationsModel.count; ++i) { - if (notificationsModel.get(i).source == notification.source && - notificationsModel.get(i).appName == notification.appName && - notificationsModel.get(i).summary == notification.summary && - notificationsModel.get(i).body == notification.body) { + if (notificationsModel.get(i).source === notification.source && + notificationsModel.get(i).appName === notification.appName && + notificationsModel.get(i).summary === notification.summary && + notificationsModel.get(i).body === notification.body) { return } } for (var i = 0; i < notificationsModel.count; ++i) { - if (notificationsModel.get(i).source == notification.source || - (notificationsModel.get(i).appName == notification.appName && - notificationsModel.get(i).summary == notification.summary && - notificationsModel.get(i).body == notification.body)) { + if (notificationsModel.get(i).source === notification.source || + (notificationsModel.get(i).appName === notification.appName && + notificationsModel.get(i).summary === notification.summary && + notificationsModel.get(i).body === notification.body)) { notificationsModel.remove(i) break } } if (notificationsModel.count > 20) { notificationsModel.remove(notificationsModel.count-1) } if (notification.isPersistent) { notification.created = new Date(); notificationsModel.inserting = true; notificationsModel.insert(0, notification); notificationsModel.inserting = false; } else if (showHistory) { notificationsHistoryModel.inserting = true; //Disable actions in this copy as they will stop working once the original notification is closed. //Only the jobUrl (which is a URL to open) can continue working as we'll handle this internally. var actions = notification.actions.filter(function (item) { return item.id.indexOf("jobUrl#") === 0; }); //create a copy of the notification. //Disable actions in this copy as they will stop working once the original notification is closed. notificationsHistoryModel.insert(0, { "compact" : notification.compact, "icon" : notification.icon, "image" : notification.image, "summary" : notification.summary, "body" : notification.body, "configurable" : false, "created" : new Date(), "urls" : notification.urls, "maximumTextHeight" : notification.maximumTextHeight, "actions" : actions, "hasDefaultAction" : false, "hasConfigureAction" : false, }); notificationsHistoryModel.inserting = false; } notificationPositioner.displayNotification(notification); } function executeAction(source, id) { //try to use the service if (id.indexOf("jobUrl#") === -1) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("invokeAction") op["actionId"] = id service.startOperationCall(op) //try to open the id as url } else if (id.indexOf("jobUrl#") !== -1) { Qt.openUrlExternally(id.slice(7)); } notificationPositioner.closePopup(source); } function configureNotification(appRealName, eventId) { var service = notificationsSource.serviceForSource("notification") var op = service.operationDescription("configureNotification") op.appRealName = appRealName op.eventId = eventId service.startOperationCall(op) } function createNotification(data) { var service = notificationsSource.serviceForSource("notification"); var op = service.operationDescription("createNotification"); // add everything from "data" to "op" for (var attrname in data) { op[attrname] = data[attrname]; } service.startOperationCall(op); } function closeNotification(source) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("userClosed") service.startOperationCall(op) } function expireNotification(source) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("expireNotification") service.startOperationCall(op) } function clearNotifications() { for (var i = 0, length = notificationsSource.sources.length; i < length; ++i) { var source = notificationsSource.sources[i]; closeNotification(source) notificationPositioner.closePopup(source); } notificationsModel.clear() clearHistory() } function clearHistory() { notificationsHistoryModel.clear() } Component { id: notificationPopupComponent NotificationPopup { } } ListModel { id: notificationsModel property bool inserting: false } ListModel { id: notificationsHistoryModel property bool inserting: false } PlasmaCore.DataSource { id: idleTimeSource property bool idle: data["UserActivity"]["IdleTime"] > 300000 engine: "powermanagement" interval: 30000 connectedSources: ["UserActivity"] //Idle with more than 5 minutes of user inactivity } PlasmaCore.DataSource { id: notificationsSource engine: "notifications" interval: 0 onSourceAdded: { connectSource(source); } onSourceRemoved: { notificationPositioner.closePopup(source); for (var i = 0; i < notificationsModel.count; ++i) { - if (notificationsModel.get(i).source == source) { + if (notificationsModel.get(i).source === source) { notificationsModel.remove(i) break } } } onNewData: { var _data = data; // Temp copy to avoid lots of context switching var actions = [] _data["hasDefaultAction"] = false _data["hasConfigureAction"] = false; if (data["actions"] && data["actions"].length % 2 == 0) { for (var i = 0; i < data["actions"].length; i += 2) { var action = data["actions"][i] - if (action == "default") { // The default action is not shown, but we want to know it's there + if (action === "default") { // The default action is not shown, but we want to know it's there _data["hasDefaultAction"] = true - } else if (action == "settings") { // configure icon in the notification for custom notification settings + } else if (action === "settings") { // configure icon in the notification for custom notification settings _data["hasConfigureAction"] = true; _data["configurable"] = true; } else { actions.push({ id: data["actions"][i], text: data["actions"][i+1] }) } } } _data["source"] = sourceName _data["actions"] = actions notificationsRoot.addNotification(_data) } } Connections { target: plasmoid.nativeInterface onAvailableScreenRectChanged: { notificationPositioner.setPlasmoidScreenGeometry(availableScreenRect); } } NotificationsHelper { id: notificationPositioner popupLocation: plasmoid.nativeInterface.screenPosition Component.onCompleted: { notificationPositioner.setPlasmoidScreenGeometry(plasmoid.nativeInterface.availableScreenRect); } onPopupShown: notificationsRoot.popupShown(popup) } Repeater { id: notificationsRepeater model: notificationsModel delegate: NotificationDelegate { listModel: notificationsModel } } RowLayout { Layout.fillWidth: true spacing: units.smallSpacing visible: historyCount > 0 width: parent.width PlasmaExtras.Heading { Layout.fillWidth: true level: 3 opacity: 0.6 text: i18n("History") } PlasmaComponents.ToolButton { Layout.rightMargin: spacerSvgFrame.margins.right iconSource: "edit-clear-history" tooltip: i18n("Clear History") onClicked: clearHistory() } } // This hack is unfortunately needed to have the buttons align, // the ones in the list contain have a margin due to a frame for being a list item. PlasmaCore.FrameSvgItem { id : spacerSvgFrame imagePath: "widgets/listitem" prefix: "normal" visible: false } // History stuff // The history is shown outside in a ListView Binding { target: historyList property: "model" value: notificationsHistoryModel when: showHistory } Binding { target: historyList property: "delegate" value: notificationsHistoryDelegate when: showHistory } Component { id: notificationsHistoryDelegate NotificationDelegate { listModel: notificationsHistoryModel } } } diff --git a/applets/notifications/package/contents/ui/configNotifications.qml b/applets/notifications/package/contents/ui/configNotifications.qml index 2f73e26a9..9d6f52d62 100644 --- a/applets/notifications/package/contents/ui/configNotifications.qml +++ b/applets/notifications/package/contents/ui/configNotifications.qml @@ -1,75 +1,75 @@ /* * 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) 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.0 import QtQuick.Controls 1.0 as QtControls import QtQuick.Layouts 1.1 as QtLayouts import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.private.notifications 1.0 Item { id: appearancePage width: childrenRect.width height: childrenRect.height signal configurationChanged property alias cfg_showNotifications: showNotificationsCheckBox.checked property alias cfg_showJobs: showJobsCheckBox.checked property alias cfg_showHistory: showHistoryCheckBox.checked QtLayouts.ColumnLayout { anchors.left: parent.left QtControls.CheckBox { id: showNotificationsCheckBox text: i18n("Show application and system notifications") } QtControls.CheckBox { id: showHistoryCheckBox text: i18n("Show a history of notifications") enabled: showNotificationsCheckBox.checked } QtControls.CheckBox { id: showJobsCheckBox text: i18n("Track file transfers and other jobs") } QtControls.CheckBox { id: useCustomPopupPositionCheckBox text: i18n("Use custom position for the notification popup") - checked: plasmoid.nativeInterface.configScreenPosition() != NotificationsHelper.Default + checked: plasmoid.nativeInterface.configScreenPosition() !== NotificationsHelper.Default } ScreenPositionSelector { id: screenPositionSelector enabled: useCustomPopupPositionCheckBox.checked selectedPosition: plasmoid.nativeInterface.screenPosition disabledPositions: [NotificationsHelper.Left, NotificationsHelper.Center, NotificationsHelper.Right] } } Component.onCompleted: { plasmoid.nativeInterface.screenPosition = Qt.binding(function() {configurationChanged(); return screenPositionSelector.selectedPosition; }); } } diff --git a/applets/panelspacer/contents/ui/main.qml b/applets/panelspacer/contents/ui/main.qml index 4d4302f52..b5c0a0358 100644 --- a/applets/panelspacer/contents/ui/main.qml +++ b/applets/panelspacer/contents/ui/main.qml @@ -1,54 +1,54 @@ /* * Copyright 2014 Marco Martin * * 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons Item { id: root z: 9999 - property bool horizontal: plasmoid.formFactor != PlasmaCore.Types.Vertical + property bool horizontal: plasmoid.formFactor !== PlasmaCore.Types.Vertical Layout.fillWidth: plasmoid.configuration.expanding Layout.fillHeight: plasmoid.configuration.expanding Layout.minimumWidth: 1 Layout.minimumHeight: 1 Layout.preferredWidth: horizontal ? plasmoid.configuration.length : 0 Layout.preferredHeight: horizontal ? 0 : plasmoid.configuration.length Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation function action_expanding() { plasmoid.configuration.expanding = plasmoid.action("expanding").checked; } Component.onCompleted: { plasmoid.setAction("expanding", i18n("Set flexible size")); var action = plasmoid.action("expanding"); action.checkable = true; action.checked = plasmoid.configuration.expanding; plasmoid.removeAction("configure"); } } diff --git a/applets/systemmonitor/common/contents/ui/Applet.qml b/applets/systemmonitor/common/contents/ui/Applet.qml index 19adba004..9055a7a67 100644 --- a/applets/systemmonitor/common/contents/ui/Applet.qml +++ b/applets/systemmonitor/common/contents/ui/Applet.qml @@ -1,157 +1,157 @@ /* * Copyright 2014 Marco Martin * * * 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 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 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 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 Item { id: rootItem signal sourceAdded(string source) property Component delegate width: units.gridUnit * 10 height: units.gridUnit * 10 Plasmoid.preferredRepresentation: plasmoid.fullRepresentation - Layout.minimumWidth: units.gridUnit * 12 * (plasmoid.formFactor == PlasmaCore.Types.Horizontal ? sourcesModel.count : 1) - Layout.minimumHeight: units.gridUnit * 4 * (plasmoid.formFactor == PlasmaCore.Types.Vertical ? sourcesModel.count : 1) + Layout.minimumWidth: units.gridUnit * 12 * (plasmoid.formFactor === PlasmaCore.Types.Horizontal ? sourcesModel.count : 1) + Layout.minimumHeight: units.gridUnit * 4 * (plasmoid.formFactor === PlasmaCore.Types.Vertical ? sourcesModel.count : 1) Layout.preferredHeight: Layout.minimumHeight function addSource(source1, friendlyName1, source2, friendlyName2) { var found = false; for (var i = 0; i < sourcesModel.count; ++i) { var obj = sourcesModel.get(i); - if (obj.source1 == encodeURIComponent(source1) && - obj.source2 == encodeURIComponent(source2)) { + if (obj.source1 === encodeURIComponent(source1) && + obj.source2 === encodeURIComponent(source2)) { found = true; break; } } if (found) { return; } smSource.connectSource(source1); if (source2) { smSource.connectSource(source2); } sourcesModel.append( {"source1": encodeURIComponent(source1), "friendlyName1": friendlyName1, "source2": encodeURIComponent(source2), "friendlyName2": friendlyName2, "dataSource": smSource}); } ListModel { id: sourcesModel } Component.onCompleted: { for (var i in smSource.sources) { smSource.sourceAdded(smSource.sources[i]); } } PlasmaCore.DataSource { id: smSource engine: "systemmonitor" interval: plasmoid.configuration.updateInterval onSourceAdded: { if (plasmoid.configuration.sources && plasmoid.configuration.sources.length > 0 && plasmoid.configuration.sources.indexOf(encodeURIComponent(source)) === -1) { return; } rootItem.sourceAdded(source); } onSourceRemoved: { for (var i = sourcesModel.count - 1; i >= 0; --i) { var obj = sourcesModel.get(i); - if (obj.source1 == source || obj.source2 == source) { + if (obj.source1 === source || obj.source2 === source) { sourcesModel.remove(i); } } smSource.disconnectSource(source); } } Connections { target: plasmoid.configuration onSourcesChanged: { - if (plasmoid.configuration.sources.length == 0) { + if (plasmoid.configuration.sources.length === 0) { for (var i in smSource.sources) { var source = smSource.sources[i]; smSource.sourceAdded(source); } } else { var sourcesToRemove = []; for (var i = sourcesModel.count - 1; i >= 0; --i) { var obj = sourcesModel.get(i); if (plasmoid.configuration.sources.indexOf(encodeURIComponent(obj.source1)) === -1) { sourcesToRemove.push(obj.source1); } } for (i = 0; i < sourcesToRemove.length; ++i) { smSource.sourceRemoved(sourcesToRemove[i]); } for (var i in plasmoid.configuration.sources) { var source = decodeURIComponent(plasmoid.configuration.sources[i]); smSource.sourceAdded(source); } } } } PlasmaExtras.Heading { id: heading width: parent.width level: 2 text: plasmoid.title - visible: plasmoid.formFactor != PlasmaCore.Types.Horizontal && plasmoid.formFactor != PlasmaCore.Types.Vertical + visible: plasmoid.formFactor !== PlasmaCore.Types.Horizontal && plasmoid.formFactor !== PlasmaCore.Types.Vertical } GridLayout { rows: 1 columns: 1 - flow: plasmoid.formFactor != PlasmaCore.Types.Horizontal ? GridLayout.LeftToRight : GridLayout.TopToBottom + flow: plasmoid.formFactor !== PlasmaCore.Types.Horizontal ? GridLayout.LeftToRight : GridLayout.TopToBottom anchors { top: heading.visible ? heading.bottom : parent.top bottom: parent.bottom } width: parent.width Repeater { model: sourcesModel delegate: rootItem.delegate } } } diff --git a/applets/systemmonitor/common/contents/ui/ConfigGeneral.qml b/applets/systemmonitor/common/contents/ui/ConfigGeneral.qml index 943768c56..3a7dae404 100644 --- a/applets/systemmonitor/common/contents/ui/ConfigGeneral.qml +++ b/applets/systemmonitor/common/contents/ui/ConfigGeneral.qml @@ -1,146 +1,146 @@ /* * Copyright 2013 Bhushan Shah * * 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 import org.kde.plasma.core 2.0 as PlasmaCore Item { id: iconsPage width: childrenRect.width height: childrenRect.height implicitWidth: mainColumn.implicitWidth implicitHeight: mainColumn.implicitHeight property var cfg_sources: [] onCfg_sourcesChanged: { if (! cfg_sources) { cfg_sources = [] } if (cfg_sources.length == 0) { for (var i in mainColumn.children) { var child = mainColumn.children[i]; if (child.checked !== undefined) { child.checked = true; } } } else { for (var i in mainColumn.children) { var child = mainColumn.children[i]; if (child.checked !== undefined) { child.checked = cfg_sources.indexOf(child.source) !== -1; } } } } property int cfg_updateInterval signal sourceAdded(string source) function addSource(source, friendlyName) { var found = false; for (var i = 0; i < sourcesModel.count; ++i) { var obj = sourcesModel.get(i); - if (obj.source == source) { + if (obj.source === source) { found = true; break; } } if (found) { return; } sourcesModel.append( {"source": encodeURIComponent(source), "friendlyName": friendlyName}); } PlasmaCore.DataSource { id: smSource engine: "systemmonitor" onSourceAdded: { iconsPage.sourceAdded(source); } onSourceRemoved: { for (var i = sourcesModel.count - 1; i >= 0; --i) { var obj = sourcesModel.get(i); - if (obj.source == source) { + if (obj.source === source) { sourcesModel.remove(i); } } } } Component.onCompleted: { for (var i in smSource.sources) { var source = smSource.sources[i]; iconsPage.sourceAdded(source); } } ListModel { id: sourcesModel } Layouts.ColumnLayout { id: mainColumn anchors.left: parent.left Layouts.RowLayout { Controls.Label { text: i18n("Update Interval:") } Controls.SpinBox { id: updateIntervalSpinBox decimals: 2 suffix: i18nc("Suffix for spinbox (seconds)", " sec") maximumValue: 1000 stepSize: 0.5 onValueChanged: cfg_updateInterval = value * 1000 Component.onCompleted: value = cfg_updateInterval / 1000 } } Repeater { id: repeater model: sourcesModel Controls.CheckBox { id: checkBox text: model.friendlyName property string source: model.source onCheckedChanged: { if (checked) { if (cfg_sources.indexOf(model.source) == -1) { cfg_sources.push(model.source); } } else { var idx = cfg_sources.indexOf(model.source); if (idx !== -1) { cfg_sources.splice(idx, 1); } } cfg_sourcesChanged(); } } } } } diff --git a/applets/systemmonitor/common/contents/ui/DoublePlotter.qml b/applets/systemmonitor/common/contents/ui/DoublePlotter.qml index 24502384e..71016d82e 100644 --- a/applets/systemmonitor/common/contents/ui/DoublePlotter.qml +++ b/applets/systemmonitor/common/contents/ui/DoublePlotter.qml @@ -1,116 +1,116 @@ /* * Copyright 2015 Marco Martin * * * 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 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 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.0 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.kquickcontrolsaddons 2.0 as KQuickAddons KQuickAddons.Plotter { id: plotter property string sensorName: model.friendlyName1 Layout.fillWidth: true Layout.fillHeight: true horizontalGridLineCount: 0 function cycle(color, degrees) { var min = Math.min(color.r, Math.min(color.g, color.b)); var max = Math.max(color.r, Math.max(color.g, color.b)); var c = max - min; var h; - if (c == 0) { + if (c === 0) { h = 0 - } else if (max == color.r) { + } else if (max === color.r) { h = ((color.g - color.b) / c) % 6; - } else if (max == color.g) { + } else if (max === color.g) { h = ((color.b - color.r) / c) + 2; - } else if (max == color.b) { + } else if (max === color.b) { h = ((color.r - color.g) / c) + 4; } var hue = (1/6) * h + (degrees/360); var saturation = c / (1 - Math.abs(2 * ((max+min)/2) - 1)); var lightness = (max + min)/2; return Qt.hsla(hue, saturation, lightness, 1.0); } property string downloadColor: theme.highlightColor property string uploadColor: cycle(theme.highlightColor, -90) dataSets: [ KQuickAddons.PlotData { color: downloadColor }, KQuickAddons.PlotData { color: uploadColor } ] PlasmaComponents.Label { id: nameLabel anchors { left: parent.left top: parent.top } } PlasmaComponents.Label { id: speedLabel wrapMode: Text.WordWrap - visible: plasmoid.formFactor != PlasmaCore.Types.Vertical + visible: plasmoid.formFactor !== PlasmaCore.Types.Vertical anchors { right: parent.right } } Connections { target: model.dataSource onNewData: { - if (sourceName.indexOf(decodeURIComponent(model.source1)) != 0 && sourceName.indexOf(decodeURIComponent(model.source2)) != 0) { + if (sourceName.indexOf(decodeURIComponent(model.source1)) !== 0 && sourceName.indexOf(decodeURIComponent(model.source2)) !== 0) { return; } var data1 = model.dataSource.data[decodeURIComponent(model.source2)]; var data2 = model.dataSource.data[decodeURIComponent(model.source1)]; if (data1 === undefined || data1.value === undefined || data2 === undefined || data2.value === undefined) { return; } plotter.addSample([data1.value, data2.value]); - if (plasmoid.formFactor != PlasmaCore.Types.Vertical) { + if (plasmoid.formFactor !== PlasmaCore.Types.Vertical) { nameLabel.text = plotter.sensorName speedLabel.text = i18n(" %2 | %4", downloadColor, formatData(data1), uploadColor, formatData(data2)) } else { nameLabel.text = plotter.sensorName+ "\n" + formatData(data1) + "\n" + formatData(data2) speedLabel.text = "" } } } } diff --git a/applets/systemmonitor/common/contents/ui/SinglePlotter.qml b/applets/systemmonitor/common/contents/ui/SinglePlotter.qml index 268d484c0..c7f9a4d2a 100644 --- a/applets/systemmonitor/common/contents/ui/SinglePlotter.qml +++ b/applets/systemmonitor/common/contents/ui/SinglePlotter.qml @@ -1,86 +1,86 @@ /* * Copyright 2015 Marco Martin * * * 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 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 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.0 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.kquickcontrolsaddons 2.0 as KQuickAddons KQuickAddons.Plotter { id: plotter property string sensorName: model.friendlyName1 Layout.fillWidth: true Layout.fillHeight: true //FIXME: doesn't seem to properly fill otherwise Layout.preferredHeight: parent.height horizontalGridLineCount: 0 dataSets: [ KQuickAddons.PlotData { color: theme.highlightColor } ] PlasmaComponents.Label { id: nameLabel anchors { left: parent.left top: parent.top } } PlasmaComponents.Label { id: speedLabel wrapMode: Text.WordWrap - visible: plasmoid.formFactor != PlasmaCore.Types.Vertical + visible: plasmoid.formFactor !== PlasmaCore.Types.Vertical anchors { right: parent.right } } Connections { target: model.dataSource onNewData: { - if (sourceName.indexOf(decodeURIComponent(model.source1)) != 0) { + if (sourceName.indexOf(decodeURIComponent(model.source1)) !== 0) { return; } var data1 = model.dataSource.data[decodeURIComponent(model.source1)]; if (data1 === undefined || data1.value === undefined) { return; } plotter.addSample([data1.value]); - if (plasmoid.formFactor != PlasmaCore.Types.Vertical) { + if (plasmoid.formFactor !== PlasmaCore.Types.Vertical) { nameLabel.text = plotter.sensorName speedLabel.text = formatData(data1) } else { nameLabel.text = plotter.sensorName+ "\n" + formatData(data1) speedLabel.text = "" } } } } diff --git a/applets/systemmonitor/diskusage/contents/ui/diskusage.qml b/applets/systemmonitor/diskusage/contents/ui/diskusage.qml index 9cde32a67..260f53eea 100644 --- a/applets/systemmonitor/diskusage/contents/ui/diskusage.qml +++ b/applets/systemmonitor/diskusage/contents/ui/diskusage.qml @@ -1,88 +1,88 @@ /* * Copyright 2015 Marco Martin * * * 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 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 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.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons import org.kde.kcoreaddons 1.0 as KCoreAddons Applet { id: root onSourceAdded: { var match = source.match(/^partitions(.+)\/filllevel/); if (match) { var freeSource = "partitions" + match[1] + "/freespace"; root.addSource(source, match[1], freeSource, match[1]); } } delegate: Item { Layout.fillWidth: true Layout.fillHeight: true PlasmaComponents.Label { id: label text: model.friendlyName1 anchors { left: parent.left bottom: progressBar.top } } PlasmaComponents.Label { id: freeSpace anchors { right: parent.right bottom: progressBar.top } } PlasmaComponents.ProgressBar { id: progressBar minimumValue: 0 maximumValue: 100 anchors { left: parent.left right: parent.right bottom: parent.bottom } } Connections { target: model.dataSource onNewData: { - if (sourceName.indexOf(decodeURIComponent(model.source1)) != 0) { + if (sourceName.indexOf(decodeURIComponent(model.source1)) !== 0) { return; } var data1 = model.dataSource.data[decodeURIComponent(model.source1)]; var data2 = model.dataSource.data[decodeURIComponent(model.source2)]; if (data1 !== undefined && data1.value !== undefined) { progressBar.value = data1.value; } if (data2 !== undefined && data2.value !== undefined) { freeSpace.text = KCoreAddons.Format.formatByteSize(data2.value * 1024); } } } } } diff --git a/applets/systemtray/package/contents/applet/CompactApplet.qml b/applets/systemtray/package/contents/applet/CompactApplet.qml index 5e1a8d61e..b8da65d0f 100644 --- a/applets/systemtray/package/contents/applet/CompactApplet.qml +++ b/applets/systemtray/package/contents/applet/CompactApplet.qml @@ -1,82 +1,82 @@ /* * Copyright 2011 Marco Martin * * 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, 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.1 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 PlasmaCore.ToolTipArea { id: appletRoot objectName: "org.kde.desktop-CompactApplet" anchors.fill: parent icon: plasmoid.icon mainText: plasmoid.toolTipMainText subText: plasmoid.toolTipSubText - location: if (plasmoid.parent && plasmoid.parent.parent.objectName == "hiddenTasksColumn" && plasmoid.location != PlasmaCore.Types.LeftEdge) { + location: if (plasmoid.parent && plasmoid.parent.parent.objectName === "hiddenTasksColumn" && plasmoid.location !== PlasmaCore.Types.LeftEdge) { return PlasmaCore.Types.RightEdge; } else { return plasmoid.location; } active: !plasmoid.expanded textFormat: plasmoid.toolTipTextFormat mainItem: plasmoid.toolTipItem ? plasmoid.toolTipItem : null property Item fullRepresentation property Item compactRepresentation Connections { target: plasmoid onContextualActionsAboutToShow: appletRoot.hideToolTip() } Layout.minimumWidth: { switch (plasmoid.formFactor) { case PlasmaCore.Types.Vertical: return 0; case PlasmaCore.Types.Horizontal: return height; default: return units.gridUnit * 3; } } Layout.minimumHeight: { switch (plasmoid.formFactor) { case PlasmaCore.Types.Vertical: return width; case PlasmaCore.Types.Horizontal: return 0; default: return units.gridUnit * 3; } } onCompactRepresentationChanged: { if (compactRepresentation) { compactRepresentation.parent = appletRoot; compactRepresentation.anchors.fill = appletRoot; compactRepresentation.visible = true; } appletRoot.visible = true; } } diff --git a/applets/systemtray/package/contents/ui/ConfigEntries.qml b/applets/systemtray/package/contents/ui/ConfigEntries.qml index 4765c7566..cdd5d13a7 100644 --- a/applets/systemtray/package/contents/ui/ConfigEntries.qml +++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml @@ -1,265 +1,265 @@ /* * Copyright 2013 Sebastian Kügler * Copyright 2014 Marco Martin * * 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.0 as QtControls import QtQuick.Layouts 1.1 as QtLayouts 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.kquickcontrolsaddons 2.0 import org.kde.kquickcontrols 2.0 as KQC QtLayouts.GridLayout { id: iconsPage signal configurationChanged property var cfg_shownItems: [] property var cfg_hiddenItems: [] property alias cfg_showAllItems: showAllCheckBox.checked columns: 2 // so we can indent the entries below... function saveConfig () { for (var i in tableView.model) { //tableView.model[i].applet.globalShortcut = tableView.model[i].shortcut } } PlasmaCore.DataSource { id: statusNotifierSource engine: "statusnotifieritem" interval: 0 onSourceAdded: { connectSource(source) } Component.onCompleted: { connectedSources = sources } } PlasmaCore.SortFilterModel { id: statusNotifierModel sourceModel: PlasmaCore.DataModel { dataSource: statusNotifierSource } } QtControls.CheckBox { id: showAllCheckBox QtLayouts.Layout.fillWidth: true QtLayouts.Layout.columnSpan: iconsPage.columns QtLayouts.Layout.row: 1 text: i18n("Always show all entries") } QtControls.Button { // just for measurement id: measureButton text: "measureButton" visible: false } // resizeToContents does not take into account the heading QtControls.Label { id: shortcutColumnMeasureLabel text: shortcutColumn.title visible: false } function retrieveAllItems() { var list = []; for (var i = 0; i < statusNotifierModel.count; ++i) { var item = statusNotifierModel.get(i); list.push({ "index": i, "taskId": item.Id, "name": item.Title, "iconName": item.IconName, "icon": item.Icon }); } var lastIndex = list.length; for (var i = 0; i < plasmoid.applets.length; ++i) { var item = plasmoid.applets[i] list.push({ "index": (i + lastIndex), "applet": item, "taskId": item.pluginName, "name": item.title, "iconName": item.icon, "shortcut": item.globalShortcut }); } list.sort(function(a, b) { return a.name.localeCompare(b.name); }); return list; } QtControls.TableView { id: tableView QtLayouts.Layout.fillWidth: true QtLayouts.Layout.fillHeight: true QtLayouts.Layout.row: 2 QtLayouts.Layout.column: 1 model: retrieveAllItems() horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff flickableItem.boundsBehavior: Flickable.StopAtBounds Component.onCompleted: { visibilityColumn.resizeToContents() shortcutColumn.resizeToContents() } // Taken from QtQuickControls BasicTableViewStyle, just to make its height sensible... rowDelegate: BorderImage { visible: styleData.selected || styleData.alternate source: "image://__tablerow/" + (styleData.alternate ? "alternate_" : "") + (tableView.activeFocus ? "active" : "") height: measureButton.height border.left: 4 ; border.right: 4 } QtControls.TableViewColumn { id: entryColumn width: tableView.viewport.width - visibilityColumn.width - shortcutColumn.width title: i18nc("Name of the system tray entry", "Entry") movable: false resizable: false delegate: QtLayouts.RowLayout { Item { // spacer QtLayouts.Layout.preferredWidth: 1 QtLayouts.Layout.fillHeight: true } QIconItem { width: units.iconSizes.small height: width icon: modelData.iconName || modelData.icon || "" } QtControls.Label { QtLayouts.Layout.fillWidth: true text: modelData.name elide: Text.ElideRight wrapMode: Text.NoWrap } } } QtControls.TableViewColumn { id: visibilityColumn title: i18n("Visibility") movable: false resizable: false delegate: QtControls.ComboBox { implicitWidth: Math.round(units.gridUnit * 6.5) // ComboBox sizing is broken enabled: !showAllCheckBox.checked currentIndex: { if (cfg_shownItems.indexOf(modelData.taskId) != -1) { return 1; } else if (cfg_hiddenItems.indexOf(modelData.taskId) != -1) { return 2; } else { return 0; } } // activated, in contrast to currentIndexChanged, only fires if the user himself changed the value onActivated: { var shownIndex = cfg_shownItems.indexOf(modelData.taskId); var hiddenIndex = cfg_hiddenItems.indexOf(modelData.taskId); switch (index) { case 0: { if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1); } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1); } break; } case 1: { - if (shownIndex == -1) { + if (shownIndex === -1) { cfg_shownItems.push(modelData.taskId); } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1); } break; } case 2: { if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1); } - if (hiddenIndex == -1) { + if (hiddenIndex === -1) { cfg_hiddenItems.push(modelData.taskId); } break; } } iconsPage.configurationChanged(); } model: [i18n("Auto"), i18n("Shown"), i18n("Hidden")] } } QtControls.TableViewColumn { id: shortcutColumn title: i18n("Keyboard Shortcut") // FIXME doesn't fit movable: false resizable: false // this Item wrapper prevents TableView from ripping apart the two KeySequenceItem buttons delegate: Item { implicitWidth: Math.max(shortcutColumnMeasureLabel.width, keySequenceItem.width) + 10 height: keySequenceItem.height KQC.KeySequenceItem { id: keySequenceItem anchors.right: parent.right keySequence: modelData.shortcut // only Plasmoids have that visible: modelData.hasOwnProperty("shortcut") onKeySequenceChanged: { - if (keySequence != modelData.shortcut) { + if (keySequence !== modelData.shortcut) { // both SNIs and plasmoids are listed in the same TableView // but they come from two separate models, so we need to subtract // the SNI model count to get the actual plasmoid index var index = modelData.index - statusNotifierModel.count plasmoid.applets[index].globalShortcut = keySequence iconsPage.configurationChanged() } shortcutColumn.resizeToContents() } } } } } } diff --git a/applets/systemtray/package/contents/ui/ConfigGeneral.qml b/applets/systemtray/package/contents/ui/ConfigGeneral.qml index e17ba0d82..21083f952 100644 --- a/applets/systemtray/package/contents/ui/ConfigGeneral.qml +++ b/applets/systemtray/package/contents/ui/ConfigGeneral.qml @@ -1,125 +1,125 @@ /* * Copyright 2013 Sebastian Kügler * Copyright 2014 Marco Martin * * 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 2.3 as QtControls import QtQuick.Layouts 1.0 as QtLayouts import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 import org.kde.kirigami 2.5 as Kirigami Item { id: iconsPage signal configurationChanged width: childrenRect.width height: childrenRect.height implicitWidth: mainColumn.implicitWidth implicitHeight: pageColumn.implicitHeight property alias cfg_applicationStatusShown: applicationStatus.checked property alias cfg_communicationsShown: communications.checked property alias cfg_systemServicesShown: systemServices.checked property alias cfg_hardwareControlShown: hardwareControl.checked property alias cfg_miscellaneousShown: miscellaneous.checked property var cfg_extraItems: [] QtControls.CheckBox { id: dummyCheckbox visible: false } Kirigami.FormLayout { id: pageColumn anchors { left: parent.left right: parent.right } Item { Kirigami.FormData.isSection: true Kirigami.FormData.label: i18n("Categories") } QtControls.CheckBox { id: applicationStatus text: i18n("Application Status") } QtControls.CheckBox { id: communications text: i18n("Communications") } QtControls.CheckBox { id: systemServices text: i18n("System Services") } QtControls.CheckBox { id: hardwareControl text: i18n("Hardware Control") } QtControls.CheckBox { id: miscellaneous text: i18n("Miscellaneous") } Item { Kirigami.FormData.isSection: true Kirigami.FormData.label: i18n("Extra Items") } Repeater { model: plasmoid.nativeInterface.availablePlasmoids delegate: QtControls.CheckBox { QtLayouts.Layout.minimumWidth: childrenRect.width checked: cfg_extraItems.indexOf(plugin) != -1 implicitWidth: itemLayout.width + itemLayout.x onCheckedChanged: { var index = cfg_extraItems.indexOf(plugin); if (checked) { - if (index == -1) { + if (index === -1) { cfg_extraItems.push(plugin); } } else { if (index > -1) { cfg_extraItems.splice(index, 1); } } configurationChanged() // qml cannot detect changes inside an Array } QtLayouts.RowLayout { id: itemLayout anchors.verticalCenter: parent.verticalCenter x: dummyCheckbox.width QIconItem { icon: model.decoration width: units.iconSizes.small height: width } QtControls.Label { text: model.display } } } } } } diff --git a/applets/systemtray/package/contents/ui/ExpanderArrow.qml b/applets/systemtray/package/contents/ui/ExpanderArrow.qml index 903a691f4..2d03db474 100644 --- a/applets/systemtray/package/contents/ui/ExpanderArrow.qml +++ b/applets/systemtray/package/contents/ui/ExpanderArrow.qml @@ -1,116 +1,116 @@ /*************************************************************************** * Copyright 2013 Sebastian Kügler * * Copyright 2015 Marco Martin * * * * 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.0 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 PlasmaCore.ToolTipArea { id: tooltip - property bool vertical: plasmoid.formFactor == PlasmaCore.Types.Vertical + property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical implicitWidth: units.iconSizes.smallMedium implicitHeight: implicitWidth visible: root.hiddenLayout.children.length > 0 subText: root.expanded ? i18n("Close popup") : i18n("Show hidden icons") MouseArea { id: arrowMouseArea anchors.fill: parent onClicked: root.expanded = !root.expanded readonly property int arrowAnimationDuration: units.shortDuration * 3 PlasmaCore.Svg { id: arrowSvg imagePath: "widgets/arrows" } PlasmaCore.SvgItem { id: arrow anchors.centerIn: parent width: Math.min(parent.width, parent.height) height: width rotation: root.expanded ? 180 : 0 Behavior on rotation { RotationAnimation { duration: arrowMouseArea.arrowAnimationDuration } } opacity: root.expanded ? 0 : 1 Behavior on opacity { NumberAnimation { duration: arrowMouseArea.arrowAnimationDuration } } svg: arrowSvg elementId: { - if (plasmoid.location == PlasmaCore.Types.TopEdge) { + if (plasmoid.location === PlasmaCore.Types.TopEdge) { return "down-arrow"; - } else if (plasmoid.location == PlasmaCore.Types.LeftEdge) { + } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { return "right-arrow"; - } else if (plasmoid.location == PlasmaCore.Types.RightEdge) { + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { return "left-arrow"; } else { return "up-arrow"; } } } PlasmaCore.SvgItem { anchors.centerIn: parent width: arrow.width height: arrow.height rotation: root.expanded ? 0 : -180 Behavior on rotation { RotationAnimation { duration: arrowMouseArea.arrowAnimationDuration } } opacity: root.expanded ? 1 : 0 Behavior on opacity { NumberAnimation { duration: arrowMouseArea.arrowAnimationDuration } } svg: arrowSvg elementId: { - if (plasmoid.location == PlasmaCore.Types.TopEdge) { + if (plasmoid.location === PlasmaCore.Types.TopEdge) { return "up-arrow"; - } else if (plasmoid.location == PlasmaCore.Types.LeftEdge) { + } else if (plasmoid.location === PlasmaCore.Types.LeftEdge) { return "left-arrow"; - } else if (plasmoid.location == PlasmaCore.Types.RightEdge) { + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { return "right-arrow"; } else { return "down-arrow"; } } } } } diff --git a/applets/systemtray/package/contents/ui/items/AbstractItem.qml b/applets/systemtray/package/contents/ui/items/AbstractItem.qml index b0b0906f1..17758bd53 100644 --- a/applets/systemtray/package/contents/ui/items/AbstractItem.qml +++ b/applets/systemtray/package/contents/ui/items/AbstractItem.qml @@ -1,140 +1,140 @@ /* * Copyright 2016 Marco Martin * * 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, 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.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents PlasmaCore.ToolTipArea { id: abstractItem height: effectiveItemSize + marginHints.top + marginHints.bottom width: labelVisible ? parent.width : effectiveItemSize + marginHints.left + marginHints.right property real effectiveItemSize: hidden ? root.hiddenItemSize : root.itemSize property string itemId property string category property alias text: label.text property bool hidden: parent.objectName == "hiddenTasksColumn" property QtObject marginHints: parent.marginHints property bool labelVisible: abstractItem.hidden && !root.activeApplet property Item iconItem //PlasmaCore.Types.ItemStatus property int status property QtObject model signal clicked(var mouse) signal wheel(var wheel) signal contextMenu(var mouse) property bool forcedHidden: plasmoid.configuration.hiddenItems.indexOf(itemId) !== -1 property bool forcedShown: plasmoid.configuration.showAllItems || plasmoid.configuration.shownItems.indexOf(itemId) !== -1 - property bool categoryShown: shownCategories.indexOf(category) != -1; + property bool categoryShown: shownCategories.indexOf(category) !== -1; readonly property int effectiveStatus: { if (!categoryShown || status === PlasmaCore.Types.HiddenStatus) { return PlasmaCore.Types.HiddenStatus } else if (forcedShown || (!forcedHidden && status !== PlasmaCore.Types.PassiveStatus)) { return PlasmaCore.Types.ActiveStatus } else { return PlasmaCore.Types.PassiveStatus } } /* subclasses need to assign to this tiiltip properties mainText: subText: icon: */ location: { if (abstractItem.parent && abstractItem.parent.objectName === "hiddenTasksColumn") { if (LayoutMirroring.enabled && plasmoid.location !== PlasmaCore.Types.RightEdge) { return PlasmaCore.Types.LeftEdge; } else if (plasmoid.location !== PlasmaCore.Types.LeftEdge) { return PlasmaCore.Types.RightEdge; } } return plasmoid.location; } //BEGIN CONNECTIONS onEffectiveStatusChanged: updateItemVisibility(abstractItem); onContainsMouseChanged: { if (hidden && containsMouse) { root.hiddenLayout.hoveredItem = abstractItem } } Component.onCompleted: updateItemVisibility(abstractItem); //dangerous but needed due how repeater reparents onParentChanged: updateItemVisibility(abstractItem); //END CONNECTIONS PulseAnimation { targetItem: iconItem running: (abstractItem.status === PlasmaCore.Types.NeedsAttentionStatus || abstractItem.status === PlasmaCore.Types.RequiresAttentionStatus ) && units.longDuration > 0 } MouseArea { id: mouseArea anchors.fill: abstractItem hoverEnabled: true drag.filterChildren: true acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton onClicked: abstractItem.clicked(mouse) onPressed: { abstractItem.hideToolTip() if (mouse.button === Qt.RightButton) { abstractItem.contextMenu(mouse) } } onWheel: { abstractItem.wheel(wheel); //Don't accept the event in order to make the scrolling by mouse wheel working //for the parent scrollview this icon is in. wheel.accepted = false; } } PlasmaComponents.Label { id: label anchors { left: parent.left leftMargin: iconItem ? iconItem.width + units.smallSpacing : 0 verticalCenter: parent.verticalCenter } opacity: labelVisible ? 1 : 0 visible: abstractItem.hidden Behavior on opacity { NumberAnimation { duration: units.longDuration easing.type: Easing.InOutQuad } } } } diff --git a/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml b/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml index 3fa92e6ae..4d9b26235 100644 --- a/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml +++ b/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml @@ -1,83 +1,83 @@ /* * Copyright 2015 Marco Martin * * 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, 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.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents AbstractItem { id: plasmoidContainer property Item applet iconItem: applet text: applet ? applet.title : "" itemId: applet ? applet.pluginName : "" category: applet ? plasmoid.nativeInterface.plasmoidCategory(applet) : "UnknownCategory" mainText: applet ? applet.toolTipMainText : "" subText: applet ? applet.toolTipSubText : "" icon: applet ? applet.icon : "" mainItem: applet && applet.toolTipItem ? applet.toolTipItem : null textFormat: applet ? applet.toolTipTextFormat : "" status: applet ? applet.status : PlasmaCore.Types.UnknownStatus - active: root.activeApplet != applet + active: root.activeApplet !== applet onClicked: { - if (applet && mouse.button == Qt.LeftButton) { + if (applet && mouse.button === Qt.LeftButton) { applet.expanded = true; } } onContextMenu: { if (applet) { plasmoid.nativeInterface.showPlasmoidMenu(applet, 0, plasmoidContainer.hidden ? applet.height : 0); } } onHeightChanged: { if (applet) { applet.width = height } } onAppletChanged: { if (!applet) { plasmoidContainer.destroy(); print("applet destroyed") } } Connections { target: applet onExpandedChanged: { if (expanded) { var oldApplet = root.activeApplet; root.activeApplet = applet; if (oldApplet) { oldApplet.expanded = false; } dialog.visible = true; - } else if (root.activeApplet == applet) { + } else if (root.activeApplet === applet) { if (!applet.parent.hidden) { dialog.visible = false; } //if not expanded we don't have an active applet anymore root.activeApplet = null; } } } } diff --git a/applets/systemtray/package/contents/ui/main.qml b/applets/systemtray/package/contents/ui/main.qml index 4e5699603..e5689bbcf 100644 --- a/applets/systemtray/package/contents/ui/main.qml +++ b/applets/systemtray/package/contents/ui/main.qml @@ -1,386 +1,386 @@ /* * Copyright 2011 Marco Martin * * 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, 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.5 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.draganddrop 2.0 as DnD import "items" MouseArea { id: root Layout.minimumWidth: vertical ? units.iconSizes.small : tasksRow.implicitWidth + (expander.visible ? expander.implicitWidth : 0) + units.smallSpacing Layout.minimumHeight: vertical ? tasksRow.implicitHeight + (expander.visible ? expander.implicitHeight : 0) + units.smallSpacing : units.smallSpacing Layout.preferredHeight: Layout.minimumHeight LayoutMirroring.enabled: !vertical && Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true property var iconSizes: ["small", "smallMedium", "medium", "large", "huge", "enormous"]; - property bool vertical: plasmoid.formFactor == PlasmaCore.Types.Vertical + property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical property int itemSize: units.roundToIconSize(Math.min(Math.min(width, height), units.iconSizes[iconSizes[plasmoid.configuration.iconSize]])) property int hiddenItemSize: units.iconSizes.smallMedium property alias expanded: dialog.visible property Item activeApplet property int status: dialog.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus property alias visibleLayout: tasksRow property alias hiddenLayout: expandedRepresentation.hiddenLayout property alias statusNotifierModel: statusNotifierModel // workaround https://bugreports.qt.io/browse/QTBUG-71238 / https://bugreports.qt.io/browse/QTBUG-72004 property Component plasmoidItemComponent: Qt.createComponent("items/PlasmoidItem.qml") Plasmoid.onExpandedChanged: { if (!plasmoid.expanded) { dialog.visible = plasmoid.expanded; } } function updateItemVisibility(item) { switch (item.effectiveStatus) { case PlasmaCore.Types.HiddenStatus: - if (item.parent == invisibleEntriesContainer) { + if (item.parent === invisibleEntriesContainer) { return; } item.parent = invisibleEntriesContainer; break; case PlasmaCore.Types.ActiveStatus: - if (visibleLayout.children.length == 0) { + if (visibleLayout.children.length === 0) { item.parent = visibleLayout; //notifications is always the first - } else if (visibleLayout.children[0].itemId == "org.kde.plasma.notifications" && - item.itemId != "org.kde.plasma.notifications") { + } else if (visibleLayout.children[0].itemId === "org.kde.plasma.notifications" && + item.itemId !== "org.kde.plasma.notifications") { plasmoid.nativeInterface.reorderItemAfter(item, visibleLayout.children[0]); - } else if (visibleLayout.children[0] != item) { + } else if (visibleLayout.children[0] !== item) { plasmoid.nativeInterface.reorderItemBefore(item, visibleLayout.children[0]); } break; case PlasmaCore.Types.PassiveStatus: - if (hiddenLayout.children.length == 0) { + if (hiddenLayout.children.length === 0) { item.parent = hiddenLayout; //notifications is always the first - } else if (hiddenLayout.children[0].itemId == "org.kde.plasma.notifications" && - item.itemId != "org.kde.plasma.notifications") { + } else if (hiddenLayout.children[0].itemId === "org.kde.plasma.notifications" && + item.itemId !== "org.kde.plasma.notifications") { plasmoid.nativeInterface.reorderItemAfter(item, hiddenLayout.children[0]); - } else if (hiddenLayout.children[0] != item) { + } else if (hiddenLayout.children[0] !== item) { plasmoid.nativeInterface.reorderItemBefore(item, hiddenLayout.children[0]); } item.x = 0; break; } } onWheel: { // Don't propagate unhandled wheel events wheel.accepted = true; } Containment.onAppletAdded: { //Allow the plasmoid expander to know in what window it will be var plasmoidContainer = plasmoidItemComponent.createObject(invisibleEntriesContainer, {"x": x, "y": y, "applet": applet}); applet.parent = plasmoidContainer applet.anchors.left = plasmoidContainer.left applet.anchors.top = plasmoidContainer.top applet.anchors.bottom = plasmoidContainer.bottom applet.width = plasmoidContainer.height applet.visible = true plasmoidContainer.visible = true //This is to make preloading effective, minimizes the scene changes if (applet.fullRepresentationItem) { applet.fullRepresentationItem.width = expandedRepresentation.width applet.fullRepresentationItem.width = expandedRepresentation.height applet.fullRepresentationItem.parent = preloadedStorage; } else { applet.fullRepresentationItemChanged.connect(function() { applet.fullRepresentationItem.width = expandedRepresentation.width applet.fullRepresentationItem.width = expandedRepresentation.height applet.fullRepresentationItem.parent = preloadedStorage; }); } } //being there forces the items to fully load, and they will be reparented in the popup one by one, this item is *never* visible Item { id: preloadedStorage visible: false } Containment.onAppletRemoved: { } Connections { target: plasmoid onUserConfiguringChanged: { if (plasmoid.userConfiguring) { dialog.visible = false } } } Connections { target: plasmoid.configuration onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems } Component.onCompleted: { //script, don't bind plasmoid.nativeInterface.allowedPlasmoids = initializePlasmoidList(); } function initializePlasmoidList() { var newKnownItems = []; var newExtraItems = []; //NOTE:why this? otherwise the interpreter will execute plasmoid.nativeInterface.defaultPlasmoids() on //every access of defaults[], resulting in a very slow iteration var defaults = []; //defaults = defaults.concat(plasmoid.nativeInterface.defaultPlasmoids); defaults = plasmoid.nativeInterface.defaultPlasmoids.slice() var candidate; //Add every plasmoid that is both not enabled explicitly and not already known for (var i = 0; i < defaults.length; ++i) { candidate = defaults[i]; if (plasmoid.configuration.knownItems.indexOf(candidate) === -1) { newKnownItems.push(candidate); if (plasmoid.configuration.extraItems.indexOf(candidate) === -1) { newExtraItems.push(candidate); } } } if (newExtraItems.length > 0) { plasmoid.configuration.extraItems = plasmoid.configuration.extraItems.slice().concat(newExtraItems); } if (newKnownItems.length > 0) { plasmoid.configuration.knownItems = plasmoid.configuration.knownItems.slice().concat(newKnownItems); } return plasmoid.configuration.extraItems; } PlasmaCore.DataSource { id: statusNotifierSource engine: "statusnotifieritem" interval: 0 onSourceAdded: { connectSource(source) } Component.onCompleted: { connectedSources = sources } } //due to the magic of property bindings this function will be //re-executed all the times a setting changes property var shownCategories: { var array = []; if (plasmoid.configuration.applicationStatusShown) { array.push("ApplicationStatus"); } if (plasmoid.configuration.communicationsShown) { array.push("Communications"); } if (plasmoid.configuration.systemServicesShown) { array.push("SystemServices"); } if (plasmoid.configuration.hardwareControlShown) { array.push("Hardware"); } if (plasmoid.configuration.miscellaneousShown) { array.push("UnknownCategory"); } //nothing? make a regexp that matches nothing - if (array.length == 0) { + if (array.length === 0) { array.push("$^") } return array; } PlasmaCore.SortFilterModel { id: statusNotifierModel sourceModel: PlasmaCore.DataModel { dataSource: statusNotifierSource } } //This is a dump for items we don't want to be seen or as an incubation, when they are //created as a nursery before going in their final place Item { id: invisibleEntriesContainer visible: false Repeater { id: tasksRepeater model: statusNotifierModel delegate: StatusNotifierItem {} } //NOTE: this exists mostly for not causing reference errors property QtObject marginHints: QtObject { property int left: 0 property int top: 0 property int right: 0 property int bottom: 0 } } CurrentItemHighLight { visualParent: tasksRow target: root.activeApplet && root.activeApplet.parent.parent == tasksRow ? root.activeApplet.parent : root location: plasmoid.location } DnD.DropArea { anchors.fill: parent preventStealing: true; /** Extracts the name of the system tray applet in the drag data if present * otherwise returns null*/ function systemTrayAppletName(event) { if (event.mimeData.formats.indexOf("text/x-plasmoidservicename") < 0) { return null; } var plasmoidId = event.mimeData.getDataAsByteArray("text/x-plasmoidservicename"); if (!plasmoid.nativeInterface.isSystemTrayApplet(plasmoidId)) { return null; } return plasmoidId; } onDragEnter: { if (!systemTrayAppletName(event)) { event.ignore(); } } onDrop: { var plasmoidId = systemTrayAppletName(event); if (!plasmoidId) { event.ignore(); return; } if (plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) { var extraItems = plasmoid.configuration.extraItems; extraItems.push(plasmoidId); plasmoid.configuration.extraItems = extraItems; } } } //Main Layout Flow { id: tasksRow spacing: 0 height: parent.height - (vertical && expander.visible ? expander.height : 0) width: parent.width - (vertical || !expander.visible ? 0 : expander.width) property string skipItems flow: vertical ? Flow.LeftToRight : Flow.TopToBottom //To make it look centered y: Math.round(height/2 - childrenRect.height/2) x: (expander.visible && LayoutMirroring.enabled ? expander.width : 0) + Math.round(width/2 - childrenRect.width/2) //Do spacing with margins, to correctly compute the number of lines property QtObject marginHints: QtObject { property int left: Math.round(units.smallSpacing / 2) property int top: Math.round(units.smallSpacing / 2) property int right: Math.round(units.smallSpacing / 2) property int bottom: Math.round(units.smallSpacing / 2) } //add doesn't seem to work used in conjunction with stackBefore/stackAfter /*add: Transition { NumberAnimation { property: "scale" from: 0 to: 1 easing.type: Easing.InQuad duration: units.longDuration } } move: Transition { NumberAnimation { properties: "x,y" easing.type: Easing.InQuad duration: units.longDuration } }*/ } ExpanderArrow { id: expander anchors { fill: parent leftMargin: vertical ? 0 : parent.width - implicitWidth topMargin: vertical ? parent.height - implicitHeight : 0 } } //Main popup PlasmaCore.Dialog { id: dialog visualParent: root flags: Qt.WindowStaysOnTopHint location: plasmoid.location hideOnWindowDeactivate: !plasmoid.configuration.pin onVisibleChanged: { if (!visible) { plasmoid.status = PlasmaCore.Types.PassiveStatus; if (root.activeApplet) { root.activeApplet.expanded = false; } } else { plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; } plasmoid.expanded = visible; } mainItem: ExpandedRepresentation { id: expandedRepresentation Keys.onEscapePressed: { root.expanded = false; } activeApplet: root.activeApplet LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true } } } diff --git a/lookandfeel/contents/logout/Logout.qml b/lookandfeel/contents/logout/Logout.qml index 954d6eb8c..2cec81a05 100644 --- a/lookandfeel/contents/logout/Logout.qml +++ b/lookandfeel/contents/logout/Logout.qml @@ -1,249 +1,249 @@ /*************************************************************************** * Copyright (C) 2014 by Aleix Pol Gonzalez * * * * 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 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.2 import QtQuick.Layouts 1.2 import QtQuick.Controls 1.1 as Controls import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kcoreaddons 1.0 as KCoreAddons import "../components" import "timer.js" as AutoTriggerTimer import org.kde.plasma.private.sessions 2.0 PlasmaCore.ColorScope { id: root colorGroup: PlasmaCore.Theme.ComplementaryColorGroup height: screenGeometry.height width: screenGeometry.width signal logoutRequested() signal haltRequested() signal suspendRequested(int spdMethod) signal rebootRequested() signal rebootRequested2(int opt) signal cancelRequested() signal lockScreenRequested() property alias backgroundColor: backgroundRect.color function sleepRequested() { root.suspendRequested(2); } function hibernateRequested() { root.suspendRequested(4); } property real timeout: 30 property real remainingTime: root.timeout property var currentAction: { switch (sdtype) { case ShutdownType.ShutdownTypeReboot: return root.rebootRequested; case ShutdownType.ShutdownTypeHalt: return root.haltRequested; default: return root.logoutRequested; } } KCoreAddons.KUser { id: kuser } // For showing a "other users are logged in" hint SessionsModel { id: sessionsModel includeUnusedSessions: false } Controls.Action { onTriggered: root.cancelRequested() shortcut: "Escape" } onRemainingTimeChanged: { if (remainingTime <= 0) { root.currentAction(); } } Timer { id: countDownTimer running: true repeat: true interval: 1000 onTriggered: remainingTime-- Component.onCompleted: { AutoTriggerTimer.addCancelAutoTriggerCallback(function() { countDownTimer.running = false; }); } } function isLightColor(color) { return Math.max(color.r, color.g, color.b) > 0.5 } Rectangle { id: backgroundRect anchors.fill: parent //use "black" because this is intended to look like a general darkening of the scene. a dark gray as normal background would just look too "washed out" color: root.isLightColor(PlasmaCore.ColorScope.backgroundColor) ? PlasmaCore.ColorScope.backgroundColor : "black" opacity: 0.5 } MouseArea { anchors.fill: parent onClicked: root.cancelRequested() } UserDelegate { width: units.iconSizes.enormous height: width anchors { horizontalCenter: parent.horizontalCenter bottom: parent.verticalCenter } constrainText: false avatarPath: kuser.faceIconUrl iconSource: "user-identity" isCurrent: true name: kuser.fullName } ColumnLayout { anchors { top: parent.verticalCenter topMargin: units.gridUnit * 2 horizontalCenter: parent.horizontalCenter } spacing: units.largeSpacing height: Math.max(implicitHeight, units.gridUnit * 10) width: Math.max(implicitWidth, units.gridUnit * 16) PlasmaComponents.Label { font.pointSize: theme.defaultFont.pointSize + 1 Layout.maximumWidth: units.gridUnit * 16 Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap font.italic: true text: i18ndp("plasma_lookandfeel_org.kde.lookandfeel", "One other user is currently logged in. If the computer is shut down or restarted, that user may lose work.", "%1 other users are currently logged in. If the computer is shut down or restarted, those users may lose work.", sessionsModel.count) visible: sessionsModel.count > 1 } RowLayout { spacing: units.largeSpacing * 2 Layout.alignment: Qt.AlignHCenter LogoutButton { id: suspendButton iconSource: "system-suspend" text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Suspend to RAM", "Sleep") action: root.sleepRequested KeyNavigation.left: logoutButton KeyNavigation.right: hibernateButton visible: spdMethods.SuspendState } LogoutButton { id: hibernateButton iconSource: "system-suspend-hibernate" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Hibernate") action: root.hibernateRequested KeyNavigation.left: suspendButton KeyNavigation.right: rebootButton visible: spdMethods.HibernateState } LogoutButton { id: rebootButton iconSource: "system-reboot" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Restart") action: root.rebootRequested KeyNavigation.left: hibernateButton KeyNavigation.right: shutdownButton - focus: sdtype == ShutdownType.ShutdownTypeReboot + focus: sdtype === ShutdownType.ShutdownTypeReboot visible: maysd } LogoutButton { id: shutdownButton iconSource: "system-shutdown" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Shut Down") action: root.haltRequested KeyNavigation.left: rebootButton KeyNavigation.right: logoutButton - focus: sdtype == ShutdownType.ShutdownTypeHalt + focus: sdtype === ShutdownType.ShutdownTypeHalt visible: maysd } LogoutButton { id: logoutButton iconSource: "system-log-out" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Log Out") action: root.logoutRequested KeyNavigation.left: shutdownButton KeyNavigation.right: suspendButton - focus: sdtype == ShutdownType.ShutdownTypeNone + focus: sdtype === ShutdownType.ShutdownTypeNone visible: canLogout } } PlasmaComponents.Label { font.pointSize: theme.defaultFont.pointSize + 1 Layout.alignment: Qt.AlignHCenter //opacity, as visible would re-layout opacity: countDownTimer.running ? 1 : 0 Behavior on opacity { OpacityAnimator { duration: units.longDuration easing.type: Easing.InOutQuad } } text: { switch (sdtype) { case ShutdownType.ShutdownTypeReboot: return i18ndp("plasma_lookandfeel_org.kde.lookandfeel", "Restarting in 1 second", "Restarting in %1 seconds", root.remainingTime); case ShutdownType.ShutdownTypeHalt: return i18ndp("plasma_lookandfeel_org.kde.lookandfeel", "Shutting down in 1 second", "Shutting down in %1 seconds", root.remainingTime); default: return i18ndp("plasma_lookandfeel_org.kde.lookandfeel", "Logging out in 1 second", "Logging out in %1 seconds", root.remainingTime); } } } RowLayout { Layout.alignment: Qt.AlignHCenter PlasmaComponents.Button { font.pointSize: theme.defaultFont.pointSize + 1 - enabled: root.currentAction != null + enabled: root.currentAction !== null text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "OK") onClicked: root.currentAction() } PlasmaComponents.Button { font.pointSize: theme.defaultFont.pointSize + 1 text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Cancel") onClicked: root.cancelRequested() } } } } diff --git a/lookandfeel/contents/runcommand/RunCommand.qml b/lookandfeel/contents/runcommand/RunCommand.qml index ea7ff6b94..95317b646 100644 --- a/lookandfeel/contents/runcommand/RunCommand.qml +++ b/lookandfeel/contents/runcommand/RunCommand.qml @@ -1,298 +1,298 @@ /* * Copyright 2014 Marco Martin * * 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, see . */ import QtQuick 2.6 import QtQuick.Layouts 1.1 import QtQuick.Window 2.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.milou 0.1 as Milou ColumnLayout { id: root property string query property string runner property bool showHistory: false LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true onQueryChanged: { queryField.text = query; } Connections { target: runnerWindow onVisibleChanged: { if (runnerWindow.visible) { queryField.forceActiveFocus(); listView.currentIndex = -1 } else { root.query = ""; root.runner = "" root.showHistory = false } } } RowLayout { Layout.alignment: Qt.AlignTop PlasmaComponents.ToolButton { iconSource: "configure" onClicked: { runnerWindow.visible = false runnerWindow.displayConfiguration() } Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure") Accessible.description: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure Search Plugins") visible: runnerWindow.canConfigure tooltip: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure...") } PlasmaComponents.TextField { id: queryField property bool allowCompletion: false clearButtonShown: true Layout.minimumWidth: units.gridUnit * 25 activeFocusOnPress: true placeholderText: results.runnerName ? i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Textfield placeholder text, query specific KRunner", "Search '%1'...", results.runnerName) : i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Textfield placeholder text", "Search...") function move_up() { if (length === 0) { root.showHistory = true; listView.forceActiveFocus(); } else if (results.count > 0) { results.forceActiveFocus(); results.decrementCurrentIndex(); } } function move_down() { if (length === 0) { root.showHistory = true; listView.forceActiveFocus(); } else if (results.count > 0) { results.forceActiveFocus(); results.incrementCurrentIndex(); } } onTextChanged: { root.query = queryField.text if (allowCompletion && length > 0) { var history = runnerWindow.history // search the first item in the history rather than the shortest matching one // this way more recently used entries take precedence over older ones (Bug 358985) for (var i = 0, j = history.length; i < j; ++i) { var item = history[i] if (item.toLowerCase().indexOf(text.toLowerCase()) === 0) { var oldText = text text = text + item.substr(oldText.length) select(text.length, oldText.length) break } } } } Keys.onPressed: { allowCompletion = (event.key !== Qt.Key_Backspace && event.key !== Qt.Key_Delete) if (event.modifiers & Qt.ControlModifier) { - if (event.key == Qt.Key_J) { + if (event.key === Qt.Key_J) { move_down() event.accepted = true; - } else if (event.key == Qt.Key_K) { + } else if (event.key === Qt.Key_K) { move_up() event.accepted = true; } } } Keys.onUpPressed: move_up() Keys.onDownPressed: move_down() Keys.onEnterPressed: results.runCurrentIndex(event) Keys.onReturnPressed: results.runCurrentIndex(event) Keys.onEscapePressed: { runnerWindow.visible = false } PlasmaCore.SvgItem { anchors { right: parent.right rightMargin: 6 // from PlasmaStyle TextFieldStyle verticalCenter: parent.verticalCenter } // match clear button width: Math.max(parent.height * 0.8, units.iconSizes.small) height: width svg: PlasmaCore.Svg { imagePath: "widgets/arrows" colorGroup: PlasmaCore.Theme.ButtonColorGroup } elementId: "down-arrow" visible: queryField.length === 0 MouseArea { anchors.fill: parent onPressed: { root.showHistory = !root.showHistory if (root.showHistory) { listView.forceActiveFocus(); // is the history list } else { queryField.forceActiveFocus(); } } } } } PlasmaComponents.ToolButton { iconSource: "window-close" onClicked: runnerWindow.visible = false Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close") Accessible.description: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close Search") tooltip: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close") } } PlasmaExtras.ScrollArea { Layout.alignment: Qt.AlignTop visible: results.count > 0 enabled: visible Layout.fillWidth: true Layout.preferredHeight: Math.min(Screen.height, results.contentHeight) Milou.ResultsView { id: results queryString: root.query runner: root.runner Keys.onPressed: { var ctrl = event.modifiers & Qt.ControlModifier; - if (ctrl && event.key == Qt.Key_J) { + if (ctrl && event.key === Qt.Key_J) { incrementCurrentIndex() - } else if (ctrl && event.key == Qt.Key_K) { + } else if (ctrl && event.key === Qt.Key_K) { decrementCurrentIndex() - } else if (event.text != "") { + } else if (event.text !== "") { queryField.text += event.text; queryField.focus = true; } } onActivated: { runnerWindow.addToHistory(queryString) runnerWindow.visible = false } onUpdateQueryString: { queryField.text = text queryField.cursorPosition = cursorPosition } } } PlasmaExtras.ScrollArea { Layout.alignment: Qt.AlignTop Layout.fillWidth: true visible: root.query.length === 0 && listView.count > 0 // don't accept keyboard input when not visible so the keys propagate to the other list enabled: visible Layout.preferredHeight: Math.min(Screen.height, listView.contentHeight) ListView { id: listView // needs this id so the delegate can access it keyNavigationWraps: true highlight: PlasmaComponents.Highlight {} highlightMoveDuration: 0 activeFocusOnTab: true // we store 50 entries in the history but only show 20 in the UI so it doesn't get too huge model: root.showHistory ? runnerWindow.history.slice(0, 20) : [] delegate: Milou.ResultDelegate { id: resultDelegate width: listView.width typeText: index === 0 ? i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Recent Queries") : "" additionalActions: [{ icon: "list-remove", text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Remove") }] Accessible.description: i18n("in category recent queries") } onActiveFocusChanged: { if (!activeFocus && currentIndex == listView.count-1) { currentIndex = 0; } } Keys.onReturnPressed: runCurrentIndex() Keys.onEnterPressed: runCurrentIndex() Keys.onTabPressed: { if (currentIndex == listView.count-1) { listView.nextItemInFocusChain(true).forceActiveFocus(); } else { incrementCurrentIndex() } } Keys.onBacktabPressed: { if (currentIndex == 0) { listView.nextItemInFocusChain(false).forceActiveFocus(); } else { decrementCurrentIndex() } } Keys.onPressed: { var ctrl = event.modifiers & Qt.ControlModifier; - if (ctrl && event.key == Qt.Key_J) { + if (ctrl && event.key === Qt.Key_J) { incrementCurrentIndex() - } else if (ctrl && event.key == Qt.Key_K) { + } else if (ctrl && event.key === Qt.Key_K) { decrementCurrentIndex() - } else if (event.text != "") { + } else if (event.text !== "") { queryField.text += event.text; queryField.focus = true; } } Keys.onUpPressed: decrementCurrentIndex() Keys.onDownPressed: incrementCurrentIndex() function runCurrentIndex() { var entry = runnerWindow.history[currentIndex] if (entry) { queryField.text = entry queryField.forceActiveFocus(); } } function runAction(actionIndex) { if (actionIndex === 0) { // QStringList changes just reset the model, so we'll remember the index and set it again var currentIndex = listView.currentIndex runnerWindow.removeFromHistory(currentIndex) listView.currentIndex = currentIndex } } } } } diff --git a/lookandfeel/contents/windowswitcher/WindowSwitcher.qml b/lookandfeel/contents/windowswitcher/WindowSwitcher.qml index e809e4f7b..74dbc0948 100644 --- a/lookandfeel/contents/windowswitcher/WindowSwitcher.qml +++ b/lookandfeel/contents/windowswitcher/WindowSwitcher.qml @@ -1,146 +1,146 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2011 Martin Gräßlin Copyright (C) 2013 Marco Martin Copyright (C) 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, see . *********************************************************************/ import QtQuick 2.0 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.kwin 2.0 as KWin KWin.Switcher { id: tabBox readonly property real screenFactor: screenGeometry.width / screenGeometry.height currentIndex: thumbnailListView.currentIndex PlasmaCore.Dialog { id: dialog location: Qt.application.layoutDirection === Qt.RightToLeft ? PlasmaCore.Types.RightEdge : PlasmaCore.Types.LeftEdge visible: tabBox.visible flags: Qt.X11BypassWindowManagerHint x: screenGeometry.x + (Qt.application.layoutDirection === Qt.RightToLeft ? screenGeometry.width - width : 0) y: screenGeometry.y mainItem: PlasmaExtras.ScrollArea { id: dialogMainItem focus: true width: tabBox.screenGeometry.width * 0.15 + (__verticalScrollBar.visible ? __verticalScrollBar.width : 0) height: tabBox.screenGeometry.height - dialog.margins.top - dialog.margins.bottom LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true ListView { id: thumbnailListView model: tabBox.model spacing: units.smallSpacing highlightMoveDuration: 250 highlightResizeDuration: 0 Connections { target: tabBox onCurrentIndexChanged: { thumbnailListView.currentIndex = tabBox.currentIndex; thumbnailListView.positionViewAtIndex(thumbnailListView.currentIndex, ListView.Contain) } } delegate: MouseArea { width: thumbnailListView.width height: delegateColumn.implicitHeight + 2 * delegateColumn.y onClicked: { if (tabBox.noModifierGrab) { tabBox.model.activate(index); } else { thumbnailListView.currentIndex = index; } } ColumnLayout { id: delegateColumn anchors.horizontalCenter: parent.horizontalCenter // anchors.centerIn causes layouting glitches y: units.smallSpacing width: parent.width - 2 * units.smallSpacing spacing: units.smallSpacing focus: index == thumbnailListView.currentIndex Accessible.name: model.caption Accessible.role: Accessible.Client Item { Layout.fillWidth: true Layout.preferredHeight: Math.round(width / tabBox.screenFactor) KWin.ThumbnailItem { anchors.fill: parent wId: windowId } } RowLayout { spacing: units.smallSpacing Layout.fillWidth: true PlasmaCore.IconItem { Layout.preferredHeight: units.iconSizes.medium Layout.preferredWidth: units.iconSizes.medium source: model.icon usesPlasmaTheme: false } PlasmaExtras.Heading { Layout.fillWidth: true height: undefined level: 4 text: model.caption elide: Text.ElideRight wrapMode: Text.WrapAtWordBoundaryOrAnywhere maximumLineCount: 2 lineHeight: 0.95 } } } } highlight: PlasmaComponents.Highlight {} } } /* * Key navigation on outer item for two reasons: * @li we have to emit the change signal * @li on multiple invocation it does not work on the list view. Focus seems to be lost. **/ Keys.onPressed: { - if (event.key == Qt.Key_Up) { + if (event.key === Qt.Key_Up) { icons.decrementCurrentIndex(); - } else if (event.key == Qt.Key_Down) { + } else if (event.key === Qt.Key_Down) { icons.incrementCurrentIndex(); } } } } diff --git a/plasmacalendarintegration/HolidaysConfig.qml b/plasmacalendarintegration/HolidaysConfig.qml index 02c4270dc..d80a6f920 100644 --- a/plasmacalendarintegration/HolidaysConfig.qml +++ b/plasmacalendarintegration/HolidaysConfig.qml @@ -1,118 +1,118 @@ /* * Copyright 2013 Kai Uwe Broulik * Copyright 2015 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 */ import QtQuick 2.0 import QtQuick.Controls 1.2 as QtControls import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.1 import org.kde.plasma.core 2.1 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kholidays 1.0 as KHolidays import org.kde.holidayeventshelperplugin 1.0 Item { id: holidaysConfig width: parent.width height: parent.height signal configurationChanged function saveConfig() { configHelper.saveConfig(); } // This is just for getting the column width QtControls.CheckBox { id: checkbox visible: false } QmlConfigHelper { id: configHelper } ColumnLayout { anchors.fill: parent QtControls.TextField { id: filter Layout.fillWidth: true placeholderText: i18nd("kholidays_calendar_plugin", "Search Holiday Regions") } QtControls.TableView { id: holidaysView signal toggleCurrent Layout.fillWidth: true Layout.fillHeight: true Keys.onSpacePressed: toggleCurrent() model: PlasmaCore.SortFilterModel { sourceModel: KHolidays.HolidayRegionsModel { id: holidaysModel } // SortFilterModel doesn't have a case-sensitivity option... // but filterRegExp always causes case-insensitive sorting filterRegExp: filter.text filterRole: "name" } QtControls.TableViewColumn { width: checkbox.width delegate: QtControls.CheckBox { id: checkBox anchors.centerIn: parent - checked: model ? configHelper.selectedRegions.indexOf(model.region) != -1 : false + checked: model ? configHelper.selectedRegions.indexOf(model.region) !== -1 : false activeFocusOnTab: false // only let the TableView as a whole get focus onClicked: { //needed for model's setData to be called if (checked) { configHelper.addRegion(model.region); } else { configHelper.removeRegion(model.region); } holidaysConfig.configurationChanged(); } } resizable: false movable: false } QtControls.TableViewColumn { role: "region" title: i18nd("kholidays_calendar_plugin", "Region") } QtControls.TableViewColumn { role: "name" title: i18nd("kholidays_calendar_plugin", "Name") } QtControls.TableViewColumn { role: "description" title: i18nd("kholidays_calendar_plugin", "Description") } } } } diff --git a/sddm-theme/Background.qml b/sddm-theme/Background.qml index bddbf5dea..7fa5d98af 100644 --- a/sddm-theme/Background.qml +++ b/sddm-theme/Background.qml @@ -1,67 +1,67 @@ /* * Copyright 2016 Boudhayan Gupta * * 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 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 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.2 FocusScope { id: sceneBackground property var sceneBackgroundType property alias sceneBackgroundColor: sceneColorBackground.color property alias sceneBackgroundImage: sceneImageBackground.source Rectangle { id: sceneColorBackground anchors.fill: parent } Image { id: sceneImageBackground anchors.fill: parent fillMode: Image.PreserveAspectCrop smooth: true; } states: [ State { name: "imageBackground" - when: sceneBackgroundType == "image" + when: sceneBackgroundType === "image" PropertyChanges { target: sceneColorBackground visible: false } PropertyChanges { target: sceneImageBackground visible: true } }, State { name: "colorBackground" - when: sceneBackgroundType != "image" + when: sceneBackgroundType !== "image" PropertyChanges { target: sceneColorBackground visible: true } PropertyChanges { target: sceneImageBackground visible: false } } ] } diff --git a/sddm-theme/Login.qml b/sddm-theme/Login.qml index 9d0ca9969..758b11999 100644 --- a/sddm-theme/Login.qml +++ b/sddm-theme/Login.qml @@ -1,120 +1,120 @@ import "components" import QtQuick 2.0 import QtQuick.Layouts 1.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents SessionManagementScreen { id: root property Item mainPasswordBox: passwordBox property bool showUsernamePrompt: !showUserList property string lastUserName property bool loginScreenUiVisible: false //the y position that should be ensured visible when the on screen keyboard is visible property int visibleBoundary: mapFromItem(loginButton, 0, 0).y onHeightChanged: visibleBoundary = mapFromItem(loginButton, 0, 0).y + loginButton.height + units.smallSpacing signal loginRequest(string username, string password) onShowUsernamePromptChanged: { if (!showUsernamePrompt) { lastUserName = "" } } /* * Login has been requested with the following username and password * If username field is visible, it will be taken from that, otherwise from the "name" property of the currentIndex */ function startLogin() { var username = showUsernamePrompt ? userNameInput.text : userList.selectedUser var password = passwordBox.text //this is partly because it looks nicer //but more importantly it works round a Qt bug that can trigger if the app is closed with a TextField focused //DAVE REPORT THE FRICKING THING AND PUT A LINK loginButton.forceActiveFocus(); loginRequest(username, password); } PlasmaComponents.TextField { id: userNameInput font.pointSize: theme.defaultFont.pointSize + 1 Layout.fillWidth: true text: lastUserName visible: showUsernamePrompt focus: showUsernamePrompt && !lastUserName //if there's a username prompt it gets focus first, otherwise password does placeholderText: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Username") onAccepted: if (root.loginScreenUiVisible) { passwordBox.forceActiveFocus() } } RowLayout { Layout.fillWidth: true PlasmaComponents.TextField { id: passwordBox font.pointSize: theme.defaultFont.pointSize + 1 Layout.fillWidth: true placeholderText: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Password") focus: !showUsernamePrompt || lastUserName echoMode: TextInput.Password revealPasswordButtonShown: true onAccepted: { if (root.loginScreenUiVisible) { startLogin(); } } Keys.onEscapePressed: { mainStack.currentItem.forceActiveFocus(); } //if empty and left or right is pressed change selection in user switch //this cannot be in keys.onLeftPressed as then it doesn't reach the password box Keys.onPressed: { - if (event.key == Qt.Key_Left && !text) { + if (event.key === Qt.Key_Left && !text) { userList.decrementCurrentIndex(); event.accepted = true } - if (event.key == Qt.Key_Right && !text) { + if (event.key === Qt.Key_Right && !text) { userList.incrementCurrentIndex(); event.accepted = true } } Connections { target: sddm onLoginFailed: { passwordBox.selectAll() passwordBox.forceActiveFocus() } } } PlasmaComponents.Button { id: loginButton Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Log In") implicitHeight: passwordBox.height - units.smallSpacing * 0.5 // otherwise it comes out taller than the password field Layout.rightMargin: 1 // prevents it from extending beyond the username field PlasmaCore.IconItem { // no iconSource because if you take away half a unit (implicitHeight), "go-next" gets cut off anchors.fill: parent anchors.margins: units.smallSpacing source: "go-next" } onClicked: startLogin(); } } } diff --git a/sddm-theme/Main.qml b/sddm-theme/Main.qml index 8f4b04ab6..d5ecd5b21 100644 --- a/sddm-theme/Main.qml +++ b/sddm-theme/Main.qml @@ -1,468 +1,468 @@ /* * Copyright 2016 David Edmundson * * 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 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 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.8 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.1 import QtGraphicalEffects 1.0 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 "components" PlasmaCore.ColorScope { id: root // If we're using software rendering, draw outlines instead of shadows // See https://bugs.kde.org/show_bug.cgi?id=398317 readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software colorGroup: PlasmaCore.Theme.ComplementaryColorGroup width: 1600 height: 900 property string notificationMessage LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true PlasmaCore.DataSource { id: keystateSource engine: "keystate" connectedSources: "Caps Lock" } Item { id: wallpaper anchors.fill: parent Repeater { model: screenModel Background { x: geometry.x; y: geometry.y; width: geometry.width; height: geometry.height sceneBackgroundType: config.type sceneBackgroundColor: config.color sceneBackgroundImage: config.background } } } MouseArea { id: loginScreenRoot anchors.fill: parent property bool uiVisible: true - property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive || config.type != "image" + property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive || config.type !== "image" hoverEnabled: true drag.filterChildren: true onPressed: uiVisible = true; onPositionChanged: uiVisible = true; onUiVisibleChanged: { if (blockUI) { fadeoutTimer.running = false; } else if (uiVisible) { fadeoutTimer.restart(); } } onBlockUIChanged: { if (blockUI) { fadeoutTimer.running = false; uiVisible = true; } else { fadeoutTimer.restart(); } } Keys.onPressed: { uiVisible = true; event.accepted = false; } //takes one full minute for the ui to disappear Timer { id: fadeoutTimer running: true interval: 60000 onTriggered: { if (!loginScreenRoot.blockUI) { loginScreenRoot.uiVisible = false; } } } WallpaperFader { - visible: config.type == "image" + visible: config.type === "image" anchors.fill: parent state: loginScreenRoot.uiVisible ? "on" : "off" source: wallpaper mainStack: mainStack footer: footer clock: clock } DropShadow { id: clockShadow anchors.fill: clock source: clock visible: !softwareRendering horizontalOffset: 1 verticalOffset: 1 radius: 6 samples: 14 spread: 0.3 color: "black" // matches Breeze window decoration and desktopcontainment Behavior on opacity { OpacityAnimator { duration: 1000 easing.type: Easing.InOutQuad } } } Clock { id: clock visible: y > 0 property Item shadow: clockShadow y: (userListComponent.userList.y + mainStack.y)/2 - height/2 anchors.horizontalCenter: parent.horizontalCenter } StackView { id: mainStack anchors { left: parent.left right: parent.right } height: root.height focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it Timer { //SDDM has a bug in 0.13 where even though we set the focus on the right item within the window, the window doesn't have focus //it is fixed in 6d5b36b28907b16280ff78995fef764bb0c573db which will be 0.14 //we need to call "window->activate()" *After* it's been shown. We can't control that in QML so we use a shoddy timer //it's been this way for all Plasma 5.x without a huge problem running: true repeat: false interval: 200 onTriggered: mainStack.forceActiveFocus() } initialItem: Login { id: userListComponent userListModel: userModel loginScreenUiVisible: loginScreenRoot.uiVisible userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0 lastUserName: userModel.lastUser showUserList: { if ( !userListModel.hasOwnProperty("count") || !userListModel.hasOwnProperty("disableAvatarsThreshold")) return (userList.y + mainStack.y) > 0 - if ( userListModel.count == 0 ) return false + if ( userListModel.count === 0 ) return false return userListModel.count <= userListModel.disableAvatarsThreshold && (userList.y + mainStack.y) > 0 } notificationMessage: { var text = "" if (keystateSource.data["Caps Lock"]["Locked"]) { text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") if (root.notificationMessage) { text += " • " } } text += root.notificationMessage return text } actionItems: [ ActionButton { iconSource: "system-suspend" text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep") onClicked: sddm.suspend() enabled: sddm.canSuspend visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-reboot" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") onClicked: sddm.reboot() enabled: sddm.canReboot visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-shutdown" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down") onClicked: sddm.powerOff() enabled: sddm.canPowerOff visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-user-prompt" text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "For switching to a username and password prompt", "Other...") onClicked: mainStack.push(userPromptComponent) enabled: true visible: !userListComponent.showUsernamePrompt && !inputPanel.keyboardActive }] onLoginRequest: { root.notificationMessage = "" sddm.login(username, password, sessionButton.currentIndex) } } Behavior on opacity { OpacityAnimator { duration: units.longDuration } } } Loader { id: inputPanel state: "hidden" property bool keyboardActive: item ? item.active : false onKeyboardActiveChanged: { if (keyboardActive) { state = "visible" } else { state = "hidden"; } } source: "components/VirtualKeyboard.qml" anchors { left: parent.left right: parent.right } function showHide() { state = state == "hidden" ? "visible" : "hidden"; } states: [ State { name: "visible" PropertyChanges { target: mainStack y: Math.min(0, root.height - inputPanel.height - userListComponent.visibleBoundary) } PropertyChanges { target: inputPanel y: root.height - inputPanel.height opacity: 1 } }, State { name: "hidden" PropertyChanges { target: mainStack y: 0 } PropertyChanges { target: inputPanel y: root.height - root.height/4 opacity: 0 } } ] transitions: [ Transition { from: "hidden" to: "visible" SequentialAnimation { ScriptAction { script: { inputPanel.item.activated = true; Qt.inputMethod.show(); } } ParallelAnimation { NumberAnimation { target: mainStack property: "y" duration: units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: units.longDuration easing.type: Easing.OutQuad } OpacityAnimator { target: inputPanel duration: units.longDuration easing.type: Easing.OutQuad } } } }, Transition { from: "visible" to: "hidden" SequentialAnimation { ParallelAnimation { NumberAnimation { target: mainStack property: "y" duration: units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: units.longDuration easing.type: Easing.InQuad } OpacityAnimator { target: inputPanel duration: units.longDuration easing.type: Easing.InQuad } } ScriptAction { script: { Qt.inputMethod.hide(); } } } } ] } Component { id: userPromptComponent Login { showUsernamePrompt: true notificationMessage: root.notificationMessage loginScreenUiVisible: loginScreenRoot.uiVisible userListModel: QtObject { property string name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Type in Username and Password") property string iconSource: "" } onLoginRequest: { root.notificationMessage = "" sddm.login(username, password, sessionButton.currentIndex) } actionItems: [ ActionButton { iconSource: "system-suspend" text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep") onClicked: sddm.suspend() enabled: sddm.canSuspend visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-reboot" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") onClicked: sddm.reboot() enabled: sddm.canReboot visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-shutdown" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down") onClicked: sddm.powerOff() enabled: sddm.canPowerOff visible: !inputPanel.keyboardActive }, ActionButton { iconSource: "system-user-list" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","List Users") onClicked: mainStack.pop() visible: !inputPanel.keyboardActive } ] } } //Footer RowLayout { id: footer anchors { bottom: parent.bottom left: parent.left right: parent.right margins: units.smallSpacing } Behavior on opacity { OpacityAnimator { duration: units.longDuration } } PlasmaComponents.ToolButton { text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard") iconName: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off" onClicked: inputPanel.showHide() visible: inputPanel.status == Loader.Ready } KeyboardButton { } SessionButton { id: sessionButton } Item { Layout.fillWidth: true } Battery { } } } Connections { target: sddm onLoginFailed: { notificationMessage = i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Login Failed") } onLoginSucceeded: { //note SDDM will kill the greeter at some random point after this //there is no certainty any transition will finish, it depends on the time it //takes to complete the init mainStack.opacity = 0 footer.opacity = 0 } } onNotificationMessageChanged: { if (notificationMessage) { notificationResetTimer.start(); } } Timer { id: notificationResetTimer interval: 3000 onTriggered: notificationMessage = "" } } diff --git a/wallpapers/image/imagepackage/contents/ui/WallpaperDelegate.qml b/wallpapers/image/imagepackage/contents/ui/WallpaperDelegate.qml index 22e839f0d..883451991 100644 --- a/wallpapers/image/imagepackage/contents/ui/WallpaperDelegate.qml +++ b/wallpapers/image/imagepackage/contents/ui/WallpaperDelegate.qml @@ -1,125 +1,125 @@ /* * Copyright 2013 Marco Martin * Copyright 2014 Sebastian Kügler * * 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.Private 1.0 import QtGraphicalEffects 1.0 import org.kde.kquickcontrolsaddons 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kirigami 2.4 as Kirigami import org.kde.kcm 1.1 as KCM KCM.GridDelegate { id: wallpaperDelegate property alias color: backgroundRect.color - property bool selected: (wallpapersGrid.currentIndex == index) + property bool selected: (wallpapersGrid.currentIndex === index) opacity: model.pendingDeletion ? 0.5 : 1 text: model.display toolTip: model.author.length > 0 ? i18ndc("plasma_wallpaper_org.kde.image", " by ", "By %1", model.author) : "" hoverEnabled: true actions: [ Kirigami.Action { icon.name: "document-open-folder" tooltip: i18nd("plasma_wallpaper_org.kde.image", "Open Containing Folder") onTriggered: imageWallpaper.wallpaperModel.openContainingFolder(index) }, Kirigami.Action { icon.name: "edit-undo" visible: model.pendingDeletion tooltip: i18nd("plasma_wallpaper_org.kde.image", "Restore wallpaper") onTriggered: imageWallpaper.wallpaperModel.setPendingDeletion(index, !model.pendingDeletion) }, Kirigami.Action { icon.name: "edit-delete" tooltip: i18nd("plasma_wallpaper_org.kde.image", "Remove Wallpaper") visible: model.removable && !model.pendingDeletion onTriggered: { imageWallpaper.wallpaperModel.setPendingDeletion(index, true); if (wallpapersGrid.currentIndex === index) { wallpapersGrid.currentIndex = (index + 1) % wallpapersGrid.count; } } } ] thumbnail: Rectangle { id: backgroundRect color: cfg_Color anchors.fill: parent QIconItem { anchors.centerIn: parent width: units.iconSizes.large height: width icon: "view-preview" visible: !walliePreview.visible } QPixmapItem { id: blurBackgroundSource visible: cfg_Blur anchors.fill: parent smooth: true pixmap: model.screenshot fillMode: QPixmapItem.PreserveAspectCrop } FastBlur { visible: cfg_Blur anchors.fill: parent source: blurBackgroundSource radius: 4 } QPixmapItem { id: walliePreview anchors.fill: parent - visible: model.screenshot != null + visible: model.screenshot !== null smooth: true pixmap: model.screenshot fillMode: { if (cfg_FillMode == Image.Stretch) { return QPixmapItem.Stretch; } else if (cfg_FillMode == Image.PreserveAspectFit) { return QPixmapItem.PreserveAspectFit; } else if (cfg_FillMode == Image.PreserveAspectCrop) { return QPixmapItem.PreserveAspectCrop; } else if (cfg_FillMode == Image.Tile) { return QPixmapItem.Tile; } else if (cfg_FillMode == Image.TileVertically) { return QPixmapItem.TileVertically; } else if (cfg_FillMode == Image.TileHorizontally) { return QPixmapItem.TileHorizontally; } return QPixmapItem.PreserveAspectFit; } } } onClicked: { cfg_Image = model.path; wallpapersGrid.forceActiveFocus(); } } diff --git a/wallpapers/image/imagepackage/contents/ui/config.qml b/wallpapers/image/imagepackage/contents/ui/config.qml index 500ee090d..273cd6621 100644 --- a/wallpapers/image/imagepackage/contents/ui/config.qml +++ b/wallpapers/image/imagepackage/contents/ui/config.qml @@ -1,336 +1,336 @@ /* * 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.Controls 2.3 as QtControls2 import QtQuick.Layouts 1.0 import QtQuick.Window 2.0 // for Screen import org.kde.plasma.wallpapers.image 2.0 as Wallpaper import org.kde.kquickcontrols 2.0 as KQuickControls import org.kde.kquickcontrolsaddons 2.0 import org.kde.kconfig 1.0 // for KAuthorized import org.kde.draganddrop 2.0 as DragDrop import org.kde.kcm 1.1 as KCM import org.kde.kirigami 2.5 as Kirigami ColumnLayout { id: root property alias cfg_Color: colorButton.color property string cfg_Image property int cfg_FillMode property alias cfg_Blur: blurRadioButton.checked property var cfg_SlidePaths: "" property int cfg_SlideInterval: 0 function saveConfig() { 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: Kirigami.Units.largeSpacing / 2 QtControls2.Label { id: positionLabel width: formAlignment - Kirigami.Units.largeSpacing anchors { verticalCenter: resizeComboBox.verticalCenter } text: i18nd("plasma_wallpaper_org.kde.image", "Positioning:") horizontalAlignment: Text.AlignRight } // TODO: port to QQC2 version once we've fixed https://bugs.kde.org/show_bug.cgi?id=403153 QtControls.ComboBox { id: resizeComboBox TextMetrics { id: resizeTextMetrics text: resizeComboBox.currentText } width: resizeTextMetrics.width + Kirigami.Units.smallSpacing * 2 + Kirigami.Units.gridUnit * 2 model: [ { 'label': i18nd("plasma_wallpaper_org.kde.image", "Scaled and Cropped"), 'fillMode': Image.PreserveAspectCrop }, { 'label': i18nd("plasma_wallpaper_org.kde.image","Scaled"), 'fillMode': Image.Stretch }, { 'label': i18nd("plasma_wallpaper_org.kde.image","Scaled, Keep Proportions"), 'fillMode': Image.PreserveAspectFit }, { 'label': i18nd("plasma_wallpaper_org.kde.image", "Centered"), 'fillMode': Image.Pad }, { 'label': i18nd("plasma_wallpaper_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) { + if (model[i]["fillMode"] === wallpaper.configuration.FillMode) { resizeComboBox.currentIndex = i; var tl = model[i]["label"].length; //resizeComboBox.textLength = Math.max(resizeComboBox.textLength, tl+5); } } } } } QtControls2.ButtonGroup { id: backgroundGroup } Row { id: blurRow spacing: Kirigami.Units.largeSpacing / 2 visible: cfg_FillMode === Image.PreserveAspectFit || cfg_FillMode === Image.Pad QtControls2.Label { id: blurLabel width: formAlignment - Kirigami.Units.largeSpacing anchors.verticalCenter: blurRadioButton.verticalCenter horizontalAlignment: Text.AlignRight text: i18nd("plasma_wallpaper_org.kde.image", "Background:") } QtControls2.RadioButton { id: blurRadioButton text: i18nd("plasma_wallpaper_org.kde.image", "Blur") QtControls2.ButtonGroup.group: backgroundGroup } } Row { id: colorRow visible: cfg_FillMode === Image.PreserveAspectFit || cfg_FillMode === Image.Pad spacing: Kirigami.Units.largeSpacing / 2 QtControls2.Label { width: formAlignment - Kirigami.Units.largeSpacing } QtControls2.RadioButton { id: colorRadioButton text: i18nd("plasma_wallpaper_org.kde.image", "Solid color") QtControls2.ButtonGroup.group: backgroundGroup checked: !cfg_Blur } KQuickControls.ColorButton { id: colorButton dialogTitle: i18nd("plasma_wallpaper_org.kde.image", "Select Background 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: Kirigami.Units.largeSpacing / 2 QtControls2.Label { Layout.minimumWidth: formAlignment - Kirigami.Units.largeSpacing horizontalAlignment: Text.AlignRight text: i18nd("plasma_wallpaper_org.kde.image","Change every:") } QtControls2.SpinBox { id: hoursInterval Layout.minimumWidth: textMetrics.width + Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit * 3 value: root.hoursIntervalValue from: 0 to: 24 editable: true onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls2.Label { text: i18nd("plasma_wallpaper_org.kde.image","Hours") } Item { Layout.preferredWidth: Kirigami.Units.gridUnit } QtControls2.SpinBox { id: minutesInterval Layout.minimumWidth: textMetrics.width + Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit * 3 value: root.minutesIntervalValue from: 0 to: 60 editable: true onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls2.Label { text: i18nd("plasma_wallpaper_org.kde.image","Minutes") } Item { Layout.preferredWidth: Kirigami.Units.gridUnit } QtControls2.SpinBox { id: secondsInterval Layout.minimumWidth: textMetrics.width + Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit * 3 value: root.secondsIntervalValue from: root.hoursIntervalValue === 0 && root.minutesIntervalValue === 0 ? 1 : 0 to: 60 editable: true onValueChanged: cfg_SlideInterval = hoursInterval.value * 3600 + minutesInterval.value * 60 + secondsInterval.value } QtControls2.Label { text: i18nd("plasma_wallpaper_org.kde.image","Seconds") } } QtControls2.ScrollView { id: foldersScroll Layout.fillHeight: true; Layout.fillWidth: true Component.onCompleted: foldersScroll.background.visible = true; ListView { id: slidePathsView anchors.margins: 4 model: imageWallpaper.slidePaths delegate: QtControls2.Label { text: modelData width: slidePathsView.width height: Math.max(paintedHeight, removeButton.height); QtControls2.ToolButton { id: removeButton anchors { verticalCenter: parent.verticalCenter right: parent.right } icon.name: "list-remove" onClicked: imageWallpaper.removeSlidePath(modelData); } } } } } } Component { id: thumbnailsComponent KCM.GridView { id: wallpapersGrid anchors.fill: parent //that min is needed as the module will be populated in an async way //and only on demand so we can't ensure it already exists view.currentIndex: Math.min(imageWallpaper.wallpaperModel.indexOf(cfg_Image), imageWallpaper.wallpaperModel.count-1) //kill the space for label under thumbnails view.model: imageWallpaper.wallpaperModel view.delegate: WallpaperDelegate { color: cfg_Color } } } DragDrop.DropArea { Layout.fillWidth: true Layout.fillHeight: true onDragEnter: { if (!event.mimeData.hasUrls) { event.ignore(); } } onDrop: { event.mimeData.urls.forEach(function (url) { if (url.indexOf("file://") === 0) { var path = url.substr(7); // 7 is length of "file://" if (configDialog.currentWallpaper === "org.kde.image") { imageWallpaper.addUsersWallpaper(path); } else { imageWallpaper.addSlidePath(path); } } }); } Loader { anchors.fill: parent sourceComponent: (configDialog.currentWallpaper == "org.kde.image") ? thumbnailsComponent : foldersComponent } } RowLayout { id: buttonsRow Layout.alignment: Qt.AlignRight | Qt.AlignVCenter QtControls2.Button { visible: (configDialog.currentWallpaper == "org.kde.slideshow") icon.name: "list-add" text: i18nd("plasma_wallpaper_org.kde.image","Add Folder...") onClicked: imageWallpaper.showAddSlidePathsDialog() } QtControls2.Button { visible: (configDialog.currentWallpaper == "org.kde.image") icon.name: "list-add" text: i18nd("plasma_wallpaper_org.kde.image","Add Image...") onClicked: imageWallpaper.showFileDialog(); } QtControls2.Button { icon.name: "get-hot-new-stuff" text: i18nd("plasma_wallpaper_org.kde.image","Get New Wallpapers...") visible: KAuthorized.authorize("ghns") onClicked: imageWallpaper.getNewWallpaper(this); } } } diff --git a/wallpapers/image/imagepackage/contents/ui/main.qml b/wallpapers/image/imagepackage/contents/ui/main.qml index 54a7c0f30..5129cd0db 100644 --- a/wallpapers/image/imagepackage/contents/ui/main.qml +++ b/wallpapers/image/imagepackage/contents/ui/main.qml @@ -1,168 +1,168 @@ /* * Copyright 2013 Marco Martin * Copyright 2014 Sebastian Kügler * 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 2.1 as QQC2 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.plasma.wallpapers.image 2.0 as Wallpaper import org.kde.plasma.core 2.0 as PlasmaCore QQC2.StackView { id: root readonly property string modelImage: imageWallpaper.wallpaperPath readonly property string configuredImage: wallpaper.configuration.Image readonly property int fillMode: wallpaper.configuration.FillMode readonly property string configColor: wallpaper.configuration.Color readonly property bool blur: wallpaper.configuration.Blur readonly property size sourceSize: Qt.size(root.width * Screen.devicePixelRatio, root.height * Screen.devicePixelRatio) //public API, the C++ part will look for those function setUrl(url) { wallpaper.configuration.Image = url imageWallpaper.addUsersWallpaper(url); } function action_next() { imageWallpaper.nextSlide(); } function action_open() { Qt.openUrlExternally(modelImage) } //private onConfiguredImageChanged: { imageWallpaper.addUrl(configuredImage) } Component.onCompleted: { - if (wallpaper.pluginName == "org.kde.slideshow") { + if (wallpaper.pluginName === "org.kde.slideshow") { wallpaper.setAction("open", i18nd("plasma_wallpaper_org.kde.image", "Open Wallpaper Image"), "document-open"); wallpaper.setAction("next", i18nd("plasma_wallpaper_org.kde.image", "Next Wallpaper Image"), "user-desktop"); } } Wallpaper.Image { id: imageWallpaper //the oneliner of difference between image and slideshow wallpapers - renderingMode: (wallpaper.pluginName == "org.kde.image") ? Wallpaper.Image.SingleImage : Wallpaper.Image.SlideShow + renderingMode: (wallpaper.pluginName === "org.kde.image") ? Wallpaper.Image.SingleImage : Wallpaper.Image.SlideShow targetSize: Qt.size(root.width, root.height) slidePaths: wallpaper.configuration.SlidePaths slideTimer: wallpaper.configuration.SlideInterval } onFillModeChanged: Qt.callLater(loadImage); onModelImageChanged: Qt.callLater(loadImage); onConfigColorChanged: Qt.callLater(loadImage); onBlurChanged: Qt.callLater(loadImage); onWidthChanged: Qt.callLater(loadImage); onHeightChanged: Qt.callLater(loadImage); function loadImage() { var isFirst = (root.currentItem == undefined); var pendingImage = baseImage.createObject(root, { "source": root.modelImage, "fillMode": root.fillMode, "sourceSize": root.sourceSize, "color": root.configColor, "blur": root.blur, "opacity": isFirst ? 1: 0}); function replaceWhenLoaded() { - if (pendingImage.status != Image.Loading) { + if (pendingImage.status !== Image.Loading) { root.replace(pendingImage, {}, isFirst ? QQC2.StackView.Immediate : QQC2.StackView.Transition);//dont' animate first show pendingImage.statusChanged.disconnect(replaceWhenLoaded); } } pendingImage.statusChanged.connect(replaceWhenLoaded); replaceWhenLoaded(); } Component { id: baseImage Image { id: mainImage property alias color: backgroundColor.color property bool blur: false asynchronous: true cache: false autoTransform: true z: -1 QQC2.StackView.onRemoved: destroy() Rectangle { id: backgroundColor anchors.fill: parent visible: mainImage.status === Image.Ready && !blurLoader.active z: -2 } Loader { id: blurLoader anchors.fill: parent z: -3 active: mainImage.blur && (mainImage.fillMode === Image.PreserveAspectFit || mainImage.fillMode === Image.Pad) sourceComponent: Item { Image { id: blurSource anchors.fill: parent asynchronous: true cache: false autoTransform: true fillMode: Image.PreserveAspectCrop source: mainImage.source sourceSize: mainImage.sourceSize visible: false // will be rendered by the blur } GaussianBlur { id: blurEffect anchors.fill: parent source: blurSource radius: 32 samples: 65 visible: blurSource.status === Image.Ready } } } } } replaceEnter: Transition { OpacityAnimator { from: 0 to: 1 duration: wallpaper.configuration.TransitionAnimationDuration } } // Keep the old image around till the new one is fully faded in // If we fade both at the same time you can see the background behind glimpse through replaceExit: Transition{ PauseAnimation { duration: wallpaper.configuration.TransitionAnimationDuration } } } diff --git a/wallpapers/image/imagepackage/platformcontents/phone/ui/WallpaperDelegate.qml b/wallpapers/image/imagepackage/platformcontents/phone/ui/WallpaperDelegate.qml index 4b11dd5c4..49f5fe822 100644 --- a/wallpapers/image/imagepackage/platformcontents/phone/ui/WallpaperDelegate.qml +++ b/wallpapers/image/imagepackage/platformcontents/phone/ui/WallpaperDelegate.qml @@ -1,129 +1,129 @@ /* * Copyright 2013 Marco Martin * Copyright 2014 Sebastian Kügler * * 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.Private 1.0 import org.kde.kquickcontrolsaddons 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents MouseArea { id: wallpaperDelegate width: wallpapersGrid.cellWidth height: wallpapersGrid.cellHeight property bool selected: (wallpapersGrid.currentIndex == index) hoverEnabled: true //note: this *doesn't* use system colors since it represent a //skeymorphic photograph rather than a widget Rectangle { id: background color: "white" anchors { fill: parent margins: units.smallSpacing } opacity: 0.8 Rectangle { color: cfg_Color anchors { fill: parent margins: units.smallSpacing * 2 } QIconItem { anchors.centerIn: parent width: units.iconSizes.large height: width icon: "view-preview" visible: !walliePreview.visible } QPixmapItem { id: walliePreview anchors.fill: parent - visible: model.screenshot != null + visible: model.screenshot !== null smooth: true pixmap: model.screenshot fillMode: { if (cfg_FillMode == Image.Stretch) { return QPixmapItem.Stretch; } else if (cfg_FillMode == Image.PreserveAspectFit) { return QPixmapItem.PreserveAspectFit; } else if (cfg_FillMode == Image.PreserveAspectCrop) { return QPixmapItem.PreserveAspectCrop; } else if (cfg_FillMode == Image.Tile) { return QPixmapItem.Tile; } else if (cfg_FillMode == Image.TileVertically) { return QPixmapItem.TileVertically; } else if (cfg_FillMode == Image.TileHorizontally) { return QPixmapItem.TileHorizontally; } return QPixmapItem.Pad; } } PlasmaComponents.ToolButton { anchors { top: parent.top right: parent.right margins: units.smallSpacing } iconSource: "list-remove" tooltip: i18nd("plasma_wallpaper_org.kde.image", "Remove wallpaper") flat: false visible: model.removable onClicked: imageWallpaper.removeWallpaper(model.packageName) } } } Rectangle { opacity: selected ? 1.0 : 0 anchors.fill: background border.width: units.smallSpacing * 2 border.color: syspal.highlight color: "transparent" Behavior on opacity { PropertyAnimation { duration: units.longDuration easing.type: Easing.OutQuad } } } Timer { interval: 1000 // FIXME TODO: Use platform value for tooltip activation delay. running: wallpaperDelegate.containsMouse && !pressed && model.display && model.author onTriggered: { Tooltip.showText(wallpaperDelegate, Qt.point(wallpaperDelegate.mouseX, wallpaperDelegate.mouseY), i18nd("plasma_wallpaper_org.kde.image", "%1 by %2", model.display, model.author)); } } onClicked: { wallpapersGrid.currentIndex = index cfg_Image = model.path } onExited: Tooltip.hideText() } diff --git a/wallpapers/image/imagepackage/platformcontents/touch/ui/WallpaperDelegate.qml b/wallpapers/image/imagepackage/platformcontents/touch/ui/WallpaperDelegate.qml index 68470f7d9..9e9ef65da 100644 --- a/wallpapers/image/imagepackage/platformcontents/touch/ui/WallpaperDelegate.qml +++ b/wallpapers/image/imagepackage/platformcontents/touch/ui/WallpaperDelegate.qml @@ -1,79 +1,79 @@ /* * Copyright 2013 Marco Martin * Copyright 2014 Sebastian Kügler * * 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.kquickcontrolsaddons 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents MouseArea { id: wallpaperDelegate width: wallpapersGrid.delegateWidth height: wallpapersGrid.delegateHeight property bool selected: (wallpapersGrid.currentIndex == index) hoverEnabled: true PlasmaCore.FrameSvgItem { id: frameSvg imagePath: "widgets/media-delegate" prefix: (wallpapersGrid.currentIndex - (wallpapersGrid.currentPage*wallpapersGrid.pageSize)) == index ? "picture-selected" : "picture" width: (wallpapersGrid.currentIndex - (wallpapersGrid.currentPage*wallpapersGrid.pageSize)) == index ? parent.width+5 : parent.width-16 height: (wallpapersGrid.currentIndex - (wallpapersGrid.currentPage*wallpapersGrid.pageSize)) == index ? parent.height+5 : parent.height-16 anchors { left: parent.left right: parent.right bottomMargin: 8 } Behavior on width { NumberAnimation { duration: 250 easing.type: Easing.InOutQuad } } Behavior on height { NumberAnimation { duration: 250 easing.type: Easing.InOutQuad } } QPixmapItem { id: walliePreview anchors { fill: parent leftMargin: parent.margins.left topMargin: parent.margins.top rightMargin: parent.margins.right bottomMargin: parent.margins.bottom } - visible: model.screenshot != null + visible: model.screenshot !== null smooth: true pixmap: model.screenshot fillMode: QPixmapItem.Stretch } } onClicked: { wallpapersGrid.currentIndex = (wallpapersGrid.currentPage*wallpapersGrid.pageSize) + index cfg_Image = model.path } }