mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-18 01:46:47 +08:00
119 lines
5.9 KiB
Diff
119 lines
5.9 KiB
Diff
--- a/api/cpp/platform.rs
|
|
+++ b/api/cpp/platform.rs
|
|
@@ -47,6 +47,11 @@
|
|
}
|
|
|
|
impl WindowAdapter for CppWindowAdapter {
|
|
+ #[cfg(target_env = "ohos")]
|
|
+ fn internal(&self, _: i_slint_core::InternalToken) -> Option<&dyn i_slint_core::window::WindowAdapterInternal> {
|
|
+ Some(self)
|
|
+ }
|
|
+
|
|
fn window(&self) -> &Window {
|
|
&self.window
|
|
}
|
|
@@ -186,6 +191,103 @@
|
|
}
|
|
}
|
|
|
|
+
|
|
+// Harmony's native editor and cursor services are provided by the C++ host.
|
|
+// Keep this extension target-local; the public C++ adapter ABI stays unchanged.
|
|
+#[cfg(target_env = "ohos")]
|
|
+mod harmony_services {
|
|
+ use super::*;
|
|
+ use core::cell::Cell;
|
|
+ use i_slint_core::window::{InputMethodRequest, WindowAdapterInternal, WindowInner};
|
|
+ type HarmonyOsCallback = unsafe extern "C" fn(*mut c_void, *const c_void, i32,
|
|
+ *const u8, usize, usize, usize, f32, f32, f32, f32, i32);
|
|
+ std::thread_local! { static CALLBACK: Cell<Option<HarmonyOsCallback>> = const { Cell::new(None) }; }
|
|
+ #[unsafe(no_mangle)]
|
|
+ pub extern "C" fn crossdesk_slint_set_os_callback(callback: HarmonyOsCallback) {
|
|
+ CALLBACK.set(Some(callback));
|
|
+ }
|
|
+ impl WindowAdapterInternal for CppWindowAdapter {
|
|
+ fn input_method_request(&self, request: InputMethodRequest) {
|
|
+ let Some(callback) = CALLBACK.get() else { return };
|
|
+ let (kind, properties) = match request {
|
|
+ InputMethodRequest::Enable(p) => (1, Some(p)),
|
|
+ InputMethodRequest::Update(p) => (2, Some(p)),
|
|
+ InputMethodRequest::Disable => (0, None),
|
|
+ _ => return,
|
|
+ };
|
|
+ let p = properties.unwrap_or_default();
|
|
+ let cursor = p.text[..p.cursor_position].encode_utf16().count();
|
|
+ let anchor = p.text[..p.anchor_position.unwrap_or(p.cursor_position)].encode_utf16().count();
|
|
+ unsafe { callback(self.user_data, self as *const Self as *const c_void, kind, p.text.as_ptr(), p.text.len(), cursor, anchor,
|
|
+ p.cursor_rect_origin.x, p.cursor_rect_origin.y, p.cursor_rect_size.width,
|
|
+ p.cursor_rect_size.height, p.input_type as i32); }
|
|
+ }
|
|
+ fn set_mouse_cursor(&self, cursor: i_slint_core::items::MouseCursor) {
|
|
+ if let Some(callback) = CALLBACK.get() {
|
|
+ unsafe { callback(self.user_data, self as *const Self as *const c_void, 3, core::ptr::null(), 0, 0, 0,
|
|
+ 0., 0., 0., 0., cursor as i32); }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // IME commands target the editor that owns the native input session. Use
|
|
+ // Slint's normal key handling (selection, clipboard, validation and undo)
|
|
+ // without mutating the window's physical modifier state.
|
|
+ #[unsafe(no_mangle)]
|
|
+ pub unsafe extern "C" fn crossdesk_slint_edit(adapter: *const c_void,
|
|
+ text: *const u8, length: usize, modifiers: i32) {
|
|
+ use i_slint_core::input::{InternalKeyEvent, KeyEvent, KeyEventType};
|
|
+ let Ok(text) = core::str::from_utf8(unsafe { core::slice::from_raw_parts(text, length) }) else { return };
|
|
+ let window = WindowInner::from_pub(&unsafe { &*(adapter as *const CppWindowAdapter) }.window);
|
|
+ let Some(item) = window.focus_item.borrow().upgrade() else { return };
|
|
+ if item.downcast::<i_slint_core::items::TextInput>().is_none() { return; }
|
|
+ let mut key_event = KeyEvent::default();
|
|
+ key_event.text = text.into();
|
|
+ key_event.modifiers.shift = modifiers & 1 != 0;
|
|
+ key_event.modifiers.control = modifiers & 2 != 0;
|
|
+ for event_type in [KeyEventType::KeyPressed, KeyEventType::KeyReleased] {
|
|
+ item.borrow().as_ref().key_event(&InternalKeyEvent {
|
|
+ key_event: key_event.clone(), event_type, ..Default::default()
|
|
+ }, &window.window_adapter(), &item);
|
|
+ }
|
|
+ }
|
|
+ #[unsafe(no_mangle)]
|
|
+ pub unsafe extern "C" fn crossdesk_slint_selection(adapter: *const c_void, anchor: i32, cursor: i32) {
|
|
+ let window = WindowInner::from_pub(&unsafe { &*(adapter as *const CppWindowAdapter) }.window);
|
|
+ let Some(item) = window.focus_item.borrow().upgrade() else { return };
|
|
+ let Some(input) = item.downcast::<i_slint_core::items::TextInput>() else { return };
|
|
+ let input = input.as_pin_ref();
|
|
+ let text = input.text();
|
|
+ // Harmony uses UTF-16 indices; Slint uses UTF-8 byte offsets. Clamp
|
|
+ // malformed ranges to character boundaries, including surrogate pairs.
|
|
+ let byte_offset = |offset: i32| -> i32 {
|
|
+ let mut utf16 = 0;
|
|
+ for (byte, ch) in text.char_indices() {
|
|
+ if utf16 + ch.len_utf16() > offset.max(0) as usize { return byte as i32; }
|
|
+ utf16 += ch.len_utf16();
|
|
+ }
|
|
+ text.len() as i32
|
|
+ };
|
|
+ input.set_selection_offsets(&window.window_adapter(), &item, byte_offset(anchor), byte_offset(cursor));
|
|
+ }
|
|
+ // Called only on the Slint thread while the owning C++ window is alive.
|
|
+ #[unsafe(no_mangle)]
|
|
+ pub unsafe extern "C" fn crossdesk_slint_composition(adapter: *const c_void,
|
|
+ commit: bool, text: *const u8, length: usize) {
|
|
+ use i_slint_core::input::{InternalKeyEvent, KeyEvent, KeyEventType};
|
|
+ let Ok(text) = core::str::from_utf8(unsafe { core::slice::from_raw_parts(text, length) }) else { return };
|
|
+ let mut key_event = KeyEvent::default();
|
|
+ key_event.text = text.into();
|
|
+ let event = if commit {
|
|
+ InternalKeyEvent { event_type: KeyEventType::CommitComposition,
|
|
+ key_event, ..Default::default() }
|
|
+ } else {
|
|
+ InternalKeyEvent { event_type: KeyEventType::UpdateComposition, preedit_text: text.into(),
|
|
+ preedit_selection: Some(text.len() as i32..text.len() as i32), ..Default::default() }
|
|
+ };
|
|
+ WindowInner::from_pub(&unsafe { &*(adapter as *const CppWindowAdapter) }.window).process_key_input(event);
|
|
+ }
|
|
+}
|
|
+
|
|
type PlatformUserData = *mut c_void;
|
|
|
|
struct CppPlatform {
|