@@ -54,63 +54,13 @@ public async Task InitializeAsync()
5454
5555 public async Task < bool > UpdatePinnedFoldersAsync ( )
5656 {
57- return await Task . Run ( UpdatePinnedFolders ) ;
58-
59- unsafe bool UpdatePinnedFolders ( )
57+ return await Task . Run ( ( ) =>
6058 {
6159 try
6260 {
63- HRESULT hr = default ;
64-
65- string szFolderShellPath = "Shell:::{3936E9E4-D92C-4EEE-A85A-BC16D5EA0819}" ;
66-
67- // Get IShellItem of the shell folder
68- var shellItemIid = typeof ( IShellItem ) . GUID ;
69- using ComPtr < IShellItem > pFolderShellItem = default ;
70- fixed ( char * pszFolderShellPath = szFolderShellPath )
71- hr = PInvoke . SHCreateItemFromParsingName ( pszFolderShellPath , null , & shellItemIid , ( void * * ) pFolderShellItem . GetAddressOf ( ) ) ;
72-
73- // Get IEnumShellItems of the quick access shell folder
74- var enumItemsBHID = PInvoke . BHID_EnumItems ;
75- Guid enumShellItemIid = typeof ( IEnumShellItems ) . GUID ;
76- using ComPtr < IEnumShellItems > pEnumShellItems = default ;
77- hr = pFolderShellItem . Get ( ) ->BindToHandler ( null , & enumItemsBHID , & enumShellItemIid , ( void * * ) pEnumShellItems . GetAddressOf ( ) ) ;
78-
79- // Enumerate recent items and populate the list
80- int index = 0 ;
8161 List < LocationItem > items = [ ] ;
82- using ComPtr < IShellItem > pShellItem = default ;
83- while ( pEnumShellItems . Get ( ) ->Next ( 1 , pShellItem . GetAddressOf ( ) ) == HRESULT . S_OK )
84- {
85- // Get top 20 items
86- if ( index is 20 )
87- break ;
88-
89- // Get whether the item is pined or not
90- using ComPtr < IShellItem2 > pShellItem2 = pShellItem . As < IShellItem2 > ( typeof ( IShellItem2 ) . GUID ) ;
91- hr = PInvoke . PSGetPropertyKeyFromName ( "System.Home.IsPinned" , out var propertyKey ) ;
92- hr = pShellItem2 . Get ( ) ->GetString ( propertyKey , out var szPropertyValue ) ;
93- if ( bool . TryParse ( szPropertyValue . ToString ( ) , out var isPinned ) && ! isPinned )
94- continue ;
95-
96- // Get the target path
97- pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_DESKTOPABSOLUTEEDITING , out var szDisplayName ) ;
98- var targetPath = szDisplayName . ToString ( ) ;
99- PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
100-
101- // Get the display name
102- pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_NORMALDISPLAY , out szDisplayName ) ;
103- var fileName = szDisplayName . ToString ( ) ;
104- PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
105-
106- items . Add ( new ( )
107- {
108- Path = targetPath ,
109- Text = fileName ,
110- } ) ;
111-
112- index ++ ;
113- }
62+ foreach ( var path in GetPinnedFolders ( ) )
63+ items . Add ( await CreateItemOf ( path ) ) ;
11464
11565 if ( items . Count is 0 )
11666 return false ;
@@ -123,16 +73,149 @@ unsafe bool UpdatePinnedFolders()
12373 _PinnedFolders . AddRange ( items ) ;
12474 }
12575
126- //var eventArgs = GetChangedActionEventArgs(snapshot, items);
76+ var eventArgs = GetChangedActionEventArgs ( snapshot , items ) ;
77+ PinnedFoldersChanged ? . Invoke ( this , eventArgs ) ;
78+ }
79+ catch
80+ {
81+ }
82+ } ) ;
83+
84+ unsafe IEnumerable < string > GetPinnedFolders ( )
85+ {
86+ HRESULT hr = default ;
87+
88+ // Get IShellItem of the shell folder
89+ var shellItemIid = typeof ( IShellItem ) . GUID ;
90+ using ComPtr < IShellItem > pFolderShellItem = default ;
91+ fixed ( char * pszFolderShellPath = "Shell:::{3936E9E4-D92C-4EEE-A85A-BC16D5EA0819}" )
92+ hr = PInvoke . SHCreateItemFromParsingName ( pszFolderShellPath , null , & shellItemIid , ( void * * ) pFolderShellItem . GetAddressOf ( ) ) ;
93+
94+ // Get IEnumShellItems of the quick access shell folder
95+ var enumItemsBHID = PInvoke . BHID_EnumItems ;
96+ Guid enumShellItemIid = typeof ( IEnumShellItems ) . GUID ;
97+ using ComPtr < IEnumShellItems > pEnumShellItems = default ;
98+ hr = pFolderShellItem . Get ( ) ->BindToHandler ( null , & enumItemsBHID , & enumShellItemIid , ( void * * ) pEnumShellItems . GetAddressOf ( ) ) ;
99+
100+ // Enumerate pinned folders
101+ int index = 0 ;
102+ using ComPtr < IShellItem > pShellItem = default ;
103+ while ( pEnumShellItems . Get ( ) ->Next ( 1 , pShellItem . GetAddressOf ( ) ) == HRESULT . S_OK )
104+ {
105+ // Get whether the item is pined or not
106+ using ComPtr < IShellItem2 > pShellItem2 = pShellItem . As < IShellItem2 > ( typeof ( IShellItem2 ) . GUID ) ;
107+ hr = PInvoke . PSGetPropertyKeyFromName ( "System.Home.IsPinned" , out var propertyKey ) ;
108+ hr = pShellItem2 . Get ( ) ->GetString ( propertyKey , out var szPropertyValue ) ;
109+ if ( bool . TryParse ( szPropertyValue . ToString ( ) , out var isPinned ) && ! isPinned )
110+ continue ;
127111
128- //PinnedFoldersChanged?.Invoke(this, eventArgs);
112+ // Get the target path
113+ pShellItem . Get ( ) ->GetDisplayName ( SIGDN . SIGDN_DESKTOPABSOLUTEEDITING , out var szDisplayName ) ;
114+ var path = szDisplayName . ToString ( ) ;
115+ PInvoke . CoTaskMemFree ( szDisplayName . Value ) ;
129116
130- return true ;
117+ yield return path ;
118+
119+ index ++ ;
131120 }
132- catch
121+ }
122+
123+ async Task < LocationItem > CreateItemOf ( string path )
124+ {
125+ var item = await FilesystemTasks . Wrap ( ( ) => DriveHelpers . GetRootFromPathAsync ( path ) ) ;
126+ var res = await FilesystemTasks . Wrap ( ( ) => StorageFileExtensions . DangerousGetFolderFromPathAsync ( path , item ) ) ;
127+ LocationItem locationItem ;
128+
129+ if ( string . Equals ( path , Constants . UserEnvironmentPaths . RecycleBinPath , StringComparison . OrdinalIgnoreCase ) )
133130 {
134- return false ;
131+ locationItem = LocationItem . Create < RecycleBinLocationItem > ( ) ;
135132 }
133+ else
134+ {
135+ locationItem = LocationItem . Create < LocationItem > ( ) ;
136+
137+ if ( path . Equals ( Constants . UserEnvironmentPaths . MyComputerPath , StringComparison . OrdinalIgnoreCase ) )
138+ locationItem . Text = "ThisPC" . GetLocalizedResource ( ) ;
139+ else if ( path . Equals ( Constants . UserEnvironmentPaths . NetworkFolderPath , StringComparison . OrdinalIgnoreCase ) )
140+ locationItem . Text = "Network" . GetLocalizedResource ( ) ;
141+ }
142+
143+ locationItem . Path = path ;
144+ locationItem . Section = SectionType . Pinned ;
145+ locationItem . MenuOptions = new ContextMenuOptions ( )
146+ {
147+ IsLocationItem = true ,
148+ ShowProperties = true ,
149+ ShowUnpinItem = true ,
150+ ShowShellItems = true ,
151+ ShowEmptyRecycleBin = string . Equals ( path , Constants . UserEnvironmentPaths . RecycleBinPath , StringComparison . OrdinalIgnoreCase )
152+ } ;
153+ locationItem . IsDefaultLocation = false ;
154+ locationItem . Text = res ? . Result ? . DisplayName ?? Path . GetFileName ( path . TrimEnd ( '\\ ' ) ) ;
155+
156+ if ( res )
157+ {
158+ locationItem . IsInvalid = false ;
159+ if ( res . Result is not null )
160+ {
161+ var result = await FileThumbnailHelper . GetIconAsync (
162+ res . Result . Path ,
163+ Constants . ShellIconSizes . Small ,
164+ true ,
165+ IconOptions . ReturnIconOnly | IconOptions . UseCurrentScale ) ;
166+
167+ locationItem . IconData = result ;
168+
169+ var bitmapImage = await MainWindow . Instance . DispatcherQueue . EnqueueOrInvokeAsync ( ( ) => locationItem . IconData . ToBitmapAsync ( ) , Microsoft . UI . Dispatching . DispatcherQueuePriority . Normal ) ;
170+ if ( bitmapImage is not null )
171+ locationItem . Icon = bitmapImage ;
172+ }
173+ }
174+ else
175+ {
176+ locationItem . Icon = await MainWindow . Instance . DispatcherQueue . EnqueueOrInvokeAsync ( ( ) => UIHelpers . GetSidebarIconResource ( Constants . ImageRes . Folder ) ) ;
177+ locationItem . IsInvalid = true ;
178+ Debug . WriteLine ( $ "Pinned item was invalid { res ? . ErrorCode } , item: { path } ") ;
179+ }
180+
181+ return locationItem ;
182+ }
183+
184+ NotifyCollectionChangedEventArgs GetChangedActionEventArgs ( IReadOnlyList < LocationItem > oldItems , IList < LocationItem > newItems )
185+ {
186+ if ( newItems . Count - oldItems . Count is 1 )
187+ {
188+ var differences = newItems . Except ( oldItems ) ;
189+ if ( differences . Take ( 2 ) . Count ( ) is 1 )
190+ return new ( NotifyCollectionChangedAction . Add , newItems . First ( ) ) ;
191+ }
192+ else if ( oldItems . Count - newItems . Count is 1 )
193+ {
194+ var differences = oldItems . Except ( newItems ) ;
195+ if ( differences . Take ( 2 ) . Count ( ) is 1 )
196+ {
197+ for ( int i = 0 ; i < oldItems . Count ; i ++ )
198+ {
199+ if ( i >= newItems . Count || ! newItems [ i ] . Equals ( oldItems [ i ] ) )
200+ return new ( NotifyCollectionChangedAction . Remove , oldItems [ i ] , index : i ) ;
201+ }
202+ }
203+ }
204+ else if ( newItems . Count == oldItems . Count )
205+ {
206+ var differences = oldItems . Except ( newItems ) ;
207+ if ( differences . Any ( ) )
208+ return new ( NotifyCollectionChangedAction . Reset ) ;
209+
210+ // First diff from reversed is the designated item
211+ for ( int i = oldItems . Count - 1 ; i >= 0 ; i -- )
212+ {
213+ if ( ! oldItems [ i ] . Equals ( newItems [ i ] ) )
214+ return new ( NotifyCollectionChangedAction . Move , oldItems [ i ] , index : 0 , oldIndex : i ) ;
215+ }
216+ }
217+
218+ return new ( NotifyCollectionChangedAction . Reset ) ;
136219 }
137220 }
138221
0 commit comments