mirror of
https://github.com/danbulant/cushy
synced 2026-07-06 11:40:37 +00:00
parent
2045d5fb4a
commit
2cec30df31
9 changed files with 1364 additions and 114 deletions
805
Cargo.lock
generated
805
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,13 +14,14 @@ readme = "./README.md"
|
||||||
rust-version = "1.80.0"
|
rust-version = "1.80.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tracing-output", "roboto-flex"]
|
default = ["tracing-output", "roboto-flex", "native-dialogs"]
|
||||||
tracing-output = ["dep:tracing-subscriber"]
|
tracing-output = ["dep:tracing-subscriber"]
|
||||||
roboto-flex = []
|
roboto-flex = []
|
||||||
plotters = ["dep:plotters", "kludgine/plotters"]
|
plotters = ["dep:plotters", "kludgine/plotters"]
|
||||||
tokio = ["dep:tokio"]
|
tokio = ["dep:tokio"]
|
||||||
tokio-multi-thread = ["tokio", "tokio/rt-multi-thread"]
|
tokio-multi-thread = ["tokio", "tokio/rt-multi-thread"]
|
||||||
serde = ["dep:serde", "figures/serde"]
|
serde = ["dep:serde", "figures/serde"]
|
||||||
|
native-dialogs = ["dep:rfd"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
|
kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
|
||||||
|
|
@ -33,6 +34,7 @@ kempt = "0.2.1"
|
||||||
intentional = "0.1.0"
|
intentional = "0.1.0"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tokio = { version = "1.40.0", optional = true, features = ["rt"] }
|
tokio = { version = "1.40.0", optional = true, features = ["rt"] }
|
||||||
|
rfd = { version = "0.15.0", optional = true }
|
||||||
|
|
||||||
tracing-subscriber = { version = "0.3", optional = true, features = [
|
tracing-subscriber = { version = "0.3", optional = true, features = [
|
||||||
"env-filter",
|
"env-filter",
|
||||||
|
|
|
||||||
46
examples/messagebox.rs
Normal file
46
examples/messagebox.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use cushy::dialog::MessageBox;
|
||||||
|
use cushy::widget::MakeWidget;
|
||||||
|
use cushy::widgets::layers::Modal;
|
||||||
|
use cushy::window::PendingWindow;
|
||||||
|
use cushy::{App, Open};
|
||||||
|
|
||||||
|
#[cushy::main]
|
||||||
|
fn main(app: &mut App) -> cushy::Result {
|
||||||
|
let modal = Modal::new();
|
||||||
|
let pending = PendingWindow::default();
|
||||||
|
let window = pending.handle();
|
||||||
|
|
||||||
|
pending
|
||||||
|
.with_root(
|
||||||
|
"Show in Modal layer"
|
||||||
|
.into_button()
|
||||||
|
.on_click({
|
||||||
|
let modal = modal.clone();
|
||||||
|
move |_| {
|
||||||
|
example_message().open(&modal);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and("Show above window".into_button().on_click({
|
||||||
|
move |_| {
|
||||||
|
example_message().open(&window);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.and("Show in app".into_button().on_click({
|
||||||
|
let app = app.clone();
|
||||||
|
move |_| {
|
||||||
|
example_message().open(&app);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.into_rows()
|
||||||
|
.centered()
|
||||||
|
.expand()
|
||||||
|
.and(modal)
|
||||||
|
.into_layers(),
|
||||||
|
)
|
||||||
|
.open(app)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example_message() -> MessageBox {
|
||||||
|
MessageBox::message("This is a dialog").with_explanation("This is some explanation text")
|
||||||
|
}
|
||||||
23
src/app.rs
23
src/app.rs
|
|
@ -5,7 +5,7 @@ use std::thread;
|
||||||
|
|
||||||
use arboard::Clipboard;
|
use arboard::Clipboard;
|
||||||
use kludgine::app::winit::error::EventLoopError;
|
use kludgine::app::winit::error::EventLoopError;
|
||||||
use kludgine::app::{AppEvent, AsApplication, Monitors};
|
use kludgine::app::{AppEvent, AsApplication, ExecutingApp, Monitors};
|
||||||
use parking_lot::{Mutex, MutexGuard};
|
use parking_lot::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use crate::fonts::FontCollection;
|
use crate::fonts::FontCollection;
|
||||||
|
|
@ -392,6 +392,13 @@ pub struct App {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
pub(crate) fn standalone() -> Self {
|
||||||
|
Self {
|
||||||
|
app: None,
|
||||||
|
cushy: Cushy::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a snapshot of information about the monitors connected to this
|
/// Returns a snapshot of information about the monitors connected to this
|
||||||
/// device.
|
/// device.
|
||||||
///
|
///
|
||||||
|
|
@ -414,6 +421,20 @@ impl App {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(kludgine::app::App::prevent_shutdown)
|
.and_then(kludgine::app::App::prevent_shutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes `callback` on the main event loop thread.
|
||||||
|
///
|
||||||
|
/// Returns true if the callback was able to be sent to be executed. The app
|
||||||
|
/// may still terminate before the callback is executed regardless of the
|
||||||
|
/// result of this function. The only way to know with certainty that
|
||||||
|
/// `callback` is executed is to have `callback` notify the caller of its
|
||||||
|
/// completion.
|
||||||
|
pub fn execute<Callback>(&self, callback: Callback) -> bool
|
||||||
|
where
|
||||||
|
Callback: FnOnce(&ExecutingApp<'_, WindowCommand>) + Send + 'static,
|
||||||
|
{
|
||||||
|
self.app.as_ref().map_or(false, |app| app.execute(callback))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A guard preventing an [`App`] from shutting down.
|
/// A guard preventing an [`App`] from shutting down.
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ impl Drop for DebugContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Observable: Send {
|
trait Observable: Send + Sync {
|
||||||
fn label(&self) -> &str;
|
fn label(&self) -> &str;
|
||||||
// fn alive(&self) -> bool;
|
// fn alive(&self) -> bool;
|
||||||
fn widget(&self) -> &WidgetInstance;
|
fn widget(&self) -> &WidgetInstance;
|
||||||
|
|
|
||||||
325
src/dialog.rs
Normal file
325
src/dialog.rs
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
//! Modal dialogs such as message boxes and file pickers.
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::widget::{MakeWidget, SharedCallback};
|
||||||
|
use crate::widgets::layers::Modal;
|
||||||
|
|
||||||
|
#[cfg(feature = "native-dialogs")]
|
||||||
|
mod native;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct MessageButtons {
|
||||||
|
kind: MessageButtonsKind,
|
||||||
|
affirmative: MessageButton,
|
||||||
|
negative: Option<MessageButton>,
|
||||||
|
cancel: Option<MessageButton>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
enum MessageButtonsKind {
|
||||||
|
YesNo,
|
||||||
|
OkCancel,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A button in a [`MessageBox`].
|
||||||
|
///
|
||||||
|
/// This type implements [`From`] for several types:
|
||||||
|
///
|
||||||
|
/// - `String`, `&str`: A button with the string's contents as the caption that
|
||||||
|
/// dismisses the message box.
|
||||||
|
/// - `FnMut()` implementors: A button with the default caption given its
|
||||||
|
/// context that invokes the closure when chosen.
|
||||||
|
///
|
||||||
|
/// To create a button with a custom caption that invokes a closure when chosen,
|
||||||
|
/// use [`MessageButton::custom`].
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct MessageButton {
|
||||||
|
callback: OptionalCallback,
|
||||||
|
caption: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageButton {
|
||||||
|
/// Returns a button with a custom caption that invokes `on_click` when
|
||||||
|
/// selected.
|
||||||
|
pub fn custom<F>(caption: impl Into<String>, mut on_click: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnMut() + Send + 'static,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
callback: OptionalCallback(Some(SharedCallback::new(move |()| on_click()))),
|
||||||
|
caption: caption.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for MessageButton {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self {
|
||||||
|
callback: OptionalCallback::default(),
|
||||||
|
caption: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'_ String> for MessageButton {
|
||||||
|
fn from(value: &'_ String) -> Self {
|
||||||
|
Self::from(value.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'_ str> for MessageButton {
|
||||||
|
fn from(value: &'_ str) -> Self {
|
||||||
|
Self::from(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F> From<F> for MessageButton
|
||||||
|
where
|
||||||
|
F: FnMut() + Send + 'static,
|
||||||
|
{
|
||||||
|
fn from(mut value: F) -> Self {
|
||||||
|
Self {
|
||||||
|
callback: OptionalCallback(Some(SharedCallback::new(move |()| value()))),
|
||||||
|
caption: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
struct OptionalCallback(Option<SharedCallback>);
|
||||||
|
|
||||||
|
impl OptionalCallback {
|
||||||
|
fn invoke(&self) {
|
||||||
|
if let Some(callback) = &self.0 {
|
||||||
|
callback.invoke(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Eq, PartialEq, Copy, Debug)]
|
||||||
|
enum MessageLevel {
|
||||||
|
Error,
|
||||||
|
Warning,
|
||||||
|
#[default]
|
||||||
|
Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A marker indicating a [`MessageBoxBuilder`] does not have a preference
|
||||||
|
/// between a yes/no/cancel or an ok/cancel configuration.
|
||||||
|
pub enum Undecided {}
|
||||||
|
|
||||||
|
/// Specializes a [`MessageBoxBuilder`] for an Ok/Cancel dialog.
|
||||||
|
pub enum OkCancel {}
|
||||||
|
|
||||||
|
/// Specializes a [`MessageBoxBuilder`] for a Yes/No dialog.
|
||||||
|
pub enum YesNoCancel {}
|
||||||
|
|
||||||
|
/// A builder for a [`MessageBox`].
|
||||||
|
#[must_use]
|
||||||
|
pub struct MessageBoxBuilder<Kind>(MessageBox, PhantomData<Kind>);
|
||||||
|
|
||||||
|
impl<Kind> MessageBoxBuilder<Kind> {
|
||||||
|
fn new(message: MessageBox) -> MessageBoxBuilder<Kind> {
|
||||||
|
Self(message, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the explanation text and returns self.
|
||||||
|
pub fn with_explanation(mut self, explanation: impl Into<String>) -> Self {
|
||||||
|
self.0.description = explanation.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays this message as a warning.
|
||||||
|
///
|
||||||
|
/// When using native dialogs, not all platforms support this stylization.
|
||||||
|
pub fn warning(mut self) -> Self {
|
||||||
|
self.0.level = MessageLevel::Warning;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays this message as an error.
|
||||||
|
///
|
||||||
|
/// When using native dialogs, not all platforms support this stylization.
|
||||||
|
pub fn error(mut self) -> Self {
|
||||||
|
self.0.level = MessageLevel::Error;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a cancel button and returns self.
|
||||||
|
pub fn with_cancel(mut self, cancel: impl Into<MessageButton>) -> Self {
|
||||||
|
self.0.buttons.cancel = Some(cancel.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the completed message box.
|
||||||
|
#[must_use]
|
||||||
|
pub fn finish(self) -> MessageBox {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBoxBuilder<Undecided> {
|
||||||
|
/// Sets the yes button and returns self.
|
||||||
|
pub fn with_yes(
|
||||||
|
Self(mut message, _): Self,
|
||||||
|
yes: impl Into<MessageButton>,
|
||||||
|
) -> MessageBoxBuilder<YesNoCancel> {
|
||||||
|
message.buttons.kind = MessageButtonsKind::YesNo;
|
||||||
|
message.buttons.affirmative = yes.into();
|
||||||
|
MessageBoxBuilder(message, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the ok button and returns self.
|
||||||
|
pub fn with_ok(
|
||||||
|
Self(mut message, _): Self,
|
||||||
|
ok: impl Into<MessageButton>,
|
||||||
|
) -> MessageBoxBuilder<OkCancel> {
|
||||||
|
message.buttons.affirmative = ok.into();
|
||||||
|
MessageBoxBuilder(message, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBoxBuilder<YesNoCancel> {
|
||||||
|
/// Sets the no button and returns self.
|
||||||
|
pub fn with_no(mut self, no: impl Into<MessageButton>) -> Self {
|
||||||
|
self.0.buttons.negative = Some(no.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBoxBuilder<OkCancel> {}
|
||||||
|
|
||||||
|
/// A dialog that displays a message.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MessageBox {
|
||||||
|
level: MessageLevel,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
buttons: MessageButtons,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBox {
|
||||||
|
fn new(title: String, kind: MessageButtonsKind) -> Self {
|
||||||
|
Self {
|
||||||
|
level: MessageLevel::default(),
|
||||||
|
title,
|
||||||
|
description: String::default(),
|
||||||
|
buttons: MessageButtons {
|
||||||
|
kind,
|
||||||
|
affirmative: MessageButton::default(),
|
||||||
|
negative: None,
|
||||||
|
cancel: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a builder for a dialog displaying `message`.
|
||||||
|
pub fn build(message: impl Into<String>) -> MessageBoxBuilder<Undecided> {
|
||||||
|
MessageBoxBuilder::new(Self::new(message.into(), MessageButtonsKind::OkCancel))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a dialog displaying `message` with an `OK` button that dismisses
|
||||||
|
/// the dialog.
|
||||||
|
#[must_use]
|
||||||
|
pub fn message(message: impl Into<String>) -> Self {
|
||||||
|
Self::build(message).finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the explanation text and returns self.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_explanation(mut self, explanation: impl Into<String>) -> Self {
|
||||||
|
self.description = explanation.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays this message as a warning.
|
||||||
|
///
|
||||||
|
/// When using native dialogs, not all platforms support this stylization.
|
||||||
|
#[must_use]
|
||||||
|
pub fn warning(mut self) -> Self {
|
||||||
|
self.level = MessageLevel::Warning;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays this message as an error.
|
||||||
|
///
|
||||||
|
/// When using native dialogs, not all platforms support this stylization.
|
||||||
|
#[must_use]
|
||||||
|
pub fn error(mut self) -> Self {
|
||||||
|
self.level = MessageLevel::Error;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a cancel button and returns self.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_cancel(mut self, cancel: impl Into<MessageButton>) -> Self {
|
||||||
|
self.buttons.cancel = Some(cancel.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens this dialog in the given target.
|
||||||
|
///
|
||||||
|
/// A target can be a [`Modal`] layer, a [`WindowHandle`], or an [`App`].
|
||||||
|
pub fn open(&self, open_in: &impl OpenMessageBox) {
|
||||||
|
open_in.open_message_box(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that can open a [`MessageBox`] as a modal dialog.
|
||||||
|
pub trait OpenMessageBox {
|
||||||
|
/// Opens `message` as a modal dialog.
|
||||||
|
fn open_message_box(&self, message: &MessageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coalesce_empty<'a>(s1: &'a str, s2: &'a str) -> &'a str {
|
||||||
|
if s1.is_empty() {
|
||||||
|
s2
|
||||||
|
} else {
|
||||||
|
s1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenMessageBox for Modal {
|
||||||
|
fn open_message_box(&self, message: &MessageBox) {
|
||||||
|
let dialog = self.build_dialog(
|
||||||
|
message
|
||||||
|
.title
|
||||||
|
.as_str()
|
||||||
|
.h5()
|
||||||
|
.and(message.description.as_str())
|
||||||
|
.into_rows(),
|
||||||
|
);
|
||||||
|
let (default_affirmative, default_negative) = match &message.buttons.kind {
|
||||||
|
MessageButtonsKind::OkCancel => ("OK", None),
|
||||||
|
MessageButtonsKind::YesNo => ("Yes", Some("No")),
|
||||||
|
};
|
||||||
|
let on_ok = message.buttons.affirmative.callback.clone();
|
||||||
|
let mut dialog = dialog.with_default_button(
|
||||||
|
coalesce_empty(&message.buttons.affirmative.caption, default_affirmative),
|
||||||
|
move || on_ok.invoke(),
|
||||||
|
);
|
||||||
|
if let (Some(negative), Some(default_negative)) =
|
||||||
|
(&message.buttons.negative, default_negative)
|
||||||
|
{
|
||||||
|
let on_negative = negative.callback.clone();
|
||||||
|
dialog = dialog.with_button(
|
||||||
|
coalesce_empty(&negative.caption, default_negative),
|
||||||
|
move || {
|
||||||
|
on_negative.invoke();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cancel) = &message.buttons.cancel {
|
||||||
|
let on_cancel = cancel.callback.clone();
|
||||||
|
dialog
|
||||||
|
.with_cancel_button(coalesce_empty(&cancel.caption, "Cancel"), move || {
|
||||||
|
on_cancel.invoke();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
175
src/dialog/native.rs
Normal file
175
src/dialog/native.rs
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use rfd::{MessageDialog, MessageDialogResult};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
coalesce_empty, MessageBox, MessageButtons, MessageButtonsKind, MessageLevel, OpenMessageBox,
|
||||||
|
};
|
||||||
|
use crate::window::WindowHandle;
|
||||||
|
use crate::App;
|
||||||
|
|
||||||
|
impl MessageButtons {
|
||||||
|
fn as_rfd_buttons(&self) -> rfd::MessageButtons {
|
||||||
|
let cancel_is_custom = self
|
||||||
|
.cancel
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |b| !b.caption.is_empty());
|
||||||
|
match self.kind {
|
||||||
|
MessageButtonsKind::YesNo => {
|
||||||
|
let negative = self.negative.as_ref().expect("no button");
|
||||||
|
if cancel_is_custom
|
||||||
|
|| !self.affirmative.caption.is_empty()
|
||||||
|
|| !negative.caption.is_empty()
|
||||||
|
{
|
||||||
|
if let Some(cancel) = &self.cancel {
|
||||||
|
rfd::MessageButtons::YesNoCancelCustom(
|
||||||
|
coalesce_empty(&self.affirmative.caption, "Yes").to_string(),
|
||||||
|
coalesce_empty(&negative.caption, "No").to_string(),
|
||||||
|
coalesce_empty(&cancel.caption, "Yes").to_string(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
rfd::MessageButtons::OkCancelCustom(
|
||||||
|
coalesce_empty(&self.affirmative.caption, "Yes").to_string(),
|
||||||
|
coalesce_empty(&negative.caption, "No").to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if self.cancel.is_some() {
|
||||||
|
rfd::MessageButtons::YesNoCancel
|
||||||
|
} else {
|
||||||
|
rfd::MessageButtons::YesNo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageButtonsKind::OkCancel => {
|
||||||
|
if let Some(cancel) = &self.cancel {
|
||||||
|
if !self.affirmative.caption.is_empty() || !cancel.caption.is_empty() {
|
||||||
|
rfd::MessageButtons::OkCancelCustom(
|
||||||
|
coalesce_empty(&self.affirmative.caption, "OK").to_string(),
|
||||||
|
coalesce_empty(&cancel.caption, "Cancel").to_string(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
rfd::MessageButtons::OkCancel
|
||||||
|
}
|
||||||
|
} else if !self.affirmative.caption.is_empty() {
|
||||||
|
rfd::MessageButtons::OkCustom(self.affirmative.caption.clone())
|
||||||
|
} else {
|
||||||
|
rfd::MessageButtons::Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MessageLevel> for rfd::MessageLevel {
|
||||||
|
fn from(value: MessageLevel) -> Self {
|
||||||
|
match value {
|
||||||
|
MessageLevel::Error => rfd::MessageLevel::Error,
|
||||||
|
MessageLevel::Warning => rfd::MessageLevel::Warning,
|
||||||
|
MessageLevel::Info => rfd::MessageLevel::Info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenMessageBox for WindowHandle {
|
||||||
|
fn open_message_box(&self, message: &MessageBox) {
|
||||||
|
let message = message.clone();
|
||||||
|
self.execute(move |context| {
|
||||||
|
// Get access to the winit handle from the window thread.
|
||||||
|
let winit = context.winit().cloned();
|
||||||
|
// We can't utilize the window handle outside of the main thread
|
||||||
|
// with winit, so we now need to move execution to the event loop
|
||||||
|
// thread.
|
||||||
|
let Some(app) = context.app().cloned() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
app.execute(move |_app| {
|
||||||
|
let mut dialog = MessageDialog::new()
|
||||||
|
.set_title(message.title)
|
||||||
|
.set_buttons(message.buttons.as_rfd_buttons())
|
||||||
|
.set_description(message.description)
|
||||||
|
.set_level(message.level.into());
|
||||||
|
if let Some(winit) = winit {
|
||||||
|
dialog = dialog.set_parent(&winit);
|
||||||
|
}
|
||||||
|
thread::spawn(move || {
|
||||||
|
handle_message_result(&dialog.show(), &message.buttons);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenMessageBox for App {
|
||||||
|
fn open_message_box(&self, message: &MessageBox) {
|
||||||
|
let shutdown_guard = self.prevent_shutdown();
|
||||||
|
let message = message.clone();
|
||||||
|
self.execute(move |_app| {
|
||||||
|
let dialog = MessageDialog::new()
|
||||||
|
.set_title(message.title)
|
||||||
|
.set_buttons(message.buttons.as_rfd_buttons())
|
||||||
|
.set_description(message.description)
|
||||||
|
.set_level(message.level.into());
|
||||||
|
thread::spawn(move || {
|
||||||
|
handle_message_result(&dialog.show(), &message.buttons);
|
||||||
|
drop(shutdown_guard);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message_result(result: &MessageDialogResult, buttons: &MessageButtons) {
|
||||||
|
match result {
|
||||||
|
MessageDialogResult::Ok | MessageDialogResult::Yes => {
|
||||||
|
buttons.affirmative.callback.invoke();
|
||||||
|
}
|
||||||
|
MessageDialogResult::No => {
|
||||||
|
buttons
|
||||||
|
.negative
|
||||||
|
.as_ref()
|
||||||
|
.expect("no button")
|
||||||
|
.callback
|
||||||
|
.invoke();
|
||||||
|
}
|
||||||
|
MessageDialogResult::Cancel => {
|
||||||
|
if matches!(buttons.kind, MessageButtonsKind::YesNo) && buttons.cancel.is_none() {
|
||||||
|
// Cancel means No in this situation.
|
||||||
|
buttons
|
||||||
|
.negative
|
||||||
|
.as_ref()
|
||||||
|
.expect("no button")
|
||||||
|
.callback
|
||||||
|
.invoke();
|
||||||
|
} else {
|
||||||
|
buttons
|
||||||
|
.cancel
|
||||||
|
.as_ref()
|
||||||
|
.expect("cancel button")
|
||||||
|
.callback
|
||||||
|
.invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageDialogResult::Custom(caption) => {
|
||||||
|
let (default_affirmative, default_negative) = match buttons.kind {
|
||||||
|
MessageButtonsKind::YesNo => ("Yes", Some("No")),
|
||||||
|
MessageButtonsKind::OkCancel => ("OK", None),
|
||||||
|
};
|
||||||
|
|
||||||
|
if coalesce_empty(&buttons.affirmative.caption, default_affirmative) == caption {
|
||||||
|
buttons.affirmative.callback.invoke();
|
||||||
|
} else if let Some(negative) = buttons.negative.as_ref().filter(|negative| {
|
||||||
|
&negative.caption == caption
|
||||||
|
|| default_negative
|
||||||
|
.map_or(false, |default_negative| default_negative == caption)
|
||||||
|
}) {
|
||||||
|
negative.callback.invoke();
|
||||||
|
} else if let Some(cancel) = buttons
|
||||||
|
.cancel
|
||||||
|
.as_ref()
|
||||||
|
.filter(|cancel| coalesce_empty(&cancel.caption, "Cancel") == caption)
|
||||||
|
{
|
||||||
|
cancel.callback.invoke();
|
||||||
|
} else {
|
||||||
|
unreachable!("no matching button")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ pub mod widget;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
|
||||||
|
pub mod dialog;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod example;
|
pub mod example;
|
||||||
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ pub trait PlatformWindowImplementation {
|
||||||
/// Marks the window to close as soon as possible.
|
/// Marks the window to close as soon as possible.
|
||||||
fn close(&mut self);
|
fn close(&mut self);
|
||||||
/// Returns the underlying `winit` window, if one exists.
|
/// Returns the underlying `winit` window, if one exists.
|
||||||
fn winit(&self) -> Option<&winit::window::Window>;
|
fn winit(&self) -> Option<&Arc<winit::window::Window>>;
|
||||||
/// Sets the window to redraw as soon as possible.
|
/// Sets the window to redraw as soon as possible.
|
||||||
fn set_needs_redraw(&mut self);
|
fn set_needs_redraw(&mut self);
|
||||||
/// Sets the window to redraw after a `duration`.
|
/// Sets the window to redraw after a `duration`.
|
||||||
|
|
@ -114,8 +114,7 @@ pub trait PlatformWindowImplementation {
|
||||||
/// [`winit::window::Window::is_resizable`], or true if this window has no
|
/// [`winit::window::Window::is_resizable`], or true if this window has no
|
||||||
/// winit window.
|
/// winit window.
|
||||||
fn is_resizable(&self) -> bool {
|
fn is_resizable(&self) -> bool {
|
||||||
self.winit()
|
self.winit().map_or(true, |win| win.is_resizable())
|
||||||
.map_or(true, winit::window::Window::is_resizable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the window can have its size changed.
|
/// Returns true if the window can have its size changed.
|
||||||
|
|
@ -124,7 +123,7 @@ pub trait PlatformWindowImplementation {
|
||||||
/// dark if this window has no winit window.
|
/// dark if this window has no winit window.
|
||||||
fn theme(&self) -> winit::window::Theme {
|
fn theme(&self) -> winit::window::Theme {
|
||||||
self.winit()
|
self.winit()
|
||||||
.and_then(winit::window::Window::theme)
|
.and_then(|win| win.theme())
|
||||||
.unwrap_or(winit::window::Theme::Dark)
|
.unwrap_or(winit::window::Theme::Dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +207,7 @@ impl PlatformWindowImplementation for kludgine::app::Window<'_, WindowCommand> {
|
||||||
self.close();
|
self.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn winit(&self) -> Option<&winit::window::Window> {
|
fn winit(&self) -> Option<&Arc<winit::window::Window>> {
|
||||||
Some(self.winit())
|
Some(self.winit())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,6 +259,8 @@ pub trait PlatformWindow {
|
||||||
fn outer_size(&self) -> Size<UPx>;
|
fn outer_size(&self) -> Size<UPx>;
|
||||||
/// Returns the shared application resources.
|
/// Returns the shared application resources.
|
||||||
fn cushy(&self) -> &Cushy;
|
fn cushy(&self) -> &Cushy;
|
||||||
|
/// Returns the app managing this window's event loop.
|
||||||
|
fn app(&self) -> Option<&App>;
|
||||||
/// Sets the window to redraw as soon as possible.
|
/// Sets the window to redraw as soon as possible.
|
||||||
fn set_needs_redraw(&mut self);
|
fn set_needs_redraw(&mut self);
|
||||||
/// Sets the window to redraw after a `duration`.
|
/// Sets the window to redraw after a `duration`.
|
||||||
|
|
@ -289,7 +290,7 @@ pub trait PlatformWindow {
|
||||||
fn set_max_inner_size(&self, max_size: Option<Size<UPx>>);
|
fn set_max_inner_size(&self, max_size: Option<Size<UPx>>);
|
||||||
|
|
||||||
/// Returns a handle to the underlying winit window, if available.
|
/// Returns a handle to the underlying winit window, if available.
|
||||||
fn winit(&self) -> Option<&winit::window::Window>;
|
fn winit(&self) -> Option<&Arc<winit::window::Window>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A currently running Cushy window.
|
/// A currently running Cushy window.
|
||||||
|
|
@ -297,7 +298,7 @@ pub struct RunningWindow<W> {
|
||||||
window: W,
|
window: W,
|
||||||
kludgine_id: KludgineId,
|
kludgine_id: KludgineId,
|
||||||
invalidation_status: InvalidationStatus,
|
invalidation_status: InvalidationStatus,
|
||||||
cushy: Cushy,
|
app: App,
|
||||||
focused: Dynamic<bool>,
|
focused: Dynamic<bool>,
|
||||||
occluded: Dynamic<bool>,
|
occluded: Dynamic<bool>,
|
||||||
inner_size: Dynamic<Size<UPx>>,
|
inner_size: Dynamic<Size<UPx>>,
|
||||||
|
|
@ -313,7 +314,7 @@ where
|
||||||
window: W,
|
window: W,
|
||||||
kludgine_id: KludgineId,
|
kludgine_id: KludgineId,
|
||||||
invalidation_status: &InvalidationStatus,
|
invalidation_status: &InvalidationStatus,
|
||||||
cushy: &Cushy,
|
app: &App,
|
||||||
focused: &Dynamic<bool>,
|
focused: &Dynamic<bool>,
|
||||||
occluded: &Dynamic<bool>,
|
occluded: &Dynamic<bool>,
|
||||||
inner_size: &Dynamic<Size<UPx>>,
|
inner_size: &Dynamic<Size<UPx>>,
|
||||||
|
|
@ -323,7 +324,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine_id,
|
kludgine_id,
|
||||||
invalidation_status: invalidation_status.clone(),
|
invalidation_status: invalidation_status.clone(),
|
||||||
cushy: cushy.clone(),
|
app: app.clone(),
|
||||||
focused: focused.clone(),
|
focused: focused.clone(),
|
||||||
occluded: occluded.clone(),
|
occluded: occluded.clone(),
|
||||||
inner_size: inner_size.clone(),
|
inner_size: inner_size.clone(),
|
||||||
|
|
@ -382,7 +383,7 @@ where
|
||||||
/// initialized when the window opened.
|
/// initialized when the window opened.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
|
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
|
||||||
self.cushy.clipboard_guard()
|
self.app.cushy().clipboard_guard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -422,6 +423,10 @@ where
|
||||||
self.kludgine_id
|
self.kludgine_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn app(&self) -> Option<&App> {
|
||||||
|
Some(&self.app)
|
||||||
|
}
|
||||||
|
|
||||||
fn focused(&self) -> &Dynamic<bool> {
|
fn focused(&self) -> &Dynamic<bool> {
|
||||||
&self.focused
|
&self.focused
|
||||||
}
|
}
|
||||||
|
|
@ -439,7 +444,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cushy(&self) -> &Cushy {
|
fn cushy(&self) -> &Cushy {
|
||||||
&self.cushy
|
self.app.cushy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_needs_redraw(&mut self) {
|
fn set_needs_redraw(&mut self) {
|
||||||
|
|
@ -490,7 +495,7 @@ where
|
||||||
self.window.set_ime_location(location);
|
self.window.set_ime_location(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn winit(&self) -> Option<&winit::window::Window> {
|
fn winit(&self) -> Option<&Arc<winit::window::Window>> {
|
||||||
self.window.winit()
|
self.window.winit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1121,14 +1126,14 @@ where
|
||||||
App: Application + ?Sized,
|
App: Application + ?Sized,
|
||||||
{
|
{
|
||||||
let this = self.make_window();
|
let this = self.make_window();
|
||||||
let cushy = app.cushy().clone();
|
let app_app = app.as_app();
|
||||||
let handle = this.pending.handle();
|
let handle = this.pending.handle();
|
||||||
OpenWindow::<T::Behavior>::open_with(
|
OpenWindow::<T::Behavior>::open_with(
|
||||||
app,
|
app,
|
||||||
sealed::Context {
|
sealed::Context {
|
||||||
user: this.context,
|
user: this.context,
|
||||||
settings: RefCell::new(sealed::WindowSettings {
|
settings: RefCell::new(sealed::WindowSettings {
|
||||||
cushy,
|
app: app_app,
|
||||||
title: this.title,
|
title: this.title,
|
||||||
redraw_status: this.pending.0.redraw_status.clone(),
|
redraw_status: this.pending.0.redraw_status.clone(),
|
||||||
on_open: this.on_open,
|
on_open: this.on_open,
|
||||||
|
|
@ -1348,7 +1353,7 @@ struct OpenWindow<T> {
|
||||||
theme_mode: Value<ThemeMode>,
|
theme_mode: Value<ThemeMode>,
|
||||||
transparent: bool,
|
transparent: bool,
|
||||||
fonts: FontState,
|
fonts: FontState,
|
||||||
cushy: Cushy,
|
app: App,
|
||||||
on_closed: Option<OnceCallback>,
|
on_closed: Option<OnceCallback>,
|
||||||
vsync: bool,
|
vsync: bool,
|
||||||
dpi_scale: Dynamic<Fraction>,
|
dpi_scale: Dynamic<Fraction>,
|
||||||
|
|
@ -1729,10 +1734,10 @@ where
|
||||||
.persist();
|
.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cushy = settings.cushy.clone();
|
let app = settings.app.clone();
|
||||||
let fonts = Self::load_fonts(
|
let fonts = Self::load_fonts(
|
||||||
&mut settings,
|
&mut settings,
|
||||||
cushy.fonts.clone(),
|
app.cushy().fonts.clone(),
|
||||||
graphics.font_system().db_mut(),
|
graphics.font_system().db_mut(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1788,7 +1793,7 @@ where
|
||||||
theme_mode,
|
theme_mode,
|
||||||
transparent: settings.transparent,
|
transparent: settings.transparent,
|
||||||
fonts,
|
fonts,
|
||||||
cushy,
|
app,
|
||||||
on_closed: settings.on_closed,
|
on_closed: settings.on_closed,
|
||||||
vsync: settings.vsync,
|
vsync: settings.vsync,
|
||||||
close_requested: settings.close_requested,
|
close_requested: settings.close_requested,
|
||||||
|
|
@ -1847,7 +1852,7 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
|
|
||||||
self.synchronize_platform_window(&mut window);
|
self.synchronize_platform_window(&mut window);
|
||||||
|
|
@ -1859,7 +1864,7 @@ where
|
||||||
window,
|
window,
|
||||||
graphics.id(),
|
graphics.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -1961,13 +1966,13 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
if self.behavior.close_requested(&mut RunningWindow::new(
|
if self.behavior.close_requested(&mut RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2108,13 +2113,13 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2167,13 +2172,13 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2211,13 +2216,13 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2256,13 +2261,13 @@ where
|
||||||
) where
|
) where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2315,7 +2320,7 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
self.cursor.location = None;
|
self.cursor.location = None;
|
||||||
self.cursor_position
|
self.cursor_position
|
||||||
|
|
@ -2325,7 +2330,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2358,13 +2363,13 @@ where
|
||||||
where
|
where
|
||||||
W: PlatformWindowImplementation,
|
W: PlatformWindowImplementation,
|
||||||
{
|
{
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2500,13 +2505,13 @@ where
|
||||||
) -> Self {
|
) -> Self {
|
||||||
context.pending.opened(window.handle());
|
context.pending.opened(window.handle());
|
||||||
let settings = context.settings.borrow();
|
let settings = context.settings.borrow();
|
||||||
let cushy = settings.cushy.clone();
|
let cushy = settings.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
let mut window = RunningWindow::new(
|
let mut window = RunningWindow::new(
|
||||||
window,
|
window,
|
||||||
graphics.id(),
|
graphics.id(),
|
||||||
&settings.redraw_status,
|
&settings.redraw_status,
|
||||||
&settings.cushy,
|
&settings.app,
|
||||||
&settings.focused,
|
&settings.focused,
|
||||||
&settings.occluded,
|
&settings.occluded,
|
||||||
&settings.inner_size,
|
&settings.inner_size,
|
||||||
|
|
@ -2528,7 +2533,7 @@ where
|
||||||
window: kludgine::app::Window<'_, WindowCommand>,
|
window: kludgine::app::Window<'_, WindowCommand>,
|
||||||
kludgine: &mut Kludgine,
|
kludgine: &mut Kludgine,
|
||||||
) {
|
) {
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
self.focused.set(window.focused());
|
self.focused.set(window.focused());
|
||||||
self.occluded.set(window.occluded());
|
self.occluded.set(window.occluded());
|
||||||
|
|
@ -2539,7 +2544,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2618,7 +2623,7 @@ where
|
||||||
window: kludgine::app::Window<'_, WindowCommand>,
|
window: kludgine::app::Window<'_, WindowCommand>,
|
||||||
kludgine: &mut Kludgine,
|
kludgine: &mut Kludgine,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let cushy = self.cushy.clone();
|
let cushy = self.app.cushy().clone();
|
||||||
let _guard = cushy.enter_runtime();
|
let _guard = cushy.enter_runtime();
|
||||||
Self::request_close(
|
Self::request_close(
|
||||||
&mut self.should_close,
|
&mut self.should_close,
|
||||||
|
|
@ -2627,7 +2632,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2815,7 +2820,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2859,7 +2864,7 @@ where
|
||||||
window,
|
window,
|
||||||
kludgine.id(),
|
kludgine.id(),
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.cushy,
|
&self.app,
|
||||||
&self.focused,
|
&self.focused,
|
||||||
&self.occluded,
|
&self.occluded,
|
||||||
self.inner_size.source(),
|
self.inner_size.source(),
|
||||||
|
|
@ -2959,7 +2964,6 @@ pub(crate) mod sealed {
|
||||||
use kludgine::app::winit::window::{Fullscreen, UserAttentionType, WindowButtons, WindowLevel};
|
use kludgine::app::winit::window::{Fullscreen, UserAttentionType, WindowButtons, WindowLevel};
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
use crate::app::Cushy;
|
|
||||||
use crate::context::sealed::InvalidationStatus;
|
use crate::context::sealed::InvalidationStatus;
|
||||||
use crate::context::EventContext;
|
use crate::context::EventContext;
|
||||||
use crate::fonts::FontCollection;
|
use crate::fonts::FontCollection;
|
||||||
|
|
@ -2968,6 +2972,7 @@ pub(crate) mod sealed {
|
||||||
use crate::widget::{Callback, OnceCallback, SharedCallback};
|
use crate::widget::{Callback, OnceCallback, SharedCallback};
|
||||||
use crate::widgets::shortcuts::ShortcutMap;
|
use crate::widgets::shortcuts::ShortcutMap;
|
||||||
use crate::window::{FileDrop, PendingWindow, ThemeMode, WindowAttributes, WindowHandle};
|
use crate::window::{FileDrop, PendingWindow, ThemeMode, WindowAttributes, WindowHandle};
|
||||||
|
use crate::App;
|
||||||
|
|
||||||
pub struct Context<C> {
|
pub struct Context<C> {
|
||||||
pub user: C,
|
pub user: C,
|
||||||
|
|
@ -2976,7 +2981,7 @@ pub(crate) mod sealed {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WindowSettings {
|
pub struct WindowSettings {
|
||||||
pub cushy: Cushy,
|
pub app: App,
|
||||||
pub redraw_status: InvalidationStatus,
|
pub redraw_status: InvalidationStatus,
|
||||||
pub title: Value<String>,
|
pub title: Value<String>,
|
||||||
pub attributes: Option<WindowAttributes>,
|
pub attributes: Option<WindowAttributes>,
|
||||||
|
|
@ -3516,7 +3521,7 @@ impl PlatformWindowImplementation for &mut VirtualState {
|
||||||
self.closed = true;
|
self.closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn winit(&self) -> Option<&winit::window::Window> {
|
fn winit(&self) -> Option<&Arc<winit::window::Window>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3659,7 +3664,7 @@ impl StandaloneWindowBuilder {
|
||||||
window,
|
window,
|
||||||
&mut kludgine::Graphics::new(&mut kludgine, device, queue),
|
&mut kludgine::Graphics::new(&mut kludgine, device, queue),
|
||||||
sealed::WindowSettings {
|
sealed::WindowSettings {
|
||||||
cushy: Cushy::default(),
|
app: App::standalone(),
|
||||||
redraw_status: InvalidationStatus::default(),
|
redraw_status: InvalidationStatus::default(),
|
||||||
title: Value::default(),
|
title: Value::default(),
|
||||||
attributes: None,
|
attributes: None,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue