Skip to content
Snippets Groups Projects
input.rs 22.4 KiB
Newer Older
sam's avatar
sam committed
#![allow(clippy::too_many_arguments, clippy::type_complexity)]

#[cfg(target_arch = "wasm32")]
use bevy::tasks::AsyncComputeTaskPool;

sam's avatar
sam committed
use bevy::{
sam's avatar
sam committed
    input::mouse::{MouseMotion, MouseScrollUnit, MouseWheel},
sam's avatar
sam committed
    prelude::*,
    window::PrimaryWindow,
};
sam edelsten's avatar
sam edelsten committed
use cosmic_text::{Action, Cursor, Edit, Motion, Selection};
sam edelsten's avatar
sam edelsten committed
#[cfg(target_arch = "wasm32")]
use crate::DefaultAttrs;
#[cfg(target_arch = "wasm32")]
use js_sys::Promise;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::JsFuture;

sam's avatar
sam committed
use crate::{
    buffer::{get_x_offset_center, get_y_offset_center, BufferExtras},
sam edelsten's avatar
sam edelsten committed
    get_node_cursor_pos, CosmicBuffer, CosmicEditor, CosmicFontSystem, CosmicMaxChars,
    CosmicMaxLines, CosmicSource, CosmicTextChanged, CosmicTextPosition, FocusedWidget, ReadOnly,
    XOffset,
sam's avatar
sam committed
#[derive(Resource)]
pub struct ClickTimer(pub Timer);

// TODO: hide this behind #cfg wasm, depends on wasm having own copy/paste fn
#[allow(dead_code)]
pub struct WasmPaste {
    text: String,
    entity: Entity,
}

#[derive(Resource)]
pub struct WasmPasteAsyncChannel {
    pub tx: crossbeam_channel::Sender<WasmPaste>,
    pub rx: crossbeam_channel::Receiver<WasmPaste>,
}

sam's avatar
sam committed
pub(crate) fn input_mouse(
sam's avatar
sam committed
    windows: Query<&Window, With<PrimaryWindow>>,
sam edelsten's avatar
sam edelsten committed
    active_editor: Res<FocusedWidget>,
sam edelsten's avatar
sam edelsten committed
    keys: Res<ButtonInput<KeyCode>>,
    buttons: Res<ButtonInput<MouseButton>>,
    mut editor_q: Query<(
sam's avatar
sam committed
        &mut CosmicEditor,
        &GlobalTransform,
        &CosmicTextPosition,
        Entity,
        &XOffset,
        &mut Sprite,
sam's avatar
sam committed
    )>,
    node_q: Query<(&Node, &GlobalTransform, &CosmicSource)>,
sam's avatar
sam committed
    mut font_system: ResMut<CosmicFontSystem>,
    mut scroll_evr: EventReader<MouseWheel>,
    camera_q: Query<(&Camera, &GlobalTransform)>,
    mut click_timer: ResMut<ClickTimer>,
    mut click_count: Local<usize>,
    time: Res<Time>,
    evr_mouse_motion: EventReader<MouseMotion>,
sam's avatar
sam committed
) {
sam's avatar
sam committed
    click_timer.0.tick(time.delta());

    let Some(active_editor_entity) = active_editor.0 else {
sam's avatar
sam committed
        return;
sam's avatar
sam committed
    if click_timer.0.finished() || !evr_mouse_motion.is_empty() {
        *click_count = 0;
    }

    if buttons.just_pressed(MouseButton::Left) {
        click_timer.0.reset();
        *click_count += 1;
    }

    if *click_count > 3 {
        *click_count = 0;
    }

    let Ok(primary_window) = windows.get_single() else {
sam edelsten's avatar
sam edelsten committed
    let scale_factor = primary_window.scale_factor();
    let Some((camera, camera_transform)) = camera_q.iter().find(|(c, _)| c.is_active) else {
        return;
    };
    if let Ok((mut editor, sprite_transform, text_position, entity, x_offset, sprite)) =
        editor_q.get_mut(active_editor_entity)
sam edelsten's avatar
sam edelsten committed
        let buffer = editor.with_buffer(|b| b.clone());

        let mut is_ui_node = false;
        let mut transform = sprite_transform;
        let (mut width, mut height) =
            (sprite.custom_size.unwrap().x, sprite.custom_size.unwrap().y);

        // TODO: this is bad loop nesting, rethink system with relationships in mind
        for (node, node_transform, source) in node_q.iter() {
            if source.0 != entity {
                continue;
            is_ui_node = true;
            transform = node_transform;
            width = node.size().x;
            height = node.size().y;
        }
sam's avatar
sam committed

        let shift = keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]);

        // if shift key is pressed
sam edelsten's avatar
sam edelsten committed
        let already_has_selection = editor.selection() != Selection::None;
sam's avatar
sam committed
        if shift && !already_has_selection {
sam edelsten's avatar
sam edelsten committed
            let cursor = editor.cursor();
            editor.set_selection(Selection::Normal(cursor));
sam's avatar
sam committed
        }

        let (padding_x, padding_y) = match text_position {
            CosmicTextPosition::Center { padding: _ } => (
sam edelsten's avatar
sam edelsten committed
                get_x_offset_center(width * scale_factor, &buffer),
                get_y_offset_center(height * scale_factor, &buffer),
sam's avatar
sam committed
            ),
            CosmicTextPosition::TopLeft { padding } => (*padding, *padding),
            CosmicTextPosition::Left { padding } => (
                *padding,
sam edelsten's avatar
sam edelsten committed
                get_y_offset_center(height * scale_factor, &buffer),
sam's avatar
sam committed
            ),
        };
        let point = |node_cursor_pos: (f32, f32)| {
            (
                (node_cursor_pos.0 * scale_factor) as i32 - padding_x,
                (node_cursor_pos.1 * scale_factor) as i32 - padding_y,
            )
        };

        if buttons.just_pressed(MouseButton::Left) {
sam edelsten's avatar
sam edelsten committed
            editor.cursor_visible = true;
            editor.cursor_timer.reset();

sam's avatar
sam committed
            if let Some(node_cursor_pos) = get_node_cursor_pos(
                primary_window,
                transform,
sam's avatar
sam committed
                (width, height),
                is_ui_node,
                camera,
                camera_transform,
            ) {
                let (mut x, y) = point(node_cursor_pos);
                x += x_offset.left as i32;
sam's avatar
sam committed
                if shift {
sam edelsten's avatar
sam edelsten committed
                    editor.action(&mut font_system.0, Action::Drag { x, y });
sam's avatar
sam committed
                } else {
sam's avatar
sam committed
                    match *click_count {
                        1 => {
sam edelsten's avatar
sam edelsten committed
                            editor.action(&mut font_system.0, Action::Click { x, y });
sam's avatar
sam committed
                        }
                        2 => {
                            // select word
sam edelsten's avatar
sam edelsten committed
                            editor.action(&mut font_system.0, Action::Motion(Motion::LeftWord));
                            let cursor = editor.cursor();
                            editor.set_selection(Selection::Normal(cursor));
                            editor.action(&mut font_system.0, Action::Motion(Motion::RightWord));
sam's avatar
sam committed
                        }
                        3 => {
                            // select paragraph
sam edelsten's avatar
sam edelsten committed
                            editor
                                .action(&mut font_system.0, Action::Motion(Motion::ParagraphStart));
                            let cursor = editor.cursor();
                            editor.set_selection(Selection::Normal(cursor));
                            editor.action(&mut font_system.0, Action::Motion(Motion::ParagraphEnd));
sam's avatar
sam committed

        if buttons.pressed(MouseButton::Left) && *click_count == 0 {
sam's avatar
sam committed
            if let Some(node_cursor_pos) = get_node_cursor_pos(
                primary_window,
                transform,
sam's avatar
sam committed
                (width, height),
                is_ui_node,
                camera,
                camera_transform,
            ) {
                let (mut x, y) = point(node_cursor_pos);
                x += x_offset.left as i32;
sam's avatar
sam committed
                if active_editor.is_changed() && !shift {
sam edelsten's avatar
sam edelsten committed
                    editor.action(&mut font_system.0, Action::Click { x, y });
sam's avatar
sam committed
                } else {
sam edelsten's avatar
sam edelsten committed
                    editor.action(&mut font_system.0, Action::Drag { x, y });
        for ev in scroll_evr.read() {
sam's avatar
sam committed
            match ev.unit {
                MouseScrollUnit::Line => {
sam edelsten's avatar
sam edelsten committed
                    editor.action(
sam's avatar
sam committed
                        &mut font_system.0,
                        Action::Scroll {
                            lines: -ev.y as i32,
                        },
                    );
                }
                MouseScrollUnit::Pixel => {
sam edelsten's avatar
sam edelsten committed
                    let line_height = buffer.metrics().line_height;
                    editor.action(
sam's avatar
sam committed
                        &mut font_system.0,
                        Action::Scroll {
                            lines: -(ev.y / line_height) as i32,
                        },
                    );
                }
            }
        }
    }
}

pub fn kb_move_cursor(
sam edelsten's avatar
sam edelsten committed
    active_editor: Res<FocusedWidget>,
sam edelsten's avatar
sam edelsten committed
    keys: Res<ButtonInput<KeyCode>>,
    mut cosmic_edit_query: Query<(&mut CosmicEditor,)>,
sam's avatar
sam committed
    mut font_system: ResMut<CosmicFontSystem>,
    let Some(active_editor_entity) = active_editor.0 else {
        return;
    };
    if let Ok((mut editor,)) = cosmic_edit_query.get_mut(active_editor_entity) {
sam edelsten's avatar
sam edelsten committed
        if keys.get_just_pressed().len() != 0 {
            editor.cursor_visible = true;
            editor.cursor_timer.reset();
        }
        let command = keypress_command(&keys);
        #[cfg(target_arch = "wasm32")]
        let command = if web_sys::window()
            .unwrap()
            .navigator()
            .user_agent()
            .unwrap_or("NoUA".into())
            .contains("Macintosh")
        {
            keys.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight])
        } else {
            command
        };

sam's avatar
sam committed
        #[cfg(target_os = "macos")]
        let option = keys.any_pressed([KeyCode::AltLeft, KeyCode::AltRight]);

        let shift = keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]);

sam's avatar
sam committed
        // if shift key is pressed
sam edelsten's avatar
sam edelsten committed
        let already_has_selection = editor.selection() != Selection::None;
sam's avatar
sam committed
        if shift && !already_has_selection {
sam edelsten's avatar
sam edelsten committed
            let cursor = editor.cursor();
            editor.set_selection(Selection::Normal(cursor));
sam's avatar
sam committed
        }

        #[cfg(target_os = "macos")]
        let should_jump = command && option;
        #[cfg(not(target_os = "macos"))]
        let should_jump = command;

sam edelsten's avatar
sam edelsten committed
        if should_jump && keys.just_pressed(KeyCode::ArrowLeft) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::PreviousWord));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam edelsten's avatar
sam edelsten committed
        if should_jump && keys.just_pressed(KeyCode::ArrowRight) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::NextWord));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam's avatar
sam committed
            }
            return;
        }
        if should_jump && keys.just_pressed(KeyCode::Home) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::BufferStart));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam's avatar
sam committed
            }
            return;
        }
        if should_jump && keys.just_pressed(KeyCode::End) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::BufferEnd));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam edelsten's avatar
sam edelsten committed
        if keys.just_pressed(KeyCode::ArrowLeft) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::Left));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam edelsten's avatar
sam edelsten committed
        if keys.just_pressed(KeyCode::ArrowRight) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::Right));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam edelsten's avatar
sam edelsten committed
        if keys.just_pressed(KeyCode::ArrowUp) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::Up));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
sam edelsten's avatar
sam edelsten committed
        if keys.just_pressed(KeyCode::ArrowDown) {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Motion(Motion::Down));
sam's avatar
sam committed
            if !shift {
sam edelsten's avatar
sam edelsten committed
                editor.set_selection(Selection::None);
        if keys.just_pressed(KeyCode::Escape) {
            editor.action(&mut font_system.0, Action::Escape);
        }
        if command && keys.just_pressed(KeyCode::KeyA) {
            editor.action(&mut font_system.0, Action::Motion(Motion::BufferEnd));
            let current_cursor = editor.cursor();
            editor.set_selection(Selection::Normal(Cursor {
                line: 0,
                index: 0,
                affinity: current_cursor.affinity,
            }));
            return;
        }
        if keys.just_pressed(KeyCode::Home) {
            editor.action(&mut font_system.0, Action::Motion(Motion::Home));
            if !shift {
                editor.set_selection(Selection::None);
            }
            return;
        }
        if keys.just_pressed(KeyCode::End) {
            editor.action(&mut font_system.0, Action::Motion(Motion::End));
            if !shift {
                editor.set_selection(Selection::None);
            }
            return;
        }
        if keys.just_pressed(KeyCode::PageUp) {
            editor.action(&mut font_system.0, Action::Motion(Motion::PageUp));
            if !shift {
                editor.set_selection(Selection::None);
            }
            return;
        }
        if keys.just_pressed(KeyCode::PageDown) {
            editor.action(&mut font_system.0, Action::Motion(Motion::PageDown));
            if !shift {
                editor.set_selection(Selection::None);
            }
        }
    }
}

pub(crate) fn kb_input_text(
    active_editor: Res<FocusedWidget>,
    keys: Res<ButtonInput<KeyCode>>,
    mut char_evr: EventReader<ReceivedCharacter>,
    mut cosmic_edit_query: Query<(
        &mut CosmicEditor,
        &mut CosmicBuffer,
        &CosmicMaxLines,
        &CosmicMaxChars,
        Entity,
        Option<&ReadOnly>,
    )>,
    mut evw_changed: EventWriter<CosmicTextChanged>,
    mut font_system: ResMut<CosmicFontSystem>,
    mut is_deleting: Local<bool>,
) {
    let Some(active_editor_entity) = active_editor.0 else {
        return;
    };

    if let Ok((mut editor, buffer, max_lines, max_chars, entity, readonly_opt)) =
        cosmic_edit_query.get_mut(active_editor_entity)
    {
        let command = keypress_command(&keys);
        if keys.get_just_pressed().len() != 0 {
            editor.cursor_visible = true;
            editor.cursor_timer.reset();
        }
        let readonly = readonly_opt.is_some();
sam edelsten's avatar
sam edelsten committed
        if keys.just_pressed(KeyCode::Backspace) & !readonly {
            // fix for issue #8
sam edelsten's avatar
sam edelsten committed
            let select = editor.selection();
            match select {
                Selection::Line(cursor) => {
                    if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index
                    {
                        editor.set_selection(Selection::None);
                    }
                }
                Selection::Normal(cursor) => {
                    if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index
                    {
                        editor.set_selection(Selection::None);
                    }
                }
                Selection::Word(cursor) => {
                    if editor.cursor().line == cursor.line && editor.cursor().index == cursor.index
                    {
                        editor.set_selection(Selection::None);
                    }
                }
                Selection::None => {}
sam's avatar
sam committed
            *is_deleting = true;
sam edelsten's avatar
sam edelsten committed
            #[cfg(target_arch = "wasm32")]
            editor.action(&mut font_system.0, Action::Backspace);
sam edelsten's avatar
sam edelsten committed
        if keys.just_released(KeyCode::Backspace) {
sam's avatar
sam committed
            *is_deleting = false;
        }
        if keys.just_pressed(KeyCode::Delete) && !readonly {
sam edelsten's avatar
sam edelsten committed
            editor.action(&mut font_system.0, Action::Delete);
            editor.with_buffer_mut(|b| b.set_redraw(true));
sam's avatar
sam committed
            return;
        }

        let mut is_edit = false;
        let mut is_return = false;
        if keys.just_pressed(KeyCode::Enter) {
            is_return = true;
            if (max_lines.0 == 0 || buffer.lines.len() < max_lines.0)
                && (max_chars.0 == 0 || buffer.get_text().len() < max_chars.0)
            {
                // to have new line on wasm rather than E
                is_edit = true;
                editor.action(&mut font_system.0, Action::Insert('\n'));

        if !is_return {
            for char_ev in char_evr.read() {
                is_edit = true;
                if *is_deleting {
                    editor.action(&mut font_system.0, Action::Backspace);
                } else if !command && (max_chars.0 == 0 || buffer.get_text().len() < max_chars.0) {
                    let b = char_ev.char.as_bytes();
                    for c in b {
                        let c: char = (*c).into();
                        editor.action(&mut font_system.0, Action::Insert(c));
                    }
                }
        evw_changed.send(CosmicTextChanged((entity, buffer.get_text())));
    }
}

pub fn kb_clipboard(
    active_editor: Res<FocusedWidget>,
    keys: Res<ButtonInput<KeyCode>>,
    mut evw_changed: EventWriter<CosmicTextChanged>,
    mut font_system: ResMut<CosmicFontSystem>,
    mut cosmic_edit_query: Query<(
        &mut CosmicEditor,
        &mut CosmicBuffer,
        &CosmicMaxLines,
        &CosmicMaxChars,
        Entity,
        Option<&ReadOnly>,
    )>,
sam edelsten's avatar
sam edelsten committed
    _channel: Option<Res<WasmPasteAsyncChannel>>,
) {
    let Some(active_editor_entity) = active_editor.0 else {
        return;
    };

    if let Ok((mut editor, buffer, max_lines, max_chars, entity, readonly_opt)) =
        cosmic_edit_query.get_mut(active_editor_entity)
    {
        let command = keypress_command(&keys);

        let readonly = readonly_opt.is_some();

sam's avatar
sam committed
        let mut is_clipboard = false;
        #[cfg(not(target_arch = "wasm32"))]
        {
            if let Ok(mut clipboard) = arboard::Clipboard::new() {
sam edelsten's avatar
sam edelsten committed
                if command && keys.just_pressed(KeyCode::KeyC) {
sam edelsten's avatar
sam edelsten committed
                    if let Some(text) = editor.copy_selection() {
sam's avatar
sam committed
                        clipboard.set_text(text).unwrap();
                        return;
                    }
                }
sam edelsten's avatar
sam edelsten committed
                if command && keys.just_pressed(KeyCode::KeyX) && !readonly {
sam edelsten's avatar
sam edelsten committed
                    if let Some(text) = editor.copy_selection() {
sam's avatar
sam committed
                        clipboard.set_text(text).unwrap();
sam edelsten's avatar
sam edelsten committed
                        editor.delete_selection();
sam's avatar
sam committed
                    }
                    is_clipboard = true;
                }
sam edelsten's avatar
sam edelsten committed
                if command && keys.just_pressed(KeyCode::KeyV) && !readonly {
sam's avatar
sam committed
                    if let Ok(text) = clipboard.get_text() {
                        for c in text.chars() {
sam edelsten's avatar
sam edelsten committed
                            if max_chars.0 == 0 || buffer.get_text().len() < max_chars.0 {
sam's avatar
sam committed
                                if c == 0xA as char {
sam edelsten's avatar
sam edelsten committed
                                    if max_lines.0 == 0 || buffer.lines.len() < max_lines.0 {
                                        editor.action(&mut font_system.0, Action::Insert(c));
sam's avatar
sam committed
                                    }
                                } else {
sam edelsten's avatar
sam edelsten committed
                                    editor.action(&mut font_system.0, Action::Insert(c));
        #[cfg(target_arch = "wasm32")]
        {
sam edelsten's avatar
sam edelsten committed
            if command && keys.just_pressed(KeyCode::KeyC) {
sam edelsten's avatar
sam edelsten committed
                if let Some(text) = editor.copy_selection() {
                    write_clipboard_wasm(text.as_str());
                    return;
                }
            }

sam edelsten's avatar
sam edelsten committed
            if command && keys.just_pressed(KeyCode::KeyX) && !readonly {
sam edelsten's avatar
sam edelsten committed
                if let Some(text) = editor.copy_selection() {
                    write_clipboard_wasm(text.as_str());
sam edelsten's avatar
sam edelsten committed
                    editor.delete_selection();
                }
                is_clipboard = true;
            }
sam edelsten's avatar
sam edelsten committed
            if command && keys.just_pressed(KeyCode::KeyV) && !readonly {
                let tx = _channel.unwrap().tx.clone();
                let _task = AsyncComputeTaskPool::get().spawn(async move {
                    let promise = read_clipboard_wasm();

                    let result = JsFuture::from(promise).await;

                    if let Ok(js_text) = result {
                        if let Some(text) = js_text.as_string() {
                            let _ = tx.try_send(WasmPaste { text, entity });
                        }
                    }
                });

                return;
            }
        }

sam edelsten's avatar
sam edelsten committed
        evw_changed.send(CosmicTextChanged((entity, buffer.get_text())));
sam edelsten's avatar
sam edelsten committed
fn keypress_command(keys: &ButtonInput<KeyCode>) -> bool {
    #[cfg(target_os = "macos")]
    let command = keys.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight]);

    #[cfg(not(target_os = "macos"))]
    let command = keys.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]);

    #[cfg(target_arch = "wasm32")]
    let command = if web_sys::window()
        .unwrap()
        .navigator()
        .user_agent()
        .unwrap_or("NoUA".into())
        .contains("Macintosh")
    {
        keys.any_pressed([KeyCode::SuperLeft, KeyCode::SuperRight])
    } else {
        command
    };

    command
}

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn write_clipboard_wasm(text: &str) {
    let clipboard = web_sys::window()
        .unwrap()
        .navigator()
        .clipboard()
        .expect("Clipboard not found!");
    let _result = clipboard.write_text(text);
}

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub fn read_clipboard_wasm() -> Promise {
    let clipboard = web_sys::window()
        .unwrap()
        .navigator()
        .clipboard()
        .expect("Clipboard not found!");
    clipboard.read_text()
}

#[cfg(target_arch = "wasm32")]
pub fn poll_wasm_paste(
    channel: Res<WasmPasteAsyncChannel>,
    mut editor_q: Query<
        (
            &mut CosmicEditor,
sam edelsten's avatar
sam edelsten committed
            &mut CosmicBuffer,
            &crate::DefaultAttrs,
            &CosmicMaxChars,
            &CosmicMaxChars,
        ),
        Without<ReadOnly>,
    >,
    mut evw_changed: EventWriter<CosmicTextChanged>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    let inlet = channel.rx.try_recv();
    match inlet {
        Ok(inlet) => {
            let entity = inlet.entity;
sam edelsten's avatar
sam edelsten committed
            if let Ok((mut editor, mut buffer, attrs, max_chars, max_lines)) =
                editor_q.get_mut(entity)
            {
                let text = inlet.text;
                let attrs = &attrs.0;
                for c in text.chars() {
sam edelsten's avatar
sam edelsten committed
                    if max_chars.0 == 0 || buffer.get_text().len() < max_chars.0 {
                        if c == 0xA as char {
sam edelsten's avatar
sam edelsten committed
                            if max_lines.0 == 0 || buffer.lines.len() < max_lines.0 {
                                editor.action(&mut font_system.0, Action::Insert(c));
                            }
                        } else {
sam edelsten's avatar
sam edelsten committed
                            editor.action(&mut font_system.0, Action::Insert(c));
sam edelsten's avatar
sam edelsten committed
                evw_changed.send(CosmicTextChanged((entity, buffer.get_text())));