egui icon indicating copy to clipboard operation
egui copied to clipboard

Is there a "Tab Control" in egui?

Open bixiyan opened this issue 2 years ago • 12 comments

Hi guys, I want create a tab control but don't know which one should I use? like this image

I tried SelectableLabel, but it has no close button.

bixiyan avatar May 12 '22 02:05 bixiyan

It looks like, egui lacks this widget.

Looks like an interesting task to me. If no one is interested in implementing this, I can do it. @emilk is it ok? Any suggestions?

I think, it's a good inspiration from imgui for what we can implement here.

Gordon01 avatar May 14 '22 13:05 Gordon01

Anyway, you can see the example of how you can "emulate" TabControl with SelectableLabel here

Gordon01 avatar May 14 '22 14:05 Gordon01

Check out https://github.com/lain-dono/shaderlab

enomado avatar May 21 '22 13:05 enomado

It would be very nice with proper tab support in egui - please make a PR!

emilk avatar May 21 '22 13:05 emilk

It would be very nice with proper tab support in egui - please make a PR!

I have a question about this: it seems that it is not possible to have an Area (which I think would be the base for the component) with a fixed size, or that grows bigger than its contents. Can that be done? A tab component that changes its size based on the selected tab would be weird, but for now, I don't see how to do it.

JosuGZ avatar Aug 04 '22 20:08 JosuGZ

There is now a crate that offers tabs and docking: https://crates.io/crates/egui_dock

emilk avatar Aug 09 '22 17:08 emilk

It would be very nice with proper tab support in egui - please make a PR!

I have a question about this: it seems that it is not possible to have an Area (which I think would be the base for the component) with a fixed size, or that grows bigger than its contents. Can that be done? A tab component that changes its size based on the selected tab would be weird, but for now, I don't see how to do it.

It seems like something like this does the trick:

diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs
index 844fc758..ce57abc9 100644
--- a/egui/src/containers/resize.rs
+++ b/egui/src/containers/resize.rs
@@ -35,6 +35,7 @@ pub struct Resize {
 
     /// If false, we are no enabled
     resizable: bool,
+    allow_wrowing_bigger_than_the_contents: bool,
 
     pub(crate) min_size: Vec2,
     pub(crate) max_size: Vec2,
@@ -50,6 +51,7 @@ impl Default for Resize {
             id: None,
             id_source: None,
             resizable: true,
+            allow_wrowing_bigger_than_the_contents: false,
             min_size: Vec2::splat(16.0),
             max_size: Vec2::splat(f32::INFINITY),
             default_size: vec2(320.0, 128.0), // TODO(emilk): preferred size of [`Resize`] area.
@@ -134,6 +136,16 @@ impl Resize {
         self.resizable
     }
 
+    /// Whether we can make it grow bigger than the contents
+    pub fn allow_grow(mut self, allow_grow: bool) -> Self {
+        self.allow_wrowing_bigger_than_the_contents = allow_grow;
+        self
+    }
+
+    pub fn allows_grow(&self) -> bool {
+        self.allow_wrowing_bigger_than_the_contents
+    }
+
     /// Not manually resizable, just takes the size of its contents.
     /// Text will not wrap, but will instead make your window width expand.
     pub fn auto_sized(self) -> Self {
@@ -271,7 +283,11 @@ impl Resize {
             content_ui,
         } = prepared;
 
-        state.last_content_size = content_ui.min_size();
+        if self.allow_wrowing_bigger_than_the_contents {
+            state.last_content_size = content_ui.max_size();
+        } else {
+            state.last_content_size = content_ui.min_size();
+        }
 
         // ------------------------------
 
diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs
index 40aa0b0d..fb1ed400 100644
--- a/egui/src/containers/window.rs
+++ b/egui/src/containers/window.rs
@@ -185,6 +185,12 @@ impl<'open> Window<'open> {
         self
     }
 
+    /// Whether we can make it grow bigger than the contents
+    pub fn allow_grow(mut self, allow_grow: bool) -> Self {
+        self.resize = self.resize.allow_grow(allow_grow);
+        self
+    }
+
     /// Can the window be collapsed by clicking on its title?
     pub fn collapsible(mut self, collapsible: bool) -> Self {
         self.collapsible = collapsible;
diff --git a/egui/src/ui.rs b/egui/src/ui.rs
index 413677c5..8ee9d119 100644
--- a/egui/src/ui.rs
+++ b/egui/src/ui.rs
@@ -432,6 +432,11 @@ impl Ui {
         self.min_rect().size()
     }
 
+    /// Size of content; same as `max_rect().size()`
+    pub fn max_size(&self) -> Vec2 {
+        self.max_rect().size()
+    }
+
     /// New widgets will *try* to fit within this rectangle.
     ///
     /// Text labels will wrap to fit within `max_rect`.

Then a fixed size window can be created with .fixed_size([700.0, 700.0]).allow_grow(true).

JosuGZ avatar Aug 09 '22 17:08 JosuGZ

There is now a crate that offers tabs and docking: https://crates.io/crates/egui_dock

Will there be an official implementation of this?

Jonne-G avatar Dec 12 '22 02:12 Jonne-G

I would love to have an official implementation of this as well

ghmendonca avatar Feb 09 '23 20:02 ghmendonca

Some basic tabs could make sense to add to egui, but I worry that it will soon lead to feature requests:

  • Scrolling of tabs when they are too many
  • Drag-drop to re-arrange tabs
  • Button to add more tabs
  • etc

Still, we could perhaps have a simple tab container that just resists all these feature requests which are better served by external libraries, such as:

  • https://crates.io/crates/egui_dock
  • https://crates.io/crates/egui_tiles

emilk avatar Jan 06 '24 22:01 emilk

depending on the implementation of the tabs, scrolling could be solved with a horizontal scroll area and a 'new tab' button could literally be that, a button that adds a tab widget to a list. For that to work though, the tab widget would have to work on its own. Not like "SelectableValue" which uses an enum that has a fixed amount of entries. Then you could just iterate over and show them.

A drag-drop feature could be possible if Sense::drag would be implemented for the widget but that might be out of scope.

If implemented that way these feature requests could easily be implemented by the programmer themselves without having to make them a core functionality.

Then again, the only thing that keeps selectable values from being used exactly like that is the enum part and maybe tabs should be something entirely unique

Qwyntex avatar Feb 02 '24 18:02 Qwyntex