diff --git a/CHANGELOG.md b/CHANGELOG.md index 8420aea..24f52ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 deletion (#33, #43, #46, #48). - Windows: six-second reversible deletion for individual and selected tasks (#47). +- Windows: the shared app accelerators now work window-wide — Ctrl+1…4 + switch smart views, Ctrl+N focuses quick add, Ctrl+F focuses search, + Ctrl+Shift+C toggles completed tasks, and Ctrl+Shift+N / Ctrl+O create + or open a database. - Linux: the About dialog now carries the same website and Report-an-Issue links as macOS and Windows (GtkAboutDialog cannot hold a second link, so the dialog is built on the plain family layout); Windows and Linux diff --git a/README.md b/README.md index 41e29cb..380b844 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,8 @@ watch architecture. actions and a one-step undo banner for deletes; ↑/↓ navigation, Return expands the row in place (title, date/time chips, notes), Esc collapses; completed tasks fold under a collapsible header. - Complete on macOS today; Linux ships the undo banner and the - keyboard flow, Windows is catching up + The full set — including the shared app accelerators (Ctrl/⌘+1…4, + N, F, ⇧C) — ships on all three platforms ([status](docs/UX-BENCHMARKS.md)). - **Due-task notifications** — a 60 s poll plus a startup check, deduped, capped at three separate banners (more → one summary). diff --git a/README.zh-CN.md b/README.zh-CN.md index 185f4d3..b886fb1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -20,7 +20,7 @@ Taskly 构建在 [Rivet](https://github.com/turinglambdaai/rivet) 之上:全 - **Reminders 风格界面**——智能视图(今天 / 计划 / 全部 / 已完成),emoji 图标 + 12 色板的自定义清单,明暗主题跟随系统,中英双语实时切换。 - **自然语言快速添加**——输入 `明天买菜` 或 `standup @9am +1d`,回车前就能在实时预览 chip 里看到解析出的排程。 -- **对齐 Reminders 的交互**——⌘/⇧ 多选、批量操作、删除一键撤销横幅;↑/↓ 导航,Return 行内展开(标题、日期/时间 chip、备注),Esc 收起;已完成任务收进可折叠分组。macOS 已全量对齐;Linux 已有撤销横幅与键盘流,Windows 正在补齐([进度](docs/UX-BENCHMARKS.md))。 +- **对齐 Reminders 的交互**——⌘/⇧ 多选、批量操作、删除一键撤销横幅;↑/↓ 导航,Return 行内展开(标题、日期/时间 chip、备注),Esc 收起;已完成任务收进可折叠分组。全套能力(含共享应用快捷键 Ctrl/⌘+1…4、N、F、⇧C)已在三平台齐装([进度](docs/UX-BENCHMARKS.md))。 - **到期提醒通知**——60 秒轮询 + 启动检查,去重,超过三条合并为一条汇总。 - **单文件数据**——所有任务存在一个 SQLite 文件(`~/.taskly/tasks.db`,WAL),丢进 iCloud/OneDrive/Dropbox 即可同步。格式文档化且稳定:[DATA-FORMAT](shared/spec/DATA-FORMAT.md)。 - **面向 agent 的 CLI**——`taskly list|add|update|done|rm|search|mklist…`,`--json` 输出稳定、退出码固定、跨平台字节级一致(63 例 golden 套件钉死)。规格:[CLI-SPEC](shared/spec/CLI-SPEC.md)。 diff --git a/docs/UX-BENCHMARKS.md b/docs/UX-BENCHMARKS.md index d7c888b..adc6bb1 100644 --- a/docs/UX-BENCHMARKS.md +++ b/docs/UX-BENCHMARKS.md @@ -31,13 +31,15 @@ Things 的灵魂是把"什么时候做"压进四个默认视图,用 **start da Things 几乎一切操作无需鼠标:全局快速录入、⌘N 新任务、⌘F 搜索、↑↓ 遍历、 ⌘1…4 切视图、Enter 展开、Esc 收起。 -- ✅ 已落地(Linux 宿主):Ctrl+1…4 切视图、Ctrl+N 聚焦快速添加、Ctrl+F 聚焦 - 搜索、Ctrl+Shift+C 切换显示已完成(PRODUCT-SPEC §8 的加速器全量接线); - Esc 阶梯(编辑器 → 搜索 → 放行)。 +- ✅ 已落地(三平台):Ctrl+1…4 切视图、Ctrl+N 聚焦快速添加、Ctrl+F 聚焦 + 搜索、Ctrl+Shift+C 切换显示已完成——Linux 宿主全量接线并带 Esc 阶梯 + (编辑器 → 搜索 → 放行);macOS 同一组命令走 ⌘ 系快捷键(⌘1…4/⌘N/ + ⌘F/⇧⌘C);Windows 用 KeyboardAccelerator 全窗口挂载同一契约,菜单上 + 另标 Ctrl+Shift+N / Ctrl+O 数据库快捷键提示。 - ✅ Linux 任务列表:GTK 原生多选支持 Ctrl/Shift 点选和方向键焦点移动; Enter(或双击)激活选中行的行内编辑,Esc 先关闭编辑、再清除任务选择。 任务列表轮询重建时按任务 ID 恢复选中状态。 -- 🔜 下一步:批量移动/完成/删除和跨视图选中策略仍须单独实现并实机验收。 +- ✅ 批量移动/完成/删除与跨视图选中策略已在三平台落地(详见 §2.4)。 ### 1.3 "安静"的哲学(quiet software) diff --git a/windows/MainWindow.xaml.cpp b/windows/MainWindow.xaml.cpp index 43eae6c..91066eb 100644 --- a/windows/MainWindow.xaml.cpp +++ b/windows/MainWindow.xaml.cpp @@ -616,6 +616,7 @@ MainWindow::MainWindow() { window->OnEffectiveThemeChanged(); } }); + InstallAppAccelerators(); InitializeBackendAsync(); } @@ -735,6 +736,11 @@ void MainWindow::ApplyLanguage() { MenuHelp().Title(t("menuHelp")); MenuNewDatabase().Text(t("menuNewDatabase")); MenuOpenDatabase().Text(t("menuOpenDatabase")); + // Hint-only: the actual accelerators are installed window-wide in + // InstallAppAccelerators (these two also have on-menu hints because + // the File menu rows exist to carry them). + MenuNewDatabase().KeyboardAcceleratorTextOverride(L"Ctrl+Shift+N"); + MenuOpenDatabase().KeyboardAcceleratorTextOverride(L"Ctrl+O"); MenuCloseDatabase().Text(t("menuCloseDatabase")); MenuExit().Text(t("menuExit")); MenuLangZh().Text(t("menuLangZh")); @@ -2496,6 +2502,10 @@ void MainWindow::OnSmartTileClick( auto const tag = sender.as().Tag(); auto const kind = static_cast(winrt::unbox_value(tag)); + SwitchSmartView(kind); +} + +void MainWindow::SwitchSmartView(ViewKind kind) { if (view_ == kind) { return; } @@ -2509,6 +2519,69 @@ void MainWindow::OnSmartTileClick( ReloadTasksAsync(); } +// Ctrl+Shift+C mirrors the checkbox without double-firing its handler: +// a programmatic IsChecked change raises Checked/Unchecked in WinUI, so +// the handler applies the state first and this only finishes the job if +// the event never ran. +void MainWindow::ToggleShowCompleted() { + auto const boxed = ShowCompletedCheck().IsChecked(); + bool const on = !(boxed != nullptr && boxed.Value()); + ShowCompletedCheck().IsChecked(on); + if (show_completed_ == on) return; + show_completed_ = on; + ReloadTasksAsync(); +} + +// PRODUCT-SPEC §8: the accelerators work window-wide without opening the +// menu. KeyboardAccelerators are scoped to the attached element's +// XamlRoot, so RootGrid covers the whole window; each accelerator calls +// the same code path its on-screen control uses. +void MainWindow::InstallAppAccelerators() { + using winrt::Microsoft::UI::Xaml::Input::KeyboardAccelerator; + using winrt::Windows::System::VirtualKey; + auto const control = winrt::Windows::System::VirtualKeyModifiers::Control; + auto const control_shift = + control | winrt::Windows::System::VirtualKeyModifiers::Shift; + auto attach = [&](VirtualKey key, + winrt::Windows::System::VirtualKeyModifiers mods, + auto&& invoked) { + KeyboardAccelerator acc{}; + acc.Modifiers(mods); + acc.Key(key); + acc.Invoked([invoked](auto const&, + winrt::Microsoft::UI::Xaml::Input:: + KeyboardAcceleratorInvokedEventArgs const& + args) { + args.Handled(true); + invoked(); + }); + RootGrid().KeyboardAccelerators().Append(acc); + }; + attach(VirtualKey::Number1, control, + [this] { SwitchSmartView(ViewKind::Today); }); + attach(VirtualKey::Number2, control, + [this] { SwitchSmartView(ViewKind::Planned); }); + attach(VirtualKey::Number3, control, + [this] { SwitchSmartView(ViewKind::All); }); + attach(VirtualKey::Number4, control, + [this] { SwitchSmartView(ViewKind::Completed); }); + attach(VirtualKey::N, control, [this] { + if (inline_task_id_.has_value()) CommitInlineTaskEditor(); + NewTaskBox().Focus(winrt::Microsoft::UI::Xaml::FocusState::Keyboard); + }); + attach(VirtualKey::F, control, + [this] { SearchBox().Focus(winrt::Microsoft::UI::Xaml::FocusState::Keyboard); }); + attach(VirtualKey::C, control_shift, [this] { ToggleShowCompleted(); }); + attach(VirtualKey::N, control_shift, [this] { + Microsoft::UI::Xaml::RoutedEventArgs args; + OnNewDatabase(nullptr, args); + }); + attach(VirtualKey::O, control, [this] { + Microsoft::UI::Xaml::RoutedEventArgs args; + OnOpenDatabase(nullptr, args); + }); +} + void MainWindow::OnUserListSelectionChanged( winrt::Windows::Foundation::IInspectable const&, Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const&) { diff --git a/windows/MainWindow.xaml.h b/windows/MainWindow.xaml.h index 1557215..094d5c5 100644 --- a/windows/MainWindow.xaml.h +++ b/windows/MainWindow.xaml.h @@ -100,6 +100,14 @@ struct MainWindow : MainWindowT { private: enum class ViewKind { Today, Planned, All, Completed, List }; + // PRODUCT-SPEC §8 app accelerators (Ctrl+1…4 / Ctrl+N / Ctrl+F / + // Ctrl+Shift+C / Ctrl+Shift+N / Ctrl+O). They reuse the command paths + // of the tiles and menu items; Windows has no View menu, so the + // accelerators are installed on the root element in code. + void InstallAppAccelerators(); + void SwitchSmartView(ViewKind kind); + void ToggleShowCompleted(); + static std::string ViewStringFor(ViewKind kind); winrt::fire_and_forget InitializeBackendAsync();