mirror of
https://github.com/danbulant/cushy
synced 2026-06-24 17:12:11 +00:00
Modal DialogBuilder
This commit is contained in:
parent
2f387c9845
commit
444fbbe4ed
3 changed files with 172 additions and 15 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -146,9 +146,9 @@ checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
version = "3.4.0"
|
version = "3.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89"
|
checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clipboard-win",
|
"clipboard-win",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,10 @@ fn main() -> cushy::Result {
|
||||||
.into_button()
|
.into_button()
|
||||||
.on_click({
|
.on_click({
|
||||||
let modal = modal.clone();
|
let modal = modal.clone();
|
||||||
move |_| {
|
move |_| modal.message("This is a modal", "Dismiss")
|
||||||
modal.present(dialog(&modal));
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.align_top()
|
.align_top()
|
||||||
.and(modal)
|
.and(modal)
|
||||||
.into_layers()
|
.into_layers()
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dialog(modal: &Modal) -> impl MakeWidget {
|
|
||||||
let modal = modal.clone();
|
|
||||||
"This is a modal"
|
|
||||||
.and("Dismiss".into_button().on_click(move |_| {
|
|
||||||
modal.dismiss();
|
|
||||||
}))
|
|
||||||
.into_rows()
|
|
||||||
.contain()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Widgets that stack in the Z-direction.
|
//! Widgets that stack in the Z-direction.
|
||||||
|
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use alot::{LotId, OrderedLots};
|
use alot::{LotId, OrderedLots};
|
||||||
|
|
@ -908,6 +909,19 @@ impl Modal {
|
||||||
self.modal.set(Some(contents.make_widget()));
|
self.modal.set(Some(contents.make_widget()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Presents a modal dialog containing `message` with a default button that
|
||||||
|
/// dismisses the dialog.
|
||||||
|
pub fn message(&self, message: impl MakeWidget, button_caption: impl MakeWidget) {
|
||||||
|
self.build_dialog(message)
|
||||||
|
.with_default_button(button_caption, || {})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a builder for a modal dialog that displays `message`.
|
||||||
|
pub fn build_dialog(&self, message: impl MakeWidget) -> DialogBuilder {
|
||||||
|
DialogBuilder::new(self, message)
|
||||||
|
}
|
||||||
|
|
||||||
/// Dismisses the modal session.
|
/// Dismisses the modal session.
|
||||||
pub fn dismiss(&self) {
|
pub fn dismiss(&self) {
|
||||||
self.modal.set(None);
|
self.modal.set(None);
|
||||||
|
|
@ -918,6 +932,18 @@ impl Modal {
|
||||||
pub fn visible(&self) -> bool {
|
pub fn visible(&self) -> bool {
|
||||||
self.modal.map_ref(Option::is_some)
|
self.modal.map_ref(Option::is_some)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a function that dismisses the modal when invoked.
|
||||||
|
///
|
||||||
|
/// The input to the function is ignored. This function takes a single
|
||||||
|
/// argument so that it is compatible with widgets that use a [`Callback`]
|
||||||
|
/// for their events.
|
||||||
|
pub fn dismiss_callback<T>(&self) -> impl FnMut(T) + Send + 'static {
|
||||||
|
let modal = self.clone();
|
||||||
|
move |_| {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MakeWidget for Modal {
|
impl MakeWidget for Modal {
|
||||||
|
|
@ -983,3 +1009,146 @@ impl Widget for ModalLayer {
|
||||||
self.presented.is_some()
|
self.presented.is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A marker type indicating a special [`DialogBuilder`] button type is not
|
||||||
|
/// present.
|
||||||
|
pub enum No {}
|
||||||
|
|
||||||
|
/// A marker type indicating a special [`DialogBuilder`] button type is present.
|
||||||
|
pub enum Yes {}
|
||||||
|
|
||||||
|
/// A modal dialog builder.
|
||||||
|
#[must_use = "DialogBuilder::show must be called for the dialog to be shown"]
|
||||||
|
pub struct DialogBuilder<HasDefault = No, HasCancel = No> {
|
||||||
|
modal: Modal,
|
||||||
|
message: WidgetInstance,
|
||||||
|
buttons: WidgetList,
|
||||||
|
_state: PhantomData<(HasDefault, HasCancel)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DialogBuilder<No, No> {
|
||||||
|
fn new(modal: &Modal, message: impl MakeWidget) -> Self {
|
||||||
|
Self {
|
||||||
|
modal: modal.clone(),
|
||||||
|
message: message.make_widget(),
|
||||||
|
buttons: WidgetList::new(),
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HasDefault, HasCancel> DialogBuilder<HasDefault, HasCancel> {
|
||||||
|
/// Adds a button with `caption` that invokes `on_click` when activated.
|
||||||
|
/// Returns self.
|
||||||
|
pub fn with_button(
|
||||||
|
mut self,
|
||||||
|
caption: impl MakeWidget,
|
||||||
|
on_click: impl FnOnce() + Send + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.push_button(caption, on_click);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a button with `caption` that invokes `on_click` when activated.
|
||||||
|
pub fn push_button(
|
||||||
|
&mut self,
|
||||||
|
caption: impl MakeWidget,
|
||||||
|
on_click: impl FnOnce() + Send + 'static,
|
||||||
|
) {
|
||||||
|
self.inner_push_button(caption, DialogButtonKind::Plain, on_click);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner_push_button(
|
||||||
|
&mut self,
|
||||||
|
caption: impl MakeWidget,
|
||||||
|
kind: DialogButtonKind,
|
||||||
|
on_click: impl FnOnce() + Send + 'static,
|
||||||
|
) {
|
||||||
|
let mut on_click = Some(on_click);
|
||||||
|
let modal = self.modal.clone();
|
||||||
|
let mut button = caption
|
||||||
|
.into_button()
|
||||||
|
.on_click(move |_| {
|
||||||
|
let Some(on_click) = on_click.take() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
modal.dismiss();
|
||||||
|
on_click();
|
||||||
|
})
|
||||||
|
.make_widget();
|
||||||
|
match kind {
|
||||||
|
DialogButtonKind::Plain => {}
|
||||||
|
DialogButtonKind::Default => button = button.into_default(),
|
||||||
|
DialogButtonKind::Cancel => button = button.into_escape(),
|
||||||
|
}
|
||||||
|
self.buttons.push(button.fit_horizontally().make_widget());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows the modal dialog.
|
||||||
|
pub fn show(mut self) {
|
||||||
|
if self.buttons.is_empty() {
|
||||||
|
self.inner_push_button("OK", DialogButtonKind::Default, || {});
|
||||||
|
}
|
||||||
|
self.modal.present(
|
||||||
|
self.message
|
||||||
|
.and(self.buttons.into_columns().centered())
|
||||||
|
.into_rows()
|
||||||
|
.contain(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HasCancel> DialogBuilder<No, HasCancel> {
|
||||||
|
/// Adds a default button with `caption` that invokes `on_click` when
|
||||||
|
/// activated.
|
||||||
|
pub fn with_default_button(
|
||||||
|
mut self,
|
||||||
|
caption: impl MakeWidget,
|
||||||
|
on_click: impl FnOnce() + Send + 'static,
|
||||||
|
) -> DialogBuilder<Yes, HasCancel> {
|
||||||
|
self.inner_push_button(caption, DialogButtonKind::Default, on_click);
|
||||||
|
let Self {
|
||||||
|
modal,
|
||||||
|
message,
|
||||||
|
buttons,
|
||||||
|
_state,
|
||||||
|
} = self;
|
||||||
|
DialogBuilder {
|
||||||
|
modal,
|
||||||
|
message,
|
||||||
|
buttons,
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HasDefault> DialogBuilder<HasDefault, No> {
|
||||||
|
/// Adds a cancel button with `caption` that invokes `on_click` when
|
||||||
|
/// activated.
|
||||||
|
pub fn with_cancel_button(
|
||||||
|
mut self,
|
||||||
|
caption: impl MakeWidget,
|
||||||
|
on_click: impl FnOnce() + Send + 'static,
|
||||||
|
) -> DialogBuilder<HasDefault, Yes> {
|
||||||
|
self.inner_push_button(caption, DialogButtonKind::Cancel, on_click);
|
||||||
|
let Self {
|
||||||
|
modal,
|
||||||
|
message,
|
||||||
|
buttons,
|
||||||
|
_state,
|
||||||
|
} = self;
|
||||||
|
DialogBuilder {
|
||||||
|
modal,
|
||||||
|
message,
|
||||||
|
buttons,
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum DialogButtonKind {
|
||||||
|
Plain,
|
||||||
|
Default,
|
||||||
|
Cancel,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue