1 use self::asset_item::AssetItem; 2 use crate::starry_toolkit::traits::focus::Focus; 3 use starry_client::base::color::Color; 4 use starry_server::base::image::Image as ImageResource; 5 use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH}; 6 use starry_toolkit::{ 7 base::{panel::Panel, rect::Rect}, 8 layout::grid::{Grid, GridArrangeType}, 9 traits::enter::Enter, 10 widgets::image::Image, 11 }; 12 use std::{collections::BTreeMap, fs, sync::Arc}; 13 pub mod asset_item; 14 15 const DESKTOP_BG_PATH: &[u8] = include_bytes!("../resource/desktop_bg.png"); 16 const LOADING_IMG_PATH: &[u8] = include_bytes!("../resource/loading.png"); 17 18 pub struct AssetManager { 19 cur_path: String, 20 asset_grid: Arc<Grid>, 21 items: BTreeMap<(usize, usize), Arc<AssetItem>>, 22 panel: Arc<Panel>, 23 // 原则上一个应用程序对应一个Panel和Window 24 // 这里额外创建一个Panel用于Loading图标优先显示 25 // 后续通过Server来显示Loading或不显示Loading 26 loading_panel: Arc<Panel>, 27 init_show: bool, 28 } 29 30 impl AssetManager { 31 pub fn new() -> Self { 32 AssetManager { 33 cur_path: String::from("/"), 34 asset_grid: Grid::new(), 35 items: BTreeMap::new(), 36 panel: Panel::new( 37 Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32), 38 "Title", 39 Color::rgb(0, 0, 0), 40 ), 41 loading_panel: Panel::new( 42 Rect::new(SCREEN_WIDTH as i32 - 64, SCREEN_HEIGHT as i32 - 64, 64, 64), 43 "Loading", 44 Color::rgb(255, 255, 255), 45 ), 46 init_show: true, 47 } 48 } 49 50 pub fn init(&mut self) { 51 self.init_loading_panel(); 52 53 let grid = self.asset_grid.clone(); 54 grid.set_upper_limit(8); 55 grid.set_space(20, 20); 56 grid.set_arrange_type(GridArrangeType::Horizontal); 57 58 // 处理输入回调 59 let self_ptr = self as *mut AssetManager; 60 grid.set_enter_callback(move |grid, char, redraw| { 61 let asset_manager: &mut AssetManager = unsafe { &mut *self_ptr }; 62 63 if char == '\n' { 64 if let Some(item) = asset_manager.items.get(&grid.focused_id.get().unwrap()) { 65 // 判断是否是文件夹 66 if item.is_dir.get() == false { 67 return; 68 } 69 70 // 返回上级目录 71 if item.file_path.borrow().eq(&"..".to_string()) { 72 if asset_manager.cur_path.len() == 1 { 73 return; 74 } else { 75 let split_path = 76 &asset_manager.cur_path[..asset_manager.cur_path.len() - 1]; 77 let slash_pos = split_path.rfind('/').unwrap(); 78 let _ = asset_manager.cur_path.split_off(slash_pos + 1); 79 } 80 } else { 81 // 打开文件夹 82 asset_manager.cur_path.push_str(&item.file_path.borrow()); 83 asset_manager.cur_path.push_str(&"/"); 84 } 85 asset_manager.refresh(); 86 } 87 88 return; 89 } 90 91 let row_offset: i32 = match char { 92 'a' => 0, 93 'A' => 0, 94 'd' => 0, 95 'D' => 0, 96 'w' => -1, 97 'W' => -1, 98 's' => 1, 99 'S' => 1, 100 _ => 0, 101 }; 102 103 let col_offset: i32 = match char { 104 'a' => -1, 105 'A' => -1, 106 'd' => 1, 107 'D' => 1, 108 'w' => 0, 109 'W' => 0, 110 's' => 0, 111 'S' => 0, 112 _ => 0, 113 }; 114 115 if row_offset == 0 && col_offset == 0 { 116 return; 117 } 118 let mut nxt_row = grid.focused_id.get().unwrap().0 as i32 + row_offset; 119 let mut nxt_col = grid.focused_id.get().unwrap().1 as i32 + col_offset; 120 loop { 121 if nxt_row < 0 122 || nxt_row >= grid.max_row.get() as i32 123 || nxt_col < 0 124 || nxt_col >= grid.max_column.get() as i32 125 { 126 return; 127 } 128 129 if grid 130 .elements 131 .borrow() 132 .contains_key(&(nxt_row as usize, nxt_col as usize)) 133 { 134 break; 135 } 136 137 nxt_row += row_offset; 138 nxt_col += col_offset; 139 } 140 141 grid.focus( 142 grid.elements 143 .borrow() 144 .get(&(nxt_row as usize, nxt_col as usize)) 145 .unwrap(), 146 ); 147 148 asset_manager.loading_panel.draw(); 149 redraw.set(true); 150 }); 151 152 self.panel.add_child(&Image::new_from_image( 153 ImageResource::from_path(DESKTOP_BG_PATH).unwrap(), 154 )); 155 156 self.panel.add_child(&(self.asset_grid)); 157 } 158 159 fn init_loading_panel(&mut self) { 160 self.loading_panel.add_child(&Image::new_from_image( 161 ImageResource::from_path(LOADING_IMG_PATH).unwrap(), 162 )); 163 } 164 165 pub fn refresh(&mut self) { 166 self.items.clear(); 167 self.asset_grid.clear(); 168 169 // 父目录 170 let parent_asset_item = AssetItem::new("..", true); 171 let (row, col) = self.asset_grid.add_element(&parent_asset_item); 172 self.items.insert((row, col), parent_asset_item.clone()); 173 174 // 读取目录中的文件列表 175 if let Ok(entries) = fs::read_dir(&self.cur_path) { 176 for entry in entries { 177 if let Ok(item) = entry { 178 let is_dir = if let Ok(metadata) = item.metadata() { 179 metadata.is_dir() 180 } else { 181 false 182 }; 183 184 let asset_item = AssetItem::new(item.file_name().to_str().unwrap(), is_dir); 185 let (row, col) = self.asset_grid.add_element(&asset_item); 186 self.items.insert((row, col), asset_item.clone()); 187 } 188 } 189 } else { 190 println!( 191 "[Error] AssetManager failed to read dir {:?}", 192 self.cur_path 193 ); 194 } 195 196 let grid = self.asset_grid.clone(); 197 if let Some(widget) = grid.elements.borrow().get(&(0, 0)) { 198 grid.focused_id.set(Some((0, 0))); 199 grid.focus(widget); 200 } 201 202 if self.init_show == true { 203 self.init_show = false 204 } else { 205 self.loading_panel.draw(); 206 } 207 self.panel.draw(); 208 } 209 210 pub fn exec(&mut self) { 211 self.panel.exec(); 212 } 213 } 214