From eaef1c8b8ee325af4f8a399b8ac3f726b4a58b70 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 18 Oct 2023 15:17:22 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/src/app.rs | 39 +++++++- crates/gpui3/src/events.rs | 116 ++++++++++++++++++---- crates/gpui3/src/interactive.rs | 70 +------------ crates/gpui3/src/platform.rs | 10 +- crates/gpui3/src/platform/mac/events.rs | 4 +- crates/gpui3/src/platform/mac/platform.rs | 8 +- crates/gpui3/src/platform/mac/window.rs | 40 ++++---- crates/gpui3/src/platform/test.rs | 2 +- crates/gpui3/src/window.rs | 58 ++++++++--- 9 files changed, 210 insertions(+), 137 deletions(-) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index fdb96902e64bb9c8542ea3b1ae51afe460316dad..e871a7fad6fda76e0c5181b7c58c739f0ec0bdd0 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -8,9 +8,10 @@ pub use model_context::*; use refineable::Refineable; use crate::{ - current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId, - MainThread, MainThreadOnly, Platform, SubscriberSet, SvgRenderer, Task, TextStyle, - TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId, + current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, + FocusEvent, FocusHandle, FocusId, LayoutId, MainThread, MainThreadOnly, Platform, + SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, + WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -54,6 +55,7 @@ impl App { this: this.clone(), text_system: Arc::new(TextSystem::new(platform.text_system())), pending_updates: 0, + flushing_effects: false, next_frame_callbacks: Default::default(), platform: MainThreadOnly::new(platform, executor.clone()), executor, @@ -97,6 +99,7 @@ pub struct AppContext { this: Weak>, pub(crate) platform: MainThreadOnly, text_system: Arc, + flushing_effects: bool, pending_updates: usize, pub(crate) next_frame_callbacks: HashMap>, pub(crate) executor: Executor, @@ -119,8 +122,10 @@ impl AppContext { pub(crate) fn update(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { self.pending_updates += 1; let result = update(self); - if self.pending_updates == 1 { + if !self.flushing_effects && self.pending_updates == 1 { + self.flushing_effects = true; self.flush_effects(); + self.flushing_effects = false; } self.pending_updates -= 1; result @@ -158,6 +163,7 @@ impl AppContext { } } Effect::Emit { .. } => self.pending_effects.push_back(effect), + Effect::FocusChanged { .. } => self.pending_effects.push_back(effect), } } @@ -168,6 +174,9 @@ impl AppContext { match effect { Effect::Notify { emitter } => self.apply_notify_effect(emitter), Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event), + Effect::FocusChanged { window_id, focused } => { + self.apply_focus_changed(window_id, focused) + } } } else { break; @@ -222,6 +231,24 @@ impl AppContext { .retain(&emitter, |handler| handler(&event, self)); } + fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option) { + self.update_window(window_id, |cx| { + if cx.window.focus == focused { + let mut listeners = mem::take(&mut cx.window.focus_change_listeners); + let focused = focused.map(FocusHandle::new); + let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new); + let event = FocusEvent { focused, blurred }; + for listener in &listeners { + listener(&event, cx); + } + + listeners.extend(cx.window.focus_change_listeners.drain(..)); + cx.window.focus_change_listeners = listeners; + } + }) + .ok(); + } + pub fn to_async(&self) -> AsyncAppContext { AsyncAppContext(unsafe { mem::transmute(self.this.clone()) }) } @@ -426,6 +453,10 @@ pub(crate) enum Effect { emitter: EntityId, event: Box, }, + FocusChanged { + window_id: WindowId, + focused: Option, + }, } #[cfg(test)] diff --git a/crates/gpui3/src/events.rs b/crates/gpui3/src/events.rs index 52719ae066e509292ea047a908229fedad9feaaa..ee5a1f4fdd5d8763ab878cba9be05f88e36f87ce 100644 --- a/crates/gpui3/src/events.rs +++ b/crates/gpui3/src/events.rs @@ -1,5 +1,9 @@ -use crate::{point, Keystroke, Modifiers, Pixels, Point}; -use std::{any::Any, ops::Deref}; +use smallvec::SmallVec; + +use crate::{ + point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext, +}; +use std::{any::Any, ops::Deref, sync::Arc}; #[derive(Clone, Debug, Eq, PartialEq)] pub struct KeyDownEvent { @@ -26,7 +30,7 @@ impl Deref for ModifiersChangedEvent { } /// The phase of a touch motion event. -/// Based on the winit enum of the same name, +/// Based on the winit enum of the same name. #[derive(Clone, Copy, Debug)] pub enum TouchPhase { Started, @@ -50,6 +54,12 @@ pub struct MouseUpEvent { pub click_count: usize, } +#[derive(Clone, Debug, Default)] +pub struct MouseClickEvent { + pub down: MouseDownEvent, + pub up: MouseUpEvent, +} + #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] pub enum MouseButton { Left, @@ -155,7 +165,7 @@ impl Deref for MouseExitEvent { } #[derive(Clone, Debug)] -pub enum Event { +pub enum InputEvent { KeyDown(KeyDownEvent), KeyUp(KeyUpEvent), ModifiersChanged(ModifiersChangedEvent), @@ -166,30 +176,94 @@ pub enum Event { ScrollWheel(ScrollWheelEvent), } -impl Event { +impl InputEvent { pub fn position(&self) -> Option> { match self { - Event::KeyDown { .. } => None, - Event::KeyUp { .. } => None, - Event::ModifiersChanged { .. } => None, - Event::MouseDown(event) => Some(event.position), - Event::MouseUp(event) => Some(event.position), - Event::MouseMoved(event) => Some(event.position), - Event::MouseExited(event) => Some(event.position), - Event::ScrollWheel(event) => Some(event.position), + InputEvent::KeyDown { .. } => None, + InputEvent::KeyUp { .. } => None, + InputEvent::ModifiersChanged { .. } => None, + InputEvent::MouseDown(event) => Some(event.position), + InputEvent::MouseUp(event) => Some(event.position), + InputEvent::MouseMoved(event) => Some(event.position), + InputEvent::MouseExited(event) => Some(event.position), + InputEvent::ScrollWheel(event) => Some(event.position), } } pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> { match self { - Event::KeyDown { .. } => None, - Event::KeyUp { .. } => None, - Event::ModifiersChanged { .. } => None, - Event::MouseDown(event) => Some(event), - Event::MouseUp(event) => Some(event), - Event::MouseMoved(event) => Some(event), - Event::MouseExited(event) => Some(event), - Event::ScrollWheel(event) => Some(event), + InputEvent::KeyDown { .. } => None, + InputEvent::KeyUp { .. } => None, + InputEvent::ModifiersChanged { .. } => None, + InputEvent::MouseDown(event) => Some(event), + InputEvent::MouseUp(event) => Some(event), + InputEvent::MouseMoved(event) => Some(event), + InputEvent::MouseExited(event) => Some(event), + InputEvent::ScrollWheel(event) => Some(event), + } + } +} + +pub struct FocusEvent { + pub blurred: Option, + pub focused: Option, +} + +pub type MouseDownListener = Arc< + dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +pub type MouseUpListener = Arc< + dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +pub type MouseClickListener = + Arc) + Send + Sync + 'static>; + +pub type MouseMoveListener = Arc< + dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; + +pub type ScrollWheelListener = Arc< + dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; + +pub type KeyDownListener = + Arc) + Send + Sync + 'static>; + +pub type KeyUpListener = + Arc) + Send + Sync + 'static>; + +pub struct EventListeners { + pub mouse_down: SmallVec<[MouseDownListener; 2]>, + pub mouse_up: SmallVec<[MouseUpListener; 2]>, + pub mouse_click: SmallVec<[MouseClickListener; 2]>, + pub mouse_move: SmallVec<[MouseMoveListener; 2]>, + pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, + pub key_down: SmallVec<[KeyDownListener; 2]>, + pub key_up: SmallVec<[KeyUpListener; 2]>, +} + +impl Default for EventListeners { + fn default() -> Self { + Self { + mouse_down: SmallVec::new(), + mouse_up: SmallVec::new(), + mouse_click: SmallVec::new(), + mouse_move: SmallVec::new(), + scroll_wheel: SmallVec::new(), + key_down: SmallVec::new(), + key_up: SmallVec::new(), } } } diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index 35bcab15a22ba94539ff1609a2e136163231398b..ecaebe7681774f451ce6d5f4b7c9c44ce0683b5b 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -1,8 +1,6 @@ -use smallvec::SmallVec; - use crate::{ - Bounds, DispatchPhase, Element, KeyDownEvent, KeyUpEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ViewContext, + DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent, + MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext, }; use std::sync::Arc; @@ -163,67 +161,3 @@ pub trait Click: Interactive { self } } - -type MouseDownListener = Arc< - dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseUpListener = Arc< - dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseClickListener = - Arc) + Send + Sync + 'static>; - -type MouseMoveListener = Arc< - dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; - -type ScrollWheelListener = Arc< - dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; - -pub type KeyDownListener = - Arc) + Send + Sync + 'static>; - -pub type KeyUpListener = - Arc) + Send + Sync + 'static>; - -pub struct EventListeners { - pub mouse_down: SmallVec<[MouseDownListener; 2]>, - pub mouse_up: SmallVec<[MouseUpListener; 2]>, - pub mouse_click: SmallVec<[MouseClickListener; 2]>, - pub mouse_move: SmallVec<[MouseMoveListener; 2]>, - pub scroll_wheel: SmallVec<[ScrollWheelListener; 2]>, - pub key_down: SmallVec<[KeyDownListener; 2]>, - pub key_up: SmallVec<[KeyUpListener; 2]>, -} - -impl Default for EventListeners { - fn default() -> Self { - Self { - mouse_down: SmallVec::new(), - mouse_up: SmallVec::new(), - mouse_click: SmallVec::new(), - mouse_move: SmallVec::new(), - scroll_wheel: SmallVec::new(), - key_down: SmallVec::new(), - key_up: SmallVec::new(), - } - } -} - -pub struct MouseClickEvent { - pub down: MouseDownEvent, - pub up: MouseUpEvent, -} diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 8bfdfbccfa216c8bb0278c795da1565bfcb3a2b7..1706ec14fe9bff02b0def88a71561742c3399503 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -5,9 +5,9 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Event, Executor, Font, FontId, FontMetrics, FontRun, - GlobalPixels, GlyphId, LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, - RenderSvgParams, Result, Scene, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, FontRun, + GlobalPixels, GlyphId, InputEvent, LineLayout, Pixels, Point, RenderGlyphParams, + RenderImageParams, RenderSvgParams, Result, Scene, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -81,7 +81,7 @@ pub(crate) trait Platform: 'static { fn on_resign_active(&self, callback: Box); fn on_quit(&self, callback: Box); fn on_reopen(&self, callback: Box); - fn on_event(&self, callback: Box bool>); + fn on_event(&self, callback: Box bool>); fn os_name(&self) -> &'static str; fn os_version(&self) -> Result; @@ -141,7 +141,7 @@ pub(crate) trait PlatformWindow { fn minimize(&self); fn zoom(&self); fn toggle_full_screen(&self); - fn on_event(&self, callback: Box bool>); + fn on_event(&self, callback: Box bool>); fn on_active_status_change(&self, callback: Box); fn on_resize(&self, callback: Box, f32)>); fn on_fullscreen(&self, callback: Box); diff --git a/crates/gpui3/src/platform/mac/events.rs b/crates/gpui3/src/platform/mac/events.rs index d776d7ab02c3cac07589b29506cf04b7bc43df64..fa8277baf477299d9a9eb6b991c28a5f03ca7ceb 100644 --- a/crates/gpui3/src/platform/mac/events.rs +++ b/crates/gpui3/src/platform/mac/events.rs @@ -1,5 +1,5 @@ use crate::{ - point, px, Event, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, + point, px, InputEvent, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, ScrollDelta, ScrollWheelEvent, TouchPhase, }; @@ -84,7 +84,7 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers { } } -impl Event { +impl InputEvent { pub unsafe fn from_native(native_event: id, window_height: Option) -> Option { let event_type = native_event.eventType(); diff --git a/crates/gpui3/src/platform/mac/platform.rs b/crates/gpui3/src/platform/mac/platform.rs index 9feb0a9546bd406839abb5aeb9cb6f61ed053076..28be8f9d2e68504265c362097a9f6cb885c27376 100644 --- a/crates/gpui3/src/platform/mac/platform.rs +++ b/crates/gpui3/src/platform/mac/platform.rs @@ -1,6 +1,6 @@ use super::BoolExt; use crate::{ - AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Event, Executor, MacDispatcher, + AnyWindowHandle, ClipboardItem, CursorStyle, DisplayId, Executor, InputEvent, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions, @@ -153,7 +153,7 @@ pub struct MacPlatformState { resign_active: Option>, reopen: Option>, quit: Option>, - event: Option bool>>, + event: Option bool>>, // menu_command: Option>, // validate_menu_command: Option bool>>, will_open_menu: Option>, @@ -621,7 +621,7 @@ impl Platform for MacPlatform { self.0.lock().reopen = Some(callback); } - fn on_event(&self, callback: Box bool>) { + fn on_event(&self, callback: Box bool>) { self.0.lock().event = Some(callback); } @@ -937,7 +937,7 @@ unsafe fn get_foreground_platform(object: &mut Object) -> &MacPlatform { extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { unsafe { - if let Some(event) = Event::from_native(native_event, None) { + if let Some(event) = InputEvent::from_native(native_event, None) { let platform = get_foreground_platform(this); if let Some(callback) = platform.0.lock().event.as_mut() { if !callback(event) { diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 03ce03b34b96ecf90cbd91b8640487a1ca4e6eb3..c73d59edde992b9031847721338a7e8867136007 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,7 +1,7 @@ use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange}; use crate::{ - display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Event, Executor, - GlobalPixels, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, + display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, GlobalPixels, + InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, @@ -286,7 +286,7 @@ struct MacWindowState { renderer: MetalRenderer, scene_to_render: Option, kind: WindowKind, - event_callback: Option bool>>, + event_callback: Option bool>>, activate_callback: Option>, resize_callback: Option, f32)>>, fullscreen_callback: Option>, @@ -300,7 +300,7 @@ struct MacWindowState { synthetic_drag_counter: usize, last_fresh_keydown: Option, traffic_light_position: Option>, - previous_modifiers_changed_event: Option, + previous_modifiers_changed_event: Option, // State tracking what the IME did after the last request ime_state: ImeState, // Retains the last IME Text @@ -854,7 +854,7 @@ impl PlatformWindow for MacWindow { .detach(); } - fn on_event(&self, callback: Box bool>) { + fn on_event(&self, callback: Box bool>) { self.0.as_ref().lock().event_callback = Some(callback); } @@ -975,9 +975,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: let mut lock = window_state.as_ref().lock(); let window_height = lock.content_size().height; - let event = unsafe { Event::from_native(native_event, Some(window_height)) }; + let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) }; - if let Some(Event::KeyDown(event)) = event { + if let Some(InputEvent::KeyDown(event)) = event { // For certain keystrokes, macOS will first dispatch a "key equivalent" event. // If that event isn't handled, it will then dispatch a "key down" event. GPUI // makes no distinction between these two types of events, so we need to ignore @@ -1045,13 +1045,13 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: key: ime_text.clone().unwrap(), }, }; - handled = callback(Event::KeyDown(event_with_ime_text)); + handled = callback(InputEvent::KeyDown(event_with_ime_text)); } if !handled { // empty key happens when you type a deadkey in input composition. // (e.g. on a brazillian keyboard typing quote is a deadkey) if !event.keystroke.key.is_empty() { - handled = callback(Event::KeyDown(event)); + handled = callback(InputEvent::KeyDown(event)); } } } @@ -1097,11 +1097,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { let is_active = unsafe { lock.native_window.isKeyWindow() == YES }; let window_height = lock.content_size().height; - let event = unsafe { Event::from_native(native_event, Some(window_height)) }; + let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) }; if let Some(mut event) = event { let synthesized_second_event = match &mut event { - Event::MouseDown( + InputEvent::MouseDown( event @ MouseDownEvent { button: MouseButton::Left, modifiers: Modifiers { control: true, .. }, @@ -1118,7 +1118,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { ..*event }; - Some(Event::MouseDown(MouseDownEvent { + Some(InputEvent::MouseDown(MouseDownEvent { button: MouseButton::Right, ..*event })) @@ -1127,7 +1127,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { // Because we map a ctrl-left_down to a right_down -> right_up let's ignore // the ctrl-left_up to avoid having a mismatch in button down/up events if the // user is still holding ctrl when releasing the left mouse button - Event::MouseUp(MouseUpEvent { + InputEvent::MouseUp(MouseUpEvent { button: MouseButton::Left, modifiers: Modifiers { control: true, .. }, .. @@ -1140,7 +1140,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { }; match &event { - Event::MouseMoved( + InputEvent::MouseMoved( event @ MouseMoveEvent { pressed_button: Some(_), .. @@ -1157,18 +1157,18 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { .detach(); } - Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return, + InputEvent::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return, - Event::MouseUp(MouseUpEvent { + InputEvent::MouseUp(MouseUpEvent { button: MouseButton::Left, .. }) => { lock.synthetic_drag_counter += 1; } - Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => { + InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => { // Only raise modifiers changed event when they have actually changed - if let Some(Event::ModifiersChanged(ModifiersChangedEvent { + if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers: prev_modifiers, })) = &lock.previous_modifiers_changed_event { @@ -1204,7 +1204,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { modifiers: Default::default(), key: ".".into(), }; - let event = Event::KeyDown(KeyDownEvent { + let event = InputEvent::KeyDown(KeyDownEvent { keystroke: keystroke.clone(), is_held: false, }); @@ -1605,7 +1605,7 @@ async fn synthetic_drag( if lock.synthetic_drag_counter == drag_id { if let Some(mut callback) = lock.event_callback.take() { drop(lock); - callback(Event::MouseMoved(event.clone())); + callback(InputEvent::MouseMoved(event.clone())); window_state.lock().event_callback = Some(callback); } } else { diff --git a/crates/gpui3/src/platform/test.rs b/crates/gpui3/src/platform/test.rs index de836f125d11bfc9b38178cf13f35a18578b9d32..4d63bb415af369eb2f1158ed80397e9a7bb7c868 100644 --- a/crates/gpui3/src/platform/test.rs +++ b/crates/gpui3/src/platform/test.rs @@ -125,7 +125,7 @@ impl Platform for TestPlatform { unimplemented!() } - fn on_event(&self, _callback: Box bool>) { + fn on_event(&self, _callback: Box bool>) { unimplemented!() } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index f5fafbb0b45abb440d359dd59508b0892da25d90..e420a28813c73430c3109c76d5ba992665b8a588 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,12 +1,13 @@ use crate::{ px, size, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect, Element, EntityId, - Event, EventEmitter, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, IsZero, - KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, LayoutId, MainThread, MainThreadOnly, - MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, PlatformAtlas, PlatformWindow, Point, - PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, - ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, - Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, + InputEvent, IsZero, KeyDownEvent, KeyDownListener, KeyUpEvent, KeyUpListener, LayoutId, + MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform, + PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, + RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, + Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, + WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::{HashMap, HashSet}; @@ -47,13 +48,12 @@ pub struct FocusId(usize); #[derive(Clone)] pub struct FocusHandle { - id: FocusId, + pub(crate) id: FocusId, } impl FocusHandle { - pub fn focus(&self, cx: &mut WindowContext) { - cx.window.focus = Some(self.id); - cx.notify(); + pub(crate) fn new(id: FocusId) -> Self { + Self { id } } pub fn is_focused(&self, cx: &WindowContext) -> bool { @@ -95,6 +95,8 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, containing_focus: HashSet, + pub(crate) focus_change_listeners: + Vec>, key_down_listeners: Vec>, key_up_listeners: @@ -104,7 +106,8 @@ pub struct Window { scale_factor: f32, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, - focus: Option, + pub(crate) last_blur: Option>, + pub(crate) focus: Option, next_focus_id: FocusId, } @@ -168,6 +171,7 @@ impl Window { focus_parents_by_child: HashMap::default(), containing_focus: HashSet::default(), mouse_listeners: HashMap::default(), + focus_change_listeners: Vec::new(), key_down_listeners: Vec::new(), key_up_listeners: Vec::new(), propagate_event: true, @@ -175,6 +179,7 @@ impl Window { scale_factor, scene_builder: SceneBuilder::new(), dirty: true, + last_blur: None, focus: None, next_focus_id: FocusId(0), } @@ -232,6 +237,34 @@ impl<'a, 'w> WindowContext<'a, 'w> { FocusHandle { id } } + pub fn focus(&mut self, handle: &FocusHandle) { + if self.window.last_blur.is_none() { + self.window.last_blur = Some(self.window.focus); + } + + let window_id = self.window.handle.id; + self.window.focus = Some(handle.id); + self.push_effect(Effect::FocusChanged { + window_id, + focused: Some(handle.id), + }); + self.notify(); + } + + pub fn blur(&mut self) { + if self.window.last_blur.is_none() { + self.window.last_blur = Some(self.window.focus); + } + + let window_id = self.window.handle.id; + self.window.focus = None; + self.push_effect(Effect::FocusChanged { + window_id, + focused: None, + }); + self.notify(); + } + pub fn run_on_main( &mut self, f: impl FnOnce(&mut MainThread>) -> R + Send + 'static, @@ -720,6 +753,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { // Clear focus state, because we determine what is focused when the new elements // in the upcoming frame are initialized. + window.focus_change_listeners.clear(); window.key_down_listeners.clear(); window.key_up_listeners.clear(); window.containing_focus.clear(); @@ -730,7 +764,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.text_system().end_frame(); } - fn dispatch_event(&mut self, event: Event) -> bool { + fn dispatch_event(&mut self, event: InputEvent) -> bool { if let Some(any_mouse_event) = event.mouse_event() { if let Some(MouseMoveEvent { position, .. }) = any_mouse_event.downcast_ref() { self.window.mouse_position = *position;