-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Replies: 1 comment · 14 replies
-
There's a drag-and-drop example on egui.rs. Here's the source: https://github.com/emilk/egui/blob/master/crates/egui_demo_lib/src/demo/drag_and_drop.rs |
Beta Was this translation helpful? Give feedback.
All reactions
-
I have shared my impl BrowserSection {
fn new() -> Self {
Self {
path: PathBuf::from("/"),
samples: Vec::new(),
}
}
fn render(&mut self, ui: &mut egui::Ui, dnd: &mut Dnd) {
self.directory_browser(ui, &self.path.clone(), dnd);
}
fn directory_browser(&mut self, ui: &mut egui::Ui, path: &std::path::Path, dnd: &mut Dnd) {
if let Ok(entries) = std::fs::read_dir(path) {
entries.filter_map(|entry| entry.ok()).for_each(|entry| {
let mut samples_clone = self.samples.clone();
let path = entry.path();
let file_tree_id = egui::Id::new("file_tree_")
.with(entry.file_name().to_string_lossy())
.with(entry.path().to_string_lossy());
// let mut dnd = Dnd::default();
// let source_col: Option<(usize, usize)> = None;
// let drop_col: Option<usize> = None;
// let mut samples: Vec<Sample> = Vec::new();
// let mut samples_clone = samples.clone();
// let file_tree_id = ui.memory(|mem| mem.dragged_id().unwrap());
// Dnd::drag_source(ui, file_tree_id, |ui| {
ui.dnd_drag_source(file_tree_id, samples_clone.clone(), |ui| {
if path.is_dir() {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
file_tree_id,
false,
)
.show_header(ui, |ui| {
let directory = ui.selectable_label(
false,
format!("📁 {}", entry.file_name().to_string_lossy()),
);
// samples_clone.push(Sample::new(
// false,
// format!("{}", entry.file_name().to_string_lossy()),
// format!("{}", entry.file_name().to_string_lossy()),
// format!("{}", entry.file_name().to_string_lossy()),
// 2,
// 132,
// "00:32".to_string(),
// 44100,
// 51243,
// format!("{}", entry.path().to_string_lossy()),
// ));
// if directory.hovered() {
// ui.ctx().set_cursor_icon(egui::CursorIcon::Grab);
// }
// let dir_sense = directory.interact(egui::Sense::click_and_drag());
// if dir_sense.dragged() {
// log::debug!("Directory is being dragged..");
// ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
// }
// if dir_sense.drag_started() {
// dnd.set_drag_id(file_tree_id);
// dnd.set_payload_type(Payload::SampleDir);
// dnd.set_payload(samples);
// }
})
.body(|ui| {
// let header = egui::CollapsingHeader::new(format!(
// "📁 {}",
// entry.file_name().to_string_lossy()
// ))
// .default_open(false)
// .show(ui, |ui| {});
self.directory_browser(ui, &path, dnd);
});
} else {
let file = ui.selectable_label(
false,
format!("📄 {}", &entry.file_name().to_string_lossy()),
);
// samples_clone.push(Sample::new(
// false,
// format!("{}", entry.file_name().to_string_lossy()),
// format!("{}", entry.file_name().to_string_lossy()),
// format!("{}", entry.file_name().to_string_lossy()),
// 2,
// 132,
// "00:32".to_string(),
// 44100,
// 51243,
// format!("{}", entry.path().to_string_lossy()),
// ));
// if file.hovered() {
// ui.ctx().set_cursor_icon(egui::CursorIcon::Grab);
// }
// let file_sense = file.interact(egui::Sense::click_and_drag());
// if file_sense.dragged() {
// log::debug!("File is being dragged..");
// ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
// }
// if file_sense.drag_started() {
// dnd.set_drag_id(file_tree_id);
// dnd.set_payload_type(Payload::SampleFile);
// dnd.set_payload(samples);
// }
}
});
// if let Some((source_col, source_row)) = source_col {
// if let Some(drop_col) = drop_col {
// if ui.input(|i| i.pointer.any_released()) {
// // do the drop:
// let item = samples_clone[source_col].remove(source_row);
// samples_clone[drop_col].push(item);
// }
// }
// }
});
}
}
} I left all the commented code I have as well from when I was creating my own
There is no option for min height or something in
This code where I asked if I should call |
Beta Was this translation helpful? Give feedback.
All reactions
-
Let's take a step back and think about how to troubleshoot and effectively communicate.
Honestly, your attitude is annoying and unproductive. I suggest you change it. If you think posting the same code over and over again will achieve a different result you're mistaken. The problem isn't that we can't use scrollbars and read the overcomplicated, incomplete fraction of code you've posted (something which has been pointed out several times), the problem is that you don't understand the code you're writing. Everyone here can see that the example code works, and everyone here (except you) has successfully used these APIs. You need to find a better way to communicate why you're unable to do the same. When I see 100 lines of unrunnable code that are 2/3 comments, I don't even waste my time reading it. If I were you, and I were serious about solving problems, here's what I would do:
Here's the secret: 99% of the time if you follow these two steps (without skipping/omitting details), you will solve the problem yourself. https://en.wikipedia.org/wiki/Rubber_duck_debugging:
You aren't describing what the code is supposed to do because you don't know what it does. That's your problem.
Then I suggest you go read the code and figure out what determines the height of a table. My point is that your table is clearly getting scaled down on the y-axis. Look carefully at the two screenshots you posted: one has lines and no background; the other has a background (which is the same color as the lines) and no (visible) lines extending downward. That strongly suggests that the table inside a When you create any widget it will return a |
Beta Was this translation helpful? Give feedback.
All reactions
-
How do you know what was my tone, when I was writing that sentence?
What makes you think I wanted to share the code multiple times? I shared it because the person I was talking with wanted me to. OK, I guess I don't understand the code that I'm writing, that's why I'm here for help.
I guess this is why you are assuming a lot when writing this reply to me, because you don't know/understand half the situation.
Yes I noticed that.
Yes, I'm making assumptions, its probably because that is what makes sense to me for now, but I know its wrong, so I'm here to ask for help, so I can clear that doubt I have. Again, if you think I am being rude, you can simply not reply to me. |
Beta Was this translation helpful? Give feedback.
All reactions
-
I said nothing about your tone. I'm referring to your attitude. The way to solve problems is not only to ask questions, it's to do work (and to show that work). Your initial post is extremely broad, and it will be better to focus on the immediate problem rather than all the extra details.
You're right, but I've already put effort into helping so it's a waste of time for it to go nowhere.
I understand the situation 100% clearly. You have a collapsible list and you want to drag and drop entries to a table. You are asking questions about code which is not visible, or which is buried in a bunch of irrelevant details making it harder to read. Your code also isn't runnable, so no one can verify your claims. You're asking us to make guesses about your guesses. This is impossible. I explained how to use the two functions you need to use, and even though you're using them you can't get your code to work. This perplexes me, because you are making calls to these functions, but you claim that it isn't working. I will pretend to be you, and solve this problem in the manner that I explained to you. Start with a minimal example. We need a collapsible list and a table. Expand for code (the boilerplate code is long)# Cargo.toml
[package]
name = "minimal-example"
version = "0.1.0"
edition = "2021"
[dependencies]
egui = "0.27"
egui_extras = "0.27" // main.rs
use eframe::egui::{self, ViewportBuilder};
use egui_extras::Column;
struct Entry {
a: String,
b: String,
}
struct MinimalExampleApp {
list_entries: Vec<Entry>,
table_entries: Vec<Entry>,
}
impl Default for MinimalExampleApp {
fn default() -> Self {
MinimalExampleApp {
list_entries: vec![
Entry { a: "drag".into(), b: "this".into() },
Entry { a: "also".into(), b: "draggable".into() },
Entry { a: "another".into(), b: "draggable".into() },
],
table_entries: vec![],
}
}
}
impl eframe::App for MinimalExampleApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
// Collapsible list here
let collapse_id = ui.make_persistent_id("collapse");
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(), collapse_id, false
)
.show_header(ui, |ui| {
ui.label("draggables");
})
.body(|ui| {
for draggable in self.list_entries.iter() {
ui.selectable_label(false, format!("{} {}", draggable.a, draggable.b));
}
});
// Table here
let mut table = egui_extras::TableBuilder::new(ui)
.column(Column::auto().resizable(true))
.column(Column::auto().resizable(true))
.header(20.0, |mut header| {
header.col(|ui| { ui.heading("a"); });
header.col(|ui| { ui.heading("b"); });
})
.body(|mut body| {
for entry in self.table_entries.iter() {
body.row(20.0, |mut row| {
row.col(|ui| { ui.label(entry.a.clone()); });
row.col(|ui| { ui.label(entry.b.clone()); });
})
}
});
});
}
}
fn main() {
let native_options = eframe::NativeOptions {
viewport: ViewportBuilder::default()
.with_inner_size((1024., 768.))
.with_resizable(true),
..Default::default()
};
let _ = eframe::run_native(
"minimal example",
native_options,
Box::new(|cc| {
Box::new(
MinimalExampleApp::default()
)
})
);
} Looks good to me. Now I will modify the // In the CollapsingState code
.body(|ui| {
for (n, draggable) in self.list_entries.iter().enumerate() {
// You will have to figure out the best way to make a unique Id
// I'm going to take a simple approach
let id = egui::Id::new(format!("drag-{}", n));
let label = egui::SelectableLabel::new(false, format!("{} {}", draggable.a, draggable.b));
let payload = draggable.clone();
ui.dnd_drag_source(id, payload, |ui| {
ui.add(label);
});
}
}); Good, the label is draggable: Worth noting: it can no longer be a selectable label. Why? How can egui tell the difference between a click and a drag? You will have to do more work for this. What you will probably do is copy-paste the source for Now I will make the table a drop zone: // Table here
let table_frame = egui::Frame::default();
let (drop_zone_rsp, drop_payload) = ui.dnd_drop_zone::<Entry, ()>(table_frame, |ui| {
egui_extras::TableBuilder::new(ui)
.column(Column::initial(100.0).resizable(true))
.column(Column::initial(100.0).resizable(true))
.header(20.0, |mut header| {
header.col(|ui| { ui.heading("a"); });
header.col(|ui| { ui.heading("b"); });
})
.body(|mut body| {
for entry in self.table_entries.iter() {
body.row(20.0, |mut row| {
row.col(|ui| { ui.label(entry.a.clone()); });
row.col(|ui| { ui.label(entry.b.clone()); });
});
}
});
}); Here it is before dragging over the table: Here it is when dragging over the table: Everything is good up till now, but the table is short and it looks weird. I look at all the methods in the documentation for TableBuilder. Hey, what's that? There's a method called // Table here
let table_frame = egui::Frame::default();
let (drop_zone_rsp, drop_payload) = ui.dnd_drop_zone::<Entry, ()>(table_frame, |ui| {
egui_extras::TableBuilder::new(ui)
.auto_shrink(egui::Vec2b::new(false, false))
// ... and so on ... I guess next time I have a problem I'll read the documentation. It only takes 5 minutes. Let's finish it off. After the if let Some(payload) = drop_payload {
self.table_entries.push((*payload).clone());
} Done. One last question: why does the table background change? I'm going to look at the source for There's the problem. Okay, cool, it's taking the colors from the current style settings. Looks like someone already found a solution. Let's go for full marks here: // Table here
let table_frame = egui::Frame::default();
ui.visuals_mut().widgets.inactive.bg_fill = egui::Color32::TRANSPARENT;
let (drop_zone_rsp, drop_payload) = ui.dnd_drop_zone::<Entry, ()>(table_frame, |ui| {
// ... and so on ... 🎉 Final `main.rs`use eframe::egui::{self, ViewportBuilder};
use egui_extras::Column;
#[derive(Clone)]
struct Entry {
a: String,
b: String,
}
struct MinimalExampleApp {
list_entries: Vec<Entry>,
table_entries: Vec<Entry>,
}
impl Default for MinimalExampleApp {
fn default() -> Self {
MinimalExampleApp {
list_entries: vec![
Entry { a: "drag".into(), b: "this".into() },
Entry { a: "also".into(), b: "draggable".into() },
Entry { a: "another".into(), b: "draggable".into() },
],
table_entries: vec![],
}
}
}
impl eframe::App for MinimalExampleApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
// Collapsible list here
let collapse_id = ui.make_persistent_id("collapse");
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(), collapse_id, false
)
.show_header(ui, |ui| {
ui.label("draggables");
})
.body(|ui| {
for (n, draggable) in self.list_entries.iter().enumerate() {
// You will have to figure out the best way to make a unique Id
// I'm going to take a simple approach
let id = egui::Id::new(format!("drag-{}", n));
let label = egui::SelectableLabel::new(false, format!("{} {}", draggable.a, draggable.b));
let payload = draggable.clone();
ui.dnd_drag_source(id, payload, |ui| {
ui.add(label);
});
}
});
// Table here
let table_frame = egui::Frame::default();
ui.visuals_mut().widgets.inactive.bg_fill = egui::Color32::TRANSPARENT;
let (drop_zone_rsp, drop_payload) = ui.dnd_drop_zone::<Entry, ()>(table_frame, |ui| {
egui_extras::TableBuilder::new(ui)
.auto_shrink(egui::Vec2b::new(false, false))
.column(Column::initial(100.0).resizable(true))
.column(Column::initial(100.0).resizable(true))
.header(20.0, |mut header| {
header.col(|ui| { ui.heading("a"); });
header.col(|ui| { ui.heading("b"); });
})
.body(|mut body| {
for entry in self.table_entries.iter() {
body.row(20.0, |mut row| {
row.col(|ui| { ui.label(entry.a.clone()); });
row.col(|ui| { ui.label(entry.b.clone()); });
});
}
});
});
if let Some(payload) = drop_payload {
self.table_entries.push((*payload).clone());
}
});
}
}
fn main() {
let native_options = eframe::NativeOptions {
viewport: ViewportBuilder::default()
.with_inner_size((1024., 768.))
.with_resizable(true),
..Default::default()
};
let _ = eframe::run_native(
"minimal example",
native_options,
Box::new(|cc| {
Box::new(
MinimalExampleApp::default()
)
})
);
} So, in about 45 minutes (mostly because I had to format this post) I solved something you've been stuck on for weeks. It isn't because I've done it before. I've never used the collapsible list widget, and I didn't know the table had an
🤣 It's hard for me to explain why "2/3 commented out" is a code smell, but it is. Please understand I was trying to help. I hope this demonstrates that what I'm saying is true. |
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 1
-
And a quick hint on how you might modify } else {
let InnerResponse { inner, response } = self.scope(add_contents);
// Check for drags:
let dnd_response = self
.interact(response.rect, id, Sense::drag()) // <-- there is also Sense::click_and_drag()
.on_hover_cursor(CursorIcon::Grab);
InnerResponse::new(inner, dnd_response | response)
} I will leave it to you to figure out how to not have the scoped UI override the
My best guess is that changing that single line might work, but I haven't tried it. |
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 2
-
I'm trying to implement drag and drop by looking at the example form the repository but can't understand how would it work for my use case.
I basically have a side panel which shows a file tree of all the files and directories on the system using
CollapsibleState
s andSelectableLabel
s. I want to be able to drag "files/directories" to another widget, which is aTable
, filling up every column with the individual file's metadata one file per row..Here is what I tried so far,
This is form
side_panel.rs
This is from
sample_viewer.rs
(the table widget)I created my own
drag_and_drop.rs
file which has the functions from the example plus a little extra,here is the
drag_and_drop.rs
I'm passing
dnd
as an argument to both thedirectory_browser
andsample_viewer
which I pass in theupdate()
inapp.rs
,Sample
is just astruct
,with a bunch of
get
andset
methods for getting and/or setting individual fields..Am I even on the right track?
ATM it just shows correct cursors and animations nothing else,
https://github.com/emilk/egui/assets/53279454/18cd84a2-99d3-4438-ba11-6f7331d71a4c
This project is a re-write of my older project https://gitlab.com/samplehive/sample-hive
Here's a peek of what I basically want,
https://github.com/emilk/egui/assets/53279454/8859c35b-d539-4f82-abe1-69ac48739e6f
Beta Was this translation helpful? Give feedback.
All reactions