Skip to content

Instantly share code, notes, and snippets.

@enomado
Created April 28, 2022 14:28
Show Gist options
  • Save enomado/5e1de10edfa55bf005d95acd0930af2b to your computer and use it in GitHub Desktop.
Save enomado/5e1de10edfa55bf005d95acd0930af2b to your computer and use it in GitHub Desktop.
use std::{borrow::BorrowMut, f64::consts::TAU};
use eframe::{
egui::{
self,
plot::{CoordinatesFormatter, Corner, Legend, Line, LineStyle, Plot, Value, Values},
Button, ComboBox, Context, CursorIcon, Id, InnerResponse, Label, LayerId, Order, Response,
Sense, Ui, Widget,
},
emath::{remap, NumExt},
epaint::{self, color, vec2, Color32, Pos2, Rect, Shape, Stroke, Vec2},
epi,
};
pub fn drag_source(
ui: &mut Ui,
id: Id,
drag_handle: impl FnOnce(&mut Ui),
drag_body: impl FnOnce(&mut Ui),
) -> Option<Rect> {
let is_being_dragged = ui.memory().is_being_dragged(id);
if !is_being_dragged {
let row_resp = ui.horizontal(|gg| {
let u = gg.scope(drag_handle);
// Check for drags:
// let response = ui.interact(response.rect, id, Sense::click());
let response = gg.interact(u.response.rect, id, Sense::drag());
if response.hovered() {
gg.output().cursor_icon = CursorIcon::Grab;
}
drag_body(gg)
});
return Some(row_resp.response.rect);
// sponse.clicked() {
// println!("source clicked")
// }
} else {
ui.output().cursor_icon = CursorIcon::Grabbing;
// let response = ui.scope(body).response;
// Paint the body to a new layer:
let layer_id = LayerId::new(Order::Tooltip, id);
// let response = ui.with_layer_id(layer_id, body).response;
// Now we move the visuals of the body to where the mouse is.
// Normally you need to decide a location for a widget first,
// because otherwise that widget cannot interact with the mouse.
// However, a dragged component cannot be interacted with anyway
// (anything with `Order::Tooltip` always gets an empty [`Response`])
// So this is fine!
// if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
// let r = response.rect.center();
// let delta = pointer_pos - r;
// ui.ctx().translate_layer(layer_id, delta);
// }
if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
let u = egui::Area::new("draggable_item")
.interactable(false)
.fixed_pos(pointer_pos)
.show(ui.ctx(), |x| {
x.horizontal(|gg| {
drag_handle(gg);
drag_body(gg)
})
});
// u.response.rect.area()
}
}
None
}
pub fn drop_target<R>(
ui: &mut Ui,
can_accept_what_is_being_dragged: bool,
is_being_dragged: bool,
body: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
// рендерит колонку
// let is_being_dragged = ui.memory().is_anything_being_dragged();
// let is_being_dragged = false;
let margin = Vec2::splat(4.0);
let outer_rect_bounds = ui.available_rect_before_wrap();
let inner_rect = outer_rect_bounds.shrink2(margin);
let where_to_put_background = ui.painter().add(Shape::Noop);
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
let ret = body(&mut content_ui);
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
// let (rect, response) = ui.allocate_at_least(outer_rect.size(), Sense::hover());
let (rect, response) = ui.allocate_at_least(outer_rect.size(), Sense::hover());
let style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
ui.visuals().widgets.active
} else {
ui.visuals().widgets.inactive
};
let mut fill = style.bg_fill;
let mut stroke = style.bg_stroke;
// let mut stroke: Stroke = (0.4, Color32::RED).into();
if is_being_dragged && !can_accept_what_is_being_dragged {
// gray out:
fill = color::tint_color_towards(fill, ui.visuals().window_fill());
stroke.color = color::tint_color_towards(stroke.color, ui.visuals().window_fill());
}
ui.painter().set(
where_to_put_background,
epaint::RectShape {
rounding: style.rounding,
fill,
stroke,
rect,
},
);
InnerResponse::new(ret, response)
}
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
pub struct DragAndDropExampleState {
#[cfg_attr(feature = "persistence", serde(skip))]
columns: Vec<Vec<String>>,
}
impl Default for DragAndDropExampleState {
fn default() -> Self {
Self {
columns: vec![
vec!["Item A", "Item B", "Item C"],
vec!["Item D", "Item E"],
vec!["Item F", "Item G", "Item H"],
]
.into_iter()
.map(|v| v.into_iter().map(ToString::to_string).collect())
.collect(),
}
}
}
pub struct DragAndDropExample<'a> {
pub state: &'a mut DragAndDropExampleState,
}
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub(crate) struct DndIntState {
// #[cfg_attr(feature = "persistence", serde(skip))]
dragging_column_id: Option<usize>,
// #[cfg_attr(feature = "persistence", serde(skip))]
drop_col: Option<usize>,
// #[cfg_attr(feature = "persistence", serde(skip))]
source_col_row: Option<(usize, usize)>,
}
impl DndIntState {
pub fn load(ctx: &Context, id: Id) -> Option<Self> {
ctx.data().get_temp(id)
}
pub fn store(self, ctx: &Context, id: Id) {
ctx.data().insert_temp(id, self);
}
}
impl<'a> Widget for DragAndDropExample<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let dummy_resp = ui.label("This is a proof-of-concept of drag-and-drop in egui.");
ui.label("Drag items between columns.");
let id_source = "my_drag_and_drop_demo";
let mut container_rect = None;
let mut row_rect = None;
let columns = &mut self.state.columns;
let id = Id::new(id_source);
let mut state = DndIntState::load(ui.ctx(), id).unwrap_or_default();
let drag_target_row_position = &mut state.dragging_column_id;
let source_col_row = &mut state.source_col_row;
let drop_col = &mut state.drop_col;
//
ui.columns(columns.len(), |uis| {
for (col_idx, column) in columns.clone().into_iter().enumerate() {
let ui = &mut uis[col_idx];
let can_accept_what_is_being_dragged = true; // We accept anything being dragged (for now) ¯\_(ツ)_/¯
//
let this_col_is_dest = drop_col.map(|x| x == col_idx).unwrap_or(false);
let response = drop_target(
ui,
this_col_is_dest,
can_accept_what_is_being_dragged,
|ui| {
//
ui.set_min_size(vec2(64.0, 100.0));
for (row_idx, item) in column.iter().enumerate() {
let item_id = Id::new(id_source).with(col_idx).with(row_idx);
if source_col_row.is_some()
&& *drag_target_row_position == Some(row_idx)
&& drop_col.map(|col| col == col_idx).unwrap_or(false)
{
ui.add(Label::new("here"));
}
let c_row_size_rect = drag_source(
ui,
item_id,
|ui| {
let response = ui.add(Label::new(item).sense(Sense::click()));
// .sense(Sense::click())
if response.clicked() {
println!("button clicked handle")
}
response.context_menu(|ui| {
if ui.button("Remove").clicked() {
columns[col_idx].remove(row_idx);
ui.close_menu();
}
});
},
|ui| {
let butt = Button::new(item).sense(Sense::click());
let response = ui.add(butt);
if response.clicked() {
println!("button clicked sss")
}
},
);
c_row_size_rect.map(|x| row_rect = Some(x));
if ui.memory().is_being_dragged(item_id) {
*source_col_row = Some((col_idx, row_idx));
}
}
if source_col_row.is_some()
&& drag_target_row_position
.map(|x| x >= column.len())
.unwrap_or(false)
&& drop_col.map(|col| col == col_idx).unwrap_or(false)
{
ui.add(Label::new("here1"));
}
},
)
.response;
let response = response.context_menu(|ui| {
if ui.button("New Item").clicked() {
columns[col_idx].push("New Item".to_string());
ui.close_menu();
}
});
// let is_being_dragged = ui.memory().is_anything_being_dragged();
let is_being_dragged = source_col_row.is_some();
if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
*drop_col = Some(col_idx);
container_rect = Some(response.rect);
}
}
});
if let (Some(drop_col), Some(row_rect), Some(container_rect)) =
(*drop_col, row_rect, container_rect)
{
if ui.memory().is_anything_being_dragged() {
let pos = ui.input().pointer.hover_pos();
let row_rectr = row_rect.size();
let offset = pos.unwrap() - container_rect.min;
let drag_position =
(((offset.y - row_rectr.y / 2.) / row_rectr.y).round() as usize);
// .at_most(self.columns[drop_col].len().saturating_sub(1));
*drag_target_row_position = Some(drag_position);
} else {
*drag_target_row_position = None;
}
} else {
*drag_target_row_position = None;
}
if let Some((source_col, source_row)) = *source_col_row {
if let Some(drop_col) = *drop_col {
//
if ui.input().pointer.any_released() {
// dbg!();
// do the drop:
if let Some(drag_target_row_position) = drag_target_row_position {
let item = columns[source_col].remove(source_row);
let insert_index =
drag_target_row_position.at_most(columns[drop_col].len());
columns[drop_col].insert(insert_index, item);
}
}
}
}
if ui.input().pointer.any_released() {
*source_col_row = None;
*drop_col = None
}
ui.vertical_centered(|ui| {
ui.label("text sdfsdf")
// ui.add(crate::__egui_github_link_file!());
});
state.store(ui.ctx(), id);
dummy_resp
}
}
impl<'a> DragAndDropExample<'a> {
pub fn new(dnd_example: &'a mut DragAndDropExampleState) -> DragAndDropExample<'a> {
DragAndDropExample { state: dnd_example }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment