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

Support for undocking and docking tabs as windows #149

Merged
merged 82 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
038fc36
Support for undocking and docking tabs as windows
Vickerinox Jul 24, 2023
cb1caf1
Added Surfaces!
Vickerinox Jul 25, 2023
59213c2
Renamings and restructure
Vickerinox Jul 26, 2023
6df14f3
Further Renaming and window rejecting tabs
Vickerinox Jul 26, 2023
1475ee9
Support for undocking and docking tabs as windows
Vickerinox Jul 24, 2023
2f6fcb2
Added Surfaces!
Vickerinox Jul 25, 2023
9ecca74
Renamings and restructure
Vickerinox Jul 26, 2023
143de19
Further Renaming and window rejecting tabs
Vickerinox Jul 26, 2023
49f5ad0
Delete abstract_tree
Adanos020 Jul 27, 2023
e3e2125
Merge branch 'window-dock/undocking' of https://github.com/Vickerinox…
Vickerinox Jul 27, 2023
6198a9c
Merge branch 'window-dock/undocking' of https://github.com/Vickerinox…
Vickerinox Jul 27, 2023
1f3660b
Experimental changes
Vickerinox Jul 27, 2023
76e877f
Small bugfixes
Vickerinox Jul 27, 2023
99b757b
Surfaces, Focus and expanding windows Bugfix
Vickerinox Jul 28, 2023
d569faf
bugfixes and consistency changes
Vickerinox Jul 28, 2023
6d28b0d
Addition to splits
Vickerinox Jul 29, 2023
3703577
Merge branch 'release-0.7' into window-dock/undocking
Adanos020 Jul 29, 2023
6bcf9e0
Panic condition fix
Vickerinox Jul 29, 2023
504ac46
changed interactions with overlay
Vickerinox Jul 29, 2023
3b13629
Further Overlay changes
Vickerinox Jul 29, 2023
8524e20
Hold time for overlay lock
Vickerinox Jul 30, 2023
03f47f1
faded windows first part
Vickerinox Jul 30, 2023
57a8439
Fading windows part 2
Vickerinox Jul 31, 2023
9167b77
Removed Fade Animation
Vickerinox Jul 31, 2023
44ed316
Hightlighting rect on hovered leaves
Vickerinox Aug 1, 2023
b8f5261
Formatting; partial highlighting of overlay icons for better target i…
Adanos020 Aug 3, 2023
176f083
Cleanup and fix clippy warnings
Adanos020 Aug 3, 2023
a35cf91
Fix tests
Adanos020 Aug 3, 2023
95c93d0
Dedicated button color fields for overlay buttons
Vickerinox Aug 4, 2023
16568a3
Fix overlay icon proportions
Adanos020 Aug 5, 2023
5ec4873
Cleanup, rename `allow_in_windows` to `allowed_in_windows`, eject con…
Adanos020 Aug 6, 2023
7239940
Small clarity change to hover data
Vickerinox Aug 7, 2023
606da28
changes to old overlay and hello example
Vickerinox Aug 7, 2023
872927c
changes to hello example and overlay style
Vickerinox Aug 8, 2023
a0515ea
refactoring HoverData
Vickerinox Aug 9, 2023
0ff1f33
Close context menu upon clicking on one of the options
Adanos020 Aug 9, 2023
c807ba8
Only show the 'Eject' option for docked tabs
Adanos020 Aug 9, 2023
a8b0868
Minor refactorings
Adanos020 Aug 9, 2023
b1a427b
Quick fix to window fade
Vickerinox Aug 10, 2023
3a03946
Second hotfix for new HoverData
Vickerinox Aug 10, 2023
fdb44a4
refining fix to nodes stealing focus
Vickerinox Aug 10, 2023
8a008ea
Make tab windows movable by dragging from any point in the tab body
Adanos020 Aug 10, 2023
01c9e59
Minor cleanup
Adanos020 Aug 11, 2023
abb9dc1
Reworking Hoverdata part 2
Vickerinox Aug 13, 2023
afd47d9
Polishing overlay
Vickerinox Aug 13, 2023
97c8da4
Changes to overlay
Vickerinox Aug 13, 2023
eef9c30
update to Highlighted areas overlay
Vickerinox Aug 13, 2023
a67d68c
Window bounds, finished hello example
Vickerinox Aug 18, 2023
1b38c7b
correction of documentation
Vickerinox Aug 18, 2023
979b48b
Correct typos in examples/hello.rs
Adanos020 Aug 18, 2023
dd89e0d
Make clippy happy and clean up comments&documentation.
Adanos020 Aug 18, 2023
2acd117
Fix rustfmt errors
Adanos020 Aug 18, 2023
dfedfe0
Fix doc errors
Adanos020 Aug 18, 2023
4848f5a
improved documentation
Vickerinox Aug 19, 2023
cc557a3
Consistency changes, improved documentation
Vickerinox Aug 20, 2023
95185af
removed unneccesary links
Vickerinox Aug 20, 2023
131c529
Improve documentation in lib.rs
Adanos020 Aug 20, 2023
c9f77f3
Make `Node::split` panic if `fraction` isn't in range 0..=1
Adanos020 Aug 20, 2023
29e4a69
Merge branch 'release-0.7' into window-dock/undocking
Adanos020 Aug 21, 2023
eaca388
Reformat
Adanos020 Aug 21, 2023
6184971
Write more about surfaces and trees
Adanos020 Aug 21, 2023
9ed7d22
Fix doc test
Adanos020 Aug 21, 2023
bdd4c94
Clarifications in the docs
Adanos020 Aug 22, 2023
ee5acf0
Add a section about `style`
Adanos020 Aug 22, 2023
d7c0239
Polishing `style`
Adanos020 Aug 23, 2023
21c0da3
Documentation grooming
Adanos020 Aug 25, 2023
a841ccf
Make clippy happy
Adanos020 Aug 25, 2023
8075371
Fix cargo doc errors
Adanos020 Aug 25, 2023
7ce8704
Update changelog
Adanos020 Aug 25, 2023
a41c8f1
added buttons to windows (+setting) restructured dock area
Vickerinox Aug 26, 2023
0022057
updated changelog, hello example, and window behavior
Vickerinox Aug 26, 2023
a095d87
updated behaviour to windows
Vickerinox Aug 27, 2023
d1c0bdc
fixed oversights
Vickerinox Aug 27, 2023
46b632e
Update readme
Adanos020 Aug 27, 2023
98f6c4a
final touches
Vickerinox Aug 27, 2023
88d9548
Merge branch 'window-dock/undocking' of https://github.com/Vickerinox…
Vickerinox Aug 27, 2023
9cdef8c
Disable closing non-closeable tabs with middle click
Adanos020 Aug 27, 2023
e18f32f
Update changelog
Adanos020 Aug 27, 2023
0ca2af3
changed sizes on window titles
Vickerinox Aug 27, 2023
8283064
Merge branch 'window-dock/undocking' of https://github.com/Vickerinox…
Vickerinox Aug 27, 2023
56ace92
Fix clippy errors
Adanos020 Aug 27, 2023
a74230b
Fix docs
Adanos020 Aug 27, 2023
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
1 change: 0 additions & 1 deletion examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,6 @@ impl eframe::App for MyApp {
});
})
});

CentralPanel::default()
// When displaying a DockArea in another UI, it looks better
// to set inner margins to 0.
Expand Down
9 changes: 5 additions & 4 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, Tree};
use egui_dock::{DockArea, NodeIndex, Style, SurfaceIndex, Tree};

fn main() -> eframe::Result<()> {
let options = NativeOptions::default();
Expand Down Expand Up @@ -36,9 +36,10 @@ impl Default for MyApp {
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".to_owned()]);
let [_, _] = tree.split_below(a, 0.7, vec!["tab4".to_owned()]);
let [_, _] = tree.split_below(b, 0.5, vec!["tab5".to_owned()]);
let [a, b] =
tree[SurfaceIndex::root()].split_left(NodeIndex::root(), 0.3, vec!["tab3".to_owned()]);
Adanos020 marked this conversation as resolved.
Show resolved Hide resolved
let [_, _] = tree[SurfaceIndex::root()].split_below(a, 0.7, vec!["tab4".to_owned()]);
let [_, _] = tree[SurfaceIndex::root()].split_below(b, 0.5, vec!["tab5".to_owned()]);

Self { tree }
}
Expand Down
6 changes: 3 additions & 3 deletions examples/tab_add.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, Tree};
use egui_dock::{DockArea, NodeIndex, NodeTree, Style};

fn main() -> eframe::Result<()> {
let options = NativeOptions::default();
Expand Down Expand Up @@ -34,13 +34,13 @@ impl egui_dock::TabViewer for TabViewer<'_> {
}

struct MyApp {
tree: Tree<usize>,
tree: NodeTree<usize>,
counter: usize,
}

impl Default for MyApp {
fn default() -> Self {
let mut tree = Tree::new(vec![1, 2]);
let mut tree = NodeTree::new(vec![1, 2]);

// You can modify the tree before constructing the dock
let [a, b] = tree.split_left(NodeIndex::root(), 0.3, vec![3]);
Expand Down
5 changes: 3 additions & 2 deletions examples/text_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl egui_dock::TabViewer for Buffers {

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

impl Default for MyApp {
Expand All @@ -53,7 +53,8 @@ impl Default for MyApp {
include_str!("../README.md").to_owned(),
);

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

Self {
buffers: Buffers { buffers },
Expand Down
246 changes: 246 additions & 0 deletions src/abstract_tree/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
use egui::Rect;

use crate::{
window::WindowState, Node, NodeIndex, NodeTree, Split, SurfaceIndex, TabDestination, TabIndex,
};

/// The basis for egui_dock
/// This tree starts with a collection of surfaces, that then breaks down into nodes, and then into tabs.
/// Indexing it will yield the NodeTree inside the surface which was indexed (panicing if it doesn't exist)
pub struct Tree<Tab> {
Vickerinox marked this conversation as resolved.
Show resolved Hide resolved
surfaces: Vec<Surface<Tab>>,
//part of the tree which is in focus
focused_surface: Option<SurfaceIndex>,
}
/// A surface is the highest level component in a [`Tree`]
/// [`Surface`]s represent an area in which nodes are placed,
/// Typically you're only using one surface, which is the root surface,
/// However if you drag a tab out in a way which creates a window,
/// you also create a new surface in which nodes can appear.
pub enum Surface<Tab> {
///An empty surface, with nothing inside
Empty,

///The root surface
Root(NodeTree<Tab>),

///A windowed surface with a state
Window(NodeTree<Tab>, WindowState),
}
impl<Tab> Surface<Tab> {
///Is this surface Empty? (in practice null)
pub const fn is_empty(&self) -> bool {
if let Self::Empty = self {
true
} else {
false
}
}

/// Get mutable access to the node tree of this surface
pub fn node_tree_mut(&mut self) -> Option<&mut NodeTree<Tab>> {
match self {
Surface::Empty => None,
Surface::Root(tree) => Some(tree),
Surface::Window(tree, _) => Some(tree),
}
}

///Get access to the node tree of this surface
pub fn node_tree(&self) -> Option<&NodeTree<Tab>> {
match self {
Surface::Empty => None,
Surface::Root(tree) => Some(tree),
Surface::Window(tree, _) => Some(tree),
}
}
}
impl<Tab> std::ops::Index<SurfaceIndex> for Tree<Tab> {
type Output = NodeTree<Tab>;

#[inline(always)]
fn index(&self, index: SurfaceIndex) -> &Self::Output {
self.surfaces[index.0]
.node_tree()
.expect("index doesn't point to a surface!")
}
}

impl<Tab> std::ops::IndexMut<SurfaceIndex> for Tree<Tab> {
#[inline(always)]
fn index_mut(&mut self, index: SurfaceIndex) -> &mut Self::Output {
self.surfaces[index.0]
.node_tree_mut()
.expect("index doesn't point to a surface!")
}
}

impl<Tab> Tree<Tab> {
///Access to the root node tree
pub fn root(&self) -> &NodeTree<Tab> {
&self[SurfaceIndex::root()]
}

///Create a new tree with these tabs at the root
pub fn new(tabs: Vec<Tab>) -> Self {
Self {
surfaces: vec![Surface::Root(NodeTree::new(tabs))],
focused_surface: None,
}
}

pub(crate) fn get_window_state_mut(
&mut self,
surface: SurfaceIndex,
) -> Option<&mut WindowState> {
if let Surface::Window(_, state) = &mut self.surfaces[surface.0] {
Some(state)
} else {
None
}
}
/// Returns the viewport `Rect` and the `Tab` inside the focused leaf node or `None` if it does not exist.
#[inline]
pub fn find_active_focused(&mut self) -> Option<(Rect, &mut Tab)> {
self.focused_surface
.and_then(|surface| self[surface].find_active_focused())
}

///Get the mutable borrow to the raw surface from a surface index
pub fn get_surface_mut(&mut self, surface: SurfaceIndex) -> Option<&mut Surface<Tab>> {
self.surfaces.get_mut(surface.0)
}

/// Returns an `Iterator` of all valid [`SurfaceIndex`]es.
#[inline]
pub(crate) fn surface_index_iter(&self) -> impl Iterator<Item = SurfaceIndex> {
(0..self.surfaces.len()).map(SurfaceIndex)
}

/// Remove a surface based on it's [`SurfaceIndex`], returning it if it existed, otherwise returning `None`.
/// # Panics
/// Panics if you try to remove the root surface *( surface index 0 )*
///
pub fn remove_surface(&mut self, surface_index: SurfaceIndex) -> Option<Surface<Tab>> {
assert!(surface_index != SurfaceIndex::root());
if surface_index.0 < self.surfaces.len() {
let surface = {
if surface_index.0 == self.surfaces.len() - 1 {
self.surfaces.pop()?
} else {
std::mem::replace(self.surfaces.get_mut(surface_index.0)?, Surface::Empty)
}
};
self.focused_surface = Some(SurfaceIndex::root());
Some(surface)
} else {
None
}
}

/// Sets the currently focused leaf to `node_index` if the node at `node_index` is a leaf.
#[inline]
pub fn set_focused_node(&mut self, (surface_index, node_index): (SurfaceIndex, NodeIndex)) {
if surface_index.0 >= self.surfaces.len() {
return;
}
if let Some(Node::Leaf { .. }) = self[surface_index].tree.get(node_index.0) {
self.focused_surface = Some(surface_index);
self[surface_index].set_focused_node(node_index);
} else {
self.focused_surface = None;
}
}

/// Get mutable access to the tree at the root surface
pub fn root_surface_mut(&mut self) -> &mut NodeTree<Tab> {
&mut self[SurfaceIndex::root()]
}
/// Moves a tab from a node to another node, you specify how the tab should
/// be moved with [`TabDestination`].
pub fn move_tab(
&mut self,
(src_surface, src_node, src_tab): (SurfaceIndex, NodeIndex, TabIndex),
(dst_surface, dst_node, dst_tab): (SurfaceIndex, NodeIndex, TabDestination),
) {
// Moving a single tab inside its own node is a no-op
if src_surface == dst_surface
&& src_node == dst_node
&& self[src_surface][src_node].tabs_count() == 1
{
return;
}

// Call `Node::remove_tab` to avoid auto remove of the node by
// `Tree::remove_tab` from Tree.
let tab = self[src_surface][src_node].remove_tab(src_tab).unwrap();

match dst_tab {
TabDestination::Split(split) => {
self[dst_surface].split(dst_node, split, 0.5, Node::leaf(tab));
}
TabDestination::Window(position) => {
let surface_index = self.add_window(vec![tab]);
self.get_window_state_mut(surface_index)
.unwrap()
.set_position(position);
}
TabDestination::Insert(index) => self[dst_surface][dst_node].insert_tab(index, tab),
TabDestination::Append => self[dst_surface][dst_node].append_tab(tab),
};

if self[src_surface][src_node].is_leaf() && self[src_surface][src_node].tabs_count() == 0 {
self[src_surface].remove_leaf(src_node);
}

if self[src_surface].is_empty() {
self.remove_surface(src_surface);
}
}

/// Currently focused leaf.
#[inline]
pub fn focused_leaf(&self) -> Option<(SurfaceIndex, NodeIndex)> {
let surface = self.focused_surface?;
self[surface].focused_leaf().map(|leaf| (surface, leaf))
}

/// Creates two new nodes by splitting a given `parent` node and assigns them as its children. The first (old) node
/// inherits content of the `parent` from before the split, and the second (new) has `tabs`.
///
/// `fraction` (in range 0..=1) specifies how much of the `parent` node's area the old node is will occupy after the
/// split.
///
/// The new node is placed relatively to the old node, in the direction specified by `split`.
///
/// Returns the indices of the old node and the new node.
pub fn split(
&mut self,
surface: SurfaceIndex,
parent: NodeIndex,
split: Split,
fraction: f32,
new: Node<Tab>,
) -> [NodeIndex; 2] {
let index = self[surface].split(parent, split, fraction, new);
self.focused_surface = Some(surface);
index
}

/// Add a window to the tree, which is disconnected from the rest of the nodes, returns it's surface index
pub fn add_window(&mut self, tabs: Vec<Tab>) -> SurfaceIndex {
let surface = Surface::Window(NodeTree::new(tabs), WindowState::new());

//find the first possible empty surface to insert our window into.
//skip the first entry as it's always the root.
for (i, item) in self.surfaces[1..].iter().enumerate() {
if item.is_empty() {
self.surfaces[i] = surface;
return SurfaceIndex(i);
}
}

self.surfaces.push(surface);
SurfaceIndex(self.surfaces.len() - 1)
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,19 @@
#![warn(missing_docs)]
#![forbid(unsafe_code)]

pub use abstract_tree::*;
pub use egui;
#[allow(deprecated)]
pub use style::*;
pub use tree::*;
pub use widgets::*;


/// The highest level tree
pub mod abstract_tree;
/// egui_dock theme (color, sizes...).
pub mod style;
/// Node tree
pub mod tree;
mod utils;

Expand Down
Loading