#![doc = include_str!("../README.md")]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
use egui::{Id, Ui};
pub use state::{DragDropConfig, DragDropItem, DragDropResponse, DragUpdate, Handle};
pub use crate::item_iterator::ItemIterator;
use crate::state::DragDropUi;
use std::hash::Hash;
mod item;
mod item_iterator;
mod state;
pub mod utils;
pub struct Dnd<'a> {
id: Id,
ui: &'a mut Ui,
drag_drop_ui: DragDropUi,
}
pub fn dnd(ui: &mut Ui, id_source: impl Hash) -> Dnd<'_> {
let id = Id::new(id_source).with("dnd");
let mut dnd_ui: DragDropUi =
ui.data_mut(|data| (*data.get_temp_mut_or_default::<DragDropUi>(id)).clone());
dnd_ui.return_animation_time = ui.style().animation_time;
dnd_ui.swap_animation_time = ui.style().animation_time;
Dnd {
id,
ui,
drag_drop_ui: dnd_ui,
}
}
impl<'a> Dnd<'a> {
pub fn new(ui: &'a mut Ui, id_source: impl Hash) -> Self {
dnd(ui, id_source)
}
#[must_use]
pub fn with_mouse_config(mut self, config: DragDropConfig) -> Self {
self.drag_drop_ui = self.drag_drop_ui.with_mouse_config(config);
self
}
#[must_use]
pub fn with_touch_config(mut self, config: Option<DragDropConfig>) -> Self {
self.drag_drop_ui = self.drag_drop_ui.with_touch_config(config);
self
}
#[must_use]
pub fn with_return_animation_time(mut self, animation_time: f32) -> Self {
self.drag_drop_ui.return_animation_time = animation_time;
self
}
#[must_use]
pub fn with_swap_animation_time(mut self, animation_time: f32) -> Self {
self.drag_drop_ui.swap_animation_time = animation_time;
self
}
#[must_use]
pub fn with_animation_time(mut self, animation_time: f32) -> Self {
self.drag_drop_ui.return_animation_time = animation_time;
self.drag_drop_ui.swap_animation_time = animation_time;
self
}
pub fn show<T: DragDropItem>(
self,
items: impl Iterator<Item = T>,
mut item_ui: impl FnMut(&mut Ui, T, Handle, ItemState),
) -> DragDropResponse {
#[allow(clippy::used_underscore_items)]
self._show_with_inner(|_id, ui, drag_drop_ui| {
drag_drop_ui.ui(ui, |ui, iter| {
items.enumerate().for_each(|(i, item)| {
iter.next(ui, item.id(), i, true, |ui, item_handle| {
item_handle.ui(ui, |ui, handle, state| item_ui(ui, item, handle, state))
});
});
})
})
}
pub fn show_sized<T: DragDropItem>(
self,
items: impl Iterator<Item = T>,
size: egui::Vec2,
mut item_ui: impl FnMut(&mut Ui, T, Handle, ItemState),
) -> DragDropResponse {
#[allow(clippy::used_underscore_items)]
self._show_with_inner(|_id, ui, drag_drop_ui| {
drag_drop_ui.ui(ui, |ui, iter| {
items.enumerate().for_each(|(i, item)| {
iter.next(ui, item.id(), i, true, |ui, item_handle| {
item_handle.ui_sized(ui, size, |ui, handle, state| {
item_ui(ui, item, handle, state);
})
});
});
})
})
}
pub fn show_vec<T>(
self,
items: &mut [T],
item_ui: impl FnMut(&mut Ui, &mut T, Handle, ItemState),
) -> DragDropResponse
where
for<'b> &'b mut T: DragDropItem,
{
let response = self.show(items.iter_mut(), item_ui);
response.update_vec(items);
response
}
pub fn show_vec_sized<T: DragDropItem>(
self,
items: &mut [T],
size: egui::Vec2,
item_ui: impl FnMut(&mut Ui, &mut T, Handle, ItemState),
) -> DragDropResponse
where
for<'b> &'b mut T: DragDropItem,
{
let response = self.show_sized(items.iter_mut(), size, item_ui);
response.update_vec(items);
response
}
pub fn show_custom(self, f: impl FnOnce(&mut Ui, &mut ItemIterator)) -> DragDropResponse {
#[allow(clippy::used_underscore_items)]
self._show_with_inner(|_id, ui, drag_drop_ui| drag_drop_ui.ui(ui, f))
}
pub fn show_custom_vec<T: DragDropItem>(
self,
items: &mut [T],
f: impl FnOnce(&mut Ui, &mut [T], &mut ItemIterator),
) -> DragDropResponse {
let response = self.show_custom(|ui, iter| f(ui, items, iter));
response.update_vec(items);
response
}
fn _show_with_inner(
self,
inner_fn: impl FnOnce(Id, &mut Ui, &mut DragDropUi) -> DragDropResponse,
) -> DragDropResponse {
let Dnd {
id,
ui,
mut drag_drop_ui,
} = self;
let response = inner_fn(id, ui, &mut drag_drop_ui);
ui.ctx().data_mut(|data| data.insert_temp(id, drag_drop_ui));
response
}
}
pub struct ItemState {
pub dragged: bool,
pub index: usize,
}