cranpose-app-shell 0.1.147

Application orchestration shell for Cranpose
Documentation
use cranpose_core::run_in_mutable_snapshot;
use cranpose_render_common::Renderer;

use crate::AppShell;

impl<R: Renderer> AppShell<R>
where
    R::Error: std::fmt::Debug,
{
    pub(crate) fn update_modal_focus(&mut self) {
        if !self.app.semantics_enabled
            && self
                .surfaces
                .iter()
                .all(|surface| surface.modal_focus.is_empty())
            && !cranpose_ui::dismissable_popup_open()
        {
            return;
        }
        for surface in &mut self.surfaces {
            let modal = surface
                .semantics_tree_for_input(&mut self.app)
                .and_then(|tree| tree.root().is_modal.then_some(tree.root().node_id));
            if modal == surface.modal_focus.last().map(|entry| entry.0) {
                continue;
            }
            let current = cranpose_ui::active_focus_target();
            let order = surface
                .layout_tree_in_context(&mut self.app)
                .map(cranpose_ui::collect_focus_order)
                .unwrap_or_default();
            let previous = previous_modal_focus(&mut surface.modal_focus, modal, current);
            let target = previous
                .filter(|previous| order.iter().any(|entry| entry.node_id == *previous))
                .or_else(|| {
                    order
                        .iter()
                        .find(|entry| Some(entry.node_id) != modal)
                        .map(|entry| entry.node_id)
                })
                .or_else(|| order.first().map(|entry| entry.node_id));
            if let Some(target) = target {
                let _ =
                    run_in_mutable_snapshot(|| cranpose_ui::request_focus_from_platform(target));
            } else {
                let _ = run_in_mutable_snapshot(|| cranpose_ui::FocusManager.clear_focus());
            }
        }
    }
}

fn previous_modal_focus(
    history: &mut Vec<(cranpose_core::NodeId, Option<cranpose_core::NodeId>)>,
    modal: Option<cranpose_core::NodeId>,
    current: Option<cranpose_core::NodeId>,
) -> Option<cranpose_core::NodeId> {
    let Some(modal) = modal else {
        let previous = history.first().and_then(|entry| entry.1);
        history.clear();
        return previous;
    };
    if let Some(index) = history.iter().position(|entry| entry.0 == modal) {
        let previous = history.get(index + 1).and_then(|entry| entry.1);
        history.truncate(index + 1);
        previous
    } else {
        history.push((modal, current));
        current
    }
}