Crates.io | floui |
lib.rs | floui |
version | 0.1.5 |
source | src |
created_at | 2022-07-13 00:42:30.351747 |
updated_at | 2022-07-18 23:21:34.751428 |
description | A SwiftUI inspired native wrapper for iOS and Android widgets |
homepage | |
repository | https://github.com/MoAlyousef/floui-rs |
max_upload_size | |
id | 624777 |
size | 50,725 |
Rust bindings for floui, pronounced "flowy", a proof-of-concept single header C++17 lib inspired by SwiftUI, which wraps native iOS and Android controls/widgets, and integrates into the de facto build environments of each platform (XCode and Android Studio).
You can check out the floui-rs-template, which is structured to be able to build for both ios or android from the command-line.
If you would like to build purely for iOS and only using Rust (no Objective-C), check the example here.
Otherwise, if you would like to do it manually:
Build your library as a static-lib:
# Cargo.toml
[lib]
crate-type = ["static-lib"]
[dependencies]
floui = "0.1"
Build against the android and ios architectures you might need.
use floui::{enums::*, prelude::*, widgets::*};
use std::cell::RefCell;
use std::rc::Rc;
fn mygui(vc: &ViewController) -> MainView {
let count = Rc::from(RefCell::from(0));
MainView::new(
&vc,
&[
&Button::new("Increment").foreground(Color::Blue).action({
let count = count.clone();
move |_| {
log("Increment clicked");
let mut c = count.borrow_mut();
*c += 1;
let t: Text = from_id("mytext").unwrap();
t.text(&format!("{}", c));
}
}),
&Text::new("0").id("mytext").center().bold(),
&Button::new("Decrement")
.foreground(Color::Red)
.action(move |_| {
log("Decrement clicked");
let mut c = count.borrow_mut();
*c -= 1;
let t: Text = from_id("mytext").unwrap();
t.text(&format!("{}", c));
}),
],
)
}
use std::os::raw::c_void;
#[no_mangle]
extern "C" fn floui_handle_events(arg1: *mut c_void) {
unsafe { ViewController::handle_events(arg1); }
}
#[no_mangle]
extern "C" fn floui_main(arg1: *mut c_void, arg2: *mut c_void, arg3: *mut c_void) -> *mut c_void {
let vc = unsafe { ViewController::new(arg1, arg2, arg3) };
mygui(&vc).underlying() as _
}
Sliders on Android take the full width of the LinearLayout, so this must be taken into consideration if code is shared also with iOS.
Adding images has to be in the project's resource file.
Using the WebView widget
ios-webview
flag in your Cargo.toml.file:///
, the files should be added to your xcode project.file:///
and the path of the file, which should be added to an assets folder (File > New > Folder > Assets folder). This then can be loaded using WebView::load_url().<uses-permission android:name="android.permission.INTERNET" />
Wrapping platform widgets doesn't require using C++, it requires that the widget implements WidgetExt, and the underlying (JNI jobject pointer for Android, UIView on iOS) can be retrieved. You can use the jni-rs crate and objc crates for such purposes.
#import "ViewController.h"
extern void *floui_main(void *, void *, void *);
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
floui_main((void *)CFBridgingRetain(self), nil, nil);
}
@end
cmake_minimum_required(VERSION 3.4.1)
project(myapplication)
find_library(log-lib log)
add_library(myapplication SHARED native-lib.cpp)
add_library(rust-lib STATIC IMPORTED)
if (ANDROID_ABI STREQUAL x86)
set(RUST_ARCH i686-linux-android)
elseif (ANDROID_ABI STREQUAL armeabi-v7a)
set(RUST_ARCH armv7-linux-androideabi)
elseif (ANDROID_ABI STREQUAL arm64-v8a)
set(RUST_ARCH aarch64-linux-android)
elseif (ANDROID_ABI STREQUAL x86_64)
set(RUST_ARCH x86_64-linux-android)
else ()
message(FATAL "Unknown architecture")
endif ()
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/debug/libapp.a)
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/release/libapp.a)
target_link_libraries(myapplication android rust-lib ${log-lib})
#include <jni.h>
#include <string>
extern "C" void *floui_main(void *, void *, void *);
extern "C" void floui_handle_events(void *);
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_myapplication_MainActivity_mainView(JNIEnv* env, jobject main_activity, jobject view) {
return (jobject) floui_main(env, main_activity, view);
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_handleEvent(JNIEnv *env, jobject thiz, jobject view) {
floui_handle_events(view);
}
package com.example.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import android.os.Bundle;
import android.view.View;
import com.google.android.material.slider.Slider;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Slider.OnChangeListener {
static {
System.loadLibrary("myapplication");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConstraintLayout layout = new ConstraintLayout(this);
setContentView(layout);
mainView(layout);
}
public native View mainView(View view);
public native void handleEvent(View view);
@Override
public void onClick(View view) {
handleEvent(view);
}
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
handleEvent(slider);
}
}