use crate::{BaseDocument, node::GeneratedTextInputEvent, util::ACTION_MOD};
use blitz_traits::{
SmolStr,
events::{BlitzInputEvent, BlitzKeyEvent, DomEvent, DomEventData},
};
use keyboard_types::Key;
use markup5ever::local_name;
pub(super) enum KeyboardOrTextInputEvent {
KeyPress(BlitzKeyEvent),
AppleStandardKeyBinding(SmolStr),
}
pub(crate) fn handle_key_or_input_event<F: FnMut(DomEvent)>(
doc: &mut BaseDocument,
target: usize,
event: KeyboardOrTextInputEvent,
dispatch_event: F,
) {
if let KeyboardOrTextInputEvent::KeyPress(event) = &event {
if event.key == Key::Tab {
doc.focus_next_node();
return;
}
if event.state.is_pressed() {
let action_mod = event.modifiers.contains(ACTION_MOD);
if action_mod {
if let Key::Character(c) = &event.key {
if c.to_lowercase() == "c" {
let has_focused_text_input = doc.focus_node_id.is_some_and(|id| {
doc.get_node(id)
.and_then(|n| n.element_data())
.is_some_and(|e| e.text_input_data().is_some())
});
if !has_focused_text_input {
if let Some(text) = doc.get_selected_text() {
let _ = doc.shell_provider.set_clipboard_text(text);
return;
}
}
}
}
}
}
}
if let Some(node_id) = doc.focus_node_id {
if target != node_id {
return;
}
let node = &mut doc.nodes[node_id];
let Some(element_data) = node.element_data_mut() else {
return;
};
if let Some(input_data) = element_data.text_input_data_mut() {
let generated_event = match event {
KeyboardOrTextInputEvent::KeyPress(blitz_key_event) => input_data
.apply_keypress_event(
&mut doc.font_ctx.lock().unwrap(),
&mut doc.layout_ctx,
&*doc.shell_provider,
blitz_key_event,
),
KeyboardOrTextInputEvent::AppleStandardKeyBinding(command) => input_data
.apply_apple_standard_keybinding(
&mut doc.font_ctx.lock().unwrap(),
&mut doc.layout_ctx,
&*doc.shell_provider,
&command,
),
};
if let Some(generated_event) = generated_event {
doc.apply_generated_text_input_event(node_id, generated_event, dispatch_event);
}
}
}
}
impl BaseDocument {
pub(crate) fn apply_generated_text_input_event<F: FnMut(DomEvent)>(
&mut self,
node_id: usize,
event: GeneratedTextInputEvent,
mut dispatch_event: F,
) {
let node = &mut self.nodes[node_id];
let element_data = node
.element_data_mut()
.expect("apply_generated_text_input_event called on a node that is not an element");
let input_data = element_data
.text_input_data_mut()
.expect("apply_generated_text_input_event called on a node that is not a text input");
match event {
GeneratedTextInputEvent::Input => {
let value = input_data.editor.raw_text().to_string();
dispatch_event(DomEvent::new(
node_id,
DomEventData::Input(BlitzInputEvent { value }),
));
self.shell_provider.request_redraw();
}
GeneratedTextInputEvent::Select | GeneratedTextInputEvent::PreEditChange => {
self.shell_provider.request_redraw();
}
GeneratedTextInputEvent::Submit => {
implicit_form_submission(self, node_id);
}
}
}
}
fn implicit_form_submission(doc: &BaseDocument, text_target: usize) {
let Some(form_owner_id) = doc.controls_to_form.get(&text_target) else {
return;
};
if doc
.controls_to_form
.iter()
.filter(|(_control_id, form_id)| *form_id == form_owner_id)
.filter_map(|(control_id, _)| doc.nodes[*control_id].element_data())
.filter(|element_data| {
element_data.attr(local_name!("type")).is_some_and(|t| {
matches!(
t,
"text"
| "search"
| "email"
| "url"
| "tel"
| "password"
| "date"
| "month"
| "week"
| "time"
| "datetime-local"
| "number"
)
})
})
.count()
> 1
{
return;
}
doc.submit_form(*form_owner_id, *form_owner_id);
}