This commit is contained in:
Nathan Sobo 2023-04-08 06:43:39 -06:00
parent 2186de38ab
commit 9d23a98157
4 changed files with 382 additions and 380 deletions

View file

@ -321,6 +321,17 @@ impl App {
state.pending_notifications.clear();
result
}
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
callback: F,
) -> Option<T> {
let mut state = self.0.borrow_mut();
let result = state.update_window(window_id, callback);
state.pending_notifications.clear();
result
}
}
impl AsyncAppContext {
@ -341,6 +352,14 @@ impl AsyncAppContext {
self.0.borrow_mut().update(callback)
}
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
callback: F,
) -> Option<T> {
self.0.borrow_mut().update_window(window_id, callback)
}
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
where
T: Entity,
@ -366,7 +385,7 @@ impl AsyncAppContext {
}
pub fn activate_window(&mut self, window_id: usize) {
self.update(|cx| cx.activate_window(window_id))
self.update_window(window_id, |cx| cx.activate_window());
}
pub fn prompt(
@ -375,8 +394,8 @@ impl AsyncAppContext {
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.update(|cx| cx.prompt(window_id, level, msg, answers))
) -> Option<oneshot::Receiver<usize>> {
self.update_window(window_id, |cx| cx.prompt(level, msg, answers))
}
pub fn platform(&self) -> Arc<dyn Platform> {
@ -528,9 +547,6 @@ pub struct AppContext {
keystroke_observations: CallbackCollection<usize, KeystrokeCallback>,
active_labeled_task_observations: CallbackCollection<(), ActiveLabeledTasksCallback>,
#[allow(clippy::type_complexity)]
presenters_and_platform_windows:
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
foreground: Rc<executor::Foreground>,
pending_effects: VecDeque<Effect>,
pending_notifications: HashSet<usize>,
@ -588,7 +604,6 @@ impl AppContext {
keystroke_observations: Default::default(),
action_dispatch_observations: Default::default(),
active_labeled_task_observations: Default::default(),
presenters_and_platform_windows: Default::default(),
foreground,
pending_effects: VecDeque::new(),
pending_notifications: Default::default(),
@ -659,9 +674,7 @@ impl AppContext {
}
pub fn remove_all_windows(&mut self) {
for (window_id, _) in self.windows.drain() {
self.presenters_and_platform_windows.remove(&window_id);
}
self.windows.clear();
self.flush_effects();
}
@ -782,14 +795,6 @@ impl AppContext {
}
}
pub fn is_topmost_window_for_position(&self, window_id: usize, position: Vector2F) -> bool {
self.presenters_and_platform_windows
.get(&window_id)
.map_or(false, |(_, window)| {
window.is_topmost_for_position(position)
})
}
pub fn has_window(&self, window_id: usize) -> bool {
self.window_ids()
.find(|window| window == &window_id)
@ -800,12 +805,6 @@ impl AppContext {
self.windows.keys().copied()
}
pub fn activate_window(&self, window_id: usize) {
if let Some((_, window)) = self.presenters_and_platform_windows.get(&window_id) {
window.activate()
}
}
pub fn window_is_active(&self, window_id: usize) -> bool {
self.windows
.get(&window_id)
@ -818,16 +817,6 @@ impl AppContext {
.map_or(false, |window| window.is_fullscreen)
}
pub fn window_bounds(&self, window_id: usize) -> Option<WindowBounds> {
let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
Some(window.bounds())
}
pub fn window_display_uuid(&self, window_id: usize) -> Option<Uuid> {
let (_, window) = self.presenters_and_platform_windows.get(&window_id)?;
window.screen().display_uuid()
}
pub fn root_view(&self, window_id: usize) -> Option<AnyViewHandle> {
self.windows
.get(&window_id)
@ -891,12 +880,6 @@ impl AppContext {
}
}
pub fn debug_elements(&self, window_id: usize) -> Option<crate::json::Value> {
self.presenters_and_platform_windows
.get(&window_id)
.and_then(|(presenter, _)| presenter.borrow().debug_elements(self))
}
pub fn active_labeled_tasks<'a>(
&'a self,
) -> impl DoubleEndedIterator<Item = &'static str> + 'a {
@ -966,35 +949,23 @@ impl AppContext {
result
}
fn show_character_palette(&self, window_id: usize) {
let (_, window) = &self.presenters_and_platform_windows[&window_id];
window.show_character_palette();
}
pub fn minimize_window(&self, window_id: usize) {
let (_, window) = &self.presenters_and_platform_windows[&window_id];
window.minimize();
}
pub fn zoom_window(&self, window_id: usize) {
let (_, window) = &self.presenters_and_platform_windows[&window_id];
window.zoom();
}
pub fn toggle_window_full_screen(&self, window_id: usize) {
let (_, window) = &self.presenters_and_platform_windows[&window_id];
window.toggle_full_screen();
}
pub fn prompt(
&self,
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
let (_, window) = &self.presenters_and_platform_windows[&window_id];
window.prompt(level, msg, answers)
callback: F,
) -> Option<T> {
self.update(|app_context| {
let mut window = app_context.windows.remove(&window_id)?;
let mut window_context = WindowContext {
app_context,
window: &mut window,
window_id,
};
let result = callback(&mut window_context);
app_context.windows.insert(window_id, window);
Some(result)
})
}
pub fn prompt_for_paths(
@ -1560,54 +1531,6 @@ impl AppContext {
false
}
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool {
if let Some(focused_view_id) = self.focused_view_id(window_id) {
let dispatch_path = self
.ancestors(window_id, focused_view_id)
.filter_map(|view_id| {
self.views
.get(&(window_id, view_id))
.map(|view| (view_id, view.keymap_context(self)))
})
.collect();
let match_result = self
.keystroke_matcher
.push_keystroke(keystroke.clone(), dispatch_path);
let mut handled_by = None;
let keystroke_handled = match &match_result {
MatchResult::None => false,
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
for (view_id, action) in matches {
if self.handle_dispatch_action_from_effect(
window_id,
Some(*view_id),
action.as_ref(),
) {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
break;
}
}
handled_by.is_some()
}
};
self.keystroke(
window_id,
keystroke.clone(),
handled_by,
match_result.clone(),
);
keystroke_handled
} else {
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
false
}
}
pub fn default_global<T: 'static + Default>(&mut self) -> &T {
let type_id = TypeId::of::<T>();
self.update(|this| {
@ -1696,6 +1619,16 @@ impl AppContext {
let root_view = this
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
.unwrap();
let platform_window =
this.platform
.open_window(window_id, window_options, this.foreground.clone());
let presenter = self.build_presenter(
window_id,
platform_window.titlebar_height(),
platform_window.appearance(),
);
this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
this.windows.insert(
window_id,
Window {
@ -1704,15 +1637,12 @@ impl AppContext {
is_active: false,
invalidation: None,
is_fullscreen: false,
platform_window,
presenter,
},
);
root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
let window =
this.platform
.open_window(window_id, window_options, this.foreground.clone());
this.register_platform_window(window_id, window);
(window_id, root_view)
})
}
@ -1727,6 +1657,15 @@ impl AppContext {
let root_view = this
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
.unwrap();
let mut platform_window = this.platform.add_status_item();
let mut presenter = self.build_presenter(
window_id,
platform_window.titlebar_height(),
platform_window.appearance(),
);
this.register_platform_window(window_id, &mut presenter, platform_window.as_mut());
let focused_view_id = root_view.id();
this.windows.insert(
window_id,
@ -1736,13 +1675,12 @@ impl AppContext {
is_active: false,
invalidation: None,
is_fullscreen: false,
platform_window,
presenter,
},
);
root_view.update(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
let status_item = this.platform.add_status_item();
this.register_platform_window(window_id, status_item);
(window_id, root_view)
})
}
@ -1754,89 +1692,79 @@ impl AppContext {
fn register_platform_window(
&mut self,
window_id: usize,
mut window: Box<dyn platform::Window>,
presenter: &mut Presenter,
platform_window: &mut dyn platform::Window,
) {
let presenter = Rc::new(RefCell::new(self.build_presenter(
window_id,
window.titlebar_height(),
window.appearance(),
)));
{
let mut app = self.upgrade();
let presenter = Rc::downgrade(&presenter);
window.on_event(Box::new(move |event| {
app.update(|cx| {
if let Some(presenter) = presenter.upgrade() {
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
if cx.dispatch_keystroke(window_id, keystroke) {
return true;
}
platform_window.on_event(Box::new(move |event| {
app.update_window(window_id, |cx| {
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
if cx.dispatch_keystroke(keystroke) {
return true;
}
presenter.borrow_mut().dispatch_event(event, false, cx)
} else {
false
}
cx.dispatch_event(event, false)
})
.unwrap_or(false)
}));
}
{
let mut app = self.upgrade();
window.on_active_status_change(Box::new(move |is_active| {
platform_window.on_active_status_change(Box::new(move |is_active| {
app.update(|cx| cx.window_changed_active_status(window_id, is_active))
}));
}
{
let mut app = self.upgrade();
window.on_resize(Box::new(move || {
platform_window.on_resize(Box::new(move || {
app.update(|cx| cx.window_was_resized(window_id))
}));
}
{
let mut app = self.upgrade();
window.on_moved(Box::new(move || {
platform_window.on_moved(Box::new(move || {
app.update(|cx| cx.window_was_moved(window_id))
}));
}
{
let mut app = self.upgrade();
window.on_fullscreen(Box::new(move |is_fullscreen| {
platform_window.on_fullscreen(Box::new(move |is_fullscreen| {
app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen))
}));
}
{
let mut app = self.upgrade();
window.on_close(Box::new(move || {
platform_window.on_close(Box::new(move || {
app.update(|cx| cx.remove_window(window_id));
}));
}
{
let mut app = self.upgrade();
window.on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows())));
platform_window
.on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows())));
}
window.set_input_handler(Box::new(WindowInputHandler {
platform_window.set_input_handler(Box::new(WindowInputHandler {
app: self.upgrade().0,
window_id,
}));
let scene = presenter.borrow_mut().build_scene(
window.content_size(),
window.scale_factor(),
let scene = presenter.build_scene(
platform_window.content_size(),
platform_window.scale_factor(),
false,
self,
);
window.present_scene(scene);
self.presenters_and_platform_windows
.insert(window_id, (presenter.clone(), window));
platform_window.present_scene(scene);
}
pub fn replace_root_view<T, F>(&mut self, window_id: usize, build_root_view: F) -> ViewHandle<T>
@ -1857,7 +1785,6 @@ impl AppContext {
pub fn remove_window(&mut self, window_id: usize) {
self.windows.remove(&window_id);
self.presenters_and_platform_windows.remove(&window_id);
self.flush_effects();
}
@ -2217,30 +2144,20 @@ impl AppContext {
}
fn update_windows(&mut self) {
let mut invalidations: HashMap<_, _> = Default::default();
for (window_id, window) in &mut self.windows {
if let Some(invalidation) = window.invalidation.take() {
invalidations.insert(*window_id, invalidation);
}
}
for (window_id, mut invalidation) in invalidations {
if let Some((presenter, mut window)) =
self.presenters_and_platform_windows.remove(&window_id)
{
{
let mut presenter = presenter.borrow_mut();
presenter.invalidate(&mut invalidation, window.appearance(), self);
let scene = presenter.build_scene(
window.content_size(),
window.scale_factor(),
false,
self,
);
window.present_scene(scene);
}
self.presenters_and_platform_windows
.insert(window_id, (presenter, window));
if let Some(mut invalidation) = window.invalidation.take() {
window.presenter.invalidate(
&mut invalidation,
window.platform_window.appearance(),
self,
);
let scene = window.presenter.build_scene(
window.platform_window.content_size(),
window.platform_window.scale_factor(),
false,
self,
);
window.platform_window.present_scene(scene);
}
}
}
@ -2306,20 +2223,21 @@ impl AppContext {
}
fn perform_window_refresh(&mut self) {
let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
for (window_id, (presenter, window)) in &mut presenters {
let mut invalidation = self.windows.get_mut(window_id).unwrap().invalidation.take();
let mut presenter = presenter.borrow_mut();
presenter.refresh(
invalidation.as_mut().unwrap_or(&mut Default::default()),
window.appearance(),
for window in self.windows.values_mut() {
let mut invalidation = window.invalidation.take().unwrap_or_default();
window.presenter.invalidate(
&mut invalidation,
window.platform_window.appearance(),
self,
);
let scene =
presenter.build_scene(window.content_size(), window.scale_factor(), true, self);
window.present_scene(scene);
let scene = window.presenter.build_scene(
window.platform_window.content_size(),
window.platform_window.scale_factor(),
true,
self,
);
window.platform_window.present_scene(scene);
}
self.presenters_and_platform_windows = presenters;
}
fn emit_global_event(&mut self, payload: Box<dyn Any>) {
@ -2365,33 +2283,19 @@ impl AppContext {
}
fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) {
//Short circuit evaluation if we're already g2g
if self
.windows
.get(&window_id)
.map(|w| w.is_fullscreen == is_fullscreen)
.unwrap_or(false)
{
return;
}
self.update_window(window_id, |cx| {
cx.window.is_fullscreen = is_fullscreen;
self.update(|this| {
let window = this.windows.get_mut(&window_id)?;
window.is_fullscreen = is_fullscreen;
let mut fullscreen_observations = this.window_fullscreen_observations.clone();
fullscreen_observations.emit(window_id, this, |callback, this| {
let mut fullscreen_observations = cx.window_fullscreen_observations.clone();
fullscreen_observations.emit(window_id, cx, |callback, this| {
callback(is_fullscreen, this)
});
if let Some((uuid, bounds)) = this
.window_display_uuid(window_id)
.zip(this.window_bounds(window_id))
{
let mut bounds_observations = this.window_bounds_observations.clone();
bounds_observations.emit(window_id, this, |callback, this| {
callback(bounds, uuid, this)
});
if let Some(uuid) = cx.window_display_uuid() {
let bounds = cx.window_bounds();
let mut bounds_observations = cx.window_bounds_observations.clone();
bounds_observations
.emit(window_id, cx, |callback, this| callback(bounds, uuid, this));
}
Some(())
@ -2563,23 +2467,27 @@ impl AppContext {
mut callback: WindowShouldCloseSubscriptionCallback,
) {
let mut app = self.upgrade();
if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
window.on_should_close(Box::new(move || app.update(|cx| callback(cx))))
if let Some(window) = self.windows.get_mut(&window_id) {
window
.platform_window
.on_should_close(Box::new(move || app.update(|cx| callback(cx))))
}
}
fn handle_window_moved(&mut self, window_id: usize) {
if let Some((display, bounds)) = self
.window_display_uuid(window_id)
.zip(self.window_bounds(window_id))
{
self.window_bounds_observations
.clone()
.emit(window_id, self, move |callback, this| {
callback(bounds, display, this);
true
});
}
self.update_window(window_id, |cx| {
if let Some(display) = cx.window_display_uuid() {
let bounds = cx.window_bounds();
cx.window_bounds_observations.clone().emit(
window_id,
self,
move |callback, this| {
callback(bounds, display, this);
true
},
);
}
});
}
fn handle_active_labeled_tasks_changed_effect(&mut self) {
@ -2806,6 +2714,8 @@ pub struct Window {
is_active: bool,
is_fullscreen: bool,
invalidation: Option<WindowInvalidation>,
presenter: Presenter,
platform_window: Box<dyn platform::Window>,
}
#[derive(Default, Clone)]
@ -3584,6 +3494,138 @@ impl<M> DerefMut for ModelContext<'_, M> {
}
}
pub struct WindowContext<'a: 'b, 'b> {
app_context: &'a mut AppContext,
window: &'b mut Window,
window_id: usize,
}
impl Deref for WindowContext<'_, '_> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app_context
}
}
impl DerefMut for WindowContext<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app_context
}
}
impl WindowContext<'_, '_> {
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let window_id = self.window_id;
if let Some(focused_view_id) = self.focused_view_id(window_id) {
let dispatch_path = self
.ancestors(window_id, focused_view_id)
.filter_map(|view_id| {
self.views
.get(&(window_id, view_id))
.map(|view| (view_id, view.keymap_context(self)))
})
.collect();
let match_result = self
.keystroke_matcher
.push_keystroke(keystroke.clone(), dispatch_path);
let mut handled_by = None;
let keystroke_handled = match &match_result {
MatchResult::None => false,
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
for (view_id, action) in matches {
if self.handle_dispatch_action_from_effect(
window_id,
Some(*view_id),
action.as_ref(),
) {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
break;
}
}
handled_by.is_some()
}
};
self.keystroke(
window_id,
keystroke.clone(),
handled_by,
match_result.clone(),
);
keystroke_handled
} else {
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
false
}
}
pub fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
self.window
.presenter
.dispatch_event(event, event_reused, self)
}
pub fn set_window_title(&mut self, title: &str) {
self.window.platform_window.set_title(title);
}
pub fn set_window_edited(&mut self, edited: bool) {
self.window.platform_window.set_edited(edited);
}
pub fn is_topmost_window_for_position(&self, position: Vector2F) -> bool {
self.window
.platform_window
.is_topmost_for_position(position)
}
pub fn activate_window(&self) {
self.window.platform_window.activate();
}
pub fn window_bounds(&self) -> WindowBounds {
self.window.platform_window.bounds()
}
pub fn window_display_uuid(&self) -> Option<Uuid> {
self.window.platform_window.screen().display_uuid()
}
pub fn debug_elements(&self) -> Option<crate::json::Value> {
self.window.presenter.debug_elements(self)
}
fn show_character_palette(&self) {
self.window.platform_window.show_character_palette();
}
pub fn minimize_window(&self) {
self.window.platform_window.minimize();
}
pub fn zoom_window(&self) {
self.window.platform_window.zoom();
}
pub fn toggle_window_full_screen(&self) {
self.window.platform_window.toggle_full_screen();
}
pub fn prompt(
&self,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.window.platform_window.prompt(level, msg, answers)
}
}
pub struct ViewContext<'a, T: ?Sized> {
app: &'a mut AppContext,
window_id: usize,
@ -3633,31 +3675,6 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.platform()
}
pub fn show_character_palette(&self) {
self.app.show_character_palette(self.window_id);
}
pub fn minimize_window(&self) {
self.app.minimize_window(self.window_id)
}
pub fn zoom_window(&self) {
self.app.zoom_window(self.window_id)
}
pub fn toggle_full_screen(&self) {
self.app.toggle_window_full_screen(self.window_id)
}
pub fn prompt(
&self,
level: PromptLevel,
msg: &str,
answers: &[&str],
) -> oneshot::Receiver<usize> {
self.app.prompt(self.window_id, level, msg, answers)
}
pub fn prompt_for_paths(
&self,
options: PathPromptOptions,
@ -3673,10 +3690,6 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.reveal_path(path)
}
pub fn debug_elements(&self) -> crate::json::Value {
self.app.debug_elements(self.window_id).unwrap()
}
pub fn focus(&mut self, handle: &AnyViewHandle) {
self.app.focus(handle.window_id, Some(handle.view_id));
}
@ -3703,20 +3716,6 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.focus(self.window_id, None);
}
pub fn set_window_title(&mut self, title: &str) {
let window_id = self.window_id();
if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
window.set_title(title);
}
}
pub fn set_window_edited(&mut self, edited: bool) {
let window_id = self.window_id();
if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) {
window.set_edited(edited);
}
}
pub fn on_window_should_close<F>(&mut self, mut callback: F)
where
F: 'static + FnMut(&mut T, &mut ViewContext<T>) -> bool,
@ -5492,19 +5491,20 @@ mod tests {
let (window_id, _) = cx.add_window(Default::default(), |_| View {
mouse_down_count: mouse_down_count.clone(),
});
let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
// Ensure window's root element is in a valid lifecycle state.
presenter.borrow_mut().dispatch_event(
Event::MouseDown(MouseButtonEvent {
position: Default::default(),
button: MouseButton::Left,
modifiers: Default::default(),
click_count: 1,
}),
false,
cx,
);
assert_eq!(mouse_down_count.load(SeqCst), 1);
cx.update_window(window_id, |cx| {
// Ensure window's root element is in a valid lifecycle state.
cx.dispatch_event(
Event::MouseDown(MouseButtonEvent {
position: Default::default(),
button: MouseButton::Left,
modifiers: Default::default(),
click_count: 1,
}),
false,
);
assert_eq!(mouse_down_count.load(SeqCst), 1);
});
}
#[crate::test(self)]
@ -6553,19 +6553,28 @@ mod tests {
}
});
cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
cx.update_window(window_id, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("a").unwrap())
});
assert_eq!(&*actions.borrow(), &["2 a"]);
actions.borrow_mut().clear();
cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
cx.update_window(window_id, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("b").unwrap());
});
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
actions.borrow_mut().clear();
cx.dispatch_keystroke(window_id, &Keystroke::parse("c").unwrap());
cx.update_window(window_id, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("c").unwrap());
});
assert_eq!(&*actions.borrow(), &["3 c"]);
actions.borrow_mut().clear();
cx.dispatch_keystroke(window_id, &Keystroke::parse("d").unwrap());
cx.update_window(window_id, |cx| {
cx.dispatch_keystroke(&Keystroke::parse("d").unwrap());
});
assert_eq!(&*actions.borrow(), &["2 d"]);
actions.borrow_mut().clear();
}
@ -6836,46 +6845,54 @@ mod tests {
}
let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0));
let presenter = cx.presenters_and_platform_windows[&window_id].0.clone();
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 0")
);
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
Some("render count: 0")
);
});
let view = cx.add_view(&root_view, |cx| {
cx.refresh_windows();
View(0)
});
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 1")
);
assert_eq!(
presenter.borrow().rendered_views[&view.id()].name(),
Some("render count: 0")
);
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
Some("render count: 1")
);
assert_eq!(
cx.window.presenter.rendered_views[&view.id()].name(),
Some("render count: 0")
);
});
cx.update(|cx| cx.refresh_windows());
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 2")
);
assert_eq!(
presenter.borrow().rendered_views[&view.id()].name(),
Some("render count: 1")
);
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
Some("render count: 2")
);
assert_eq!(
cx.window.presenter.rendered_views[&view.id()].name(),
Some("render count: 1")
);
});
cx.update(|cx| {
cx.refresh_windows();
drop(view);
});
assert_eq!(
presenter.borrow().rendered_views[&root_view.id()].name(),
Some("render count: 3")
);
assert_eq!(presenter.borrow().rendered_views.len(), 1);
cx.update_window(window_id, |cx| {
assert_eq!(
cx.window.presenter.rendered_views[&root_view.id()].name(),
Some("render count: 3")
);
assert_eq!(cx.window.presenter.rendered_views.len(), 1);
});
}
#[crate::test(self)]

View file

@ -84,31 +84,28 @@ impl TestAppContext {
}
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
let handled = self.cx.borrow_mut().update(|cx| {
let presenter = cx
.presenters_and_platform_windows
.get(&window_id)
.unwrap()
.0
.clone();
let handled = self
.cx
.borrow_mut()
.update_window(window_id, |cx| {
if cx.dispatch_keystroke(&keystroke) {
return true;
}
if cx.dispatch_keystroke(window_id, &keystroke) {
return true;
}
if cx.window.presenter.dispatch_event(
Event::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held,
}),
false,
cx,
) {
return true;
}
if presenter.borrow_mut().dispatch_event(
Event::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
is_held,
}),
false,
cx,
) {
return true;
}
false
});
false
})
.unwrap_or(false);
if !handled && !keystroke.cmd && !keystroke.ctrl {
WindowInputHandler {
@ -244,7 +241,7 @@ impl TestAppContext {
use postage::prelude::Sink as _;
let mut done_tx = self
.window_mut(window_id)
.platform_window_mut(window_id)
.pending_prompts
.borrow_mut()
.pop_front()
@ -253,20 +250,23 @@ impl TestAppContext {
}
pub fn has_pending_prompt(&self, window_id: usize) -> bool {
let window = self.window_mut(window_id);
let window = self.platform_window_mut(window_id);
let prompts = window.pending_prompts.borrow_mut();
!prompts.is_empty()
}
pub fn current_window_title(&self, window_id: usize) -> Option<String> {
self.window_mut(window_id).title.clone()
self.platform_window_mut(window_id).title.clone()
}
pub fn simulate_window_close(&self, window_id: usize) -> bool {
let handler = self.window_mut(window_id).should_close_handler.take();
let handler = self
.platform_window_mut(window_id)
.should_close_handler
.take();
if let Some(mut handler) = handler {
let should_close = handler();
self.window_mut(window_id).should_close_handler = Some(handler);
self.platform_window_mut(window_id).should_close_handler = Some(handler);
should_close
} else {
false
@ -274,47 +274,34 @@ impl TestAppContext {
}
pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
let mut window = self.window_mut(window_id);
let mut window = self.platform_window_mut(window_id);
window.size = size;
let mut handlers = mem::take(&mut window.resize_handlers);
drop(window);
for handler in &mut handlers {
handler();
}
self.window_mut(window_id).resize_handlers = handlers;
self.platform_window_mut(window_id).resize_handlers = handlers;
}
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
let mut handlers = BTreeMap::new();
{
let mut cx = self.cx.borrow_mut();
for (window_id, (_, window)) in &mut cx.presenters_and_platform_windows {
let window = window
.as_any_mut()
.downcast_mut::<platform::test::Window>()
.unwrap();
handlers.insert(
*window_id,
mem::take(&mut window.active_status_change_handlers),
);
}
};
let mut handlers = handlers.into_iter().collect::<Vec<_>>();
handlers.sort_unstable_by_key(|(window_id, _)| Some(*window_id) == to_activate);
for (window_id, mut window_handlers) in handlers {
for window_handler in &mut window_handlers {
window_handler(Some(window_id) == to_activate);
self.cx.borrow_mut().update(|cx| {
for window_id in cx
.windows
.keys()
.filter(|window_id| Some(**window_id) != to_activate)
{
cx.window_changed_active_status(*window_id, false)
}
self.window_mut(window_id)
.active_status_change_handlers
.extend(window_handlers);
}
if let Some(to_activate) = to_activate {
cx.window_changed_active_status(to_activate, true)
}
});
}
pub fn is_window_edited(&self, window_id: usize) -> bool {
self.window_mut(window_id).edited
self.platform_window_mut(window_id).edited
}
pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
@ -337,13 +324,11 @@ impl TestAppContext {
self.assert_dropped(weak);
}
fn window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
std::cell::RefMut::map(self.cx.borrow_mut(), |state| {
let (_, window) = state
.presenters_and_platform_windows
.get_mut(&window_id)
.unwrap();
let window = state.windows.get_mut(&window_id).unwrap();
let test_window = window
.platform_window
.as_any_mut()
.downcast_mut::<platform::test::Window>()
.unwrap();

View file

@ -91,8 +91,7 @@ impl InputHandler for WindowInputHandler {
fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
let app = self.app.borrow();
let (presenter, _) = app.presenters_and_platform_windows.get(&self.window_id)?;
let presenter = presenter.borrow();
presenter.rect_for_text_range(range_utf16, &app)
let window = app.windows.get(&self.window_id)?;
window.presenter.rect_for_text_range(range_utf16, &app)
}
}

View file

@ -315,9 +315,10 @@ impl Presenter {
}
}
if cx.is_topmost_window_for_position(self.window_id, *position) {
cx.platform().set_cursor_style(style_to_assign);
}
// TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// if cx.is_topmost_window_for_position(self.window_id, *position) {
// cx.platform().set_cursor_style(style_to_assign);
// }
if !event_reused {
if pressed_button.is_some() {