Warning, /plasma/plasma-workspace/kcms/nightcolor/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.1
0009 import QtQuick.Controls 2.5 as QQC2
0010 
0011 import org.kde.kirigami 2.20 as Kirigami
0012 import org.kde.kcmutils as KCM
0013 
0014 import org.kde.colorcorrect as CC
0015 
0016 import org.kde.private.kcms.nightcolor 1.0
0017 
0018 KCM.SimpleKCM {
0019     id: root
0020     property int error: cA.error
0021     property bool defaultRequested: false
0022     property QtObject locator
0023     readonly property bool doneLocating: locator && !(locator.latitude == 0 && locator.longitude == 0)
0024     implicitHeight: Kirigami.Units.gridUnit * 29
0025     implicitWidth: Kirigami.Units.gridUnit * 35
0026 
0027     CC.CompositorAdaptor {
0028         id: cA
0029     }
0030 
0031     CC.SunCalc {
0032         id: sunCalc
0033     }
0034 
0035     // the Geolocator object is created dynamically so we can have control over when geolocation is attempted
0036     // because the object attempts geolocation immediately when created, which is unnecessary (and bad for privacy)
0037 
0038     function startLocator() {
0039         root.locator = Qt.createQmlObject('import org.kde.colorcorrect as CC; CC.Geolocator {}', root, "geoLocatorObj");
0040     }
0041 
0042     function endLocator() {
0043         root.locator?.destroy();
0044     }
0045 
0046     Connections {
0047         target: kcm.nightColorSettings
0048         function onActiveChanged() {
0049             if (kcm.nightColorSettings.active && kcm.nightColorSettings.mode == NightColorMode.Automatic) {
0050                 startLocator();
0051             } else {
0052                 endLocator();
0053             }
0054         }
0055     }
0056 
0057     Component.onCompleted: {
0058         if (kcm.nightColorSettings.mode == NightColorMode.Automatic && kcm.nightColorSettings.active) {
0059             startLocator();
0060         }
0061     }
0062 
0063     // Update backend when locator is changed
0064     Connections {
0065         target: root.locator
0066         function onLatitudeChanged() {
0067             kcm.nightColorSettings.latitudeAuto = Math.round(root.locator.latitude * 100) / 100
0068         }
0069         function onLongitudeChanged() {
0070             kcm.nightColorSettings.longitudeAuto = Math.round(root.locator.longitude * 100) / 100
0071         }
0072     }
0073 
0074     header: ColumnLayout{
0075         Kirigami.InlineMessage {
0076             id: errorMessage
0077             Layout.fillWidth: true
0078             visible: error != CC.CompositorAdaptor.ErrorCodeSuccess
0079             type: Kirigami.MessageType.Error
0080             text: cA.errorText
0081         }
0082     }
0083 
0084     Timer {
0085         id: previewTimer
0086         interval: Kirigami.Units.humanMoment
0087         onTriggered: cA.stopPreview()
0088     }
0089 
0090     ColumnLayout {
0091         spacing: 0
0092 
0093         QQC2.Label {
0094             Layout.margins: Kirigami.Units.gridUnit
0095             Layout.alignment: Qt.AlignHCenter
0096 
0097             Layout.maximumWidth: Math.round(root.width - (Kirigami.Units.gridUnit * 2))
0098             text: i18n("The blue light filter makes the colors on the screen warmer.")
0099             textFormat: Text.PlainText
0100             wrapMode: Text.WordWrap
0101         }
0102 
0103         DayNightView {
0104             Layout.margins: Kirigami.Units.smallSpacing
0105             Layout.bottomMargin: Kirigami.Units.gridUnit
0106             Layout.maximumWidth: Kirigami.Units.gridUnit * 30
0107             Layout.alignment: Qt.AlignCenter
0108 
0109             readonly property real latitude: kcm.nightColorSettings.mode === NightColorMode.Location
0110                 ? kcm.nightColorSettings.latitudeFixed
0111                 : (locator?.locatingDone) ? locator.latitude : kcm.nightColorSettings.latitudeAuto
0112             readonly property real longitude: kcm.nightColorSettings.mode === NightColorMode.Location
0113                 ? kcm.nightColorSettings.longitudeFixed
0114                 : (locator?.locatingDone) ? locator.longitude : kcm.nightColorSettings.longitudeAuto
0115 
0116             readonly property var morningTimings: sunCalc.getMorningTimings(latitude, longitude)
0117             readonly property var eveningTimings: sunCalc.getEveningTimings(latitude, longitude)
0118 
0119             enabled: kcm.nightColorSettings.active
0120 
0121             dayTemperature: kcm.nightColorSettings.dayTemperature
0122             nightTemperature: kcm.nightColorSettings.nightTemperature
0123 
0124             alwaysOn: kcm.nightColorSettings.mode === NightColorMode.Constant
0125             dayTransitionOn: kcm.nightColorSettings.mode === NightColorMode.Timings
0126                 ? minutesForFixed(kcm.nightColorSettings.morningBeginFixed)
0127                 : minutesForDate(morningTimings.begin)
0128             dayTransitionOff: kcm.nightColorSettings.mode === NightColorMode.Timings
0129                 ? (minutesForFixed(kcm.nightColorSettings.morningBeginFixed) + kcm.nightColorSettings.transitionTime) % 1440
0130                 : minutesForDate(morningTimings.end)
0131             nightTransitionOn: kcm.nightColorSettings.mode === NightColorMode.Timings
0132                 ? minutesForFixed(kcm.nightColorSettings.eveningBeginFixed)
0133                 : minutesForDate(eveningTimings.begin)
0134             nightTransitionOff: kcm.nightColorSettings.mode === NightColorMode.Timings
0135                 ? (minutesForFixed(kcm.nightColorSettings.eveningBeginFixed) + kcm.nightColorSettings.transitionTime) % 1440
0136                 : minutesForDate(eveningTimings.end)
0137 
0138             function minutesForFixed(dateString) {
0139                 // The fixed timings format is "hhmm"
0140                 const hours = parseInt(dateString.substring(0, 2), 10)
0141                 const mins = parseInt(dateString.substring(2, 4), 10)
0142                 return hours * 60 + mins
0143             }
0144         }
0145 
0146 
0147         Kirigami.FormLayout {
0148             id: parentLayout
0149 
0150             QQC2.ComboBox {
0151                 id: modeSwitcher
0152                 // Work around https://bugs.kde.org/show_bug.cgi?id=403153
0153                 Layout.minimumWidth: Kirigami.Units.gridUnit * 17
0154                 Kirigami.FormData.label: i18n("Switching times:")
0155                 currentIndex: kcm.nightColorSettings.active ? kcm.nightColorSettings.mode + 1 : 0
0156                 model: [
0157                     i18n("Always off"),  // This is not actually a Mode, but represents Night Color being disabled
0158                     i18n("Sunset and sunrise at current location"),
0159                     i18n("Sunset and sunrise at manual location"),
0160                     i18n("Custom times"),
0161                     i18n("Always on night light")
0162                 ]
0163                 onCurrentIndexChanged: {
0164                     if (currentIndex !== 0) {
0165                         kcm.nightColorSettings.mode = currentIndex - 1;
0166                     }
0167                     kcm.nightColorSettings.active = (currentIndex !== 0);
0168                     if (currentIndex - 1 == NightColorMode.Automatic && kcm.nightColorSettings.active) {
0169                         startLocator();
0170                     } else {
0171                         endLocator();
0172                     }
0173                 }
0174             }
0175 
0176             // Workaround for Layout.margins not working in Kirigami FormLayout (bug 434625)
0177             Item { implicitHeight: Kirigami.Units.largeSpacing }
0178 
0179             Item {
0180                 Kirigami.FormData.isSection: true
0181             }
0182 
0183             GridLayout {
0184                 Kirigami.FormData.label: i18n("Day light temperature:")
0185                 Kirigami.FormData.buddyFor: tempSliderDay
0186                 enabled: kcm.nightColorSettings.active && kcm.nightColorSettings.mode !== NightColorMode.Constant
0187 
0188                 columns: 4
0189 
0190                 QQC2.Slider {
0191                     id: tempSliderDay
0192                     // Match combobox width
0193                     Layout.minimumWidth: modeSwitcher.width
0194                     Layout.columnSpan: 3
0195                     from: kcm.maxDayTemp
0196                     to: kcm.minDayTemp
0197                     stepSize: -100
0198                     live: true
0199 
0200                     value: kcm.nightColorSettings.dayTemperature
0201 
0202                     onMoved: {
0203                         kcm.nightColorSettings.dayTemperature = value
0204                         cA.preview(value)
0205 
0206                         // This can fire for scroll events; in this case we need
0207                         // to use a timer to make the preview message disappear, since
0208                         // we can't make it disappear in the onPressedChanged handler
0209                         // since there is no press
0210                         if (!pressed) {
0211                             previewTimer.restart()
0212                         }
0213                     }
0214                     onPressedChanged: {
0215                         if (!pressed) {
0216                             cA.stopPreview()
0217                         }
0218                     }
0219 
0220                     KCM.SettingStateBinding {
0221                         configObject: kcm.nightColorSettings
0222                         settingName: "DayTemperature"
0223                         extraEnabledConditions: kcm.nightColorSettings.active
0224                     }
0225                 }
0226                 QQC2.Label {
0227                     text: i18nc("Color temperature in Kelvin", "%1K", tempSliderDay.value)
0228                     textFormat: Text.PlainText
0229                 }
0230                 //row 2
0231                 QQC2.Label {
0232                     text: i18nc("Night colour blue-ish; no blue light filter activated", "Cool (no filter)")
0233                     textFormat: Text.PlainText
0234                 }
0235                 Item {
0236                     Layout.fillWidth: true
0237                 }
0238                 QQC2.Label {
0239                     text: i18nc("Night colour red-ish", "Warm")
0240                     textFormat: Text.PlainText
0241                 }
0242                 Item {}
0243             }
0244 
0245             GridLayout {
0246                 Kirigami.FormData.label: i18n("Night light temperature:")
0247                 Kirigami.FormData.buddyFor: tempSliderNight
0248                 enabled: kcm.nightColorSettings.active
0249 
0250                 columns: 4
0251 
0252                 QQC2.Slider {
0253                     id: tempSliderNight
0254                     // Match combobox width
0255                     Layout.minimumWidth: modeSwitcher.width
0256                     Layout.columnSpan: 3
0257                     from: kcm.maxNightTemp
0258                     to: kcm.minNightTemp
0259                     stepSize: -100
0260                     live: true
0261 
0262                     value: kcm.nightColorSettings.nightTemperature
0263 
0264                     onMoved: {
0265                         kcm.nightColorSettings.nightTemperature = value
0266                         cA.preview(value)
0267 
0268                         // This can fire for scroll events; in this case we need
0269                         // to use a timer to make the preview disappear, since
0270                         // we can't make it disappear in the onPressedChanged handler
0271                         // since there is no press
0272                         if (!pressed) {
0273                             previewTimer.restart()
0274                         }
0275                     }
0276                     onPressedChanged: {
0277                         if (!pressed) {
0278                             cA.stopPreview()
0279                         }
0280                     }
0281 
0282                     KCM.SettingStateBinding {
0283                         configObject: kcm.nightColorSettings
0284                         settingName: "NightTemperature"
0285                         extraEnabledConditions: kcm.nightColorSettings.active
0286                     }
0287                 }
0288                 QQC2.Label {
0289                     text: i18nc("Color temperature in Kelvin", "%1K", tempSliderNight.value)
0290                     textFormat: Text.PlainText
0291                 }
0292                 //row 2
0293                 QQC2.Label {
0294                     text: i18nc("Night colour blue-ish; no blue light filter activated", "Cool (no filter)")
0295                     textFormat: Text.PlainText
0296                 }
0297                 Item {
0298                     Layout.fillWidth: true
0299                 }
0300                 QQC2.Label {
0301                     text: i18nc("Night colour red-ish", "Warm")
0302                     textFormat: Text.PlainText
0303                 }
0304                 Item {}
0305             }
0306 
0307             Item { implicitHeight: Kirigami.Units.largeSpacing }
0308 
0309             // Show current location in auto mode
0310             QQC2.Label {
0311                 Kirigami.FormData.label: i18nc("@label The coordinates for the current location", "Current location:")
0312 
0313                 visible: kcm.nightColorSettings.mode === NightColorMode.Automatic && kcm.nightColorSettings.active
0314                     && root.doneLocating
0315                 enabled: kcm.nightColorSettings.active
0316                 wrapMode: Text.Wrap
0317                 text: i18n("Latitude: %1°   Longitude: %2°", Math.round((locator?.latitude || 0) * 100)/100, Math.round((locator?.longitude || 0) * 100)/100)
0318                 textFormat: Text.PlainText
0319             }
0320 
0321             // Inform about geolocation access in auto mode
0322             // The system settings window likes to take over the cursor with a plain label.
0323             // The TextEdit 'takes priority' over the system settings window trying to eat the mouse,
0324             // allowing us to use the HoverHandler boilerplate for proper link handling
0325             TextEdit {
0326                 Layout.maximumWidth: modeSwitcher.width
0327 
0328                 visible: modeSwitcher.currentIndex - 1 === NightColorMode.Automatic && kcm.nightColorSettings.active
0329                 enabled: kcm.nightColorSettings.active
0330 
0331                 textFormat: TextEdit.RichText
0332                 wrapMode: Text.Wrap
0333                 readOnly: true
0334 
0335                 color: Kirigami.Theme.textColor
0336                 selectedTextColor: Kirigami.Theme.highlightedTextColor
0337                 selectionColor: Kirigami.Theme.highlightColor
0338 
0339                 text: xi18nc("@info", "The device's location will be periodically updated using GPS (if available), or by sending network information to <link url='https://location.services.mozilla.com'>Mozilla Location Service</link>.")
0340                 font: Kirigami.Theme.smallFont
0341 
0342                 onLinkActivated: (url) => Qt.openUrlExternally(url)
0343 
0344                 HoverHandler {
0345                     acceptedButtons: Qt.NoButton
0346                     cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
0347                 }
0348             }
0349 
0350             // Show time entry fields in manual timings mode
0351             TimeField {
0352                 id: evenBeginManField
0353                 // Match combobox width
0354                 Layout.minimumWidth: modeSwitcher.width
0355                 Layout.maximumWidth: modeSwitcher.width
0356                 visible: kcm.nightColorSettings.mode === NightColorMode.Timings && kcm.nightColorSettings.active
0357                 Kirigami.FormData.label: i18n("Begin night light at:")
0358                 backend: kcm.nightColorSettings.eveningBeginFixed
0359                 onBackendChanged: {
0360                     kcm.nightColorSettings.eveningBeginFixed = backend;
0361                 }
0362 
0363                 KCM.SettingStateBinding {
0364                     configObject: kcm.nightColorSettings
0365                     settingName: "EveningBeginFixed"
0366                     extraEnabledConditions: kcm.nightColorSettings.active && kcm.nightColorSettings.mode === NightColorMode.Timings
0367                 }
0368 
0369                 QQC2.ToolTip {
0370                     text: i18n("Input format: HH:MM")
0371                 }
0372             }
0373 
0374             TimeField {
0375                 id: mornBeginManField
0376                 // Match combobox width
0377                 Layout.minimumWidth: modeSwitcher.width
0378                 Layout.maximumWidth: modeSwitcher.width
0379                 visible: kcm.nightColorSettings.mode === NightColorMode.Timings && kcm.nightColorSettings.active
0380                 Kirigami.FormData.label: i18n("Begin day light at:")
0381                 backend: kcm.nightColorSettings.morningBeginFixed
0382                 onBackendChanged: {
0383                     kcm.nightColorSettings.morningBeginFixed = backend;
0384                 }
0385 
0386                 KCM.SettingStateBinding {
0387                     configObject: kcm.nightColorSettings
0388                     settingName: "MorningBeginFixed"
0389                     extraEnabledConditions: kcm.nightColorSettings.active && kcm.nightColorSettings.mode === NightColorMode.Timings
0390                 }
0391 
0392                 QQC2.ToolTip {
0393                     text: i18n("Input format: HH:MM")
0394                 }
0395             }
0396 
0397             QQC2.SpinBox {
0398                 id: transTimeField
0399                 visible: kcm.nightColorSettings.mode === NightColorMode.Timings && kcm.nightColorSettings.active
0400                 // Match width of combobox and input fields
0401                 Layout.minimumWidth: modeSwitcher.width
0402                 Kirigami.FormData.label: i18n("Transition duration:")
0403                 from: 1
0404                 to: 600 // less than 12 hours (in minutes: 720)
0405                 value: kcm.nightColorSettings.transitionTime
0406                 editable: true
0407                 onValueModified: {
0408                     kcm.nightColorSettings.transitionTime = value;
0409                 }
0410                 textFromValue: function(value, locale) {
0411                     return i18np("%1 minute", "%1 minutes", value)
0412                 }
0413                 valueFromText: function(text, locale) {
0414                     return parseInt(text);
0415                 }
0416 
0417                 KCM.SettingStateBinding {
0418                     configObject: kcm.nightColorSettings
0419                     settingName: "TransitionTime"
0420                     extraEnabledConditions: kcm.nightColorSettings.active
0421                 }
0422 
0423                 QQC2.ToolTip {
0424                     text: i18n("Input minutes - min. 1, max. 600")
0425                 }
0426             }
0427 
0428             QQC2.Label {
0429                 id: manualTimingsError
0430                 visible: {
0431                     var day = 86400000;
0432                     var trTime = transTimeField.value * 60 * 1000;
0433                     var mor = mornBeginManField.getNormedDate();
0434                     var eve = evenBeginManField.getNormedDate();
0435 
0436                     var diffMorEve = eve > mor ? eve - mor : mor - eve;
0437                     var diffMin = Math.min(diffMorEve, day - diffMorEve);
0438 
0439                     return diffMin <= trTime && kcm.nightColorSettings.active;
0440                 }
0441                 font.italic: true
0442                 text: i18n("Error: Transition time overlaps.")
0443                 textFormat: Text.PlainText
0444             }
0445         }
0446 
0447         // Show location chooser in manual location mode
0448         LocationsFixedView {
0449             visible: kcm.nightColorSettings.mode === NightColorMode.Location && kcm.nightColorSettings.active
0450             Layout.alignment: Qt.AlignHCenter
0451             enabled: kcm.nightColorSettings.active
0452         }
0453 
0454         Item {
0455             visible: kcm.nightColorSettings.active
0456                 && kcm.nightColorSettings.mode === NightColorMode.Automatic
0457                 && (!locator || !root.doneLocating)
0458             Layout.topMargin: Kirigami.Units.largeSpacing * 4
0459             Layout.fillWidth: true
0460             implicitHeight: loadingPlaceholder.implicitHeight
0461 
0462             Kirigami.LoadingPlaceholder {
0463                 id: loadingPlaceholder
0464 
0465                 text: i18nc("@info:placeholder", "Locating…")
0466                 anchors.centerIn: parent
0467             }
0468         }
0469     }
0470 }