fn bridge_ready() -> bool {
blinc_core::native_bridge::NativeBridgeState::is_initialized()
}
pub fn haptic_selection() {
return;
#[allow(unreachable_code)]
{
if !bridge_ready() {
return;
}
let _ = blinc_core::native_bridge::native_call::<(), _>("haptics", "selection", ());
}
}
pub fn haptic_impact_light() {
return;
#[allow(unreachable_code)]
{
if !bridge_ready() {
return;
}
use blinc_core::native_bridge::NativeValue;
let _ = blinc_core::native_bridge::native_call::<(), _>(
"haptics",
"impact",
vec![NativeValue::Int32(0)],
);
}
}
pub mod edit_menu_actions {
pub const CUT: u32 = 0x01;
pub const COPY: u32 = 0x02;
pub const PASTE: u32 = 0x04;
pub const SELECT_ALL: u32 = 0x08;
}
pub fn show_edit_menu(
anchor_x: f32,
anchor_y: f32,
selection_x: f32,
selection_y: f32,
selection_width: f32,
selection_height: f32,
actions: u32,
) {
if !bridge_ready() {
return;
}
use blinc_core::native_bridge::NativeValue;
let _ = blinc_core::native_bridge::native_call::<(), _>(
"edit_menu",
"show",
vec![
NativeValue::Float32(anchor_x),
NativeValue::Float32(anchor_y),
NativeValue::Float32(selection_x),
NativeValue::Float32(selection_y),
NativeValue::Float32(selection_width),
NativeValue::Float32(selection_height),
NativeValue::Int32(actions as i32),
],
);
}
pub fn hide_edit_menu() {
if !bridge_ready() {
return;
}
let _ = blinc_core::native_bridge::native_call::<(), _>("edit_menu", "hide", ());
}
pub fn word_boundary_left(text: &str, char_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect();
if char_pos == 0 || chars.is_empty() {
return 0;
}
let mut pos = char_pos.min(chars.len());
while pos > 0 && chars[pos - 1].is_whitespace() {
pos -= 1;
}
if pos == 0 {
return 0;
}
let is_word = chars[pos - 1].is_alphanumeric() || chars[pos - 1] == '_';
if is_word {
while pos > 0 && (chars[pos - 1].is_alphanumeric() || chars[pos - 1] == '_') {
pos -= 1;
}
} else {
while pos > 0
&& !chars[pos - 1].is_alphanumeric()
&& chars[pos - 1] != '_'
&& !chars[pos - 1].is_whitespace()
{
pos -= 1;
}
}
pos
}
pub fn word_boundary_right(text: &str, char_pos: usize) -> usize {
let chars: Vec<char> = text.chars().collect();
let len = chars.len();
if char_pos >= len || chars.is_empty() {
return len;
}
let mut pos = char_pos;
let is_word = chars[pos].is_alphanumeric() || chars[pos] == '_';
let is_ws = chars[pos].is_whitespace();
if is_ws {
while pos < len && chars[pos].is_whitespace() {
pos += 1;
}
} else if is_word {
while pos < len && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
pos += 1;
}
} else {
while pos < len
&& !chars[pos].is_alphanumeric()
&& chars[pos] != '_'
&& !chars[pos].is_whitespace()
{
pos += 1;
}
}
while pos < len && chars[pos].is_whitespace() {
pos += 1;
}
pos
}
pub fn word_at_position(text: &str, char_pos: usize) -> (usize, usize) {
let chars: Vec<char> = text.chars().collect();
let len = chars.len();
if len == 0 || char_pos >= len {
return (char_pos, char_pos);
}
let ch = chars[char_pos];
let is_word = ch.is_alphanumeric() || ch == '_';
if ch.is_whitespace() {
let mut start = char_pos;
let mut end = char_pos;
while start > 0 && chars[start - 1].is_whitespace() {
start -= 1;
}
while end < len && chars[end].is_whitespace() {
end += 1;
}
(start, end)
} else if is_word {
let mut start = char_pos;
let mut end = char_pos;
while start > 0 && (chars[start - 1].is_alphanumeric() || chars[start - 1] == '_') {
start -= 1;
}
while end < len && (chars[end].is_alphanumeric() || chars[end] == '_') {
end += 1;
}
(start, end)
} else {
let mut start = char_pos;
let mut end = char_pos;
while start > 0
&& !chars[start - 1].is_alphanumeric()
&& chars[start - 1] != '_'
&& !chars[start - 1].is_whitespace()
{
start -= 1;
}
while end < len
&& !chars[end].is_alphanumeric()
&& chars[end] != '_'
&& !chars[end].is_whitespace()
{
end += 1;
}
(start, end)
}
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android", target_os = "ios")))]
pub fn clipboard_read() -> Option<String> {
arboard::Clipboard::new()
.ok()
.and_then(|mut cb| cb.get_text().ok())
.filter(|t| !t.is_empty())
}
#[cfg(any(target_os = "android", target_os = "ios"))]
pub fn clipboard_read() -> Option<String> {
if !bridge_ready() {
return None;
}
let result: blinc_core::native_bridge::NativeResult<String> =
blinc_core::native_bridge::native_call("clipboard", "paste", ());
match result {
Ok(s) if !s.is_empty() => Some(s),
_ => None,
}
}
#[cfg(target_arch = "wasm32")]
pub fn clipboard_read() -> Option<String> {
None
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android", target_os = "ios")))]
pub fn clipboard_write(text: &str) -> bool {
arboard::Clipboard::new()
.ok()
.and_then(|mut cb| cb.set_text(text.to_string()).ok())
.is_some()
}
#[cfg(any(target_os = "android", target_os = "ios"))]
pub fn clipboard_write(text: &str) -> bool {
if !bridge_ready() {
return false;
}
let result: blinc_core::native_bridge::NativeResult<()> =
blinc_core::native_bridge::native_call("clipboard", "copy", (text,));
result.is_ok()
}
#[cfg(target_arch = "wasm32")]
pub fn clipboard_write(text: &str) -> bool {
let Some(window) = web_sys::window() else {
return false;
};
let clipboard = window.navigator().clipboard();
let _ = clipboard.write_text(text);
true
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android", target_os = "ios")))]
pub fn clipboard_read_image() -> Option<(Vec<u8>, u32, u32)> {
let mut cb = arboard::Clipboard::new().ok()?;
let img = cb.get_image().ok()?;
Some((img.bytes.into_owned(), img.width as u32, img.height as u32))
}
#[cfg(any(target_arch = "wasm32", target_os = "android", target_os = "ios"))]
pub fn clipboard_read_image() -> Option<(Vec<u8>, u32, u32)> {
None
}
#[cfg(not(any(target_arch = "wasm32", target_os = "android", target_os = "ios")))]
pub fn clipboard_write_image(rgba: &[u8], width: u32, height: u32) -> bool {
let img = arboard::ImageData {
width: width as usize,
height: height as usize,
bytes: std::borrow::Cow::Borrowed(rgba),
};
arboard::Clipboard::new()
.ok()
.and_then(|mut cb| cb.set_image(img).ok())
.is_some()
}
#[cfg(any(target_arch = "wasm32", target_os = "android", target_os = "ios"))]
pub fn clipboard_write_image(_rgba: &[u8], _width: u32, _height: u32) -> bool {
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_word_boundary_left() {
assert_eq!(word_boundary_left("hello world", 5), 0);
assert_eq!(word_boundary_left("hello world", 6), 0);
assert_eq!(word_boundary_left("hello world", 11), 6);
assert_eq!(word_boundary_left("fn main() {", 3), 0);
assert_eq!(word_boundary_left("fn main() {", 8), 7); }
#[test]
fn test_word_boundary_right() {
assert_eq!(word_boundary_right("hello world", 0), 6);
assert_eq!(word_boundary_right("hello world", 6), 11);
assert_eq!(word_boundary_right("fn main() {", 0), 3);
}
#[test]
fn test_word_at_position() {
assert_eq!(word_at_position("hello world", 2), (0, 5));
assert_eq!(word_at_position("hello world", 7), (6, 11));
assert_eq!(word_at_position("hello world", 5), (5, 6)); }
}