Skip to main content

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 {}