Early UI work.

This commit is contained in:
2026-03-20 16:46:18 -04:00
parent 9ab1167fef
commit 39ede248cf
15 changed files with 3560 additions and 1 deletions

View File

@@ -0,0 +1,247 @@
use ruin_reactivity::{Cell, cell};
use ruin_ui::{
Color, PlatformEvent, Point, PreparedText, Rect, SceneSnapshot, UiRuntime, UiSize,
WindowController, WindowSpec, WindowUpdate,
};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt};
fn install_tracing() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(
"info,ruin_runtime::runtime=debug,ruin_ui::platform=debug,ruin_reactivity::effect=debug",
)
});
let fmt_layer = fmt::layer()
.with_target(true)
.with_thread_ids(true)
.with_thread_names(true)
.compact();
let _ = tracing_subscriber::registry()
.with(filter)
.with(fmt_layer)
.try_init();
}
fn log_platform_event(event: &PlatformEvent) {
match event {
PlatformEvent::Opened { window_id } => {
tracing::info!(event = "window_opened", window_id = window_id.raw());
}
PlatformEvent::Configured {
window_id,
configuration,
} => {
tracing::info!(
event = "window_configured",
window_id = window_id.raw(),
width = configuration.actual_inner_size.width,
height = configuration.actual_inner_size.height,
visible = configuration.visible,
"window configured"
);
}
PlatformEvent::VisibilityChanged { window_id, visible } => {
tracing::info!(
event = "visibility_changed",
window_id = window_id.raw(),
visible,
"window visibility changed"
);
}
PlatformEvent::FramePresented {
window_id,
scene_version,
item_count,
} => {
tracing::info!(
event = "frame_presented",
window_id = window_id.raw(),
scene_version,
item_count,
"headless backend presented scene"
);
}
PlatformEvent::Closed { window_id } => {
tracing::info!(event = "window_closed", window_id = window_id.raw());
}
PlatformEvent::CloseRequested { window_id } => {
tracing::info!(
event = "close_requested",
window_id = window_id.raw(),
"compositor-like close request received"
);
}
}
}
fn attach_window_scene(window: &WindowController) -> (Cell<usize>, ruin_reactivity::EffectHandle) {
let counter = cell(0usize);
let scene_window = window.clone();
let scene_effect = scene_window.attach_scene_effect({
let counter = counter.clone();
move || {
let version = counter.get() as u64 + 1;
let value = counter.get();
let mut scene = SceneSnapshot::new(version, UiSize::new(640.0, 360.0));
scene
.push_quad(
Rect::new(0.0, 0.0, 640.0, 360.0),
Color::rgb(0x12, 0x19, 0x28),
)
.push_quad(
Rect::new(24.0, 24.0, 592.0, 64.0),
Color::rgb(0x2B, 0x3A, 0x67),
)
.push_text(PreparedText::monospace(
format!("RUIN explicit prototype | counter = {value}"),
Point::new(40.0, 64.0),
18.0,
9.0,
Color::rgb(0xFF, 0xFF, 0xFF),
));
scene
}
});
(counter, scene_effect)
}
#[ruin_runtime::async_main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
install_tracing();
tracing::info!(
event = "prototype_start",
"starting explicit-construction UI prototype with a headless backend"
);
let mut ui = UiRuntime::headless();
let window = ui.create_window(
WindowSpec::new("RUIN Explicit Prototype")
.app_id("dev.ruin.prototype")
.requested_inner_size(UiSize::new(640.0, 360.0)),
)?;
let (counter, _scene_effect) = attach_window_scene(&window);
let _ = counter.set(1);
let _ = counter.set(2);
let _ = counter.set(3);
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::Configured { window_id, .. } if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
let event = ui
.wait_for_event_matching(
|event| matches!(event, PlatformEvent::Opened { window_id } if *window_id == window.id()),
)
.await?;
log_platform_event(&event);
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::FramePresented {
window_id,
scene_version: 4,
..
} if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
window.update(WindowUpdate::new().visible(false))?;
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::VisibilityChanged {
window_id,
visible: false,
} if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
window.update(WindowUpdate::new().visible(true))?;
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::VisibilityChanged {
window_id,
visible: true,
} if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
let _ = counter.set(4);
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::FramePresented {
window_id,
scene_version: 4,
..
} if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::FramePresented {
window_id,
scene_version: 5,
..
} if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
window.emit_close_requested()?;
let event = ui
.wait_for_event_matching(|event| {
matches!(
event,
PlatformEvent::CloseRequested { window_id } if *window_id == window.id()
)
})
.await?;
log_platform_event(&event);
window.update(WindowUpdate::new().open(false))?;
let event = ui
.wait_for_event_matching(
|event| matches!(event, PlatformEvent::Closed { window_id } if *window_id == window.id()),
)
.await?;
log_platform_event(&event);
ui.shutdown()?;
tracing::info!(
event = "prototype_done",
"explicit-construction UI prototype finished"
);
Ok(())
}