Skip to content

filter_map_tabs does not update focus #296

@chieltbest

Description

@chieltbest

Describe the bug
When using filter_map_tabs in a DockState that includes Empty surfaces or filtered tabs, the focus is not updated appropriately. This can cause a panic in some cases.

To Reproduce
Small repro for a panic on startup because of this behaviour:
main.cpp:

use eframe::{egui, NativeOptions, Storage};

use egui_dock::{DockArea, DockState, Style};
use serde::{Deserialize, Serialize};

fn main() -> eframe::Result<()> {
	let options = NativeOptions::default();
	eframe::run_native(
		"My egui App",
		options,
		Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),
	)
}

struct TabViewer {}

impl egui_dock::TabViewer for TabViewer {
	type Tab = ();

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

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

#[derive(Serialize, Deserialize)]
struct MyApp {
	tree: DockState<()>,
}

impl MyApp {
	fn new(cc: &eframe::CreationContext<'_>) -> Self {
		cc.storage.and_then(|storage| {
			eframe::get_value(storage, "app")
				.map(|app: MyApp| {
					MyApp {
						tree: app.tree.filter_map_tabs(|_| Some(())),
					}
				})
		}).unwrap_or_default()
	}
}

impl Default for MyApp {
	fn default() -> Self {
		Self {
			tree: DockState::new(vec![()]),
		}
	}
}

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, &mut TabViewer {});
	}

	fn save(&mut self, storage: &mut dyn Storage) {
		eframe::set_value(storage, "app", self);
	}

	fn persist_egui_memory(&self) -> bool {
		false
	}
}

app.ron:

{
    "app": "(tree:(surfaces:[Main((nodes:[],focused_node:Some((0)),collapsed:false,collapsed_leaf_count:0)),Empty,Window((nodes:[Leaf((rect:(min:(x:455.0,y:386.375),max:(x:809.84375,y:724.78125)),viewport:(min:(x:455.0,y:410.375),max:(x:809.84375,y:724.78125)),tabs:[()],active:(0),scroll:0.0,collapsed:false))],focused_node:Some((0)),collapsed:false,collapsed_leaf_count:0),(screen_rect:None,dragged:false,next_position:None,next_size:None,expanded_height:None,new:false,minimized:false))],focused_surface:Some((2)),translations:(tab_context_menu:(close_button:\"Close\",eject_button:\"Eject\"),leaf:(close_button_disabled_tooltip:\"This leaf contains non-closable tabs.\",close_all_button:\"Close window\",close_all_button_menu_hint:\"Right click to close this window.\",close_all_button_modifier_hint:\"Press modifier keys (Shift by default) to close this window.\",close_all_button_modifier_menu_hint:\"Press modifier keys (Shift by default) or right click to close this window.\",close_all_button_disabled_tooltip:\"This window contains non-closable tabs.\",minimize_button:\"Minimize window\",minimize_button_menu_hint:\"Right click to minimize this window.\",minimize_button_modifier_hint:\"Press modifier keys (Shift by default) to minimize this window.\",minimize_button_modifier_menu_hint:\"Press modifier keys (Shift by default) or right click to minimize this window.\"))))",
}

Cargo.toml

[package]
name = "egui_dock_test"
version = "0.1.0"
edition = "2024"

[dependencies]
eframe = { version = "0.33.0", features = ["persistence"] }
egui_dock = { version = "0.18.0", features = ["serde"] }
serde = { version = "1.0.228", features = ["derive"] }

This will panic with the following message:

thread 'main' (796775) panicked at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/dock_state/mod.rs:292:13:
index out of bounds: the len is 2 but the index is 2
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library/std/src/panicking.rs:698:5
   1: core::panicking::panic_fmt
             at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library/core/src/panicking.rs:75:14
   2: core::panicking::panic_bounds_check
             at /rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library/core/src/panicking.rs:271:5
   3: <usize as core::slice::index::SliceIndex<[T]>>::index
             at /home/chiel/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs:267:10
   4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
             at /home/chiel/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs:18:15
   5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
             at /home/chiel/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3621:9
   6: <egui_dock::dock_state::DockState<Tab> as core::ops::index::Index<egui_dock::dock_state::surface_index::SurfaceIndex>>::index
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/dock_state/mod.rs:44:28
   7: egui_dock::dock_state::DockState<Tab>::focused_leaf
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/dock_state/mod.rs:292:13
   8: egui_dock::widgets::dock_area::show::leaf::<impl egui_dock::widgets::dock_area::DockArea<Tab>>::tabs
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/widgets/dock_area/show/leaf.rs:280:39
   9: egui_dock::widgets::dock_area::show::leaf::<impl egui_dock::widgets::dock_area::DockArea<Tab>>::tab_bar
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/widgets/dock_area/show/leaf.rs:167:36
  10: egui_dock::widgets::dock_area::show::leaf::<impl egui_dock::widgets::dock_area::DockArea<Tab>>::show_leaf
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/widgets/dock_area/show/leaf.rs:50:32
  11: egui_dock::widgets::dock_area::show::<impl egui_dock::widgets::dock_area::DockArea<Tab>>::render_nodes
             at /home/chiel/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui_dock-0.18.0/src/widgets/dock_area/show/mod.rs:326:22
  12: egui_dock::widgets::dock_area::show::window_surface::<impl egui_dock::widgets::dock_area::DockArea<Tab>>::show_window_surface::{{closure}}
...
(snip)

Similar panics can happen when filtering out tabs.

Expected behavior
The focused_surface and focused_node should update to the new surface and node index of the node that was previously focused, and if that node does not exist anymore the focus should move to a sensible location that does not cause a panic.

Screenshots
N/A

Additional context
I have had two different crashes reported by two different users where in both cases they were unable to open the application because I filter all the open tabs at the beginning of the program (here), meaning they had to reset/delete their user configuration to be able to make the program usable again.

I'd like to extend a small thank you to the people working on this library, it has been incredibly useful so far. :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions