use std::rc::Rc;
use gpui::prelude::*;
use gpui::{div, px, AnyElement, App, ElementId, IntoElement, SharedString, Window};
use super::chip::DragChip;
use crate::theme::theme;
type ItemBuilder = Rc<dyn Fn(usize, &mut Window, &mut App) -> AnyElement + 'static>;
type ReorderHandler = Rc<dyn Fn(usize, usize, &mut Window, &mut App) + 'static>;
type Labeler = Rc<dyn Fn(usize) -> SharedString + 'static>;
#[derive(Clone)]
struct SortDrag {
group: SharedString,
index: usize,
}
#[derive(IntoElement)]
pub struct SortableList {
id: ElementId,
group: SharedString,
count: usize,
item: ItemBuilder,
labeler: Option<Labeler>,
gap: f32,
on_reorder: Option<ReorderHandler>,
}
impl SortableList {
pub fn new<E>(
id: impl Into<SharedString>,
count: usize,
item: impl Fn(usize, &mut Window, &mut App) -> E + 'static,
) -> Self
where
E: IntoElement,
{
let group: SharedString = id.into();
SortableList {
id: ElementId::Name(group.clone()),
group,
count,
item: Rc::new(move |i, window, cx| item(i, window, cx).into_any_element()),
labeler: None,
gap: 4.0,
on_reorder: None,
}
}
pub fn label_of(mut self, labeler: impl Fn(usize) -> SharedString + 'static) -> Self {
self.labeler = Some(Rc::new(labeler));
self
}
pub fn gap(mut self, gap: f32) -> Self {
self.gap = gap.max(0.0);
self
}
pub fn on_reorder(
mut self,
handler: impl Fn(usize, usize, &mut Window, &mut App) + 'static,
) -> Self {
self.on_reorder = Some(Rc::new(handler));
self
}
}
impl RenderOnce for SortableList {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let t = theme(cx);
let accent = t.primary().hsla();
let mut root = div().id(self.id).flex().flex_col().gap(px(self.gap));
for i in 0..self.count {
let content = (self.item)(i, window, cx);
let label = match &self.labeler {
Some(labeler) => labeler(i),
None => SharedString::from(format!("Item {}", i + 1)),
};
let chip = DragChip {
value: SortDrag {
group: self.group.clone(),
index: i,
},
label,
};
let mut row = div()
.id(("guise-sortable-row", i))
.cursor_grab()
.border_t_2()
.border_color(gpui::transparent_black())
.on_drag(chip, |dragged: &DragChip<SortDrag>, _off, _w, cx| {
cx.new(|_| dragged.clone())
})
.drag_over::<DragChip<SortDrag>>(move |style, _drag, _window, _cx| {
style.border_color(accent)
})
.child(content);
if let Some(handler) = self.on_reorder.clone() {
let group = self.group.clone();
row = row.on_drop(move |dragged: &DragChip<SortDrag>, window, cx| {
if dragged.value.group == group && dragged.value.index != i {
handler(dragged.value.index, i, window, cx);
}
});
}
root = root.child(row);
}
root
}
}