use crate::image_loader::ThumbnailImageLoader;
use crate::ui::draw_thumbnail_stack;
use crate::ui_state::EditorModelUiState;
use egui::*;
use hydrate_model::{AssetId, EditorModel};
use std::sync::Mutex;
#[derive(Clone)]
pub enum DragDropPayload {
AssetReferences(AssetId, Vec<AssetId>),
}
lazy_static::lazy_static! {
static ref DRAG_DROP_PAYLOAD : Mutex<Option<DragDropPayload>> = {
Mutex::new(None)
};
}
pub fn render_payload(
ui: &mut egui::Ui,
payload: &DragDropPayload,
editor_model: &EditorModel,
editor_model_ui_state: &EditorModelUiState,
thumbnail_image_loader: &ThumbnailImageLoader,
) {
match payload {
DragDropPayload::AssetReferences(primary_asset_id, all_asset_ids) => {
if all_asset_ids.len() > 1 {
ui.label(format!("{} selected assets", all_asset_ids.len()));
} else {
let path = editor_model
.asset_path(*primary_asset_id, &editor_model_ui_state.asset_path_cache);
if let Some(path) = path {
ui.label(format!("{}", path.as_str()));
} else {
ui.label(primary_asset_id.to_string());
}
}
draw_thumbnail_stack(
ui,
editor_model,
thumbnail_image_loader,
*primary_asset_id,
all_asset_ids.iter().copied(),
);
}
}
}
pub fn set_payload(payload: DragDropPayload) {
*DRAG_DROP_PAYLOAD.lock().unwrap() = Some(payload);
}
pub fn peek_payload() -> Option<DragDropPayload> {
DRAG_DROP_PAYLOAD.lock().unwrap().clone()
}
pub fn take_payload() -> Option<DragDropPayload> {
DRAG_DROP_PAYLOAD.lock().unwrap().take()
}
pub fn drag_source<ParamsT>(
ui: &mut Ui,
id: Id,
editor_model: &EditorModel,
editor_model_ui_state: &EditorModelUiState,
thumbnail_image_loader: &ThumbnailImageLoader,
params: &mut ParamsT,
create_payload_fn: impl FnOnce(&mut ParamsT) -> DragDropPayload,
body: impl FnOnce(&mut Ui, &mut ParamsT) -> Response,
) {
let is_being_dragged = ui.memory(|mem| mem.is_being_dragged(id));
if !is_being_dragged {
let response = body(ui, params);
let mut allow_check_for_drag = false;
ui.input(|input| {
if let Some(press_origin) = input.pointer.press_origin() {
if let Some(latest_pos) = input.pointer.latest_pos() {
if press_origin.distance(latest_pos) > 6.0 {
allow_check_for_drag = true;
}
}
}
});
if allow_check_for_drag && response.hovered() {
ui.memory_mut(|mem| mem.set_dragged_id(id));
set_payload(create_payload_fn(params));
}
} else {
ui.ctx().set_cursor_icon(CursorIcon::Grabbing);
let pointer_pos = ui.ctx().input(|input| input.pointer.latest_pos());
body(ui, params);
if let Some(mut pointer_pos) = pointer_pos {
pointer_pos.x += 10.0;
egui::Area::new("dragged_source")
.movable(false)
.fixed_pos(pointer_pos)
.show(ui.ctx(), |ui| {
render_payload(
ui,
&create_payload_fn(params),
editor_model,
editor_model_ui_state,
thumbnail_image_loader,
);
});
}
}
}
pub fn drop_target<R>(
ui: &mut Ui,
can_accept_what_is_being_dragged: bool,
body: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
let is_being_dragged = ui.memory(|mem| mem.is_anything_being_dragged());
let margin = Vec2::splat(0.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 style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
ui.visuals().widgets.active
} else {
ui.visuals().widgets.inactive
};
if is_being_dragged {
let mut fill = Some(style.bg_fill);
let mut stroke = style.bg_stroke;
if !can_accept_what_is_being_dragged {
fill = None;
stroke.color = ui.visuals().gray_out(stroke.color);
}
ui.painter().set(
where_to_put_background,
if let Some(fill) = fill {
epaint::RectShape::new(rect, style.rounding, fill, stroke)
} else {
epaint::RectShape::stroke(rect, style.rounding, stroke)
},
);
}
InnerResponse::new(ret, response)
}
pub fn try_take_dropped_payload(
ui: &egui::Ui,
response: &egui::Response,
) -> Option<DragDropPayload> {
let is_being_dragged = ui.memory(|mem| mem.is_anything_being_dragged());
let any_released = ui.input(|input| input.pointer.any_released());
if is_being_dragged && response.hovered() && any_released {
take_payload()
} else {
None
}
}