modal: Only focus modal when it not focused on. (#250)
- Fix dropdown to mouse down out to dismiss. https://github.com/user-attachments/assets/f5ab4aca-bfdc-460e-ae5a-cd0a31a30906
This commit is contained in:
parent
97e61a507b
commit
8f0ba2e5c3
6 changed files with 60 additions and 33 deletions
|
|
@ -11,6 +11,7 @@ use ui::{
|
|||
button::{Button, ButtonStyle, ButtonStyled as _},
|
||||
checkbox::Checkbox,
|
||||
date_picker::DatePicker,
|
||||
dropdown::Dropdown,
|
||||
h_flex,
|
||||
input::TextInput,
|
||||
list::{List, ListDelegate, ListItem},
|
||||
|
|
@ -149,6 +150,7 @@ pub struct ModalStory {
|
|||
input1: View<TextInput>,
|
||||
input2: View<TextInput>,
|
||||
date_picker: View<DatePicker>,
|
||||
dropdown: View<Dropdown<Vec<String>>>,
|
||||
modal_overlay: bool,
|
||||
model_show_close: bool,
|
||||
model_padding: bool,
|
||||
|
|
@ -247,6 +249,18 @@ impl ModalStory {
|
|||
let input2 = cx.new_view(|cx| TextInput::new(cx).placeholder("Input on the Window"));
|
||||
let date_picker =
|
||||
cx.new_view(|cx| DatePicker::new("birthday-picker", cx).placeholder("Date of Birth"));
|
||||
let dropdown = cx.new_view(|cx| {
|
||||
Dropdown::new(
|
||||
"dropdown1",
|
||||
vec![
|
||||
"Option 1".to_string(),
|
||||
"Option 2".to_string(),
|
||||
"Option 3".to_string(),
|
||||
],
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
|
|
@ -256,6 +270,7 @@ impl ModalStory {
|
|||
input1,
|
||||
input2,
|
||||
date_picker,
|
||||
dropdown,
|
||||
modal_overlay: true,
|
||||
model_show_close: true,
|
||||
model_padding: true,
|
||||
|
|
@ -322,10 +337,10 @@ impl ModalStory {
|
|||
let modal_padding = self.model_padding;
|
||||
let input1 = self.input1.clone();
|
||||
let date_picker = self.date_picker.clone();
|
||||
let dropdown = self.dropdown.clone();
|
||||
let view = cx.view().clone();
|
||||
|
||||
cx.open_modal(move |modal, cx| {
|
||||
input1.focus_handle(cx).focus(cx);
|
||||
modal
|
||||
.title("Form Modal")
|
||||
.overlay(overlay)
|
||||
|
|
@ -337,6 +352,7 @@ impl ModalStory {
|
|||
.child("This is a modal dialog.")
|
||||
.child("You can put anything here.")
|
||||
.child(input1.clone())
|
||||
.child(dropdown.clone())
|
||||
.child(date_picker.clone()),
|
||||
)
|
||||
.footer(
|
||||
|
|
@ -388,6 +404,8 @@ impl ModalStory {
|
|||
),
|
||||
)
|
||||
});
|
||||
|
||||
self.input1.focus_handle(cx).focus(cx);
|
||||
}
|
||||
|
||||
fn on_action_test_action(&mut self, _: &TestAction, cx: &mut ViewContext<Self>) {
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ impl ColorPicker {
|
|||
}
|
||||
|
||||
fn on_escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
||||
cx.propagate();
|
||||
|
||||
self.open = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
|
@ -343,7 +345,6 @@ impl Render for ColorPicker {
|
|||
.position(self.resolved_corner(self.bounds))
|
||||
.child(
|
||||
div()
|
||||
.track_focus(&self.focus_handle)
|
||||
.occlude()
|
||||
.map(|this| match self.anchor {
|
||||
AnchorCorner::TopLeft | AnchorCorner::TopRight => {
|
||||
|
|
|
|||
|
|
@ -150,29 +150,32 @@ where
|
|||
}
|
||||
|
||||
fn cancel(&mut self, cx: &mut ViewContext<List<Self>>) {
|
||||
if let Some(view) = self.dropdown.upgrade() {
|
||||
cx.update_view(&view, |view, cx| {
|
||||
view.focus(cx);
|
||||
view.open = false;
|
||||
let dropdown = self.dropdown.clone();
|
||||
cx.defer(move |_, cx| {
|
||||
_ = dropdown.update(cx, |this, cx| {
|
||||
this.open = false;
|
||||
this.focus(cx);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn confirm(&mut self, ix: Option<usize>, cx: &mut ViewContext<List<Self>>) {
|
||||
self.selected_index = ix;
|
||||
|
||||
if let Some(view) = self.dropdown.upgrade() {
|
||||
cx.update_view(&view, |view, cx| {
|
||||
let selected_value = self
|
||||
.selected_index
|
||||
.and_then(|ix| self.delegate.get(ix))
|
||||
.map(|item| item.value().clone());
|
||||
let selected_value = self
|
||||
.selected_index
|
||||
.and_then(|ix| self.delegate.get(ix))
|
||||
.map(|item| item.value().clone());
|
||||
let dropdown = self.dropdown.clone();
|
||||
|
||||
cx.defer(move |_, cx| {
|
||||
_ = dropdown.update(cx, |this, cx| {
|
||||
cx.emit(DropdownEvent::Confirm(selected_value.clone()));
|
||||
view.focus(cx);
|
||||
view.selected_value = selected_value;
|
||||
view.open = false;
|
||||
this.selected_value = selected_value;
|
||||
this.open = false;
|
||||
this.focus(cx);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn perform_search(&mut self, query: &str, cx: &mut ViewContext<List<Self>>) -> Task<()> {
|
||||
|
|
@ -469,6 +472,9 @@ where
|
|||
}
|
||||
|
||||
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
|
||||
// Propagate the event to the parent view, for example to the Modal to support ENTER to confirm.
|
||||
cx.propagate();
|
||||
|
||||
if !self.open {
|
||||
self.open = true;
|
||||
cx.notify();
|
||||
|
|
@ -489,6 +495,9 @@ where
|
|||
}
|
||||
|
||||
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
||||
// Propagate the event to the parent view, for example to the Modal to support ESC to close.
|
||||
cx.propagate();
|
||||
|
||||
self.open = false;
|
||||
cx.notify();
|
||||
}
|
||||
|
|
@ -566,7 +575,7 @@ where
|
|||
let view = cx.view().clone();
|
||||
let bounds = self.bounds;
|
||||
let allow_open = !(self.open || self.disabled);
|
||||
let outline_visible = is_focused && !self.disabled;
|
||||
let outline_visible = self.open || is_focused && !self.disabled;
|
||||
|
||||
// If the size has change, set size to self.list, to change the QueryInput size.
|
||||
if self.list.read(cx).size != self.size {
|
||||
|
|
@ -676,7 +685,6 @@ where
|
|||
})
|
||||
.child(
|
||||
v_flex()
|
||||
.track_focus(&self.list.focus_handle(cx))
|
||||
.occlude()
|
||||
.mt_1p5()
|
||||
.bg(cx.theme().background)
|
||||
|
|
@ -688,7 +696,10 @@ where
|
|||
cx.dispatch_action(Box::new(Escape));
|
||||
})
|
||||
.child(self.list.clone()),
|
||||
),
|
||||
)
|
||||
.on_mouse_down_out(cx.listener(|this, _, cx| {
|
||||
this.escape(&Escape, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.with_priority(1),
|
||||
|
|
|
|||
|
|
@ -103,7 +103,12 @@ impl<'a> ContextModal for WindowContext<'a> {
|
|||
fn close_modal(&mut self) {
|
||||
Root::update(self, move |root, cx| {
|
||||
root.active_modals.pop();
|
||||
if root.active_modals.len() == 0 {
|
||||
|
||||
if let Some(top_modal) = root.active_modals.last() {
|
||||
// Focus the next modal.
|
||||
top_modal.focus_handle.focus(cx);
|
||||
} else {
|
||||
// Restore focus if there are no more modals.
|
||||
root.focus_back(cx);
|
||||
}
|
||||
cx.notify();
|
||||
|
|
@ -288,8 +293,6 @@ impl Root {
|
|||
return None;
|
||||
}
|
||||
|
||||
let modals_len = active_modals.len();
|
||||
|
||||
Some(
|
||||
div().children(active_modals.iter().enumerate().map(|(i, active_modal)| {
|
||||
let mut modal = Modal::new(cx);
|
||||
|
|
@ -302,14 +305,6 @@ impl Root {
|
|||
// So we keep the focus handle in the `active_modal`, this is owned by the `Root`.
|
||||
modal.focus_handle = active_modal.focus_handle.clone();
|
||||
|
||||
// Focus to the top modal.
|
||||
if i == modals_len - 1 {
|
||||
// Check to avoid focus, when the modal is already focused.
|
||||
if !modal.focus_handle.contains_focused(cx) {
|
||||
cx.focus(&modal.focus_handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep only have one overlay, we only render the first modal with overlay.
|
||||
if has_overlay {
|
||||
modal.overlay_visible = false;
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ pub trait StyledExt: Styled + Sized {
|
|||
/// Render a 1px blue border, when if the element is focused
|
||||
fn debug_focused(self, focus_handle: &FocusHandle, cx: &WindowContext) -> Self {
|
||||
if cfg!(debug_assertions) {
|
||||
if focus_handle.is_focused(cx) {
|
||||
if focus_handle.contains_focused(cx) {
|
||||
self.debug_blue()
|
||||
} else {
|
||||
self
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ impl DatePicker {
|
|||
cx.subscribe(&calendar, |this, _, ev: &CalendarEvent, cx| match ev {
|
||||
CalendarEvent::Selected(date) => {
|
||||
this.update_date(*date, true, cx);
|
||||
this.focus_handle.focus(cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
|
@ -138,6 +139,7 @@ impl DatePicker {
|
|||
|
||||
fn escape(&mut self, _: &Escape, cx: &mut ViewContext<Self>) {
|
||||
self.open = false;
|
||||
self.focus_handle.focus(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +197,7 @@ impl Render for DatePicker {
|
|||
.id(self.id.clone())
|
||||
.key_context("DatePicker")
|
||||
.track_focus(&self.focus_handle)
|
||||
.on_action(cx.listener(Self::escape))
|
||||
.when(self.open, |this| this.on_action(cx.listener(Self::escape)))
|
||||
.w_full()
|
||||
.relative()
|
||||
.map(|this| match self.width {
|
||||
|
|
|
|||
Loading…
Reference in a new issue