Skip to content

Commit

Permalink
Initial work for adding soft keyboard input
Browse files Browse the repository at this point in the history
  • Loading branch information
simlay committed Mar 5, 2024
1 parent 4d4d6e5 commit 324d220
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 2 deletions.
5 changes: 5 additions & 0 deletions examples/ime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ fn main() -> Result<(), impl std::error::Error> {
let mut may_show_ime = false;
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
#[cfg(ios_platform)]
{
use winit::platform::ios::WindowExtIOS;
window.set_keyboard_visible(true);
}

event_loop.run(move |event, elwt| {
if let Event::WindowEvent { event, .. } = event {
Expand Down
7 changes: 7 additions & 0 deletions src/platform/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub trait WindowExtIOS {
///
/// The default is to not recognize gestures.
fn recognize_rotation_gesture(&self, should_recognize: bool);
fn set_keyboard_visible(&self, visible: bool);
}

impl WindowExtIOS for Window {
Expand Down Expand Up @@ -157,6 +158,12 @@ impl WindowExtIOS for Window {
self.window
.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
}

#[inline]
fn set_keyboard_visible(&self, visible: bool) {
self.window
.maybe_queue_on_main(move |w| w.set_keyboard_visible(visible))
}
}

/// Additional methods on [`WindowBuilder`] that are specific to iOS.
Expand Down
2 changes: 2 additions & 0 deletions src/platform_impl/ios/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,12 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
if !processing_redraws && event.is_redraw() {
log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() {
/*
log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}",
event
);
*/
}
handler.handle_event(event)
}
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ mod monitor;
mod uikit;
mod view;
mod window;
mod text_field;

use std::fmt;

Expand Down
92 changes: 92 additions & 0 deletions src/platform_impl/ios/text_field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#![allow(clippy::unnecessary_cast)]

use icrate::Foundation::{CGRect, CGPoint, CGSize, MainThreadMarker, NSObject, NSObjectProtocol};
use objc2::rc::Id;
use objc2::runtime::ProtocolObject;
use objc2::{
declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};

use super::uikit::{
UITextView, UITextViewDelegate,
UIResponder,
};

declare_class!(
pub(crate) struct WinitTextField;

unsafe impl ClassType for WinitTextField {
#[inherits(UIResponder, NSObject)]
type Super = UITextView;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitUITextView";
}

impl DeclaredClass for WinitTextField { }

unsafe impl WinitTextField { }
);

declare_class!(
pub(crate) struct WinitTextFieldDelegate;

unsafe impl ClassType for WinitTextFieldDelegate {
type Super = NSObject;
type Mutability = mutability::MainThreadOnly;
const NAME: &'static str = "WinitTextViewDelegate";
}

impl DeclaredClass for WinitTextFieldDelegate {
type Ivars = ();
}

unsafe impl NSObjectProtocol for WinitTextFieldDelegate {}

unsafe impl UITextViewDelegate for WinitTextFieldDelegate {

#[method(textViewDidBeginEditing:)]
unsafe fn textViewDidBeginEditing(&self, sender: &UITextView) {
let text = sender.text();
println!("DidBeginEditing: {text}");
}

#[method(textViewDidEndEditing:)]
unsafe fn textViewDidEndEditing(&self, sender: &UITextView) {
let text = sender.text();
println!("DidEndEditing: {text}");
}

#[method(textViewDidChange:)]
unsafe fn textViewDidChange(&self, sender: &UITextView) {
let text = sender.text();
println!("ShouldEndEditing: {text}");
}

}
);

impl WinitTextField {
pub(crate) fn new(
mtm: MainThreadMarker,
) -> Id<Self> {
// TODO: This should be hidden someplace.
let frame = CGRect {
origin: CGPoint {
x: 20.0,
y: 50.0,
},
size: CGSize {
width: 200.0,
height: 40.0,
},
};
let this: Id<WinitTextField> = unsafe { msg_send_id![Self::alloc(), init] };
this.setFrame(frame);
let delegate = mtm.alloc();
let delegate: Id<WinitTextFieldDelegate> = unsafe { msg_send_id![delegate, init] };

this.setDelegate(Some(ProtocolObject::from_ref(delegate.as_ref())));

this
}
}
2 changes: 2 additions & 0 deletions src/platform_impl/ios/uikit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod trait_collection;
mod view;
mod view_controller;
mod window;
mod text_field;

pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace;
Expand All @@ -38,6 +39,7 @@ pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollectio
pub(crate) use self::view::{UIEdgeInsets, UIView};
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
pub(crate) use self::window::UIWindow;
pub(crate) use self::text_field::{UITextView, UITextViewDelegate};

#[link(name = "UIKit", kind = "framework")]
extern "C" {
Expand Down
62 changes: 62 additions & 0 deletions src/platform_impl/ios/uikit/text_field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

use icrate::Foundation::{NSObject, NSString};
use objc2::{extern_class, extern_methods, mutability, ClassType, extern_protocol, ProtocolType};
use objc2::rc::Id;
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::mutability::IsMainThreadOnly;
use super::UIView;

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITextView;

unsafe impl ClassType for UITextView {
#[inherits(NSObject)]
type Super = UIView;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITextView {
#[method(frame)]
pub fn text(&self) -> &NSString;

// These are methods from UIResponder
#[method(becomeFirstResponder)]
pub fn focus(&self) -> bool;

#[method(resignFirstResponder)]
pub fn unfocus(&self) -> bool;

#[method_id(@__retain_semantics Other delegate)]
pub unsafe fn delegate(&self) -> Option<Id<ProtocolObject<dyn UITextViewDelegate>>>;

#[method(setDelegate:)]
pub fn setDelegate(&self, delegate: Option<&ProtocolObject<dyn UITextViewDelegate>>);
}
);
extern_protocol!(
pub unsafe trait UITextViewDelegate: NSObjectProtocol + IsMainThreadOnly {
#[optional]
#[method(textViewShouldBeginEditing:)]
unsafe fn textViewShouldBeginEditing(&self, sender: &UITextView) -> bool;

#[optional]
#[method(textViewDidBeginEditing:)]
unsafe fn textViewDidBeginEditing(&self, sender: &UITextView);

#[optional]
#[method(textViewShouldEndEditing:)]
unsafe fn textViewShouldEndEditing(&self, sender: &UITextView) -> bool;

#[optional]
#[method(textViewDidEndEditing:)]
unsafe fn textViewDidEndEditing(&self, sender: &UITextView);

#[optional]
#[method(textViewDidChange:)]
unsafe fn textViewDidChange(&self, sender: &UITextView);

}
unsafe impl ProtocolType for dyn UITextViewDelegate {}
);
4 changes: 4 additions & 0 deletions src/platform_impl/ios/uikit/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};


use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController};

extern_class!(
Expand Down Expand Up @@ -46,6 +47,9 @@ extern_methods!(
#[method(setRootViewController:)]
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);

#[method(addSubview:)]
pub fn addSubview(&self, view: &UIView);

#[method(convertRect:toCoordinateSpace:)]
pub fn convertRect_toCoordinateSpace(
&self,
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/ios/view.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell};

use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use icrate::Foundation::{CGFloat, CGRect, CGPoint, CGSize, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
use objc2::rc::Id;
use objc2::runtime::AnyClass;
use objc2::{
Expand Down
15 changes: 14 additions & 1 deletion src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, Main
use log::{debug, warn};
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{class, msg_send};
use objc2::{class, msg_send, msg_send_id, ClassType};

use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController};
use super::text_field::WinitTextField;
use crate::{
cursor::Cursor,
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
Expand All @@ -31,6 +32,7 @@ pub struct Inner {
window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>,
view: Id<WinitView>,
text_field: Id<WinitTextField>,
gl_or_metal_backed: bool,
}

Expand All @@ -50,6 +52,13 @@ impl Inner {
pub fn set_visible(&self, visible: bool) {
self.window.setHidden(!visible)
}
pub fn set_keyboard_visible(&self, visible: bool) {
if visible {
self.text_field.focus();
} else {
self.text_field.unfocus();
}
}

pub fn is_visible(&self) -> Option<bool> {
warn!("`Window::is_visible` is ignored on iOS");
Expand Down Expand Up @@ -439,6 +448,9 @@ impl Window {
};

let view = WinitView::new(mtm, &window_attributes, frame);
let text_view = WinitTextField::new(mtm);

view.addSubview(text_view.as_super());

let gl_or_metal_backed = unsafe {
let layer_class = WinitView::layerClass();
Expand Down Expand Up @@ -488,6 +500,7 @@ impl Window {
window,
view_controller,
view,
text_field: text_view,
gl_or_metal_backed,
};
Ok(Window {
Expand Down

0 comments on commit 324d220

Please sign in to comment.