From cad8f7a62dc644abe1cf9ca5bcd4fa1beab4b96f Mon Sep 17 00:00:00 2001 From: JoeZane Date: Mon, 14 Apr 2025 04:32:47 +0800 Subject: [PATCH] upd: Add async task support to `Object` and `Element`; impr: handle widget remove. --- examples/Cargo.toml | 4 ++ examples/async_task/async_task_element.rs | 16 +++++ examples/async_task/async_task_object.rs | 12 ++++ examples/async_task/async_task_widget.rs | 48 +++++++++++-- examples/async_task/main.rs | 2 + examples/remove_demo/child_widget.rs | 48 +++++++++++++ examples/remove_demo/main.rs | 23 ++++++ examples/remove_demo/remove_widget.rs | 87 +++++++++++++++++++++++ examples/window/test_widget.rs | 16 +++-- macros/src/async_task.rs | 28 ++++---- macros/src/extend_element.rs | 22 +++++- macros/src/extend_object.rs | 29 +++++++- macros/src/extend_widget.rs | 16 +++-- macros/src/layout.rs | 56 +++++++++++---- macros/src/pane.rs | 12 ++++ macros/src/scroll_area.rs | 6 ++ macros/src/split_pane.rs | 6 ++ macros/src/stack.rs | 16 +++++ tlib/src/actions.rs | 20 ++++-- tlib/src/object.rs | 4 +- tlib/src/timer.rs | 14 ++-- tmui/src/animation/frame_animator.rs | 7 +- tmui/src/animation/mgr.rs | 7 +- tmui/src/application_window.rs | 62 +++++++++++++++- tmui/src/container.rs | 5 ++ tmui/src/graphics/board.rs | 30 ++++++-- tmui/src/graphics/element.rs | 2 +- tmui/src/hbox.rs | 11 +++ tmui/src/input/focus_mgr.rs | 30 +++++++- tmui/src/loading.rs | 7 +- tmui/src/overlay.rs | 10 +++ tmui/src/pane.rs | 10 +++ tmui/src/primitive/close_handler.rs | 24 +++++-- tmui/src/scroll_area.rs | 4 ++ tmui/src/shortcut/mgr.rs | 14 +++- tmui/src/split_pane.rs | 4 ++ tmui/src/stack.rs | 13 ++++ tmui/src/vbox.rs | 10 +++ tmui/src/widget/mod.rs | 66 +++++++++++++++-- tmui/src/widget/widget_inner.rs | 10 +-- tmui/src/widget/win_widget.rs | 4 +- tmui/tests/container_test.rs | 7 +- 42 files changed, 730 insertions(+), 92 deletions(-) create mode 100644 examples/async_task/async_task_element.rs create mode 100644 examples/async_task/async_task_object.rs create mode 100644 examples/remove_demo/child_widget.rs create mode 100644 examples/remove_demo/main.rs create mode 100644 examples/remove_demo/remove_widget.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9605ec37..c0308eb5 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -113,6 +113,10 @@ path = "pane_layout/main.rs" name = "pane_widget" path = "pane_widget/main.rs" +[[bin]] +name = "remove_demo" +path = "remove_demo/main.rs" + [[bin]] name = "run_after" path = "run_after/main.rs" diff --git a/examples/async_task/async_task_element.rs b/examples/async_task/async_task_element.rs new file mode 100644 index 00000000..6c3ba326 --- /dev/null +++ b/examples/async_task/async_task_element.rs @@ -0,0 +1,16 @@ +use tlib::object::ObjectSubclass; +use tmui::prelude::*; + +#[extends(Element)] +#[async_task(name = "AsyncElement", value = "Vec")] +pub struct AsyncTaskElement {} + +impl ObjectSubclass for AsyncTaskElement { + const NAME: &'static str = "AsyncTaskElement"; +} + +impl ObjectImpl for AsyncTaskElement {} + +impl ElementImpl for AsyncTaskElement { + fn on_renderer(&mut self, _cr: &DrawingContext) {} +} diff --git a/examples/async_task/async_task_object.rs b/examples/async_task/async_task_object.rs new file mode 100644 index 00000000..d54e1558 --- /dev/null +++ b/examples/async_task/async_task_object.rs @@ -0,0 +1,12 @@ +use tlib::object::ObjectSubclass; +use tmui::prelude::*; + +#[extends(Object)] +#[async_task(name = "AsyncObject", value = "bool")] +pub struct AsyncTaskObject {} + +impl ObjectSubclass for AsyncTaskObject { + const NAME: &'static str = "AsyncTaskObject"; +} + +impl ObjectImpl for AsyncTaskObject {} diff --git a/examples/async_task/async_task_widget.rs b/examples/async_task/async_task_widget.rs index 28679514..90ae9287 100644 --- a/examples/async_task/async_task_widget.rs +++ b/examples/async_task/async_task_widget.rs @@ -6,20 +6,23 @@ use tmui::{ widget::WidgetImpl, }; +use crate::{async_task_element::AsyncTaskElement, async_task_object::AsyncTaskObject}; + #[extends(Widget)] #[async_task(name = "AsyncTask", value = "i32")] #[async_task(name = "AsyncTask2", value = "f32")] #[async_task(name = "AsyncTask3", value = "()")] -pub struct AsyncTaskWidget {} +pub struct AsyncTaskWidget { + async_object: Box, + async_element: Box, +} impl ObjectSubclass for AsyncTaskWidget { const NAME: &'static str = "AsyncTaskWidget"; } impl ObjectImpl for AsyncTaskWidget { - fn construct(&mut self) { - self.parent_construct(); - + fn initialize(&mut self) { self.async_task( async { tokio::time::sleep(Duration::from_secs(1)).await; @@ -27,7 +30,10 @@ impl ObjectImpl for AsyncTaskWidget { 12 }, |_, val| { - println!("{} => then", thread::current().name().unwrap()); + println!( + "{} => then, should be last one", + thread::current().name().unwrap() + ); assert_eq!(val, 12) }, ); @@ -44,6 +50,38 @@ impl ObjectImpl for AsyncTaskWidget { ); self.async_task3(async {}, |_, _val| {}); + + self.async_object.async_object( + async { + println!("{} => async obejct", thread::current().name().unwrap()); + true + }, + |_, val| { + println!("{} => then obejct", thread::current().name().unwrap()); + assert!(val) + }, + ); + + self.async_element.async_element( + async { + println!("{} => async element", thread::current().name().unwrap()); + vec![1, 2, 3] + }, + |_, val| { + println!("{} => then element", thread::current().name().unwrap()); + assert_eq!(val, vec![1, 2, 3]) + }, + ); + + async_do!( + { + println!("{} => async_do!", thread::current().name().unwrap()); + () + } => + || { + println!("{} => then async_do!", thread::current().name().unwrap()); + } + ) } } diff --git a/examples/async_task/main.rs b/examples/async_task/main.rs index 1426d609..4a787780 100644 --- a/examples/async_task/main.rs +++ b/examples/async_task/main.rs @@ -1,3 +1,5 @@ +mod async_task_element; +mod async_task_object; mod async_task_widget; use async_task_widget::AsyncTaskWidget; diff --git a/examples/remove_demo/child_widget.rs b/examples/remove_demo/child_widget.rs new file mode 100644 index 00000000..5cdfc95c --- /dev/null +++ b/examples/remove_demo/child_widget.rs @@ -0,0 +1,48 @@ +use tmui::{ + prelude::*, + tlib::object::{ObjectImpl, ObjectSubclass}, + widget::WidgetImpl, +}; + +#[extends(Widget)] +pub struct ChildWidget {} + +impl ObjectSubclass for ChildWidget { + const NAME: &'static str = "ChildWidget"; +} + +impl ObjectImpl for ChildWidget { + fn construct(&mut self) { + self.parent_construct(); + + self.width_request(200); + self.height_request(100); + self.set_background(Color::GREY_LIGHT); + + let mut child: Box = Object::new(&[]); + child.set_halign(Align::Center); + child.set_valign(Align::Center); + child.set_background(Color::YELLOW); + child.width_request(100); + child.height_request(100); + + let mut child_c: Box = Object::new(&[]); + child_c.set_halign(Align::Center); + child_c.set_valign(Align::Center); + child_c.set_background(Color::GREEN); + child_c.width_request(50); + child_c.height_request(50); + child.child(child_c); + + self.child(child); + } +} + +impl WidgetImpl for ChildWidget {} + +impl ChildWidget { + #[inline] + pub fn new() -> Box { + Object::new(&[]) + } +} diff --git a/examples/remove_demo/main.rs b/examples/remove_demo/main.rs new file mode 100644 index 00000000..257aed04 --- /dev/null +++ b/examples/remove_demo/main.rs @@ -0,0 +1,23 @@ +pub mod child_widget; +pub mod remove_widget; + +use remove_widget::RemoveWidget; +use tmui::{application::Application, application_window::ApplicationWindow, prelude::*}; + +fn main() { + log4rs::init_file("examples/log4rs.yaml", Default::default()).unwrap(); + + let app = Application::builder() + .width(1280) + .height(800) + .title("Remove Demo") + .build(); + + app.connect_activate(build_ui); + + app.run(); +} + +fn build_ui(window: &mut ApplicationWindow) { + window.child(RemoveWidget::new()); +} diff --git a/examples/remove_demo/remove_widget.rs b/examples/remove_demo/remove_widget.rs new file mode 100644 index 00000000..3d0382c7 --- /dev/null +++ b/examples/remove_demo/remove_widget.rs @@ -0,0 +1,87 @@ +use tmui::{ + button::Button, + label::Label, + prelude::*, + tlib::object::{ObjectImpl, ObjectSubclass}, + widget::WidgetImpl, +}; + +use crate::child_widget::ChildWidget; + +#[extends(Widget, Layout(VBox))] +#[derive(Childrenable)] +pub struct RemoveWidget { + #[children] + top: Box, + #[children] + bottom: Box, + + to_remove: ObjectId, + widget_id: ObjectId, +} + +impl ObjectSubclass for RemoveWidget { + const NAME: &'static str = "RemoveWidget"; +} + +impl ObjectImpl for RemoveWidget { + fn construct(&mut self) { + self.parent_construct(); + + self.bottom.set_margin_top(5); + + self.set_hexpand(true); + self.set_vexpand(true); + let mut button_1 = Button::new(Some("Remove Left")); + let mut button_2 = Button::new(Some("Remove Right")); + button_1.width_request(100); + button_2.width_request(100); + connect!( + button_1, + mouse_pressed(), + self, + remove_left_pressed(MouseEvent) + ); + connect!( + button_2, + mouse_pressed(), + self, + remove_right_pressed(MouseEvent) + ); + self.top.add_child(button_1); + self.top.add_child(button_2); + + self.bottom.add_child(Label::new(Some("Label 1"))); + let label2 = Label::new(Some("Label 2")); + self.to_remove = label2.id(); + self.bottom.add_child(label2); + self.bottom.add_child(Label::new(Some("Label 3"))); + self.bottom.add_child(Label::new(Some("Label 4"))); + + let child_widget = ChildWidget::new(); + self.widget_id = child_widget.id(); + self.bottom.add_child(child_widget); + } +} + +impl WidgetImpl for RemoveWidget {} + +impl RemoveWidget { + #[inline] + pub fn new() -> Box { + Object::new(&[]) + } + + pub fn remove_left_pressed(&mut self, _: MouseEvent) { + self.bottom.remove_children(self.to_remove); + } + + pub fn remove_right_pressed(&mut self, _: MouseEvent) { + self.window() + .find_id_mut(self.widget_id) + .unwrap() + .downcast_mut::() + .unwrap() + .remove_child(); + } +} diff --git a/examples/window/test_widget.rs b/examples/window/test_widget.rs index 788359d8..598c9d6a 100644 --- a/examples/window/test_widget.rs +++ b/examples/window/test_widget.rs @@ -2,9 +2,17 @@ use lazy_static::lazy_static; use log::debug; use std::time::Duration; use tlib::{ - close_handler, connect, disconnect, object::{ObjectImpl, ObjectSubclass}, timer::Timer + close_handler, connect, disconnect, + object::{ObjectImpl, ObjectSubclass}, + timer::Timer, +}; +use tmui::{ + animation::frame_animator::FrameAnimator, + prelude::*, + primitive::frame::Frame, + tlib::{figure::Color, frame_animator}, + widget::WidgetImpl, }; -use tmui::{animation::frame_animator::FrameAnimator, prelude::*, primitive::frame::Frame, tlib::{figure::Color, frame_animator}, widget::WidgetImpl}; lazy_static! { static ref COLORS: [Color; 3] = [Color::RED, Color::GREEN, Color::BLUE]; @@ -67,7 +75,7 @@ impl TestWidget { impl FrameAnimator for TestWidget { #[inline] - fn on_frame(&mut self, frame: Frame){ + fn on_frame(&mut self, frame: Frame) { println!("widget on frame: {:?}", frame) } } @@ -76,4 +84,4 @@ impl CloseHandler for TestWidget { fn handle(&mut self) { println!("Application has closed."); } -} \ No newline at end of file +} diff --git a/macros/src/async_task.rs b/macros/src/async_task.rs index cf456ea8..14cc496a 100644 --- a/macros/src/async_task.rs +++ b/macros/src/async_task.rs @@ -1,9 +1,7 @@ -use std::str::FromStr; use proc_macro2::{Ident, TokenStream}; use quote::quote; -use syn::{ - Attribute, DeriveInput, Meta, MetaList, MetaNameValue, NestedMeta, -}; +use std::str::FromStr; +use syn::{Attribute, DeriveInput, Meta, MetaList, MetaNameValue, NestedMeta}; use crate::SplitGenericsRef; @@ -32,9 +30,7 @@ impl<'a> AsyncTask<'a> { for meta in nested { match meta { NestedMeta::Meta(Meta::NameValue(MetaNameValue { - ref path, - ref lit, - .. + ref path, ref lit, .. })) => { let ident = path.get_ident().unwrap(); @@ -57,8 +53,10 @@ impl<'a> AsyncTask<'a> { }, "value" => match lit { syn::Lit::Str(lit) => { - let token = TokenStream::from_str(&lit.value()).expect("`async_task` value parse error."); - let ty = syn::parse2::(token).expect("`async_task` value parse error."); + let token = TokenStream::from_str(&lit.value()) + .expect("`async_task` value parse error."); + let ty = syn::parse2::(token) + .expect("`async_task` value parse error."); res.value = Some(ty) } _ => return None, @@ -142,12 +140,10 @@ impl<'a> AsyncTask<'a> { } )) } - _ => { - Err(syn::Error::new_spanned( - ast, - "`async_task` should defined on struct.", - )) - } + _ => Err(syn::Error::new_spanned( + ast, + "`async_task` should defined on struct.", + )), } } @@ -166,7 +162,7 @@ impl<'a> AsyncTask<'a> { let (_, ty_generics, _) = self.generics; Ok(quote!( - fn #name_snake<_F, _T>(&mut self, future: _F, then: _T) + pub fn #name_snake<_F, _T>(&mut self, future: _F, then: _T) where _F: std::future::Future + Send + 'static, _T: FnOnce(&'static mut #widget #ty_generics, #value) + 'static, diff --git a/macros/src/extend_element.rs b/macros/src/extend_element.rs index 9c0d55e1..3eee1dd3 100644 --- a/macros/src/extend_element.rs +++ b/macros/src/extend_element.rs @@ -1,4 +1,4 @@ -use crate::{extend_object, SplitGenericsRef}; +use crate::{extend_object, general_attr::GeneralAttr, SplitGenericsRef}; use proc_macro2::Ident; use quote::quote; use syn::{parse::Parser, DeriveInput}; @@ -10,6 +10,12 @@ pub(crate) fn expand( let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let general_attr = + GeneralAttr::parse(ast, (&impl_generics, &ty_generics, &where_clause), false)?; + + let async_task_clause = &general_attr.async_task_impl_clause; + let async_method_clause = &general_attr.async_task_method_clause; + match &mut ast.data { syn::Data::Struct(ref mut struct_data) => { match &mut struct_data.fields { @@ -17,6 +23,14 @@ pub(crate) fn expand( fields.named.push(syn::Field::parse_named.parse2(quote! { pub element: Element })?); + + if general_attr.is_async_task { + for async_field in general_attr.async_task_fields.iter() { + fields.named.push(syn::Field::parse_named.parse2(quote! { + #async_field + })?); + } + } } _ => { return Err(syn::Error::new_spanned( @@ -57,6 +71,8 @@ pub(crate) fn expand( #element_trait_impl_clause + #async_task_clause + impl #impl_generics ElementAcquire for #name #ty_generics #where_clause {} impl #impl_generics SuperType for #name #ty_generics #where_clause { @@ -72,6 +88,10 @@ pub(crate) fn expand( type_registry.register::<#name #ty_generics, ReflectElementImpl>(); } } + + impl #impl_generics #name #ty_generics #where_clause { + #async_method_clause + } }) } _ => Err(syn::Error::new_spanned( diff --git a/macros/src/extend_object.rs b/macros/src/extend_object.rs index 73c925e6..89e43e3e 100644 --- a/macros/src/extend_object.rs +++ b/macros/src/extend_object.rs @@ -2,7 +2,7 @@ use proc_macro2::Ident; use quote::quote; use syn::{parse::Parser, DeriveInput}; -use crate::SplitGenericsRef; +use crate::{general_attr::GeneralAttr, SplitGenericsRef}; pub(crate) fn expand( ast: &mut DeriveInput, @@ -11,6 +11,12 @@ pub(crate) fn expand( let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let general_attr = + GeneralAttr::parse(ast, (&impl_generics, &ty_generics, &where_clause), false)?; + + let async_task_clause = &general_attr.async_task_impl_clause; + let async_method_clause = &general_attr.async_task_method_clause; + match &mut ast.data { syn::Data::Struct(ref mut struct_data) => { match &mut struct_data.fields { @@ -18,6 +24,14 @@ pub(crate) fn expand( fields.named.push(syn::Field::parse_named.parse2(quote! { pub object: Object })?); + + if general_attr.is_async_task { + for async_field in general_attr.async_task_fields.iter() { + fields.named.push(syn::Field::parse_named.parse2(quote! { + #async_field + })?); + } + } } _ => { return Err(syn::Error::new_spanned( @@ -50,6 +64,8 @@ pub(crate) fn expand( #object_trait_impl_clause + #async_task_clause + impl #impl_generics ObjectAcquire for #name #ty_generics #where_clause {} impl #impl_generics SuperType for #name #ty_generics #where_clause { @@ -67,6 +83,10 @@ pub(crate) fn expand( type_registry.register::<#name #ty_generics, ReflectObjectOperation>(); } } + + impl #impl_generics #name #ty_generics #where_clause { + #async_method_clause + } }) } _ => Err(syn::Error::new_spanned( @@ -192,5 +212,12 @@ pub(crate) fn gen_object_trait_impl_clause( impl #impl_generics IsA for #name #ty_generics #where_clause {} impl #impl_generics IsA<#name #ty_generics> for #name #ty_generics #where_clause {} + + impl #impl_generics Drop for #name #ty_generics #where_clause { + fn drop(&mut self) { + self.disconnect_all(); + self.on_drop(); + } + } )) } diff --git a/macros/src/extend_widget.rs b/macros/src/extend_widget.rs index 42a2a94a..484e98bd 100644 --- a/macros/src/extend_widget.rs +++ b/macros/src/extend_widget.rs @@ -290,20 +290,28 @@ pub(crate) fn gen_widget_trait_impl_clause( #[inline] fn child<_T: WidgetImpl>(&mut self, mut child: Box<_T>) { if self.super_type().is_a(Container::static_type()) { - panic!("function `child()` was invalid in `Container`") + panic!("function `child()` was invalid in `Container`, use `add_child()` instead") } child.set_parent(self); - self.#(#widget_path).*.child_internal(child) + self.#(#widget_path).*._child_internal(child) } #[inline] unsafe fn _child_ref(&mut self, child: *mut dyn WidgetImpl) { if self.super_type().is_a(Container::static_type()) { - panic!("function `child()` was invalid in `Container`") + panic!("function `_child_ref()` was invalid in `Container`") } let child_mut = tlib::ptr_mut!(child); child_mut.set_parent(self); - self.#(#widget_path).*.child_ref_internal(child_mut) + self.#(#widget_path).*._child_ref_internal(child_mut) + } + + #[inline] + fn remove_child(&mut self) { + if self.super_type().is_a(Container::static_type()) { + panic!("function `remove_child()` was invalid in `Container`, use `remove_children()` instead") + } + self.#(#widget_path).*._remove_child_internal() } } diff --git a/macros/src/layout.rs b/macros/src/layout.rs index b530dc60..6fa24af3 100644 --- a/macros/src/layout.rs +++ b/macros/src/layout.rs @@ -1,12 +1,15 @@ use crate::{ extend_container, - pane::{generate_pane_add_child, generate_pane_impl}, + pane::{generate_pane_add_child, generate_pane_impl, generate_pane_remove_children}, scroll_area::{ generate_scroll_area_add_child, generate_scroll_area_get_children, - generate_scroll_area_impl, + generate_scroll_area_impl, generate_scroll_area_remove_children, }, - split_pane::{generate_split_pane_add_child, generate_split_pane_impl}, - stack::{generate_stack_add_child, generate_stack_impl}, + split_pane::{ + generate_split_pane_add_child, generate_split_pane_impl, + generate_split_pane_remove_children, + }, + stack::{generate_stack_add_child, generate_stack_impl, generate_stack_remove_children}, }; use proc_macro2::Ident; use quote::quote; @@ -236,19 +239,42 @@ fn gen_layout_clause( proc_macro2::TokenStream::new() }; - let add_child_clause = match (is_split_pane, is_stack, is_scroll_area, is_pane) { - (false, false, false, false) => { + let (add_child_clause, remove_children_clause) = match ( + is_split_pane, + is_stack, + is_scroll_area, + is_pane, + ) { + (false, false, false, false) => ( quote! { child.set_parent(self); ApplicationWindow::initialize_dynamic_component(child.as_mut(), self.is_in_tree()); self.container.children.push(child); self.update(); - } - } - (true, _, _, _) => generate_split_pane_add_child()?, - (_, true, _, _) => generate_stack_add_child()?, - (_, _, true, _) => generate_scroll_area_add_child(name)?, - (_, _, _, true) => generate_pane_add_child()?, + }, + quote! { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + }, + ), + (true, _, _, _) => ( + generate_split_pane_add_child()?, + generate_split_pane_remove_children()?, + ), + (_, true, _, _) => ( + generate_stack_add_child()?, + generate_stack_remove_children()?, + ), + (_, _, true, _) => ( + generate_scroll_area_add_child(name)?, + generate_scroll_area_remove_children()?, + ), + (_, _, _, true) => (generate_pane_add_child()?, generate_pane_remove_children()?), }; let children_clause = if is_scroll_area { @@ -308,12 +334,18 @@ fn gen_layout_clause( } impl ContainerImplExt for #name { + #[inline] fn add_child(&mut self, mut child: Box) where T: WidgetImpl, { #add_child_clause } + + #[inline] + fn remove_children(&mut self, id: ObjectId) { + #remove_children_clause + } } impl Layout for #name { diff --git a/macros/src/pane.rs b/macros/src/pane.rs index 452e5487..1cdb5d5e 100644 --- a/macros/src/pane.rs +++ b/macros/src/pane.rs @@ -15,6 +15,18 @@ pub(crate) fn generate_pane_add_child() -> syn::Result )) } +pub(crate) fn generate_pane_remove_children() -> syn::Result { + Ok(quote!( + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + )) +} + pub(crate) fn generate_pane_type_register(name: &Ident) -> syn::Result { Ok(quote!( type_registry.register::<#name, ReflectSizeUnifiedAdjust>(); diff --git a/macros/src/scroll_area.rs b/macros/src/scroll_area.rs index a3b7384c..286fd6e8 100644 --- a/macros/src/scroll_area.rs +++ b/macros/src/scroll_area.rs @@ -10,6 +10,12 @@ pub(crate) fn generate_scroll_area_add_child( ))) } +pub(crate) fn generate_scroll_area_remove_children() -> syn::Result { + Ok(quote!( + // TODO + )) +} + pub(crate) fn generate_scroll_area_get_children() -> syn::Result { Ok(quote!( #[inline] diff --git a/macros/src/split_pane.rs b/macros/src/split_pane.rs index 02ec7584..8ba74bc1 100644 --- a/macros/src/split_pane.rs +++ b/macros/src/split_pane.rs @@ -23,6 +23,12 @@ pub(crate) fn generate_split_pane_add_child() -> syn::Result syn::Result { + Ok(quote! { + // TODO + }) +} + pub(crate) fn generate_split_pane_impl(name: &Ident) -> syn::Result { Ok(quote! { impl SizeUnifiedAdjust for #name { diff --git a/macros/src/stack.rs b/macros/src/stack.rs index 1c85646c..72a2e259 100644 --- a/macros/src/stack.rs +++ b/macros/src/stack.rs @@ -15,6 +15,22 @@ pub(crate) fn generate_stack_add_child() -> syn::Result syn::Result { + Ok(quote! { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + if self.current_index == self.container.children.len() { + self.current_index -= 1; + } + + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + }) +} + pub(crate) fn generate_stack_inner_initial() -> syn::Result { Ok(quote! { let idx = self.current_index(); diff --git a/tlib/src/actions.rs b/tlib/src/actions.rs index edacc8d3..a152aa55 100644 --- a/tlib/src/actions.rs +++ b/tlib/src/actions.rs @@ -83,13 +83,16 @@ impl ActionHub { } #[inline] - pub fn with(f: F) -> R + pub fn with(f: F) where - F: FnOnce(&mut Self) -> R, + F: FnOnce(&mut Self), { INSTANCE_PTR.with(|ptr| { - let hub = unsafe { ptr.load(Ordering::Acquire).as_mut().unwrap() }; - f(hub) + unsafe { + if let Some(hub) = ptr.load(Ordering::Acquire).as_mut() { + f(hub) + } + }; }) } @@ -189,18 +192,25 @@ impl ActionHub { pub type FnHandleValue = Box>)>; pub trait ActionExt: ObjectOperation { + #[inline] fn connect(&self, signal: Signal, target: ObjectId, f: FnHandleValue) { ActionHub::with(|hub| hub.connect_action(signal, target, f)); } + #[inline] fn disconnect(&self, emiter: Option, signal: Option<&str>, target: Option) { ActionHub::with(|hub| hub.disconnect_action(emiter, signal, target)); } + #[inline] fn disconnect_all(&self) { - ActionHub::with(|hub| hub.disconnect_action(Some(self.id()), None, None)); + ActionHub::with(|hub| { + hub.disconnect_action(Some(self.id()), None, None); + hub.disconnect_action(None, None, Some(self.id())); + }); } + #[inline] fn create_action_with_no_param(&self, signal: Signal) -> Action { Action::with_no_param(signal) } diff --git a/tlib/src/object.rs b/tlib/src/object.rs index d9788e9d..1da17d35 100644 --- a/tlib/src/object.rs +++ b/tlib/src/object.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use crate::{ prelude::{AsAny, FromType, Reflect, ReflectTrait, TypeRegistry}, types::{IsA, ObjectType, StaticType, Type, TypeDowncast}, @@ -312,6 +311,9 @@ pub trait ObjectImpl: ObjectImplExt + InnerInitializer + TypeName { /// Override to register the reflect type info to [`TypeRegistry`] in this function. fn type_register(&self, type_registry: &mut TypeRegistry) {} + + /// Call on drop. + fn on_drop(&mut self) {} } #[reflect_trait] diff --git a/tlib/src/timer.rs b/tlib/src/timer.rs index a31a50f1..ba6d7d2e 100644 --- a/tlib/src/timer.rs +++ b/tlib/src/timer.rs @@ -104,13 +104,6 @@ pub struct Timer { triggered: Cell, } -impl Drop for Timer { - fn drop(&mut self) { - self.disconnect_all(); - TimerHub::with(|hub| hub.delete_timer(self.id())) - } -} - impl Timer { /// Normal constructor to build a `Timer` pub fn new() -> Box { @@ -181,7 +174,12 @@ impl ObjectSubclass for Timer { const NAME: &'static str = "Timer"; } -impl ObjectImpl for Timer {} +impl ObjectImpl for Timer { + #[inline] + fn on_drop(&mut self) { + TimerHub::with(|hub| hub.delete_timer(self.id())) + } +} #[cfg(test)] mod tests { diff --git a/tmui/src/animation/frame_animator.rs b/tmui/src/animation/frame_animator.rs index 600b69be..8f170472 100644 --- a/tmui/src/animation/frame_animator.rs +++ b/tmui/src/animation/frame_animator.rs @@ -1,6 +1,6 @@ use crate::{primitive::frame::Frame, widget::WidgetImpl}; use std::{cell::RefCell, ptr::NonNull}; -use tlib::{nonnull_mut, prelude::*}; +use tlib::{nonnull_mut, nonnull_ref, prelude::*}; #[reflect_trait] #[allow(unused_variables)] @@ -39,6 +39,11 @@ impl FrameAnimatorMgr { self.frame_animators.push(NonNull::new(frame_animator)) } + #[inline] + pub(crate) fn remove_frame_animator(&mut self, id: ObjectId) { + self.frame_animators.retain(|r| nonnull_ref!(r).id() != id); + } + #[inline] pub(crate) fn process(&mut self, frame: Frame) { for frame_animator in self.frame_animators.iter_mut() { diff --git a/tmui/src/animation/mgr.rs b/tmui/src/animation/mgr.rs index 5d8ca492..cf0a5417 100644 --- a/tmui/src/animation/mgr.rs +++ b/tmui/src/animation/mgr.rs @@ -1,7 +1,7 @@ use super::snapshot::Snapshot; use crate::primitive::frame::Frame; use std::{cell::RefCell, ptr::NonNull}; -use tlib::nonnull_mut; +use tlib::{nonnull_mut, nonnull_ref, object::ObjectId}; thread_local! { static INSTANCE: RefCell = RefCell::new(AnimationMgr::new()); @@ -30,6 +30,11 @@ impl AnimationMgr { self.snapshots.push(NonNull::new(snapshot)) } + #[inline] + pub(crate) fn remove_snapshot(&mut self, id: ObjectId) { + self.snapshots.retain(|r| nonnull_ref!(r).id() != id); + } + #[inline] pub(crate) fn process(&mut self, frame: Frame) { for snapshot in self.snapshots.iter_mut() { diff --git a/tmui/src/application_window.rs b/tmui/src/application_window.rs index ed458dad..c7879063 100644 --- a/tmui/src/application_window.rs +++ b/tmui/src/application_window.rs @@ -13,6 +13,7 @@ use crate::{ prelude::*, primitive::{global_watch::GlobalWatchEvent, Message}, runtime::{wed, window_context::OutputSender}, + shortcut::mgr::ShortcutMgr, tooltip::TooltipStrat, widget::{ index_children, @@ -93,6 +94,7 @@ pub struct ApplicationWindow { overlaids: IntMap, root_ancestors: Vec, win_widgets: Vec, + removed: Vec>, #[cfg(not(win_dialog))] input_dialog: Option>, @@ -473,7 +475,7 @@ impl ApplicationWindow { INTIALIZE_PHASE.with(|p| { if *p.borrow() { panic!( - "`{}` Can not add ui component in function `ObjectImpl::initialize()`.", + "`{}` Can not add ui component in function `ObjectImpl::initialize()`, add in `ObjectImpl::construct()` or after intialized.", widget.name() ) } @@ -709,6 +711,8 @@ impl ApplicationWindow { #[inline] pub(crate) fn iter_execute(&mut self) { + self.handle_removed_widget(); + self.iter_executors .iter_mut() .for_each(|hnd| nonnull_mut!(hnd).iter_execute()); @@ -718,6 +722,62 @@ impl ApplicationWindow { .for_each(|hnd| nonnull_mut!(hnd).handle_inner()); } + #[inline] + pub fn _add_removed_widget(&mut self, widget: Box) { + self.removed.push(widget); + } + + pub(crate) fn handle_removed_widget(&mut self) { + for removed in self.removed.iter_mut() { + let mut ids = vec![removed.id()]; + ids.extend(removed.children_index().iter().copied()); + + for id in ids.into_iter() { + self.widgets.remove(&id); + self.iter_executors.retain(|r| nonnull_ref!(r).id() != id); + self.shadow_mouse_watch + .retain(|r| nonnull_ref!(r).id() != id); + self.crs_win_handlers.retain(|r| nonnull_ref!(r).id() != id); + if self.focused_widget == id { + self.focused_widget = 0; + } + self.focused_widget_mem.retain(|r| *r != id); + if self.pressed_widget == id { + self.pressed_widget = 0; + } + if let Some(modal) = self.modal_widget { + if modal == id { + self.modal_widget = None; + } + } + if self.mouse_over_widget.is_some() + && nonnull_ref!(self.mouse_over_widget).id() == id + { + self.mouse_over_widget = None; + } + self.mouse_enter_widgets + .retain_mut(|r| nonnull_ref!(r).id() != id); + self.run_afters.retain_mut(|r| nonnull_ref!(r).id() != id); + for map in self.watch_map.values_mut() { + map.remove(&id); + } + self.overlaids.remove(&id); + self.root_ancestors.retain_mut(|r| *r != id); + self.win_widgets.retain_mut(|r| nonnull_ref!(r).id() != id); + + nonnull_ref!(self.board).remove_element(id); + AnimationMgr::with(|mgr| mgr.borrow_mut().remove_snapshot(id)); + FrameAnimatorMgr::with(|mgr| mgr.borrow_mut().remove_frame_animator(id)); + LoadingMgr::with(|mgr| mgr.borrow_mut().remove_loading(id)); + FocusMgr::with(|mgr| mgr.borrow_mut().remove(id)); + ShortcutMgr::with(|mgr| mgr.borrow_mut().remove_shortcut_all(id)); + CloseHandlerMgr::remove(id); + } + } + + self.removed.clear(); + } + #[inline] pub(crate) fn overlaids(&self) -> &IntMap { &self.overlaids diff --git a/tmui/src/container.rs b/tmui/src/container.rs index 29530f1b..056bced3 100644 --- a/tmui/src/container.rs +++ b/tmui/src/container.rs @@ -273,6 +273,11 @@ pub trait ContainerImplExt: ContainerImpl { fn add_child(&mut self, child: Box) where T: WidgetImpl; + + /// Go to[`Function defination`](ContainerImplExt::remove_children) (Defined in [`ContainerImplExt`]) + /// Remove the children with id + /// Children with `#[children]` annotated can't be removed. + fn remove_children(&mut self, id: ObjectId); } pub trait ContainerPointEffective { diff --git a/tmui/src/graphics/board.rs b/tmui/src/graphics/board.rs index 902841bb..e7bc0947 100644 --- a/tmui/src/graphics/board.rs +++ b/tmui/src/graphics/board.rs @@ -1,7 +1,13 @@ -use super::{drawing_context::DrawingContext, element::{ElementImpl, HierachyZ}}; +use super::{ + drawing_context::DrawingContext, + element::{ElementImpl, HierachyZ}, +}; use crate::{ - backend::Backend, opti::tracker::Tracker, primitive::{bitmap::Bitmap, frame::Frame}, - shared_widget::ReflectSharedWidgetImpl, skia_safe::Surface, + backend::Backend, + opti::tracker::Tracker, + primitive::{bitmap::Bitmap, frame::Frame}, + shared_widget::ReflectSharedWidgetImpl, + skia_safe::Surface, }; use std::{cell::RefCell, ptr::NonNull, sync::Arc}; use tipc::{ @@ -79,12 +85,24 @@ impl Board { self.element_list.borrow_mut().push(NonNull::new(element)) } + pub(crate) fn remove_element(&self, id: ObjectId) { + self.element_list + .borrow_mut() + .retain(|r| nonnull_ref!(r).id() != id); + } + #[inline] pub(crate) fn shuffle(&self) { self.element_list.borrow_mut().sort_by(|a, b| { - let a = nonnull_ref!(a).z_index(); - let b = nonnull_ref!(b).z_index(); - a.cmp(&b) + let (a, b) = (nonnull_ref!(a), nonnull_ref!(b)); + let a_z_index = a.z_index(); + let b_z_index = b.z_index(); + + if a_z_index == b_z_index { + a.id().cmp(&b.id()) + } else { + a_z_index.cmp(&b_z_index) + } }); } diff --git a/tmui/src/graphics/element.rs b/tmui/src/graphics/element.rs index 9db9ba78..09c7eebf 100644 --- a/tmui/src/graphics/element.rs +++ b/tmui/src/graphics/element.rs @@ -253,7 +253,7 @@ pub trait ElementAcquire: ElementImpl + Default {} /// The hierarchy of widget on the z-axis, the higher the numerical value, /// the higher the widget position -pub(crate) const TOP_Z_INDEX: u32 = 100000; +pub(crate) const TOP_Z_INDEX: u32 = 100000000; pub(crate) trait HierachyZ { fn z_index(&self) -> u32; diff --git a/tmui/src/hbox.rs b/tmui/src/hbox.rs index 164ce76e..be9610a4 100644 --- a/tmui/src/hbox.rs +++ b/tmui/src/hbox.rs @@ -58,6 +58,17 @@ impl ContainerImplExt for HBox { self.container.children.push(child); self.update(); } + + #[inline] + fn remove_children(&mut self, id: ObjectId) { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + } } impl Layout for HBox { diff --git a/tmui/src/input/focus_mgr.rs b/tmui/src/input/focus_mgr.rs index bbf8a8d4..6e2c09c6 100644 --- a/tmui/src/input/focus_mgr.rs +++ b/tmui/src/input/focus_mgr.rs @@ -30,7 +30,10 @@ impl FocusMgr { pub(crate) fn add(&mut self, root: ObjectId, ele: &mut dyn InputEle) { if root == 0 { - warn!("Add input ele `{}` to `FocusMgr` failed, the `root` is 0.", ele.name()); + warn!( + "Add input ele `{}` to `FocusMgr` failed, the `root` is 0.", + ele.name() + ); return; } let mut shuffle = false; @@ -54,6 +57,31 @@ impl FocusMgr { } } + pub(crate) fn remove(&mut self, id: ObjectId) { + if self.cur_root == id { + self.eles.remove(&id); + self.cur_root = 0; + self.current = None; + } else if self.cur_root == 0 { + self.current = None; + for eles in self.eles.values_mut() { + eles.retain(|r| nonnull_ref!(r).id() != id); + } + } else { + for (&root, eles) in self.eles.iter_mut() { + if let Some(index) = eles.iter().position(|r| nonnull_ref!(r).id() == id) { + eles.remove(index); + if let Some(current) = self.current { + if current >= eles.len() && self.cur_root == root && current != 0 { + self.current = Some(current - 1); + } + } + break; + } + } + } + } + /// Should pre-check the widget is `InputEle`. pub(crate) fn set_currrent(&mut self, root: ObjectId, id: Option) { self.cur_root = root; diff --git a/tmui/src/loading.rs b/tmui/src/loading.rs index ad7a1597..ae70a3a9 100644 --- a/tmui/src/loading.rs +++ b/tmui/src/loading.rs @@ -2,7 +2,7 @@ use crate::{graphics::painter::Painter, primitive::frame::Frame, widget::WidgetI use std::{cell::RefCell, ptr::NonNull}; use tlib::{ figure::{Color, FRect, Rect}, - nonnull_mut, + nonnull_mut, nonnull_ref, prelude::*, skia_safe::{ClipOp, PaintStyle}, }; @@ -150,6 +150,11 @@ impl LoadingMgr { self.loadings.push(NonNull::new(loading)) } + #[inline] + pub(crate) fn remove_loading(&mut self, id: ObjectId) { + self.loadings.retain(|r| nonnull_ref!(r).id() != id); + } + #[inline] pub(crate) fn process(&mut self, frame: Frame) { for loadings in self.loadings.iter_mut() { diff --git a/tmui/src/overlay.rs b/tmui/src/overlay.rs index ce46ad24..5489eb02 100644 --- a/tmui/src/overlay.rs +++ b/tmui/src/overlay.rs @@ -62,6 +62,16 @@ impl ContainerImplExt for Overlay { { panic!("Use function `add_overlay()` instead.") } + + fn remove_children(&mut self, id: ObjectId) { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + } } impl Layout for Overlay { diff --git a/tmui/src/pane.rs b/tmui/src/pane.rs index c0ad98e1..966d53d6 100644 --- a/tmui/src/pane.rs +++ b/tmui/src/pane.rs @@ -227,6 +227,16 @@ impl ContainerImplExt for Pane { self.container.children.push(child); self.update(); } + + fn remove_children(&mut self, id: ObjectId) { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + } } impl ContainerScaleCalculate for Pane { diff --git a/tmui/src/primitive/close_handler.rs b/tmui/src/primitive/close_handler.rs index 52f91c19..c05a9f70 100644 --- a/tmui/src/primitive/close_handler.rs +++ b/tmui/src/primitive/close_handler.rs @@ -1,5 +1,7 @@ use std::{cell::RefCell, ptr::NonNull}; -use tlib::{nonnull_mut, prelude::*}; +use tlib::{nonnull_mut, nonnull_ref, prelude::*}; + +use crate::widget::WidgetImpl; thread_local! { static INSTANCE: RefCell = RefCell::new(CloseHandlerMgr::new()); @@ -18,14 +20,24 @@ impl CloseHandlerMgr { #[inline] pub(crate) fn process() { INSTANCE.with(|ins| { - ins.borrow_mut().handlers.iter_mut().for_each(|h| nonnull_mut!(h).handle()) + ins.borrow_mut() + .handlers + .iter_mut() + .for_each(|h| nonnull_mut!(h).handle()) }) } #[inline] pub fn register(handler: &mut dyn CloseHandler) { - INSTANCE.with(|ins| { - ins.borrow_mut().handlers.push(NonNull::new(handler)) + INSTANCE.with(|ins| ins.borrow_mut().handlers.push(NonNull::new(handler))) + } + + #[inline] + pub fn remove(id: ObjectId) { + INSTANCE.with(|inst| { + inst.borrow_mut() + .handlers + .retain_mut(|r| nonnull_ref!(r).id() != id) }) } } @@ -33,6 +45,6 @@ impl CloseHandlerMgr { pub trait CloseHandlerRequire: CloseHandler {} #[reflect_trait] -pub trait CloseHandler { +pub trait CloseHandler: WidgetImpl { fn handle(&mut self); -} \ No newline at end of file +} diff --git a/tmui/src/scroll_area.rs b/tmui/src/scroll_area.rs index eaf3f9eb..9aa27833 100644 --- a/tmui/src/scroll_area.rs +++ b/tmui/src/scroll_area.rs @@ -311,6 +311,10 @@ impl ContainerImplExt for ScrollArea { { panic!("Please use `set_area()` instead in `ScrollArea`") } + + fn remove_children(&mut self, _: ObjectId) { + // TODO + } } impl Layout for ScrollArea { diff --git a/tmui/src/shortcut/mgr.rs b/tmui/src/shortcut/mgr.rs index 5d78687d..ae79c8a1 100644 --- a/tmui/src/shortcut/mgr.rs +++ b/tmui/src/shortcut/mgr.rs @@ -1,10 +1,10 @@ use super::{Shortcut, ShortcutTrigger}; use crate::widget::{WidgetHnd, WidgetImpl}; -use std::{cell::RefCell, ptr::NonNull}; use nohash_hasher::IntMap; +use std::{cell::RefCell, ptr::NonNull}; use tlib::{ events::{EventTrait, EventType, KeyEvent}, - nonnull_mut, + nonnull_mut, nonnull_ref, object::ObjectId, }; @@ -66,6 +66,16 @@ impl ShortcutMgr { .push((NonNull::new(widget), Box::new(f))); } + #[inline] + pub(crate) fn remove_shortcut_all(&mut self, id: ObjectId) { + for shortcuts in self.shortcuts.values_mut() { + shortcuts.retain(|(r, _)| nonnull_ref!(r).id() != id); + } + for shortcuts in self.global_shortcuts.values_mut() { + shortcuts.retain(|(r, _)| nonnull_ref!(r).id() != id); + } + } + #[inline] pub(crate) fn trigger(&mut self, id: ObjectId) -> bool { let mut trigged = false; diff --git a/tmui/src/split_pane.rs b/tmui/src/split_pane.rs index 63cc353d..fe420521 100644 --- a/tmui/src/split_pane.rs +++ b/tmui/src/split_pane.rs @@ -81,6 +81,10 @@ impl ContainerImplExt for SplitPane { self.container.children.push(child); self.update(); } + + fn remove_children(&mut self, _id: ObjectId) { + // TODO + } } impl Layout for SplitPane { diff --git a/tmui/src/stack.rs b/tmui/src/stack.rs index 4ee0b461..b34c3d2f 100644 --- a/tmui/src/stack.rs +++ b/tmui/src/stack.rs @@ -97,6 +97,19 @@ impl ContainerImplExt for Stack { self.container.children.push(child); self.update(); } + + fn remove_children(&mut self, id: ObjectId) { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + if self.current_index == self.container.children.len() && self.current_index != 0 { + self.current_index -= 1; + } + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + } } impl Layout for Stack { diff --git a/tmui/src/vbox.rs b/tmui/src/vbox.rs index 8a53b9d7..b3489465 100644 --- a/tmui/src/vbox.rs +++ b/tmui/src/vbox.rs @@ -60,6 +60,16 @@ impl ContainerImplExt for VBox { self.container.children.push(child); self.update(); } + + fn remove_children(&mut self, id: ObjectId) { + if let Some(index) = self.container.children.iter().position(|w| w.id() == id) { + let removed = self.container.children.remove(index); + + let window = ApplicationWindow::window(); + window._add_removed_widget(removed); + window.layout_change(self); + } + } } impl Layout for VBox { diff --git a/tmui/src/widget/mod.rs b/tmui/src/widget/mod.rs index 5943e676..1b76d87d 100644 --- a/tmui/src/widget/mod.rs +++ b/tmui/src/widget/mod.rs @@ -24,7 +24,8 @@ use derivative::Derivative; use log::error; #[cfg(verbose_logging)] use log::info; -use std::{collections::HashSet, ptr::NonNull, slice::Iter}; +use nohash_hasher::IntSet; +use std::{ptr::NonNull, slice::Iter}; use tlib::{ bitflags::bitflags, emit, @@ -47,7 +48,7 @@ pub struct Widget { child: Option>, child_ref: WidgetHnd, - children_index: HashSet, + children_index: IntSet, old_rect: FRect, old_image_rect: FRect, @@ -286,10 +287,14 @@ impl WidgetSignals for dyn WidgetImpl {} ////////////////////////////////////// Widget Implements ////////////////////////////////////// impl Widget { #[inline] - pub fn child_internal(&mut self, mut child: Box) + pub fn _child_internal(&mut self, mut child: Box) where T: WidgetImpl, { + if child.get_parent_ref().is_none() { + panic!("Do not call `child_internal()` directly, use `child()` instead.") + } + ApplicationWindow::initialize_dynamic_component(child.as_mut(), self.is_in_tree()); self.child = Some(child); @@ -297,13 +302,26 @@ impl Widget { } #[inline] - pub fn child_ref_internal(&mut self, child: &mut dyn WidgetImpl) { + pub fn _child_ref_internal(&mut self, child: &mut dyn WidgetImpl) { + if child.get_parent_ref().is_none() { + panic!("Do not call `child_internal()` directly, use `child()` instead.") + } + ApplicationWindow::initialize_dynamic_component(child, self.is_in_tree()); self.child = None; self.child_ref = NonNull::new(child); } + #[inline] + pub fn _remove_child_internal(&mut self) { + if let Some(child) = self.child.take() { + let window = ApplicationWindow::window(); + window._add_removed_widget(child); + window.layout_change(self); + } + } + /// Notify all the child widget to invalidate. #[inline] fn notify_invalidate(&mut self) { @@ -1491,16 +1509,50 @@ impl dyn WidgetImpl { impl AsMutPtr for dyn WidgetImpl {} pub trait ChildOp: WidgetImpl { - /// @see [`Widget::child_internal`](Widget)
+ /// @see [`Widget::_child_internal`](Widget::_child_internal)
/// Go to[`Function defination`](ChildOp::child) (Defined in [`ChildOp`]) fn child(&mut self, child: Box); /// # Safety /// Do not call this function directly, this crate will handle the lifetime of child widget automatically. /// - /// @see [`Widget::child_ref_internal`](Widget)
+ /// @see [`Widget::_child_ref_internal`](Widget::_child_ref_internal)
/// Go to[`Function defination`](ChildOp::_child_ref) (Defined in [`ChildOp`]) unsafe fn _child_ref(&mut self, child: *mut dyn WidgetImpl); + + /// @see [`Widget::_remove_child_internal`](Widget::_remove_child_internal)
+ /// Go to[`Function defination`](ChildOp::child) (Defined in [`ChildOp`]) + /// Remove current child. + /// Child with `#[child]` annotated can't be removed. + fn remove_child(&mut self); +} +impl ChildOp for Widget { + #[inline] + fn child<_T: WidgetImpl>(&mut self, mut child: Box<_T>) { + if self.super_type().is_a(Container::static_type()) { + panic!("function `child()` was invalid in `Container`, use `add_child()` instead") + } + child.set_parent(self); + self._child_internal(child) + } + + #[inline] + unsafe fn _child_ref(&mut self, child: *mut dyn WidgetImpl) { + if self.super_type().is_a(Container::static_type()) { + panic!("function `_child_ref()` was invalid in `Container`") + } + let child_mut = tlib::ptr_mut!(child); + child_mut.set_parent(self); + self._child_ref_internal(child_mut) + } + + #[inline] + fn remove_child(&mut self) { + if self.super_type().is_a(Container::static_type()) { + panic!("function `remove_child()` was invalid in `Container`, use `remove_children()` instead") + } + self._remove_child_internal() + } } ////////////////////////////////////// Widget Layouts impl ////////////////////////////////////// @@ -1583,7 +1635,7 @@ impl WindowAcquire for T { ////////////////////////////////////// IterExecutor ////////////////////////////////////// #[reflect_trait] -pub trait IterExecutor { +pub trait IterExecutor: WidgetImpl { /// This function will be executed in each iteration of the UI main thread loop. fn iter_execute(&mut self); } diff --git a/tmui/src/widget/widget_inner.rs b/tmui/src/widget/widget_inner.rs index bcfd0e3b..4673c3d0 100644 --- a/tmui/src/widget/widget_inner.rs +++ b/tmui/src/widget/widget_inner.rs @@ -1,6 +1,6 @@ use super::{Container, EventBubble, WidgetImpl}; use crate::graphics::painter::Painter; -use std::collections::HashSet; +use nohash_hasher::IntSet; use tlib::{ figure::{FRect, Size}, namespace::Overflow, @@ -44,9 +44,9 @@ pub(crate) trait WidgetInnerExt { fn set_manage_by_container(&mut self, manage_by_container: bool); - fn children_index(&self) -> &HashSet; + fn children_index(&self) -> &IntSet; - fn children_index_mut(&mut self) -> &mut HashSet; + fn children_index_mut(&mut self) -> &mut IntSet; fn child_image_rect_union(&self) -> &FRect; @@ -164,12 +164,12 @@ macro_rules! widget_inner_ext_impl { } #[inline] - fn children_index(&self) -> &HashSet { + fn children_index(&self) -> &IntSet { &self.widget_props().children_index } #[inline] - fn children_index_mut(&mut self) -> &mut HashSet { + fn children_index_mut(&mut self) -> &mut IntSet { &mut self.widget_props_mut().children_index } diff --git a/tmui/src/widget/win_widget.rs b/tmui/src/widget/win_widget.rs index 2f582251..0bf67165 100644 --- a/tmui/src/widget/win_widget.rs +++ b/tmui/src/widget/win_widget.rs @@ -21,7 +21,7 @@ pub trait WinWidget: WidgetImpl + WidgetSignals { pub trait CrossWinWidget {} #[reflect_trait] -pub trait CrossWinMsgHandlerInner { +pub trait CrossWinMsgHandlerInner: WidgetImpl { fn handle_inner(&mut self); } @@ -72,7 +72,7 @@ pub(crate) fn handle_win_widget_create(win_widget: &mut dyn WinWidget, inner: bo ) .inner_window(inner) .win_widget_id(win_widget.id()) - .on_activate(move |win| { + .on_activate(move |win| { win.set_transparency(0); child_proc_fn(win); }), diff --git a/tmui/tests/container_test.rs b/tmui/tests/container_test.rs index 46473125..dc296b37 100644 --- a/tmui/tests/container_test.rs +++ b/tmui/tests/container_test.rs @@ -1,6 +1,9 @@ use tlib::object::{ObjectImpl, ObjectSubclass}; use tmui::{ - container::{ContainerImpl, ContainerImplExt, ContainerScaleCalculate, SCALE_ADAPTION, ContainerLayoutEnum}, + container::{ + ContainerImpl, ContainerImplExt, ContainerLayoutEnum, ContainerScaleCalculate, + SCALE_ADAPTION, + }, label::Label, prelude::*, widget::{Widget, WidgetImpl}, @@ -48,6 +51,8 @@ impl ContainerImplExt for TestContainer { { self.container.children.push(child) } + + fn remove_children(&mut self, _: ObjectId) {} } impl Layout for TestContainer {