khora_core/ui/app/runtime.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8
9//! `App` trait — what every standalone Khora tool implements.
10//!
11//! The trait is intentionally tiny: an `update` per frame plus
12//! optional lifecycle hooks. The concrete `run_native` boot function
13//! that drives an `App` impl lives in `khora-infra::ui::egui::app`
14//! and is re-exported through `khora-sdk`.
15
16use super::AppContext;
17
18/// One application — implements `update` and gets called once per
19/// frame.
20///
21/// Tools should be single types (a struct holding all the app state)
22/// implementing this trait. The boot helper instantiates the type
23/// once, then drives `update` for the lifetime of the window.
24pub trait App {
25 /// Per-frame update — paint UI, react to input, dispatch async
26 /// completions, etc.
27 fn update(&mut self, ctx: &mut dyn AppContext);
28
29 /// Called once after the backend is up and before the first
30 /// `update`. Apps install their theme / fonts / DPI snapping
31 /// here. Default is a no-op.
32 fn on_start(&mut self, ctx: &mut dyn AppContext) {
33 let _ = ctx;
34 }
35
36 /// Called once when the window is closing. Persist state here.
37 /// Default is a no-op.
38 fn on_exit(&mut self) {}
39}
40
41/// Optional lifecycle hooks — implement on the same type as [`App`]
42/// to hook startup / shutdown without inflating the main trait.
43pub trait AppLifecycle {
44 /// Called once after the backend is up but before the first
45 /// `update`. Apps install their theme / fonts here.
46 fn on_start(&mut self, ctx: &mut dyn AppContext) {
47 let _ = ctx;
48 }
49
50 /// Called once when the window is closing. Persist state here.
51 fn on_exit(&mut self) {}
52}
53
54// Default `AppLifecycle` impl so apps that don't need it don't have
55// to declare anything extra — the boot helper detects whether the
56// concrete type opted in via a separate trait bound.
57impl<T: ?Sized> AppLifecycle for T {}