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:
+30
-14
@@ -53,6 +53,16 @@ export component MainWindow inherits Window {
|
|||||||
|
|
||||||
in-out property <SidebarSelection> selection: SidebarSelection.All;
|
in-out property <SidebarSelection> selection: SidebarSelection.All;
|
||||||
|
|
||||||
|
// ── Phase 7 C3 fix: AboutDialog overlay. The previous version
|
||||||
|
// inherited Slint's `Dialog` (a PopupWindow-derived type) and
|
||||||
|
// instantiated it inline; without a runtime `.show()` call,
|
||||||
|
// close() was a no-op and the Close button became inert. We
|
||||||
|
// now drive the dialog with a plain boolean (toggled from Rust)
|
||||||
|
// and render it as a manual Rectangle overlay in `if
|
||||||
|
// root.show-about: AboutDialog { … }`. See dialogs.slint. ──
|
||||||
|
in-out property <bool> show-about: false;
|
||||||
|
callback close-about();
|
||||||
|
|
||||||
// ── Phase 2 C2: mock messages + selection ────────────────────
|
// ── Phase 2 C2: mock messages + selection ────────────────────
|
||||||
in-out property <[MessageItem]> messages;
|
in-out property <[MessageItem]> messages;
|
||||||
in-out property <int> selected-message-index: -1;
|
in-out property <int> selected-message-index: -1;
|
||||||
@@ -225,6 +235,13 @@ export component MainWindow inherits Window {
|
|||||||
clicked => {
|
clicked => {
|
||||||
root.selection = SidebarSelection.About;
|
root.selection = SidebarSelection.About;
|
||||||
root.about-clicked();
|
root.about-clicked();
|
||||||
|
// Phase 7 C3 fix: surface the (now
|
||||||
|
// Rectangle-overlay) About dialog. The
|
||||||
|
// Rust `about-clicked` callback still runs
|
||||||
|
// for telemetry; the actual show happens
|
||||||
|
// here so the dialog opens the moment the
|
||||||
|
// user clicks the nav row.
|
||||||
|
root.show-about = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,21 +432,20 @@ export component MainWindow inherits Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// About entry stays as a simple right-pane marker; the
|
// Phase 7 C3 fix: the About page is no longer a separate
|
||||||
// modal AboutDialog overlay is wired through AboutDialog in
|
// right-pane marker. Clicking the sidebar About row opens
|
||||||
// this component (visibility toggled via `about-clicked`).
|
// a modal dialog overlay (see `if root.show-about:
|
||||||
if root.selection == SidebarSelection.About: Rectangle {
|
// AboutDialog { … }` further down), so there's nothing
|
||||||
background: bg;
|
// to show in the right pane for `selection == About`.
|
||||||
Text {
|
|
||||||
x: 24px;
|
|
||||||
y: 24px;
|
|
||||||
text: "About — see the dialog.";
|
|
||||||
color: text-muted;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AboutDialog {}
|
// Phase 7 C3 fix: conditional About overlay driven by
|
||||||
|
// `show-about`. See the comment on the property above for why
|
||||||
|
// we no longer use Slint's `Dialog` type here.
|
||||||
|
if root.show-about: AboutDialog {
|
||||||
|
close-dialog => {
|
||||||
|
root.show-about = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+165
-102
@@ -1,79 +1,120 @@
|
|||||||
// AGENTS.md §54 modals. C3 only ships the About dialog (called from the
|
// AGENTS.md §54 modals.
|
||||||
// sidebar) and a generic confirm dialog template; full delete-confirm flow
|
//
|
||||||
// lands in Phase 5 once `AppConfig` persistence is wired.
|
// 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> version-string: "v0.1.0";
|
||||||
in property <string> build-date: "2026-09-19";
|
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> bg-color: #28292d;
|
||||||
in property <color> text-primary-color: #e6e6e6;
|
in property <color> text-primary-color: #e6e6e6;
|
||||||
in property <color> text-secondary-color: #a0a0a0;
|
in property <color> text-secondary-color: #a0a0a0;
|
||||||
in property <color> accent-color: #4ea1ff;
|
in property <color> accent-color: #4ea1ff;
|
||||||
in property <color> button-bg: #2f3035;
|
in property <color> button-bg: #2f3035;
|
||||||
in property <color> button-border: #3a3b40;
|
in property <color> button-border: #3a3b40;
|
||||||
|
in property <color> backdrop-color: #000000;
|
||||||
|
|
||||||
title: "About Gotify Client";
|
callback close-dialog();
|
||||||
background: bg-color;
|
|
||||||
|
|
||||||
VerticalLayout {
|
background: transparent;
|
||||||
padding: 24px;
|
|
||||||
spacing: 12px;
|
|
||||||
width: 360px;
|
|
||||||
|
|
||||||
Text {
|
// Backdrop: full-window dimmed layer that swallows clicks so
|
||||||
text: "Windows Gotify Client (Rust)";
|
// clicking outside the card dismisses the dialog.
|
||||||
color: text-primary-color;
|
Rectangle {
|
||||||
font-size: 16px;
|
x: 0;
|
||||||
font-weight: 700;
|
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.
|
// Card: 360x240-ish centered modal content.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 100px;
|
x: (parent.width - self.width) / 2;
|
||||||
height: 32px;
|
y: (parent.height - self.height) / 2;
|
||||||
background: button-bg;
|
width: 380px;
|
||||||
border-color: button-border;
|
height: 280px;
|
||||||
border-width: 1px;
|
background: bg-color;
|
||||||
border-radius: 6px;
|
border-color: button-border;
|
||||||
x: parent.width - self.width;
|
border-width: 1px;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
VerticalLayout {
|
||||||
|
padding: 24px;
|
||||||
|
spacing: 10px;
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
x: (parent.width - self.width) / 2;
|
text: "Windows Gotify Client (Rust)";
|
||||||
y: (parent.height - self.height) / 2;
|
|
||||||
text: "Close";
|
|
||||||
color: text-primary-color;
|
color: text-primary-color;
|
||||||
font-size: 12px;
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
TouchArea {
|
Text {
|
||||||
clicked => {
|
text: version-string;
|
||||||
root.close();
|
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> title-text: "Confirm";
|
||||||
in property <string> message: "Are you sure?";
|
in property <string> message: "Are you sure?";
|
||||||
in property <string> confirm-label: "Confirm";
|
in property <string> confirm-label: "Confirm";
|
||||||
@@ -88,65 +129,87 @@ export component ConfirmDialog inherits Dialog {
|
|||||||
in property <color> danger-color: #f44336;
|
in property <color> danger-color: #f44336;
|
||||||
in property <color> button-bg: #2f3035;
|
in property <color> button-bg: #2f3035;
|
||||||
in property <color> button-border: #3a3b40;
|
in property <color> button-border: #3a3b40;
|
||||||
|
in property <color> backdrop-color: #000000;
|
||||||
|
|
||||||
title: title-text;
|
background: transparent;
|
||||||
background: bg-color;
|
|
||||||
|
|
||||||
VerticalLayout {
|
Rectangle {
|
||||||
padding: 24px;
|
x: 0;
|
||||||
spacing: 16px;
|
y: 0;
|
||||||
width: 360px;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
Text {
|
background: backdrop-color;
|
||||||
text: message;
|
opacity: 0.55;
|
||||||
color: text-primary-color;
|
TouchArea {
|
||||||
font-size: 13px;
|
clicked => {
|
||||||
wrap: word-wrap;
|
root.cancelled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HorizontalLayout {
|
Rectangle {
|
||||||
spacing: 12px;
|
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 {
|
Text {
|
||||||
width: 90px;
|
text: message;
|
||||||
height: 32px;
|
color: text-primary-color;
|
||||||
background: button-bg;
|
font-size: 13px;
|
||||||
border-color: button-border;
|
wrap: word-wrap;
|
||||||
border-width: 1px;
|
}
|
||||||
border-radius: 6px;
|
|
||||||
Text {
|
HorizontalLayout {
|
||||||
x: (parent.width - self.width) / 2;
|
spacing: 12px;
|
||||||
y: (parent.height - self.height) / 2;
|
|
||||||
text: cancel-label;
|
Rectangle { horizontal-stretch: 1; }
|
||||||
color: text-primary-color;
|
|
||||||
font-size: 12px;
|
Rectangle {
|
||||||
}
|
width: 90px;
|
||||||
TouchArea {
|
height: 32px;
|
||||||
clicked => {
|
background: button-bg;
|
||||||
root.cancelled();
|
border-color: button-border;
|
||||||
root.close();
|
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 {
|
||||||
Rectangle {
|
width: 90px;
|
||||||
width: 90px;
|
height: 32px;
|
||||||
height: 32px;
|
background: danger-color;
|
||||||
background: danger-color;
|
border-radius: 6px;
|
||||||
border-radius: 6px;
|
Text {
|
||||||
Text {
|
x: (parent.width - self.width) / 2;
|
||||||
x: (parent.width - self.width) / 2;
|
y: (parent.height - self.height) / 2;
|
||||||
y: (parent.height - self.height) / 2;
|
text: confirm-label;
|
||||||
text: confirm-label;
|
color: #ffffff;
|
||||||
color: #ffffff;
|
font-size: 12px;
|
||||||
font-size: 12px;
|
font-weight: 600;
|
||||||
font-weight: 600;
|
}
|
||||||
}
|
TouchArea {
|
||||||
TouchArea {
|
clicked => {
|
||||||
clicked => {
|
root.confirmed();
|
||||||
root.confirmed();
|
}
|
||||||
root.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user