style(ui): login dialog matches Steam-layout in Prism dark
CI / test (3.10) (push) Successful in 7s
CI / test (3.11) (push) Successful in 7s
CI / test (3.12) (push) Successful in 7s
CI / build-pyz (push) Successful in 5s
CI / release (push) Has been skipped

Same skeleton as the conflict dialog: header (Prism-green badge +
uppercase title), body copy, content, foot row.

Specifics:
  - badge: '+' on Prism-green circle (vs the conflict's '!' on
    light gray) — green reads as 'setup / new', not warning
  - command callout: monospace, green text, dark inset card
  - token input: monospace, dark inset, green focus border, green
    selection highlight
  - 'Skip cloud sync' = secondary (dark surface, green hover border)
  - 'Save and continue' = primary (Prism-green fill, black text)

Same Enter-to-submit + format validation behavior as before;
on-screen error reuses Prism's BrightText red.
This commit is contained in:
2026-06-04 23:32:32 +02:00
parent 016c88f60f
commit aba6472292
+131 -28
View File
@@ -191,12 +191,94 @@ class QtProgressWindow:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
_LOGIN_QSS = """
QDialog { background: #313131; }
QLabel#title {
color: white;
font-size: 20pt;
font-weight: bold;
letter-spacing: 2px;
}
QLabel#badge {
color: #313131;
background: #96db59;
border-radius: 16px;
font-size: 20pt;
font-weight: bold;
qproperty-alignment: AlignCenter;
min-width: 32px;
max-width: 32px;
min-height: 32px;
max-height: 32px;
}
QLabel#body {
color: #c8c8c8;
font-size: 10pt;
}
QLabel#hint {
color: #8a8a8a;
font-size: 9pt;
}
QLabel#error {
color: #ff7373;
font-size: 9pt;
}
QLabel#cmd {
color: #96db59;
background: #222222;
border: 1px solid #3a3a3a;
border-radius: 3px;
padding: 6px 10px;
font-family: "JetBrains Mono", "Source Code Pro", Menlo, Consolas, monospace;
font-size: 10pt;
}
QLineEdit#token {
background: #222222;
border: 1px solid #3a3a3a;
border-radius: 3px;
padding: 8px 10px;
color: white;
font-family: "JetBrains Mono", "Source Code Pro", Menlo, Consolas, monospace;
font-size: 10pt;
selection-background-color: #96db59;
selection-color: black;
}
QLineEdit#token:focus { border: 1px solid #96db59; }
QPushButton#secondary {
background: #303030;
color: white;
border: 1px solid #4a4a4a;
border-radius: 2px;
padding: 8px 20px;
font-size: 10pt;
}
QPushButton#secondary:hover { background: #3a3a3a; border-color: #96db59; }
QPushButton#secondary:pressed { background: #222222; }
QPushButton#primary {
background: #96db59;
color: #1a1a1a;
border: none;
border-radius: 2px;
padding: 8px 24px;
font-size: 10pt;
font-weight: bold;
}
QPushButton#primary:hover { background: #a8e670; }
QPushButton#primary:pressed { background: #7fbf48; }
QPushButton#primary:disabled { background: #4a5a3a; color: #888888; }
"""
def prompt_login_qt() -> str | None: def prompt_login_qt() -> str | None:
"""Modal dialog to collect a fresh ``discord_id:password`` token. """Modal dialog to collect a fresh ``discord_id:password`` token.
Returned string is the validated raw token (caller writes to disk). Returned string is the validated raw token (caller writes to disk).
Returns ``None`` if the user picked "Skip cloud sync" or closed the Returns ``None`` if the user picked "Skip cloud sync" or closed the
dialog — caller should treat that as "don't sync, don't block launch." dialog — caller should treat that as "don't sync, don't block launch."
Visually matches the conflict dialog: Prism dark surface, circled
badge + uppercase title, monospace input field with Prism-green
focus accent, primary "Save and continue" button in Prism green.
""" """
QtWidgets, QtCore, _ = import_qt() QtWidgets, QtCore, _ = import_qt()
app_existed = QtWidgets.QApplication.instance() is not None app_existed = QtWidgets.QApplication.instance() is not None
@@ -205,58 +287,79 @@ def prompt_login_qt() -> str | None:
_apply_prism_dark(app) _apply_prism_dark(app)
dialog = QtWidgets.QDialog() dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Cloud sync — first time setup") dialog.setWindowTitle("Cloud sync — connect account")
dialog.setFixedSize(480, 260) dialog.setFixedSize(560, 360)
dialog.setStyleSheet(_LOGIN_QSS)
layout = QtWidgets.QVBoxLayout(dialog) outer = QtWidgets.QVBoxLayout(dialog)
layout.setContentsMargins(20, 20, 20, 20) outer.setContentsMargins(28, 24, 28, 20)
layout.setSpacing(10) outer.setSpacing(14)
title = QtWidgets.QLabel("Connect to cloud save") header = QtWidgets.QHBoxLayout()
font = title.font() header.setSpacing(12)
font.setBold(True) badge = QtWidgets.QLabel("+")
font.setPointSize(font.pointSize() + 2) badge.setObjectName("badge")
title.setFont(font) header.addWidget(badge)
layout.addWidget(title) title = QtWidgets.QLabel("CONNECT CLOUD SAVE")
title.setObjectName("title")
header.addWidget(title)
header.addStretch(1)
outer.addLayout(header)
body = QtWidgets.QLabel( body = QtWidgets.QLabel(
"In Discord, message the bot:\n" "To enable cross-machine save sync, message the Discord bot to "
" /cloud register\n\n" "register this account. The bot will DM you a one-line token — "
"It will DM you a one-line token. Paste it below:" "paste it below."
) )
body.setObjectName("body")
body.setWordWrap(True) body.setWordWrap(True)
layout.addWidget(body) outer.addWidget(body)
cmd = QtWidgets.QLabel("/cloud register")
cmd.setObjectName("cmd")
cmd.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
cmd.setFixedWidth(180)
outer.addWidget(cmd)
outer.addSpacing(4)
field_label = QtWidgets.QLabel("Token")
field_label.setObjectName("hint")
outer.addWidget(field_label)
field = QtWidgets.QLineEdit() field = QtWidgets.QLineEdit()
field.setPlaceholderText("123456789012345678:a1b2c3d4…") field.setObjectName("token")
field.setPlaceholderText("123456789012345678:a1b2c3d4e5f6…")
field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password) field.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
layout.addWidget(field) outer.addWidget(field)
error = QtWidgets.QLabel("") error = QtWidgets.QLabel("")
error.setStyleSheet("color: #ff6b6b;") error.setObjectName("error")
layout.addWidget(error) outer.addWidget(error)
layout.addStretch(1) outer.addStretch(1)
button_row = QtWidgets.QHBoxLayout() foot = QtWidgets.QHBoxLayout()
skip = QtWidgets.QPushButton("Skip cloud sync") skip = QtWidgets.QPushButton("Skip cloud sync")
button_row.addWidget(skip) skip.setObjectName("secondary")
button_row.addStretch(1) foot.addWidget(skip)
foot.addStretch(1)
save = QtWidgets.QPushButton("Save and continue") save = QtWidgets.QPushButton("Save and continue")
save.setObjectName("primary")
save.setDefault(True) save.setDefault(True)
button_row.addWidget(save) foot.addWidget(save)
layout.addLayout(button_row) outer.addLayout(foot)
chosen: dict[str, str | None] = {"value": None} chosen: dict[str, str | None] = {"value": None}
def on_save() -> None: def on_save() -> None:
token = field.text().strip() token = field.text().strip()
if ":" not in token: if ":" not in token:
error.setText("Token must be discord_id:password") error.setText("Token must look like discord_id:password.")
return return
head, sep, tail = token.partition(":") head, _sep, tail = token.partition(":")
if not head.isdigit() or not tail: if not head.isdigit() or not tail:
error.setText("discord_id must be numeric; password must be non-empty") error.setText("discord_id must be numeric and password non-empty.")
return return
chosen["value"] = token chosen["value"] = token
dialog.accept() dialog.accept()