beuvy-runtime 0.1.0

A low-level Bevy UI kit with reusable controls and utility-class styling.
Documentation
use super::{can_insert_char, sync_display_change};
use crate::input::{
    DisabledInput, InputCursorPosition, InputField, InputValueChangedMessage, active_input_entity,
    push_value_changed, sync_window_ime,
};
use crate::text::FontResource;
use bevy::input_focus::InputFocus;
use bevy::prelude::*;
use bevy::window::{Ime, PrimaryWindow};

pub(crate) fn handle_ime_input(
    mut commands: Commands,
    mut ime_events: MessageReader<Ime>,
    fields_marker: Query<(), With<InputField>>,
    mut fields: Query<(Entity, &mut InputField, Has<DisabledInput>)>,
    input_focus: Res<InputFocus>,
    time: Res<Time>,
    font_resource: Res<FontResource>,
    mut value_changed: MessageWriter<InputValueChangedMessage>,
) {
    let Some(active) = active_input_entity(&input_focus, &fields_marker) else {
        return;
    };
    let Ok((entity, mut field, disabled)) = fields.get_mut(active) else {
        return;
    };
    if disabled {
        return;
    }

    let mut changed = false;
    let mut display_changed = false;

    for ime in ime_events.read() {
        match ime {
            Ime::Preedit { value, cursor, .. } if cursor.is_some() => {
                field.edit_state.set_preedit(value.clone(), *cursor);
                display_changed = true;
            }
            Ime::Preedit { cursor, .. } if cursor.is_none() => {
                field.edit_state.clear_preedit();
                display_changed = true;
            }
            Ime::Commit { value, .. } => {
                let value: String = value
                    .chars()
                    .filter(|chr| can_insert_char(&field, *chr))
                    .collect();
                changed |= field.edit_state.commit_preedit_text(&value);
                display_changed |= !value.is_empty();
            }
            _ => {}
        }
    }

    if display_changed {
        sync_display_change(&mut commands, &font_resource, &mut field, disabled, &time);
    }
    if changed {
        push_value_changed(&mut value_changed, entity, &field);
    }
}

pub(crate) fn sync_input_ime_state(
    input_focus: Res<InputFocus>,
    fields: Query<(&InputCursorPosition, Has<DisabledInput>), With<InputField>>,
    mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
) {
    let Some(mut primary_window) = primary_window.iter_mut().next() else {
        return;
    };

    let Some(entity) = input_focus.get() else {
        sync_window_ime(&mut primary_window, false, Vec2::ZERO);
        return;
    };

    let Ok((cursor_position, disabled)) = fields.get(entity) else {
        sync_window_ime(&mut primary_window, false, Vec2::ZERO);
        return;
    };

    sync_window_ime(
        &mut primary_window,
        !disabled,
        Vec2::new(cursor_position.x.max(0.0), cursor_position.y.max(0.0)),
    );
}