History

gpui(windows): Move jumplist creation to background thread (#47956)

This can block the main thread too much otherwise Release Notes: - N/A *or* Added/Fixed/Improved ...

Lukas Wirth committed 9 months ago

Commit 19765cd1 parent 28f04615 files · +77 −51Browse files

Unified · Split

5 files
  1. crates/gpui/src/app.rs +1 −1
  2. crates/gpui/src/platform.rs +2 −2
  3. crates/gpui/src/platform/windows/destination_list.rs +21 −17
  4. crates/gpui/src/platform/windows/platform.rs +26 −5
  5. crates/workspace/src/history_manager.rs +27 −26
crates/gpui/src/app.rs+1−1
@@ -2010,7 +2010,7 @@ impl App { · 2009 unmodified lines
2010 &self,2010 &self,
2011 menus: Vec<MenuItem>,2011 menus: Vec<MenuItem>,
2012 entries: Vec<SmallVec<[PathBuf; 2]>>,2012 entries: Vec<SmallVec<[PathBuf; 2]>>,
2013 ) -> Vec<SmallVec<[PathBuf; 2]>> {2013 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
2014 self.platform.update_jump_list(menus, entries)2014 self.platform.update_jump_list(menus, entries)
2015 }2015 }
2016 2016
crates/gpui/src/platform.rs+2−2
@@ -254,8 +254,8 @@ pub(crate) trait Platform: 'static { · 253 unmodified lines
254 &self,254 &self,
255 _menus: Vec<MenuItem>,255 _menus: Vec<MenuItem>,
256 _entries: Vec<SmallVec<[PathBuf; 2]>>,256 _entries: Vec<SmallVec<[PathBuf; 2]>>,
257 ) -> Vec<SmallVec<[PathBuf; 2]>> {257 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
258 Vec::new()258 Task::ready(Vec::new())
259 }259 }
260 fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);260 fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
261 fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);261 fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
crates/gpui/src/platform/windows/destination_list.rs+21−17
@@ -1,4 +1,4 @@
1use std::path::PathBuf;1use std::{path::PathBuf, sync::Arc};
2 2
3use itertools::Itertools;3use itertools::Itertools;
4use smallvec::SmallVec;4use smallvec::SmallVec;
@@ -20,25 +20,25 @@ use windows::{ · 15 unmodified lines
20 core::{GUID, HSTRING, Interface},20 core::{GUID, HSTRING, Interface},
21};21};
22 22
23use crate::{Action, MenuItem};23use crate::{Action, MenuItem, SharedString};
24 24
25pub(crate) struct JumpList {25pub(crate) struct JumpList {
26 pub(crate) dock_menus: Vec<DockMenuItem>,26 pub(crate) dock_menus: Vec<DockMenuItem>,
27 pub(crate) recent_workspaces: Vec<SmallVec<[PathBuf; 2]>>,27 pub(crate) recent_workspaces: Arc<[SmallVec<[PathBuf; 2]>]>,
28}28}
29 29
30impl JumpList {30impl JumpList {
31 pub(crate) fn new() -> Self {31 pub(crate) fn new() -> Self {
32 Self {32 Self {
33 dock_menus: Vec::new(),33 dock_menus: Vec::default(),
34 recent_workspaces: Vec::new(),34 recent_workspaces: Arc::default(),
35 }35 }
36 }36 }
37}37}
38 38
39pub(crate) struct DockMenuItem {39pub(crate) struct DockMenuItem {
40 pub(crate) name: String,40 pub(crate) name: SharedString,
41 pub(crate) description: String,41 pub(crate) description: SharedString,
42 pub(crate) action: Box<dyn Action>,42 pub(crate) action: Box<dyn Action>,
43}43}
44 44
@@ -46,11 +46,11 @@ impl DockMenuItem { · 1 unmodified line
46 pub(crate) fn new(item: MenuItem) -> anyhow::Result<Self> {46 pub(crate) fn new(item: MenuItem) -> anyhow::Result<Self> {
47 match item {47 match item {
48 MenuItem::Action { name, action, .. } => Ok(Self {48 MenuItem::Action { name, action, .. } => Ok(Self {
49 name: name.clone().into(),49 name: name.clone(),
50 description: if name == "New Window" {50 description: if name == "New Window" {
51 "Opens a new window".to_string()51 "Opens a new window".into()
52 } else {52 } else {
53 name.into()53 name
54 },54 },
55 action,55 action,
56 }),56 }),
@@ -62,11 +62,12 @@ impl DockMenuItem { · 5 unmodified lines
62// This code is based on the example from Microsoft:62// This code is based on the example from Microsoft:
63// https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePropertyHandler/RecipePropertyHandler.cpp63// https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePropertyHandler/RecipePropertyHandler.cpp
64pub(crate) fn update_jump_list(64pub(crate) fn update_jump_list(
65 jump_list: &JumpList,65 recent_workspaces: &[SmallVec<[PathBuf; 2]>],
66 dock_menus: &[(SharedString, SharedString)],
66) -> anyhow::Result<Vec<SmallVec<[PathBuf; 2]>>> {67) -> anyhow::Result<Vec<SmallVec<[PathBuf; 2]>>> {
67 let (list, removed) = create_destination_list()?;68 let (list, removed) = create_destination_list()?;
68 add_recent_folders(&list, &jump_list.recent_workspaces, removed.as_ref())?;69 add_recent_folders(&list, recent_workspaces, removed.as_ref())?;
69 add_dock_menu(&list, &jump_list.dock_menus)?;70 add_dock_menu(&list, dock_menus)?;
70 unsafe { list.CommitList() }?;71 unsafe { list.CommitList() }?;
71 Ok(removed)72 Ok(removed)
72}73}
@@ -110,14 +111,17 @@ fn create_destination_list() -> anyhow::Result<(ICustomDestinationList, Vec<Smal · 37 unmodified lines
110 Ok((list, removed))111 Ok((list, removed))
111}112}
112 113
113fn add_dock_menu(list: &ICustomDestinationList, dock_menus: &[DockMenuItem]) -> anyhow::Result<()> {114fn add_dock_menu(
115 list: &ICustomDestinationList,
116 dock_menus: &[(SharedString, SharedString)],
117) -> anyhow::Result<()> {
114 unsafe {118 unsafe {
115 let tasks: IObjectCollection =119 let tasks: IObjectCollection =
116 CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;120 CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;
117 for (idx, dock_menu) in dock_menus.iter().enumerate() {121 for (idx, (name, description)) in dock_menus.iter().enumerate() {
118 let argument = HSTRING::from(format!("--dock-action {}", idx));122 let argument = HSTRING::from(format!("--dock-action {}", idx));
119 let description = HSTRING::from(dock_menu.description.as_str());123 let description = HSTRING::from(description.as_str());
120 let display = dock_menu.name.as_str();124 let display = name.as_str();
121 let task = create_shell_link(argument, description, None, display)?;125 let task = create_shell_link(argument, description, None, display)?;
122 tasks.AddObject(&task)?;126 tasks.AddObject(&task)?;
123 }127 }
crates/gpui/src/platform/windows/platform.rs+26−5
@@ -240,14 +240,25 @@ impl WindowsPlatform { · 239 unmodified lines
240 }240 }
241 });241 });
242 self.inner.state.jump_list.borrow_mut().dock_menus = actions;242 self.inner.state.jump_list.borrow_mut().dock_menus = actions;
243 update_jump_list(&self.inner.state.jump_list.borrow()).log_err();243 let borrow = self.inner.state.jump_list.borrow();
244 let dock_menus = borrow
245 .dock_menus
246 .iter()
247 .map(|menu| (menu.name.clone(), menu.description.clone()))
248 .collect::<Vec<_>>();
249 let recent_workspaces = borrow.recent_workspaces.clone();
250 self.background_executor
251 .spawn(async move {
252 update_jump_list(&recent_workspaces, &dock_menus).log_err();
253 })
254 .detach();
244 }255 }
245 256
246 fn update_jump_list(257 fn update_jump_list(
247 &self,258 &self,
248 menus: Vec<MenuItem>,259 menus: Vec<MenuItem>,
249 entries: Vec<SmallVec<[PathBuf; 2]>>,260 entries: Vec<SmallVec<[PathBuf; 2]>>,
250 ) -> Vec<SmallVec<[PathBuf; 2]>> {261 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
251 let mut actions = Vec::new();262 let mut actions = Vec::new();
252 menus.into_iter().for_each(|menu| {263 menus.into_iter().for_each(|menu| {
253 if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {264 if let Some(dock_menu) = DockMenuItem::new(menu).log_err() {
@@ -256,8 +267,18 @@ impl WindowsPlatform { · 2 unmodified lines
256 });267 });
257 let mut jump_list = self.inner.state.jump_list.borrow_mut();268 let mut jump_list = self.inner.state.jump_list.borrow_mut();
258 jump_list.dock_menus = actions;269 jump_list.dock_menus = actions;
259 jump_list.recent_workspaces = entries;270 jump_list.recent_workspaces = entries.into();
260 update_jump_list(&jump_list).log_err().unwrap_or_default()271 let dock_menus = jump_list
272 .dock_menus
273 .iter()
274 .map(|menu| (menu.name.clone(), menu.description.clone()))
275 .collect::<Vec<_>>();
276 let recent_workspaces = jump_list.recent_workspaces.clone();
277 self.background_executor.spawn(async move {
278 update_jump_list(&recent_workspaces, &dock_menus)
279 .log_err()
280 .unwrap_or_default()
281 })
261 }282 }
262 283
263 fn find_current_active_window(&self) -> Option<HWND> {284 fn find_current_active_window(&self) -> Option<HWND> {
@@ -760,7 +781,7 @@ impl Platform for WindowsPlatform { · 496 unmodified lines
760 &self,781 &self,
761 menus: Vec<MenuItem>,782 menus: Vec<MenuItem>,
762 entries: Vec<SmallVec<[PathBuf; 2]>>,783 entries: Vec<SmallVec<[PathBuf; 2]>>,
763 ) -> Vec<SmallVec<[PathBuf; 2]>> {784 ) -> Task<Vec<SmallVec<[PathBuf; 2]>>> {
764 self.update_jump_list(menus, entries)785 self.update_jump_list(menus, entries)
765 }786 }
766}787}
crates/workspace/src/history_manager.rs+27−26
@@ -2,7 +2,7 @@ use std::path::PathBuf; · 1 unmodified line
2 2
3use gpui::{AppContext, Entity, Global, MenuItem};3use gpui::{AppContext, Entity, Global, MenuItem};
4use smallvec::SmallVec;4use smallvec::SmallVec;
5use ui::App;5use ui::{App, Context};
6use util::{ResultExt, paths::PathExt};6use util::{ResultExt, paths::PathExt};
7 7
8use crate::{8use crate::{
@@ -71,7 +71,12 @@ impl HistoryManager { · 62 unmodified lines
71 cx.set_global(GlobalHistoryManager(history_manager));71 cx.set_global(GlobalHistoryManager(history_manager));
72 }72 }
73 73
74 pub fn update_history(&mut self, id: WorkspaceId, entry: HistoryManagerEntry, cx: &App) {74 pub fn update_history(
75 &mut self,
76 id: WorkspaceId,
77 entry: HistoryManagerEntry,
78 cx: &mut Context<'_, HistoryManager>,
79 ) {
75 if let Some(pos) = self.history.iter().position(|e| e.id == id) {80 if let Some(pos) = self.history.iter().position(|e| e.id == id) {
76 self.history.remove(pos);81 self.history.remove(pos);
77 }82 }
@@ -79,7 +84,7 @@ impl HistoryManager { · 1 unmodified line
79 self.update_jump_list(cx);84 self.update_jump_list(cx);
80 }85 }
81 86
82 pub fn delete_history(&mut self, id: WorkspaceId, cx: &App) {87 pub fn delete_history(&mut self, id: WorkspaceId, cx: &mut Context<'_, HistoryManager>) {
83 let Some(pos) = self.history.iter().position(|e| e.id == id) else {88 let Some(pos) = self.history.iter().position(|e| e.id == id) else {
84 return;89 return;
85 };90 };
@@ -87,7 +92,7 @@ impl HistoryManager { · 1 unmodified line
87 self.update_jump_list(cx);92 self.update_jump_list(cx);
88 }93 }
89 94
90 fn update_jump_list(&mut self, cx: &App) {95 fn update_jump_list(&mut self, cx: &mut Context<'_, HistoryManager>) {
91 let menus = vec![MenuItem::action("New Window", NewWindow)];96 let menus = vec![MenuItem::action("New Window", NewWindow)];
92 let entries = self97 let entries = self
93 .history98 .history
@@ -96,29 +101,25 @@ impl HistoryManager { · 2 unmodified lines
96 .map(|entry| entry.path.clone())101 .map(|entry| entry.path.clone())
97 .collect::<Vec<_>>();102 .collect::<Vec<_>>();
98 let user_removed = cx.update_jump_list(menus, entries);103 let user_removed = cx.update_jump_list(menus, entries);
99 self.remove_user_removed_workspaces(user_removed, cx);104 cx.spawn(async move |this, cx| {
100 }105 let user_removed = user_removed.await;
101 106 if user_removed.is_empty() {
102 pub fn remove_user_removed_workspaces(107 return;
103 &mut self,
104 user_removed: Vec<SmallVec<[PathBuf; 2]>>,
105 cx: &App,
106 ) {
107 if user_removed.is_empty() {
108 return;
109 }
110 let mut deleted_ids = Vec::new();
111 for idx in (0..self.history.len()).rev() {
112 if let Some(entry) = self.history.get(idx)
113 && user_removed.contains(&entry.path)
114 {
115 deleted_ids.push(entry.id);
116 self.history.remove(idx);
117 }108 }
118 }109 let mut deleted_ids = Vec::new();
119 cx.spawn(async move |_| {110 if let Ok(()) = this.update(cx, |this, _| {
120 for id in deleted_ids.iter() {111 for idx in (0..this.history.len()).rev() {
121 WORKSPACE_DB.delete_workspace_by_id(*id).await.log_err();112 if let Some(entry) = this.history.get(idx)
113 && user_removed.contains(&entry.path)
114 {
115 deleted_ids.push(entry.id);
116 this.history.remove(idx);
117 }
118 }
119 }) {
120 for id in deleted_ids.iter() {
121 WORKSPACE_DB.delete_workspace_by_id(*id).await.log_err();
122 }
122 }123 }
123 })124 })
124 .detach();125 .detach();