Przeglądaj źródła

增加了starry_applications文件夹 开始实现基本的资源管理器 (#9)

* 增加了starry_applications文件夹 

* 开始实现基本的资源管理器
R0ronoa 1 rok temu
rodzic
commit
49182ea7bc

+ 1 - 0
.vscode/settings.json

@@ -3,5 +3,6 @@
         "./starry_client/Cargo.toml",
         "./starry_server/Cargo.toml",
         "./starry_toolkit/Cargo.toml",
+        "./starry_applications/Cargo.toml",
     ],
 }

+ 2 - 0
Makefile

@@ -2,8 +2,10 @@ fmt:
 	@ $(MAKE) -C starry_client fmt
 	@ $(MAKE) -C starry_server fmt
 	@ $(MAKE) -C starry_toolkit fmt
+	@ $(MAKE) -C starry_applications fmt
 
 fmt-check:
 	@ $(MAKE) -C starry_client fmt-check
 	@ $(MAKE) -C starry_server fmt-check
 	@ $(MAKE) -C starry_toolkit fmt-check
+	@ $(MAKE) -C starry_applications fmt-check

+ 13 - 0
starry_applications/Cargo.toml

@@ -0,0 +1,13 @@
+[package]
+name = "starry_apps"
+version = "0.1.0"
+edition = "2021"
+description = "The Applications of Starry Engine"
+authors = [ "2447742618 <2447742618@qq.com>" ]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+starry_client = {path = "../starry_client" }
+starry_toolkit = {path = "../starry_toolkit" }
+starry_server = {path = "../starry_server" }

+ 53 - 0
starry_applications/Makefile

@@ -0,0 +1,53 @@
+ifdef DADK_CURRENT_BUILD_DIR
+# 如果是在dadk中编译,那么安装到dadk的安装目录中
+	INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
+else
+# 如果是在本地编译,那么安装到当前目录下的install目录中
+	INSTALL_DIR = ./install
+endif
+
+ifeq ($(ARCH), x86_64)
+	export RUST_TARGET=x86_64-unknown-linux-musl
+else ifeq ($(ARCH), riscv64)
+	export RUST_TARGET=riscv64gc-unknown-linux-gnu
+else 
+# 默认为x86_86,用于本地编译
+	export RUST_TARGET=x86_64-unknown-linux-musl
+endif
+
+run:
+	cargo run --target $(RUST_TARGET)
+
+build:
+	cargo build --target $(RUST_TARGET)
+
+clean:
+	cargo clean --target $(RUST_TARGET)
+
+test:
+	cargo test --target $(RUST_TARGET)
+
+doc:
+	cargo doc --target $(RUST_TARGET)
+
+fmt:
+	cargo fmt
+
+fmt-check:
+	cargo fmt --check
+
+run-release:
+	cargo run --target $(RUST_TARGET) --release
+
+build-release:
+	cargo build --target $(RUST_TARGET) --release
+
+clean-release:
+	cargo clean --target $(RUST_TARGET) --release
+
+test-release:
+	cargo test --target $(RUST_TARGET) --release
+
+.PHONY: install
+install:
+	cargo install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force

+ 122 - 0
starry_applications/src/asset_manager/code/asset_item.rs

@@ -0,0 +1,122 @@
+use std::{
+    cell::{Cell, RefCell},
+    sync::Arc,
+};
+
+use starry_client::base::{color::Color, renderer::Renderer};
+use starry_toolkit::{
+    base::{point::Point, rect::Rect},
+    traits::{text::Text, transform::Transform},
+    widgets::{image::Image, label::Label, HorizontalPlacement, VerticalPlacement, Widget},
+};
+
+use crate::starry_server::base::image::Image as ImageResource;
+
+const FILE_ICON_PATH: &[u8] = include_bytes!("../resource/file_icon.png");
+const DIR_ICON_PATH: &[u8] = include_bytes!("../resource/dir_icon.png");
+
+pub enum AssetType {
+    Folder,
+    File,
+}
+
+pub struct AssetItem {
+    pub rect: Cell<Rect>,
+    local_position: Cell<Point>,
+    vertical_placement: Cell<VerticalPlacement>,
+    horizontal_placement: Cell<HorizontalPlacement>,
+    children: RefCell<Vec<Arc<dyn Widget>>>,
+    /// 缓存值
+    cache_focused: Cell<bool>,
+}
+
+impl AssetItem {
+    pub const ITEM_WIDTH: u32 = 144;
+    pub const ITEM_HEIGHT: u32 = 144;
+
+    pub fn new(file_name: &str, is_dir: bool) -> Self {
+        let item = AssetItem {
+            rect: Cell::new(Rect::new(0, 0, Self::ITEM_WIDTH, Self::ITEM_HEIGHT)),
+            local_position: Cell::new(Point::new(0, 0)),
+            vertical_placement: Cell::new(VerticalPlacement::Absolute),
+            horizontal_placement: Cell::new(HorizontalPlacement::Absolute),
+            children: RefCell::new(Vec::new()),
+            cache_focused: Cell::new(false),
+        };
+
+        // 背景Image
+        let bg = Image::from_color(160, 160, Color::rgba(0, 0, 0, 0));
+        item.add_child(bg);
+
+        // 文件图标Image
+        if let Some(icon) = match is_dir {
+            true => ImageResource::from_path(DIR_ICON_PATH),
+            false => ImageResource::from_path(FILE_ICON_PATH),
+        } {
+            let icon = Image::from_image(icon);
+            icon.horizontal_placement().set(HorizontalPlacement::Center);
+            icon.vertical_placement().set(VerticalPlacement::Top);
+            item.add_child(icon);
+        }
+
+        // 文件名Label
+        let name = Label::new();
+        name.text(file_name);
+        name.horizontal_placement().set(HorizontalPlacement::Center);
+        name.vertical_placement().set(VerticalPlacement::Bottom);
+        item.add_child(name);
+
+        return item;
+    }
+}
+
+impl Transform for AssetItem {}
+
+impl Widget for AssetItem {
+    fn name(&self) -> &str {
+        "AssetItem"
+    }
+
+    fn rect(&self) -> &Cell<Rect> {
+        &self.rect
+    }
+
+    fn vertical_placement(&self) -> &Cell<VerticalPlacement> {
+        &self.vertical_placement
+    }
+
+    fn horizontal_placement(&self) -> &Cell<HorizontalPlacement> {
+        &self.horizontal_placement
+    }
+
+    fn local_position(&self) -> &Cell<Point> {
+        &self.local_position
+    }
+
+    fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>> {
+        &self.children
+    }
+
+    fn draw(&self, renderer: &mut dyn Renderer, focused: bool) {
+        if focused != self.cache_focused.get() {
+            self.cache_focused.set(focused);
+
+            // 如果当前被选中,则背景高亮
+            let mut children = self.children.borrow_mut();
+            if focused {
+                children[0] = Image::from_color(
+                    Self::ITEM_WIDTH,
+                    Self::ITEM_HEIGHT,
+                    Color::rgba(0, 255, 255, 128),
+                );
+            } else {
+                children[0] =
+                    Image::from_color(Self::ITEM_WIDTH, Self::ITEM_HEIGHT, Color::rgba(0, 0, 0, 0));
+            }
+        }
+
+        for child in self.children.borrow().iter() {
+            child.draw(renderer, focused);
+        }
+    }
+}

+ 76 - 0
starry_applications/src/asset_manager/code/mod.rs

@@ -0,0 +1,76 @@
+use crate::starry_toolkit::traits::focus::Focus;
+use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH};
+use starry_toolkit::{
+    base::{panel::Panel, rect::Rect},
+    layout::grid::Grid,
+    traits::transform::Transform,
+    widgets::image::Image,
+};
+use std::{cell::RefCell, fs, sync::Arc};
+
+use self::asset_item::AssetItem;
+
+use crate::starry_server::base::image::Image as ImageResource;
+
+pub mod asset_item;
+
+const DESKTOP_BG_PATH: &[u8] = include_bytes!("../resource/desktop_bg.png");
+
+pub struct AssetViewer {
+    cur_path: String,
+    asset_grid: RefCell<Arc<Grid>>,
+}
+
+impl AssetViewer {
+    pub fn new() -> Self {
+        AssetViewer {
+            cur_path: String::from("/"),
+            asset_grid: RefCell::new(Grid::new()),
+        }
+    }
+
+    pub fn init(&self) {
+        let grid = self.asset_grid.borrow();
+        grid.resize(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32);
+        grid.reposition(0, 0);
+        grid.set_max_columns(5);
+        grid.set_space(20, 20);
+    }
+
+    pub fn refresh(&self) {
+        // 读取目录中的文件列表
+        if let Ok(entries) = fs::read_dir(&self.cur_path) {
+            for entry in entries {
+                if let Ok(item) = entry {
+                    let item = AssetItem::new(
+                        item.file_name().to_str().unwrap(),
+                        item.metadata().unwrap().is_dir(),
+                    );
+                    self.asset_grid.borrow_mut().add(&Arc::new(item));
+                }
+            }
+        }
+
+        // TODO 代码整理
+        let grid = self.asset_grid.borrow_mut();
+        let elements = grid.elements.borrow();
+        if let Some(widget) = elements.get(&(0, 0)) {
+            grid.focused_id.set(Some((0, 0)));
+            grid.focus(widget);
+        }
+    }
+
+    pub fn draw(&self) {
+        let panel = Panel::new(
+            Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32),
+            "Title",
+        );
+
+        panel.add_child(&Image::from_image(
+            ImageResource::from_path(DESKTOP_BG_PATH).unwrap(),
+        ));
+
+        panel.add_child(&(self.asset_grid.borrow()));
+        panel.draw();
+    }
+}

+ 1 - 0
starry_applications/src/asset_manager/mod.rs

@@ -0,0 +1 @@
+pub mod code;

+ 0 - 0
starry_server/src/asset/desktop_bg.png → starry_applications/src/asset_manager/resource/desktop_bg.png


BIN
starry_applications/src/asset_manager/resource/dir_icon.png


BIN
starry_applications/src/asset_manager/resource/file_icon.png


+ 5 - 0
starry_applications/src/lib.rs

@@ -0,0 +1,5 @@
+extern crate starry_client;
+extern crate starry_server;
+extern crate starry_toolkit;
+
+pub mod asset_manager;

+ 10 - 0
starry_applications/src/main.rs

@@ -0,0 +1,10 @@
+use starry_apps::asset_manager::code::AssetViewer;
+
+fn main() {
+    let viewer = AssetViewer::new();
+    viewer.init();
+    viewer.refresh();
+    viewer.draw();
+
+    loop {}
+}

+ 1 - 1
starry_server/Cargo.toml

@@ -2,7 +2,7 @@
 name = "starry_server"
 version = "0.1.0"
 edition = "2021"
-description = "The server of Starry Engine"
+description = "The Server of Starry Engine"
 authors = [ "2447742618 <2447742618@qq.com>" ]
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

+ 2 - 2
starry_server/src/core/mod.rs

@@ -25,8 +25,8 @@ pub const SCREEN_WIDTH: usize = 1440;
 #[allow(dead_code)]
 pub const SCREEN_HEIGHT: usize = 900;
 
-static DESKTOP_BG: &[u8] = include_bytes!("../asset/desktop_bg.png");
-static CURSOR_NORMAL: &[u8] = include_bytes!("../asset/cursor_normal.png");
+static DESKTOP_BG: &[u8] = include_bytes!("../resource/desktop_bg.png");
+static CURSOR_NORMAL: &[u8] = include_bytes!("../resource/cursor_normal.png");
 
 static mut STARRY_SERVER: Option<Arc<StarryServer>> = None;
 

+ 0 - 0
starry_server/src/asset/cursor_normal.png → starry_server/src/resource/cursor_normal.png


BIN
starry_server/src/resource/desktop_bg.png


+ 4 - 0
starry_toolkit/src/base/panel.rs

@@ -185,4 +185,8 @@ impl Focus for Panel {
     fn focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>> {
         self.focused_widget.clone()
     }
+
+    fn focus(&self, widget: &Arc<dyn Widget>) {
+        (*self.focused_widget.borrow_mut()) = Some(widget.clone());
+    }
 }

+ 9 - 8
starry_toolkit/src/layout/grid.rs

@@ -29,9 +29,11 @@ pub struct Grid {
     /// 当前列数
     current_column: Cell<usize>,
     /// 元素字典
-    elements: RefCell<BTreeMap<(usize, usize), Arc<dyn Widget>>>,
+    pub elements: RefCell<BTreeMap<(usize, usize), Arc<dyn Widget>>>,
     /// 当前选中的元素id(行列号)
-    focused_id: Cell<Option<(usize, usize)>>,
+    pub focused_id: Cell<Option<(usize, usize)>>,
+    /// 当前聚焦的widget
+    pub focused_widget: RefCell<Option<Arc<dyn Widget>>>,
 }
 
 impl Grid {
@@ -49,6 +51,7 @@ impl Grid {
             current_column: Cell::new(0),
             elements: RefCell::new(BTreeMap::new()),
             focused_id: Cell::new(None),
+            focused_widget: RefCell::new(None),
         })
     }
 
@@ -206,12 +209,10 @@ impl Transform for Grid {
 
 impl Focus for Grid {
     fn focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>> {
-        if let Some((row, column)) = self.focused_id.get() {
-            if let Some(widget) = self.elements.borrow().get(&(row, column)) {
-                return RefCell::new(Some((*widget).clone()));
-            }
-        }
+        self.focused_widget.clone()
+    }
 
-        return RefCell::new(None);
+    fn focus(&self, widget: &Arc<dyn Widget>) {
+        (*self.focused_widget.borrow_mut()) = Some(widget.clone());
     }
 }

+ 0 - 2
starry_toolkit/src/main.rs

@@ -6,8 +6,6 @@ use starry_toolkit::{
     widgets::label::Label,
 };
 
-// const IMAGE_PATH: &[u8] = include_bytes!("./asset/desktop_bg.png");
-
 fn main() {
     let panel = Panel::new(
         Rect::new(0, 0, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32),

+ 1 - 4
starry_toolkit/src/traits/focus.rs

@@ -7,10 +7,7 @@ pub trait Focus {
     fn focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>>;
 
     /// 聚焦于给定Widget
-    fn focused(&self, widget: &Arc<dyn Widget>) {
-        let focused = self.focused_widget();
-        (*focused.borrow_mut()) = Some(widget.clone());
-    }
+    fn focus(&self, widget: &Arc<dyn Widget>);
 
     /// 判断当前是否聚焦于给定Widget
     fn is_focused(&self, widget: &Arc<dyn Widget>) -> bool {

+ 10 - 6
starry_toolkit/src/widgets/image.rs

@@ -12,7 +12,7 @@ use crate::{
 
 use super::{HorizontalPlacement, VerticalPlacement, Widget};
 
-use crate::starry_server::base::image::Image as ImageAsset;
+use crate::starry_server::base::image::Image as ImageResource;
 
 pub struct Image {
     pub rect: Cell<Rect>,
@@ -21,19 +21,23 @@ pub struct Image {
     horizontal_placement: Cell<HorizontalPlacement>,
     children: RefCell<Vec<Arc<dyn Widget>>>,
     /// 图像源数据
-    pub image: RefCell<ImageAsset>,
+    pub image: RefCell<ImageResource>,
 }
 
 impl Image {
     pub fn new(width: u32, height: u32) -> Arc<Self> {
-        Self::from_image(ImageAsset::new(width as i32, height as i32))
+        Self::from_image(ImageResource::new(width as i32, height as i32))
     }
 
     pub fn from_color(width: u32, height: u32, color: Color) -> Arc<Self> {
-        Self::from_image(ImageAsset::from_color(width as i32, height as i32, color))
+        Self::from_image(ImageResource::from_color(
+            width as i32,
+            height as i32,
+            color,
+        ))
     }
 
-    pub fn from_image(image: ImageAsset) -> Arc<Self> {
+    pub fn from_image(image: ImageResource) -> Arc<Self> {
         Arc::new(Image {
             rect: Cell::new(Rect::new(0, 0, image.width() as u32, image.height() as u32)),
             local_position: Cell::new(Point::new(0, 0)),
@@ -45,7 +49,7 @@ impl Image {
     }
 
     pub fn from_path(path: &[u8]) -> Option<Arc<Self>> {
-        if let Some(image) = ImageAsset::from_path(path) {
+        if let Some(image) = ImageResource::from_path(path) {
             Some(Self::from_image(image))
         } else {
             None

+ 1 - 6
starry_toolkit/src/widgets/label.rs

@@ -89,12 +89,7 @@ impl Widget for Label {
                 {
                     // 默认渲染白色字体
                     // TODO 应用主题(Theme)颜色
-                    renderer.char(
-                        current_rect.x,
-                        current_rect.y,
-                        char,
-                        Color::rgb(255, 255, 255),
-                    );
+                    renderer.char(current_rect.x, current_rect.y, char, Color::rgb(0, 0, 0));
                 }
                 current_rect.x += 8;
             }

+ 55 - 2
starry_toolkit/src/widgets/mod.rs

@@ -73,6 +73,59 @@ pub trait Widget: Any {
     /// 更新组件状态
     fn update(&self) {}
 
-    /// TODO
-    fn arrange(&self) {}
+    /// 重新排布子对象
+    /// TODO 增加margin字段后相应处理
+    fn arrange(&self) {
+        let parent_rect = self.rect().get();
+
+        for child in self.children().borrow_mut().iter() {
+            let mut child_rect = child.rect().get();
+            let child_position = child.local_position().get();
+
+            match child.vertical_placement().get() {
+                VerticalPlacement::Absolute => {
+                    child_rect.y = parent_rect.y + child_position.y;
+                }
+                VerticalPlacement::Stretch => {
+                    child_rect.height = parent_rect.height;
+                    child_rect.y = parent_rect.y;
+                }
+                VerticalPlacement::Top => {
+                    child_rect.y = parent_rect.y;
+                }
+                VerticalPlacement::Center => {
+                    child_rect.y = parent_rect.y + parent_rect.height as i32 / 2
+                        - child_rect.height as i32 / 2;
+                }
+                VerticalPlacement::Bottom => {
+                    child_rect.y =
+                        parent_rect.y + parent_rect.height as i32 - child_rect.height as i32;
+                }
+            }
+
+            match child.horizontal_placement().get() {
+                HorizontalPlacement::Absolute => {
+                    child_rect.x = parent_rect.x + child_position.x;
+                }
+                HorizontalPlacement::Stretch => {
+                    child_rect.width = parent_rect.width;
+                    child_rect.x = parent_rect.x;
+                }
+                HorizontalPlacement::Left => {
+                    child_rect.x = parent_rect.x;
+                }
+                HorizontalPlacement::Center => {
+                    child_rect.x =
+                        parent_rect.x + parent_rect.width as i32 / 2 - child_rect.width as i32 / 2;
+                }
+                HorizontalPlacement::Right => {
+                    child_rect.x =
+                        parent_rect.x + parent_rect.width as i32 - child_rect.width as i32;
+                }
+            }
+
+            child.rect().set(child_rect);
+            child.arrange();
+        }
+    }
 }