use crate::canvas::box_select::draw_band;
use crate::canvas::caches::Caches;
use crate::canvas::definition_drawing::{Placement, draw_outline, fixture_footprint};
use crate::canvas::shapes::Shapes;
use crate::canvas::view::fit_scene_square;
use egui::emath::TSTransform;
use egui::{Color32, PointerButton, Pos2, Rect, Response, Sense, Ui};
use gled_model::collection::Collections;
use gled_model::patching::Patch;
use gled_model::patching::fixture::FixtureId;
use gled_model::patching::layout::LayoutRef;
const MIN_FIXTURE_PX: f32 = 12.0;
const IN_LAYOUT: Color32 = Color32::from_rgb(110, 200, 110);
const IN_LAYOUT_HOVERED: Color32 = Color32::from_rgb(175, 255, 175);
const AVAILABLE: Color32 = Color32::from_gray(85);
const AVAILABLE_HOVERED: Color32 = Color32::from_gray(150);
pub(crate) type Band = Option<(Pos2, Pos2)>;
pub(crate) fn member_preview(
ui: &mut Ui,
patch: &Patch,
layout: LayoutRef,
collections: &Collections,
caches: &mut Caches,
band: &mut Band,
) -> Vec<(FixtureId, bool)> {
let (rect, response) =
ui.allocate_exact_size(ui.available_size_before_wrap(), Sense::click_and_drag());
let to_global = fit_scene_square(rect);
let painter = ui.painter_at(rect);
let layout = patch.layout(layout);
let member = |id: &FixtureId| layout.is_some_and(|layout| layout.contains(*id));
let pointer = ui
.ctx()
.pointer_hover_pos()
.filter(|position| rect.contains(*position));
let scene_pointer = pointer.map(|position| to_global.inverse() * position);
let mut clicked = None;
let mut covered = vec![];
let band_rect = band.map(|(start, end)| Rect::from_two_pos(start, end));
for (id, position) in &patch.topology.default_layout.members {
let Some(definition) = patch.definition(*id, collections) else {
continue;
};
let footprint = fixture_footprint(
&definition.data,
position,
to_global.scaling,
MIN_FIXTURE_PX,
);
let hovered = scene_pointer.is_some_and(|at| footprint.contains(at));
if hovered {
clicked = Some(*id);
}
if let Some(band_rect) = band_rect
&& band_rect.intersects(footprint.transformed(to_global).bounds())
{
covered.push(*id);
}
let member = member(id);
let placement = Placement {
position,
to_global,
min_screen_px: MIN_FIXTURE_PX,
};
draw_outline(
&painter,
&definition.data,
Some(definition.id.id),
&placement,
match (member, hovered) {
(true, false) => IN_LAYOUT,
(true, true) => IN_LAYOUT_HOVERED,
(false, false) => AVAILABLE,
(false, true) => AVAILABLE_HOVERED,
},
caches,
);
}
if let Some(band_rect) = band_rect {
let mut shapes = Shapes::default();
draw_band(&mut shapes, &TSTransform::IDENTITY, band_rect);
painter.add(shapes.into_shape());
}
let mut moves = vec![];
if response.clicked()
&& let Some(id) = clicked
{
moves.push((id, !member(&id)));
}
let including = !ui.input(|input| input.modifiers.alt);
moves.extend(drag(&response, band, covered).map(|id| (id, including)));
moves
}
fn drag(
response: &Response,
band: &mut Band,
covered: Vec<FixtureId>,
) -> impl Iterator<Item = FixtureId> {
if response.drag_started_by(PointerButton::Primary)
&& let Some(at) = response.interact_pointer_pos()
{
*band = Some((at, at));
}
if response.dragged()
&& let Some((start, _)) = *band
&& let Some(at) = response.interact_pointer_pos()
{
*band = Some((start, at));
}
let swept = response.drag_stopped().then(|| band.take()).flatten();
swept
.filter(|(start, end)| (*end - *start).length() > 1.0)
.map(|_| covered)
.unwrap_or_default()
.into_iter()
}