Git Product home page Git Product logo

egui_dock's Introduction

egui_dock: docking system for egui

github Crates.io docs.rs egui_version

Originally created by @lain-dono, this library provides a docking system for egui.

Contributing

Before contributing, please read the contribution guide.

This library is a collaborative project developed with direct involvement of its users.

Please feel free to open new issues and pull requests, and participate in discussions! A lot of our discussions take place on egui's official Discord server, in the #egui_dock channel.

Features

  • Opening and closing tabs.
  • Moving tabs between nodes and resizing.
  • Dragging tabs out into new egui windows.
  • Highly customizable look and feel.
  • High degree of control over behaviour of the whole dock area and of individual tabs.
  • Manipulating tabs and dock layout from code.

Quick start

Add egui and egui_dock to your project's dependencies.

[dependencies]
egui = "0.27"
egui_dock = "0.12"

Then proceed by setting up egui, following its quick start guide. Once that's done, you can start using egui_dock – more details on that can be found in the documentation.

Examples

The Git repository of this crate contains some example applications demonstrating how to achieve certain effects. You can find all of them in the examples folder.

You can run them with Cargo from the crate's root directory, for example: cargo run --example hello.

Demo

demo

Alternatives

It's a library aiming to achieve similar goals in addition to being more flexible and customizable.

One feature it supports that egui_dock does not at the moment is the ability to divide nodes into more than two children, enabling horizontal, vertical, and grid layouts.

Note

egui_tiles is much earlier in development than egui_dock and doesn't yet support a lot of features.

egui_dock's People

Contributors

adamgreig avatar adanos020 avatar avarel avatar awestlake87 avatar burtonageo avatar emilk avatar enomado avatar gui-yom avatar itsethra avatar jakobhellermann avatar jerrody avatar knoellle avatar lain-dono avatar lvaroqui avatar morphexe avatar msheldyakov avatar parkertenbroeck avatar past9 avatar paul-hansen avatar regenjacob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

egui_dock's Issues

The enabled/disabled state of the ui does not propagate to the widgets inside the tabs

Disclaimer: I'm not entirely sure if this is a bug or actually the desired behaviour.

I was looking for a way to disable the entire ui, including all tabs and their contents. So I tried setting the ui to disabled before passing it to the DockArea:

    CentralPanel::default()
        .show(ctx, |ui| {
            // Set disabled
            ui.set_enabled(false);
        
            // Show dock area
            DockArea::new(&mut self.tree)
                .show_inside(ui, &mut self.context);
        });

This causes all tabs inside the dock area to be disabled, as expected. But any content of the tabs, like buttons, sliders, etc. still stays interactive. In egui itself the enabled/disabled state seems to propagate down to any sub-ui, so it might be consistent for egui_dock to also behave like this.

Please Update to egui 0.22

Hello! I wanted to inform you that Egui 0.22 was released yesterday. Although there weren't many changes, transitioning to the new version should be relatively straightforward. I personally use Egui and Egui Dock as well, and I'm interested in updating to 0.22. It would be great if you could update it and release a new version at your earliest convenience. Thank you! 😄

assertion failed: max_rect.is_finite()

After the update to egui-0.22.0 and egui_dock-0.61.0 we get an assertion failure in egui/src/layout.rs[420].

After a little bit of digging i found this part in egui_dock/widgets/dock_area/mod.rs[415]:

 let tabbar_inner_rect = Rect::from_min_size(
                (tabbar_outer_rect.min - pos2(-*scroll, 0.0)).to_pos2(),
                vec2(f32::INFINITY, tabbar_outer_rect.height()),
);

I don't fully understand what should happen here, but my first suggestion would be to replace the

                vec2(f32::INFINITY, tabbar_outer_rect.height()),

with

                vec2( tabbar_outer_rect.width(), tabbar_outer_rect.height()),

Although this may effect some scrolling behavior.

To Reproduce
Enable feature 'extra_debug_asserts'

egui = { version = "0.22.0", features = [
    "extra_debug_asserts"
] }

Scroll of DockArea

Is your feature request related to a problem? Please describe.

Yes, I'm getting frustrated when I use the 'scroll_area_in_tabs' method. This method only gives the scroll area for setting two-way scrolling, but cannot set horizontal scrolling and vertical scrolling separately.

Describe the solution you'd like

I hope that the method 'scroll_area_in_tabs' can include the function of setting one-way scrolling, or implement this function with a new method

derive Clone for Tree and Node

I want to make feature - different dock layouts, so i want this structures be Clonnable.

If you worry about Tab requires to be also Clonnable - since clone(x) = deserialize(serialize(x)) it could be used with feature = "serde" easily,

Make `ScrollArea` optional, or remove completely.

I have a situation where I don't want my whole tab enclosed in a ScrollArea. I want to add it myself, but only to a region of my tab.

I don't think it is the job of a docking library to also provide scrolling, especially since you cannot opt out of it. I would remove it completely. Users can just add a ScrollArea themselves if they want one.

Add community showcases of `egui_dock`

It's pretty hard for people to understand what exactly they can make with egui_dock. Maybe uncustomizable toy library or a very flexible builder for any kind of application like engine editors or desktop applications?

Showcase in Markdown (for example, SHOWCASE.md) or like in imgui with every release add showcases. Anyway, I think that we should make it, it inspires and people immidietly understand what they can make, and, of course, for the other people, it's possible to show their work.

DockArea inside a Window can cause it to grow indefinitely

egui = "0.20"
egui_dock = "0.3.1"

Describe the bug
When splitting horizontally (e.g. moving a tab below another), the height of the containing window will grow forever.

To Reproduce

  1. Open the simple example, and modify the code as follow:
impl eframe::App for MyApp {
  fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
    Window::new("Hello")
      .show(ctx, |ui| {
        DockArea::new(&mut self.tree)
          .style(Style::from_egui(ctx.style().as_ref()))
          .show_inside(ui, &mut TabViewer {});
      });
  }
}
  1. Run the example: cargo run --example simple.

Expected behavior
Splitting should divide in areas half of the original height without causing a resizing.

Screenshots
window_issue2

Additional context
This doesn't happen when the DockArea is contained within a Panel (e.g. CentralPanel), a ScrollArea, etc.
I believe it is somehow causes by calculating the split based on percentage/ratio of the available size.

It's also fine if the Window size is fixed. However since Window doesn't have a max_height, there is no upper bound, when resizing is allowed.

Proposal of contribution rules.

I think need to make a CONTRIBUTING.md guide that includes:

  • Squashing commits. It's pretty hard to track commits if was made a PR that contains 2413241343242 commits and we merge it.
  • Some code-style rules.

I can write this CONTRIBUTING.md.

Splits of very narrow nodes happen in wrong directions

Describe the bug
If a node you're splitting is narrow enough, the tab you're putting in it will not always be placed on the side you've picked. For example (shown below), if you have a very narrow tab on the right side of the DockArea and attempt to place another tab on its right hand side, it will instead land above it instead.

To Reproduce
Steps to reproduce the behavior:

  1. Run the hello example
  2. Move all tabs minus one to the right, so that the rightmost ones get very narrow (you may need shrink your application window to reproduce the exact conditions)
  3. Try moving the last tab to the right hand side of the rightmost tab.
  4. If the previously rightmost tab was narrow enough, the new tab will be inserted above it.

Expected behavior
In the example I've described, the tab should be placed on the right hand side, i.e. the side the user picked.

Screenshots
image

Add "fast_egui_dock" feature

Maybe it's not worth it, but I would prefer to add an optional feature that enables smallvec and rayon.
Anyway, egui_dock isn't so expensive in computations, but I would prefer to add this to minimize this computation amount and make egui_dock as lightweight as possible.

Add in `README.md` guide about how to use `egui_dock` aka "quick start"

This is pretty hard to understand how to use egui_dock at beginner steps and would be good to write a guideline that explains step by step what needs to do to make it works and also cover usage cases of GAPI (Vulkan, OpenGL, and maybe-maybe DX12 (rusty_d3d12), but optional I'm not sure if it needs).

Allow for more than 2 splits per node

Is your feature request related to a problem? Please describe.
At the moment, a node can only be split horizontally or vertically once, with a fraction indicating how far apart the two are.

In the scenario where I split horizontally twice, I would love for it to instead split into 3 equally-sized chunks [A | B | C], rather than

[A | [B | C]

Peek 2023-06-30 02-00

Describe alternatives you've considered
We could hack around this by twiddling with fraction values based on the tree as a whole, but it's probably easy enough to migrate into a non-binary tree structure. probably.

Additional context
I'm planning to work on this over the weekend, but making an issue for it just incase this is something you're interested in.

Light mode settings not being applied to Dock Area tabs in version 0.4.0

Describe the bug
Some light mode style settings from egui are not being set in egui_dock::Style. This is very noticeable in the tab bar background colour with the colour being permanently in dark mode.
This behaviour is not present in release 0.3

To Reproduce
Steps to reproduce the behavior:

  1. Update to egui_dock 0.4.0 and egui to 0.21
  2. Choose any of the demos in the repo
  3. Modify the demo to set the visuals to light mode
  4. Observer the tab background colour is set to dark mode

Expected behavior
In light mode, the tab background colour should be be in light mode

Screenshots
egui_dock_tab_stuck_in_dark_mode

Additional context
The tab_bar_background_color and separator_color_hovered fields differ when compared against style settings from version 0.3
egui_dock_style_diff

The area behind an active tab is transparent

Describe the bug
egui_dock 0.4.2

The area behind an active tab is not cleared with a background color.
This becomes an issue in bevy_editor_pls where a camera is projected behind egui and sometimes makes the text unreadable.

To Reproduce
Steps to reproduce the behavior:

  1. Clone the bevy_editor_pls repo
  2. If 0.4.2 is released, skip this step, otherwise add the following to crates/bevy_editor_pls_core Cargo.toml to use the transparency fix (context)
    egui_dock = { git = "https://github.com/Adanos020/egui_dock.git", branch = "fix-clear-background" }
  3. Run the basic example
    cd crates/bevy_editor_pls && cargo run --example basic
  4. Press E to open the editor view
  5. See tabs

Expected behavior
The area behind the active tab should be cleared with a background color (like before 0.4). Or be configurable somehow.

Screenshots
image

Context
jakobhellermann/bevy_editor_pls#68

Option for full-width tabs

Thanks so much for taking on egui_dock and publishing it!

I've been playing with making the tab bars look a bit more like the qt and pyqtgraph ones, where the tab widths expand to fill the tab bar, so that if there's only one tab it's more like a window with a title. In my application there's often lots of windows but rarely any tabs, so I prefer this style.

My experimental branch is here, it didn't take many changes to hardcode this, though I think there might be a more elegant way to centre the title text in the tab. I wanted to check if there was interest in including this as an option in Style? Or even as the default style? I'd be happy to tidy the code up and open a PR if so.

Here's a screenshot of how it currently looks with the traits example:

image

Save and restore dock state when closing and reopening the application

Is your feature request related to a problem? Please describe.
I find it inconvenient when the dock state is not saved upon closing the application, causing me to lose my preferred layout and settings.

Describe the solution you'd like
I would like a feature that automatically saves the state of the dock when the application is closed, and then restores that state when the application is reopened. This would help maintain a consistent user experience across different sessions.

Describe alternatives you've considered
I have considered manually saving and loading the dock state, but it would be more convenient if this process was automated.

Additional context
This feature would be beneficial for users who frequently close and reopen the application, as it would save them time and effort in setting up their preferred dock layout each time.

allow to add padding to tab body

it should be possible to set an padding for the tab body.

before:
image

after adding padding:
image

currently i have hard coded it into the lib.

`is_pointer_over_area` is false over DockArea

Describe the bug
Egui doesn't seem to recognize that the egui dock background is there and will return is_pointer_over_area as false.

To Reproduce

Screenshots
https://user-images.githubusercontent.com/5142292/216484419-98152000-1c66-41e2-9757-9ee45e2a76de.mp4

Additional context
This is an issue with stuff like bevy_mod_picking and possibly others where they expect this method to reliably return that they are in an egui area.

Style.separator.interact_size

For me it is always difficult to resize the layout when the separator width is set to 1px (default). I feel the most comfortable when it set to about 10px-15px, but then the ui looks ugly.

A new field, Style.separator.interact_size could represent the logical thickness of a separator and would not affect the graphical thickness (Style.separator.width).

Allow close button to be hidden on specific tabs, and prevent dragging of specific tabs

Is your feature request related to a problem? Please describe.
No problem just looking for some additional functionality

Describe the solution you'd like
I want to be able to hide the close button on specific tabs and also prevent specific tabs from being dragged.
The use case I have is I have a center panel that I need to remain in the center, but all the tabs on the sides and bottom can be moved as required.

Additionally I'd also like to be able to hide the title for the tabs that i'm preventing from dragging - but that's not really the topic of this request - i'll look into that later.

Describe alternatives you've considered
can't really think of a way to deliver the use cases i'm trying to build in a different way.

Additional context
i've made the change in my forked branch, just really want to know if you're interested in including the functionality into the crate.
i'll make a PR - happy to make improvements, or changes to the way i've implemented it if it'll allow you to include it,
also understand that this might not be a use case that you're interested, so really just putting it up in case you want it.

Refactor `Style`

Is your feature request related to a problem? Please describe.
The Style struct has been getting more and more unwieldy, now containing 36 fields and no separation between those describing behaviour and those describing visuals. This is inconsistent with egui's Style which has separate Visuals and Interaction fields, among others.

Describe the solution you'd like
Create new structs: Visuals and Interaction. Where appropriate, create more structs to separate fields by what part of the docking system they affect (tabs, tab bar, separators, etc.).

Additional context
I'm also considering deprecating StyleBuilder since constructing Style can just as easily be done without it:

// With StyleBuilder
let style = StyleBuilder::from_egui(ui.style())
    .expand_tabs(true)
    .show_name_when_hovered(true)
    .build();

// Without StyleBuilder
let style = Style {
    expand_tabs: true,
    tab_hover_name: true,
    ..Style::from_egui(ui.style())
};

This would vastly decrease duplication of code and name discrepancy (as shown in the example above) in the style module.

I want to hear other people's feedback before committing to this.

Adding `TabViewer::closable(..)` ?

Right now, it doesn't seem possible to set the close button for all widgets in bulk, and dynamically set whether or not to render the close button for individual widgets...

When configuring a dock, some widgets may not define a closing behaviour. Not handling the TabViewer::on_close() call serves this purpose, but it would be nice to add a TabViewer::closable() function so that we can avoid defining a closing behaviour for objects that can't be closed, so that the close button itself doesn't appear in the tab bar and context menu.

from

fn __(vwr: &mut TabViewer, style: &Style, tab: &mut Tab) {
  if style.show_close && close_btn(ui) && vwr.on_close(tab)  {
    /* do something closinbg here */
  }
}

to

fn __(vwr: &mut TabViewer, style: &Style, tab: &mut Tab) {
  if style.show_close && vwr.closable(tab) && close_btn(ui) && vwr.on_close(tab) {
    /* do something closinbg here */
  }
}

Streamline exposed module structure

Currently the import section of lib.rs looks like this:

use egui::style::Margin;
use egui::*;

#[allow(deprecated)]
pub use crate::{
    dynamic_tab::{DynamicTabViewer, DynamicTree, Tab, TabBuilder},
    style::{Style, StyleBuilder, TabAddAlign},
    tree::{Node, NodeIndex, Split, TabIndex, Tree},
};
pub use egui;
use utils::*;

mod dynamic_tab;
mod style;
mod tree;
mod utils;

Every type from the sub modules like tree, style, etc. has to be manually added to the pub use section to be accessible to users of the crate. It essentially flattens the structure as viewed from the outside, since every type has to be imported directly from egui_dock.
This approach and has to be maintained manually and as such is prone to errors (see #60).

My suggestion would be to expose the sub modules directly via pub mod and then just non-pub using the types for use within the crate's source files to avoid long qualified paths.

This would be a breaking change, since users would have to adjust their include paths from e.g. egui_dock::Node to egui_dock::tree::Node.

ScrollArea inside tab : panic with `attempt to add with overflow`

Describe the bug
Showing a tab with a Frame or a ScrollArea in anything other than the root node cause a panic.

To Reproduce
I made a minimal reproducer here : https://github.com/Gui-Yom/egui_dock_repro
Run with cargo run.

Expected behavior
No panic, like in 0.5.0.

Additional context
I suspect the problem arise from this commit : 19c43dd in src/widgets/dock_area/mod.rs. Previously, the tabs were drawn after drawing the separators which assigned them a tab rect that isn't infinite.

Prepare to the `0.3`

There're enough changes for the release, but before we do it.

TODO before release:

  • Merge #70
  • Update README.md and add Quickstart in `README.md

Nesting DockArea in another DockArea produces weird results

While it looks correct
image
clicking on Linear or Tree doesn't really do anything, it kinda behaves like moving but almost immediately stops. Chunks, Viewport and Properties work as regular.

Also, things stop being rendered once you put the second DockArea in a TopBottomPanel
image

My code:

use eframe::{
    egui::{menu, Context, TopBottomPanel, Ui, WidgetText},
    App, Frame, NativeOptions,
};
use egui_dock::{DockArea, NodeIndex, TabViewer, Tree};

fn main() {
    eframe::run_native(
        "BSP Editor",
        NativeOptions::default(),
        Box::new(|_| Box::new(BspEditor::new())),
    );
}

#[derive(Debug, Eq, PartialEq)]
enum ChunkTab {
    Linear,
    Tree,
}

struct ChunkTabs;

impl TabViewer for ChunkTabs {
    type Tab = ChunkTab;

    fn ui(&mut self, _ui: &mut Ui, tab: &mut Self::Tab) {
        match *tab {
            ChunkTab::Linear => {
                _ui.label("Linear");
                _ui.button("Linear");
            }
            ChunkTab::Tree => {
                _ui.label("Tree");
                _ui.button("Tree");
            }
        }
    }

    fn title(&mut self, tab: &mut Self::Tab) -> WidgetText {
        match *tab {
            ChunkTab::Linear => "Linear",
            ChunkTab::Tree => "Tree",
        }
        .into()
    }    
}

#[derive(Debug, Eq, PartialEq)]
enum Tab {
    Chunks,
    Viewport,
    Properties,
}

struct Tabs {
    tree: Tree<ChunkTab>,
}

impl Tabs {
    fn new() -> Self {
        Self {
            tree: Tree::new(vec![ChunkTab::Linear, ChunkTab::Tree]),
        }
    }
}

impl TabViewer for Tabs {
    type Tab = Tab;

    fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) {
        match *tab {
            Tab::Chunks => {
                TopBottomPanel::bottom("chunks").show_inside(ui, |ui| {
                    DockArea::new(&mut self.tree).show_inside(ui, &mut ChunkTabs);
                });
            }
            Tab::Viewport => {}
            Tab::Properties => {}
        }
    }

    fn title(&mut self, tab: &mut Self::Tab) -> WidgetText {
        match *tab {
            Tab::Chunks => "Chunks",
            Tab::Viewport => "Viewport",
            Tab::Properties => "Properties",
        }
        .into()
    }
}

struct BspEditor {
    tree: Tree<Tab>,
}

impl BspEditor {
    fn new() -> Self {
        let mut tree = Tree::new(vec![Tab::Chunks]);

        let [_tree_panel, viewport_panel] =
            tree.split_right(NodeIndex::root(), 0.25, vec![Tab::Viewport]);

        tree.split_right(viewport_panel, 0.66, vec![Tab::Properties]);

        Self { tree }
    }
}

impl App for BspEditor {
    fn update(&mut self, context: &Context, _frame: &mut Frame) {
        TopBottomPanel::top("menu").show(context, |ui| {
            menu::bar(ui, |ui| {
                ui.menu_button("File", |ui| {
                    if ui.button("New").clicked() {
                        todo!();
                    }

                    if ui.button("Open").clicked() {
                        todo!();
                    }

                    if ui.button("Save").clicked() {
                        todo!();
                    }

                    if ui.button("Save as").clicked() {
                        todo!();
                    }
                });
            });
        });

        DockArea::new(&mut self.tree).show(context, &mut Tabs::new());
    }
}

Tab loses focus when it's title is edited

Describe the bug
I have a TextEdit in a tab which edits the tab title. When I enter a letter, the text edit loses focus and I need to click it again to edit

Expected behavior
The text edit works fine

EDIT: Forgot to mention that the Tab in TabViewer is not the title

`.tabs_are_draggable(false)` makes tabs not clickable

Describe the bug

Calling StyleBuilder.tabs_are_draggable(false) makes tabs not selectable, essentially breaking egui_dock because tabs can't be clicked between.

To Reproduce

  1. Create a new DockArea and set its style
  2. Call StyleBuilder.tabs_are_draggable(false)
  3. Run the app and try to switch between tabs

Example:

egui_dock::DockArea::new(&mut self.tree)
    .style(
        StyleBuilder::from_egui(ctx.style().as_ref())
            .tabs_are_draggable(false)
            .build(),
    )
    .show(ctx, &mut TopbarTabViewer);

Expected behavior

I expect tabs not to be draggable (as explained in the function description), but I still want to be able to switch between the tabs by clicking on them.

Additional context

I am on version 0.3.1 of egui_dock, 0.20.1 of egui, and 0.9.1 of bevy.

Tab close button hides behind add button in narrow leaves.

https://github.com/Adanos020/egui_dock/assets/95856287/4cad5e22-bade-43a6-8a2f-7479e69b5c6d
The button to close the CodeEditor Tab hides behind the add button of the leaf.

Minimal example

use eframe::{egui::{self, Sense}, NativeOptions, epaint::{Color32, vec2}, CreationContext};
use egui_dock::{DockArea, NodeIndex, Style, Tree};

fn main() -> eframe::Result<()> {
    eframe::run_native("App", NativeOptions::default(), Box::new(|cc| Box::new(App::new(cc))))
}

struct App {
    tree: Tree<String>,
}

impl App {
    fn new(_: &CreationContext) -> Self {

        let mut tree = Tree::new(vec!["L1".to_string()]);

        tree.split_right(NodeIndex::root(), 0.5, vec!["R1".to_string()]);

		tree.push_to_focused_leaf("R2".to_string());
		tree.push_to_focused_leaf("R3".to_string());
		tree.push_to_focused_leaf("R4".to_string());
		
        Self { tree }
    }
}

impl eframe::App for App {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
		let mut style = Style::from_egui(ctx.style().as_ref());
		style.separator.width = 10.0;
		style.separator.color_hovered = style.separator.color_idle;
		style.separator.color_idle = Color32::from_rgba_unmultiplied(128, 128, 128, 10);


        DockArea::new(&mut self.tree)
			.show_add_buttons(true)
			.show_add_popup(true)
            .style(style)
            .show(ctx, &mut TabViewer {});
    }
}

struct TabViewer {}

impl egui_dock::TabViewer for TabViewer {
    type Tab = String;

    fn ui(&mut self, _ui: &mut egui::Ui, _tab: &mut Self::Tab) {}

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

	fn add_popup(&mut self, ui: &mut egui::Ui, _node: egui_dock::NodeIndex) {
		ui.allocate_at_least(vec2(100.0, 1.0), Sense { click: false, drag: false, focusable: false });
		ui.separator();
		ui.label("Text");
	}
}

Expected behavior
The horizontal scrollbar should appear earlier, as soon as there is some overlap between the tab and the add button.

Disable drag to split behavior

Currently, I do not see a way to disable the tab drag to split behavior. Even without this, the dock widget acts as an excellent abstraction for tabbed panels.

Using multiple trees with different `egui` panels causes id clashes.

Describe the bug
When using egui_dock with multiple egui panels, id clashes can occur, which causes ugly debug text to appear.

To Reproduce
Use egui_dock with a different tree for each panel. A minimal example is provided below in code:

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release

use eframe::{egui, NativeOptions};
use egui_dock::{DockArea, Style, Tree};

fn main() {
    let options = NativeOptions::default();
    eframe::run_native(
        "My Multipanel egui App",
        options,
        Box::new(|_cc| Box::new(MyApp::default())),
    );
}

struct TabViewer {}

impl egui_dock::TabViewer for TabViewer {
    type Tab = usize;

    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 {
        format!("Tab {tab}").into()
    }
}

struct MyApp {
    left_panel_tree: Tree<usize>,
    right_panel_tree: Tree<usize>,
    bottom_panel_tree: Tree<usize>,
}

impl Default for MyApp {
    fn default() -> Self {
        let left_panel_tree = Tree::new(vec![1, 2]);
        let right_panel_tree = Tree::new(vec![3, 4]);
        let bottom_panel_tree = Tree::new(vec![5, 6]);

        Self {
            left_panel_tree,
            right_panel_tree,
            bottom_panel_tree,
        }
    }
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::SidePanel::left("Left Panel").resizable(true).show(ctx, |ui| {
            DockArea::new(&mut self.left_panel_tree)
                .style(Style::from_egui(ctx.style().as_ref()))
                .show_inside(ui, &mut TabViewer {});
        });

        egui::SidePanel::right("Right Panel").resizable(true).show(ctx, |ui| {
            DockArea::new(&mut self.right_panel_tree)
                .style(Style::from_egui(ctx.style().as_ref()))
                .show_inside(ui, &mut TabViewer {});
        });

        egui::TopBottomPanel::bottom("Bottom Panel").resizable(true).show(ctx, |ui| {
            DockArea::new(&mut self.bottom_panel_tree)
                .style(Style::from_egui(ctx.style().as_ref()))
                .show_inside(ui, &mut TabViewer {});
        });
    }
}

Run the example, and observe that egui reports id conflict errors. The result of running the example produces the screenshot below.

Expected behavior
I should be able to use a different Tree for each panel and only show the tabs which correspond to that Tree in the chosen panel. Alternatively, egui_dock could provide a replacement Panel API to allow drag-drop between different panels.

Screenshots
Screenshot of running the above example code:

image

Necessity in `lib.rs` decomposition

Currently show_inside method is a "uber" method that currently is very hard to read, and maintain, and very hard to understand what happens inside it.

In my opinion, we should separate it into many methods, in other words decompose it.

How to specify drag-area size? (not separator style thickness)

Hey! Really nice extension to egui :)

I see that I can specify a bunch of styles for my tabs, but how do I specify the drag area's size?

It can be hard to hit the exact line where I can drag and it would be nice to be able to make this area a bit larger and more forgiving to the pointer.

Note that I would like to do this without necessarily making the drawn line wider.

Users should be able to close tabs

Currently, after the Tree is constructed, existing tabs cannot be closed.

Developers should be able to specify if a tab should have an X button which upon clicking, closes the tab.

Splitter has wrong behavior when dragging

Describe the bug
When the splitter is dragged out of the clamp range and back, it's in the wrong position.

To Reproduce
Just run the demo and drag the splitter.

Expected behavior
The splitter should always follow the pointer.

Screenshots
Here's a video showing different behaviors between egui::DragValue and splitters:

Rizlium.2023-01-20.20-49-16.mp4

Additional context
Possible solution: use another value to store the "last dragged value" (the same as DragValues do) and clamp this value, not the splitters' position.
current code (hsplit):

*fraction = (*fraction + delta / range).clamp(min, max);

We'll need to store fraction when dragging starts and use this value for clamping:

*fraction = (drag_start + delta / range).clamp(min, max); 

Expand styling options for tabs

Is your feature request related to a problem? Please describe.
The currently available styling options dont quite allow me to create a styling that closely follows the rest of the egui styling.

I have put together a simple panel to show what i would like to achieve.
This panel uses only selectableLabel and Frame::group.
image

Describe the solution you'd like

  1. Tabs required their own styling for active/inactive and hover.
    Right now inactive tabs take on the color of the Tab bar. However it should be possible to seperate an active tab from an inactive tab from the tab bar from the panel background.

  2. Tabs need a spacing style to seperate tabs from each other.
    Tabs are very much pressed together right now. It would be nice to visually relax this by seprating the tabs a little.

  3. Tab bodies need their own background color to seperate them from the tabs.
    Technically not necessary but still usefull. Otherwise you have to create a new frame on top of the panel body with the actual background color you want.

  4. Border around the dock area.
    There exists a border style however it doesnt really work. It is somehow connected to the top margin.
    In either case a border around the table would include the table bar.
    You can already kinda do this with another frame around the DockArea. However if you have any rounding on the frame, the tabs will poke their corners through the border.

  5. A Border around a tab body.
    You can also do this by setting the inner_margin to zero and open a new frame inside the tab. However it would way nice if you could do this with a simple style instead.

Many complex types do not implement the `std::fmt::Debug` trait

Is your feature request related to a problem? Please describe.
When embedding egui_dock types in other types in my code, I cannot use the #[derive(Debug)] attribute on them as they themselves do not implement the Debug trait.

Describe the solution you'd like
All egui_dock types should implement the Debug trait. For complex types, an opaque debug impl should be acceptable, e.g:

use std::fmt;

impl<Tab> fmt::Debug for Tree<Tab> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Tree").finish_non_exhaustive()
    }
}

Describe alternatives you've considered
The obvious alternative is to add a custom Debug impl and skip over the egui_dock types embedded in my types, but this would add extra code. Another alternative is to make my types not implement Debug, but this is inconvenient when debugging, and there are areas in my code where generics enforce that a type implements Debug.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.