History manager (#26369)
While working on implementing `add_recent_documents` for Windows, I found that the process is significantly more complex compared to macOS. On macOS, simply registering the `add_recent_documents` function is enough, as the system handles everything automatically. On Windows, however, there are two cases to consider: - **Files opened by the app**: These appear in the "Recent" section (as shown in the screenshot, "test.txt") and are managed automatically by Windows (by setting windows registry), similar to macOS.  - **Folders opened by the app**: This is more complicated because Windows does not handle it automatically, requiring the application to track opened folders manually. To address this, this PR introduces a `History Manager` along with `HistoryManagerEvent::Update` and `HistoryManagerEvent::Delete` events to simplify the process of managing recently opened folders. https://github.com/user-attachments/assets/a2581c15-7653-4faf-96b0-7c48ab1dcc8d Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
5734ffbb18
commit
a5fe6d1e61
14 changed files with 482 additions and 66 deletions
|
@ -14,10 +14,7 @@ use itertools::Itertools;
|
|||
use parking_lot::RwLock;
|
||||
use smallvec::SmallVec;
|
||||
use windows::{
|
||||
UI::{
|
||||
StartScreen::{JumpList, JumpListItem},
|
||||
ViewManagement::UISettings,
|
||||
},
|
||||
UI::ViewManagement::UISettings,
|
||||
Win32::{
|
||||
Foundation::*,
|
||||
Graphics::{
|
||||
|
@ -52,7 +49,7 @@ pub(crate) struct WindowsPlatform {
|
|||
pub(crate) struct WindowsPlatformState {
|
||||
callbacks: PlatformCallbacks,
|
||||
menus: Vec<OwnedMenu>,
|
||||
dock_menu_actions: Vec<Box<dyn Action>>,
|
||||
jump_list: JumpList,
|
||||
// NOTE: standard cursor handles don't need to close.
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
}
|
||||
|
@ -70,12 +67,12 @@ struct PlatformCallbacks {
|
|||
impl WindowsPlatformState {
|
||||
fn new() -> Self {
|
||||
let callbacks = PlatformCallbacks::default();
|
||||
let dock_menu_actions = Vec::new();
|
||||
let jump_list = JumpList::new();
|
||||
let current_cursor = load_cursor(CursorStyle::Arrow);
|
||||
|
||||
Self {
|
||||
callbacks,
|
||||
dock_menu_actions,
|
||||
jump_list,
|
||||
current_cursor,
|
||||
menus: Vec::new(),
|
||||
}
|
||||
|
@ -189,9 +186,10 @@ impl WindowsPlatform {
|
|||
let mut lock = self.state.borrow_mut();
|
||||
if let Some(mut callback) = lock.callbacks.app_menu_action.take() {
|
||||
let Some(action) = lock
|
||||
.dock_menu_actions
|
||||
.jump_list
|
||||
.dock_menus
|
||||
.get(action_idx)
|
||||
.map(|action| action.boxed_clone())
|
||||
.map(|dock_menu| dock_menu.action.boxed_clone())
|
||||
else {
|
||||
lock.callbacks.app_menu_action = Some(callback);
|
||||
log::error!("Dock menu for index {action_idx} not found");
|
||||
|
@ -254,33 +252,35 @@ impl WindowsPlatform {
|
|||
false
|
||||
}
|
||||
|
||||
fn configure_jump_list(&self, menus: Vec<MenuItem>) -> Result<()> {
|
||||
let jump_list = JumpList::LoadCurrentAsync()?.get()?;
|
||||
let items = jump_list.Items()?;
|
||||
items.Clear()?;
|
||||
fn set_dock_menus(&self, menus: Vec<MenuItem>) {
|
||||
let mut actions = Vec::new();
|
||||
for item in menus.into_iter() {
|
||||
let item = match item {
|
||||
MenuItem::Separator => JumpListItem::CreateSeparator()?,
|
||||
MenuItem::Submenu(_) => {
|
||||
log::error!("Set `MenuItemSubmenu` for dock menu on Windows is not supported.");
|
||||
continue;
|
||||
}
|
||||
MenuItem::Action { name, action, .. } => {
|
||||
let idx = actions.len();
|
||||
actions.push(action.boxed_clone());
|
||||
let item_args = format!("--dock-action {}", idx);
|
||||
JumpListItem::CreateWithArguments(
|
||||
&HSTRING::from(item_args),
|
||||
&HSTRING::from(name.as_ref()),
|
||||
)?
|
||||
}
|
||||
};
|
||||
items.Append(&item)?;
|
||||
}
|
||||
jump_list.SaveAsync()?.get()?;
|
||||
self.state.borrow_mut().dock_menu_actions = actions;
|
||||
Ok(())
|
||||
menus.into_iter().for_each(|menu| {
|
||||
if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
|
||||
actions.push(dock_menu);
|
||||
}
|
||||
});
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.jump_list.dock_menus = actions;
|
||||
update_jump_list(&lock.jump_list).log_err();
|
||||
}
|
||||
|
||||
fn update_jump_list(
|
||||
&self,
|
||||
menus: Vec<MenuItem>,
|
||||
entries: Vec<SmallVec<[PathBuf; 2]>>,
|
||||
) -> Vec<SmallVec<[PathBuf; 2]>> {
|
||||
let mut actions = Vec::new();
|
||||
menus.into_iter().for_each(|menu| {
|
||||
if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
|
||||
actions.push(dock_menu);
|
||||
}
|
||||
});
|
||||
let mut lock = self.state.borrow_mut();
|
||||
lock.jump_list.dock_menus = actions;
|
||||
lock.jump_list.recent_workspaces = entries;
|
||||
update_jump_list(&lock.jump_list)
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ impl Platform for WindowsPlatform {
|
|||
}
|
||||
|
||||
fn set_dock_menu(&self, menus: Vec<MenuItem>, _keymap: &Keymap) {
|
||||
self.configure_jump_list(menus).log_err();
|
||||
self.set_dock_menus(menus);
|
||||
}
|
||||
|
||||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
|
||||
|
@ -673,6 +673,14 @@ impl Platform for WindowsPlatform {
|
|||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_jump_list(
|
||||
&self,
|
||||
menus: Vec<MenuItem>,
|
||||
entries: Vec<SmallVec<[PathBuf; 2]>>,
|
||||
) -> Vec<SmallVec<[PathBuf; 2]>> {
|
||||
self.update_jump_list(menus, entries)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsPlatform {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue