Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ cargo mobile init

After some straightforward prompts, you'll be asked to select a template pack. Template packs are used to generate project boilerplate, i.e. using the `wry` template pack gives you a [wry](https://github.com/tauri-apps/wry) project that runs out-of-the-box on desktop and mobile.

| name | info |
| --------- | ------------------------------------- |
| wry | Minimal wry project |
| name | info |
| --------- | ----------------------------------------------------------------------------------------------------------------------|
| wry | Minimal wry project |
| egui | Full egui + winit + wgpu example based on [agdk-egui example](https://github.com/rust-mobile/rust-android-examples) |

**Template pack contribution is welcomed**

Expand Down
10 changes: 10 additions & 0 deletions templates/apps/egui/.gitignore.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Rust
target/
**/*.rs.bk

# cargo-mobile
.cargo/
/gen

# macOS
.DS_Store
38 changes: 38 additions & 0 deletions templates/apps/egui/Cargo.toml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "{{app.name}}"
version = "0.1.0"
authors = ["{{author}}"]
edition = "2018"
resolver = "2"

[lib]
crate-type = ["staticlib", "cdylib", "rlib"]

[[bin]]
name = "{{app.name}}-desktop"
path = "gen/bin/desktop.rs"

[package.metadata.cargo-android]
app-dependencies = [
"com.google.android.material:material:1.8.0",
]
project-dependencies = [ "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21" ]
app-plugins = [ "org.jetbrains.kotlin.android" ]
app-theme-parent = "Theme.MaterialComponents.DayNight.DarkActionBar"

[dependencies]
log = "0.4.14"
chrono = "0.4"
pollster = "0.2"
egui = "0.22"
wgpu = "0.16"
winit = { version = "0.28", features = ["android-native-activity"] }
egui_demo_lib = "0.22"
egui-winit = { version = "0.22", default-features = false }
egui-wgpu = { version = "0.22", features = [ "winit" ] }

[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.13.1"

[target.'cfg(not(target_os = "android"))'.dependencies]
env_logger = "0.9"
5 changes: 5 additions & 0 deletions templates/apps/egui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# egui

This is an example based on [agdk-egui example](https://github.com/rust-mobile/rust-android-examples), using `egui`, `winit` and `wgpu` to run [egui_demo_app](https://github.com/emilk/egui/tree/master/egui_demo_app).

To run this on desktop, just do `cargo run` like normal! For mobile, use `cargo android run` and `cargo apple run` respectively (or use `cargo android open` and `cargo apple open` to open in Android Studio and Xcode respectively).
4 changes: 4 additions & 0 deletions templates/apps/egui/gen/bin/desktop.rs.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{{snake-case app.name}}::main();
}
168 changes: 168 additions & 0 deletions templates/apps/egui/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;

use winit::event::Event::*;
use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopWindowTarget};

use egui_wgpu::winit::Painter;
use egui_winit::State;

const INITIAL_WIDTH: u32 = 1920;
const INITIAL_HEIGHT: u32 = 1080;

/// A custom event type for the winit app.
enum Event {
RequestRedraw,
}

/// Enable egui to request redraws via a custom Winit event...
#[derive(Clone)]
struct RepaintSignal(std::sync::Arc<std::sync::Mutex<winit::event_loop::EventLoopProxy<Event>>>);

fn create_window<T>(
event_loop: &EventLoopWindowTarget<T>,
state: &mut State,
painter: &mut Painter,
) -> winit::window::Window {
let window = winit::window::WindowBuilder::new()
.with_decorations(true)
.with_resizable(true)
.with_transparent(false)
.with_title("egui winit + wgpu example")
.with_inner_size(winit::dpi::PhysicalSize {
width: INITIAL_WIDTH,
height: INITIAL_HEIGHT,
})
.build(event_loop)
.unwrap();

pollster::block_on(painter.set_window(Some(&window))).unwrap();

// NB: calling set_window will lazily initialize render state which
// means we will be able to query the maximum supported texture
// dimensions
if let Some(max_size) = painter.max_texture_side() {
state.set_max_texture_side(max_size);
}

let pixels_per_point = window.scale_factor() as f32;
state.set_pixels_per_point(pixels_per_point);

window.request_redraw();

window
}

fn _main(event_loop: EventLoop<Event>) {
let ctx = egui::Context::default();
let repaint_signal = RepaintSignal(std::sync::Arc::new(std::sync::Mutex::new(
event_loop.create_proxy(),
)));
ctx.set_request_repaint_callback(move |_| {
repaint_signal
.0
.lock()
.unwrap()
.send_event(Event::RequestRedraw)
.ok();
});

let mut state = State::new(&event_loop);
let mut painter = Painter::new(
egui_wgpu::WgpuConfiguration::default(),
1, // msaa samples
None,
false,
);
let mut window: Option<winit::window::Window> = None;
let mut egui_demo_windows = egui_demo_lib::DemoWindows::default();

event_loop.run(move |event, event_loop, control_flow| match event {
Resumed => match window {
None => {
window = Some(create_window(event_loop, &mut state, &mut painter));
}
Some(ref window) => {
pollster::block_on(painter.set_window(Some(window))).unwrap();
window.request_redraw();
}
},
Suspended => {
window = None;
}
RedrawRequested(..) => {
if let Some(window) = window.as_ref() {
let raw_input = state.take_egui_input(window);

let full_output = ctx.run(raw_input, |ctx| {
egui_demo_windows.ui(ctx);
});
state.handle_platform_output(window, &ctx, full_output.platform_output);

painter.paint_and_update_textures(
state.pixels_per_point(),
egui::Rgba::default().to_array(),
&ctx.tessellate(full_output.shapes),
&full_output.textures_delta,
false,
);

if full_output.repaint_after.is_zero() {
window.request_redraw();
}
}
}
MainEventsCleared | UserEvent(Event::RequestRedraw) => {
if let Some(window) = window.as_ref() {
window.request_redraw();
}
}
WindowEvent { event, .. } => {
match event {
winit::event::WindowEvent::Resized(size) => {
painter.on_window_resized(size.width, size.height);
}
winit::event::WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}

let response = state.on_event(&ctx, &event);
if response.repaint {
if let Some(window) = window.as_ref() {
window.request_redraw();
}
}
}
_ => (),
});
}

#[allow(dead_code)]
#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: AndroidApp) {
use winit::platform::android::EventLoopBuilderExtAndroid;

android_logger::init_once(
android_logger::Config::default().with_max_level(log::LevelFilter::Warn),
);

let event_loop = EventLoopBuilder::with_user_event()
.with_android_app(app)
.build();
_main(event_loop);
}

#[allow(dead_code)]
#[cfg(not(target_os = "android"))]
fn main() {
env_logger::builder()
.filter_level(log::LevelFilter::Warn)
.parse_default_env()
.init();

let event_loop = EventLoopBuilder::with_user_event().build();
_main(event_loop);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<!-- Base application theme. -->
<style name="AppTheme" parent="{{android-app-theme-parent}}">
<item name="android:windowFullscreen">true</item>
<!-- Customize your theme here. -->
</style>
</resources>