18a41b76cSR0ronoa use self::asset_item::AssetItem; 249182ea7SR0ronoa use crate::starry_toolkit::traits::focus::Focus; 3b0262857SR0ronoa use starry_client::base::color::Color; 48a41b76cSR0ronoa use starry_server::base::image::Image as ImageResource; 549182ea7SR0ronoa use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH}; 649182ea7SR0ronoa use starry_toolkit::{ 749182ea7SR0ronoa base::{panel::Panel, rect::Rect}, 8b0262857SR0ronoa layout::grid::{Grid, GridArrangeType}, 98a41b76cSR0ronoa traits::enter::Enter, 102b942a51SR0ronoa widgets::image::Image, 1149182ea7SR0ronoa }; 128a41b76cSR0ronoa use std::{collections::BTreeMap, fs, sync::Arc}; 1349182ea7SR0ronoa pub mod asset_item; 1449182ea7SR0ronoa 1549182ea7SR0ronoa const DESKTOP_BG_PATH: &[u8] = include_bytes!("../resource/desktop_bg.png"); 16*1bee64b6SR0ronoa const LOADING_IMG_PATH: &[u8] = include_bytes!("../resource/loading.png"); 1749182ea7SR0ronoa 188a41b76cSR0ronoa pub struct AssetManager { 1949182ea7SR0ronoa cur_path: String, 208a41b76cSR0ronoa asset_grid: Arc<Grid>, 218a41b76cSR0ronoa items: BTreeMap<(usize, usize), Arc<AssetItem>>, 22*1bee64b6SR0ronoa panel: Arc<Panel>, 23*1bee64b6SR0ronoa // 原则上一个应用程序对应一个Panel和Window 24*1bee64b6SR0ronoa // 这里额外创建一个Panel用于Loading图标优先显示 25*1bee64b6SR0ronoa // 后续通过Server来显示Loading或不显示Loading 26*1bee64b6SR0ronoa loading_panel: Arc<Panel>, 27*1bee64b6SR0ronoa init_show: bool, 2849182ea7SR0ronoa } 2949182ea7SR0ronoa 308a41b76cSR0ronoa impl AssetManager { 3149182ea7SR0ronoa pub fn new() -> Self { 328a41b76cSR0ronoa AssetManager { 3349182ea7SR0ronoa cur_path: String::from("/"), 348a41b76cSR0ronoa asset_grid: Grid::new(), 358a41b76cSR0ronoa items: BTreeMap::new(), 368a41b76cSR0ronoa panel: Panel::new( 378a41b76cSR0ronoa Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32), 388a41b76cSR0ronoa "Title", 398a41b76cSR0ronoa Color::rgb(0, 0, 0), 408a41b76cSR0ronoa ), 41*1bee64b6SR0ronoa loading_panel: Panel::new( 42*1bee64b6SR0ronoa Rect::new(SCREEN_WIDTH as i32 - 64, SCREEN_HEIGHT as i32 - 64, 64, 64), 43*1bee64b6SR0ronoa "Loading", 44*1bee64b6SR0ronoa Color::rgb(255, 255, 255), 45*1bee64b6SR0ronoa ), 46*1bee64b6SR0ronoa init_show: true, 4749182ea7SR0ronoa } 4849182ea7SR0ronoa } 4949182ea7SR0ronoa 508a41b76cSR0ronoa pub fn init(&mut self) { 51*1bee64b6SR0ronoa self.init_loading_panel(); 52*1bee64b6SR0ronoa 538a41b76cSR0ronoa let grid = self.asset_grid.clone(); 54*1bee64b6SR0ronoa grid.set_upper_limit(8); 5549182ea7SR0ronoa grid.set_space(20, 20); 56b0262857SR0ronoa grid.set_arrange_type(GridArrangeType::Horizontal); 578a41b76cSR0ronoa 582b942a51SR0ronoa // 处理输入回调 598a41b76cSR0ronoa let self_ptr = self as *mut AssetManager; 608a41b76cSR0ronoa grid.set_enter_callback(move |grid, char, redraw| { 618a41b76cSR0ronoa let asset_manager: &mut AssetManager = unsafe { &mut *self_ptr }; 628a41b76cSR0ronoa 63*1bee64b6SR0ronoa if char == '\n' { 648a41b76cSR0ronoa if let Some(item) = asset_manager.items.get(&grid.focused_id.get().unwrap()) { 65*1bee64b6SR0ronoa // 判断是否是文件夹 66*1bee64b6SR0ronoa if item.is_dir.get() == false { 67*1bee64b6SR0ronoa return; 68*1bee64b6SR0ronoa } 69*1bee64b6SR0ronoa 70*1bee64b6SR0ronoa // 返回上级目录 712b942a51SR0ronoa if item.file_path.borrow().eq(&"..".to_string()) { 722b942a51SR0ronoa if asset_manager.cur_path.len() == 1 { 732b942a51SR0ronoa return; 742b942a51SR0ronoa } else { 752b942a51SR0ronoa let split_path = 762b942a51SR0ronoa &asset_manager.cur_path[..asset_manager.cur_path.len() - 1]; 772b942a51SR0ronoa let slash_pos = split_path.rfind('/').unwrap(); 782b942a51SR0ronoa let _ = asset_manager.cur_path.split_off(slash_pos + 1); 792b942a51SR0ronoa } 802b942a51SR0ronoa } else { 81*1bee64b6SR0ronoa // 打开文件夹 828a41b76cSR0ronoa asset_manager.cur_path.push_str(&item.file_path.borrow()); 838a41b76cSR0ronoa asset_manager.cur_path.push_str(&"/"); 842b942a51SR0ronoa } 858a41b76cSR0ronoa asset_manager.refresh(); 8649182ea7SR0ronoa } 8749182ea7SR0ronoa 888a41b76cSR0ronoa return; 898a41b76cSR0ronoa } 908a41b76cSR0ronoa 918a41b76cSR0ronoa let row_offset: i32 = match char { 928a41b76cSR0ronoa 'a' => 0, 938a41b76cSR0ronoa 'A' => 0, 948a41b76cSR0ronoa 'd' => 0, 958a41b76cSR0ronoa 'D' => 0, 968a41b76cSR0ronoa 'w' => -1, 978a41b76cSR0ronoa 'W' => -1, 988a41b76cSR0ronoa 's' => 1, 998a41b76cSR0ronoa 'S' => 1, 1008a41b76cSR0ronoa _ => 0, 1018a41b76cSR0ronoa }; 1028a41b76cSR0ronoa 1038a41b76cSR0ronoa let col_offset: i32 = match char { 1048a41b76cSR0ronoa 'a' => -1, 1058a41b76cSR0ronoa 'A' => -1, 1068a41b76cSR0ronoa 'd' => 1, 1078a41b76cSR0ronoa 'D' => 1, 1088a41b76cSR0ronoa 'w' => 0, 1098a41b76cSR0ronoa 'W' => 0, 1108a41b76cSR0ronoa 's' => 0, 1118a41b76cSR0ronoa 'S' => 0, 1128a41b76cSR0ronoa _ => 0, 1138a41b76cSR0ronoa }; 1148a41b76cSR0ronoa 1158a41b76cSR0ronoa if row_offset == 0 && col_offset == 0 { 1168a41b76cSR0ronoa return; 1178a41b76cSR0ronoa } 1188a41b76cSR0ronoa let mut nxt_row = grid.focused_id.get().unwrap().0 as i32 + row_offset; 1198a41b76cSR0ronoa let mut nxt_col = grid.focused_id.get().unwrap().1 as i32 + col_offset; 1208a41b76cSR0ronoa loop { 1218a41b76cSR0ronoa if nxt_row < 0 1228a41b76cSR0ronoa || nxt_row >= grid.max_row.get() as i32 1238a41b76cSR0ronoa || nxt_col < 0 1248a41b76cSR0ronoa || nxt_col >= grid.max_column.get() as i32 1258a41b76cSR0ronoa { 1268a41b76cSR0ronoa return; 1278a41b76cSR0ronoa } 1288a41b76cSR0ronoa 1298a41b76cSR0ronoa if grid 1308a41b76cSR0ronoa .elements 1318a41b76cSR0ronoa .borrow() 1328a41b76cSR0ronoa .contains_key(&(nxt_row as usize, nxt_col as usize)) 1338a41b76cSR0ronoa { 1348a41b76cSR0ronoa break; 1358a41b76cSR0ronoa } 1368a41b76cSR0ronoa 1378a41b76cSR0ronoa nxt_row += row_offset; 1388a41b76cSR0ronoa nxt_col += col_offset; 1398a41b76cSR0ronoa } 1408a41b76cSR0ronoa 1418a41b76cSR0ronoa grid.focus( 1428a41b76cSR0ronoa grid.elements 1438a41b76cSR0ronoa .borrow() 1448a41b76cSR0ronoa .get(&(nxt_row as usize, nxt_col as usize)) 1458a41b76cSR0ronoa .unwrap(), 1468a41b76cSR0ronoa ); 1478a41b76cSR0ronoa 148*1bee64b6SR0ronoa asset_manager.loading_panel.draw(); 149*1bee64b6SR0ronoa redraw.set(true); 1508a41b76cSR0ronoa }); 1518a41b76cSR0ronoa 152*1bee64b6SR0ronoa self.panel.add_child(&Image::new_from_image( 1538a41b76cSR0ronoa ImageResource::from_path(DESKTOP_BG_PATH).unwrap(), 1548a41b76cSR0ronoa )); 1558a41b76cSR0ronoa 1568a41b76cSR0ronoa self.panel.add_child(&(self.asset_grid)); 1578a41b76cSR0ronoa } 1588a41b76cSR0ronoa 159*1bee64b6SR0ronoa fn init_loading_panel(&mut self) { 160*1bee64b6SR0ronoa self.loading_panel.add_child(&Image::new_from_image( 161*1bee64b6SR0ronoa ImageResource::from_path(LOADING_IMG_PATH).unwrap(), 162*1bee64b6SR0ronoa )); 163*1bee64b6SR0ronoa } 164*1bee64b6SR0ronoa 1658a41b76cSR0ronoa pub fn refresh(&mut self) { 1668a41b76cSR0ronoa self.items.clear(); 1678a41b76cSR0ronoa self.asset_grid.clear(); 1688a41b76cSR0ronoa 1692b942a51SR0ronoa // 父目录 1702b942a51SR0ronoa let parent_asset_item = AssetItem::new("..", true); 171*1bee64b6SR0ronoa let (row, col) = self.asset_grid.add_element(&parent_asset_item); 1722b942a51SR0ronoa self.items.insert((row, col), parent_asset_item.clone()); 1732b942a51SR0ronoa 17449182ea7SR0ronoa // 读取目录中的文件列表 17549182ea7SR0ronoa if let Ok(entries) = fs::read_dir(&self.cur_path) { 17649182ea7SR0ronoa for entry in entries { 17749182ea7SR0ronoa if let Ok(item) = entry { 1782b942a51SR0ronoa let is_dir = if let Ok(metadata) = item.metadata() { 1792b942a51SR0ronoa metadata.is_dir() 1802b942a51SR0ronoa } else { 1812b942a51SR0ronoa false 1822b942a51SR0ronoa }; 1832b942a51SR0ronoa 1842b942a51SR0ronoa let asset_item = AssetItem::new(item.file_name().to_str().unwrap(), is_dir); 185*1bee64b6SR0ronoa let (row, col) = self.asset_grid.add_element(&asset_item); 1868a41b76cSR0ronoa self.items.insert((row, col), asset_item.clone()); 18749182ea7SR0ronoa } 18849182ea7SR0ronoa } 1898a41b76cSR0ronoa } else { 1908a41b76cSR0ronoa println!( 1918a41b76cSR0ronoa "[Error] AssetManager failed to read dir {:?}", 1928a41b76cSR0ronoa self.cur_path 1938a41b76cSR0ronoa ); 19449182ea7SR0ronoa } 19549182ea7SR0ronoa 1968a41b76cSR0ronoa let grid = self.asset_grid.clone(); 1978a41b76cSR0ronoa if let Some(widget) = grid.elements.borrow().get(&(0, 0)) { 19849182ea7SR0ronoa grid.focused_id.set(Some((0, 0))); 19949182ea7SR0ronoa grid.focus(widget); 20049182ea7SR0ronoa } 2018a41b76cSR0ronoa 202*1bee64b6SR0ronoa if self.init_show == true { 203*1bee64b6SR0ronoa self.init_show = false 204*1bee64b6SR0ronoa } else { 205*1bee64b6SR0ronoa self.loading_panel.draw(); 206*1bee64b6SR0ronoa } 2078a41b76cSR0ronoa self.panel.draw(); 20849182ea7SR0ronoa } 20949182ea7SR0ronoa 2108a41b76cSR0ronoa pub fn exec(&mut self) { 2118a41b76cSR0ronoa self.panel.exec(); 21249182ea7SR0ronoa } 21349182ea7SR0ronoa } 214