Quellcode durchsuchen

完善toolkit库 增加了Grid组件等 (#8)

R0ronoa vor 1 Jahr
Ursprung
Commit
6f3c183754

+ 11 - 2
starry_toolkit/src/base/panel.rs

@@ -11,7 +11,7 @@ use starry_client::{
     window::Window,
 };
 
-use crate::widgets::Widget;
+use crate::{traits::focus::Focus, widgets::Widget};
 
 use super::rect::Rect;
 
@@ -72,6 +72,8 @@ pub struct Panel {
     pub widgets: RefCell<Vec<Arc<dyn Widget>>>,
     /// 窗口是否打开
     pub running: Cell<bool>,
+    /// 当前聚焦的窗口
+    pub focused_widget: RefCell<Option<Arc<dyn Widget>>>,
 }
 
 impl Panel {
@@ -84,6 +86,7 @@ impl Panel {
             window: RefCell::new(window),
             widgets: RefCell::new(Vec::new()),
             running: Cell::new(true),
+            focused_widget: RefCell::new(None),
         }
     }
 
@@ -169,7 +172,7 @@ impl Panel {
     /// 渲染单个组件
     pub fn draw_widget(&self, renderer: &mut dyn Renderer, widget: &Arc<dyn Widget>) {
         widget.update();
-        widget.draw(renderer);
+        widget.draw(renderer, self.is_focused(widget));
 
         // 渲染子组件
         for child in widget.children().borrow().iter() {
@@ -177,3 +180,9 @@ impl Panel {
         }
     }
 }
+
+impl Focus for Panel {
+    fn focused_widget(&self) -> RefCell<Option<Arc<dyn Widget>>> {
+        self.focused_widget.clone()
+    }
+}

+ 217 - 0
starry_toolkit/src/layout/grid.rs

@@ -0,0 +1,217 @@
+use std::{
+    cell::{Cell, RefCell},
+    collections::BTreeMap,
+    sync::Arc,
+};
+
+use starry_client::base::renderer::Renderer;
+
+use crate::{
+    base::{point::Point, rect::Rect},
+    traits::{focus::Focus, transform::Transform},
+    widgets::{HorizontalPlacement, VerticalPlacement, Widget},
+};
+
+pub struct Grid {
+    pub rect: Cell<Rect>,
+    local_position: Cell<Point>,
+    vertical_placement: Cell<VerticalPlacement>,
+    horizontal_placement: Cell<HorizontalPlacement>,
+    children: RefCell<Vec<Arc<dyn Widget>>>,
+    /// x坐标间隔
+    space_x: Cell<i32>,
+    /// y坐标间隔
+    space_y: Cell<i32>,
+    /// 每行的最大列数
+    max_columns: Cell<usize>,
+    /// 当前行数
+    current_row: Cell<usize>,
+    /// 当前列数
+    current_column: Cell<usize>,
+    /// 元素字典
+    elements: RefCell<BTreeMap<(usize, usize), Arc<dyn Widget>>>,
+    /// 当前选中的元素id(行列号)
+    focused_id: Cell<Option<(usize, usize)>>,
+}
+
+impl Grid {
+    pub fn new() -> Arc<Self> {
+        Arc::new(Grid {
+            rect: Cell::new(Rect::default()),
+            local_position: Cell::new(Point::new(0, 0)),
+            vertical_placement: Cell::new(VerticalPlacement::Absolute),
+            horizontal_placement: Cell::new(HorizontalPlacement::Absolute),
+            children: RefCell::new(vec![]),
+            space_x: Cell::new(0),
+            space_y: Cell::new(0),
+            max_columns: Cell::new(0),
+            current_row: Cell::new(0),
+            current_column: Cell::new(0),
+            elements: RefCell::new(BTreeMap::new()),
+            focused_id: Cell::new(None),
+        })
+    }
+
+    /// 设置最大列数
+    pub fn set_max_columns(&self, columns: usize) -> &Self {
+        self.max_columns.set(columns);
+        self
+    }
+
+    pub fn add<T: Widget>(&self, element: &Arc<T>) {
+        if self.current_column.get() == self.max_columns.get() {
+            self.current_row.set(self.current_row.get() + 1);
+            self.current_column.set(0);
+        }
+
+        self.elements.borrow_mut().insert(
+            (self.current_row.get(), self.current_column.get()),
+            element.clone(),
+        );
+        self.current_column.set(self.current_column.get() + 1);
+        self.arrange(false);
+    }
+
+    pub fn insert<T: Widget>(&self, column: usize, row: usize, element: &Arc<T>) {
+        self.elements
+            .borrow_mut()
+            .insert((row, column), element.clone());
+
+        self.arrange(false);
+    }
+
+    pub fn clear(&self) {
+        self.elements.borrow_mut().clear();
+    }
+
+    pub fn remove(&self, column: usize, row: usize) {
+        self.elements.borrow_mut().remove(&(row, column));
+    }
+
+    pub fn set_space(&self, x: i32, y: i32) -> &Self {
+        self.space_x.set(x);
+        self.space_y.set(y);
+        self
+    }
+
+    pub fn arrange(&self, resize_children: bool) {
+        let mut cols = Vec::new();
+        let mut rows = Vec::new();
+        for (&(col, row), entry) in self.elements.borrow().iter() {
+            while col >= cols.len() {
+                cols.push(Rect::default());
+            }
+            while row >= rows.len() {
+                rows.push(Rect::default());
+            }
+            let rect = entry.rect().get();
+            if rect.width >= cols[col].width {
+                cols[col as usize].width = rect.width;
+            }
+            if rect.width >= rows[row].width {
+                rows[row as usize].width = rect.width;
+            }
+            if rect.height >= cols[col].height {
+                cols[col as usize].height = rect.height;
+            }
+            if rect.height >= rows[row].height {
+                rows[row as usize].height = rect.height;
+            }
+        }
+
+        let rect = self.rect.get();
+        let space_x = self.space_x.get();
+        let space_y = self.space_y.get();
+
+        let mut x = rect.x;
+        for col in cols.iter_mut() {
+            col.x = x;
+            x += col.width as i32 + space_x;
+        }
+
+        let mut y = rect.y;
+        for row in rows.iter_mut() {
+            row.y = y;
+            y += row.height as i32 + space_y;
+        }
+
+        for (&(col, row), child) in self.elements.borrow().iter() {
+            let mut rect = child.rect().get();
+            rect.x = cols[col].x;
+            rect.y = rows[row].y;
+            if resize_children {
+                rect.width = cols[col].width;
+                rect.height = rows[row].height;
+            }
+            child.rect().set(rect);
+
+            child.arrange();
+        }
+    }
+}
+
+impl Widget for Grid {
+    fn name(&self) -> &str {
+        "Grid"
+    }
+
+    fn rect(&self) -> &Cell<Rect> {
+        &self.rect
+    }
+
+    fn local_position(&self) -> &Cell<Point> {
+        &self.local_position
+    }
+
+    fn vertical_placement(&self) -> &Cell<VerticalPlacement> {
+        &self.vertical_placement
+    }
+
+    fn horizontal_placement(&self) -> &Cell<HorizontalPlacement> {
+        &self.horizontal_placement
+    }
+
+    fn children(&self) -> &RefCell<Vec<Arc<dyn Widget>>> {
+        &self.children
+    }
+
+    fn draw(&self, renderer: &mut dyn Renderer, _focused: bool) {
+        fn draw_widget(widget: &Arc<dyn Widget>, renderer: &mut dyn Renderer, focused: bool) {
+            widget.update();
+            widget.draw(renderer, focused);
+
+            for child in widget.children().borrow().iter() {
+                draw_widget(child, renderer, focused);
+            }
+        }
+
+        for (&(_col, _row), widget) in self.elements.borrow().iter() {
+            draw_widget(widget, renderer, self.is_focused(widget));
+        }
+    }
+}
+
+impl Transform for Grid {
+    fn reposition(&self, x: i32, y: i32) -> &Self {
+        let mut rect = self.rect().get();
+        rect.x = x;
+        rect.y = y;
+        self.rect.set(rect);
+
+        self.arrange(false);
+
+        self
+    }
+}
+
+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()));
+            }
+        }
+
+        return RefCell::new(None);
+    }
+}

+ 1 - 0
starry_toolkit/src/layout/mod.rs

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

+ 1 - 0
starry_toolkit/src/lib.rs

@@ -2,5 +2,6 @@ extern crate starry_client;
 extern crate starry_server;
 
 pub mod base;
+pub mod layout;
 pub mod traits;
 pub mod widgets;

+ 28 - 15
starry_toolkit/src/main.rs

@@ -1,11 +1,12 @@
 use starry_server::core::{SCREEN_HEIGHT, SCREEN_WIDTH};
 use starry_toolkit::{
     base::{panel::Panel, rect::Rect},
-    traits::{place::Place, text::Text},
-    widgets::{image::Image, label::Label},
+    layout::grid::Grid,
+    traits::{text::Text, transform::Transform},
+    widgets::label::Label,
 };
 
-const IMAGE_PATH: &[u8] = include_bytes!("./asset/desktop_bg.png");
+// const IMAGE_PATH: &[u8] = include_bytes!("./asset/desktop_bg.png");
 
 fn main() {
     let panel = Panel::new(
@@ -13,18 +14,30 @@ fn main() {
         "Title",
     );
 
-    // Label
-    let label = Label::new();
-    label.position(100, 100);
-    label.text("hello world");
-    label.text_offset(50, 50);
-    panel.add_child(&label);
-
-    // Image
-    let image = Image::from_path(IMAGE_PATH).unwrap();
-    image.position(0, SCREEN_HEIGHT as i32 / 2);
-    image.size(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32 / 2);
-    panel.add_child(&image);
+    let label1 = Label::new();
+    label1.text("hello world");
+
+    let label2 = Label::new();
+    label2.text("hello world");
+
+    let label3 = Label::new();
+    label3.text("hello world");
+
+    let grid = Grid::new();
+    grid.set_space(10, 10);
+    grid.resize(500, 500);
+    grid.set_max_columns(2);
+    grid.add(&label1);
+    grid.add(&label2);
+    grid.add(&label3);
+
+    panel.add_child(&grid);
+
+    // // Image
+    // let image = Image::from_path(IMAGE_PATH).unwrap();
+    // image.reposition(0, SCREEN_HEIGHT as i32 / 2);
+    // image.resize(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32 / 2);
+    // panel.add_child(&image);
 
     panel.draw();
 

+ 25 - 0
starry_toolkit/src/traits/focus.rs

@@ -0,0 +1,25 @@
+use std::{cell::RefCell, sync::Arc};
+
+use crate::widgets::Widget;
+
+pub trait Focus {
+    /// 返回当前聚焦的Widget
+    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());
+    }
+
+    /// 判断当前是否聚焦于给定Widget
+    fn is_focused(&self, widget: &Arc<dyn Widget>) -> bool {
+        if let Some(ref focused_widget) = *self.focused_widget().borrow_mut() {
+            if Arc::ptr_eq(&widget, &focused_widget) {
+                return true;
+            }
+        }
+
+        false
+    }
+}

+ 2 - 1
starry_toolkit/src/traits/mod.rs

@@ -1,2 +1,3 @@
-pub mod place;
+pub mod focus;
 pub mod text;
+pub mod transform;

+ 3 - 3
starry_toolkit/src/traits/place.rs → starry_toolkit/src/traits/transform.rs

@@ -1,7 +1,7 @@
 use crate::widgets::{HorizontalPlacement, VerticalPlacement, Widget};
 
-pub trait Place: Sized + Widget {
-    fn position(&self, x: i32, y: i32) -> &Self {
+pub trait Transform: Sized + Widget {
+    fn reposition(&self, x: i32, y: i32) -> &Self {
         let mut position = self.local_position().get();
         let mut rect = self.rect().get();
         position.x = x;
@@ -14,7 +14,7 @@ pub trait Place: Sized + Widget {
         self
     }
 
-    fn size(&self, width: u32, height: u32) -> &Self {
+    fn resize(&self, width: u32, height: u32) -> &Self {
         let mut rect = self.rect().get();
         rect.width = width;
         rect.height = height;

+ 3 - 3
starry_toolkit/src/widgets/image.rs

@@ -7,7 +7,7 @@ use starry_client::base::{color::Color, renderer::Renderer};
 
 use crate::{
     base::{point::Point, rect::Rect},
-    traits::place::Place,
+    traits::transform::Transform,
 };
 
 use super::{HorizontalPlacement, VerticalPlacement, Widget};
@@ -53,7 +53,7 @@ impl Image {
     }
 }
 
-impl Place for Image {}
+impl Transform for Image {}
 
 impl Widget for Image {
     fn name(&self) -> &str {
@@ -80,7 +80,7 @@ impl Widget for Image {
         &self.children
     }
 
-    fn draw(&self, renderer: &mut dyn Renderer) {
+    fn draw(&self, renderer: &mut dyn Renderer, _focused: bool) {
         let rect = self.rect.get();
         let image = self.image.borrow();
         renderer.image(rect.x, rect.y, rect.width, rect.height, image.data());

+ 4 - 4
starry_toolkit/src/widgets/label.rs

@@ -7,7 +7,7 @@ use starry_client::base::{color::Color, renderer::Renderer};
 
 use crate::{
     base::{point::Point, rect::Rect},
-    traits::{place::Place, text::Text},
+    traits::{text::Text, transform::Transform},
 };
 
 use super::{HorizontalPlacement, VerticalPlacement, Widget};
@@ -37,14 +37,14 @@ impl Label {
 
     fn adjust_size(&self) {
         let text = self.text.borrow();
-        self.size(
+        self.resize(
             text.len() as u32 * 8 + 2 * self.text_offset.get().x as u32,
             16 + 2 * self.text_offset.get().y as u32,
         );
     }
 }
 
-impl Place for Label {}
+impl Transform for Label {}
 
 impl Widget for Label {
     fn name(&self) -> &str {
@@ -71,7 +71,7 @@ impl Widget for Label {
         &self.children
     }
 
-    fn draw(&self, renderer: &mut dyn Renderer) {
+    fn draw(&self, renderer: &mut dyn Renderer, _focused: bool) {
         let origin_rect = self.rect().get();
         let mut current_rect = self.rect().get(); // 当前字符渲染矩形
         let origin_x = origin_rect.x;

+ 1 - 1
starry_toolkit/src/widgets/mod.rs

@@ -68,7 +68,7 @@ pub trait Widget: Any {
     }
 
     /// 渲染组件
-    fn draw(&self, renderer: &mut dyn Renderer);
+    fn draw(&self, renderer: &mut dyn Renderer, focused: bool);
 
     /// 更新组件状态
     fn update(&self) {}