Warning, /plasma/kscreen/kcm/ui/OutputPanel.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 import QtCore
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.15
0009 import QtQuick.Controls 2.15 as QQC2
0010 import QtQuick.Dialogs
0011 import org.kde.kirigami 2.20 as Kirigami
0012 import org.kde.kitemmodels 1.0
0013 
0014 import org.kde.kcmutils as KCM
0015 import org.kde.private.kcm.kscreen 1.0 as KScreen
0016 
0017 Kirigami.FormLayout {
0018     id: root
0019 
0020     property KSortFilterProxyModel enabledOutputs
0021     property var element: model
0022 
0023     signal reorder()
0024 
0025     QQC2.CheckBox {
0026        text: i18n("Enabled")
0027        checked: element.enabled
0028        onToggled: element.enabled = checked
0029        visible: kcm.multipleScreensAvailable
0030     }
0031 
0032     RowLayout {
0033         visible: kcm.primaryOutputSupported && root.enabledOutputs.count >= 2
0034 
0035         QQC2.Button {
0036             visible: root.enabledOutputs.count >= 3
0037             text: i18n("Change Screen Priorities…")
0038             icon.name: "document-edit"
0039             onClicked: root.reorder();
0040         }
0041 
0042         QQC2.RadioButton {
0043             visible: root.enabledOutputs.count === 2
0044             text: i18n("Primary")
0045             checked: element.priority === 1
0046             onToggled: element.priority = 1
0047         }
0048 
0049         KCM.ContextualHelpButton {
0050             toolTipText: xi18nc("@info", "This determines which screen your main desktop appears on, along with any Plasma Panels in it. Some older games also use this setting to decide which screen to appear on.<nl/><nl/>It has no effect on what screen notifications or other windows appear on.")
0051         }
0052     }
0053 
0054     RowLayout {
0055         Kirigami.FormData.label: i18n("Resolution:")
0056 
0057         QQC2.ComboBox {
0058             id: resolutionCombobox
0059             Layout.minimumWidth: Kirigami.Units.gridUnit * 11
0060             visible: count > 1
0061             model: element.resolutions
0062             onActivated: element.resolutionIndex = currentIndex;
0063             Component.onCompleted: currentIndex = Qt.binding(function() {return element.resolutionIndex});
0064         }
0065         // When the combobox is has only one item, it's basically non-interactive
0066         // and is serving purely in a descriptive role, so make this explicit by
0067         // using a label instead
0068         QQC2.Label {
0069             id: singleResolutionLabel
0070             visible: resolutionCombobox.count <= 1
0071             text: element.resolutions[0] || ""
0072         }
0073         KCM.ContextualHelpButton {
0074             visible: resolutionCombobox.count <= 1
0075             toolTipText: xi18nc("@info", "&quot;%1&quot; is the only resolution supported by this display.", singleResolutionLabel.text)
0076         }
0077     }
0078 
0079     RowLayout {
0080         Layout.fillWidth: true
0081         // Set the same limit as the device ComboBox
0082         Layout.maximumWidth: Kirigami.Units.gridUnit * 16
0083 
0084         visible: kcm.perOutputScaling
0085         Kirigami.FormData.label: i18n("Scale:")
0086 
0087         QQC2.Slider {
0088             id: scaleSlider
0089 
0090             Layout.fillWidth: true
0091             from: 0.5
0092             to: 3
0093             stepSize: 0.25
0094             live: true
0095             value: element.scale
0096             onMoved: element.scale = value
0097         }
0098         QQC2.SpinBox {
0099             id: spinbox
0100             // Because QQC2 SpinBox doesn't natively support decimal step
0101             // sizes: https://bugreports.qt.io/browse/QTBUG-67349
0102             readonly property real factor: 20.0
0103             readonly property real realValue: value / factor
0104 
0105             from : 0.5 * factor
0106             to : 3.0 * factor
0107             stepSize: 1
0108             value: element.scale * factor
0109             validator: DoubleValidator {
0110                 bottom: Math.min(spinbox.from, spinbox.to) * spinbox.factor
0111                 top:  Math.max(spinbox.from, spinbox.to) * spinbox.factor
0112             }
0113             textFromValue: (value, locale) =>
0114                 i18nc("Global scale factor expressed in percentage form", "%1%",
0115                     parseFloat(value * 1.0 / factor * 100.0))
0116             valueFromText: (text, locale) =>
0117                 Number.fromLocaleString(locale, text.replace("%", "")) * factor / 100.0
0118 
0119             onValueModified: element.scale = realValue
0120         }
0121     }
0122 
0123     Orientation {}
0124 
0125     RowLayout {
0126         Kirigami.FormData.label: i18n("Refresh rate:")
0127 
0128         QQC2.ComboBox {
0129             id: refreshRateCombobox
0130             Layout.minimumWidth: Kirigami.Units.gridUnit * 11
0131             visible: count > 1
0132             model: element.refreshRates
0133             onActivated: element.refreshRateIndex = currentIndex;
0134             Component.onCompleted: currentIndex = Qt.binding(function() {return element.refreshRateIndex});
0135         }
0136         // When the combobox is has only one item, it's basically non-interactive
0137         // and is serving purely in a descriptive role, so make this explicit by
0138         // using a label instead
0139         QQC2.Label {
0140             id: singleRefreshRateLabel
0141             visible: refreshRateCombobox.count <= 1
0142             text: element.refreshRates[0] || ""
0143         }
0144         KCM.ContextualHelpButton {
0145             visible: refreshRateCombobox.count <= 1
0146             toolTipText: i18n("\"%1\" is the only refresh rate supported by this display.", singleRefreshRateLabel.text)
0147         }
0148     }
0149 
0150     QQC2.ComboBox {
0151         Kirigami.FormData.label: i18n("Adaptive sync:")
0152         Layout.minimumWidth: Kirigami.Units.gridUnit * 11
0153         model: [
0154             { label: i18n("Never"), value: KScreen.Output.VrrPolicy.Never },
0155             { label: i18n("Automatic"), value: KScreen.Output.VrrPolicy.Automatic },
0156             { label: i18n("Always"), value: KScreen.Output.VrrPolicy.Always },
0157         ]
0158         textRole: "label"
0159         valueRole: "value"
0160         visible: element.capabilities & KScreen.Output.Capability.Vrr
0161 
0162         onActivated: element.vrrPolicy = currentValue;
0163         Component.onCompleted: currentIndex = indexOfValue(element.vrrPolicy);
0164     }
0165 
0166     RowLayout {
0167         Kirigami.FormData.label: i18n("Overscan:")
0168         visible: element.capabilities & KScreen.Output.Capability.Overscan
0169 
0170         QQC2.SpinBox {
0171             from: 0
0172             to: 100
0173             value: element.overscan
0174             onValueModified: element.overscan = value
0175             textFromValue: (value, locale) =>
0176             i18nc("Overscan expressed in percentage form", "%1%", value)
0177             valueFromText: (text, locale) =>
0178             Number.fromLocaleString(locale, text.replace("%", ""))
0179         }
0180 
0181         KCM.ContextualHelpButton {
0182             toolTipText: xi18nc("@info", `Determines how much padding is put around the image sent to the display
0183                                           to compensate for part of the content being cut off around the edges.<nl/><nl/>
0184                                           This is sometimes needed when using a TV as a screen`)
0185         }
0186     }
0187 
0188     RowLayout {
0189         Kirigami.FormData.label: i18n("RGB range:")
0190         visible: element.capabilities & KScreen.Output.Capability.RgbRange
0191 
0192         QQC2.ComboBox {
0193             Layout.minimumWidth: Kirigami.Units.gridUnit * 11
0194             model: [
0195                 { label: i18n("Automatic"), value: KScreen.Output.RgbRange.Automatic },
0196                 { label: i18n("Full"), value: KScreen.Output.RgbRange.Full },
0197                 { label: i18n("Limited"), value: KScreen.Output.RgbRange.Limited }
0198             ]
0199             textRole: "label"
0200             valueRole: "value"
0201 
0202             onActivated: element.rgbRange = currentValue;
0203             Component.onCompleted: currentIndex = indexOfValue(element.rgbRange);
0204         }
0205 
0206         KCM.ContextualHelpButton {
0207             toolTipText: xi18nc("@info", `Determines whether or not the range of possible color values needs to be limited for the display.
0208                                           This should only be changed if the colors on the screen look washed out.`)
0209         }
0210     }
0211 
0212     RowLayout {
0213         Kirigami.FormData.label: i18nc("@label:textbox", "Color Profile:")
0214         visible: element.capabilities & KScreen.Output.Capability.IccProfile
0215         spacing: Kirigami.Units.smallSpacing
0216 
0217         Kirigami.ActionTextField {
0218             id: iccProfileField
0219             onTextChanged: element.iccProfilePath = text
0220             onTextEdited: element.iccProfilePath = text
0221             placeholderText: i18nc("@info:placeholder", "Enter ICC profile path…")
0222             enabled: !element.hdr
0223 
0224             rightActions: Kirigami.Action {
0225                 icon.name: "edit-clear-symbolic"
0226                 visible: iccProfileField.text !== ""
0227                 onTriggered: {
0228                     iccProfileField.text = ""
0229                 }
0230             }
0231 
0232             Component.onCompleted: text = element.iccProfilePath;
0233         }
0234 
0235         QQC2.Button {
0236             icon.name: "document-open-symbolic"
0237             text: i18nc("@action:button", "Select ICC profile…")
0238             display: QQC2.AbstractButton.IconOnly
0239             onClicked: fileDialogComponent.incubateObject(root);
0240             enabled: !element.hdr
0241 
0242             QQC2.ToolTip.visible: hovered
0243             QQC2.ToolTip.text: text
0244             QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0245 
0246             Accessible.role: Accessible.Button
0247             Accessible.name: text
0248             Accessible.description: i18n("Opens a file picker for the ICC profile")
0249             Accessible.onPressAction: onClicked();
0250         }
0251 
0252         Component {
0253             id: fileDialogComponent
0254 
0255             FileDialog {
0256                 id: fileDialog
0257                 title: i18nc("@title:window", "Select ICC Profile")
0258                 currentFolder: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
0259                 nameFilters: ["ICC profiles (*.icc *.icm)"]
0260 
0261                 onAccepted: {
0262                     iccProfileField.text = urlToProfilePath(selectedFile);
0263                     destroy();
0264                 }
0265                 onRejected: destroy();
0266                 Component.onCompleted: open();
0267 
0268                 function urlToProfilePath(qmlUrl) {
0269                     const url = new URL(qmlUrl);
0270                     let path = decodeURIComponent(url.pathname);
0271                     // Remove the leading slash from the url
0272                     if (url.protocol === "file:" && path.charAt(1) === ':') {
0273                         path = path.substring(1);
0274                     }
0275                     return path;
0276                 }
0277             }
0278         }
0279 
0280         KCM.ContextualHelpButton {
0281             visible: element.hdr
0282             toolTipText: i18nc("@info:tooltip", "ICC profiles aren't compatible with HDR yet")
0283         }
0284     }
0285 
0286     RowLayout {
0287         Kirigami.FormData.label: i18nc("@label", "High Dynamic Range:")
0288         visible: (element.capabilities & KScreen.Output.Capability.HighDynamicRange) && (element.capabilities & KScreen.Output.Capability.WideColorGamut)
0289         spacing: Kirigami.Units.smallSpacing
0290 
0291         QQC2.CheckBox {
0292             text: i18nc("@option:check", "Enable HDR")
0293             checked: element.hdr
0294             onToggled: element.hdr = checked
0295         }
0296 
0297         KCM.ContextualHelpButton {
0298             toolTipText: i18nc("@info:tooltip", "HDR allows compatible applications to show brighter and more vivid colors. Note that this feature is still experimental")
0299         }
0300     }
0301 
0302     RowLayout {
0303         Layout.fillWidth: true
0304         // Set the same limit as the device ComboBox
0305         Layout.maximumWidth: Kirigami.Units.gridUnit * 16
0306         spacing: Kirigami.Units.smallSpacing
0307 
0308         visible: element.hdr
0309         Kirigami.FormData.label: i18nc("@label", "SDR Brightness:")
0310 
0311         QQC2.Slider {
0312             Layout.fillWidth: true
0313             from: 50
0314             to: element.peakBrightness == 0 ? 500 : element.peakBrightness
0315             stepSize: 50
0316             live: true
0317             value: element.sdrBrightness
0318             onMoved: element.sdrBrightness = value
0319         }
0320         QQC2.SpinBox {
0321             from : 50
0322             to : element.peakBrightness == 0 ? 500 : element.peakBrightness
0323             stepSize: 10
0324             value: element.sdrBrightness
0325             onValueModified: element.sdrBrightness = value
0326         }
0327         KCM.ContextualHelpButton {
0328             toolTipText: i18nc("@info:tooltip", "Sets the brightness of non-HDR content on the screen, in nits")
0329         }
0330     }
0331 
0332     RowLayout {
0333         Layout.fillWidth: true
0334         // Set the same limit as the device ComboBox
0335         Layout.maximumWidth: Kirigami.Units.gridUnit * 16
0336         spacing: Kirigami.Units.smallSpacing
0337 
0338         visible: element.hdr
0339         Kirigami.FormData.label: i18nc("@label", "SDR Color Intensity:")
0340 
0341         QQC2.Slider {
0342             Layout.fillWidth: true
0343             from: 0
0344             to: 100
0345             stepSize: 10
0346             live: true
0347             value: element.sdrGamutWideness * 100
0348             onMoved: element.sdrGamutWideness = value / 100.0
0349         }
0350         QQC2.SpinBox {
0351             // Because QQC2 SpinBox doesn't natively support decimal step
0352             // sizes: https://bugreports.qt.io/browse/QTBUG-67349
0353             readonly property real factor: 20.0
0354             readonly property real realValue: value / factor
0355 
0356             from : 0
0357             to : 1.0 * factor
0358             stepSize: 1
0359             value: element.sdrGamutWideness * factor
0360             validator: DoubleValidator {
0361                 bottom: Math.min(spinbox.from, spinbox.to) * spinbox.factor
0362                 top:  Math.max(spinbox.from, spinbox.to) * spinbox.factor
0363             }
0364             textFromValue: (value, locale) =>
0365             i18nc("Color intensity factor expressed in percentage form", "%1%",
0366                   parseFloat(value * 1.0 / factor * 100.0))
0367             valueFromText: (text, locale) =>
0368             Number.fromLocaleString(locale, text.replace("%", "")) * factor / 100.0
0369 
0370             onValueModified: element.sdrGamutWideness = realValue
0371         }
0372         KCM.ContextualHelpButton {
0373             toolTipText: i18nc("@info:tooltip", "Increases the intensity of non-HDR content on the screen")
0374         }
0375     }
0376 
0377     QQC2.ComboBox {
0378         Kirigami.FormData.label: i18n("Replica of:")
0379         Layout.minimumWidth: Kirigami.Units.gridUnit * 11
0380         model: element.replicationSourceModel
0381         visible: kcm.outputReplicationSupported && kcm.multipleScreensAvailable
0382 
0383         onModelChanged: enabled = (count > 1);
0384         onCountChanged: enabled = (count > 1);
0385 
0386         Component.onCompleted: currentIndex = element.replicationSourceIndex;
0387         onActivated: element.replicationSourceIndex = currentIndex;
0388     }
0389 }