Multi-window support

Closes #91

There's some details to still figure out, which are in new issues:

- #109: When opening a window, no handle is returned that gives access to the
  window from the opener. Technically this can all be wired up manually,
  with exception of requeesting the window close.
- #107: How can a window close itself? Once we have a handle type, we still
  need a mechanism to allow a button on a window request that the window
  closes gracefully. The examples that currently close the window
  call exit instad.
This commit is contained in:
Jonathan Johnson 2023-12-21 14:57:29 -08:00
parent 4c9e2d5989
commit f1a2a711ff
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
12 changed files with 431 additions and 102 deletions

View file

@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Many bounds required `UnwindSafe` due to a misunderstanding on how to handle
this trait in `appit`. All requirements for `UnwindSafe` have been removed.
- `Gooey` no longer implements default. To gain access to a `Gooey` instance,
create a `PendingApp` or get a reference to the running `App`.
- `Window::new` no longer accepts a `Gooey` parameter. The window now adopts the
`Gooey` from the application it is opened within.
- `MakeWidget::into_window()` no longer takes any parameters.
### Changed
@ -30,6 +35,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
validations. This was already available on `when` conditioned validations.
- `Dynamic::[try_]compare_swap` allows swapping the contents of a dynamic after
verifying the current contents.
- [#91][91]: Multi-window support has been implemented. `PendingApp` allows
opening one or more windows before starting the program. `App` is a handle to
the running application that can be used to open additional windows at
runtime.
`Open` is a new trait that allows various types to open as a window given a
reference to an application. This trait is implemented for all types that
implemented `Run`, which means any type that was previously able to be run as
a standalone executable can now be opened as a window within a multi-window
application.
The `multi-window` example demonstates using this feature to open multiple
windows before starting Gooey as well as dynamically opening windows at
runtime.
- `Window::on_close` sets a callback to be invoked when the window has closed.
[91]: https://github.com/khonsulabs/gooey/issues/91
## v0.1.3 (2023-12-19)

164
Cargo.lock generated
View file

@ -2,6 +2,22 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ab_glyph"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225"
dependencies = [
"ab_glyph_rasterizer",
"owned_ttf_parser",
]
[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
[[package]]
name = "addr2line"
version = "0.21.0"
@ -53,9 +69,9 @@ checksum = "b072fc284b73a3e4154e2decdbaad711daca0e8fedfceb0d7b1cbe2dffb00e2b"
[[package]]
name = "android-activity"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d"
checksum = "39b801912a977c3fd52d80511fe1c0c8480c6f957f21ae2ce1b92ffe970cf4b9"
dependencies = [
"android-properties",
"bitflags 2.4.1",
@ -90,7 +106,7 @@ dependencies = [
[[package]]
name = "appit"
version = "0.1.1"
source = "git+https://github.com/khonsulabs/appit#36a413865b6ac93e04b8a32023397714a165304d"
source = "git+https://github.com/khonsulabs/appit#1e162ed8df4470522d6dbfb1567b54df74ba911f"
dependencies = [
"winit",
]
@ -120,9 +136,15 @@ dependencies = [
"parking_lot",
"thiserror",
"winapi",
"x11rb",
"x11rb 0.12.0",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -805,6 +827,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.2.11"
@ -1147,7 +1179,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kludgine"
version = "0.6.1"
source = "git+https://github.com/khonsulabs/kludgine#bb1a2cc237b353ebd91bef62109b9bb8e3cf32e7"
source = "git+https://github.com/khonsulabs/kludgine#76a378ca124b2f965049e65aada1b0e839b37aff"
dependencies = [
"ahash",
"alot",
@ -1452,8 +1484,7 @@ dependencies = [
"log",
"ndk-sys",
"num_enum",
"raw-window-handle 0.5.2",
"raw-window-handle 0.6.0",
"raw-window-handle",
"thiserror",
]
@ -1637,6 +1668,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owned_ttf_parser"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
dependencies = [
"ttf-parser 0.20.0",
]
[[package]]
name = "palette"
version = "0.7.3"
@ -1827,9 +1867,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.70"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
@ -1961,12 +2001,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "raw-window-handle"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544"
[[package]]
name = "rayon"
version = "1.8.0"
@ -2127,6 +2161,19 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sctk-adwaita"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b2eaf3a5b264a521b988b2e73042e742df700c4f962cde845d1541adb46550"
dependencies = [
"ab_glyph",
"log",
"memmap2 0.9.3",
"smithay-client-toolkit",
"tiny-skia",
]
[[package]]
name = "self_cell"
version = "1.0.3"
@ -2269,6 +2316,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "svg_fmt"
version = "0.4.1"
@ -2287,9 +2340,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.41"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
@ -2355,6 +2408,31 @@ dependencies = [
"weezl",
]
[[package]]
name = "tiny-skia"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"cfg-if",
"log",
"tiny-skia-path",
]
[[package]]
name = "tiny-skia-path"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -2763,7 +2841,7 @@ dependencies = [
"naga",
"parking_lot",
"profiling",
"raw-window-handle 0.5.2",
"raw-window-handle",
"smallvec",
"static_assertions",
"wasm-bindgen",
@ -2788,7 +2866,7 @@ dependencies = [
"naga",
"parking_lot",
"profiling",
"raw-window-handle 0.5.2",
"raw-window-handle",
"rustc-hash",
"smallvec",
"thiserror",
@ -2829,7 +2907,7 @@ dependencies = [
"parking_lot",
"profiling",
"range-alloc",
"raw-window-handle 0.5.2",
"raw-window-handle",
"renderdoc-sys",
"rustc-hash",
"smallvec",
@ -3116,9 +3194,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winit"
version = "0.29.4"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7"
checksum = "2cc935117ef48caed8822a893efef723e07ad868f668dfc4d244aea8873b07f9"
dependencies = [
"ahash",
"android-activity",
@ -3141,9 +3219,10 @@ dependencies = [
"once_cell",
"orbclient",
"percent-encoding",
"raw-window-handle 0.5.2",
"raw-window-handle",
"redox_syscall 0.3.5",
"rustix",
"sctk-adwaita",
"smithay-client-toolkit",
"smol_str",
"unicode-segmentation",
@ -3157,7 +3236,7 @@ dependencies = [
"web-time",
"windows-sys 0.48.0",
"x11-dl",
"x11rb",
"x11rb 0.13.0",
"xkbcommon-dl",
]
@ -3187,15 +3266,26 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading 0.7.4",
"gethostname 0.3.0",
"nix",
"once_cell",
"winapi",
"winapi-wsapoll",
"x11rb-protocol",
"x11rb-protocol 0.12.0",
]
[[package]]
name = "x11rb"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
dependencies = [
"as-raw-xcb-connection",
"gethostname 0.4.3",
"libc",
"libloading 0.8.1",
"once_cell",
"rustix",
"x11rb-protocol 0.13.0",
]
[[package]]
@ -3207,6 +3297,12 @@ dependencies = [
"nix",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
[[package]]
name = "xcursor"
version = "0.3.5"
@ -3267,18 +3363,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
[[package]]
name = "zerocopy"
version = "0.7.31"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.31"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",

View file

@ -46,6 +46,11 @@ unicode-segmentation = "1.10.1"
# alot = { git = "https://github.com/khonsulabs/alot" }
# kempt = { path = "../objectmap" }
# [patch."https://github.com/khonsulabs/kludgine"]
# kludgine = { path = "../kludgine" }
# [patch."https://github.com/khonsulabs/appit"]
# appit = { path = "../appit" }
[profile.dev.package."*"]
opt-level = 2

View file

@ -2,7 +2,7 @@ use gooey::value::Dynamic;
use gooey::widget::{MakeWidget, WidgetInstance};
use gooey::widgets::container::ContainerShadow;
use gooey::window::ThemeMode;
use gooey::{Gooey, Run};
use gooey::Run;
use kludgine::figures::units::Lp;
use kludgine::figures::Point;
@ -10,7 +10,7 @@ fn main() -> gooey::Result {
let theme_mode = Dynamic::default();
set_of_containers(3, theme_mode.clone())
.centered()
.into_window(Gooey::default())
.into_window()
.themed_mode(theme_mode)
.run()
}

57
examples/multi-window.rs Normal file
View file

@ -0,0 +1,57 @@
use gooey::value::{Dynamic, MapEach};
use gooey::widget::MakeWidget;
use gooey::{Application, Open, PendingApp, Run};
fn main() -> gooey::Result {
let app = PendingApp::default();
let open_windows = Dynamic::new(0_usize);
let counter = Dynamic::new(0_usize);
(&open_windows, &counter)
.map_each(|(open, counter)| {
format!(
"There are {open} other window(s) open. {counter} total windows have been opened"
)
})
.and(open_window_button(&app, &open_windows, &counter))
.into_rows()
.centered()
.open(&app)?;
app.run()
}
fn open_window_button(
app: &impl Application,
open_windows: &Dynamic<usize>,
counter: &Dynamic<usize>,
) -> impl MakeWidget {
let app = app.as_app();
let open_windows = open_windows.clone();
let counter = counter.clone();
"Open Another Window".into_button().on_click(move |()| {
open_another_window(&app, &open_windows, &counter);
})
}
fn open_another_window(
app: &impl Application,
open_windows: &Dynamic<usize>,
counter: &Dynamic<usize>,
) {
let my_number = counter.map_mut(|count| {
*count += 1;
*count
});
let open_windows = open_windows.clone();
open_windows.map_mut(|open_windows| *open_windows += 1);
format!("This is window {my_number}")
.and(open_window_button(app, &open_windows, counter))
.into_rows()
.centered()
.into_window()
.on_close(move || open_windows.map_mut(|open_windows| *open_windows -= 1))
.open(app)
.expect("error opening another window");
}

View file

@ -13,13 +13,13 @@ use gooey::widgets::input::InputValue;
use gooey::widgets::slider::Slidable;
use gooey::widgets::Space;
use gooey::window::ThemeMode;
use gooey::{Gooey, Run};
use gooey::{Open, PendingApp};
use kludgine::figures::units::Lp;
use kludgine::Color;
use palette::OklabHue;
fn main() -> gooey::Result {
let gooey = Gooey::default();
let app = PendingApp::default();
let (theme_mode, theme_switcher) = dark_mode_picker();
@ -79,7 +79,7 @@ fn main() -> gooey::Result {
.and(editors.neutral.1)
.and(editors.neutral_variant.1)
.and("Copy to Clipboard".into_button().on_click({
let gooey = gooey.clone();
let gooey = app.gooey().clone();
move |()| {
if let Some(mut clipboard) = gooey.clipboard_guard() {
let builder = color_scheme_builder.get();
@ -115,9 +115,9 @@ fn main() -> gooey::Result {
.themed(theme)
.pad()
.expand()
.into_window(gooey)
.into_window()
.themed_mode(theme_mode)
.run()
.run_in(app)
}
struct Scheme<Primary, Other = Primary> {

View file

@ -8,7 +8,8 @@ fn main() -> gooey::Result {
let occluded = Dynamic::new(false);
let inner_size = Dynamic::new(Size::default());
let widgets = focused.map_each(|v| format!("focused: {:?}", v))
let widgets = focused
.map_each(|v| format!("focused: {:?}", v))
.and(occluded.map_each(|v| format!("occluded: {:?}", v)))
.and(inner_size.map_each(|v| format!("inner_size: {:?}", v)))
.into_rows()

View file

@ -1,5 +1,5 @@
# gooey-macros
This crate contains procedural macros that [`Gooey`][gooey] exposes.
This crate contains procedural macros that [Gooey][gooey] exposes.
[gooey]: https://github.com/khonsulabs/gooey

View file

@ -1,24 +1,55 @@
use std::sync::{Arc, Mutex, MutexGuard};
use arboard::Clipboard;
use kludgine::app::{AppEvent, AsApplication};
use crate::utils::IgnorePoison;
use crate::window::sealed::WindowCommand;
/// A GUI application.
/// A Gooey application that has not started running yet.
pub struct PendingApp {
app: kludgine::app::PendingApp<WindowCommand>,
gooey: Gooey,
}
impl PendingApp {
/// The shared resources this application utilizes.
pub const fn gooey(&self) -> &Gooey {
&self.gooey
}
}
impl Run for PendingApp {
fn run(self) -> crate::Result {
self.app.run()
}
}
impl Default for PendingApp {
fn default() -> Self {
Self {
app: kludgine::app::PendingApp::default(),
gooey: Gooey {
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
},
}
}
}
impl AsApplication<AppEvent<WindowCommand>> for PendingApp {
fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> {
self.app.as_application()
}
}
/// Shared resources for a GUI application.
#[derive(Clone)]
pub struct Gooey {
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
}
impl Default for Gooey {
fn default() -> Self {
Self {
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
}
}
}
impl Gooey {
/// Returns a locked mutex guard to the OS's clipboard, if one was able to be
/// initialized when the window opened.
@ -29,3 +60,68 @@ impl Gooey {
.map(|mutex| mutex.lock().ignore_poison())
}
}
/// A type that is a Gooey application.
pub trait Application: AsApplication<AppEvent<WindowCommand>> {
/// Returns the shared resources for the application.
fn gooey(&self) -> &Gooey;
/// Returns this type as an [`App`] handle.
fn as_app(&self) -> App;
}
impl Application for PendingApp {
fn gooey(&self) -> &Gooey {
&self.gooey
}
fn as_app(&self) -> App {
App {
app: self.app.as_app(),
gooey: self.gooey.clone(),
}
}
}
/// A handle to a Gooey application.
#[derive(Clone)]
pub struct App {
app: kludgine::app::App<WindowCommand>,
gooey: Gooey,
}
impl Application for App {
fn gooey(&self) -> &Gooey {
&self.gooey
}
fn as_app(&self) -> App {
self.clone()
}
}
impl AsApplication<AppEvent<WindowCommand>> for App {
fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> {
self.app.as_application()
}
}
/// A type that can be run as an application.
pub trait Run: Sized {
/// Runs the provided type, returning `Ok(())` upon successful execution and
/// program exit. Note that this function may not ever return on some
/// platforms.
fn run(self) -> crate::Result;
}
/// A type that can be opened as a window in an application.
pub trait Open: Sized {
/// Opens the provided type as a window inside of `app`.
fn open<App>(self, app: &App) -> crate::Result
where
App: Application;
/// Runs the provided type inside of the pending `app`, returning `Ok(())`
/// upon successful execution and program exit. Note that this function may
/// not ever return on some platforms.
fn run_in(self, app: PendingApp) -> crate::Result;
}

View file

@ -23,7 +23,7 @@ pub mod widgets;
pub mod window;
use std::ops::Sub;
pub use app::Gooey;
pub use app::{App, Application, Gooey, Open, PendingApp, Run};
pub use kludgine;
use kludgine::app::winit::error::EventLoopError;
use kludgine::figures::units::UPx;
@ -117,14 +117,6 @@ impl Sub<UPx> for ConstraintLimit {
/// this crate.
pub type Result<T = (), E = EventLoopError> = std::result::Result<T, E>;
/// A type that can be run as an application.
pub trait Run: Sized {
/// Runs the provided type, returning `Ok(())` upon successful execution and
/// program exit. Note that this function may not ever return on some
/// platforms.
fn run(self) -> crate::Result;
}
/// Counts the number of expressions passed to it.
///
/// This is used inside of Gooey macros to preallocate collections.

View file

@ -18,7 +18,7 @@ use kludgine::figures::units::{Px, UPx};
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, Size};
use kludgine::Color;
use crate::app::Gooey;
use crate::app::{Application, Open, PendingApp, Run};
use crate::context::sealed::WindowHandle;
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
use crate::styles::components::{
@ -44,7 +44,7 @@ use crate::widgets::{
Style, Themed, ThemedMode, Validated, Wrap,
};
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
use crate::{ConstraintLimit, Run};
use crate::ConstraintLimit;
/// A type that makes up a graphical user interface.
///
@ -459,7 +459,24 @@ where
T: MakeWidget,
{
fn run(self) -> crate::Result {
self.make_widget().run()
Window::<WidgetInstance>::new(self.make_widget()).run()
}
}
impl<T> Open for T
where
T: MakeWidget,
{
fn open<App>(self, app: &App) -> crate::Result
where
App: Application,
{
Window::<WidgetInstance>::new(self.make_widget()).open(app)
}
fn run_in(self, app: PendingApp) -> crate::Result {
Window::<WidgetInstance>::new(self.make_widget()).open(&app)?;
app.run()
}
}
@ -900,8 +917,8 @@ pub trait MakeWidget: Sized {
fn make_widget(self) -> WidgetInstance;
/// Returns a new window containing `self` as the root widget.
fn into_window(self, gooey: Gooey) -> Window<WidgetInstance> {
Window::new(self.make_widget(), gooey.clone())
fn into_window(self) -> Window<WidgetInstance> {
Window::new(self.make_widget())
}
/// Associates `styles` with this widget.
@ -1512,11 +1529,6 @@ impl WidgetInstance {
WidgetGuard(self.data.widget.lock().ignore_poison())
}
/// Runs this widget instance as an application.
pub fn run(self) -> crate::Result {
Window::<WidgetInstance>::new(self, Gooey::default()).run()
}
/// Returns the id of the widget that should receive focus after this
/// widget.
///

View file

@ -26,7 +26,7 @@ use kludgine::Kludgine;
use tracing::Level;
use crate::animation::{LinearInterpolate, PercentBetween, ZeroToOne};
use crate::app::Gooey;
use crate::app::{Application, Gooey, Open, PendingApp, Run};
use crate::context::{
AsEventContext, EventContext, Exclusive, GraphicsContext, InvalidationStatus, LayoutContext,
WidgetContext,
@ -37,10 +37,11 @@ use crate::tree::Tree;
use crate::utils::ModifiersExt;
use crate::value::{Dynamic, DynamicReader, Generation, IntoDynamic, IntoValue, Value};
use crate::widget::{
EventHandling, MountedWidget, RootBehavior, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
EventHandling, MountedWidget, OnceCallback, RootBehavior, Widget, WidgetId, WidgetInstance,
HANDLED, IGNORED,
};
use crate::window::sealed::WindowCommand;
use crate::{initialize_tracing, ConstraintLimit, Run};
use crate::{initialize_tracing, ConstraintLimit};
/// A currently running Gooey window.
pub struct RunningWindow<'window> {
@ -125,7 +126,6 @@ where
Behavior: WindowBehavior,
{
context: Behavior::Context,
gooey: Gooey,
/// The attributes of this window.
pub attributes: WindowAttributes,
/// The colors to use to theme the user interface.
@ -152,6 +152,7 @@ where
/// during drawing operations.
pub font_data_to_load: Vec<Vec<u8>>,
on_closed: Option<OnceCallback>,
inner_size: Option<Dynamic<Size<UPx>>>,
occluded: Option<Dynamic<bool>>,
focused: Option<Dynamic<bool>>,
@ -164,8 +165,7 @@ where
Behavior::Context: Default,
{
fn default() -> Self {
let context = Behavior::Context::default();
Self::new(context, Gooey::default())
Self::new(Behavior::Context::default())
}
}
@ -175,7 +175,7 @@ impl Window<WidgetInstance> {
where
W: Widget,
{
Self::new(WidgetInstance::new(widget), Gooey::default())
Self::new(WidgetInstance::new(widget))
}
/// Sets `focused` to be the dynamic updated when this window's focus status
@ -252,6 +252,15 @@ impl Window<WidgetInstance> {
self.font_data_to_load.push(font_data);
self
}
/// Invokes `on_close` when this window is closed.
pub fn on_close<Function>(mut self, on_close: Function) -> Self
where
Function: FnOnce() + Send + 'static,
{
self.on_closed = Some(OnceCallback::new(|()| on_close()));
self
}
}
impl<Behavior> Window<Behavior>
@ -260,7 +269,7 @@ where
{
/// Returns a new instance using `context` to initialize the window upon
/// opening.
pub fn new(context: Behavior::Context, gooey: Gooey) -> Self {
pub fn new(context: Behavior::Context) -> Self {
static EXECUTABLE_NAME: OnceLock<String> = OnceLock::new();
let title = EXECUTABLE_NAME
@ -281,7 +290,7 @@ where
title,
..WindowAttributes::default()
},
gooey,
on_closed: None,
context,
load_system_fonts: true,
theme: Value::default(),
@ -308,25 +317,51 @@ where
{
fn run(self) -> crate::Result {
initialize_tracing();
GooeyWindow::<Behavior>::run_with(sealed::Context {
user: self.context,
settings: RefCell::new(sealed::WindowSettings {
gooey: self.gooey,
transparent: self.attributes.transparent,
attributes: Some(self.attributes),
occluded: self.occluded,
focused: self.focused,
inner_size: self.inner_size,
theme: Some(self.theme),
theme_mode: self.theme_mode,
font_data_to_load: self.font_data_to_load,
serif_font_family: self.serif_font_family,
sans_serif_font_family: self.sans_serif_font_family,
fantasy_font_family: self.fantasy_font_family,
monospace_font_family: self.monospace_font_family,
cursive_font_family: self.cursive_font_family,
}),
})
let app = PendingApp::default();
self.open(&app)?;
app.run()
}
}
impl<Behavior> Open for Window<Behavior>
where
Behavior: WindowBehavior,
{
fn open<App>(self, app: &App) -> crate::Result
where
App: Application,
{
let gooey = app.gooey().clone();
let _handle = GooeyWindow::<Behavior>::open_with(
app,
sealed::Context {
user: self.context,
settings: RefCell::new(sealed::WindowSettings {
gooey,
on_closed: self.on_closed,
transparent: self.attributes.transparent,
attributes: Some(self.attributes),
occluded: self.occluded,
focused: self.focused,
inner_size: self.inner_size,
theme: Some(self.theme),
theme_mode: self.theme_mode,
font_data_to_load: self.font_data_to_load,
serif_font_family: self.serif_font_family,
sans_serif_font_family: self.sans_serif_font_family,
fantasy_font_family: self.fantasy_font_family,
monospace_font_family: self.monospace_font_family,
cursive_font_family: self.cursive_font_family,
}),
},
)?;
Ok(())
}
fn run_in(self, app: PendingApp) -> crate::Result {
self.open(&app)?;
app.run()
}
}
@ -359,7 +394,7 @@ pub trait WindowBehavior: Sized + 'static {
/// Runs this behavior as an application, initialized with `context`.
fn run_with(context: Self::Context) -> crate::Result {
Window::<Self>::new(context, Gooey::default()).run()
Window::<Self>::new(context).run()
}
}
@ -385,6 +420,7 @@ struct GooeyWindow<T> {
transparent: bool,
fonts: FontState,
gooey: Gooey,
on_closed: Option<OnceCallback>,
}
impl<T> GooeyWindow<T>
@ -580,6 +616,7 @@ where
let focused = settings.focused.take().unwrap_or_default();
let theme = settings.theme.take().expect("theme always present");
let inner_size = settings.inner_size.take().unwrap_or_default();
let on_closed = settings.on_closed.take();
inner_size.set(window.inner_size());
@ -676,6 +713,7 @@ where
transparent,
fonts,
gooey,
on_closed,
}
}
@ -1298,6 +1336,14 @@ where
}
}
impl<Behavior> Drop for GooeyWindow<Behavior> {
fn drop(&mut self) {
if let Some(on_closed) = self.on_closed.take() {
on_closed.invoke(());
}
}
}
fn recursively_handle_event(
context: &mut EventContext<'_, '_>,
mut each_widget: impl FnMut(&mut EventContext<'_, '_>) -> EventHandling,
@ -1322,10 +1368,11 @@ pub(crate) mod sealed {
use kludgine::figures::units::UPx;
use kludgine::figures::Size;
use crate::app::Gooey;
use crate::styles::{FontFamilyList, ThemePair};
use crate::value::{Dynamic, Value};
use crate::widget::OnceCallback;
use crate::window::{ThemeMode, WindowAttributes};
use crate::Gooey;
pub struct Context<C> {
pub user: C,
@ -1347,6 +1394,7 @@ pub(crate) mod sealed {
pub monospace_font_family: FontFamilyList,
pub cursive_font_family: FontFamilyList,
pub font_data_to_load: Vec<Vec<u8>>,
pub on_closed: Option<OnceCallback>,
}
#[derive(Clone)]