style(ui): login dialog matches Steam-layout in Prism dark
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:
+131
-28
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user