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

Example with multiple Pane types? #5

Open
DoctorWhoof opened this issue Jun 4, 2023 · 4 comments
Open

Example with multiple Pane types? #5

DoctorWhoof opened this issue Jun 4, 2023 · 4 comments

Comments

@DoctorWhoof
Copy link

DoctorWhoof commented Jun 4, 2023

I recently found this crate and it is exactly what I needed and solves problems I was struggling with when using egui by itself. Thanks! However, I'm running into an issue and I can't figure out a way (I'm new to Rust, and that doesn't help):

What's the best way to implement multiple types of Pane, all in the same tree? I can see that the tree.ui(behavior, ui) function takes in a single behavior at the root, and I assume the individual Panes can have their own types that implement the Behavior trait, but... as soon as I use tiles.insert_pane(PaneType) with two different Pane types (both implementing Behavior) I get a "mismatched types" compiler error, even when I implementing the trait for Box<(dyn MyPaneType + 'static)> types, hoping to get around the type error.

I also tried getting around it by using an enum and handling each pane type in a match statement, but I'm still running into issues when passing data from the app to the panes, since the pane_ui function defined in the Behavior trait doesn't allow passing custom data. Or does it? I can't figure it out.

Long story short, would it be possible to add a tiny, very simple example that demonstrates using two different pane types that ideally display data from the main App, instead of creating multiple panes with the same Behavior as the examples currently do?

Thank you so much!

@DoctorWhoof
Copy link
Author

DoctorWhoof commented Jun 5, 2023

I solved the problem of sharing the app data with the panes via fields containing Arc<Data>, which feels like cheating but whatever!

I'm still using a "PaneKind" enum that contain the necessary additional data for each Pane kind, and a single Pane type that uses a match statement to process each enum kind. Just wondering if there's a cleaner solution and would love to see a professional example. Thanks!

@cguentherTUChemnitz
Copy link

I am also running at the moment one big Pane implementation, which knows it all and encapsulates it's own representation switch by an internal enum of PaneType matching over this, containing the different payload for the different types. But this overall feels like a workaround.

I tried to split my one Pane in multiple types all implementing impl egui_tiles::Behavior<SpecificPane> for TreeBehavior but than i lack the knowledge how i should specificy the type of the tree. According the the example it is egui_tiles::Tree<Pane>. But now i have different types of pane. My initial thinking was to search for trait and go for egui_tiles::Tree<Box<dyn pane_trait>>. But as far as i understand, the trait is bound to the specific Pane type by impl egui_tiles::Behavior<SpecificPane> for TreeBehavior and i am not able to define my tree by tree: egui_tiles::Tree<Box<dyn egui_tiles::Behavior<_>>>, due to rusts:

the placeholder `_` is not allowed within types on item signatures for structs

Am i missing here something trivial? It would be really nice to have a best-practice by an example on how to handle the different panes. Does anyone have a bit of insights how the expected handling is for putting different panes in one tree?

@cguentherTUChemnitz
Copy link

Ok i think i understand now what my misunderstanding was. I tried to use the Panes for conceptional distinction where it is not belonging to. After some refactoring i came up with the following solution:

Instead of distinct different types on Panes, i implement different types of egui::widgets handling their state and widget representation. Also a full context page is represented as a widget. That means i came down now to implement the Pane just as following in main:

enum Pane {
    DistinctType1(DistinctType1Widget),
    DistinctType1(DistinctType2Widget),
    DistinctType3(DistinctType3Widget),
}

impl egui_tiles::Behavior<Pane> for TreeBehavior {
    fn simplification_options(&self) -> egui_tiles::SimplificationOptions {
        egui_tiles::SimplificationOptions {
            all_panes_must_have_tabs: true,
            ..Default::default()
        }
    }

    fn tab_title_for_pane(&mut self, pane: &Pane) -> egui::WidgetText {
        match pane {
            Pane::DistinctType1(_) => egui::WidgetText::from("DistinctType1"),
            Pane::DistinctType2(_) => egui::WidgetText::from("DistinctType2"),
            Pane::DistinctType3(_) => egui::WidgetText::from("DistinctType3"),
        }
    }

    fn pane_ui(
        &mut self,
        ui: &mut egui::Ui,
        _tile_id: egui_tiles::TileId,
        pane: &mut Pane,
    ) -> egui_tiles::UiResponse {
        match pane {
            Pane::DistinctType1(widget) => ui.add(widget),
            Pane::DistinctType2(widget) => ui.add(widget),
            Pane::DistinctType3(widget) => ui.add(widget),
        };
        egui_tiles::UiResponse::None
    }
}

My DistinctTypeXWidget just implements the egui::Widget trait and handles its own state.

Creating new distinct widgets in a pane is looking like that:

impl eframe::App for MyMainApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
            ui.horizontal(|ui| {
                if ui.button("➕ DistinctType1").clicked() {
                    register_new_pane(Pane::DistinctType1(DistinctType1Widget::new()), self);
                }
                if ui.button("➕ DistinctType2").clicked() {
                    register_new_pane(Pane::DistinctType2(DistinctType2Widget::new()), self);
                }
                if ui.button("➕ DistinctType3").clicked() {
                    register_new_pane(Pane::DistinctType3(DistinctType3Widget::new()), self);
                }
            });
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            self.tree.ui(&mut self.behavior, ui);
        });
    }
}

This makes it compatible to example code again for handling the tree, since it handles only one type of Pane again. @DoctorWhoof does that maybe help you also conceptually?

@DoctorWhoof
Copy link
Author

That seems like a great solution! I’m on vacation at a healthy distance from my laptop, but will try this when I have a chance. Thanks for taking the time to reply, I’m sure this will be helpful to others as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants