Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: TabViewer #24

Merged
merged 9 commits into from
Sep 2, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
- `TabBuilder` for the `BuiltTab`
- Support for all implementations of `Into<WidgetText>` in tab titles
- Style editor in the `hello` example
- Added `Tree::find_tab`
emilk marked this conversation as resolved.
Show resolved Hide resolved

## Changed

- If a tab is dropped onto the tab bar it will be inserted into the index that it is dropped onto.
- Now when you drag a tab it has an outline along the entire length of the edges of it
- Bumped MSRV to `1.62`
- `Tree` is now generic over how you want to represent a tab

## Breaking changes

Expand All @@ -25,6 +27,7 @@
- Renamed `Style::separator_size` to `Style::separator_width`
- Removed `Style::tab_text_color` as you can now set the tab text color of a tab by passing `RichText` for its title
- Removed the requirement of creating your own Context type
- Renamed `Tree::set_focused` to `Tree::set_focused_node`

## Fixed

Expand Down
33 changes: 0 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,6 @@ This fork aims to provide documentation and further development if necessary.

![demo](images/demo.gif "Demo")

## Usage

First, construct the initial tree:

```rust
use egui::{Color32, RichText, style::Margin};
use egui_dock::{TabBuilder, Tree, NodeIndex};

let tab1 = TabBuilder::default()
.title(RichText::new("Tab 1").color(Color32::BLUE))
.content(|ui| {
ui.label("Tab 1");
})
.build();
let tab2 = TabBuilder::default()
.title("Tab 2")
.inner_margin(Margin::same(4.0))
.content(|ui| {
ui.label("Tab 2");
})
.build();

let mut tree = Tree::new(vec![tab1]);

tree.split_left(NodeIndex::root(), 0.20, vec![tab2]);
```

Then, you can show the dock.

```rust
DockArea::new(&mut tree).show(ctx);
```

## Contribution

Feel free to open issues and pull requests.
10 changes: 6 additions & 4 deletions examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -27,7 +27,7 @@ struct MyContext {
struct MyApp {
_context: Rc<RefCell<MyContext>>,
style: Rc<RefCell<Style>>,
tree: Tree,
tree: DynamicTree,
}

impl Default for MyApp {
Expand Down Expand Up @@ -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]);
Expand All @@ -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 {});
}
}
59 changes: 21 additions & 38 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<String>,
}

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 }
}
Expand All @@ -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 {});
}
}
86 changes: 86 additions & 0 deletions examples/text_editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release

use std::collections::BTreeMap;

use eframe::{egui, NativeOptions};

/// We identify tabs by the title of the file we are editing.
type Title = String;

fn main() {
let options = NativeOptions::default();
eframe::run_native(
"Text editor examples",
options,
Box::new(|_cc| Box::new(MyApp::default())),
);
}

struct Buffers {
buffers: BTreeMap<Title, String>,
}

impl egui_dock::TabViewer for Buffers {
type Tab = Title;

fn ui(&mut self, ui: &mut egui::Ui, title: &mut Title) {
let text = self.buffers.entry(title.clone()).or_default();
egui::TextEdit::multiline(text)
.desired_width(f32::INFINITY)
.show(ui);
}

fn title(&mut self, title: &mut Title) -> egui::WidgetText {
egui::WidgetText::from(&*title)
}
}

struct MyApp {
buffers: Buffers,
tree: egui_dock::Tree<String>,
}

impl Default for MyApp {
fn default() -> Self {
let mut buffers = BTreeMap::default();
buffers.insert(
"CHANGELOG.md".to_owned(),
include_str!("../CHANGELOG.md").to_owned(),
);
buffers.insert("LICENSE".to_owned(), include_str!("../LICENSE").to_owned());
buffers.insert(
"README.md".to_owned(),
include_str!("../README.md").to_owned(),
);

let tree = egui_dock::Tree::new(vec!["README.md".to_owned(), "CHANGELOG.md".to_owned()]);

Self {
buffers: Buffers { buffers },
tree,
}
}
}

impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::SidePanel::left("documents").show(ctx, |ui| {
for title in self.buffers.buffers.keys() {
let tab_location = self.tree.find_tab(title);
let is_open = tab_location.is_some();
if ui.selectable_label(is_open, title).clicked() {
if let Some((node_index, tab_index)) = tab_location {
self.tree.set_active_tab(node_index, tab_index);
} else {
// Open the file for editing:
self.tree.push_to_focused_leaf(title.clone());
}
}
}
});

egui_dock::DockArea::new(&mut self.tree)
.style(egui_dock::Style::from_egui(ctx.style().as_ref()))
.show(ctx, &mut self.buffers);
}
}
8 changes: 4 additions & 4 deletions examples/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use egui::{
Window,
};

use egui_dock::{DockArea, NodeIndex, Tab, TabBuilder, Tree};
use egui_dock::{DockArea, DynamicTree, NodeIndex, Tab, TabBuilder};

fn main() {
let options = NativeOptions::default();
Expand All @@ -18,7 +18,7 @@ fn main() {
}

struct MyApp {
tree: Tree,
tree: DynamicTree,
}

impl Default for MyApp {
Expand Down Expand Up @@ -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]);
Expand All @@ -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 {});
}
}

Expand Down
35 changes: 32 additions & 3 deletions src/tab.rs → src/dynamic_tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct TabBuilder {
force_close: Option<ForceClose>,
}

/// Dockable tab that can be used in `Tree`s.
/// Dockable tab that can be used in [`crate::Tree`]s.
pub trait Tab {
/// Actual tab content.
fn ui(&mut self, ui: &mut Ui);
Expand Down Expand Up @@ -132,7 +132,7 @@ impl TabBuilder {
///
/// If no function is set the default behavior is to always return true.
///
/// See [Tab](crate::tab::Tab) `on_close` for more detail
/// See [`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
Expand All @@ -143,9 +143,38 @@ impl TabBuilder {
///
/// If no function is set the default behavior is to always return false.
///
/// See [Tab](crate::tab::Tab) `force_close` for more detail
/// See [`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
}
}

// ----------------------------------------------------------------------------

/// A type-def for when using [`Tab`] or [`TabBuilder`].
pub type DynamicTree = crate::Tree<Box<dyn Tab>>;

/// For use with [`crate::DockArea::show`] when using [`DynamicTree`].
#[derive(Default)]
pub struct DynamicTabViewer {}

impl crate::TabViewer for DynamicTabViewer {
type Tab = Box<dyn Tab>;

fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) {
tab.ui(ui)
}

fn title(&mut self, tab: &mut Self::Tab) -> WidgetText {
tab.title()
}

fn on_close(&mut self, tab: &mut Self::Tab) -> bool {
tab.on_close()
}

fn force_close(&mut self, tab: &mut Self::Tab) -> bool {
tab.force_close()
}
}
Loading