pub struct AppLauncher { /* private fields */ }Expand description
Platform-agnostic application launcher.
Platform-agnostic application launcher.
This builder provides a unified API for launching Compose applications on different platforms (desktop, Android, Web) with proper inversion of control. It abstracts away the differences between window creation, event loops, and surface initialization.
§When to use
Use AppLauncher as the standard entry point for any Cranpose application.
It handles the boilerplate of:
- Creating a window or attaching to a view.
- Initializing the graphics context (WGPU instance, Surface, Adapter, Device).
- Setting up the main event loop.
- Bridging platform events to the Cranpose runtime.
§Example
use cranpose::AppLauncher;
// Desktop
#[cfg(all(
feature = "desktop-shell",
feature = "renderer-wgpu",
not(target_os = "android")
))]
fn main() {
AppLauncher::new()
.with_title("My App")
.with_size(1024, 768)
.run(|| {
// Your composable UI here
});
}
// Android
#[cfg(all(feature = "android", target_os = "android"))]
#[unsafe(no_mangle)]
fn android_main(app: android_activity::AndroidApp) {
AppLauncher::new().with_title("My App").run(app, || {
// Your composable UI here
});
}
#[cfg(not(any(
all(
feature = "desktop-shell",
feature = "renderer-wgpu",
not(target_os = "android")
),
all(feature = "android", target_os = "android")
)))]
fn main() {}Implementations§
Source§impl AppLauncher
impl AppLauncher
Sourcepub fn with_title(self, title: impl Into<String>) -> Self
pub fn with_title(self, title: impl Into<String>) -> Self
Set the window title.
§Arguments
title- The string to display in the window title bar (Desktop/Web) or the activity label (Android).
Sourcepub fn with_capabilities(self, capabilities: &Capabilities<'static>) -> Self
pub fn with_capabilities(self, capabilities: &Capabilities<'static>) -> Self
State what this application asks of a device.
The value comes from the build script, through
app_capabilities!:
cranpose::app_capabilities!();
AppLauncher::new().with_capabilities(&CAPABILITIES)The platform builds read the same declaration, so the permissions in the manifest and the answers a service gives at run time cannot drift apart.
Sourcepub fn with_application_id(self, application_id: impl Into<String>) -> Self
pub fn with_application_id(self, application_id: impl Into<String>) -> Self
Set the id every framework-owned storage path is scoped by.
Use the same reverse-DNS identifier the app is packaged under, so a desktop build writes beside its own data rather than beside every other Cranpose application.
§Arguments
application_id- for examplecom.example.notes.
Sourcepub fn with_size(self, width: u32, height: u32) -> Self
pub fn with_size(self, width: u32, height: u32) -> Self
Set the initial window size.
Desktop uses this as the initial primary window size. Android applies
it as a best-effort host-window request only when the activity starts
in a multi-window mode (freeform / desktop windowing such as DeX);
fullscreen activities ignore it and keep the display-sized,
edge-to-edge surface, because shrinking the fullscreen window would
leave uncovered (black) strips of display around the surface. Web
ignores it: the canvas takes the box the host page lays it out in, or
the viewport under Self::with_web_fill_viewport.
§Arguments
width- The initial width in logical pixels.height- The initial height in logical pixels.
Sourcepub fn with_window_wrapping_content(self) -> Self
pub fn with_window_wrapping_content(self) -> Self
Desktop only: keep the primary window the size of what it lays out, measured after every update, so a window that holds a stack of panes is exactly that stack and shrinks when a pane leaves for a window of its own. The window keeps its top-left corner as it resizes. The initial size applies until the first layout; other platforms ignore this.
Sourcepub fn with_web_fill_viewport(self, fill: bool) -> Self
pub fn with_web_fill_viewport(self, fill: bool) -> Self
Web only: size the canvas to the full browser viewport and keep it tracking that viewport’s size as the window resizes, instead of taking the box the host page’s stylesheet lays the canvas out in. Other platforms ignore this.
Sourcepub fn with_custom_cursor_size(self, size: CustomCursorSize) -> Self
pub fn with_custom_cursor_size(self, size: CustomCursorSize) -> Self
Desktop and web: how big the app’s own cursor images appear when the
person has enlarged the system pointer. See CustomCursorSize.
Sourcepub fn with_fonts(self, fonts: &'static [&'static [u8]]) -> Self
pub fn with_fonts(self, fonts: &'static [&'static [u8]]) -> Self
Set fonts to use for text rendering.
§Arguments
fonts- A slice of static byte slices, each representing a font file (e.g.,.ttfor.otf).
§Example
use cranpose::AppLauncher;
// In specialized environments, you might include bytes:
// static REGULAR: &[u8] = include_bytes!("../assets/MyFont.ttf");
static DUMMY_FONT: &[u8] = &[];
static FONTS: &[&[u8]] = &[DUMMY_FONT];
AppLauncher::new().with_fonts(FONTS);Sourcepub fn with_font_family(self, family: &FontFamily) -> Self
pub fn with_font_family(self, family: &FontFamily) -> Self
Register a font family from files on disk.
Each FontFile declares the weight and
style its file provides, and a TextStyle naming the same family picks
between them. The files are read and parsed here, once, before the app
runs — nothing re-reads them per frame or per string.
A family whose files cannot be read is reported and skipped; text asking for it falls back to the default face rather than disappearing.
§Example
use cranpose::{
AppLauncher,
text::{FontFamily, FontFile, FontWeight},
};
let roboto = FontFamily::file_backed(vec![
FontFile::new("/system/fonts/Roboto-Regular.ttf"),
FontFile::new("/system/fonts/Roboto-Regular.ttf").with_weight(FontWeight::MEDIUM),
FontFile::new("/system/fonts/Roboto-Regular.ttf").with_weight(FontWeight::BOLD),
])
.expect("a family needs at least one file");
let launcher = AppLauncher::new().with_font_family(&roboto);Sourcepub fn with_font_face_bytes(
self,
family: &FontFamily,
weight: FontWeight,
style: FontStyle,
bytes: impl Into<Vec<u8>>,
) -> Self
pub fn with_font_face_bytes( self, family: &FontFamily, weight: FontWeight, style: FontStyle, bytes: impl Into<Vec<u8>>, ) -> Self
Register a font family from bytes the app already holds.
Use this for fonts that are not files on disk — an archive entry, a
download cache, or an asset cranpose-assets resolved out of a desktop
bundle (its load_bytes returns exactly what this wants). For an APK
asset use AppLauncher::with_android_asset_font instead: APK entries
are not filesystem paths, so a path resolver cannot reach them.
use cranpose::{
AppLauncher,
text::{FontFamily, FontStyle, FontWeight},
};
let launcher = AppLauncher::new().with_font_face_bytes(
&FontFamily::named("Roboto"),
FontWeight::NORMAL,
FontStyle::Normal,
bytes,
);Sourcepub fn with_system_font_family(
self,
directory: impl AsRef<Path>,
family: &FontFamily,
) -> Self
pub fn with_system_font_family( self, directory: impl AsRef<Path>, family: &FontFamily, ) -> Self
Bind a generic family (FontFamily::SansSerif, Serif, Monospace,
Cursive) to the platform’s own typeface for it, at Regular, Medium and
Bold.
Styles keep naming the generic family; they simply stop resolving to the
framework’s bundled fallback. On Android this is how an app matches what
Jetpack Compose draws for FontFamily.SansSerif, because the platform
backs that alias with its own Roboto.
directory is the platform’s font directory —
ANDROID_SYSTEM_FONT_DIR on Android. If nothing there backs the
family, the failure is reported and the bundled fallback keeps serving.
Sourcepub fn with_system_fonts(
self,
family: &FontFamily,
weights: &[FontWeight],
) -> Self
pub fn with_system_fonts( self, family: &FontFamily, weights: &[FontWeight], ) -> Self
Registers a family from the fonts this platform ships, without the application naming a directory.
Where a system keeps its fonts is the platform’s business, and an
application that spells the path out has target-specific code in it and
draws in the wrong typeface on the target it did not spell out. Every
weight in weights is resolved the way the platform resolves it, so a
weight the system has no file for lands on the face it would have
returned rather than being skipped.
Platforms with no readable font directory — the browser, which has no filesystem and draws with the fonts the page already has — register nothing and leave the app on its own faces.
Sourcepub fn with_log_tag(self, tag: impl Into<String>) -> Self
pub fn with_log_tag(self, tag: impl Into<String>) -> Self
The tag this application’s log lines carry.
Android routes every line through one tag and adb logcat -s <tag> is
how anyone reads them. Naming it here means an application never
initialises a platform logger of its own to get its name onto its lines.
Sourcepub fn with_window_icon(self, icon: ImageBitmap) -> Self
pub fn with_window_icon(self, icon: ImageBitmap) -> Self
Desktop only: the picture every window the application opens carries, in the title bar, taskbar and task switcher of Windows and Linux.
macOS, Android, iOS and the web take the application’s icon from what the platform packaged and ignore it. A square picture of 64 to 256 pixels a side reads well at every size those desktops draw it.
Sourcepub fn with_developer_inspector(self, enabled: bool) -> Self
pub fn with_developer_inspector(self, enabled: bool) -> Self
Enables the developer inspector independently of application semantics.
Enabled by default in debug builds and disabled in release builds. Robot drivers default to disabled; this override wins in either builder order. Open it with the floating Inspector control; drag the control or panel title to move it.
Sourcepub fn with_fonts_from(
self,
register: impl FnOnce(&mut SoftwareTextFontRegistry) -> Result<(), FontLoadError>,
) -> Self
pub fn with_fonts_from( self, register: impl FnOnce(&mut SoftwareTextFontRegistry) -> Result<(), FontLoadError>, ) -> Self
Register fonts through the registry directly, for apps that want the
per-face Result rather than a logged warning.
Sourcepub fn with_android_use_system_fonts(self, use_system_fonts: bool) -> Self
pub fn with_android_use_system_fonts(self, use_system_fonts: bool) -> Self
Enable system font loading on Android (default: false).
When false, only fonts provided via with_fonts(), with_font_family()
and friends are used. When true, the platform’s sans-serif, serif
and monospace faces are registered from
ANDROID_SYSTEM_FONT_DIR in addition, so styles naming those generic
families draw in the system typeface.
Android backs those aliases with variable fonts on modern builds; the
registry instances them per weight on their wght axis rather than
drawing every weight at the file’s default.
Text that names no family at all also lands on the system face, because
registered faces outrank the plain with_fonts() bytes on a tie — the
same thing Compose does, where FontFamily.Default is sans-serif on
Android. An app that wants its own bundled font for unnamed text should
leave this off and register its family by name instead.
Sourcepub fn with_android_gpu_backend(self, backend: AndroidGpuBackend) -> Self
pub fn with_android_gpu_backend(self, backend: AndroidGpuBackend) -> Self
Selects the Android graphics API. Other platforms ignore this setting.
The default is Vulkan. OpenGL ES uses the same WGPU renderer through the device’s GLES driver. Profiling overrides can replace this choice.
Sourcepub fn with_android_overlay_window(
self,
options: AndroidOverlayWindowOptions,
) -> Self
pub fn with_android_overlay_window( self, options: AndroidOverlayWindowOptions, ) -> Self
Render the Android root into a floating TYPE_APPLICATION_OVERLAY surface.
This Android-only mode requires the host app to declare
android.permission.SYSTEM_ALERT_WINDOW, include Cranpose’s Android Java
helper sources, and obtain overlay permission before launch. Other
platforms ignore this setting and keep their normal primary surface.
Sourcepub fn with_headless(self, headless: bool) -> Self
pub fn with_headless(self, headless: bool) -> Self
Enable headless mode for robot testing.
When headless mode is enabled, the window is created but not shown. This allows robot tests to:
- Run in parallel without windows overlapping or stealing focus
- Run in CI environments without a display server (using Xvfb or similar)
- Execute faster by skipping window decoration rendering
Note: The app still creates a full WGPU surface for accurate rendering tests.
§Example
use cranpose::AppLauncher;
#[cfg(all(
feature = "desktop-shell",
feature = "renderer-wgpu",
not(target_os = "android")
))]
{
let launcher = AppLauncher::new()
.with_title("Robot Test")
.with_size(800, 600)
.with_headless(true);
#[cfg(feature = "robot")]
let launcher = launcher.with_test_driver(|robot| {
robot.wait_for_idle().unwrap();
robot.click(100.0, 100.0).unwrap();
robot.exit().unwrap();
});
launcher.run(|| {
// Your composable UI here
});
}Sourcepub fn with_fps_counter(self, enabled: bool) -> Self
pub fn with_fps_counter(self, enabled: bool) -> Self
Enable FPS counter overlay (desktop only).
When enabled, displays a real-time FPS counter in the top-right corner. This is rendered directly by the renderer (not via composition) so it doesn’t affect performance measurements.
§Example
use cranpose::AppLauncher;
#[cfg(all(
feature = "desktop-shell",
feature = "renderer-wgpu",
not(target_os = "android")
))]
{
AppLauncher::new()
.with_title("My App")
.with_fps_counter(true)
.run(|| {
// Your composable UI here
});
}Sourcepub fn with_frame_pacing_mode(self, mode: FramePacingMode) -> Self
pub fn with_frame_pacing_mode(self, mode: FramePacingMode) -> Self
Set the initial desktop frame pacing mode.
This controls whether the desktop surface uses vsync or no-vsync presentation and, for hard caps, limits redraw scheduling to the requested frame rate.
Sourcepub fn with_frame_pacing_controls(self, enabled: bool) -> Self
pub fn with_frame_pacing_controls(self, enabled: bool) -> Self
Enable clickable frame pacing controls in the desktop development overlay.
Enabling the controls also enables the FPS overlay because the controls are rendered as part of that overlay.
Sourcepub fn with_recording(self, path: impl Into<PathBuf>) -> Self
pub fn with_recording(self, path: impl Into<PathBuf>) -> Self
Enable input recording mode.
When enabled, all mouse and keyboard events are recorded with precise timestamps. On app exit, a robot test file is generated that can replay the exact interaction sequence.
§Example
use cranpose::AppLauncher;
AppLauncher::new()
.with_title("My App")
.with_recording(".cranpose-tmp/my_test.rs")
.run(|| {
// Interact with the app, then close
// Recording is saved automatically
});Sourcepub fn try_run(self, content: impl FnMut() + 'static) -> Result<(), LaunchError>
pub fn try_run(self, content: impl FnMut() + 'static) -> Result<(), LaunchError>
Run the application (Desktop platform).
This method blocks the current thread and starts the platform event loop.
It should be the last call in your main function.
§Arguments
content- The root composable function of your application.
Sourcepub fn run(self, content: impl FnMut() + 'static) -> !
pub fn run(self, content: impl FnMut() + 'static) -> !
Run the application (Desktop platform).
Use AppLauncher::try_run when the caller needs a typed launch failure.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for AppLauncher
impl !UnwindSafe for AppLauncher
impl Freeze for AppLauncher
impl Send for AppLauncher
impl Sync for AppLauncher
impl Unpin for AppLauncher
impl UnsafeUnpin for AppLauncher
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.