#[allow(unused_imports)]
pub use repose_app::MultiTouchDelta;
pub use repose_app::TouchGestureState;
use repose_core::input::Modifiers;
use repose_core::runtime::Frame;
use repose_core::{RenderBackend, Vec2};
pub(crate) fn request_redraw(window: &Option<std::sync::Arc<winit::window::Window>>) {
if let Some(w) = window {
w.request_redraw();
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn setup_clipboard() -> Option<clipawl::Clipboard> {
let cb = clipawl::Clipboard::new().ok();
repose_core::clipboard::set_clipboard_read_fn(Box::new(|| clipawl::blocking::read().ok()));
repose_core::clipboard::set_clipboard_fn(Box::new(|text| {
if let Err(e) = clipawl::blocking::write(text) {
eprintln!("clipboard write error: {e}");
}
}));
repose_core::clipboard::set_primary_fn(Box::new(|text| {
let mut opts = clipawl::ClipboardOptions::default();
opts.linux.selection = clipawl::LinuxSelection::Primary;
match clipawl::Clipboard::new_with_options(opts) {
Ok(cb) => {
if let Err(e) = pollster::block_on(cb.write(text)) {
eprintln!("primary selection write error: {e}");
}
}
Err(e) => eprintln!("primary clipboard init error: {e}"),
}
}));
cb
}
pub fn sync_viewport(
rt: &mut repose_app::ReposeRuntime,
backend: &mut Option<repose_render_wgpu::WgpuBackend>,
size: winit::dpi::PhysicalSize<u32>,
scale: f32,
) {
rt.set_viewport_and_scale(size.width, size.height, scale);
if let Some(b) = backend {
b.configure_surface(size.width, size.height);
b.set_pixels_per_point(scale);
}
}
#[allow(dead_code)]
pub fn sync_ime_for_focused(
window: &winit::window::Window,
rt: &repose_app::ReposeRuntime,
frame: &repose_core::runtime::Frame,
) {
let Some(fid) = rt.sched.focused else {
set_ime_for_textfield(window, false);
return;
};
let Some(hit) = frame.hit_regions.iter().find(|h| h.id == fid) else {
set_ime_for_textfield(window, false);
return;
};
set_ime_for_textfield_ex(
window,
true,
hit.keyboard_type.ime_purpose_hint(),
hit.auto_correct.unwrap_or(true),
hit.capitalization,
);
let sf = window.scale_factor();
window.set_ime_cursor_area(
winit::dpi::LogicalPosition::new(hit.rect.x as f64 / sf, hit.rect.y as f64 / sf),
winit::dpi::LogicalSize::new(hit.rect.w as f64 / sf, hit.rect.h as f64 / sf),
);
}
#[allow(dead_code)]
pub fn needs_compose(
dirty: bool,
frame_requested: bool,
present_requested: bool,
has_frame: bool,
continuous: bool,
animation_active: bool,
wakeup_due: bool,
) -> bool {
continuous
|| dirty
|| frame_requested
|| (present_requested && has_frame)
|| animation_active
|| wakeup_due
}
#[allow(dead_code)]
pub(crate) fn is_textfield_in_frame(frame_cache: &Option<Frame>, id: u64) -> bool {
repose_app::is_textfield_in_frame_cache(frame_cache, id)
}
#[allow(dead_code)]
pub fn dispatch_file_drop(
frame: &Frame,
pos: Vec2,
paths: Vec<std::path::PathBuf>,
modifiers: Modifiers,
) -> bool {
let mut files = Vec::new();
for p in paths {
let name = p
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("file")
.to_string();
files.push(repose_core::dnd::DroppedFile {
name,
path: Some(p),
});
}
let payload: repose_core::dnd::DragPayload =
std::rc::Rc::new(repose_core::dnd::DroppedFiles { files });
let Some(target_id) = repose_core::dnd::dnd_target_id_at(frame, pos) else {
return false;
};
let Some(hit) = frame.hit_regions.iter().find(|h| h.id == target_id) else {
return false;
};
let Some(cb) = &hit.on_drop else {
return false;
};
cb(repose_core::dnd::DropEvent {
source_id: 0,
target_id,
position: pos,
modifiers,
payload,
});
true
}
pub(crate) fn update_modifiers(modifiers: &mut Modifiers, state: &winit::keyboard::ModifiersState) {
modifiers.shift = state.shift_key();
modifiers.ctrl = state.control_key();
modifiers.alt = state.alt_key();
modifiers.meta = state.super_key();
modifiers.command = if cfg!(target_os = "macos") {
modifiers.meta
} else {
modifiers.ctrl
};
}
#[allow(dead_code)]
pub(crate) fn hit_index_by_id(frame: &Frame, id: u64) -> Option<usize> {
repose_app::hit_index_by_id(frame, id)
}
pub(crate) fn winit_key_to_repose(
ev: &winit::event::KeyEvent,
mapped_key: &repose_core::input::Key,
mods: &repose_core::input::Modifiers,
) -> repose_core::input::KeyEvent {
let utf16 = match mapped_key {
repose_core::input::Key::Character(c) => *c as u16,
_ => 0,
};
repose_core::input::KeyEvent {
key: mapped_key.clone(),
modifiers: *mods,
is_repeat: ev.repeat,
event_type: if ev.state == winit::event::ElementState::Pressed {
repose_core::input::KeyEventType::Down
} else {
repose_core::input::KeyEventType::Up
},
utf16_code_point: utf16,
}
}
#[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
pub(crate) fn map_cursor(c: repose_core::CursorIcon) -> winit::window::CursorIcon {
use winit::window::CursorIcon as W;
match c {
repose_core::CursorIcon::Default => W::Default,
repose_core::CursorIcon::Pointer => W::Pointer,
repose_core::CursorIcon::Text => W::Text,
repose_core::CursorIcon::EwResize => W::EwResize,
repose_core::CursorIcon::NsResize => W::NsResize,
repose_core::CursorIcon::NwseResize => W::NwseResize,
repose_core::CursorIcon::NeswResize => W::NeswResize,
repose_core::CursorIcon::Grab => W::Grab,
repose_core::CursorIcon::Grabbing => W::Grabbing,
}
}
pub fn map_ime_purpose(hint: repose_core::ImePurposeHint) -> winit::window::ImePurpose {
match hint {
repose_core::ImePurposeHint::Password => winit::window::ImePurpose::Password,
_ => winit::window::ImePurpose::Normal,
}
}
pub fn set_ime_for_textfield(window: &winit::window::Window, is_textfield: bool) {
set_ime_for_textfield_ex(
window,
is_textfield,
repose_core::ImePurposeHint::Normal,
true,
repose_core::KeyboardCapitalization::Unspecified,
);
}
pub fn set_ime_for_textfield_ex(
window: &winit::window::Window,
is_textfield: bool,
purpose: repose_core::ImePurposeHint,
_auto_correct: bool,
_capitalization: repose_core::KeyboardCapitalization,
) {
if is_textfield {
window.set_ime_allowed(true);
window.set_ime_purpose(map_ime_purpose(purpose));
} else {
window.set_ime_allowed(false);
}
}
pub(crate) fn map_key(
key: winit::keyboard::PhysicalKey,
mods: &repose_core::input::Modifiers,
) -> repose_core::input::Key {
use repose_core::input::Key;
use winit::keyboard::{KeyCode, PhysicalKey};
let shifted = |a: char, b: char| Key::Character(if mods.shift { b } else { a });
match key {
PhysicalKey::Code(KeyCode::Enter) => Key::Enter,
PhysicalKey::Code(KeyCode::Tab) => Key::Tab,
PhysicalKey::Code(KeyCode::Backspace) => Key::Backspace,
PhysicalKey::Code(KeyCode::Delete) => Key::Delete,
PhysicalKey::Code(KeyCode::Insert) => Key::Insert,
PhysicalKey::Code(KeyCode::Escape) => Key::Escape,
PhysicalKey::Code(KeyCode::ArrowLeft) => Key::ArrowLeft,
PhysicalKey::Code(KeyCode::ArrowRight) => Key::ArrowRight,
PhysicalKey::Code(KeyCode::ArrowUp) => Key::ArrowUp,
PhysicalKey::Code(KeyCode::ArrowDown) => Key::ArrowDown,
PhysicalKey::Code(KeyCode::Home) => Key::Home,
PhysicalKey::Code(KeyCode::End) => Key::End,
PhysicalKey::Code(KeyCode::PageUp) => Key::PageUp,
PhysicalKey::Code(KeyCode::PageDown) => Key::PageDown,
PhysicalKey::Code(KeyCode::Space) => Key::Space,
PhysicalKey::Code(KeyCode::KeyA) => Key::Character('a'),
PhysicalKey::Code(KeyCode::KeyB) => Key::Character('b'),
PhysicalKey::Code(KeyCode::KeyC) => Key::Character('c'),
PhysicalKey::Code(KeyCode::KeyD) => Key::Character('d'),
PhysicalKey::Code(KeyCode::KeyE) => Key::Character('e'),
PhysicalKey::Code(KeyCode::KeyF) => Key::Character('f'),
PhysicalKey::Code(KeyCode::KeyG) => Key::Character('g'),
PhysicalKey::Code(KeyCode::KeyH) => Key::Character('h'),
PhysicalKey::Code(KeyCode::KeyI) => Key::Character('i'),
PhysicalKey::Code(KeyCode::KeyJ) => Key::Character('j'),
PhysicalKey::Code(KeyCode::KeyK) => Key::Character('k'),
PhysicalKey::Code(KeyCode::KeyL) => Key::Character('l'),
PhysicalKey::Code(KeyCode::KeyM) => Key::Character('m'),
PhysicalKey::Code(KeyCode::KeyN) => Key::Character('n'),
PhysicalKey::Code(KeyCode::KeyO) => Key::Character('o'),
PhysicalKey::Code(KeyCode::KeyP) => Key::Character('p'),
PhysicalKey::Code(KeyCode::KeyQ) => Key::Character('q'),
PhysicalKey::Code(KeyCode::KeyR) => Key::Character('r'),
PhysicalKey::Code(KeyCode::KeyS) => Key::Character('s'),
PhysicalKey::Code(KeyCode::KeyT) => Key::Character('t'),
PhysicalKey::Code(KeyCode::KeyU) => Key::Character('u'),
PhysicalKey::Code(KeyCode::KeyV) => Key::Character('v'),
PhysicalKey::Code(KeyCode::KeyW) => Key::Character('w'),
PhysicalKey::Code(KeyCode::KeyX) => Key::Character('x'),
PhysicalKey::Code(KeyCode::KeyY) => Key::Character('y'),
PhysicalKey::Code(KeyCode::KeyZ) => Key::Character('z'),
PhysicalKey::Code(KeyCode::Digit0) => shifted('0', ')'),
PhysicalKey::Code(KeyCode::Digit1) => shifted('1', '!'),
PhysicalKey::Code(KeyCode::Digit2) => shifted('2', '@'),
PhysicalKey::Code(KeyCode::Digit3) => shifted('3', '#'),
PhysicalKey::Code(KeyCode::Digit4) => shifted('4', '$'),
PhysicalKey::Code(KeyCode::Digit5) => shifted('5', '%'),
PhysicalKey::Code(KeyCode::Digit6) => shifted('6', '^'),
PhysicalKey::Code(KeyCode::Digit7) => shifted('7', '&'),
PhysicalKey::Code(KeyCode::Digit8) => shifted('8', '*'),
PhysicalKey::Code(KeyCode::Digit9) => shifted('9', '('),
PhysicalKey::Code(KeyCode::Minus) => shifted('-', '_'),
PhysicalKey::Code(KeyCode::Equal) => shifted('=', '+'),
PhysicalKey::Code(KeyCode::BracketLeft) => shifted('[', '{'),
PhysicalKey::Code(KeyCode::BracketRight) => shifted(']', '}'),
PhysicalKey::Code(KeyCode::Backslash) => shifted('\\', '|'),
PhysicalKey::Code(KeyCode::Semicolon) => shifted(';', ':'),
PhysicalKey::Code(KeyCode::Quote) => shifted('\'', '"'),
PhysicalKey::Code(KeyCode::Comma) => shifted(',', '<'),
PhysicalKey::Code(KeyCode::Period) => shifted('.', '>'),
PhysicalKey::Code(KeyCode::Slash) => shifted('/', '?'),
PhysicalKey::Code(KeyCode::Backquote) => shifted('`', '~'),
PhysicalKey::Code(KeyCode::NumpadAdd) => Key::Character('+'),
PhysicalKey::Code(KeyCode::NumpadSubtract) => Key::Character('-'),
PhysicalKey::Code(KeyCode::NumpadMultiply) => Key::Character('*'),
PhysicalKey::Code(KeyCode::NumpadDivide) => Key::Character('/'),
PhysicalKey::Code(KeyCode::NumpadEnter) => Key::Enter,
PhysicalKey::Code(KeyCode::NumpadDecimal) => Key::Character('.'),
PhysicalKey::Code(KeyCode::Numpad0) => Key::Character('0'),
PhysicalKey::Code(KeyCode::Numpad1) => Key::Character('1'),
PhysicalKey::Code(KeyCode::Numpad2) => Key::Character('2'),
PhysicalKey::Code(KeyCode::Numpad3) => Key::Character('3'),
PhysicalKey::Code(KeyCode::Numpad4) => Key::Character('4'),
PhysicalKey::Code(KeyCode::Numpad5) => Key::Character('5'),
PhysicalKey::Code(KeyCode::Numpad6) => Key::Character('6'),
PhysicalKey::Code(KeyCode::Numpad7) => Key::Character('7'),
PhysicalKey::Code(KeyCode::Numpad8) => Key::Character('8'),
PhysicalKey::Code(KeyCode::Numpad9) => Key::Character('9'),
PhysicalKey::Code(KeyCode::F1) => Key::F(1),
PhysicalKey::Code(KeyCode::F2) => Key::F(2),
PhysicalKey::Code(KeyCode::F3) => Key::F(3),
PhysicalKey::Code(KeyCode::F4) => Key::F(4),
PhysicalKey::Code(KeyCode::F5) => Key::F(5),
PhysicalKey::Code(KeyCode::F6) => Key::F(6),
PhysicalKey::Code(KeyCode::F7) => Key::F(7),
PhysicalKey::Code(KeyCode::F8) => Key::F(8),
PhysicalKey::Code(KeyCode::F9) => Key::F(9),
PhysicalKey::Code(KeyCode::F10) => Key::F(10),
PhysicalKey::Code(KeyCode::F11) => Key::F(11),
PhysicalKey::Code(KeyCode::F12) => Key::F(12),
_ => Key::Unknown,
}
}