Changeset View
Changeset View
Standalone View
Standalone View
lib/qml/ResultDelegate.qml
Show All 17 Lines | |||||
18 | * | 18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | 20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
21 | * | 21 | * | ||
22 | */ | 22 | */ | ||
23 | 23 | | |||
24 | import QtQuick 2.1 | 24 | import QtQuick 2.1 | ||
25 | import QtQuick.Layouts 1.1 | 25 | import QtQuick.Layouts 1.1 | ||
26 | import QtQuick.Controls 2.1 | ||||
26 | 27 | | |||
28 | import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons | ||||
27 | import org.kde.plasma.core 2.0 as PlasmaCore | 29 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
28 | import org.kde.plasma.components 2.0 as PlasmaComponents | 30 | import org.kde.plasma.components 3.0 as PlasmaComponents | ||
29 | 31 | | |||
30 | MouseArea { | 32 | PlasmaComponents.ItemDelegate | ||
33 | { | ||||
31 | id: resultDelegate | 34 | id: resultDelegate | ||
32 | 35 | | |||
33 | property variant theModel: model | 36 | property variant theModel: model | ||
37 | property alias typeText: typeLabel.text | ||||
34 | 38 | | |||
35 | readonly property bool isCurrent: ListView.isCurrentItem // cannot properly Connect {} to this | 39 | readonly property bool isCurrent: ListView.isCurrentItem // cannot properly Connect {} to this | ||
36 | readonly property bool sectionHasChanged: typeof reversed !== "undefined" && ( | 40 | readonly property bool sectionHasChanged: typeof reversed !== "undefined" && ( | ||
37 | (reversed && ListView.section != ListView.nextSection) | 41 | (reversed && ListView.section != ListView.nextSection) | ||
38 | || (!reversed && ListView.section != ListView.previousSection) | 42 | || (!reversed && ListView.section != ListView.previousSection) | ||
39 | ) | 43 | ) | ||
40 | 44 | | |||
41 | property int activeAction: -1 | 45 | property int activeAction: -1 | ||
42 | 46 | | |||
43 | property string typeText: sectionHasChanged ? ListView.section : "" | | |||
44 | property var additionalActions: typeof actions !== "undefined" ? actions : [] | 47 | property var additionalActions: typeof actions !== "undefined" ? actions : [] | ||
45 | property int categoryWidth: units.gridUnit * 10 | 48 | property int categoryWidth: units.gridUnit * 10 | ||
46 | 49 | | |||
47 | Accessible.role: Accessible.ListItem | 50 | Accessible.role: Accessible.ListItem | ||
48 | Accessible.name: displayLabel.text | 51 | Accessible.name: displayLabel.text | ||
49 | Accessible.description: { | 52 | Accessible.description: { | ||
50 | var section = ListView.section; | 53 | var section = ListView.section; | ||
51 | if (!section) { | 54 | if (!section) { | ||
52 | return ""; | 55 | return ""; | ||
53 | } | 56 | } | ||
54 | var subtext = subtextLabel.text; | 57 | var subtext = subtextLabel.text; | ||
55 | if (subtext.length > 0) { | 58 | if (subtext.length > 0) { | ||
56 | return i18nd("milou", "%1, in category %2", subtext, section); | 59 | return i18nd("milou", "%1, in category %2", subtext, section); | ||
57 | } else { | 60 | } else { | ||
58 | return i18nd("milou", "in category %1", section); | 61 | return i18nd("milou", "in category %1", section); | ||
59 | } | 62 | } | ||
60 | } | 63 | } | ||
61 | 64 | | |||
62 | property bool __pressed: false | | |||
63 | property int __pressX: -1 | | |||
64 | property int __pressY: -1 | | |||
65 | | ||||
66 | onIsCurrentChanged: { | 65 | onIsCurrentChanged: { | ||
67 | if (!isCurrent) { | 66 | if (!isCurrent) { | ||
68 | activeAction = -1 | 67 | activeAction = -1 | ||
69 | } | 68 | } | ||
70 | } | 69 | } | ||
71 | 70 | | |||
72 | function activateNextAction() { | 71 | function activateNextAction() { | ||
73 | if (activeAction === actionsRepeater.count - 1) { // last action, do nothing | 72 | if (activeAction === actionsRepeater.count - 1) { // last action, do nothing | ||
Show All 10 Lines | 79 | function activatePreviousAction() { | |||
84 | --activeAction | 83 | --activeAction | ||
85 | return true | 84 | return true | ||
86 | } | 85 | } | ||
87 | 86 | | |||
88 | function activateLastAction() { | 87 | function activateLastAction() { | ||
89 | activeAction = actionsRepeater.count - 1 | 88 | activeAction = actionsRepeater.count - 1 | ||
90 | } | 89 | } | ||
91 | 90 | | |||
92 | width: listItem.implicitWidth | 91 | onClicked: { | ||
93 | height: listItem.implicitHeight | | |||
94 | | ||||
95 | acceptedButtons: Qt.LeftButton | | |||
96 | hoverEnabled: true | | |||
97 | onPressed: { | | |||
98 | __pressed = true; | | |||
99 | __pressX = mouse.x; | | |||
100 | __pressY = mouse.y; | | |||
101 | } | | |||
102 | | ||||
103 | onReleased: { | | |||
104 | if (__pressed) { | | |||
105 | listView.currentIndex = model.index | 92 | listView.currentIndex = model.index | ||
106 | listView.runCurrentIndex() | 93 | listView.runCurrentIndex() | ||
107 | } | 94 | } | ||
108 | 95 | | |||
109 | __pressed = false; | 96 | KQuickControlsAddons.MouseEventListener { | ||
110 | __pressX = -1; | 97 | anchors.fill: parent | ||
111 | __pressY = -1; | 98 | hoverEnabled: true | ||
99 | | ||||
100 | property int pressX: -1 | ||||
101 | property int pressY: -1 | ||||
102 | | ||||
103 | | ||||
104 | onPressed: { | ||||
105 | pressX = mouse.x | ||||
106 | pressY = mouse.y | ||||
112 | } | 107 | } | ||
113 | 108 | | |||
114 | onPositionChanged: { | 109 | onPositionChanged: { | ||
115 | if (__pressX != -1 && typeof dragHelper !== "undefined" && dragHelper.isDrag(__pressX, __pressY, mouse.x, mouse.y)) { | 110 | if (pressed && pressX >= 0 && typeof dragHelper !== "undefined" && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { | ||
116 | var mimeData = ListView.view.model.getMimeData(index); | 111 | var mimeData = resultDelegate.ListView.view.model.getMimeData(index); | ||
117 | if (mimeData) { | 112 | if (mimeData) { | ||
118 | dragHelper.startDrag(root, mimeData, model.decoration); | 113 | dragHelper.startDrag(root, mimeData, model.decoration); | ||
119 | __pressed = false; | 114 | pressX = -1 | ||
120 | __pressX = -1; | 115 | pressY = -1 | ||
121 | __pressY = -1; | | |||
122 | } | 116 | } | ||
123 | } | 117 | } | ||
124 | 118 | | |||
125 | if (!listView.moved && listView.mouseMovedGlobally()) { | 119 | if (!listView.moved && listView.mouseMovedGlobally()) { | ||
126 | listView.moved = true | 120 | listView.moved = true | ||
127 | listView.currentIndex = index | 121 | listView.currentIndex = index | ||
128 | } | 122 | } | ||
129 | } | 123 | } | ||
124 | } | ||||
130 | 125 | | |||
131 | onContainsMouseChanged: { | 126 | onHoveredChanged: { | ||
132 | if (!containsMouse) { | | |||
133 | __pressed = false; | | |||
134 | __pressX = -1; | | |||
135 | __pressY = -1; | | |||
136 | } else { | | |||
137 | if (listView.moved) { | 127 | if (listView.moved) { | ||
138 | listView.currentIndex = index | 128 | listView.currentIndex = index | ||
139 | } else if (listView.mouseMovedGlobally()) { | 129 | } else if (listView.mouseMovedGlobally()) { | ||
140 | listView.moved = true | 130 | listView.moved = true | ||
141 | listView.currentIndex = index | 131 | listView.currentIndex = index | ||
142 | } | 132 | } | ||
143 | } | 133 | } | ||
144 | } | | |||
145 | | ||||
146 | PlasmaComponents.Label { | | |||
147 | id: typeText | | |||
148 | text: resultDelegate.typeText | | |||
149 | color: theme.textColor | | |||
150 | opacity: 0.5 | | |||
151 | | ||||
152 | horizontalAlignment: Text.AlignRight | | |||
153 | verticalAlignment: Text.AlignVCenter | | |||
154 | elide: Text.ElideRight | | |||
155 | textFormat: Text.PlainText | | |||
156 | | ||||
157 | width: resultDelegate.categoryWidth - units.largeSpacing | | |||
158 | anchors { | | |||
159 | left: parent.left | | |||
160 | verticalCenter: listItem.verticalCenter | | |||
161 | } | | |||
162 | } | | |||
163 | | ||||
164 | PlasmaComponents.ListItem { | | |||
165 | id: listItem | | |||
166 | 134 | | |||
167 | readonly property int indexModifier: reversed ? 0 : 1 | 135 | readonly property int indexModifier: reversed ? 0 : 1 | ||
136 | width: parent.width | ||||
168 | 137 | | |||
169 | // fake pressed look | 138 | // fake pressed look | ||
broulik: Move that comment to where it belongs | |||||
139 | highlighted: ListView.isCurrentItem | ||||
170 | checked: resultDelegate.pressed | 140 | checked: resultDelegate.pressed | ||
171 | separatorVisible: resultDelegate.sectionHasChanged | | |||
172 | && !resultDelegate.isCurrent | | |||
173 | && (index === 0 || resultDelegate.ListView.view.currentIndex !== (index - indexModifier)) | | |||
174 | 141 | | |||
175 | Item { | 142 | Rectangle { //Separator | ||
broulik: This should be a `Svg` from the Plasma Theme | |||||
apol: There's no separator svg. you suggest creating a new one? | |||||
broulik: Use whatever the `PlasmaComponents.ListItem` uses | |||||
176 | id: labelWrapper | | |||
177 | anchors { | 143 | anchors { | ||
178 | left: parent.left | 144 | left: parent.left | ||
179 | right: parent.right | 145 | right: parent.right | ||
180 | leftMargin: resultDelegate.categoryWidth | | |||
181 | } | 146 | } | ||
182 | height: Math.max(typePixmap.height, displayLabel.height, subtextLabel.height) | 147 | height: Math.floor(units.devicePixelRatio) | ||
148 | Layout.preferredWidth: Math.floor(units.devicePixelRatio) | ||||
149 | Layout.preferredHeight: Math.floor(units.devicePixelRatio) | ||||
150 | color: Qt.tint(theme.textColor, Qt.rgba(theme.backgroundColor.r, theme.backgroundColor.g, theme.backgroundColor.b, 0.7)) | ||||
151 | | ||||
152 | visible: resultDelegate.sectionHasChanged | ||||
153 | && !resultDelegate.isCurrent | ||||
154 | && (index === 0 || resultDelegate.ListView.view.currentIndex !== (index - indexModifier)) | ||||
155 | } | ||||
183 | 156 | | |||
184 | RowLayout { | 157 | RowLayout { | ||
158 | id: contents | ||||
185 | anchors { | 159 | anchors { | ||
186 | left: parent.left | 160 | fill: parent | ||
187 | right: actionsRow.left | | |||
188 | rightMargin: units.smallSpacing | 161 | rightMargin: units.smallSpacing | ||
189 | } | 162 | } | ||
190 | 163 | | |||
164 | PlasmaComponents.Label { | ||||
165 | id: typeLabel | ||||
166 | text: sectionHasChanged ? resultDelegate.ListView.section : "" | ||||
167 | color: theme.textColor | ||||
168 | opacity: 0.7 | ||||
broulik: Where does that random `0.7` come from? | |||||
169 | | ||||
170 | horizontalAlignment: Text.AlignRight | ||||
171 | verticalAlignment: Text.AlignVCenter | ||||
172 | elide: Text.ElideRight | ||||
173 | textFormat: Text.PlainText | ||||
174 | | ||||
175 | Layout.minimumWidth: resultDelegate.categoryWidth - units.largeSpacing | ||||
176 | Layout.rightMargin: units.largeSpacing | ||||
177 | } | ||||
178 | | ||||
191 | PlasmaCore.IconItem { | 179 | PlasmaCore.IconItem { | ||
192 | id: typePixmap | 180 | id: typePixmap | ||
193 | Layout.preferredWidth: units.iconSizes.small | 181 | Layout.preferredWidth: units.iconSizes.small | ||
194 | Layout.preferredHeight: units.iconSizes.small | 182 | Layout.preferredHeight: units.iconSizes.small | ||
195 | Layout.fillHeight: true | 183 | Layout.fillHeight: true | ||
196 | source: model.decoration | 184 | source: model.decoration | ||
197 | usesPlasmaTheme: false | 185 | usesPlasmaTheme: false | ||
198 | animated: false | 186 | animated: false | ||
199 | } | 187 | } | ||
200 | 188 | | |||
201 | PlasmaComponents.Label { | 189 | PlasmaComponents.Label { | ||
202 | id: displayLabel | 190 | id: displayLabel | ||
203 | text: String(typeof modelData !== "undefined" ? modelData : model.display) | 191 | text: String(typeof modelData !== "undefined" ? modelData : model.display) | ||
204 | 192 | | |||
205 | height: undefined | 193 | height: undefined | ||
206 | 194 | | |||
207 | elide: Text.ElideMiddle | 195 | elide: Text.ElideMiddle | ||
208 | wrapMode: Text.NoWrap | 196 | wrapMode: Text.NoWrap | ||
209 | maximumLineCount: 1 | 197 | maximumLineCount: 1 | ||
210 | verticalAlignment: Text.AlignVCenter | 198 | verticalAlignment: Text.AlignVCenter | ||
211 | textFormat: Text.PlainText | 199 | textFormat: Text.PlainText | ||
212 | | ||||
213 | Layout.maximumWidth: labelWrapper.width - typePixmap.width - actionsRow.width | | |||
214 | } | 200 | } | ||
215 | 201 | | |||
216 | PlasmaComponents.Label { | 202 | PlasmaComponents.Label { | ||
217 | id: subtextLabel | 203 | id: subtextLabel | ||
218 | text: model.isDuplicate > 1 || resultDelegate.isCurrent ? String(model.subtext || "") : "" | 204 | text: model.isDuplicate > 1 || resultDelegate.isCurrent ? String(model.subtext || "") : "" | ||
219 | 205 | | |||
220 | color: theme.textColor | 206 | color: theme.textColor | ||
221 | // HACK If displayLabel is too long it will shift this label outside boundaries | 207 | // HACK If displayLabel is too long it will shift this label outside boundaries | ||
222 | // but still render the text leading to it overlapping the action buttons looking horrible | 208 | // but still render the text leading to it overlapping the action buttons looking horrible | ||
223 | opacity: width > 0 ? 0.3 : 0 | 209 | opacity: width > 0 ? 0.3 : 0 | ||
224 | 210 | | |||
225 | height: undefined | 211 | height: undefined | ||
226 | 212 | | |||
227 | elide: Text.ElideMiddle | 213 | elide: Text.ElideMiddle | ||
228 | wrapMode: Text.NoWrap | 214 | wrapMode: Text.NoWrap | ||
229 | maximumLineCount: 1 | 215 | maximumLineCount: 1 | ||
230 | verticalAlignment: Text.AlignVCenter | 216 | verticalAlignment: Text.AlignVCenter | ||
231 | textFormat: Text.PlainText | 217 | textFormat: Text.PlainText | ||
232 | 218 | | |||
233 | Layout.fillWidth: true | 219 | Layout.fillWidth: true | ||
234 | } | 220 | } | ||
235 | } | | |||
236 | | ||||
237 | Row { | | |||
238 | id: actionsRow | | |||
239 | anchors.right: parent.right | | |||
240 | anchors.verticalCenter: parent.verticalCenter | | |||
241 | visible: resultDelegate.isCurrent | | |||
242 | 221 | | |||
243 | Repeater { | 222 | Repeater { | ||
244 | id: actionsRepeater | 223 | id: actionsRepeater | ||
245 | model: resultDelegate.additionalActions | 224 | model: resultDelegate.additionalActions | ||
246 | 225 | | |||
247 | PlasmaComponents.ToolButton { | 226 | PlasmaComponents.ToolButton { | ||
248 | width: height | 227 | visible: resultDelegate.isCurrent && modelData.visible !== false | ||
249 | height: listItem.height | 228 | Layout.fillHeight: true | ||
250 | visible: modelData.visible || true | 229 | Layout.preferredWidth: height | ||
251 | enabled: modelData.enabled || true | 230 | enabled: modelData.enabled !== false | ||
252 | tooltip: { | 231 | ToolTip.visible: hovered && ToolTip.text.length > 0 | ||
QQC2 tooltip stuff doesn't really work for KRunner because they're not separate windows :( Also, don't use ToolTip attached property as it cannot respect the theme's delay settings broulik: QQC2 tooltip stuff doesn't really work for KRunner because they're not separate windows… | |||||
Please don't change it. The alternative (using a ToolTip instance) is ridiculously heavy. davidedmundson: >Also, don't use ToolTip attached property as it cannot respect the theme's delay settings… | |||||
QQC2 tooltip stuff doesn't really work for KRunner because they're not separate windows :( it looks good to me. apol: `QQC2 tooltip stuff doesn't really work for KRunner because they're not separate windows :(` it… | |||||
232 | ToolTip.text: { | ||||
253 | var text = modelData.text || "" | 233 | var text = modelData.text || "" | ||
254 | if (index === 0) { // Shift+Return will invoke first action | 234 | if (index === 0) { // Shift+Return will invoke first action | ||
255 | text = i18ndc("milou", "placeholder is action e.g. run in terminal, in parenthesis is shortcut", "%1 (Shift+Return)", text) | 235 | text = i18ndc("milou", "placeholder is action e.g. run in terminal, in parenthesis is shortcut", "%1 (Shift+Return)", text) | ||
QQC2 tooltip doesn't seem to handle mnemonics, leading to text like "Run in &Terminal" broulik: QQC2 tooltip doesn't seem to handle mnemonics, leading to text like "Run in &Terminal" | |||||
Can't see that, see screenshot. apol: Can't see that, see screenshot.
Also if that's the case, then we should fix it like we did for… | |||||
256 | } | 236 | } | ||
257 | return text | 237 | return text | ||
258 | } | 238 | } | ||
259 | Accessible.role: Accessible.Button | 239 | Accessible.role: Accessible.Button | ||
260 | Accessible.name: modelData.text | 240 | Accessible.name: modelData.text | ||
261 | checkable: checked | 241 | checkable: checked | ||
262 | checked: resultDelegate.activeAction === index | 242 | checked: resultDelegate.activeAction === index | ||
263 | focus: resultDelegate.activeAction === index | 243 | focus: resultDelegate.activeAction === index | ||
264 | 244 | | |||
265 | PlasmaCore.IconItem { | 245 | PlasmaCore.IconItem { | ||
266 | anchors.centerIn: parent | 246 | anchors.centerIn: parent | ||
267 | width: units.iconSizes.small | 247 | width: units.iconSizes.small | ||
268 | height: units.iconSizes.small | 248 | height: units.iconSizes.small | ||
269 | // ToolButton cannot cope with QIcon | 249 | // ToolButton cannot cope with QIcon | ||
270 | source: modelData.icon || "" | 250 | source: modelData.icon || "" | ||
271 | active: parent.hovered || parent.checked | 251 | active: parent.hovered || parent.checked | ||
272 | } | 252 | } | ||
273 | 253 | | |||
274 | onClicked: resultDelegate.ListView.view.runAction(index) | 254 | onClicked: resultDelegate.ListView.view.runAction(index) | ||
275 | } | 255 | } | ||
276 | } | 256 | } | ||
277 | } | 257 | } | ||
278 | } | 258 | } | ||
279 | } | | |||
280 | } | |
Move that comment to where it belongs