Detailed changes
@@ -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<usize>, cx: &AppContext) -> Option<String> {
-// 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<Range<usize>> {
-// // 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::<OffsetUtf16>(cx).range();
-// Some(range.start.0..range.end.0)
-// }
-// fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
-// let snapshot = self.buffer.read(cx).read(cx);
-// let range = self.text_highlights::<InputComposition>(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>) {
-// self.clear_highlights::<InputComposition>(cx);
-// self.ime_transaction.take();
-// }
-
-// fn replace_text_in_range(
-// &mut self,
-// range_utf16: Option<Range<usize>>,
-// text: &str,
-// cx: &mut ViewContext<Self>,
-// ) {
-// 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<usize>) -> Option<String> {
+ // 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::<OffsetUtf16>(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<Range<usize>> {
+ // 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::<OffsetUtf16>(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<Range<usize>> {
+ // let snapshot = self.buffer.read(cx).read(cx);
+ // let range = self.text_highlights::<InputComposition>(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>) {
+ // self.clear_highlights::<InputComposition>(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<Range<usize>>,
+ // text: &str,
+ cx: &mut ViewContext<Self>,
+ ) {
+ // 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::<OffsetUtf16>(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<Range<usize>>,
-// text: &str,
-// new_selected_range_utf16: Option<Range<usize>>,
-// cx: &mut ViewContext<Self>,
-// ) {
-// 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::<OffsetUtf16>(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<Range<usize>>,
+ text: &str,
+ new_selected_range_utf16: Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ // 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::<OffsetUtf16>(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::<Vec<_>>()
-// };
+ // 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::<Vec<_>>()
+ // };
+
+ // if text.is_empty() {
+ // this.unmark_text(cx);
+ // } else {
+ // this.highlight_text::<InputComposition>(
+ // marked_ranges.clone(),
+ // this.style(cx).composition_mark,
+ // cx,
+ // );
+ // }
-// if text.is_empty() {
-// this.unmark_text(cx);
-// } else {
-// this.highlight_text::<InputComposition>(
-// 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::<Vec<_>>();
+
+ // 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::<Vec<_>>();
-
-// 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::<InputComposition>(cx).is_none() {
+ // self.ime_transaction.take();
+ // }
+ todo!()
+ }
-// if self.text_highlights::<InputComposition>(cx).is_none() {
-// self.ime_transaction.take();
-// }
-// }
-// }
+ fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<gpui::Bounds<f32>> {
+ todo!()
+ }
+}
// fn build_style(
// settings: &ThemeSettings,
@@ -2623,6 +2623,10 @@ impl Element<Editor> 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,
@@ -154,7 +154,7 @@ type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
// }
pub struct AppContext {
- this: Weak<AppCell>,
+ pub(crate) this: Weak<AppCell>,
pub(crate) platform: Rc<dyn Platform>,
app_metadata: AppMetadata,
text_system: Arc<TextSystem>,
@@ -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::{
@@ -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<Pixels>,
requested_cursor_style: Option<CursorStyle>,
+ requested_input_handler: Option<Box<dyn PlatformInputHandler>>,
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<V>(&mut self, handler: WeakView<V>, cx: ViewContext<V>)
+ 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<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
@@ -0,0 +1,89 @@
+use crate::{AnyWindowHandle, AppCell, Context, PlatformInputHandler, ViewContext, WeakView};
+use std::{ops::Range, rc::Weak};
+
+pub struct WindowInputHandler<V>
+where
+ V: InputHandler,
+{
+ pub cx: Weak<AppCell>,
+ pub window: AnyWindowHandle,
+ pub handler: WeakView<V>,
+}
+
+impl<V: InputHandler + 'static> PlatformInputHandler for WindowInputHandler<V> {
+ fn selected_text_range(&self) -> Option<std::ops::Range<usize>> {
+ self.update(|view, cx| view.selected_text_range(cx))
+ .flatten()
+ }
+
+ fn marked_text_range(&self) -> Option<std::ops::Range<usize>> {
+ self.update(|view, cx| view.marked_text_range(cx)).flatten()
+ }
+
+ fn text_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<String> {
+ self.update(|view, cx| view.text_for_range(range_utf16, cx))
+ .flatten()
+ }
+
+ fn replace_text_in_range(
+ &mut self,
+ replacement_range: Option<std::ops::Range<usize>>,
+ 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<std::ops::Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<std::ops::Range<usize>>,
+ ) {
+ 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<usize>) -> Option<crate::Bounds<f32>> {
+ self.update(|view, cx| view.bounds_for_range(range_utf16, cx))
+ .flatten()
+ }
+}
+
+impl<V: InputHandler + 'static> WindowInputHandler<V> {
+ fn update<T>(&self, f: impl FnOnce(&mut V, &mut ViewContext<V>) -> T) -> Option<T> {
+ 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<usize>, cx: &mut ViewContext<Self>) -> Option<String>;
+ fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
+ fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
+ fn replace_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ text: &str,
+ cx: &mut ViewContext<Self>,
+ );
+ fn replace_and_mark_text_in_range(
+ &mut self,
+ range: Option<Range<usize>>,
+ new_text: &str,
+ new_selected_range: Option<Range<usize>>,
+ cx: &mut ViewContext<Self>,
+ );
+ fn bounds_for_range(
+ &self,
+ range_utf16: std::ops::Range<usize>,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<crate::Bounds<f32>>;
+}