draftomen 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- draftomen/__init__.py +17 -0
- draftomen/assets/draftomen.icns +0 -0
- draftomen/assets/draftomen.ico +0 -0
- draftomen/assets/draftomen_logo.png +0 -0
- draftomen/audit.py +606 -0
- draftomen/backtest.py +449 -0
- draftomen/benchmark.py +834 -0
- draftomen/carddb.py +1869 -0
- draftomen/cardimages.py +313 -0
- draftomen/cli.py +891 -0
- draftomen/config.py +133 -0
- draftomen/deckbuilder.py +2723 -0
- draftomen/events.py +772 -0
- draftomen/logfollow.py +451 -0
- draftomen/mock_session.py +745 -0
- draftomen/paths.py +120 -0
- draftomen/pickengine.py +1117 -0
- draftomen/pool.py +1259 -0
- draftomen/preferences.py +289 -0
- draftomen/qml/AboutDialog.qml +156 -0
- draftomen/qml/AppBar.qml +120 -0
- draftomen/qml/BacktestView.qml +316 -0
- draftomen/qml/BuildView.qml +1151 -0
- draftomen/qml/CardPreview.qml +434 -0
- draftomen/qml/DimensionalButton.qml +42 -0
- draftomen/qml/DimensionalComboBox.qml +182 -0
- draftomen/qml/DimensionalSurface.qml +118 -0
- draftomen/qml/DimensionalTabButton.qml +41 -0
- draftomen/qml/LiveDraftView.qml +468 -0
- draftomen/qml/Main.qml +175 -0
- draftomen/qml/NavigationRail.qml +226 -0
- draftomen/qml/PoolSummaryPanel.qml +322 -0
- draftomen/qml/PrivacyDialog.qml +96 -0
- draftomen/qml/RecentPickThumbnail.qml +83 -0
- draftomen/qml/RecentPicksGallery.qml +333 -0
- draftomen/qml/RecommendationRow.qml +404 -0
- draftomen/qml/SettingsSwitch.qml +141 -0
- draftomen/qml/SettingsView.qml +531 -0
- draftomen/qml/StateBanner.qml +189 -0
- draftomen/qml/StatusStrip.qml +84 -0
- draftomen/qml/Theme.qml +71 -0
- draftomen/qml/qmldir +23 -0
- draftomen/qt_adapter.py +884 -0
- draftomen/qt_gui.py +338 -0
- draftomen/qt_mock.py +63 -0
- draftomen/ranking.py +126 -0
- draftomen/replay.py +480 -0
- draftomen/session.py +3668 -0
- draftomen/setinfo.py +26 -0
- draftomen/seventeen.py +2766 -0
- draftomen/splash.py +617 -0
- draftomen/tui.py +4007 -0
- draftomen/watch.py +336 -0
- draftomen-0.3.0.dist-info/METADATA +156 -0
- draftomen-0.3.0.dist-info/RECORD +59 -0
- draftomen-0.3.0.dist-info/WHEEL +5 -0
- draftomen-0.3.0.dist-info/entry_points.txt +6 -0
- draftomen-0.3.0.dist-info/licenses/LICENSE +21 -0
- draftomen-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import QtQuick 2.15
|
|
2
|
+
import QtQuick.Controls 2.15
|
|
3
|
+
import QtQuick.Layouts 1.15
|
|
4
|
+
|
|
5
|
+
Rectangle {
|
|
6
|
+
id: root
|
|
7
|
+
|
|
8
|
+
property var recommendation: null
|
|
9
|
+
property bool loading: false
|
|
10
|
+
property var imageState: null
|
|
11
|
+
property bool detailedIntel: false
|
|
12
|
+
property bool constrainImageFrameToHeight: false
|
|
13
|
+
// Keep the existing detailed-preview sizing by default; Live Draft's
|
|
14
|
+
// wide preview can opt into a larger bounded image.
|
|
15
|
+
property real detailedImageMaximumWidth: 200
|
|
16
|
+
property real detailedImageWidthRatio: 0.52
|
|
17
|
+
|
|
18
|
+
readonly property bool imageCurrent: Boolean(
|
|
19
|
+
recommendation
|
|
20
|
+
&& imageState
|
|
21
|
+
&& imageState.grp_id === recommendation.card.grp_id
|
|
22
|
+
)
|
|
23
|
+
readonly property bool imageLoading: Boolean(
|
|
24
|
+
loading || imageCurrent && imageState.phase === "loading"
|
|
25
|
+
)
|
|
26
|
+
readonly property bool imageAvailable: Boolean(
|
|
27
|
+
imageCurrent
|
|
28
|
+
&& imageState.image_path
|
|
29
|
+
&& imageState.phase === "ready"
|
|
30
|
+
)
|
|
31
|
+
readonly property string colorsText: {
|
|
32
|
+
if (!recommendation)
|
|
33
|
+
return "—"
|
|
34
|
+
if (recommendation.card.colors.length === 0)
|
|
35
|
+
return "Colorless"
|
|
36
|
+
return recommendation.card.colors.join(" · ")
|
|
37
|
+
}
|
|
38
|
+
readonly property string manaValueText: recommendation
|
|
39
|
+
&& recommendation.card.mana_value !== null
|
|
40
|
+
&& recommendation.card.mana_value !== undefined
|
|
41
|
+
? Number(recommendation.card.mana_value).toFixed(0) : "—"
|
|
42
|
+
readonly property string alsaText: recommendation
|
|
43
|
+
&& recommendation.average_last_seen_at !== null
|
|
44
|
+
&& recommendation.average_last_seen_at !== undefined
|
|
45
|
+
? Number(recommendation.average_last_seen_at).toFixed(2) : "—"
|
|
46
|
+
readonly property string winRateText: recommendation
|
|
47
|
+
&& recommendation.win_rate !== null
|
|
48
|
+
&& recommendation.win_rate !== undefined
|
|
49
|
+
? (Number(recommendation.win_rate) * 100).toFixed(1) + "%" : "—"
|
|
50
|
+
|
|
51
|
+
color: Theme.surfaceLow
|
|
52
|
+
border.color: Theme.outline
|
|
53
|
+
border.width: 1
|
|
54
|
+
radius: Theme.radius
|
|
55
|
+
Accessible.role: Accessible.Pane
|
|
56
|
+
Accessible.name: {
|
|
57
|
+
if (recommendation) {
|
|
58
|
+
const prefix = root.detailedIntel
|
|
59
|
+
? "Focused card intel, "
|
|
60
|
+
: "Selected card, "
|
|
61
|
+
return prefix + recommendation.card.name
|
|
62
|
+
}
|
|
63
|
+
return root.detailedIntel ? "Focused card intel" : "Card details"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
readonly property real imageFrameAvailableHeight: Math.max(
|
|
67
|
+
0,
|
|
68
|
+
root.height - Theme.panelPadding * 2
|
|
69
|
+
- previewHeading.implicitHeight
|
|
70
|
+
- (previewDetails.visible
|
|
71
|
+
? previewDetailsColumn.implicitHeight : 0)
|
|
72
|
+
- previewLayout.rowSpacing * (previewDetails.visible ? 2 : 1)
|
|
73
|
+
)
|
|
74
|
+
readonly property real detailedImageFrameAvailableHeight: Math.max(
|
|
75
|
+
0,
|
|
76
|
+
root.height - Theme.panelPadding * 2
|
|
77
|
+
- previewHeading.implicitHeight
|
|
78
|
+
- previewLayout.rowSpacing * 2
|
|
79
|
+
)
|
|
80
|
+
readonly property real detailedContentWidth: root.width
|
|
81
|
+
- Theme.panelPadding * 2 - previewLayout.columnSpacing
|
|
82
|
+
readonly property real detailedImageFrameWidth: {
|
|
83
|
+
const heightBound = Math.max(
|
|
84
|
+
0,
|
|
85
|
+
root.detailedImageFrameAvailableHeight / 1.4
|
|
86
|
+
)
|
|
87
|
+
const maximumWidth = Math.max(
|
|
88
|
+
0, root.detailedImageMaximumWidth
|
|
89
|
+
)
|
|
90
|
+
const contentBound = Math.max(
|
|
91
|
+
0,
|
|
92
|
+
root.detailedContentWidth
|
|
93
|
+
* Math.max(0, root.detailedImageWidthRatio)
|
|
94
|
+
)
|
|
95
|
+
return Math.min(
|
|
96
|
+
maximumWidth,
|
|
97
|
+
heightBound,
|
|
98
|
+
Math.max(120, contentBound)
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
readonly property real imageFrameWidth: {
|
|
102
|
+
if (root.detailedIntel)
|
|
103
|
+
return root.detailedImageFrameWidth
|
|
104
|
+
if (root.constrainImageFrameToHeight) {
|
|
105
|
+
return Math.max(
|
|
106
|
+
0,
|
|
107
|
+
Math.min(
|
|
108
|
+
260,
|
|
109
|
+
root.width - Theme.panelPadding * 2,
|
|
110
|
+
root.imageFrameAvailableHeight / 1.4
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
return Math.max(0, Math.min(
|
|
115
|
+
240,
|
|
116
|
+
root.width - Theme.panelPadding * 2
|
|
117
|
+
))
|
|
118
|
+
}
|
|
119
|
+
readonly property real imageFrameHeight: root.imageFrameWidth * 1.4
|
|
120
|
+
readonly property real previewDetailsSideMargin: {
|
|
121
|
+
if (root.detailedIntel || !root.constrainImageFrameToHeight)
|
|
122
|
+
return 0
|
|
123
|
+
return Math.max(
|
|
124
|
+
0,
|
|
125
|
+
(root.width - Theme.panelPadding * 2 - root.imageFrameWidth) / 2
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
GridLayout {
|
|
130
|
+
id: previewLayout
|
|
131
|
+
anchors.fill: parent
|
|
132
|
+
anchors.margins: Theme.panelPadding
|
|
133
|
+
columns: root.detailedIntel ? 2 : 1
|
|
134
|
+
rowSpacing: root.detailedIntel ? 10
|
|
135
|
+
: root.constrainImageFrameToHeight ? 18 : 12
|
|
136
|
+
columnSpacing: 12
|
|
137
|
+
|
|
138
|
+
Label {
|
|
139
|
+
id: previewHeading
|
|
140
|
+
objectName: "cardPreviewHeading"
|
|
141
|
+
Layout.columnSpan: root.detailedIntel ? 2 : 1
|
|
142
|
+
text: root.detailedIntel ? "FOCUSED INTEL" : "SELECTED CARD"
|
|
143
|
+
color: root.detailedIntel ? Theme.primary : Theme.textMuted
|
|
144
|
+
font.pixelSize: Theme.textPixelSize(11)
|
|
145
|
+
font.bold: true
|
|
146
|
+
font.letterSpacing: 1.2
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Rectangle {
|
|
150
|
+
id: imageFrame
|
|
151
|
+
objectName: "cardPreviewImageFrame"
|
|
152
|
+
Layout.alignment: root.detailedIntel ? Qt.AlignTop : Qt.AlignHCenter
|
|
153
|
+
Layout.preferredWidth: root.imageFrameWidth
|
|
154
|
+
Layout.preferredHeight: root.imageFrameHeight
|
|
155
|
+
color: Theme.background
|
|
156
|
+
border.color: root.imageLoading ? Theme.warning : Theme.outline
|
|
157
|
+
border.width: 2
|
|
158
|
+
radius: 10
|
|
159
|
+
clip: true
|
|
160
|
+
|
|
161
|
+
Image {
|
|
162
|
+
id: cardImage
|
|
163
|
+
objectName: "cardPreviewImage"
|
|
164
|
+
anchors.fill: parent
|
|
165
|
+
source: root.imageAvailable ? root.imageState.image_path : ""
|
|
166
|
+
fillMode: Image.PreserveAspectFit
|
|
167
|
+
asynchronous: true
|
|
168
|
+
visible: root.imageAvailable && status === Image.Ready
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ColumnLayout {
|
|
172
|
+
id: previewFallback
|
|
173
|
+
objectName: "cardPreviewFallback"
|
|
174
|
+
anchors.centerIn: parent
|
|
175
|
+
width: parent.width - 32
|
|
176
|
+
spacing: 10
|
|
177
|
+
visible: !cardImage.visible
|
|
178
|
+
|
|
179
|
+
Rectangle {
|
|
180
|
+
Layout.alignment: Qt.AlignHCenter
|
|
181
|
+
Layout.preferredWidth: 48
|
|
182
|
+
Layout.preferredHeight: 48
|
|
183
|
+
radius: 24
|
|
184
|
+
color: root.recommendation
|
|
185
|
+
&& root.recommendation.card.colors.length > 0
|
|
186
|
+
? Theme.colorForMana(root.recommendation.card.colors[0])
|
|
187
|
+
: Theme.surfaceHigh
|
|
188
|
+
opacity: 0.85
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Label {
|
|
192
|
+
Layout.fillWidth: true
|
|
193
|
+
text: {
|
|
194
|
+
if (root.imageLoading)
|
|
195
|
+
return "Loading card image"
|
|
196
|
+
if (root.recommendation)
|
|
197
|
+
return root.recommendation.card.name
|
|
198
|
+
return "No card selected"
|
|
199
|
+
}
|
|
200
|
+
color: Theme.text
|
|
201
|
+
font.pixelSize: Theme.textPixelSize(16)
|
|
202
|
+
font.bold: true
|
|
203
|
+
horizontalAlignment: Text.AlignHCenter
|
|
204
|
+
wrapMode: Text.WordWrap
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
Label {
|
|
208
|
+
Layout.fillWidth: true
|
|
209
|
+
text: {
|
|
210
|
+
if (cardImage.status === Image.Error)
|
|
211
|
+
return "Card image could not be displayed."
|
|
212
|
+
if (root.imageCurrent)
|
|
213
|
+
return root.imageState.message
|
|
214
|
+
return "Card image unavailable"
|
|
215
|
+
}
|
|
216
|
+
color: Theme.textMuted
|
|
217
|
+
font.pixelSize: Theme.textPixelSize(11)
|
|
218
|
+
horizontalAlignment: Text.AlignHCenter
|
|
219
|
+
wrapMode: Text.WordWrap
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Flickable {
|
|
225
|
+
id: previewDetails
|
|
226
|
+
objectName: "cardPreviewDetails"
|
|
227
|
+
visible: root.recommendation !== null
|
|
228
|
+
Layout.alignment: root.detailedIntel ? Qt.AlignTop : Qt.AlignLeft
|
|
229
|
+
Layout.fillWidth: true
|
|
230
|
+
Layout.leftMargin: root.previewDetailsSideMargin
|
|
231
|
+
Layout.rightMargin: root.previewDetailsSideMargin
|
|
232
|
+
Layout.maximumWidth: root.detailedIntel
|
|
233
|
+
? Math.max(0, root.detailedContentWidth - root.imageFrameWidth)
|
|
234
|
+
: root.width
|
|
235
|
+
Layout.preferredHeight: root.detailedIntel
|
|
236
|
+
? root.imageFrameHeight
|
|
237
|
+
: previewDetailsColumn.implicitHeight
|
|
238
|
+
contentWidth: width
|
|
239
|
+
contentHeight: previewDetailsColumn.implicitHeight
|
|
240
|
+
clip: true
|
|
241
|
+
boundsBehavior: Flickable.StopAtBounds
|
|
242
|
+
activeFocusOnTab: root.detailedIntel
|
|
243
|
+
Accessible.role: Accessible.Pane
|
|
244
|
+
Accessible.name: "Scrollable card details"
|
|
245
|
+
Accessible.description: "Use Up and Down, Page Up, Page Down, Home, or End to scroll the card details."
|
|
246
|
+
|
|
247
|
+
function scrollTo(position) {
|
|
248
|
+
contentY = Math.max(0, Math.min(
|
|
249
|
+
position, Math.max(0, contentHeight - height)
|
|
250
|
+
))
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
Keys.onPressed: function(event) {
|
|
254
|
+
const pageStep = Math.max(1, height - 24)
|
|
255
|
+
const lineStep = 40
|
|
256
|
+
switch (event.key) {
|
|
257
|
+
case Qt.Key_Up:
|
|
258
|
+
previewDetails.scrollTo(contentY - lineStep)
|
|
259
|
+
break
|
|
260
|
+
case Qt.Key_Down:
|
|
261
|
+
previewDetails.scrollTo(contentY + lineStep)
|
|
262
|
+
break
|
|
263
|
+
case Qt.Key_PageUp:
|
|
264
|
+
previewDetails.scrollTo(contentY - pageStep)
|
|
265
|
+
break
|
|
266
|
+
case Qt.Key_PageDown:
|
|
267
|
+
previewDetails.scrollTo(contentY + pageStep)
|
|
268
|
+
break
|
|
269
|
+
case Qt.Key_Home:
|
|
270
|
+
previewDetails.scrollTo(0)
|
|
271
|
+
break
|
|
272
|
+
case Qt.Key_End:
|
|
273
|
+
previewDetails.scrollTo(contentHeight - height)
|
|
274
|
+
break
|
|
275
|
+
default:
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
event.accepted = true
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
ScrollBar.vertical: ScrollBar {
|
|
282
|
+
objectName: "cardPreviewScrollBar"
|
|
283
|
+
policy: ScrollBar.AsNeeded
|
|
284
|
+
width: 8
|
|
285
|
+
Accessible.name: "Card details vertical scrollbar"
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
ColumnLayout {
|
|
289
|
+
id: previewDetailsColumn
|
|
290
|
+
width: previewDetails.width
|
|
291
|
+
spacing: 6
|
|
292
|
+
|
|
293
|
+
Label {
|
|
294
|
+
objectName: "cardPreviewName"
|
|
295
|
+
Layout.fillWidth: true
|
|
296
|
+
text: root.recommendation ? root.recommendation.card.name : ""
|
|
297
|
+
color: Theme.text
|
|
298
|
+
font.pixelSize: Theme.textPixelSize(18)
|
|
299
|
+
font.bold: true
|
|
300
|
+
wrapMode: Text.WordWrap
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Label {
|
|
304
|
+
objectName: "cardPreviewFacts"
|
|
305
|
+
Layout.fillWidth: true
|
|
306
|
+
text: {
|
|
307
|
+
if (!root.recommendation)
|
|
308
|
+
return ""
|
|
309
|
+
if (root.detailedIntel)
|
|
310
|
+
return root.colorsText + " · "
|
|
311
|
+
+ root.recommendation.card.types.join(" · ")
|
|
312
|
+
+ " · MV " + root.manaValueText
|
|
313
|
+
return root.recommendation.card.types.join(" · ")
|
|
314
|
+
+ " · MV " + root.recommendation.card.mana_value
|
|
315
|
+
}
|
|
316
|
+
color: Theme.textMuted
|
|
317
|
+
font.pixelSize: Theme.textPixelSize(12)
|
|
318
|
+
wrapMode: Text.WordWrap
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
GridLayout {
|
|
322
|
+
visible: root.detailedIntel
|
|
323
|
+
objectName: "cardPreviewScores"
|
|
324
|
+
Layout.fillWidth: true
|
|
325
|
+
columns: 2
|
|
326
|
+
columnSpacing: 12
|
|
327
|
+
rowSpacing: 4
|
|
328
|
+
|
|
329
|
+
Label { text: "DO Score"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
330
|
+
Label {
|
|
331
|
+
text: root.recommendation ? root.recommendation.score : "—"
|
|
332
|
+
color: Theme.primary
|
|
333
|
+
font.family: fixedFontFamily
|
|
334
|
+
font.pixelSize: Theme.textPixelSize(16)
|
|
335
|
+
font.bold: true
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Label { text: "17L WR"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
339
|
+
Label {
|
|
340
|
+
text: root.winRateText
|
|
341
|
+
color: Theme.text
|
|
342
|
+
font.family: fixedFontFamily
|
|
343
|
+
font.pixelSize: Theme.textPixelSize(15)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
Label { text: "Grade"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
347
|
+
Label {
|
|
348
|
+
text: root.recommendation
|
|
349
|
+
? root.recommendation.letter_grade || "—" : "—"
|
|
350
|
+
color: Theme.warning
|
|
351
|
+
font.pixelSize: Theme.textPixelSize(15)
|
|
352
|
+
font.bold: true
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
Label { text: "ALSA"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
356
|
+
Label {
|
|
357
|
+
text: root.alsaText
|
|
358
|
+
color: Theme.text
|
|
359
|
+
font.family: fixedFontFamily
|
|
360
|
+
font.pixelSize: Theme.textPixelSize(15)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
Label { text: "Fit"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
364
|
+
Label {
|
|
365
|
+
Layout.fillWidth: true
|
|
366
|
+
text: root.recommendation
|
|
367
|
+
? root.recommendation.color_fit || "Open" : "—"
|
|
368
|
+
color: Theme.text
|
|
369
|
+
elide: Text.ElideRight
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
Label { text: "Source"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11) }
|
|
373
|
+
Label {
|
|
374
|
+
Layout.fillWidth: true
|
|
375
|
+
text: root.recommendation
|
|
376
|
+
? root.recommendation.source_label || "Unavailable" : "—"
|
|
377
|
+
color: Theme.text
|
|
378
|
+
elide: Text.ElideRight
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
RowLayout {
|
|
383
|
+
visible: !root.detailedIntel
|
|
384
|
+
Layout.fillWidth: true
|
|
385
|
+
|
|
386
|
+
Label {
|
|
387
|
+
text: root.recommendation ? "DO " + root.recommendation.score : ""
|
|
388
|
+
color: Theme.primary
|
|
389
|
+
font.family: fixedFontFamily
|
|
390
|
+
font.bold: true
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
Label {
|
|
394
|
+
text: root.recommendation && root.recommendation.win_rate !== null
|
|
395
|
+
? "17L " + (root.recommendation.win_rate * 100).toFixed(1) + "%"
|
|
396
|
+
: "17L —"
|
|
397
|
+
color: Theme.textMuted
|
|
398
|
+
font.family: fixedFontFamily
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
Label {
|
|
402
|
+
text: root.recommendation
|
|
403
|
+
? root.recommendation.letter_grade || "—" : ""
|
|
404
|
+
color: Theme.warning
|
|
405
|
+
font.bold: true
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
Label {
|
|
410
|
+
objectName: "cardPreviewExplanation"
|
|
411
|
+
Layout.fillWidth: true
|
|
412
|
+
text: {
|
|
413
|
+
if (!root.recommendation)
|
|
414
|
+
return ""
|
|
415
|
+
if (root.detailedIntel) {
|
|
416
|
+
return root.recommendation.explanation
|
|
417
|
+
|| "Explanation unavailable."
|
|
418
|
+
}
|
|
419
|
+
return root.recommendation.explanation || ""
|
|
420
|
+
}
|
|
421
|
+
color: Theme.textMuted
|
|
422
|
+
font.pixelSize: Theme.textPixelSize(12)
|
|
423
|
+
wrapMode: Text.WordWrap
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
Item {
|
|
429
|
+
Layout.columnSpan: root.detailedIntel ? 2 : 1
|
|
430
|
+
Layout.fillHeight: true
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import QtQuick 2.15
|
|
2
|
+
import QtQuick.Controls 2.15
|
|
3
|
+
|
|
4
|
+
Button {
|
|
5
|
+
id: control
|
|
6
|
+
|
|
7
|
+
property bool dimensionalSelected: checked
|
|
8
|
+
property bool expanded: false
|
|
9
|
+
property bool accented: true
|
|
10
|
+
property color accentColor: Theme.primary
|
|
11
|
+
property color accentContentColor: Theme.primaryContent
|
|
12
|
+
readonly property color contentColor: {
|
|
13
|
+
if (!control.enabled)
|
|
14
|
+
return Theme.controlDisabledContent
|
|
15
|
+
return control.accented ? control.accentContentColor : Theme.neutralContent
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
implicitHeight: Theme.targetHeight
|
|
19
|
+
|
|
20
|
+
background: DimensionalSurface {
|
|
21
|
+
stateEnabled: control.enabled
|
|
22
|
+
stateHovered: control.hovered
|
|
23
|
+
statePressed: control.down
|
|
24
|
+
stateFocused: control.activeFocus
|
|
25
|
+
stateSelected: control.dimensionalSelected
|
|
26
|
+
stateExpanded: control.expanded
|
|
27
|
+
accented: control.accented
|
|
28
|
+
accentColor: control.accentColor
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
contentItem: Text {
|
|
32
|
+
transform: Translate {
|
|
33
|
+
y: control.down ? Theme.controlPressOffset : 0
|
|
34
|
+
}
|
|
35
|
+
text: control.text
|
|
36
|
+
color: control.contentColor
|
|
37
|
+
font: control.font
|
|
38
|
+
horizontalAlignment: Text.AlignHCenter
|
|
39
|
+
verticalAlignment: Text.AlignVCenter
|
|
40
|
+
elide: Text.ElideRight
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
pragma ComponentBehavior: Bound
|
|
2
|
+
|
|
3
|
+
import QtQuick 2.15
|
|
4
|
+
import QtQml 2.15
|
|
5
|
+
import QtQuick.Controls 2.15
|
|
6
|
+
import QtQuick.Layouts 1.15
|
|
7
|
+
|
|
8
|
+
ComboBox {
|
|
9
|
+
id: control
|
|
10
|
+
|
|
11
|
+
property bool accented: true
|
|
12
|
+
property color accentColor: Theme.primary
|
|
13
|
+
property color accentContentColor: Theme.primaryContent
|
|
14
|
+
readonly property color contentColor: {
|
|
15
|
+
if (!control.enabled)
|
|
16
|
+
return Theme.controlDisabledContent
|
|
17
|
+
return control.accented ? control.accentContentColor : Theme.neutralContent
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
implicitWidth: 150
|
|
21
|
+
implicitHeight: Theme.targetHeight
|
|
22
|
+
|
|
23
|
+
function labelFor(model, index) {
|
|
24
|
+
if (!control.textRole)
|
|
25
|
+
return control.textAt(index)
|
|
26
|
+
|
|
27
|
+
const roleValue = model[control.textRole]
|
|
28
|
+
if (control.textRole === "screen_name" && !roleValue)
|
|
29
|
+
return model.account_id ? String(model.account_id) : ""
|
|
30
|
+
|
|
31
|
+
if (roleValue === undefined || roleValue === null)
|
|
32
|
+
return ""
|
|
33
|
+
|
|
34
|
+
const label = String(roleValue)
|
|
35
|
+
if (control.textRole === "pair" && model.automatic)
|
|
36
|
+
return label + " · automatic"
|
|
37
|
+
return label
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
background: DimensionalSurface {
|
|
41
|
+
stateEnabled: control.enabled
|
|
42
|
+
stateHovered: control.hovered
|
|
43
|
+
statePressed: control.pressed
|
|
44
|
+
stateFocused: control.activeFocus
|
|
45
|
+
stateSelected: control.currentIndex >= 0
|
|
46
|
+
stateExpanded: control.popup.visible
|
|
47
|
+
accented: control.accented
|
|
48
|
+
accentColor: control.accentColor
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
contentItem: Text {
|
|
52
|
+
transform: Translate {
|
|
53
|
+
y: control.pressed ? Theme.controlPressOffset : 0
|
|
54
|
+
}
|
|
55
|
+
leftPadding: 12
|
|
56
|
+
rightPadding: control.indicator.width + 10
|
|
57
|
+
text: control.displayText
|
|
58
|
+
color: control.contentColor
|
|
59
|
+
font: control.font
|
|
60
|
+
verticalAlignment: Text.AlignVCenter
|
|
61
|
+
elide: Text.ElideRight
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
indicator: Canvas {
|
|
65
|
+
id: chevron
|
|
66
|
+
objectName: "dimensionalComboIndicator"
|
|
67
|
+
x: control.width - width - 10
|
|
68
|
+
y: (control.height - height) / 2
|
|
69
|
+
+ (control.pressed ? Theme.controlPressOffset : 0)
|
|
70
|
+
width: 14
|
|
71
|
+
height: 10
|
|
72
|
+
opacity: control.enabled ? 1 : 0.45
|
|
73
|
+
rotation: control.popup.visible ? 180 : 0
|
|
74
|
+
onPaint: {
|
|
75
|
+
const context = getContext("2d")
|
|
76
|
+
context.clearRect(0, 0, width, height)
|
|
77
|
+
context.strokeStyle = control.contentColor
|
|
78
|
+
context.lineWidth = 1.5
|
|
79
|
+
context.lineCap = "square"
|
|
80
|
+
context.beginPath()
|
|
81
|
+
context.moveTo(2, 3)
|
|
82
|
+
context.lineTo(7, 8)
|
|
83
|
+
context.lineTo(12, 3)
|
|
84
|
+
context.stroke()
|
|
85
|
+
}
|
|
86
|
+
onRotationChanged: requestPaint()
|
|
87
|
+
onOpacityChanged: requestPaint()
|
|
88
|
+
}
|
|
89
|
+
Connections {
|
|
90
|
+
target: control
|
|
91
|
+
function onContentColorChanged() {
|
|
92
|
+
chevron.requestPaint()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
delegate: ItemDelegate {
|
|
97
|
+
id: comboDelegate
|
|
98
|
+
objectName: "dimensionalComboDelegate"
|
|
99
|
+
required property var model
|
|
100
|
+
required property int index
|
|
101
|
+
|
|
102
|
+
width: comboPopup.availableWidth
|
|
103
|
+
height: Theme.targetHeight
|
|
104
|
+
text: control.labelFor(comboDelegate.model, comboDelegate.index)
|
|
105
|
+
highlighted: control.highlightedIndex === comboDelegate.index
|
|
106
|
+
Accessible.name: text
|
|
107
|
+
|
|
108
|
+
background: DimensionalSurface {
|
|
109
|
+
stateEnabled: comboDelegate.enabled
|
|
110
|
+
stateHovered: comboDelegate.hovered || comboDelegate.highlighted
|
|
111
|
+
statePressed: comboDelegate.down
|
|
112
|
+
stateFocused: comboDelegate.activeFocus
|
|
113
|
+
stateSelected: control.currentIndex === comboDelegate.index
|
|
114
|
+
stateExpanded: comboDelegate.highlighted
|
|
115
|
+
accented: control.currentIndex === comboDelegate.index
|
|
116
|
+
accentColor: control.accentColor
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
contentItem: RowLayout {
|
|
120
|
+
anchors.fill: parent
|
|
121
|
+
anchors.topMargin: comboDelegate.down ? Theme.controlPressOffset : 0
|
|
122
|
+
anchors.leftMargin: 10
|
|
123
|
+
anchors.rightMargin: 10
|
|
124
|
+
spacing: 8
|
|
125
|
+
|
|
126
|
+
Text {
|
|
127
|
+
objectName: "dimensionalComboSelectedIndicator"
|
|
128
|
+
visible: control.currentIndex === comboDelegate.index
|
|
129
|
+
text: "✓"
|
|
130
|
+
color: comboDelegate.enabled
|
|
131
|
+
? control.accentContentColor : Theme.controlDisabledContent
|
|
132
|
+
font.bold: true
|
|
133
|
+
Layout.preferredWidth: 14
|
|
134
|
+
horizontalAlignment: Text.AlignHCenter
|
|
135
|
+
verticalAlignment: Text.AlignVCenter
|
|
136
|
+
Accessible.ignored: true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Text {
|
|
140
|
+
Layout.fillWidth: true
|
|
141
|
+
text: comboDelegate.text
|
|
142
|
+
color: {
|
|
143
|
+
if (!comboDelegate.enabled)
|
|
144
|
+
return Theme.controlDisabledContent
|
|
145
|
+
if (control.currentIndex === comboDelegate.index)
|
|
146
|
+
return control.accentContentColor
|
|
147
|
+
return Theme.neutralContent
|
|
148
|
+
}
|
|
149
|
+
font: comboDelegate.font
|
|
150
|
+
verticalAlignment: Text.AlignVCenter
|
|
151
|
+
elide: Text.ElideRight
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
popup: Popup {
|
|
157
|
+
id: comboPopup
|
|
158
|
+
objectName: "dimensionalComboPopup"
|
|
159
|
+
y: control.height + 4
|
|
160
|
+
width: control.width
|
|
161
|
+
padding: 4
|
|
162
|
+
implicitHeight: Math.min(320, comboList.contentHeight + padding * 2)
|
|
163
|
+
modal: false
|
|
164
|
+
|
|
165
|
+
background: DimensionalSurface {
|
|
166
|
+
stateEnabled: control.enabled
|
|
167
|
+
stateExpanded: true
|
|
168
|
+
accented: false
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
contentItem: ListView {
|
|
172
|
+
id: comboList
|
|
173
|
+
objectName: "dimensionalComboList"
|
|
174
|
+
clip: true
|
|
175
|
+
implicitHeight: contentHeight
|
|
176
|
+
model: comboPopup.visible ? control.delegateModel : null
|
|
177
|
+
currentIndex: control.highlightedIndex
|
|
178
|
+
boundsBehavior: Flickable.StopAtBounds
|
|
179
|
+
ScrollIndicator.vertical: ScrollIndicator { }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|