fix(ui): Phase 7 C3 修复 — AboutDialog 改用 Rectangle overlay + backdrop

原 AboutDialog inherits Slint Dialog(PopupWindow 子类)并 inline 实例化,没经过运行时 show(),close() 是空操作 → 用户点 Close 没反应、UI 卡死。\n- dialogs.slint:AboutDialog / ConfirmDialog 改为 inherits Rectangle,手写 backdrop(点击关闭)+ content card(Close 按钮调 close-dialog 回调)\n- app.slint:新增 show-about bool 属性;Sidebar About 点击同时触发 about-clicked + show-about=true;最外层 if root.show-about: AboutDialog 渲染\n- 删掉 selection==About 的右栏 marker(现在 About 是模态,不再需要)
This commit is contained in:
2026-09-19 19:40:16 +08:00
parent cd67fe9e8c
commit d2de452c53
2 changed files with 195 additions and 116 deletions
+165 -102
View File
@@ -1,79 +1,120 @@
// AGENTS.md §54 modals. C3 only ships the About dialog (called from the
// sidebar) and a generic confirm dialog template; full delete-confirm flow
// lands in Phase 5 once `AppConfig` persistence is wired.
// AGENTS.md §54 modals.
//
// Phase 7 C3 fix: the original `inherits Dialog` (a Slint
// PopupWindow-derived type) was instantiated inline inside
// MainWindow. PopupWindow expects to be `.show()`n via the Rust
// runtime; without that, it stays "declared but never properly
// popped up", and its `close()` no-op'd — the Close button became
// inert and the user had to force-kill the process.
//
// Fix: make AboutDialog an inline Rectangle overlay backed by a
// `visible` property toggled from Rust. A full-window dimmed
// backdrop catches outside-clicks to dismiss; the content card
// stays interactive. Same wiring pattern the rest of the UI uses
// (Slint `if root.show-about: AboutDialog { ... }`).
export component AboutDialog inherits Dialog {
export component AboutDialog inherits Rectangle {
in property <string> version-string: "v0.1.0";
in property <string> build-date: "2026-09-19";
// Theme-aware color tokens (Dark defaults; C4 will widen).
in property <color> bg-color: #28292d;
in property <color> text-primary-color: #e6e6e6;
in property <color> text-secondary-color: #a0a0a0;
in property <color> accent-color: #4ea1ff;
in property <color> button-bg: #2f3035;
in property <color> button-border: #3a3b40;
in property <color> backdrop-color: #000000;
title: "About Gotify Client";
background: bg-color;
callback close-dialog();
VerticalLayout {
padding: 24px;
spacing: 12px;
width: 360px;
background: transparent;
Text {
text: "Windows Gotify Client (Rust)";
color: text-primary-color;
font-size: 16px;
font-weight: 700;
// Backdrop: full-window dimmed layer that swallows clicks so
// clicking outside the card dismisses the dialog.
Rectangle {
x: 0;
y: 0;
width: 100%;
height: 100%;
background: backdrop-color;
opacity: 0.55;
TouchArea {
clicked => {
root.close-dialog();
}
}
Text {
text: version-string;
color: accent-color;
font-size: 13px;
}
Text {
text: "Built " + build-date;
color: text-secondary-color;
font-size: 11px;
}
Rectangle { height: 8px; }
Text {
text: "Lightweight, modern, Windows-native Gotify\ndesktop client. Phase 2 of 8.";
color: text-secondary-color;
font-size: 12px;
wrap: word-wrap;
}
Rectangle { height: 12px; }
}
// OK button.
Rectangle {
width: 100px;
height: 32px;
background: button-bg;
border-color: button-border;
border-width: 1px;
border-radius: 6px;
x: parent.width - self.width;
// Card: 360x240-ish centered modal content.
Rectangle {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
width: 380px;
height: 280px;
background: bg-color;
border-color: button-border;
border-width: 1px;
border-radius: 12px;
VerticalLayout {
padding: 24px;
spacing: 10px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: "Close";
text: "Windows Gotify Client (Rust)";
color: text-primary-color;
font-size: 12px;
font-size: 16px;
font-weight: 700;
}
TouchArea {
clicked => {
root.close();
Text {
text: version-string;
color: accent-color;
font-size: 13px;
}
Text {
text: "Built " + build-date;
color: text-secondary-color;
font-size: 11px;
}
Rectangle { height: 8px; }
Text {
text: "Lightweight, modern, Windows-native Gotify\ndesktop client. Phase 1–8 complete.";
color: text-secondary-color;
font-size: 12px;
wrap: word-wrap;
}
Rectangle { height: 8px; }
// Close button row.
HorizontalLayout {
alignment: end;
Rectangle {
width: 100px;
height: 32px;
background: button-bg;
border-color: button-border;
border-width: 1px;
border-radius: 6px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: "Close";
color: text-primary-color;
font-size: 12px;
}
// TouchArea last so it sits on top of the Text.
TouchArea {
clicked => {
root.close-dialog();
}
}
}
}
}
}
}
export component ConfirmDialog inherits Dialog {
export component ConfirmDialog inherits Rectangle {
in property <string> title-text: "Confirm";
in property <string> message: "Are you sure?";
in property <string> confirm-label: "Confirm";
@@ -88,65 +129,87 @@ export component ConfirmDialog inherits Dialog {
in property <color> danger-color: #f44336;
in property <color> button-bg: #2f3035;
in property <color> button-border: #3a3b40;
in property <color> backdrop-color: #000000;
title: title-text;
background: bg-color;
background: transparent;
VerticalLayout {
padding: 24px;
spacing: 16px;
width: 360px;
Text {
text: message;
color: text-primary-color;
font-size: 13px;
wrap: word-wrap;
Rectangle {
x: 0;
y: 0;
width: 100%;
height: 100%;
background: backdrop-color;
opacity: 0.55;
TouchArea {
clicked => {
root.cancelled();
}
}
}
HorizontalLayout {
spacing: 12px;
Rectangle {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
width: 380px;
height: 180px;
background: bg-color;
border-color: button-border;
border-width: 1px;
border-radius: 12px;
Rectangle { horizontal-stretch: 1; }
VerticalLayout {
padding: 24px;
spacing: 16px;
Rectangle {
width: 90px;
height: 32px;
background: button-bg;
border-color: button-border;
border-width: 1px;
border-radius: 6px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: cancel-label;
color: text-primary-color;
font-size: 12px;
}
TouchArea {
clicked => {
root.cancelled();
root.close();
Text {
text: message;
color: text-primary-color;
font-size: 13px;
wrap: word-wrap;
}
HorizontalLayout {
spacing: 12px;
Rectangle { horizontal-stretch: 1; }
Rectangle {
width: 90px;
height: 32px;
background: button-bg;
border-color: button-border;
border-width: 1px;
border-radius: 6px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: cancel-label;
color: text-primary-color;
font-size: 12px;
}
TouchArea {
clicked => {
root.cancelled();
}
}
}
}
Rectangle {
width: 90px;
height: 32px;
background: danger-color;
border-radius: 6px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: confirm-label;
color: #ffffff;
font-size: 12px;
font-weight: 600;
}
TouchArea {
clicked => {
root.confirmed();
root.close();
Rectangle {
width: 90px;
height: 32px;
background: danger-color;
border-radius: 6px;
Text {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
text: confirm-label;
color: #ffffff;
font-size: 12px;
font-weight: 600;
}
TouchArea {
clicked => {
root.confirmed();
}
}
}
}