use super::preview_state::{FacetOverlay, FacetOwners, SolidPreviewState};
use crate::{EditorModel, MainWindow};
use slint::{ComponentHandle, Model};
use std::{cell::RefCell, sync::Arc};
#[must_use]
pub fn tier_row_counts(ui: &MainWindow) -> (usize, usize) {
let concave = ui
.global::<EditorModel>()
.get_tiers()
.iter()
.filter(|row| row.kind == 1)
.count();
let total = ui.global::<EditorModel>().get_tiers().row_count();
(total - concave, concave)
}
#[must_use]
pub fn clicked_tier_row(ui: &MainWindow, owners: &FacetOwners, facet_id: u32) -> Option<usize> {
let (flat_count, concave_count) = tier_row_counts(ui);
owners.table_position(facet_id as usize, flat_count, concave_count)
}
thread_local! {
static FACET_OVERLAY: RefCell<FacetOverlay> = const {
RefCell::new(FacetOverlay {
hovered: None,
selected_facet: None,
multi_selected: Vec::new(),
provisional: Vec::new(),
moved: Vec::new(),
})
};
static SELECTED_FACET_ID: RefCell<Option<u32>> = const { RefCell::new(None) };
static DRAWN_TIERS: RefCell<Option<Arc<Vec<bool>>>> = const { RefCell::new(None) };
}
pub const fn apply_hover(overlay: &mut FacetOverlay, hovered: Option<u32>) {
overlay.hovered = hovered;
}
pub const fn apply_click(overlay: &mut FacetOverlay, facet: u32) {
overlay.selected_facet = Some(facet);
}
pub const fn apply_cleared_pick(overlay: &mut FacetOverlay) {
overlay.hovered = None;
overlay.selected_facet = None;
}
pub fn resubmit_facet_overlay(
preview_state: &SolidPreviewState,
mutate: impl FnOnce(&mut FacetOverlay),
) {
let overlay = FACET_OVERLAY.with(|cell| {
let mut overlay = cell.borrow_mut();
mutate(&mut overlay);
overlay.clone()
});
preview_state.set_outlines(&overlay.provisional, &overlay.moved);
preview_state.request_facet_overlay(overlay);
}
pub fn set_hovered_facet(preview_state: &SolidPreviewState, facet: Option<u32>) {
if FACET_OVERLAY.with(|cell| cell.borrow().hovered) == facet {
return;
}
resubmit_facet_overlay(preview_state, |overlay| apply_hover(overlay, facet));
}
pub fn select_clicked_facet(preview_state: &SolidPreviewState, facet: u32) {
set_selected_facet_id(Some(facet));
resubmit_facet_overlay(preview_state, |overlay| apply_click(overlay, facet));
}
pub fn clear_clicked_facet(preview_state: &SolidPreviewState) {
set_selected_facet_id(None);
resubmit_facet_overlay(preview_state, apply_cleared_pick);
}
#[must_use]
pub fn selected_facet_id() -> Option<u32> {
SELECTED_FACET_ID.with(|cell| *cell.borrow())
}
pub fn set_selected_facet_id(facet: Option<u32>) {
SELECTED_FACET_ID.with(|cell| *cell.borrow_mut() = facet);
}
pub fn note_drawn_tiers(tiers: Option<Arc<Vec<bool>>>) {
DRAWN_TIERS.with(|cell| *cell.borrow_mut() = tiers);
}
#[must_use]
pub fn drawn_tiers() -> Option<Arc<Vec<bool>>> {
DRAWN_TIERS.with(|cell| cell.borrow().clone())
}
#[cfg(test)]
mod tests {
use super::*;
fn busy() -> FacetOverlay {
FacetOverlay {
hovered: Some(1),
selected_facet: Some(2),
multi_selected: vec![3, 4],
provisional: vec![5],
moved: vec![6, 7],
}
}
#[test]
fn a_hover_keeps_the_clicked_facet_the_multi_select_and_both_outlines() {
let mut merged = busy();
apply_hover(&mut merged, Some(9));
assert_eq!(merged.hovered, Some(9));
assert_eq!(merged.selected_facet, Some(2));
assert_eq!(merged.multi_selected, vec![3, 4]);
assert_eq!(merged.provisional, vec![5]);
assert_eq!(merged.moved, vec![6, 7]);
apply_hover(&mut merged, None);
assert_eq!(
merged.selected_facet,
Some(2),
"leaving a facet keeps the click"
);
}
#[test]
fn a_click_keeps_the_hover_the_multi_select_and_both_outlines() {
let mut merged = busy();
apply_click(&mut merged, 8);
assert_eq!(merged.selected_facet, Some(8));
assert_eq!(merged.hovered, Some(1));
assert_eq!(merged.multi_selected, vec![3, 4]);
assert_eq!(merged.provisional, vec![5]);
assert_eq!(merged.moved, vec![6, 7]);
}
#[test]
fn a_click_that_misses_clears_only_the_pick() {
let mut merged = busy();
apply_cleared_pick(&mut merged);
assert_eq!((merged.hovered, merged.selected_facet), (None, None));
assert_eq!(merged.multi_selected, vec![3, 4]);
assert_eq!(merged.provisional, vec![5]);
assert_eq!(merged.moved, vec![6, 7]);
}
#[test]
fn merging_into_an_empty_overlay_changes_one_field() {
let mut hovered = FacetOverlay::default();
apply_hover(&mut hovered, Some(3));
assert_eq!(
hovered,
FacetOverlay {
hovered: Some(3),
..FacetOverlay::default()
}
);
let mut cleared = FacetOverlay::default();
apply_cleared_pick(&mut cleared);
assert_eq!(cleared, FacetOverlay::default());
}
#[test]
fn the_drawn_tiers_are_remembered_until_the_next_frame_replaces_them() {
note_drawn_tiers(None);
assert_eq!(drawn_tiers(), None);
note_drawn_tiers(Some(Arc::new(vec![true, false, true])));
assert_eq!(drawn_tiers().as_deref(), Some(&vec![true, false, true]));
note_drawn_tiers(None);
assert_eq!(drawn_tiers(), None, "a finished frame forgets the cut");
}
#[test]
fn the_selected_facet_id_is_one_value_for_both_views() {
set_selected_facet_id(None);
assert_eq!(selected_facet_id(), None);
set_selected_facet_id(Some(17));
assert_eq!(selected_facet_id(), Some(17));
set_selected_facet_id(None);
assert_eq!(selected_facet_id(), None);
}
}