indicatrix-cut 0.2.0

Desktop faceting-design editor: library browsing, spectral 3D rendering, material retargeting, and a solid inspection view.
Documentation
//! Rust-side wiring for the Diagram view mode's own hover/click callbacks.
//!
//! `app.slint`'s `editor_solid_diagram_facet_hover`/`_click`, forwarded from
//! `solid_viewport.slint`'s `diagram_facet_hover`/`diagram_facet_click` -- see that
//! component's own doc comment for why Diagram mode needs separate callbacks from
//! the Solid rasterizer's `facet_hover`/`facet_click`.
//!
//! Unlike `gui::editor::callbacks::tier_actions`'s `setup_solid_facet_hover_callback`/
//! `setup_solid_facet_click_callback` (which build a fresh `facet_map::FacetMap`
//! from `EditorState`'s `Design` on every hover), these callbacks do NOT need
//! `Design`/`FacetMap` access at all: `preview_state::render_request` already
//! builds the facet-id-indexed hover-text and tier tables on the WORKER thread
//! (see [`super::preview_state::PreviewFrame::diagram_hover_text`]'s doc comment
//! for why) and hands them here as plain `Arc<Mutex<..>>` state, exactly like
//! `solid_pick`/`solid_last_solved` already work for the Solid view. That is what
//! lets this module live under `solid_preview/` and be wired from `gui::mod`
//! directly, with no dependency on `gui::editor`'s `EditorState`.

use super::preview_state::PickBuffer;
use crate::{EditorModel, MainWindow, SolidPreviewModel};
use slint::ComponentHandle;
use std::sync::{Arc, Mutex, PoisonError};

/// The diagram's own per-pixel facet-picking buffer, one frame behind.
///
/// Same contract as `gui::mod`'s `solid_pick` for the ordinary Solid view --
/// written by `SlintSolidSink::apply` from `PreviewFrame::diagram_pick`.
pub type DiagramPick = Arc<Mutex<Option<PickBuffer>>>;
/// Facet id -> hover tooltip text, from `PreviewFrame::diagram_hover_text`.
pub type DiagramHoverText = Arc<Mutex<Option<Vec<String>>>>;
/// Facet id -> owning tier index, from `PreviewFrame::diagram_facet_tier`.
pub type DiagramFacetTier = Arc<Mutex<Option<Vec<Option<usize>>>>>;

/// Wires `editor_solid_diagram_facet_hover`/`_click`. Call once from
/// `gui::build_main_window`, alongside the ordinary Solid hover/click wiring.
pub fn setup_diagram_hover_and_click_callbacks(
    ui: &MainWindow,
    pick: &DiagramPick,
    hover_text: &DiagramHoverText,
    facet_tier: &DiagramFacetTier,
) {
    {
        let pick = Arc::clone(pick);
        let hover_text = Arc::clone(hover_text);
        let ui_weak = ui.as_weak();
        ui.global::<SolidPreviewModel>()
            .on_diagram_facet_hover(move |x: f32, y: f32| {
                let Some(ui) = ui_weak.upgrade() else {
                    return;
                };
                let Some(facet_id) = pick
                    .lock()
                    .unwrap_or_else(PoisonError::into_inner)
                    .as_ref()
                    .and_then(|pick| pick.facet_at(x.max(0.0) as u32, y.max(0.0) as u32))
                else {
                    ui.global::<SolidPreviewModel>()
                        .set_diagram_hover_text("".into());
                    return;
                };
                let text = hover_text
                    .lock()
                    .unwrap_or_else(PoisonError::into_inner)
                    .as_ref()
                    .and_then(|table| table.get(facet_id as usize))
                    .cloned()
                    .unwrap_or_default();
                ui.global::<SolidPreviewModel>()
                    .set_diagram_hover_text(text.into());
            });
    }

    {
        let pick = Arc::clone(pick);
        let facet_tier = Arc::clone(facet_tier);
        let ui_weak = ui.as_weak();
        ui.global::<SolidPreviewModel>()
            .on_diagram_facet_click(move |x: f32, y: f32| {
                let Some(ui) = ui_weak.upgrade() else {
                    return;
                };
                let Some(facet_id) = pick
                    .lock()
                    .unwrap_or_else(PoisonError::into_inner)
                    .as_ref()
                    .and_then(|pick| pick.facet_at(x.max(0.0) as u32, y.max(0.0) as u32))
                else {
                    return;
                };
                let tier_index = facet_tier
                    .lock()
                    .unwrap_or_else(PoisonError::into_inner)
                    .as_ref()
                    .and_then(|table| table.get(facet_id as usize).copied())
                    .flatten();
                if let Some(tier_index) = tier_index {
                    ui.global::<EditorModel>()
                        .set_selected_tier_index(tier_index as i32);
                }
            });
    }
}