diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index c63c531cebddcfa1f807ee5460a54b1976d9c175..61db3fb49253d1a5ceeb06f7fe45900f841cb8ab 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -38,8 +38,8 @@ use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ actions, div, px, relative, AnyElement, AppContext, BackgroundExecutor, Context, DispatchContext, Div, Element, Entity, EventEmitter, FocusHandle, FontStyle, FontWeight, Hsla, - Model, Pixels, Render, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, - WeakView, WindowContext, + Model, Pixels, PlatformInputHandler, Render, Styled, Subscription, Task, TextStyle, View, + ViewContext, VisualContext, WeakView, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -82,7 +82,7 @@ use std::{ }; pub use sum_tree::Bias; use sum_tree::TreeMap; -use text::Rope; +use text::{OffsetUtf16, Rope}; use theme::{ ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings, }; @@ -9485,214 +9485,225 @@ impl Render for Editor { // false // } -// -// fn text_for_range(&self, range_utf16: Range, cx: &AppContext) -> Option { -// Some( -// self.buffer -// .read(cx) -// .read(cx) -// .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end)) -// .collect(), -// ) -// } - -// fn selected_text_range(&self, cx: &AppContext) -> Option> { -// // Prevent the IME menu from appearing when holding down an alphabetic key -// // while input is disabled. -// if !self.input_enabled { -// return None; -// } - -// let range = self.selections.newest::(cx).range(); -// Some(range.start.0..range.end.0) -// } -// fn marked_text_range(&self, cx: &AppContext) -> Option> { -// let snapshot = self.buffer.read(cx).read(cx); -// let range = self.text_highlights::(cx)?.1.get(0)?; -// Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0) -// } - -// fn unmark_text(&mut self, cx: &mut ViewContext) { -// self.clear_highlights::(cx); -// self.ime_transaction.take(); -// } - -// fn replace_text_in_range( -// &mut self, -// range_utf16: Option>, -// text: &str, -// cx: &mut ViewContext, -// ) { -// if !self.input_enabled { -// cx.emit(Event::InputIgnored { text: text.into() }); -// return; -// } - -// self.transact(cx, |this, cx| { -// let new_selected_ranges = if let Some(range_utf16) = range_utf16 { -// let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); -// Some(this.selection_replacement_ranges(range_utf16, cx)) -// } else { -// this.marked_text_ranges(cx) -// }; +impl PlatformInputHandler for Editor { + fn text_for_range(&self, range_utf16: Range) -> Option { + // Some( + // self.buffer + // .read(cx) + // .read(cx) + // .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end)) + // .collect(), + // ) + todo!() + } -// let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| { -// let newest_selection_id = this.selections.newest_anchor().id; -// this.selections -// .all::(cx) -// .iter() -// .zip(ranges_to_replace.iter()) -// .find_map(|(selection, range)| { -// if selection.id == newest_selection_id { -// Some( -// (range.start.0 as isize - selection.head().0 as isize) -// ..(range.end.0 as isize - selection.head().0 as isize), -// ) -// } else { -// None -// } -// }) -// }); + fn selected_text_range(&self) -> Option> { + // Prevent the IME menu from appearing when holding down an alphabetic key + // while input is disabled. + // if !self.input_enabled { + // return None; + // } -// cx.emit(Event::InputHandled { -// utf16_range_to_replace: range_to_replace, -// text: text.into(), -// }); + // let range = self.selections.newest::(cx).range(); + // Some(range.start.0..range.end.0) + todo!() + } -// if let Some(new_selected_ranges) = new_selected_ranges { -// this.change_selections(None, cx, |selections| { -// selections.select_ranges(new_selected_ranges) -// }); -// } + fn marked_text_range(&self, cx: &AppContext) -> Option> { + // let snapshot = self.buffer.read(cx).read(cx); + // let range = self.text_highlights::(cx)?.1.get(0)?; + // Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0) + todo!() + } -// this.handle_input(text, cx); -// }); + fn unmark_text(&mut self, cx: &mut ViewContext) { + // self.clear_highlights::(cx); + // self.ime_transaction.take(); + todo!() + } -// if let Some(transaction) = self.ime_transaction { -// self.buffer.update(cx, |buffer, cx| { -// buffer.group_until_transaction(transaction, cx); -// }); -// } + fn replace_text_in_range( + &mut self, + //range_utf16: Option>, + // text: &str, + cx: &mut ViewContext, + ) { + // if !self.input_enabled { + // cx.emit(Event::InputIgnored { text: text.into() }); + // return; + // } -// self.unmark_text(cx); -// } + // self.transact(cx, |this, cx| { + // let new_selected_ranges = if let Some(range_utf16) = range_utf16 { + // let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); + // Some(this.selection_replacement_ranges(range_utf16, cx)) + // } else { + // this.marked_text_ranges(cx) + // }; + + // let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| { + // let newest_selection_id = this.selections.newest_anchor().id; + // this.selections + // .all::(cx) + // .iter() + // .zip(ranges_to_replace.iter()) + // .find_map(|(selection, range)| { + // if selection.id == newest_selection_id { + // Some( + // (range.start.0 as isize - selection.head().0 as isize) + // ..(range.end.0 as isize - selection.head().0 as isize), + // ) + // } else { + // None + // } + // }) + // }); + + // cx.emit(Event::InputHandled { + // utf16_range_to_replace: range_to_replace, + // text: text.into(), + // }); + + // if let Some(new_selected_ranges) = new_selected_ranges { + // this.change_selections(None, cx, |selections| { + // selections.select_ranges(new_selected_ranges) + // }); + // } -// fn replace_and_mark_text_in_range( -// &mut self, -// range_utf16: Option>, -// text: &str, -// new_selected_range_utf16: Option>, -// cx: &mut ViewContext, -// ) { -// if !self.input_enabled { -// cx.emit(Event::InputIgnored { text: text.into() }); -// return; -// } + // this.handle_input(text, cx); + // }); -// let transaction = self.transact(cx, |this, cx| { -// let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) { -// let snapshot = this.buffer.read(cx).read(cx); -// if let Some(relative_range_utf16) = range_utf16.as_ref() { -// for marked_range in &mut marked_ranges { -// marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end; -// marked_range.start.0 += relative_range_utf16.start; -// marked_range.start = -// snapshot.clip_offset_utf16(marked_range.start, Bias::Left); -// marked_range.end = -// snapshot.clip_offset_utf16(marked_range.end, Bias::Right); -// } -// } -// Some(marked_ranges) -// } else if let Some(range_utf16) = range_utf16 { -// let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); -// Some(this.selection_replacement_ranges(range_utf16, cx)) -// } else { -// None -// }; + // if let Some(transaction) = self.ime_transaction { + // self.buffer.update(cx, |buffer, cx| { + // buffer.group_until_transaction(transaction, cx); + // }); + // } -// let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| { -// let newest_selection_id = this.selections.newest_anchor().id; -// this.selections -// .all::(cx) -// .iter() -// .zip(ranges_to_replace.iter()) -// .find_map(|(selection, range)| { -// if selection.id == newest_selection_id { -// Some( -// (range.start.0 as isize - selection.head().0 as isize) -// ..(range.end.0 as isize - selection.head().0 as isize), -// ) -// } else { -// None -// } -// }) -// }); + // self.unmark_text(cx); + todo!() + } -// cx.emit(Event::InputHandled { -// utf16_range_to_replace: range_to_replace, -// text: text.into(), -// }); + fn replace_and_mark_text_in_range( + &mut self, + range_utf16: Option>, + text: &str, + new_selected_range_utf16: Option>, + cx: &mut ViewContext, + ) { + // if !self.input_enabled { + // cx.emit(Event::InputIgnored { text: text.into() }); + // return; + // } -// if let Some(ranges) = ranges_to_replace { -// this.change_selections(None, cx, |s| s.select_ranges(ranges)); -// } + // let transaction = self.transact(cx, |this, cx| { + // let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) { + // let snapshot = this.buffer.read(cx).read(cx); + // if let Some(relative_range_utf16) = range_utf16.as_ref() { + // for marked_range in &mut marked_ranges { + // marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end; + // marked_range.start.0 += relative_range_utf16.start; + // marked_range.start = + // snapshot.clip_offset_utf16(marked_range.start, Bias::Left); + // marked_range.end = + // snapshot.clip_offset_utf16(marked_range.end, Bias::Right); + // } + // } + // Some(marked_ranges) + // } else if let Some(range_utf16) = range_utf16 { + // let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); + // Some(this.selection_replacement_ranges(range_utf16, cx)) + // } else { + // None + // }; + + // let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| { + // let newest_selection_id = this.selections.newest_anchor().id; + // this.selections + // .all::(cx) + // .iter() + // .zip(ranges_to_replace.iter()) + // .find_map(|(selection, range)| { + // if selection.id == newest_selection_id { + // Some( + // (range.start.0 as isize - selection.head().0 as isize) + // ..(range.end.0 as isize - selection.head().0 as isize), + // ) + // } else { + // None + // } + // }) + // }); + + // cx.emit(Event::InputHandled { + // utf16_range_to_replace: range_to_replace, + // text: text.into(), + // }); + + // if let Some(ranges) = ranges_to_replace { + // this.change_selections(None, cx, |s| s.select_ranges(ranges)); + // } -// let marked_ranges = { -// let snapshot = this.buffer.read(cx).read(cx); -// this.selections -// .disjoint_anchors() -// .iter() -// .map(|selection| { -// selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot) -// }) -// .collect::>() -// }; + // let marked_ranges = { + // let snapshot = this.buffer.read(cx).read(cx); + // this.selections + // .disjoint_anchors() + // .iter() + // .map(|selection| { + // selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot) + // }) + // .collect::>() + // }; + + // if text.is_empty() { + // this.unmark_text(cx); + // } else { + // this.highlight_text::( + // marked_ranges.clone(), + // this.style(cx).composition_mark, + // cx, + // ); + // } -// if text.is_empty() { -// this.unmark_text(cx); -// } else { -// this.highlight_text::( -// marked_ranges.clone(), -// this.style(cx).composition_mark, -// cx, -// ); -// } + // this.handle_input(text, cx); + + // if let Some(new_selected_range) = new_selected_range_utf16 { + // let snapshot = this.buffer.read(cx).read(cx); + // let new_selected_ranges = marked_ranges + // .into_iter() + // .map(|marked_range| { + // let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0; + // let new_start = OffsetUtf16(new_selected_range.start + insertion_start); + // let new_end = OffsetUtf16(new_selected_range.end + insertion_start); + // snapshot.clip_offset_utf16(new_start, Bias::Left) + // ..snapshot.clip_offset_utf16(new_end, Bias::Right) + // }) + // .collect::>(); + + // drop(snapshot); + // this.change_selections(None, cx, |selections| { + // selections.select_ranges(new_selected_ranges) + // }); + // } + // }); -// this.handle_input(text, cx); - -// if let Some(new_selected_range) = new_selected_range_utf16 { -// let snapshot = this.buffer.read(cx).read(cx); -// let new_selected_ranges = marked_ranges -// .into_iter() -// .map(|marked_range| { -// let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0; -// let new_start = OffsetUtf16(new_selected_range.start + insertion_start); -// let new_end = OffsetUtf16(new_selected_range.end + insertion_start); -// snapshot.clip_offset_utf16(new_start, Bias::Left) -// ..snapshot.clip_offset_utf16(new_end, Bias::Right) -// }) -// .collect::>(); - -// drop(snapshot); -// this.change_selections(None, cx, |selections| { -// selections.select_ranges(new_selected_ranges) -// }); -// } -// }); + // self.ime_transaction = self.ime_transaction.or(transaction); + // if let Some(transaction) = self.ime_transaction { + // self.buffer.update(cx, |buffer, cx| { + // buffer.group_until_transaction(transaction, cx); + // }); + // } -// self.ime_transaction = self.ime_transaction.or(transaction); -// if let Some(transaction) = self.ime_transaction { -// self.buffer.update(cx, |buffer, cx| { -// buffer.group_until_transaction(transaction, cx); -// }); -// } + // if self.text_highlights::(cx).is_none() { + // self.ime_transaction.take(); + // } + todo!() + } -// if self.text_highlights::(cx).is_none() { -// self.ime_transaction.take(); -// } -// } -// } + fn bounds_for_range(&self, range_utf16: Range) -> Option> { + todo!() + } +} // fn build_style( // settings: &ThemeSettings, diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 5af5d97e03ceed35569acbb950e0f75d3fceb7c6..3ff68e18978161185f55dffa3eb0eab55a49ed3e 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -2623,6 +2623,10 @@ impl Element for EditorElement { } }); + if editor.focus_handle.is_focused(cx) { + cx.set_input_handler(editor.handle); + } + cx.with_content_mask(ContentMask { bounds }, |cx| { let gutter_bounds = Bounds { origin: bounds.origin, diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 63d2143a6726773be7508c77c72bdd6b1052fe3d..42d34e0c202e9ceaddbabeb85e6f6fac23502d39 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -154,7 +154,7 @@ type ReleaseListener = Box; // } pub struct AppContext { - this: Weak, + pub(crate) this: Weak, pub(crate) platform: Rc, app_metadata: AppMetadata, text_system: Arc, diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index 348da17356dd3f48d1a507cfcb0c8d19a4413d2b..e59b196a91ff252da9a41179912b1e7e718ba0c1 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -24,6 +24,7 @@ mod text_system; mod util; mod view; mod window; +mod window_input_handler; mod private { /// A mechanism for restricting implementations of a trait to only those in GPUI. @@ -64,6 +65,7 @@ pub use text_system::*; pub use util::arc_cow::ArcCow; pub use view::*; pub use window::*; +pub use window_input_handler::*; use derive_more::{Deref, DerefMut}; use std::{ diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 5b0349c7d48ff9a84478b624205b65c9c1be2c75..4fc940069cafce0b63cc1e2605e22dfeb25ba685 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -2,13 +2,14 @@ use crate::{ px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, - GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, - KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, - PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, - RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, - VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, + GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, InputHandler, IsZero, KeyListener, + KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, + MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, + PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, + Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription, + TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView, + WindowBounds, WindowInputHandler, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; use collections::HashMap; @@ -191,6 +192,7 @@ pub struct Window { default_prevented: bool, mouse_position: Point, requested_cursor_style: Option, + requested_input_handler: Option>, scale_factor: f32, bounds: WindowBounds, bounds_observers: SubscriberSet<(), AnyObserver>, @@ -285,6 +287,7 @@ impl Window { default_prevented: true, mouse_position, requested_cursor_style: None, + requested_input_handler: None, scale_factor, bounds, bounds_observers: SubscriberSet::new(), @@ -676,6 +679,17 @@ impl<'a> WindowContext<'a> { self.window.requested_cursor_style = Some(style) } + pub fn set_input_handler(&mut self, handler: WeakView, cx: ViewContext) + where + V: InputHandler + 'static, + { + self.window.requested_input_handler = Some(Box::new(WindowInputHandler { + cx: cx.app.this.clone(), + window: cx.window_handle(), + handler, + })) + } + /// Called during painting to invoke the given closure in a new stacking context. The given /// z-index is interpreted relative to the previous call to `stack`. pub fn stack(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { diff --git a/crates/gpui2/src/window_input_handler.rs b/crates/gpui2/src/window_input_handler.rs new file mode 100644 index 0000000000000000000000000000000000000000..caae5838ce46171a1584b272d7e916d100e13881 --- /dev/null +++ b/crates/gpui2/src/window_input_handler.rs @@ -0,0 +1,89 @@ +use crate::{AnyWindowHandle, AppCell, Context, PlatformInputHandler, ViewContext, WeakView}; +use std::{ops::Range, rc::Weak}; + +pub struct WindowInputHandler +where + V: InputHandler, +{ + pub cx: Weak, + pub window: AnyWindowHandle, + pub handler: WeakView, +} + +impl PlatformInputHandler for WindowInputHandler { + fn selected_text_range(&self) -> Option> { + self.update(|view, cx| view.selected_text_range(cx)) + .flatten() + } + + fn marked_text_range(&self) -> Option> { + self.update(|view, cx| view.marked_text_range(cx)).flatten() + } + + fn text_for_range(&self, range_utf16: std::ops::Range) -> Option { + self.update(|view, cx| view.text_for_range(range_utf16, cx)) + .flatten() + } + + fn replace_text_in_range( + &mut self, + replacement_range: Option>, + text: &str, + ) { + self.update(|view, cx| view.replace_text_in_range(replacement_range, text, cx)); + } + + fn replace_and_mark_text_in_range( + &mut self, + range_utf16: Option>, + new_text: &str, + new_selected_range: Option>, + ) { + self.update(|view, cx| { + view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx) + }); + } + + fn unmark_text(&mut self) { + self.update(|view, cx| view.unmark_text(cx)); + } + + fn bounds_for_range(&self, range_utf16: std::ops::Range) -> Option> { + self.update(|view, cx| view.bounds_for_range(range_utf16, cx)) + .flatten() + } +} + +impl WindowInputHandler { + fn update(&self, f: impl FnOnce(&mut V, &mut ViewContext) -> T) -> Option { + let cx = self.cx.upgrade()?; + let mut cx = cx.borrow_mut(); + cx.update_window(self.window, |_, cx| self.handler.update(cx, f).ok()) + .ok()? + } +} + +pub trait InputHandler: Sized { + fn text_for_range(&self, range: Range, cx: &mut ViewContext) -> Option; + fn selected_text_range(&self, cx: &mut ViewContext) -> Option>; + fn marked_text_range(&self, cx: &mut ViewContext) -> Option>; + fn unmark_text(&mut self, cx: &mut ViewContext); + fn replace_text_in_range( + &mut self, + range: Option>, + text: &str, + cx: &mut ViewContext, + ); + fn replace_and_mark_text_in_range( + &mut self, + range: Option>, + new_text: &str, + new_selected_range: Option>, + cx: &mut ViewContext, + ); + fn bounds_for_range( + &self, + range_utf16: std::ops::Range, + cx: &mut ViewContext, + ) -> Option>; +}