Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS runtime using uikit. #631

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2991f61
Got it to commit for ios runtiem
simlay Apr 16, 2020
7384bec
Added ios example and ios things
simlay May 28, 2020
88e0ffe
Moar updates
simlay May 28, 2020
98fd67c
Updates from rebase
simlay May 29, 2020
781da4d
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay May 29, 2020
1d5decb
Got it to commit for ios runtiem
simlay Apr 16, 2020
4a8748f
Added ios example and ios things
simlay May 28, 2020
7e6d7e2
Moar updates
simlay May 28, 2020
4b8401b
Updates from rebase
simlay May 29, 2020
34ce207
minor updates
simlay Jun 13, 2020
0b18415
Added initial text widget
simlay Jun 15, 2020
7e69378
Merge branch 'add-ios-runtime' of github.com:simlay/iced into add-ios…
simlay Jun 15, 2020
341338e
Added initial text and text input widgets
simlay Jun 17, 2020
40ced01
Merge branch 'master' of github.com:hecrj/iced into add-ios-runtime
simlay Jun 17, 2020
4f615d4
Minor updates
simlay Jun 17, 2020
65aad31
Added more structure to the eventing system
simlay Jun 18, 2020
46ed1c2
Added hashing logic
simlay Jun 29, 2020
987f9c3
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jun 30, 2020
21f6564
Updates to use WidgetPointers
simlay Jul 3, 2020
1d42991
Addd layout stuff, got UIStackview working
simlay Jul 8, 2020
5cf3dfc
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jul 8, 2020
59c7c1d
updates from merge
simlay Jul 8, 2020
0d9d30b
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jul 10, 2020
bd22844
Got widget events to propigate for columns
simlay Jul 11, 2020
cdbc3c6
moar updates
simlay Jul 13, 2020
458816d
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jul 13, 2020
96512cd
Cleaned up some of the widget tree node structures
simlay Jul 20, 2020
3760f32
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jul 20, 2020
7c1809c
Added a reasonable widget shadow representation
simlay Jul 24, 2020
d1a3869
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Jul 24, 2020
40a3d7a
Updates for winit
simlay Jul 24, 2020
325a4be
Minor updates
simlay Jul 27, 2020
9c20c70
Testing out stuff for rust-bindgen 1835
simlay Jul 27, 2020
1881ad5
Merge branch 'master' of https://github.com/hecrj/iced into testing-r…
simlay Jul 31, 2020
510a273
Tested out using a closure as a poorly thought out factory pattern
simlay Jul 31, 2020
665b66f
Removed closure in widgetnode
simlay Jul 31, 2020
1deeff4
Cleaned up update logic
simlay Aug 2, 2020
078db61
Merge branch 'master' of https://github.com/hecrj/iced into testing-r…
simlay Aug 2, 2020
3fba03d
fixed uikit-sys binding
simlay Aug 2, 2020
eecb800
removed commented code
simlay Aug 2, 2020
c809263
Added button widget
simlay Aug 4, 2020
285b589
Not sure
simlay Aug 14, 2020
53eccef
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Aug 29, 2020
9b0bafa
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Sep 8, 2020
cdaf63e
Updates for new bindgen features and to fix the merge
simlay Sep 8, 2020
dafa259
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Sep 14, 2020
bd51af8
Minor cleanups for unsafe blocks
simlay Sep 16, 2020
0cbac2a
Merge branch 'master' of https://github.com/hecrj/iced into add-ios-r…
simlay Nov 24, 2020
9b6f4dc
Merge branch 'add-ios-runtime' of github.com:simlay/iced into add-ios…
simlay Nov 24, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ members = [
"native",
"style",
"web",
"ios",
"wgpu",
"winit",
"examples/bezier_tool",
Expand Down Expand Up @@ -86,7 +87,7 @@ iced_core = { version = "0.2", path = "core" }
iced_futures = { version = "0.1", path = "futures" }
thiserror = "1.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
[target.'cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))'.dependencies]
iced_winit = { version = "0.1", path = "winit" }
iced_glutin = { version = "0.1", path = "glutin", optional = true }
iced_wgpu = { version = "0.2", path = "wgpu", optional = true }
Expand All @@ -95,6 +96,9 @@ iced_glow = { version = "0.1", path = "glow", optional = true}
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced_web = { version = "0.2", path = "web" }

[target.'cfg(target_os = "ios")'.dependencies]
iced_ios = { version = "0.1", path = "ios" }

[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
features = ["image", "svg", "canvas"]
1 change: 1 addition & 0 deletions ios/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cargo.lock
31 changes: 31 additions & 0 deletions ios/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "iced_ios"
version = "0.1.0"
authors = ["Sebastian Imlay <[email protected]>"]
edition = "2018"
description = "iOS backend for iced"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
twox-hash = "1.5"
raw-window-handle = "^0.3"
winit = { git = "https://github.com/rust-windowing/winit", branch = "master"}
objc = "0.2.7"
log = "0.4"
num-traits = "0.2"
uikit-sys = { git = "https://github.com/simlay/uikit-sys", branch = "bindgen-category-and-no-copy" }
dispatch = "0.2.0"

[dependencies.iced_core]
version = "0.2"
path = "../core"

[dependencies.iced_style]
version = "0.1"
path = "../style"

[dependencies.iced_futures]
version = "0.1"
path = "../futures"
features = ["thread-pool"]
18 changes: 18 additions & 0 deletions ios/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: test example

example: example-run

example-build:
RUST_BACKTRACE=full RUST_LOG=debug cargo bundle --format ios --target x86_64-apple-ios --example ios-example

example-install: example-build
xcrun simctl install booted $(PWD)/../target/x86_64-apple-ios/debug/examples/bundle/ios/ios-example.app/

example-run: example-install
xcrun simctl launch --console booted com.github.iced.simple

test:
cargo dinghy --platform auto-ios-x86_64 test

check:
cargo check --target x86_64-apple-ios
199 changes: 199 additions & 0 deletions ios/src/application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use crate::{
event::{EventHandler, WidgetEvent},
widget::{Widget, WidgetNode},
Command, Element, Executor, Runtime, Subscription,
};
use winit::{
event,
event_loop::{ControlFlow, EventLoop},
platform::ios::{
WindowExtIOS
},
monitor::MonitorHandle,
window::{
WindowBuilder,
Window,
Fullscreen,
},
};

use uikit_sys::{
self,
//CGRect,
//CGPoint,
//CGSize,
id,
IUIColor,
//IUISwitch,
//IUIView,
UIColor,
//UISwitch,
UIView,
//UIView_UIViewGeometry,
//UIView_UIViewHierarchy,
//UIView,
//UIViewController,
UIView_UIViewRendering,
};

pub trait Application: Sized {
type Executor: Executor;

/// The type of __messages__ your [`Application`] will produce.
///
/// [`Application`]: trait.Application.html
type Message: std::fmt::Debug + Send;

/// The data needed to initialize your [`Application`].
///
/// [`Application`]: trait.Application.html
type Flags;

/// Initializes the [`Application`].
///
/// Here is where you should return the initial state of your app.
///
/// Additionally, you can return a [`Command`](struct.Command.html) if you
/// need to perform some async action in the background on startup. This is
/// useful if you want to load state from a file, perform an initial HTTP
/// request, etc.
///
/// [`Application`]: trait.Application.html
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>)
where
Self: Sized;

/// Returns the current title of the [`Application`].
///
/// This title can be dynamic! The runtime will automatically update the
/// title of your application when necessary.
///
/// [`Application`]: trait.Application.html
fn title(&self) -> String;

/// Handles a __message__ and updates the state of the [`Application`].
///
/// This is where you define your __update logic__. All the __messages__,
/// produced by either user interactions or commands, will be handled by
/// this method.
///
/// Any [`Command`] returned will be executed immediately in the background.
///
/// [`Application`]: trait.Application.html
/// [`Command`]: struct.Command.html
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;

/// Returns the widgets to display in the [`Application`].
///
/// These widgets can produce __messages__ based on user interaction.
///
/// [`Application`]: trait.Application.html
fn view(&mut self) -> Element<'_, Self::Message>;

/// Returns the event [`Subscription`] for the current state of the
/// application.
///
/// A [`Subscription`] will be kept alive as long as you keep returning it,
/// and the __messages__ produced will be handled by
/// [`update`](#tymethod.update).
///
/// By default, this method returns an empty [`Subscription`].
///
/// [`Subscription`]: struct.Subscription.html
fn subscription(&self) -> Subscription<Self::Message> {
Subscription::none()
}

/// Runs the [`Application`].
///
/// [`Application`]: trait.Application.html
fn run(flags: Self::Flags)
where
Self: 'static + Sized,
{
let event_loop: EventLoop<WidgetEvent> = EventLoop::with_user_event();
let proxy = event_loop.create_proxy();
EventHandler::init(proxy.clone());
let (sender, _receiver) =
iced_futures::futures::channel::mpsc::unbounded();

let mut runtime = {
let executor = Self::Executor::new().expect("Create executor");

Runtime::new(executor, sender)
};

let (mut app, command) = runtime.enter(|| Self::new(flags));
runtime.spawn(command);

let title = app.title();

let window = {
let mut window_builder = WindowBuilder::new();

window_builder = window_builder
.with_title(title)
.with_decorations(true)
;
window_builder.build(&event_loop).expect("Open window")
};

let root_view: UIView = UIView(window.ui_view() as id);
// TODO: Make this a debug feature
unsafe {
root_view.setBackgroundColor_(UIColor::redColor());
}
let mut widget_tree: WidgetNode = WidgetNode::new(0 as id, crate::widget::WidgetType::BaseElement, 0);

event_loop.run(
move |event: winit::event::Event<WidgetEvent>, _, control_flow| {
let mut messages: Vec<Self::Message> = Vec::new();
match event {
event::Event::MainEventsCleared => {}
event::Event::UserEvent(widget_event) => {
{
let mut element = app.view();
element.widget.on_widget_event(
widget_event,
&mut messages,
&widget_tree,
);
trace!("Root widget before: {:?}", widget_tree);
}
debug!("NEW MESSAGES! {:?}", messages);
for message in messages {
let (command, subscription) = runtime.enter(|| {
let command = app.update(message);
let subscription = app.subscription();

(command, subscription)
});

runtime.spawn(command);
runtime.track(subscription);
}
let element = app.view();

element.update(&mut widget_tree, Some(root_view.clone()));
trace!("Root widget after: {:#?}", widget_tree);
}
event::Event::RedrawRequested(_) => {}
event::Event::WindowEvent {
event: _window_event,
..
} => {}
event::Event::NewEvents(event::StartCause::Init) => {
trace!("WINDOW INIT!");
let element = app.view();
widget_tree = element.build_uiview(true);
let root_view: UIView = UIView(window.ui_view() as id);
widget_tree.draw(root_view);
}
_ => {
*control_flow = ControlFlow::Wait;
}
}
},
);
}
}
89 changes: 89 additions & 0 deletions ios/src/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use winit::event_loop::EventLoopProxy;
use objc::{
declare::ClassDecl,
runtime::{
Object,
Class,
Sel,
},
};

use uikit_sys::id;



#[derive(PartialEq, Clone, Debug)]
pub struct WidgetEvent {
pub widget_id: u64,
pub id: usize,
}

#[derive(Debug)]
pub struct EventHandler {
pub id: id,
pub widget_id: u64,
}

static mut PROXY : Option<EventLoopProxy<WidgetEvent>> = None;
static mut COUNTER: Option<u64> = None;
impl EventHandler {
pub fn init(proxy: EventLoopProxy<WidgetEvent>) {
unsafe {
COUNTER = Some(0);
PROXY = Some(proxy);
}
}
pub fn new(objc_id: id) -> Self
{
let mut widget_id = 0;
// TODO: Figure out how to make this unsafe block much smaller.
let obj = unsafe {
let obj: id = objc::msg_send![Self::class(), alloc];
let obj: id = objc::msg_send![obj, init];

if let Some(mut counter) = COUNTER {
counter += 1;
COUNTER = Some(counter);
widget_id = counter;
(*obj).set_ivar::<u64>("widget_id", widget_id);
}
(*obj).set_ivar::<id>("objc_id", objc_id);
obj
};
trace!("NEW EVENTHANDLER WITH WIDGET ID :{:?}", widget_id);
Self{id: obj, widget_id}
}
extern "C" fn event(this: &Object, _cmd: objc::runtime::Sel)
{

// TODO: Figure out how to make this unsafe block smaller.
unsafe {
if let Some(ref proxy) = PROXY {
let widget_id = *this.get_ivar::<u64>("widget_id");
let id = *this.get_ivar::<id>("objc_id");
let _ = proxy.send_event(WidgetEvent { widget_id, id: id as usize} );
}
}
}

fn class() -> &'static Class
{
let cls_name = "RustEventHandler";
match Class::get(cls_name) {
Some(cls) => cls,
None => {
let superclass = objc::class!(NSObject);
let mut decl = ClassDecl::new(cls_name, superclass).unwrap();
unsafe {
decl.add_method(
objc::sel!(sendEvent),
Self::event as extern "C" fn(&Object, Sel),
);
}
decl.add_ivar::<u64>("widget_id");
decl.add_ivar::<id>("objc_id");
decl.register()
}
}
}
}
4 changes: 4 additions & 0 deletions ios/src/keyboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

pub type Event = ();
pub type KeyCode = ();
pub type ModifiersState = ();
Loading