diff --git a/examples/hello.rs b/examples/hello.rs index a11b738..9ba12d2 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -7,7 +7,7 @@ use eframe::{egui, NativeOptions}; use egui::color_picker::{color_picker_color32, Alpha}; use egui::{Color32, RichText, Slider}; -use egui_dock::{DockArea, NodeIndex, Style, TabBuilder, Tree}; +use egui_dock::{DockArea, DynamicTree, NodeIndex, Style, TabBuilder}; fn main() { let options = NativeOptions::default(); @@ -27,7 +27,7 @@ struct MyContext { struct MyApp { _context: Rc>, style: Rc>, - tree: Tree, + tree: DynamicTree, } impl Default for MyApp { @@ -164,7 +164,7 @@ impl Default for MyApp { }) .build(); - let mut tree = Tree::new(vec![node_tree, style_editor]); + let mut tree = DynamicTree::new(vec![node_tree, style_editor]); let [a, b] = tree.split_left(NodeIndex::root(), 0.3, vec![inspector]); let [_, _] = tree.split_below(a, 0.7, vec![files, assets]); @@ -181,6 +181,8 @@ impl Default for MyApp { impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { let style = self.style.borrow().clone(); - DockArea::new(&mut self.tree).style(style).show(ctx); + DockArea::new(&mut self.tree) + .style(style) + .show(ctx, &mut egui_dock::DynamicTabViewer {}); } } diff --git a/examples/simple.rs b/examples/simple.rs index ebd6fde..08beb99 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -2,7 +2,7 @@ use eframe::{egui, NativeOptions}; -use egui_dock::{DockArea, NodeIndex, Style, TabBuilder, Tree}; +use egui_dock::{DockArea, NodeIndex, Style, Tree}; fn main() { let options = NativeOptions::default(); @@ -13,49 +13,32 @@ fn main() { ); } +struct TabViewer {} + +impl egui_dock::TabViewer for TabViewer { + type Tab = String; + + fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) { + ui.label(format!("Content of {tab}")); + } + + fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { + (&*tab).into() + } +} + struct MyApp { - tree: Tree, + tree: Tree, } impl Default for MyApp { fn default() -> Self { - let tab1 = TabBuilder::default() - .title("Tab 1") - .content(|ui| { - ui.label("Tab 1"); - }) - .build(); - let tab2 = TabBuilder::default() - .title("Tab 2") - .content(|ui| { - ui.label("Tab 2"); - }) - .build(); - let tab3 = TabBuilder::default() - .title("Tab 3") - .content(|ui| { - ui.label("Tab 3"); - }) - .build(); - let tab4 = TabBuilder::default() - .title("Tab 4") - .content(|ui| { - ui.label("Tab 4"); - }) - .build(); - let tab5 = TabBuilder::default() - .title("Tab 5") - .content(|ui| { - ui.label("Tab 5"); - }) - .build(); - - let mut tree = Tree::new(vec![tab1, tab2]); + let mut tree = Tree::new(vec!["tab1".to_owned(), "tab2".to_owned()]); // You can modify the tree before constructing the dock - let [a, b] = tree.split_left(NodeIndex::root(), 0.3, vec![tab3]); - let [_, _] = tree.split_below(a, 0.7, vec![tab4]); - let [_, _] = tree.split_below(b, 0.5, vec![tab5]); + let [a, b] = tree.split_left(NodeIndex::root(), 0.3, vec!["tab3".to_owned()]); + let [_, _] = tree.split_below(a, 0.7, vec!["tab4".to_owned()]); + let [_, _] = tree.split_below(b, 0.5, vec!["tab5".to_owned()]); Self { tree } } @@ -65,6 +48,6 @@ impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { DockArea::new(&mut self.tree) .style(Style::from_egui(ctx.style().as_ref())) - .show(ctx); + .show(ctx, &mut TabViewer {}); } } diff --git a/examples/traits.rs b/examples/traits.rs index ef4d0fa..01f8350 100644 --- a/examples/traits.rs +++ b/examples/traits.rs @@ -6,7 +6,7 @@ use egui::{ Window, }; -use egui_dock::{DockArea, NodeIndex, TabBuilder, TabTrait, Tree}; +use egui_dock::{DockArea, DynamicTab, DynamicTree, NodeIndex, TabBuilder}; fn main() { let options = NativeOptions::default(); @@ -18,7 +18,7 @@ fn main() { } struct MyApp { - tree: Tree, + tree: DynamicTree, } impl Default for MyApp { @@ -50,7 +50,7 @@ impl Default for MyApp { }) .build(); - let mut tree = Tree::new(vec![tab1, tab2]); + let mut tree = DynamicTree::new(vec![tab1, tab2]); // You can modify the tree before constructing the dock let [a, b] = tree.split_left(NodeIndex::root(), 0.3, vec![tab3]); @@ -71,7 +71,7 @@ impl eframe::App for MyApp { .push_to_focused_leaf(Box::new(Editor::new("New Text".into()))); } }); - DockArea::new(&mut self.tree).show(ctx); + DockArea::new(&mut self.tree).show(ctx, &mut egui_dock::DynamicTabViewer {}); } } @@ -100,7 +100,7 @@ impl Editor { } } -impl TabTrait for Editor { +impl DynamicTab for Editor { fn ui(&mut self, ui: &mut Ui) { if self.show_save { Window::new("Save") diff --git a/src/lib.rs b/src/lib.rs index fb0dd8e..bdc2c9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,14 +47,16 @@ pub use style::{Style, StyleBuilder}; use tree::TabIndex; use utils::*; -pub use self::tab::{TabBuilder, TabTrait}; +pub use self::dynamic_tab::{DynamicTab, DynamicTabViewer, DynamicTree, TabBuilder}; pub use self::tree::{Node, NodeIndex, Split, Tree}; +mod dynamic_tab; mod style; -mod tab; mod tree; mod utils; +// ---------------------------------------------------------------------------- + struct HoverData { rect: Rect, tabs: Option, @@ -120,6 +122,7 @@ impl State { // ---------------------------------------------------------------------------- +/// How we view a tab when its in a [`Tree`]. pub trait TabViewer { type Tab; diff --git a/src/tab.rs b/src/tab.rs deleted file mode 100644 index 11e7997..0000000 --- a/src/tab.rs +++ /dev/null @@ -1,151 +0,0 @@ -use egui::style::Margin; -use egui::{Frame, ScrollArea, Ui, WidgetText}; - -pub type TabContent = Box; -pub type OnClose = Box bool + 'static>; -pub type ForceClose = Box bool + 'static>; - -pub struct TabBuilder { - title: Option, - inner_margin: Margin, - add_content: Option, - on_close: Option, - force_close: Option, -} - -/// Dockable tab that can be used in `Tree`s. -pub trait TabTrait { - /// Actual tab content. - fn ui(&mut self, ui: &mut Ui); - - /// The title to be displayed. - fn title(&mut self) -> WidgetText; - - /// This is called when the tabs close button is pressed. - /// - /// Returns `true` if the tab should close immediately, `false` otherwise. - /// - /// NOTE if returning false `ui` will still be called once more if this tab is active. - fn on_close(&mut self) -> bool { - true - } - - /// This is called every frame after `ui` is called (if the tab is active). - /// - /// Returns `true` if the tab should be forced to close, `false` otherwise. - /// - /// In the event this function returns true the tab will be removed without calling `on_close`. - fn force_close(&mut self) -> bool { - false - } -} - -pub struct BuiltTab { - pub title: WidgetText, - pub inner_margin: Margin, - pub add_content: TabContent, - on_close: Option, - force_close: Option, -} - -impl TabTrait for BuiltTab { - fn ui(&mut self, ui: &mut Ui) { - ScrollArea::both() - .id_source(self.title.text().to_string() + " - egui_dock::Tab") - .show(ui, |ui| { - Frame::none() - .inner_margin(self.inner_margin) - .show(ui, |ui| { - let available_rect = ui.available_rect_before_wrap(); - ui.expand_to_include_rect(available_rect); - (self.add_content)(ui); - }); - }); - } - - fn title(&mut self) -> WidgetText { - self.title.clone() - } - - fn on_close(&mut self) -> bool { - match &mut self.on_close { - Some(on_close) => on_close(), - None => true, - } - } - - fn force_close(&mut self) -> bool { - match &mut self.force_close { - Some(force_close) => force_close(), - None => false, - } - } -} - -impl Default for TabBuilder { - fn default() -> Self { - Self { - title: None, - inner_margin: Margin::same(4.0), - add_content: None, - on_close: None, - force_close: None, - } - } -} - -impl TabBuilder { - /// Constructs a `Tab` out of accumulated data. - /// - /// # Panics - /// Panics if `title` or `add_contents` is unset. - pub fn build(self) -> Box { - Box::new(BuiltTab { - title: self.title.expect("Missing tab title"), - inner_margin: self.inner_margin, - add_content: self.add_content.expect("Missing tab content"), - on_close: self.on_close, - force_close: self.force_close, - }) - } - - /// Sets the text displayed in the tab bar. - pub fn title(mut self, title: impl Into) -> Self { - self.title = Some(title.into()); - self - } - - /// Sets the margins around the tab's content. - pub fn inner_margin(mut self, margin: Margin) -> Self { - self.inner_margin = margin; - self - } - - /// Sets the function that adds content to the tab. - pub fn content(mut self, add_content: impl FnMut(&mut Ui) + 'static) -> Self { - self.add_content = Some(Box::new(add_content)); - self - } - - /// Sets the function that is called when the close button is pressed. - /// The function should return `true` if the tab should close immediately, `false` otherwise. - /// - /// If no function is set the default behavior is to always return true. - /// - /// See [Tab](crate::tab::Tab) `on_close` for more detail - pub fn on_close(mut self, on_close: impl FnMut() -> bool + 'static) -> Self { - self.on_close = Some(Box::new(on_close)); - self - } - - /// Sets the function that is called every frame to determine if the tab should close. - /// The function should return `true` if the tab should be forced to close, `false` otherwise. - /// - /// If no function is set the default behavior is to always return false. - /// - /// See [Tab](crate::tab::Tab) `force_close` for more detail - pub fn force_close(mut self, force_close: impl FnMut() -> bool + 'static) -> Self { - self.force_close = Some(Box::new(force_close)); - self - } -}