Skip to content

Commit

Permalink
Nested modals
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Oct 4, 2024
1 parent e78d1d2 commit 4f3ef7d
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 84 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
winit, and naga. Thanks to @bluenote10 for the feedback!
- `WrapperWidget::activate`'s default implementation now activates the wrapped
widget.
- `Space` now intercepts mouse events if its color has a non-zero alpha channel.

### Fixed

Expand All @@ -91,6 +92,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`Dynamic<T>` change callbacks has been fixed.
- `Stack` no longer unwraps a `Resize` child if the resize widget is resizing in
the direction opposite of the Stack's orientation.
- If the layout of widgets changes during a redraw, the currently hovered widget
is now properly updated immediately. Previously, the hover would only update
on the next cursor event.

### Added

Expand Down Expand Up @@ -211,6 +215,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Choosing one or more files
- Choosing a single folder/directory
- Choosing one or more folders/directories
- `DynamicGuard::unlocked` executes a closure while the guard is temporarily
unlocked.


[139]: https://github.com/khonsulabs/cushy/issues/139
Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ kludgine = { git = "https://github.com/khonsulabs/kludgine", features = [
"app",
] }
figures = { version = "0.4.0" }
alot = "0.3"
alot = "0.3.2"
interner = "0.2.1"
kempt = "0.2.1"
intentional = "0.1.0"
Expand Down
50 changes: 50 additions & 0 deletions examples/nested-modals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use cushy::dialog::MessageBox;
use cushy::widget::MakeWidget;
use cushy::widgets::layers::{Modal, ModalTarget};
use cushy::Run;

fn main() -> cushy::Result {
let modal = Modal::new();

"Show Modal"
.into_button()
.on_click({
let modal = modal.clone();
move |_| show_modal(&modal, 1)
})
.align_top()
.pad()
.and(modal)
.into_layers()
.run()
}

fn show_modal(present_in: &impl ModalTarget, level: usize) {
let handle = present_in.pending_handle();
handle
.build_dialog(
format!("Modal level: {level}")
.and("Go Deeper".into_button().on_click({
let handle = handle.clone();
move |_| {
show_modal(&handle, level + 1);
}
}))
.and("Show message".into_button().on_click({
let handle = handle.clone();
move |_| {
MessageBox::message("This is a MessageBox shown above a modal")
.open(&handle);
}
}))
.into_rows(),
)
.with_default_button("Close", || {})
.with_cancel_button("Close All", {
let handle = handle.clone();
move || {
handle.layer().dismiss();
}
})
.show();
}
7 changes: 5 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ impl<'context> EventContext<'context> {
}

pub(crate) fn update_hovered_widget(&mut self) {
self.cursor.widget = None;
let current_hover = self.cursor.widget.take();

if let Some(location) = self.cursor.location {
for widget in self.tree.widgets_under_point(location) {
let mut widget_context = self.for_other(&widget);
Expand All @@ -317,7 +318,9 @@ impl<'context> EventContext<'context> {
let relative = location - widget_layout.origin;

if widget_context.hit_test(relative) {
widget_context.hover(location);
if current_hover != Some(widget.id()) {
widget_context.hover(location);
}
drop(widget_context);
self.cursor.widget = Some(widget.id());
break;
Expand Down
19 changes: 14 additions & 5 deletions src/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::value::{Destination, Dynamic, Source};
use crate::widget::{MakeWidget, OnceCallback, SharedCallback, WidgetList};
use crate::widgets::button::{ButtonKind, ClickCounter};
use crate::widgets::input::InputValue;
use crate::widgets::layers::Modal;
use crate::widgets::layers::{Modal, ModalTarget};
use crate::widgets::Custom;
use crate::ModifiersExt;

Expand Down Expand Up @@ -274,7 +274,10 @@ impl MessageBox {

/// Opens this dialog in the given target.
///
/// A target can be a [`Modal`] layer, a [`WindowHandle`], or an [`App`].
/// A target can be a [`Modal`] layer, a
/// [`ModalHandle`](crate::widgets::layers::ModalHandle), a
/// [`WindowHandle`](crate::window::WindowHandle), or an
/// [`App`](crate::App).
pub fn open(&self, open_in: &impl OpenMessageBox) {
open_in.open_message_box(self);
}
Expand All @@ -294,9 +297,13 @@ fn coalesce_empty<'a>(s1: &'a str, s2: &'a str) -> &'a str {
}
}

impl OpenMessageBox for Modal {
impl<T> OpenMessageBox for T
where
T: ModalTarget,
{
fn open_message_box(&self, message: &MessageBox) {
let dialog = self.build_dialog(
let handle = self.pending_handle();
let dialog = handle.build_dialog(
message
.title
.as_str()
Expand Down Expand Up @@ -716,7 +723,9 @@ impl MakeWidget for FilePickerWidget {
};

let chosen_paths = Dynamic::<Vec<PathBuf>>::default();
let confirm_enabled = chosen_paths.map_each(|paths| !paths.is_empty());
let confirm_enabled = chosen_paths.map_each(move |paths| {
!paths.is_empty() && paths.iter().all(|p| p.is_file() == kind.is_file())
});

let browsing_directory = Dynamic::new(
self.picker
Expand Down
15 changes: 12 additions & 3 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1536,11 +1536,12 @@ where
}

impl<'a, T> DynamicMutexGuard<'a, T> {
fn unlocked(&mut self, while_unlocked: impl FnOnce()) {
fn unlocked<R>(&mut self, while_unlocked: impl FnOnce() -> R) -> R {
let previous_state = self.dynamic.during_callback_state.lock().take();
MutexGuard::unlocked(&mut self.guard, while_unlocked);
let result = MutexGuard::unlocked(&mut self.guard, while_unlocked);

*self.dynamic.during_callback_state.lock() = previous_state;
result
}
}

Expand Down Expand Up @@ -2196,7 +2197,7 @@ impl<'a, T> DynamicOrOwnedGuard<'a, T> {
}
}

fn unlocked(&mut self, while_unlocked: impl FnOnce()) {
fn unlocked<R>(&mut self, while_unlocked: impl FnOnce() -> R) -> R {
match self {
Self::Dynamic(guard) => guard.unlocked(while_unlocked),
Self::Owned(_) | Self::OwnedRef(_) => while_unlocked(),
Expand Down Expand Up @@ -2252,6 +2253,14 @@ impl<T, const READONLY: bool> DynamicGuard<'_, T, READONLY> {
pub fn prevent_notifications(&mut self) {
self.prevent_notifications = true;
}

/// Executes `while_unlocked` while this guard is temporarily unlocked.
pub fn unlocked<F, R>(&mut self, while_unlocked: F) -> R
where
F: FnOnce() -> R,
{
self.guard.unlocked(while_unlocked)
}
}

impl<'a, T, const READONLY: bool> Deref for DynamicGuard<'a, T, READONLY> {
Expand Down
Loading

0 comments on commit 4f3ef7d

Please sign in to comment.