// Main window + sidebar (AGENTS.md §6, §7). // // Phase 7 C4: // - UI 字符串全部翻译为简体中文(AGENTS.md §55:默认中文)。 // - 优先级过滤从 Slint 侧 visible 谓词改为 Rust 侧重新构建 // filtered VecModel(参见 message-list.slint 的注释)。 // - 新增 `priority-filter-changed(int)` 回调,Sidebar 切换过滤 // 时通知 Rust 重建 filtered model。 import { AppearanceMode } from "./theme.slint"; import { NavItem, SectionLabel } from "./components.slint"; import { MessageItem, Priority } from "./message-card.slint"; import { MessageList } from "./message-list.slint"; import { EmptyState } from "./empty-state.slint"; import { SettingsPage } from "./settings-page.slint"; import { AboutDialog } from "./dialogs.slint"; export enum SidebarSelection { All, Settings, About, } export component MainWindow inherits Window { title: "Gotify"; preferred-width: 960px; preferred-height: 680px; min-width: 760px; min-height: 520px; in-out property theme: AppearanceMode.Dark; out property is-dark: theme != AppearanceMode.Light; out property bg: is-dark ? #1f2024 : #f5f6f8; out property surface: is-dark ? #28292d : #ffffff; out property surface-elev: is-dark ? #2f3035 : #fafbfc; out property border: is-dark ? #3a3b40 : #e4e6ea; out property text-primary: is-dark ? #e6e6e6 : #1a1a1a; out property text-second: is-dark ? #a0a0a0 : #5c5c5c; out property text-muted: is-dark ? #6a6a6a : #9a9a9a; out property accent: is-dark ? #4ea1ff : #0a66c2; out property success: #4caf50; out property warning: #ff9800; out property danger: #f44336; out property priority-low: #6a6a6a; out property priority-normal: is-dark ? #4ea1ff : #0a66c2; out property priority-high: #ff9800; out property priority-max: #f44336; background: bg; in-out property selection: SidebarSelection.All; // ── Phase 7 C4 fix: AboutDialog overlay (see dialogs.slint header) ── in-out property show-about: false; callback close-about(); // ── 消息 + 选择 ── in-out property <[MessageItem]> messages; in-out property selected-message-index: -1; callback test-notification-clicked(); callback message-card-clicked(int); // ── Phase 7 C4: 优先级过滤。sidebar 的高亮状态由此属性驱动,但 // 真正的列表过滤已搬到 Rust 侧(每次切换重建 filtered VecModel, // 避免 Slint `visible: false` 在 for 循环里不折叠空间导致的布局 // bug)。0=全部, 1=低, 2=普通, 3=高, 4=最高 ── in-out property priority-filter: 0; callback priority-filter-changed(int); // ── 设置 / About / 窗口状态 ── in-out property appearance-index: 2; callback about-clicked(); callback appearance-changed-persist(int); in-out property desktop-notify: true; in-out property sound: true; in-out property notify-high: true; in-out property notify-max: true; in-out property start-with-windows: false; in-out property start-minimized: false; in-out property close-to-tray: true; callback setting-toggled(string, bool); in-out property is-connected: false; in-out property status-text: "未连接"; callback test-connection-requested(); // Phase 7 C5: active-server summary forwarded into SettingsPage // so the Connection section reflects the real AppConfig instead // of the old hardcoded "Home / NAS" placeholders. in property active-server-name: "(未配置服务器)"; in property active-server-url: ""; in property active-server-enabled: false; HorizontalLayout { // §7 Sidebar width: 220-240px. Rectangle { width: 232px; background: surface; VerticalLayout { padding: 20px; spacing: 6px; Text { text: "Gotify"; color: text-primary; font-size: 18px; font-weight: 700; } Text { text: "Windows 客户端 · Phase 7"; color: text-muted; font-size: 10px; } Rectangle { height: 12px; } // Live status row (Phase 3 C2): dot + text. HorizontalLayout { spacing: 6px; alignment: center; Rectangle { width: 8px; height: 8px; border-radius: 4px; background: is-connected ? success : text-muted; } Text { text: is-connected ? "已连接" : "未连接"; color: is-connected ? success : text-muted; font-size: 11px; font-weight: 600; } } Rectangle { height: 12px; } SectionLabel { label: "消息"; text-color: text-muted; } NavItem { label: "全部"; selected: root.selection == SidebarSelection.All && root.priority-filter == 0; clicked => { root.selection = SidebarSelection.All; root.priority-filter = 0; root.priority-filter-changed(0); } } Rectangle { height: 12px; } SectionLabel { label: "服务器"; text-color: text-muted; } Text { text: "(未配置服务器)"; color: text-muted; font-size: 11px; } Rectangle { height: 12px; } SectionLabel { label: "过滤"; text-color: text-muted; } NavItem { label: "最高"; selected: root.selection == SidebarSelection.All && root.priority-filter == 4; has-accent: true; accent-color: priority-max; clicked => { root.selection = SidebarSelection.All; root.priority-filter = 4; root.priority-filter-changed(4); } } NavItem { label: "高"; selected: root.selection == SidebarSelection.All && root.priority-filter == 3; has-accent: true; accent-color: priority-high; clicked => { root.selection = SidebarSelection.All; root.priority-filter = 3; root.priority-filter-changed(3); } } NavItem { label: "普通"; selected: root.selection == SidebarSelection.All && root.priority-filter == 2; has-accent: true; accent-color: priority-normal; clicked => { root.selection = SidebarSelection.All; root.priority-filter = 2; root.priority-filter-changed(2); } } NavItem { label: "低"; selected: root.selection == SidebarSelection.All && root.priority-filter == 1; has-accent: true; accent-color: priority-low; clicked => { root.selection = SidebarSelection.All; root.priority-filter = 1; root.priority-filter-changed(1); } } Rectangle { height: 12px; } SectionLabel { label: "应用"; text-color: text-muted; } NavItem { label: "设置"; selected: root.selection == SidebarSelection.Settings; clicked => { root.selection = SidebarSelection.Settings; } } NavItem { label: "关于"; selected: root.selection == SidebarSelection.About; clicked => { root.selection = SidebarSelection.About; root.about-clicked(); root.show-about = true; } } } } // Right pane — driven by `selection`. Rectangle { background: bg; HorizontalLayout { padding: 0; spacing: 0; // 消息列(All 视图时可见)。 Rectangle { background: bg; horizontal-stretch: 1; visible: root.selection == SidebarSelection.All; VerticalLayout { padding: 24px; spacing: 4px; Text { text: messages.length == 0 ? "消息" : "消息 · " + messages.length; color: text-primary; font-size: 18px; font-weight: 700; } Text { text: status-text; color: text-muted; font-size: 11px; } Rectangle { height: 12px; } if messages.length > 0: MessageList { messages: messages; selected-index: root.selected-message-index; vertical-stretch: 1; message-clicked(idx) => { root.selected-message-index = idx; root.message-card-clicked(idx); } } if messages.length == 0: EmptyState { vertical-stretch: 1; test-notification => { root.test-notification-clicked(); } } } } // 详情面板:选中消息且在 All 视图时显示。 if root.selection == SidebarSelection.All && root.selected-message-index >= 0 && root.selected-message-index < root.messages.length: Rectangle { width: 360px; background: surface-elev; border-color: border; border-width: 1px; VerticalLayout { padding: 20px; spacing: 8px; HorizontalLayout { spacing: 8px; alignment: center; Rectangle { width: 10px; height: 10px; border-radius: 5px; background: root.messages[root.selected-message-index].priority == Priority.Low ? priority-low : root.messages[root.selected-message-index].priority == Priority.Normal ? priority-normal : root.messages[root.selected-message-index].priority == Priority.High ? priority-high : priority-max; } Text { text: root.messages[root.selected-message-index].app_name; color: text-second; font-size: 12px; font-weight: 600; horizontal-stretch: 1; } Text { text: root.messages[root.selected-message-index].timestamp_unix == 0 ? "" : "id #" + root.messages[root.selected-message-index].id; color: text-muted; font-size: 11px; } } Text { text: root.messages[root.selected-message-index].title != "" ? root.messages[root.selected-message-index].title : root.messages[root.selected-message-index].body; color: text-primary; font-size: 16px; font-weight: 700; wrap: word-wrap; } Text { text: root.messages[root.selected-message-index].relative_time; color: text-muted; font-size: 11px; } Rectangle { height: 1px; background: border; } Text { text: root.messages[root.selected-message-index].body; color: text-primary; font-size: 13px; wrap: word-wrap; vertical-stretch: 1; } if root.messages[root.selected-message-index].has_click_url: Rectangle { height: 32px; background: accent; border-radius: 6px; TouchArea { clicked => { root.message-card-clicked(root.selected-message-index); } } Text { x: 0; y: 0; width: 100%; height: 100%; text: "打开链接 ↗"; color: white; font-size: 12px; font-weight: 600; horizontal-alignment: center; vertical-alignment: center; } } } } } if root.selection == SidebarSelection.Settings: SettingsPage { appearance-index: root.appearance-index; desktop-notify: root.desktop-notify; sound: root.sound; notify-high: root.notify-high; notify-max: root.notify-max; start-with-windows: root.start-with-windows; start-minimized: root.start-minimized; close-to-tray: root.close-to-tray; // Phase 7 C5: forward active server config so the // Connection section shows the real entry instead // of the hardcoded "Home / NAS" placeholders. active-server-name: root.active-server-name; active-server-url: root.active-server-url; active-server-enabled: root.active-server-enabled; appearance-changed(idx) => { root.appearance-index = idx; root.theme = idx == 0 ? AppearanceMode.System : idx == 1 ? AppearanceMode.Light : AppearanceMode.Dark; root.appearance-changed-persist(idx); } test-connection-requested => { root.test-connection-requested(); } toggle-persist(label, value) => { root.setting-toggled(label, value); } } } } if root.show-about: AboutDialog { close-dialog => { root.show-about = false; } } }