Warning, /plasma/kwin/src/kcms/rules/ui/RulesEditor.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2020 Ismael Asensio <isma.af@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 import QtQuick 0008 import QtQuick.Layouts 0009 import QtQuick.Controls as QQC2 0010 import org.kde.kirigami 2.19 as Kirigami 0011 import org.kde.kcmutils as KCM 0012 import org.kde.kitemmodels 0013 import org.kde.kcms.kwinrules 0014 0015 0016 KCM.ScrollViewKCM { 0017 id: rulesEditor 0018 0019 title: kcm.rulesModel.description 0020 0021 view: ListView { 0022 id: rulesView 0023 clip: true 0024 0025 model: enabledRulesModel 0026 delegate: RuleItemDelegate { 0027 ListView.onAdd: { 0028 // Try to position the new added item into the visible view 0029 // FIXME: It only works when moving towards the end of the list 0030 ListView.view.currentIndex = index 0031 } 0032 } 0033 section { 0034 property: "section" 0035 delegate: Kirigami.ListSectionHeader { 0036 width: ListView.view.width 0037 label: section 0038 } 0039 } 0040 0041 highlightRangeMode: ListView.ApplyRange 0042 0043 add: Transition { 0044 NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: Kirigami.Units.longDuration * 3 } 0045 } 0046 removeDisplaced: Transition { 0047 NumberAnimation { property: "y"; duration: Kirigami.Units.longDuration } 0048 } 0049 0050 // We need to center on the free space below contentItem, not the full 0051 // ListView. This invisible item helps make that positioning work no 0052 // matter the window height 0053 Item { 0054 anchors { 0055 left: parent.left 0056 right: parent.right 0057 top: parent.contentItem.bottom 0058 bottom: parent.bottom 0059 } 0060 visible: rulesView.count <= 4 0061 0062 Kirigami.PlaceholderMessage { 0063 id: hintArea 0064 anchors.centerIn: parent 0065 width: parent.width - (Kirigami.Units.largeSpacing * 4) 0066 text: i18n("No window properties changed") 0067 explanation: xi18nc("@info", "Click the <interface>Add Property...</interface> button below to add some window properties that will be affected by the rule") 0068 } 0069 } 0070 } 0071 0072 header: ColumnLayout { 0073 visible: warningList.count > 0 0074 Repeater { 0075 id: warningList 0076 model: kcm.rulesModel.warningMessages 0077 0078 delegate: Kirigami.InlineMessage { 0079 text: modelData 0080 visible: true 0081 Layout.fillWidth: true 0082 } 0083 } 0084 } 0085 0086 footer: RowLayout { 0087 QQC2.Button { 0088 text: checked ? i18n("Close") : i18n("Add Property...") 0089 icon.name: checked ? "dialog-close" : "list-add" 0090 checkable: true 0091 checked: propertySheet.visible 0092 onToggled: { 0093 propertySheet.visible = checked; 0094 } 0095 } 0096 Item { 0097 Layout.fillWidth: true 0098 } 0099 QQC2.Button { 0100 id: detectButton 0101 text: i18n("Detect Window Properties") 0102 icon.name: "edit-find" 0103 enabled: !propertySheet.visible && !errorDialog.visible 0104 onClicked: { 0105 overlayModel.onlySuggestions = true; 0106 kcm.rulesModel.detectWindowProperties(Math.max(delaySpin.value * 1000, 0107 Kirigami.Units.shortDuration)); 0108 } 0109 } 0110 QQC2.SpinBox { 0111 id: delaySpin 0112 enabled: detectButton.enabled 0113 Layout.preferredWidth: Math.max(metricsInstant.advanceWidth, metricsAfter.advanceWidth) + Kirigami.Units.gridUnit * 4 0114 from: 0 0115 to: 30 0116 textFromValue: (value, locale) => { 0117 return (value == 0) ? i18n("Instantly") 0118 : i18np("After %1 second", "After %1 seconds", value) 0119 } 0120 0121 TextMetrics { 0122 id: metricsInstant 0123 font: delaySpin.font 0124 text: i18n("Instantly") 0125 } 0126 TextMetrics { 0127 id: metricsAfter 0128 font: delaySpin.font 0129 text: i18np("After %1 second", "After %1 seconds", 99) 0130 } 0131 } 0132 0133 } 0134 0135 Connections { 0136 target: kcm.rulesModel 0137 function onShowSuggestions() { 0138 if (errorDialog.visible) { 0139 return; 0140 } 0141 overlayModel.onlySuggestions = true; 0142 propertySheet.visible = true; 0143 } 0144 function onShowErrorMessage(title, message) { 0145 errorDialog.title = title 0146 errorDialog.message = message 0147 errorDialog.open() 0148 } 0149 } 0150 0151 Kirigami.Dialog { 0152 id: errorDialog 0153 0154 property alias message: errorLabel.text 0155 0156 preferredWidth: rulesEditor.width - Kirigami.Units.gridUnit * 6 0157 maximumWidth: Kirigami.Units.gridUnit * 35 0158 footer: null // Just use the close button on top 0159 0160 ColumnLayout { 0161 // Wrap it in a Layout so we can apply margins to the text while keeping implicit sizes 0162 Kirigami.Heading { 0163 id: errorLabel 0164 level: 4 0165 wrapMode: Text.WordWrap 0166 0167 Layout.fillWidth: true 0168 Layout.margins: Kirigami.Units.largeSpacing 0169 } 0170 } 0171 } 0172 0173 Kirigami.OverlaySheet { 0174 id: propertySheet 0175 0176 title: i18n("Add property to the rule") 0177 0178 footer: Kirigami.SearchField { 0179 id: searchField 0180 horizontalAlignment: Text.AlignLeft 0181 } 0182 0183 ListView { 0184 id: overlayView 0185 model: overlayModel 0186 Layout.preferredWidth: Kirigami.Units.gridUnit * 28 0187 clip: true 0188 0189 section { 0190 property: "section" 0191 delegate: Kirigami.ListSectionHeader { 0192 label: section 0193 height: implicitHeight 0194 } 0195 } 0196 0197 delegate: QQC2.ItemDelegate { 0198 id: propertyDelegate 0199 highlighted: false 0200 width: ListView.view.width 0201 0202 contentItem: RowLayout { 0203 Kirigami.Icon { 0204 source: model.icon 0205 Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium 0206 Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium 0207 Layout.alignment: Qt.AlignVCenter 0208 } 0209 QQC2.Label { 0210 id: itemNameLabel 0211 text: model.name 0212 horizontalAlignment: Qt.AlignLeft 0213 Layout.preferredWidth: implicitWidth 0214 Layout.fillWidth: true 0215 Layout.alignment: Qt.AlignVCenter 0216 } 0217 QQC2.Label { 0218 id: suggestedLabel 0219 text: formatValue(model.suggested, model.type, model.options) 0220 horizontalAlignment: Text.AlignRight 0221 elide: Text.ElideRight 0222 opacity: 0.7 0223 Layout.maximumWidth: propertyDelegate.width - itemNameLabel.implicitWidth - Kirigami.Units.gridUnit * 6 0224 Layout.alignment: Qt.AlignVCenter 0225 QQC2.ToolTip { 0226 text: suggestedLabel.text 0227 visible: hovered && suggestedLabel.truncated 0228 } 0229 } 0230 0231 KCM.ContextualHelpButton { 0232 Layout.rightMargin: Kirigami.Units.largeSpacing 0233 visible: model.description.length > 0 0234 toolTipText: model.description 0235 } 0236 0237 QQC2.ToolButton { 0238 icon.name: (model.enabled) ? "dialog-ok-apply" : "list-add" 0239 onClicked: addProperty(); 0240 Layout.preferredWidth: implicitWidth 0241 Layout.leftMargin: -Kirigami.Units.smallSpacing 0242 Layout.rightMargin: -Kirigami.Units.smallSpacing 0243 Layout.alignment: Qt.AlignVCenter 0244 } 0245 } 0246 0247 onClicked: { 0248 addProperty(); 0249 propertySheet.close(); 0250 } 0251 0252 function addProperty() { 0253 model.enabled = true; 0254 if (model.suggested != null) { 0255 model.value = model.suggested; 0256 model.suggested = null; 0257 } 0258 } 0259 } 0260 } 0261 0262 onVisibleChanged: { 0263 searchField.text = ""; 0264 if (visible) { 0265 searchField.forceActiveFocus(); 0266 } else { 0267 overlayModel.onlySuggestions = false; 0268 } 0269 } 0270 } 0271 0272 function formatValue(value, type, options) { 0273 if (value == null) { 0274 return ""; 0275 } 0276 switch (type) { 0277 case RuleItem.Boolean: 0278 return value ? i18n("Yes") : i18n("No"); 0279 case RuleItem.Percentage: 0280 return i18n("%1 %", value); 0281 case RuleItem.Point: 0282 return i18nc("Coordinates (x, y)", "(%1, %2)", value.x, value.y); 0283 case RuleItem.Size: 0284 return i18nc("Size (width, height)", "(%1, %2)", value.width, value.height); 0285 case RuleItem.Option: 0286 return options.textOfValue(value); 0287 case RuleItem.NetTypes: 0288 var selectedValue = value.toString(2).length - 1; 0289 return options.textOfValue(selectedValue); 0290 case RuleItem.OptionList: 0291 return Array.from(value, item => options.textOfValue(item) ).join(", "); 0292 } 0293 return value; 0294 } 0295 0296 KSortFilterProxyModel { 0297 id: enabledRulesModel 0298 sourceModel: kcm.rulesModel 0299 filterRowCallback: (source_row, source_parent) => { 0300 var index = sourceModel.index(source_row, 0, source_parent); 0301 return sourceModel.data(index, RulesModel.EnabledRole); 0302 } 0303 } 0304 0305 KSortFilterProxyModel { 0306 id: overlayModel 0307 sourceModel: kcm.rulesModel 0308 0309 property bool onlySuggestions: false 0310 onOnlySuggestionsChanged: { 0311 invalidateFilter(); 0312 } 0313 0314 filterString: searchField.text.trim().toLowerCase() 0315 filterRowCallback: (source_row, source_parent) => { 0316 var index = sourceModel.index(source_row, 0, source_parent); 0317 0318 var hasSuggestion = sourceModel.data(index, RulesModel.SuggestedValueRole) != null; 0319 var isOptional = sourceModel.data(index, RulesModel.SelectableRole); 0320 var isEnabled = sourceModel.data(index, RulesModel.EnabledRole); 0321 0322 var showItem = hasSuggestion || (!onlySuggestions && isOptional && !isEnabled); 0323 0324 if (!showItem) { 0325 return false; 0326 } 0327 if (filterString.length > 0) { 0328 return sourceModel.data(index, RulesModel.NameRole).toLowerCase().includes(filterString) 0329 } 0330 return true; 0331 } 0332 } 0333 0334 }