use winit::event::{ElementState, MouseScrollDelta, WindowEvent, MouseButton as WMouseButton};
use winit::keyboard::{Key, KeyCode, NamedKey, PhysicalKey};
use uzor::input::core::coordinator::InputCoordinator;
use uzor::input::keyboard::keyboard::KeyPress;
use uzor::types::WidgetId;
#[derive(Default, Debug, Clone, Copy)]
pub struct ModifierState {
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
pub meta: bool,
}
#[derive(Default, Debug)]
pub struct BridgeOutput {
pub cursor_moved: Option<(f64, f64)>,
pub left_down: Option<((f64, f64), Option<WidgetId>)>,
pub left_up: Option<((f64, f64), Option<WidgetId>)>,
pub right_up: Option<(f64, f64)>,
pub wheel: Option<((f64, f64), (f64, f64))>,
pub text_changed: bool,
pub focus_cleared: bool,
}
pub struct WinitInputBridge {
pub modifiers: ModifierState,
pub last_mouse_pos: (f64, f64),
clipboard: Option<arboard::Clipboard>,
text_dragging: bool,
}
impl WinitInputBridge {
pub fn new() -> Self {
Self {
modifiers: ModifierState::default(),
last_mouse_pos: (0.0, 0.0),
clipboard: None,
text_dragging: false,
}
}
fn clipboard(&mut self) -> Option<&mut arboard::Clipboard> {
if self.clipboard.is_none() {
self.clipboard = arboard::Clipboard::new().ok();
}
self.clipboard.as_mut()
}
pub fn handle_event(
&mut self,
coord: &mut InputCoordinator,
focused_text_field: Option<&WidgetId>,
event: &WindowEvent,
) -> BridgeOutput {
let mut out = BridgeOutput::default();
match event {
WindowEvent::CursorMoved { position, .. } => {
let pos = (position.x, position.y);
self.last_mouse_pos = pos;
out.cursor_moved = Some(pos);
if self.text_dragging {
coord.text_fields_mut().on_drag_move(pos.0);
out.text_changed = true;
}
}
WindowEvent::MouseInput {
state: ElementState::Pressed,
button: WMouseButton::Left,
..
} => {
let (x, y) = self.last_mouse_pos;
let drag_target = coord.process_drag_press(x, y);
out.left_down = Some(((x, y), drag_target));
coord.text_fields_mut().on_drag_start(x, y);
self.text_dragging = coord.text_fields().focused().is_some();
if self.text_dragging {
out.text_changed = true;
}
}
WindowEvent::MouseInput {
state: ElementState::Released,
button: WMouseButton::Left,
..
} => {
let (x, y) = self.last_mouse_pos;
if self.text_dragging {
coord.text_fields_mut().on_drag_end();
self.text_dragging = false;
out.text_changed = true;
}
let clicked = coord.process_click(x, y);
out.left_up = Some(((x, y), clicked));
}
WindowEvent::MouseInput {
state: ElementState::Released,
button: WMouseButton::Right,
..
} => {
let (x, y) = self.last_mouse_pos;
out.right_up = Some((x, y));
}
WindowEvent::MouseWheel { delta, .. } => {
let (dx, dy) = match delta {
MouseScrollDelta::LineDelta(x, y) => (*x as f64, *y as f64),
MouseScrollDelta::PixelDelta(p) => (p.x / 20.0, p.y / 20.0),
};
out.wheel = Some((self.last_mouse_pos, (dx, dy)));
}
WindowEvent::ModifiersChanged(m) => {
let st = m.state();
self.modifiers.shift = st.shift_key();
self.modifiers.ctrl = st.control_key();
self.modifiers.alt = st.alt_key();
self.modifiers.meta = st.super_key();
}
WindowEvent::KeyboardInput { event: ke, .. }
if ke.state == ElementState::Pressed =>
{
if let Some(id) = focused_text_field {
let consumed = self.handle_text_key(coord, id, &ke.logical_key, &ke.physical_key);
if consumed {
out.text_changed = true;
} else if let Key::Named(NamedKey::Escape) = ke.logical_key {
coord.clear_focus();
out.focus_cleared = true;
} else if let Key::Named(NamedKey::Enter) = ke.logical_key {
coord.clear_focus();
out.focus_cleared = true;
}
}
}
_ => {}
}
out
}
fn handle_text_key(
&mut self,
coord: &mut InputCoordinator,
_id: &WidgetId,
key: &Key,
physical: &PhysicalKey,
) -> bool {
let m = self.modifiers;
if m.ctrl {
if let PhysicalKey::Code(code) = physical {
match code {
KeyCode::KeyA => {
coord.text_fields_mut().on_key(KeyPress::SelectAll);
return true;
}
KeyCode::KeyC => {
if let Some(sel) = coord.text_fields().copy_selection() {
if let Some(cb) = self.clipboard() {
let _ = cb.set_text(sel);
}
}
return true;
}
KeyCode::KeyV => {
let text = self
.clipboard()
.and_then(|cb| cb.get_text().ok())
.unwrap_or_default();
if !text.is_empty() {
coord.text_fields_mut().on_key(KeyPress::Paste(text));
}
return true;
}
KeyCode::KeyX => {
if let Some(sel) = coord.text_fields().copy_selection() {
if let Some(cb) = self.clipboard() {
let _ = cb.set_text(sel);
}
coord.text_fields_mut().on_key(KeyPress::Delete);
}
return true;
}
_ => {}
}
}
}
match key {
Key::Named(NamedKey::Backspace) => {
coord.text_fields_mut().on_char('\x08');
true
}
Key::Named(NamedKey::Delete) => {
coord.text_fields_mut().on_key(KeyPress::Delete);
true
}
Key::Named(NamedKey::Enter) => {
false
}
Key::Named(NamedKey::Escape) => {
false
}
Key::Named(NamedKey::ArrowLeft) => {
let kp = if m.shift { KeyPress::ShiftLeft } else { KeyPress::ArrowLeft };
coord.text_fields_mut().on_key(kp);
true
}
Key::Named(NamedKey::ArrowRight) => {
let kp = if m.shift { KeyPress::ShiftRight } else { KeyPress::ArrowRight };
coord.text_fields_mut().on_key(kp);
true
}
Key::Named(NamedKey::Home) => {
let kp = if m.shift { KeyPress::ShiftHome } else { KeyPress::Home };
coord.text_fields_mut().on_key(kp);
true
}
Key::Named(NamedKey::End) => {
let kp = if m.shift { KeyPress::ShiftEnd } else { KeyPress::End };
coord.text_fields_mut().on_key(kp);
true
}
Key::Character(s) => {
if m.ctrl || m.meta {
return false;
}
let mut consumed = false;
for ch in s.chars() {
if !ch.is_control() {
coord.text_fields_mut().on_char(ch);
consumed = true;
}
}
consumed
}
_ => false,
}
}
pub fn copy_selection(&mut self, coord: &mut InputCoordinator) {
if let Some(sel) = coord.text_fields().copy_selection() {
if let Some(cb) = self.clipboard() {
let _ = cb.set_text(sel);
}
}
}
pub fn paste(&mut self, coord: &mut InputCoordinator) {
let text = self
.clipboard()
.and_then(|cb| cb.get_text().ok())
.unwrap_or_default();
if !text.is_empty() {
coord.text_fields_mut().on_key(KeyPress::Paste(text));
}
}
}
impl Default for WinitInputBridge {
fn default() -> Self {
Self::new()
}
}