basalt 0.21.0

A window/ui framework built upon vulkan.
Documentation
use std::cmp::Reverse;
use std::collections::HashMap;

use crate::input::state::{HookState, WindowState};
use crate::input::{Hook, InputHookCtrl, InputHookID, NO_HOOK_WEIGHT};
use crate::window::WindowID;

pub(in crate::input) fn character(
    hooks: &mut HashMap<InputHookID, Hook>,
    win_state: &mut HashMap<WindowID, WindowState>,
    win: WindowID,
    c: char,
) {
    let window_state = win_state
        .entry(win)
        .or_insert_with(|| WindowState::new(win));

    let is_valid_target: Box<dyn Fn(&Hook) -> bool> = match window_state.focused_bin_id() {
        Some(bin) => {
            Box::new(move |hook: &Hook| -> bool {
                hook.is_for_window_id(win) || hook.is_for_bin_id(bin)
            })
        },
        None => Box::new(|hook: &Hook| -> bool { hook.is_for_window_id(win) }),
    };

    let mut call_in_order: Vec<_> = hooks
        .iter_mut()
        .filter_map(|(hook_id, hook)| {
            if is_valid_target(hook) {
                if let HookState::Character {
                    weight, ..
                } = &mut hook.state
                {
                    Some((*weight, *hook_id, hook))
                } else {
                    None
                }
            } else {
                None
            }
        })
        .collect();

    call_in_order.sort_by_key(|(weight, ..)| Reverse(*weight));
    let mut remove_hooks = Vec::new();

    for (weight, hook_id, hook) in call_in_order {
        if let HookState::Character {
            method, ..
        } = &mut hook.state
        {
            let hook_target = match hook.target_wk.upgrade() {
                Some(some) => some,
                None => {
                    remove_hooks.push(hook_id);
                    continue;
                },
            };

            match method(hook_target, window_state, c.into()) {
                InputHookCtrl::Retain => (),
                InputHookCtrl::RetainNoPass => {
                    if weight != NO_HOOK_WEIGHT {
                        break;
                    }
                },
                InputHookCtrl::Remove => {
                    remove_hooks.push(hook_id);
                },
                InputHookCtrl::RemoveNoPass => {
                    remove_hooks.push(hook_id);

                    if weight != NO_HOOK_WEIGHT {
                        break;
                    }
                },
            }
        } else {
            unreachable!()
        }
    }

    for hook_id in remove_hooks {
        hooks.remove(&hook_id);
    }
}