use objc2::{declare_class, mutability, rc::Id, runtime::Sel, sel, ClassType, DeclaredClass}; use objc2_app_kit::{NSEvent, NSResponder, NSTextInputClient, NSView}; use objc2_foundation::{ NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString, NSNotFound, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSString, NSUInteger, }; use objc2_metal_kit::MTKView; use super::{Vars, WinEvent, WinRef}; declare_class!( pub struct XLoopView; unsafe impl ClassType for XLoopView { #[inherits(NSView, NSResponder, NSObject)] type Super = MTKView; type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "XLoopView"; } impl DeclaredClass for XLoopView { type Ivars = Vars; } unsafe impl XLoopView { #[method(acceptsFirstResponder)] fn accepts_first_responder(&self) -> bool { true } #[method(isFlipped)] fn is_flipped(&self) -> bool { true } #[method(draw)] unsafe fn draw(&self) { self.draw_() } #[method(mouseDown:)] unsafe fn mouse_down(&self, event: &NSEvent) { self.mouse(event) } #[method(mouseUp:)] unsafe fn mouse_up(&self, event: &NSEvent) { self.mouse(event) } #[method(mouseDragged:)] unsafe fn mouse_dragged(&self, event: &NSEvent) { self.mouse(event) } #[method(rightMouseDown:)] unsafe fn right_mouse_down(&self, event: &NSEvent) { self.mouse(event) } #[method(rightMouseUp:)] unsafe fn right_mouse_up(&self, event: &NSEvent) { self.mouse(event) } #[method(rightMouseDragged:)] unsafe fn right_mouse_dragged(&self, event: &NSEvent) { self.mouse(event) } #[method(otherMouseDown:)] unsafe fn other_mouse_down(&self, event: &NSEvent) { self.mouse(event) } #[method(otherMouseUp:)] unsafe fn other_mouse_up(&self, event: &NSEvent) { self.mouse(event) } #[method(otherMouseDragged:)] unsafe fn other_mouse_dragged(&self, event: &NSEvent) { self.mouse(event) } #[method(scrollWheel:)] unsafe fn scroll_wheel(&self, event: &NSEvent) { self.wheel(event) } #[method(keyDown:)] unsafe fn key_down(&self, event: &NSEvent) { self.interpretKeyEvents(&NSArray::from_slice(&[&*event])); self.key(event) } #[method(keyUp:)] unsafe fn key_up(&self, event: &NSEvent) { self.key(event) } } unsafe impl NSTextInputClient for XLoopView { #[method(hasMarkedText)] fn has_marked_text(&self) -> bool { log::trace!("hasMarkedText"); self.ivars().marked_text.borrow().length() > 0 } #[method(markedRange)] fn marked_range(&self) -> NSRange { log::trace!("markedRange"); let length = self.ivars().marked_text.borrow().length(); if length > 0 { NSRange::new(0, length) }else{ NSRange::new(NSNotFound as NSUInteger, 0) } } #[method(selectedRange)] fn selected_range(&self) -> NSRange { log::trace!("selectedRange"); NSRange::new(NSNotFound as NSUInteger, 0) } #[method(setMarkedText:selectedRange:replacementRange:)] fn set_marked_text(&self, string: &NSObject, selected_range: NSRange, _replacement_range: NSRange) { let (marked_text, string) = if string.is_kind_of::() { let string: *const NSObject = string; let string: *const NSAttributedString = string.cast(); let string = unsafe { &*string }; ( NSMutableAttributedString::from_attributed_nsstring(string), string.string(), ) } else { let string: *const NSObject = string; let string: *const NSString = string.cast(); let string = unsafe { &*string }; ( NSMutableAttributedString::from_nsstring(string), string.copy(), ) }; log::info!("setMarkedText:selectedRange:replacementRange:{string:?} {selected_range:?}"); let began = self.ivars().marked_text.borrow().length()<=0; *self.ivars().marked_text.borrow_mut() = marked_text; let action = began.then_some(types::Action::Update).unwrap_or(types::Action::Begin); self.commit(types::EvtCommit::new(action,Some(string.to_string().into()))); } #[method(unmarkText)] fn unmark_text(&self) { log::trace!("unmarkText"); *self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new(); self.inputContext().expect("input context").discardMarkedText(); } #[method_id(validAttributesForMarkedText)] fn valid_attributes_for_marked_text(&self) -> Id> { log::trace!("validAttributesForMarkedText"); NSArray::new() } #[method_id(attributedSubstringForProposedRange:actualRange:)] fn attributed_substring_for_proposed_range(&self, _range: NSRange, _actual_range: *mut NSRange) -> Option> { log::trace!("attributedSubstringForProposedRange:actualRange:"); None } #[method(characterIndexForPoint:)] fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger { log::trace!("characterIndexForPoint:"); 0 } #[method(firstRectForCharacterRange:actualRange:)] fn first_rect_for_character_range(&self, _range: NSRange, _actual_range: *mut NSRange) -> NSRect { log::trace!("firstRectForCharacterRange:actualRange:"); let mut rect = NSRect::ZERO; rect.origin.y = 100.; // TODO self.window().expect("window").convertRectToScreen(self.convertRect_toView(rect, None)) } #[method(insertText:replacementRange:)] fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) { log::info!("insertText:replacementRange:{string:?}"); *self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new(); let string = if string.is_kind_of::() { let string: *const NSObject = string; let string: *const NSAttributedString = string.cast(); unsafe { &*string }.string().to_string() } else { let string: *const NSObject = string; let string: *const NSString = string.cast(); unsafe { &*string }.to_string() }; self.commit(types::EvtCommit::new(types::Action::End,Some(string.into()))); } #[method(doCommandBySelector:)] unsafe fn do_command_by_selector(&self, _command: Sel) { log::info!("doCommandBySelector:{_command:?}"); if _command == sel!(insertNewline:) { self.commit(types::EvtCommit::new(types::Action::End, Some("\n".into()))); return; } if _command == sel!(insertTab:) { self.commit(types::EvtCommit::new(types::Action::End, Some("\t".into()))); return; } if _command == sel!(deleteBackward:) { self.commit(types::EvtCommit::new(types::Action::Back, None)); return; } use objc2::runtime::MessageReceiver; if self.respondsToSelector(_command) { self.send_message(_command, (self,)) } } } ); impl XLoopView { fn draw_(&self) { if let Some(win) = self.window() { self.ivars().delegate.req_draw(WinRef(&win, &self), 0); //TODO } } fn mouse(&self, evt: &NSEvent) { if let Some(win) = self.window() { self.ivars().delegate.on_mouse(WinRef(&win, self), WinEvent(evt)); } } fn wheel(&self, evt: &NSEvent) { if let Some(win) = self.window() { self.ivars().delegate.on_wheel(WinRef(&win, self), WinEvent(evt)); } } fn key(&self, evt: &NSEvent) { if let Some(win) = self.window() { self.ivars().delegate.on_key(WinRef(&win, self), WinEvent(evt)); } } fn commit(&self, evt: types::EvtCommit) { if let Some(win) = self.window() { self.ivars().delegate.on_commit(WinRef(&win, self), &evt); } } }