App::monitors + run()/on_startup()

Closes #163
This commit is contained in:
Jonathan Johnson 2024-09-06 11:50:35 -07:00
parent a3c57d50d3
commit bf78da333d
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
6 changed files with 114 additions and 6 deletions

View file

@ -70,6 +70,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Window::resize_increments`
- `Window::transparent`
- `Window::visible`
- `run(&mut App)` is a new function that can provide a more concise way of
executing applications that would normally require using `PendingApp`.
- `PendingApp::on_startup` allows executing a function once the application's
event loop has begun.
- `App::monitors()` returns a snapshot of the currently configured monitors
attached to the device. A new example demonstrating this API is available at
`examples/monitors.rs`.
[139]: https://github.com/khonsulabs/cushy/issues/139

6
Cargo.lock generated
View file

@ -124,8 +124,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "appit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a158c9d2660ce603c741d513b44cdd97a4553e0749d4e49e45b9480fc72c162"
source = "git+https://github.com/khonsulabs/appit#4cab6aedd10d179ed730397dc48ca6b23af6a431"
dependencies = [
"winit",
]
@ -1310,8 +1309,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bc8757e03860f7a9a15e9616060ed620a12500a61c9ff9b457cccfc5f20af1f"
source = "git+https://github.com/khonsulabs/kludgine#a468845f84cbee812778f91fe55cf02716efebd8"
dependencies = [
"ahash",
"alot",

View file

@ -22,7 +22,9 @@ tokio = ["dep:tokio"]
tokio-multi-thread = ["tokio", "tokio/rt-multi-thread"]
[dependencies]
kludgine = { version = "0.10.0", features = ["app"] }
kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
"app",
] }
figures = { version = "0.4.0" }
alot = "0.3"
interner = "0.2.1"

53
examples/monitors.rs Normal file
View file

@ -0,0 +1,53 @@
use cushy::widget::{MakeWidget, WidgetInstance, WidgetList};
use cushy::{Application, Open, PendingApp};
use kludgine::app::{Monitor, Monitors};
fn main() -> cushy::Result {
// Monitor information is only available through winit after the application
// has started up.
assert!(PendingApp::default().as_app().monitors().is_none());
cushy::run(|app| {
let monitors = app.monitors();
"Monitors"
.h1()
.and(list_monitors(monitors))
.into_rows()
.vertical_scroll()
.expand()
.open(app)
.expect("app running");
})
}
fn list_monitors(monitors: Option<Monitors>) -> WidgetInstance {
if let Some(monitors) = monitors {
monitors
.available
.into_iter()
.enumerate()
.map(|(index, monitor)| monitor_info(index, monitor, monitors.primary.as_ref()))
.collect::<WidgetList>()
.into_rows()
.make_widget()
} else {
"No monitor information available".make_widget()
}
}
fn monitor_info(index: usize, monitor: Monitor, primary: Option<&Monitor>) -> impl MakeWidget {
let mut name = monitor
.name()
.unwrap_or_else(|| format!("Monitor {}", index + 1));
if primary.map_or(false, |primary| primary == &monitor) {
name.push_str(" (Primary)");
}
let region = monitor.region();
let region = format!(
"{},{} @ {}x{}",
region.origin.x, region.origin.y, region.size.width, region.size.height
);
name.h3().and(region).into_rows().contain()
}

View file

@ -1,8 +1,9 @@
use std::marker::PhantomData;
use std::sync::Arc;
use std::thread;
use arboard::Clipboard;
use kludgine::app::{AppEvent, AsApplication};
use kludgine::app::{AppEvent, AsApplication, Monitors};
use parking_lot::{Mutex, MutexGuard};
use crate::animation;
@ -30,6 +31,30 @@ impl PendingApp {
pub const fn cushy(&self) -> &Cushy {
&self.cushy
}
/// Executes `on_startup` once the application event loop has begun.
///
/// Some APIs are not available until after the application has started
/// running. For example, `App::monitors` requires the event loop to have
/// been started.
pub fn on_startup<F>(&mut self, on_startup: F)
where
F: FnOnce(&mut App) + Send + 'static,
{
let mut app = self.as_app();
self.app.on_startup(move |_app| {
// Accessing some information from this closure needs to use `_app`
// instead of `App`. For example, accessing monitor information
// requires the window thread to respond to a message. Trying to do
// that in this closure would cause the thread to block. So, we
// execute our on_startup callbacks in their own thread.
thread::spawn(move || {
let cushy = app.cushy.clone();
let _guard = cushy.enter_runtime();
on_startup(&mut app);
});
});
}
}
impl Run for PendingApp {
@ -293,6 +318,17 @@ pub struct App {
cushy: Cushy,
}
impl App {
/// Returns a snapshot of information about the monitors connected to this
/// device.
///
/// Returns None if the app is not currently running.
#[must_use]
pub fn monitors(&self) -> Option<Monitors> {
self.app.as_ref().and_then(kludgine::app::App::monitors)
}
}
impl Application for App {
fn cushy(&self) -> &Cushy {
&self.cushy

View file

@ -45,6 +45,17 @@ pub use {figures, kludgine};
pub use self::graphics::Graphics;
pub use self::tick::{InputState, Tick};
/// Starts running a Cushy application, invoking `app_init` after the event loop
/// has started.
pub fn run<F>(app_init: F) -> Result
where
F: FnOnce(&mut App) + Send + 'static,
{
let mut app = PendingApp::default();
app.on_startup(app_init);
app.run()
}
/// A limit used when measuring a widget.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ConstraintLimit {