First commit. Version 0.1.0
This commit is contained in:
385
docs/onboarding.md
Normal file
385
docs/onboarding.md
Normal file
@@ -0,0 +1,385 @@
|
||||
# ltk onboarding
|
||||
|
||||
This guide is for the first hour with `ltk`: what environment you need, how to
|
||||
run the examples, how to build a minimal app, when to use layer-shell vs a
|
||||
regular window, and what theme/font assumptions the toolkit currently makes.
|
||||
|
||||
If you already know the basics and want the deeper rationale, read
|
||||
[`docs/architecture.md`](./architecture.md) next.
|
||||
|
||||
## What `ltk` is
|
||||
|
||||
`ltk` is a Rust UI toolkit for Wayland. It is aimed first at the Eydos shell
|
||||
stack, but it can also be used to build normal client applications and
|
||||
runtime-free UI surfaces.
|
||||
|
||||
At a high level:
|
||||
|
||||
- Implement the [`App`] trait.
|
||||
- Return an [`Element<Msg>`] tree from `view()`.
|
||||
- React to user input by handling messages in `update()`.
|
||||
- Start the event loop with `ltk::run(app)`.
|
||||
|
||||
The model is declarative and Elm-shaped: the widget tree is rebuilt from your
|
||||
state, then `ltk` handles layout, drawing and input dispatch.
|
||||
|
||||
If you are browsing the crate through `cargo doc`, the public API is also
|
||||
grouped conceptually into three entry points:
|
||||
|
||||
- `ltk::window` — basic application windows
|
||||
- `ltk::shell` — layer-shell and overlays
|
||||
- `ltk::runtime` — advanced runtime hooks and runtime-free embedding
|
||||
|
||||
Most users should start with `ltk::window` and ignore the other two until they
|
||||
have a normal app window running.
|
||||
|
||||
## Before you start
|
||||
|
||||
`ltk` is not a browser toolkit and not a cross-platform desktop toolkit. Today
|
||||
it assumes:
|
||||
|
||||
- a running **Wayland** session
|
||||
- Wayland client libraries available through Rust dependencies
|
||||
- a usable system font such as `google-sora-fonts`, `liberation-fonts` or
|
||||
`dejavu-fonts`
|
||||
- an installed `default` theme, or a development theme directory exposed
|
||||
through `LTK_THEMES_DIR`
|
||||
|
||||
The rendering backend is selected automatically:
|
||||
|
||||
- **GLES** when EGL/GLES is available
|
||||
- **software** fallback otherwise, or when `LTK_FORCE_SOFTWARE=1`
|
||||
|
||||
## Fastest way to see it working
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
cargo run --example showcase
|
||||
```
|
||||
|
||||
Other useful examples:
|
||||
|
||||
- `cargo run --example widgets` — broad widget survey
|
||||
- `cargo run --example inputs` — text entry
|
||||
- `cargo run --example scroll` — scroll viewport patterns
|
||||
- `cargo run --example mini_shell` — overlays, animation and theme switching
|
||||
|
||||
All examples require a running Wayland compositor.
|
||||
|
||||
## Theme and font setup
|
||||
|
||||
`ltk` currently expects a theme named `default`. Lookup order is:
|
||||
|
||||
1. `LTK_THEMES_DIR/<id>/`
|
||||
2. `$XDG_DATA_HOME/ltk/themes/<id>/`
|
||||
3. `/usr/share/ltk/themes/<id>/`
|
||||
|
||||
For development inside this repository, the simplest setup is:
|
||||
|
||||
```bash
|
||||
export LTK_THEMES_DIR="$PWD/themes"
|
||||
```
|
||||
|
||||
That makes `ThemeDocument::find("default")` resolve to
|
||||
`$PWD/themes/default/theme.json`.
|
||||
|
||||
Font loading is separate from theme lookup. `Canvas` walks a chain of
|
||||
common system font paths (`fonts-sora`, `fonts-liberation`, `fonts-dejavu`,
|
||||
`fonts-freefont`, …) and uses the first one it finds. If nothing matches,
|
||||
it falls back to an embedded Sora Regular (~50 KB, SIL OFL 1.1) shipped
|
||||
inside the crate, so canvas construction never panics on a system without
|
||||
the expected fonts. Installing one of the listed packages is still
|
||||
recommended for richer glyph coverage.
|
||||
|
||||
## Your first app
|
||||
|
||||
The smallest useful `ltk` app implements `App`, returns a tree from `view()`,
|
||||
updates its state in `update()`, and calls `ltk::run(...)`.
|
||||
|
||||
```rust,no_run
|
||||
use ltk::{ App, Element, Keysym, button, column, spacer, text };
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Msg
|
||||
{
|
||||
Increment,
|
||||
}
|
||||
|
||||
struct CounterApp
|
||||
{
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl App for CounterApp
|
||||
{
|
||||
type Message = Msg;
|
||||
|
||||
fn view( &self ) -> Element<Msg>
|
||||
{
|
||||
column::<Msg>()
|
||||
.padding( 32.0 )
|
||||
.spacing( 16.0 )
|
||||
.center_y( true )
|
||||
.push( text( "Hello from ltk" ).size( 28.0 ) )
|
||||
.push( text( format!( "Count: {}", self.value ) ).size( 18.0 ) )
|
||||
.push( spacer() )
|
||||
.push( button( "Increment" ).on_press( Msg::Increment ) )
|
||||
.into()
|
||||
}
|
||||
|
||||
fn update( &mut self, msg: Msg )
|
||||
{
|
||||
match msg
|
||||
{
|
||||
Msg::Increment => self.value += 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn on_key( &mut self, keysym: Keysym ) -> Option<Msg>
|
||||
{
|
||||
if keysym == Keysym::Escape
|
||||
{
|
||||
std::process::exit( 0 );
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
ltk::run( CounterApp { value: 0 } );
|
||||
}
|
||||
```
|
||||
|
||||
### Minimal `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "my-ltk-app"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ltk = { path = "../ltk" }
|
||||
```
|
||||
|
||||
If you vend `ltk` from crates.io later, replace the `path` dependency with a
|
||||
versioned one.
|
||||
|
||||
## Public API Layers
|
||||
|
||||
`ltk` exposes most items at the crate root, but for documentation and discovery
|
||||
it is useful to think of the library in three layers.
|
||||
|
||||
### 1. `ltk::window`
|
||||
|
||||
This is the default entry point for third-party applications.
|
||||
|
||||
Use it for:
|
||||
|
||||
- normal application windows
|
||||
- tools and prototypes
|
||||
- most widget/layout work
|
||||
|
||||
The APIs you will usually touch first live here conceptually:
|
||||
|
||||
- `App`
|
||||
- `Element<Msg>`
|
||||
- `button`, `text`, `text_edit`, `image`
|
||||
- `column`, `row`, `stack`, `grid`, `spacer`
|
||||
- `container`, `scroll`, `slider`, `toggle`, `checkbox`, `radio`
|
||||
- `Color`
|
||||
- `run`
|
||||
|
||||
### 2. `ltk::shell`
|
||||
|
||||
This layer groups the APIs that matter when your surface is part of the shell
|
||||
rather than a normal app window.
|
||||
|
||||
Use it for:
|
||||
|
||||
- bars and docks
|
||||
- homescreens
|
||||
- notifications
|
||||
- greeters and lock screens
|
||||
- transient overlays
|
||||
|
||||
The most important APIs in this layer are:
|
||||
|
||||
- `ShellMode`
|
||||
- `Layer`
|
||||
- `Anchor`
|
||||
- `OverlaySpec`
|
||||
- `OverlayId`
|
||||
- `overlays()`
|
||||
|
||||
### 3. `ltk::runtime`
|
||||
|
||||
This layer is for advanced integration points.
|
||||
|
||||
Use it when you need:
|
||||
|
||||
- external wakeups via `set_channel_sender()`
|
||||
- timer-driven or async state via `poll_external()` / `poll_interval()`
|
||||
- redraw narrowing via `invalidate_after()`
|
||||
- runtime theme state access
|
||||
- runtime-free embedding through `core::UiSurface`
|
||||
|
||||
Most applications do not need to start here.
|
||||
|
||||
## Regular app window vs shell surface
|
||||
|
||||
Most consumers should start with a **regular window**.
|
||||
|
||||
Default behaviour:
|
||||
|
||||
- `shell_mode()` defaults to `ShellMode::Window`
|
||||
- `ltk::run(app)` creates an xdg-shell toplevel
|
||||
|
||||
Use this for:
|
||||
|
||||
- normal applications
|
||||
- internal tools
|
||||
- prototypes while learning the toolkit
|
||||
|
||||
Switch to **layer-shell** only when you are building a shell component:
|
||||
|
||||
- top bar
|
||||
- dock
|
||||
- homescreen
|
||||
- notification surface
|
||||
- lock screen / greeter
|
||||
|
||||
The knobs you will usually override are:
|
||||
|
||||
- `shell_mode()`
|
||||
- `layer_anchor()`
|
||||
- `layer_size()`
|
||||
- `exclusive_zone()`
|
||||
- `keyboard_exclusive()`
|
||||
- `background_color()`
|
||||
|
||||
For a non-trivial layer-shell example, use `examples/mini_shell.rs` as the
|
||||
reference entry point.
|
||||
|
||||
## The APIs you will touch first
|
||||
|
||||
In practice, most first apps only need a small subset of the surface area.
|
||||
|
||||
Start here:
|
||||
|
||||
- `App`
|
||||
- `Element<Msg>`
|
||||
- `button`, `text`, `text_edit`, `image`
|
||||
- `column`, `row`, `stack`, `grid`, `spacer`
|
||||
- `container`, `scroll`, `slider`, `toggle`, `checkbox`, `radio`
|
||||
- `Color`
|
||||
- `run`
|
||||
|
||||
Do not start with these unless you need them:
|
||||
|
||||
- `ltk::shell`
|
||||
- `ltk::runtime`
|
||||
- `overlays()`
|
||||
- gesture hooks such as `on_swipe_*`
|
||||
- `set_channel_sender()` / `poll_external()`
|
||||
- `core::UiSurface`
|
||||
- custom theming APIs
|
||||
|
||||
## Message flow and state
|
||||
|
||||
The expected shape is:
|
||||
|
||||
1. user interaction emits a `Message`
|
||||
2. `update()` mutates app state
|
||||
3. `view()` rebuilds the UI from that state
|
||||
|
||||
Example:
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
enum Msg
|
||||
{
|
||||
NameChanged( String ),
|
||||
Submit,
|
||||
}
|
||||
```
|
||||
|
||||
For small apps, one top-level `enum Msg` is enough. Once the app grows, split
|
||||
state by screen/panel and wrap sub-messages in the top-level enum:
|
||||
|
||||
```rust,no_run
|
||||
# #[ derive( Clone ) ] pub enum HomeMsg {}
|
||||
# #[ derive( Clone ) ] pub enum SettingsMsg {}
|
||||
enum AppMsg
|
||||
{
|
||||
Home( HomeMsg ),
|
||||
Settings( SettingsMsg ),
|
||||
Quit,
|
||||
}
|
||||
```
|
||||
|
||||
This is the pattern used by `examples/mini_shell.rs`.
|
||||
|
||||
## Recommended learning order
|
||||
|
||||
If you are new to the library, this order minimizes confusion:
|
||||
|
||||
1. Run `examples/showcase.rs`.
|
||||
2. Read the crate-level docs in `src/lib.rs`, especially `ltk::window`.
|
||||
3. Build a plain xdg-shell window with `button`, `text`, `column`.
|
||||
4. Add input handling with `text_edit` or `slider`.
|
||||
5. Only then look at `ltk::shell` for overlays and layer-shell.
|
||||
6. Move to `ltk::runtime` only when you need advanced hooks or embedding.
|
||||
|
||||
## Performance rules of thumb
|
||||
|
||||
`ltk` is designed to sleep when idle and redraw only on real changes, but the
|
||||
application can still make bad choices. Keep these rules in mind:
|
||||
|
||||
- keep `view()` pure and cheap
|
||||
- do not do filesystem I/O, parsing or image decoding inside `view()`
|
||||
- cache expensive derived data on your app struct
|
||||
- leave `poll_interval()` as `None` unless you genuinely need periodic wakeups
|
||||
- only return `true` from `is_animating()` while something is actually moving
|
||||
|
||||
On mobile targets, the last two matter directly for battery life.
|
||||
|
||||
## When to use `core::UiSurface`
|
||||
|
||||
Most apps should ignore `core` at first.
|
||||
|
||||
Use `core::UiSurface` when you want `ltk`'s layout/drawing/hit-testing without
|
||||
`ltk::run()`. Typical cases:
|
||||
|
||||
- compositor-side decorations
|
||||
- embedding `ltk` widgets in another render loop
|
||||
- offscreen rendering or previews
|
||||
|
||||
There is coverage for that path in `tests/core_surface.rs`.
|
||||
|
||||
## Current assumptions and rough edges
|
||||
|
||||
This repo is usable, but a few current behaviours are worth knowing up front:
|
||||
|
||||
- examples and docs assume Wayland, not X11
|
||||
- theming is process-global
|
||||
- theme discovery currently expects a `default` theme on disk (a B/W
|
||||
fallback document kicks in when missing, with a red banner on every
|
||||
frame so the gap is impossible to miss)
|
||||
- the architecture docs mention downstream consumer repos that are not part of
|
||||
this repository
|
||||
|
||||
None of that blocks learning the toolkit, but it matters when you evaluate
|
||||
`ltk` as a third-party dependency.
|
||||
|
||||
## What to read next
|
||||
|
||||
- [`docs/architecture.md`](./architecture.md) — multi-surface patterns,
|
||||
theming, animation and performance
|
||||
- [`examples/showcase.rs`](../examples/showcase.rs) — smallest visual tour
|
||||
- [`examples/widgets.rs`](../examples/widgets.rs) — broader widget coverage
|
||||
- [`examples/mini_shell.rs`](../examples/mini_shell.rs) — overlays and shell
|
||||
patterns
|
||||
- [`tests/core_surface.rs`](../tests/core_surface.rs) — runtime-free rendering
|
||||
Reference in New Issue
Block a user