@@ -2,6 +2,7 @@ use crate::macos;
22use crate :: macos:: focus_this_app;
33
44use global_hotkey:: { GlobalHotKeyEvent , HotKeyState } ;
5+ use iced:: Alignment ;
56use iced:: Fill ;
67use iced:: alignment:: Vertical ;
78use iced:: futures;
@@ -11,10 +12,16 @@ use iced::stream;
1112use iced:: widget:: Button ;
1213use iced:: widget:: Row ;
1314use iced:: widget:: Text ;
15+ use iced:: widget:: container;
16+ use iced:: widget:: image:: Handle ;
17+ use iced:: widget:: image:: Viewer ;
18+ use iced:: widget:: scrollable;
1419use iced:: widget:: text:: LineHeight ;
1520use iced:: widget:: { Column , operation, space, text_input} ;
1621use iced:: window:: { self , Id , Settings } ;
1722use iced:: { Element , Subscription , Task , Theme } ;
23+ use icns:: IconFamily ;
24+ use image:: RgbaImage ;
1825use objc2:: rc:: Retained ;
1926use objc2_app_kit:: NSRunningApplication ;
2027
@@ -43,6 +50,25 @@ fn log_error_and_exit(msg: &str) {
4350 exit ( -1 )
4451}
4552
53+ fn handle_from_icns ( path : & Path ) -> Option < Handle > {
54+ let data = std:: fs:: read ( path) . ok ( ) ?;
55+ let family = IconFamily :: read ( std:: io:: Cursor :: new ( & data) ) . ok ( ) ?;
56+
57+ let icon_type = family. available_icons ( ) ;
58+
59+ let icon = family. get_icon_with_type ( * icon_type. get ( 0 ) ?) . ok ( ) ?;
60+ let image = RgbaImage :: from_raw (
61+ icon. width ( ) as u32 ,
62+ icon. height ( ) as u32 ,
63+ icon. data ( ) . to_vec ( ) ,
64+ ) ?;
65+ Some ( Handle :: from_rgba (
66+ image. width ( ) ,
67+ image. height ( ) ,
68+ image. into_raw ( ) ,
69+ ) )
70+ }
71+
4672pub fn get_installed_apps ( dir : impl AsRef < Path > ) -> Vec < App > {
4773 fs:: read_dir ( dir)
4874 . unwrap_or_else ( |x| {
@@ -75,11 +101,38 @@ pub fn get_installed_apps(dir: impl AsRef<Path>) -> Vec<App> {
75101 exit ( -1 )
76102 } ) ;
77103
104+ let direntry = fs:: read_dir ( format ! ( "{}/Contents/Resources" , path_str) )
105+ . into_iter ( )
106+ . flatten ( )
107+ . filter_map ( |x| {
108+ let file = x. ok ( ) ?;
109+ let name = file. file_name ( ) ;
110+ let file_name = name. to_str ( ) ?;
111+ if file_name. ends_with ( ".icns" ) {
112+ Some ( file. path ( ) )
113+ } else {
114+ None
115+ }
116+ } )
117+ . collect :: < Vec < PathBuf > > ( ) ;
118+
119+ let icons = if direntry. len ( ) > 1 {
120+ let icns_vec = direntry
121+ . iter ( )
122+ . filter ( |x| x. ends_with ( "AppIcon.icns" ) )
123+ . collect :: < Vec < & PathBuf > > ( ) ;
124+ handle_from_icns ( icns_vec. first ( ) . unwrap_or ( & & PathBuf :: new ( ) ) )
125+ } else if direntry. len ( ) > 0 {
126+ handle_from_icns ( direntry. first ( ) . unwrap_or ( & PathBuf :: new ( ) ) )
127+ } else {
128+ None
129+ } ;
130+
78131 let name = file_name. strip_suffix ( ".app" ) . unwrap ( ) . to_string ( ) ;
79132
80133 Some ( App {
81134 open_command : format ! ( "open {}" , path_str) ,
82- icon_path : None ,
135+ icons ,
83136 name_lc : name. to_lowercase ( ) ,
84137 name,
85138 } )
@@ -91,7 +144,7 @@ pub fn get_installed_apps(dir: impl AsRef<Path>) -> Vec<App> {
91144#[ derive( Debug , Clone ) ]
92145pub struct App {
93146 open_command : String ,
94- icon_path : Option < PathBuf > ,
147+ icons : Option < iced :: widget :: image :: Handle > ,
95148 name : String ,
96149 name_lc : String ,
97150}
@@ -100,6 +153,18 @@ impl App {
100153 pub fn render ( & self ) -> impl Into < iced:: Element < ' _ , Message > > {
101154 let mut tile = Row :: new ( ) . width ( Fill ) . height ( 55 ) ;
102155
156+ if let Some ( icon) = & self . icons {
157+ tile = tile
158+ . push ( Viewer :: new ( icon) . height ( 35 ) . width ( 35 ) )
159+ . align_y ( Alignment :: Center ) ;
160+ } else {
161+ tile = tile
162+ . push ( space ( ) . height ( Fill ) )
163+ . width ( 55 )
164+ . height ( 55 )
165+ . align_y ( Alignment :: Center ) ;
166+ }
167+
103168 tile = tile. push (
104169 Button :: new (
105170 Text :: new ( self . name . clone ( ) )
@@ -108,11 +173,20 @@ impl App {
108173 . align_y ( Vertical :: Center ) ,
109174 )
110175 . on_press ( Message :: RunShellCommand ( self . open_command . clone ( ) ) )
111- . width ( Fill )
112- . height ( Fill ) ,
113- ) ;
114-
115- tile
176+ . style ( |_, _| iced:: widget:: button:: Style {
177+ background : Some ( iced:: Background :: Color (
178+ Theme :: KanagawaDragon . palette ( ) . background ,
179+ ) ) ,
180+ text_color : Theme :: KanagawaDragon . palette ( ) . text ,
181+ ..Default :: default ( )
182+ } )
183+ . width ( Fill ) . height ( 55 )
184+ ) . width ( Fill ) ;
185+ container ( tile) . style ( |_| iced:: widget:: container:: Style {
186+ text_color : Some ( Theme :: KanagawaDragon . palette ( ) . text ) ,
187+ background : Some ( iced:: Background :: Color ( Theme :: KanagawaDragon . palette ( ) . background ) ) ,
188+ ..Default :: default ( )
189+ } ) . width ( Fill ) . height ( Fill )
116190 }
117191}
118192
@@ -175,7 +249,7 @@ pub struct Tile {
175249impl Tile {
176250 /// A base window
177251 pub fn new ( ) -> ( Self , Task < Message > ) {
178- // let _ = create_tray_icons();
252+ // let _ = create_tray_icons();
179253 let ( id, open) = window:: open ( default_settings ( ) ) ;
180254 let _ = window:: run ( id, |handle| {
181255 macos:: macos_window_config (
@@ -229,7 +303,7 @@ impl Tile {
229303 } else if self . query_lc == "randomvar" {
230304 self . results = vec ! [ App {
231305 open_command: "" . to_string( ) ,
232- icon_path : None ,
306+ icons : None ,
233307 name: rand:: random_range( 0 ..100 ) . to_string( ) ,
234308 name_lc: String :: new( ) ,
235309 } ] ;
@@ -240,7 +314,6 @@ impl Tile {
240314 height : DEFAULT_WINDOW_HEIGHT + 55. ,
241315 } ,
242316 ) ;
243-
244317 }
245318
246319 let filter_vec = if self . query_lc . starts_with ( & self . prev_query_lc ) {
@@ -355,7 +428,10 @@ impl Tile {
355428 search_results = search_results. push ( ( result) . render ( ) ) ;
356429 }
357430
358- Column :: new ( ) . push ( title_input) . push ( search_results) . into ( )
431+ Column :: new ( )
432+ . push ( title_input)
433+ . push ( scrollable ( search_results) )
434+ . into ( )
359435 } else {
360436 space ( ) . into ( )
361437 }
0 commit comments