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
draftomen/preferences.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Persisted user settings for Draftomen interfaces.
|
|
2
|
+
Keep optional behavior and layout choices stable across app restarts.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import tempfile
|
|
9
|
+
from dataclasses import asdict, dataclass
|
|
10
|
+
from os import PathLike
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Literal, TypeAlias
|
|
13
|
+
|
|
14
|
+
from draftomen.paths import app_data_dir
|
|
15
|
+
|
|
16
|
+
PathInput: TypeAlias = str | PathLike[str]
|
|
17
|
+
CardImagePreviewMode: TypeAlias = Literal["auto", "show", "hide"]
|
|
18
|
+
|
|
19
|
+
TUI_PREFERENCES_FILE_NAME = "tui-preferences.json"
|
|
20
|
+
TUI_PREFERENCES_SCHEMA_VERSION = 1
|
|
21
|
+
CARD_IMAGE_PREVIEW_MODES = frozenset({"auto", "show", "hide"})
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class TuiVisibilityPreferences:
|
|
26
|
+
"""User-controlled Textual behavior and optional interface elements.
|
|
27
|
+
Missing settings use safe defaults so existing files remain compatible.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
secondary_columns: bool = True
|
|
31
|
+
build_details: bool = False
|
|
32
|
+
splash_enabled: bool = True
|
|
33
|
+
pool_metadata: bool = True
|
|
34
|
+
pool_color_distribution: bool = True
|
|
35
|
+
pool_mana_curve: bool = True
|
|
36
|
+
account_identifier: bool = True
|
|
37
|
+
draft_identifier: bool = True
|
|
38
|
+
mana_pips_and_sources: bool = True
|
|
39
|
+
attribution: bool = True
|
|
40
|
+
focused_card_details: bool = True
|
|
41
|
+
card_image_preview: CardImagePreviewMode = "auto"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def tui_preferences_path(*, app_dir: PathInput | None = None) -> Path:
|
|
45
|
+
"""Return the path used for persisted TUI visibility preferences.
|
|
46
|
+
The file lives directly in Draftomen's per-user application directory.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
root = Path(app_data_dir() if app_dir is None else app_dir).expanduser()
|
|
50
|
+
return root / TUI_PREFERENCES_FILE_NAME
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_tui_preferences(
|
|
54
|
+
*,
|
|
55
|
+
app_dir: PathInput | None = None,
|
|
56
|
+
) -> tuple[TuiVisibilityPreferences, str | None]:
|
|
57
|
+
"""Load persisted visibility preferences, falling back safely to defaults.
|
|
58
|
+
A warning describes malformed or unsupported configuration without blocking startup.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
path = tui_preferences_path(app_dir=app_dir)
|
|
63
|
+
raw_text = path.read_text(encoding="utf-8")
|
|
64
|
+
except FileNotFoundError:
|
|
65
|
+
return TuiVisibilityPreferences(), None
|
|
66
|
+
except (OSError, UnicodeDecodeError) as error:
|
|
67
|
+
return TuiVisibilityPreferences(), f"Could not load TUI preferences: {error}"
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
raw_data = json.loads(raw_text)
|
|
71
|
+
except json.JSONDecodeError as error:
|
|
72
|
+
return TuiVisibilityPreferences(), f"Could not load TUI preferences: {error}"
|
|
73
|
+
|
|
74
|
+
if not isinstance(raw_data, dict):
|
|
75
|
+
return (
|
|
76
|
+
TuiVisibilityPreferences(),
|
|
77
|
+
"Could not load TUI preferences: expected an object.",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
version = raw_data.get("version")
|
|
81
|
+
if version != TUI_PREFERENCES_SCHEMA_VERSION:
|
|
82
|
+
return (
|
|
83
|
+
TuiVisibilityPreferences(),
|
|
84
|
+
"Could not load TUI preferences: unsupported settings version.",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
visibility = raw_data.get("visibility")
|
|
88
|
+
if not isinstance(visibility, dict):
|
|
89
|
+
return (
|
|
90
|
+
TuiVisibilityPreferences(),
|
|
91
|
+
"Could not load TUI preferences: expected a visibility object.",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
defaults = TuiVisibilityPreferences()
|
|
95
|
+
values = asdict(defaults)
|
|
96
|
+
invalid_fields: list[str] = []
|
|
97
|
+
for field_name, default_value in values.items():
|
|
98
|
+
value = visibility.get(field_name, default_value)
|
|
99
|
+
if field_name == "card_image_preview":
|
|
100
|
+
if not isinstance(value, str) or value not in CARD_IMAGE_PREVIEW_MODES:
|
|
101
|
+
invalid_fields.append(field_name)
|
|
102
|
+
continue
|
|
103
|
+
elif type(value) is not bool:
|
|
104
|
+
invalid_fields.append(field_name)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
values[field_name] = value
|
|
108
|
+
|
|
109
|
+
preferences = TuiVisibilityPreferences(**values)
|
|
110
|
+
if not invalid_fields:
|
|
111
|
+
return preferences, None
|
|
112
|
+
|
|
113
|
+
fields = ", ".join(invalid_fields)
|
|
114
|
+
return preferences, f"TUI preferences used defaults for invalid fields: {fields}."
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def save_tui_preferences(
|
|
118
|
+
*,
|
|
119
|
+
preferences: TuiVisibilityPreferences,
|
|
120
|
+
app_dir: PathInput | None = None,
|
|
121
|
+
) -> str | None:
|
|
122
|
+
"""Atomically save visibility preferences and return a non-fatal error message.
|
|
123
|
+
The active TUI can continue using preferences when persistence fails.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
path = tui_preferences_path(app_dir=app_dir)
|
|
127
|
+
temporary_path: Path | None = None
|
|
128
|
+
try:
|
|
129
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
with tempfile.NamedTemporaryFile(
|
|
131
|
+
mode="w",
|
|
132
|
+
encoding="utf-8",
|
|
133
|
+
dir=path.parent,
|
|
134
|
+
delete=False,
|
|
135
|
+
) as temporary_file:
|
|
136
|
+
json.dump(
|
|
137
|
+
{
|
|
138
|
+
"version": TUI_PREFERENCES_SCHEMA_VERSION,
|
|
139
|
+
"visibility": asdict(preferences),
|
|
140
|
+
},
|
|
141
|
+
temporary_file,
|
|
142
|
+
indent=2,
|
|
143
|
+
sort_keys=True,
|
|
144
|
+
)
|
|
145
|
+
temporary_file.write("\n")
|
|
146
|
+
temporary_path = Path(temporary_file.name)
|
|
147
|
+
|
|
148
|
+
temporary_path.replace(path)
|
|
149
|
+
except OSError as error:
|
|
150
|
+
return f"Could not save TUI preferences: {error}"
|
|
151
|
+
finally:
|
|
152
|
+
if temporary_path is not None:
|
|
153
|
+
try:
|
|
154
|
+
temporary_path.unlink()
|
|
155
|
+
except FileNotFoundError:
|
|
156
|
+
pass
|
|
157
|
+
except OSError:
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
GUI_PREFERENCES_FILE_NAME = "gui-preferences.json"
|
|
164
|
+
GUI_PREFERENCES_SCHEMA_VERSION = 1
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass(frozen=True)
|
|
168
|
+
class GuiDisplayPreferences:
|
|
169
|
+
"""User-controlled desktop display choices independent from the live session.
|
|
170
|
+
Functional draft choices remain owned by explicit live-session commands.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
compact_density: bool = False
|
|
174
|
+
secondary_stats: bool = True
|
|
175
|
+
card_preview: bool = True
|
|
176
|
+
detailed_build_context: bool = True
|
|
177
|
+
system_text_scaling: bool = True
|
|
178
|
+
show_backtest: bool = False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def gui_preferences_path(*, app_dir: PathInput | None = None) -> Path:
|
|
182
|
+
"""Return the path used for persisted desktop display preferences.
|
|
183
|
+
The file lives directly in Draftomen's per-user application directory.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
root = Path(app_data_dir() if app_dir is None else app_dir).expanduser()
|
|
187
|
+
return root / GUI_PREFERENCES_FILE_NAME
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def load_gui_preferences(
|
|
191
|
+
*,
|
|
192
|
+
app_dir: PathInput | None = None,
|
|
193
|
+
) -> tuple[GuiDisplayPreferences, str | None]:
|
|
194
|
+
"""Load desktop display preferences, falling back safely to defaults.
|
|
195
|
+
A warning describes malformed or unsupported configuration without blocking startup.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
path = gui_preferences_path(app_dir=app_dir)
|
|
200
|
+
raw_text = path.read_text(encoding="utf-8")
|
|
201
|
+
except FileNotFoundError:
|
|
202
|
+
return GuiDisplayPreferences(), None
|
|
203
|
+
except (OSError, UnicodeDecodeError) as error:
|
|
204
|
+
return GuiDisplayPreferences(), f"Could not load GUI preferences: {error}"
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
raw_data = json.loads(raw_text)
|
|
208
|
+
except json.JSONDecodeError as error:
|
|
209
|
+
return GuiDisplayPreferences(), f"Could not load GUI preferences: {error}"
|
|
210
|
+
|
|
211
|
+
if not isinstance(raw_data, dict):
|
|
212
|
+
return GuiDisplayPreferences(), "Could not load GUI preferences: expected an object."
|
|
213
|
+
if raw_data.get("version") != GUI_PREFERENCES_SCHEMA_VERSION:
|
|
214
|
+
return (
|
|
215
|
+
GuiDisplayPreferences(),
|
|
216
|
+
"Could not load GUI preferences: unsupported settings version.",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
display = raw_data.get("display")
|
|
220
|
+
if not isinstance(display, dict):
|
|
221
|
+
return (
|
|
222
|
+
GuiDisplayPreferences(),
|
|
223
|
+
"Could not load GUI preferences: expected a display object.",
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
defaults = GuiDisplayPreferences()
|
|
227
|
+
values = asdict(defaults)
|
|
228
|
+
invalid_fields: list[str] = []
|
|
229
|
+
for field_name, default_value in values.items():
|
|
230
|
+
value = display.get(field_name, default_value)
|
|
231
|
+
if type(value) is not bool:
|
|
232
|
+
invalid_fields.append(field_name)
|
|
233
|
+
continue
|
|
234
|
+
values[field_name] = value
|
|
235
|
+
|
|
236
|
+
preferences = GuiDisplayPreferences(**values)
|
|
237
|
+
if not invalid_fields:
|
|
238
|
+
return preferences, None
|
|
239
|
+
return (
|
|
240
|
+
preferences,
|
|
241
|
+
"GUI preferences used defaults for invalid fields: "
|
|
242
|
+
+ ", ".join(invalid_fields)
|
|
243
|
+
+ ".",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def save_gui_preferences(
|
|
248
|
+
*,
|
|
249
|
+
preferences: GuiDisplayPreferences,
|
|
250
|
+
app_dir: PathInput | None = None,
|
|
251
|
+
) -> str | None:
|
|
252
|
+
"""Atomically save desktop display preferences and return a non-fatal error.
|
|
253
|
+
The active GUI can continue using a changed preference when persistence fails.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
path = gui_preferences_path(app_dir=app_dir)
|
|
257
|
+
temporary_path: Path | None = None
|
|
258
|
+
try:
|
|
259
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
260
|
+
with tempfile.NamedTemporaryFile(
|
|
261
|
+
mode="w",
|
|
262
|
+
encoding="utf-8",
|
|
263
|
+
dir=path.parent,
|
|
264
|
+
delete=False,
|
|
265
|
+
) as temporary_file:
|
|
266
|
+
json.dump(
|
|
267
|
+
{
|
|
268
|
+
"version": GUI_PREFERENCES_SCHEMA_VERSION,
|
|
269
|
+
"display": asdict(preferences),
|
|
270
|
+
},
|
|
271
|
+
temporary_file,
|
|
272
|
+
indent=2,
|
|
273
|
+
sort_keys=True,
|
|
274
|
+
)
|
|
275
|
+
temporary_file.write("\n")
|
|
276
|
+
temporary_path = Path(temporary_file.name)
|
|
277
|
+
temporary_path.replace(path)
|
|
278
|
+
except OSError as error:
|
|
279
|
+
return f"Could not save GUI preferences: {error}"
|
|
280
|
+
finally:
|
|
281
|
+
if temporary_path is not None:
|
|
282
|
+
try:
|
|
283
|
+
temporary_path.unlink()
|
|
284
|
+
except FileNotFoundError:
|
|
285
|
+
pass
|
|
286
|
+
except OSError:
|
|
287
|
+
pass
|
|
288
|
+
|
|
289
|
+
return None
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import QtQuick 2.15
|
|
2
|
+
import QtQuick.Controls 2.15
|
|
3
|
+
import QtQuick.Layouts 1.15
|
|
4
|
+
|
|
5
|
+
Dialog {
|
|
6
|
+
id: root
|
|
7
|
+
|
|
8
|
+
required property string applicationVersion
|
|
9
|
+
property var returnFocusItem: null
|
|
10
|
+
readonly property string projectWebsite: "https://www.draftomen.com"
|
|
11
|
+
readonly property string projectRepository: "https://github.com/andreagrandi/draftomen"
|
|
12
|
+
|
|
13
|
+
objectName: "aboutDialog"
|
|
14
|
+
parent: Overlay.overlay
|
|
15
|
+
modal: true
|
|
16
|
+
focus: true
|
|
17
|
+
title: "About Draft Omen"
|
|
18
|
+
width: Math.min(460, Math.max(300, parent ? parent.width - 32 : 460))
|
|
19
|
+
x: parent ? Math.max(16, Math.round((parent.width - width) / 2)) : 16
|
|
20
|
+
y: parent ? Math.max(16, Math.round((parent.height - height) / 2)) : 16
|
|
21
|
+
padding: 16
|
|
22
|
+
|
|
23
|
+
Overlay.modal: Rectangle {
|
|
24
|
+
color: "#99000000"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
background: Rectangle {
|
|
28
|
+
color: Theme.surface
|
|
29
|
+
border.color: Theme.outline
|
|
30
|
+
border.width: 1
|
|
31
|
+
radius: Theme.radius
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
header: Rectangle {
|
|
35
|
+
implicitHeight: 52
|
|
36
|
+
color: Theme.surfaceHigh
|
|
37
|
+
border.color: Theme.outline
|
|
38
|
+
border.width: 1
|
|
39
|
+
|
|
40
|
+
Label {
|
|
41
|
+
anchors.fill: parent
|
|
42
|
+
anchors.leftMargin: 16
|
|
43
|
+
anchors.rightMargin: 16
|
|
44
|
+
text: root.title
|
|
45
|
+
color: Theme.text
|
|
46
|
+
font.pixelSize: Theme.textPixelSize(18)
|
|
47
|
+
font.bold: true
|
|
48
|
+
verticalAlignment: Text.AlignVCenter
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onClosed: {
|
|
53
|
+
const opener = root.returnFocusItem
|
|
54
|
+
root.returnFocusItem = null
|
|
55
|
+
if (opener && opener.visible && opener.enabled)
|
|
56
|
+
opener.forceActiveFocus()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
contentItem: ColumnLayout {
|
|
60
|
+
spacing: 12
|
|
61
|
+
implicitWidth: 360
|
|
62
|
+
|
|
63
|
+
Label {
|
|
64
|
+
objectName: "aboutDialogTitle"
|
|
65
|
+
Layout.fillWidth: true
|
|
66
|
+
text: "Draft Omen"
|
|
67
|
+
color: Theme.text
|
|
68
|
+
font.pixelSize: Theme.textPixelSize(24)
|
|
69
|
+
font.bold: true
|
|
70
|
+
horizontalAlignment: Text.AlignHCenter
|
|
71
|
+
Accessible.name: text
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Image {
|
|
75
|
+
objectName: "aboutDialogLogo"
|
|
76
|
+
Layout.alignment: Qt.AlignHCenter
|
|
77
|
+
Layout.preferredWidth: 180
|
|
78
|
+
Layout.preferredHeight: 180
|
|
79
|
+
source: "../assets/draftomen_logo.png"
|
|
80
|
+
sourceSize.width: 360
|
|
81
|
+
sourceSize.height: 360
|
|
82
|
+
fillMode: Image.PreserveAspectFit
|
|
83
|
+
smooth: true
|
|
84
|
+
mipmap: true
|
|
85
|
+
Accessible.name: "Draft Omen logo"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Label {
|
|
89
|
+
objectName: "aboutDialogVersion"
|
|
90
|
+
Layout.fillWidth: true
|
|
91
|
+
text: "Version " + root.applicationVersion
|
|
92
|
+
color: Theme.text
|
|
93
|
+
horizontalAlignment: Text.AlignHCenter
|
|
94
|
+
Accessible.name: "Draft Omen version " + root.applicationVersion
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Label {
|
|
98
|
+
objectName: "aboutDialogAuthor"
|
|
99
|
+
Layout.fillWidth: true
|
|
100
|
+
text: "Made with ❤️ by Andrea Grandi"
|
|
101
|
+
color: Theme.text
|
|
102
|
+
horizontalAlignment: Text.AlignHCenter
|
|
103
|
+
Accessible.name: text
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
Label {
|
|
107
|
+
objectName: "aboutDialogLicense"
|
|
108
|
+
Layout.fillWidth: true
|
|
109
|
+
text: "Draft Omen is licensed under the MIT License"
|
|
110
|
+
color: Theme.text
|
|
111
|
+
horizontalAlignment: Text.AlignHCenter
|
|
112
|
+
Accessible.name: text
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
DimensionalButton {
|
|
116
|
+
id: websiteButton
|
|
117
|
+
objectName: "aboutDialogWebsite"
|
|
118
|
+
Layout.alignment: Qt.AlignHCenter
|
|
119
|
+
text: "Project website"
|
|
120
|
+
implicitWidth: 140
|
|
121
|
+
Accessible.name: "Open Draft Omen project website"
|
|
122
|
+
Accessible.description: root.projectWebsite
|
|
123
|
+
onClicked: Qt.openUrlExternally(root.projectWebsite)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
DimensionalButton {
|
|
127
|
+
objectName: "aboutDialogRepository"
|
|
128
|
+
Layout.alignment: Qt.AlignHCenter
|
|
129
|
+
text: "GitHub repository"
|
|
130
|
+
implicitWidth: 160
|
|
131
|
+
Accessible.name: "Open Draft Omen GitHub repository"
|
|
132
|
+
Accessible.description: root.projectRepository
|
|
133
|
+
onClicked: Qt.openUrlExternally(root.projectRepository)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
footer: DialogButtonBox {
|
|
138
|
+
implicitHeight: 58
|
|
139
|
+
alignment: Qt.AlignRight
|
|
140
|
+
background: Rectangle {
|
|
141
|
+
color: Theme.surfaceHigh
|
|
142
|
+
border.color: Theme.outline
|
|
143
|
+
border.width: 1
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
DimensionalButton {
|
|
147
|
+
id: closeButton
|
|
148
|
+
objectName: "aboutDialogCloseButton"
|
|
149
|
+
text: "Close"
|
|
150
|
+
accented: false
|
|
151
|
+
implicitWidth: 96
|
|
152
|
+
Accessible.name: "Close About dialog"
|
|
153
|
+
onClicked: root.close()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
draftomen/qml/AppBar.qml
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
pragma ComponentBehavior: Bound
|
|
2
|
+
|
|
3
|
+
import QtQuick 2.15
|
|
4
|
+
import QtQuick.Controls 2.15
|
|
5
|
+
import QtQuick.Layouts 1.15
|
|
6
|
+
|
|
7
|
+
Rectangle {
|
|
8
|
+
id: root
|
|
9
|
+
|
|
10
|
+
required property var sessionState
|
|
11
|
+
required property var provider
|
|
12
|
+
required property bool narrow
|
|
13
|
+
signal settingsRequested()
|
|
14
|
+
|
|
15
|
+
color: Theme.surfaceLow
|
|
16
|
+
implicitHeight: 68
|
|
17
|
+
|
|
18
|
+
RowLayout {
|
|
19
|
+
anchors.fill: parent
|
|
20
|
+
anchors.leftMargin: 20
|
|
21
|
+
anchors.rightMargin: 20
|
|
22
|
+
spacing: 16
|
|
23
|
+
|
|
24
|
+
Label {
|
|
25
|
+
objectName: "appBarBrandTitle"
|
|
26
|
+
visible: root.narrow
|
|
27
|
+
text: "DRAFTOMEN"
|
|
28
|
+
color: Theme.primary
|
|
29
|
+
font.pixelSize: Theme.textPixelSize(18)
|
|
30
|
+
font.bold: true
|
|
31
|
+
font.letterSpacing: 1.2
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Rectangle {
|
|
35
|
+
visible: root.narrow
|
|
36
|
+
Layout.preferredWidth: 1
|
|
37
|
+
Layout.preferredHeight: 34
|
|
38
|
+
color: Theme.outline
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ColumnLayout {
|
|
42
|
+
Layout.fillWidth: true
|
|
43
|
+
Layout.minimumWidth: 0
|
|
44
|
+
spacing: 2
|
|
45
|
+
|
|
46
|
+
Label {
|
|
47
|
+
text: root.sessionState.status ? root.sessionState.status.message : "Starting Draft Omen."
|
|
48
|
+
color: Theme.text
|
|
49
|
+
elide: Text.ElideRight
|
|
50
|
+
Layout.fillWidth: true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Label {
|
|
54
|
+
text: root.sessionState.draft
|
|
55
|
+
? root.sessionState.draft.set_code + " · " + root.sessionState.draft.event_name
|
|
56
|
+
: "No active draft"
|
|
57
|
+
color: Theme.textMuted
|
|
58
|
+
font.pixelSize: Theme.textPixelSize(11)
|
|
59
|
+
Layout.fillWidth: true
|
|
60
|
+
elide: Text.ElideRight
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
DimensionalComboBox {
|
|
65
|
+
id: accountSelector
|
|
66
|
+
objectName: "accountSelector"
|
|
67
|
+
Layout.preferredWidth: 180
|
|
68
|
+
visible: root.sessionState.accounts
|
|
69
|
+
&& root.sessionState.accounts.length > 0
|
|
70
|
+
model: root.sessionState.accounts || []
|
|
71
|
+
textRole: "screen_name"
|
|
72
|
+
valueRole: "account_id"
|
|
73
|
+
currentIndex: {
|
|
74
|
+
const activeAccount = root.sessionState.active_account
|
|
75
|
+
if (!activeAccount)
|
|
76
|
+
return -1
|
|
77
|
+
for (let index = 0; index < model.length; index++) {
|
|
78
|
+
if (model[index].account_id === activeAccount.account_id)
|
|
79
|
+
return index
|
|
80
|
+
}
|
|
81
|
+
return -1
|
|
82
|
+
}
|
|
83
|
+
displayText: root.sessionState.active_account
|
|
84
|
+
? root.sessionState.active_account.screen_name
|
|
85
|
+
|| root.sessionState.active_account.account_id
|
|
86
|
+
: "Choose Arena account"
|
|
87
|
+
Accessible.name: "Arena account"
|
|
88
|
+
Accessible.description: "Choose the Arena account whose live draft is shown."
|
|
89
|
+
onActivated: root.provider.chooseAccount(currentValue)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
DimensionalComboBox {
|
|
93
|
+
id: scenarioSelector
|
|
94
|
+
visible: root.provider && root.provider.mockMode
|
|
95
|
+
Layout.preferredWidth: 126
|
|
96
|
+
model: root.provider ? root.provider.scenarios : []
|
|
97
|
+
currentIndex: root.provider
|
|
98
|
+
? Math.max(0, root.provider.scenarios.indexOf(root.provider.scenario))
|
|
99
|
+
: -1
|
|
100
|
+
Accessible.name: "Representative state"
|
|
101
|
+
Accessible.description: "Choose deterministic visual-development data."
|
|
102
|
+
onActivated: root.provider.selectScenario(currentText)
|
|
103
|
+
}
|
|
104
|
+
DimensionalButton {
|
|
105
|
+
objectName: "settingsButton"
|
|
106
|
+
text: "Settings"
|
|
107
|
+
Accessible.name: "Open settings"
|
|
108
|
+
Accessible.description: "Open draft guidance, display, and accessibility settings."
|
|
109
|
+
onClicked: root.settingsRequested()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Rectangle {
|
|
114
|
+
anchors.bottom: parent.bottom
|
|
115
|
+
width: parent.width
|
|
116
|
+
height: 1
|
|
117
|
+
color: Theme.outline
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|