xref: /StarryEngine/starry_applications/src/asset_manager/code/mod.rs (revision 282ef85ca13266353a8fb594bdc60918a2715518)
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