6
6
using CommunityToolkit . Mvvm . Input ;
7
7
using Flow . Launcher . Core . Plugin ;
8
8
using Flow . Launcher . Infrastructure . Hotkey ;
9
+ using Flow . Launcher . Infrastructure . Image ;
9
10
using Flow . Launcher . Plugin ;
10
11
using Flow . Launcher . Resources . Controls ;
11
12
using Flow . Launcher . SettingPages . ViewModels ;
12
13
using Flow . Launcher . ViewModel ;
13
14
using iNKORE . UI . WPF . Modern . Controls ;
15
+ using System . Threading . Tasks ;
16
+ using System . Windows . Media ;
14
17
15
18
namespace Flow . Launcher . SettingPages . Views ;
16
19
@@ -53,8 +56,10 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e)
53
56
54
57
var excard = new SettingsExpander ( )
55
58
{
56
- Header = metadata . Name ,
57
- Margin = new Thickness ( 0 , 4 , 0 , 0 )
59
+ Header = metadata . Name + " " + Localize . hotkeys ( ) ,
60
+ Margin = new Thickness ( 0 , 4 , 0 , 0 ) ,
61
+ HeaderIcon = new Image ( ) { Source = ImageLoader . LoadingImage } ,
62
+ Tag = metadata
58
63
} ;
59
64
60
65
var sortedHotkeyInfo = hotkeyInfo . OrderBy ( h => h . Id ) . ToList ( ) ;
@@ -96,6 +101,9 @@ private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e)
96
101
}
97
102
PluginHotkeySettings . Children . Add ( excard ) ;
98
103
}
104
+
105
+ // Load plugin icons into SettingsExpander asynchronously
106
+ _ = LoadPluginIconsAsync ( ) ;
99
107
}
100
108
101
109
private static void ChangePluginHotkey ( PluginMetadata metadata , BasePluginHotkey pluginHotkey , HotkeyModel newHotkey )
@@ -109,4 +117,52 @@ private static void ChangePluginHotkey(PluginMetadata metadata, BasePluginHotkey
109
117
PluginManager . ChangePluginHotkey ( metadata , windowPluginHotkey , newHotkey ) ;
110
118
}
111
119
}
120
+
121
+ private async Task LoadPluginIconsAsync ( )
122
+ {
123
+ // Snapshot list to avoid collection modification issues
124
+ var expanders = PluginHotkeySettings . Children
125
+ . OfType < SettingsExpander > ( )
126
+ . Where ( e => e . Tag is PluginMetadata m && ! string . IsNullOrEmpty ( m . IcoPath ) )
127
+ . ToList ( ) ;
128
+
129
+ // Fire all loads concurrently
130
+ var tasks = expanders . Select ( async expander =>
131
+ {
132
+ if ( expander . Tag is not PluginMetadata metadata ) return ;
133
+ try
134
+ {
135
+ var iconSource = await App . API . LoadImageAsync ( metadata . IcoPath ) ;
136
+ if ( iconSource == null ) return ;
137
+
138
+ // Marshal back to UI thread if needed
139
+ if ( ! Dispatcher . CheckAccess ( ) )
140
+ {
141
+ await Dispatcher . InvokeAsync ( ( ) => ApplyIcon ( expander , iconSource ) ) ;
142
+ }
143
+ else
144
+ {
145
+ ApplyIcon ( expander , iconSource ) ;
146
+ }
147
+ }
148
+ catch
149
+ {
150
+ // Swallow exceptions to avoid impacting UI; optionally log if logging infra exists
151
+ }
152
+ } ) ;
153
+
154
+ await Task . WhenAll ( tasks ) ;
155
+ }
156
+
157
+ private static void ApplyIcon ( SettingsExpander expander , ImageSource iconSource )
158
+ {
159
+ if ( expander . HeaderIcon is Image img )
160
+ {
161
+ img . Source = iconSource ;
162
+ }
163
+ else
164
+ {
165
+ expander . HeaderIcon = new Image { Source = iconSource } ;
166
+ }
167
+ }
112
168
}
0 commit comments