Position IME input according to where the selection is rendered

This commit is contained in:
Antonio Scandurra 2022-07-21 17:35:40 +02:00
parent 3c5d7e001e
commit 97ce3998ec
31 changed files with 563 additions and 27 deletions

View File

@ -11,7 +11,7 @@ use gpui::{
fonts::{FontId, HighlightStyle},
Entity, ModelContext, ModelHandle,
};
use language::{Point, Subscription as BufferSubscription};
use language::{OffsetUtf16, Point, Subscription as BufferSubscription};
use settings::Settings;
use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
use sum_tree::{Bias, TreeMap};
@ -549,6 +549,12 @@ impl ToDisplayPoint for usize {
}
}
impl ToDisplayPoint for OffsetUtf16 {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
self.to_offset(&map.buffer_snapshot).to_display_point(map)
}
}
impl ToDisplayPoint for Point {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
map.point_to_display_point(*self, Bias::Left)

View File

@ -30,7 +30,7 @@ use gpui::{
WeakViewHandle,
};
use json::json;
use language::{Bias, DiagnosticSeverity, Selection};
use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection};
use project::ProjectPath;
use settings::Settings;
use smallvec::SmallVec;
@ -1517,6 +1517,43 @@ impl Element for EditorElement {
}
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
bounds: RectF,
_: RectF,
layout: &Self::LayoutState,
_: &Self::PaintState,
_: &gpui::MeasurementContext,
) -> Option<RectF> {
let text_bounds = RectF::new(
bounds.origin() + vec2f(layout.gutter_size.x(), 0.0),
layout.text_size,
);
let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.);
let scroll_position = layout.snapshot.scroll_position();
let start_row = scroll_position.y() as u32;
let scroll_top = scroll_position.y() * layout.line_height;
let scroll_left = scroll_position.x() * layout.em_width;
let range_start =
OffsetUtf16(range_utf16.start).to_display_point(&layout.snapshot.display_snapshot);
if range_start.row() < start_row {
return None;
}
let line = layout
.line_layouts
.get((range_start.row() - start_row) as usize)?;
let range_start_x = line.x_for_index(range_start.column() as usize);
let range_start_y = range_start.row() as f32 * layout.line_height;
Some(RectF::new(
content_origin + vec2f(range_start_x, range_start_y + layout.line_height)
- vec2f(scroll_left, scroll_top),
vec2f(layout.em_width, layout.line_height),
))
}
fn debug(
&self,
bounds: RectF,

View File

@ -2,11 +2,12 @@ use gpui::{
color::Color,
fonts::{Properties, Weight},
text_layout::RunStyle,
DebugContext, Element as _, Quad,
DebugContext, Element as _, MeasurementContext, Quad,
};
use log::LevelFilter;
use pathfinder_geometry::rect::RectF;
use simplelog::SimpleLogger;
use std::ops::Range;
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
@ -112,6 +113,18 @@ impl gpui::Element for TextElement {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
_: RectF,

View File

@ -3,6 +3,7 @@ pub mod action;
use crate::{
elements::ElementBox,
executor::{self, Task},
geometry::rect::RectF,
keymap::{self, Binding, Keystroke},
platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions},
presenter::Presenter,
@ -445,6 +446,13 @@ 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)
}
}
#[cfg(any(test, feature = "test-support"))]

View File

@ -31,7 +31,9 @@ use crate::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json, Action, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext,
json,
presenter::MeasurementContext,
Action, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext,
SizeConstraint, View,
};
use core::panic;
@ -41,7 +43,7 @@ use std::{
borrow::Cow,
cell::RefCell,
mem,
ops::{Deref, DerefMut},
ops::{Deref, DerefMut, Range},
rc::Rc,
};
@ -49,6 +51,11 @@ trait AnyElement {
fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F;
fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext);
fn dispatch_event(&mut self, event: &Event, cx: &mut EventContext) -> bool;
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
cx: &MeasurementContext,
) -> Option<RectF>;
fn debug(&self, cx: &DebugContext) -> serde_json::Value;
fn size(&self) -> Vector2F;
@ -83,6 +90,16 @@ pub trait Element {
cx: &mut EventContext,
) -> bool;
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
bounds: RectF,
visible_bounds: RectF,
layout: &Self::LayoutState,
paint: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF>;
fn metadata(&self) -> Option<&dyn Any> {
None
}
@ -287,6 +304,26 @@ impl<T: Element> AnyElement for Lifecycle<T> {
}
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
cx: &MeasurementContext,
) -> Option<RectF> {
if let Lifecycle::PostPaint {
element,
bounds,
visible_bounds,
layout,
paint,
..
} = self
{
element.rect_for_text_range(range_utf16, *bounds, *visible_bounds, layout, paint, cx)
} else {
None
}
}
fn size(&self) -> Vector2F {
match self {
Lifecycle::Empty | Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
@ -385,6 +422,14 @@ impl ElementRc {
self.element.borrow_mut().dispatch_event(event, cx)
}
pub fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
cx: &MeasurementContext,
) -> Option<RectF> {
self.element.borrow().rect_for_text_range(range_utf16, cx)
}
pub fn size(&self) -> Vector2F {
self.element.borrow().size()
}

View File

@ -1,6 +1,8 @@
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
json,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
use json::ToJson;
@ -94,6 +96,18 @@ impl Element for Align {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: std::ops::Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
bounds: pathfinder_geometry::rect::RectF,

View File

@ -1,6 +1,7 @@
use super::Element;
use crate::{
json::{self, json},
presenter::MeasurementContext,
DebugContext, PaintContext,
};
use json::ToJson;
@ -67,6 +68,18 @@ where
false
}
fn rect_for_text_range(
&self,
_: std::ops::Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -1,9 +1,13 @@
use std::ops::Range;
use json::ToJson;
use serde_json::json;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
json,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
@ -165,6 +169,18 @@ impl Element for ConstrainedBox {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -1,3 +1,5 @@
use std::ops::Range;
use crate::{
color::Color,
geometry::{
@ -7,6 +9,7 @@ use crate::{
},
json::ToJson,
platform::CursorStyle,
presenter::MeasurementContext,
scene::{self, Border, CursorRegion, Quad},
Element, ElementBox, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
@ -271,6 +274,18 @@ impl Element for Container {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
bounds: RectF,

View File

@ -1,9 +1,12 @@
use std::ops::Range;
use crate::{
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
presenter::MeasurementContext,
DebugContext,
};
use crate::{Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint};
@ -67,6 +70,18 @@ impl Element for Empty {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -1,11 +1,11 @@
use crate::{
geometry::vector::Vector2F, CursorRegion, DebugContext, Element, ElementBox, Event,
EventContext, LayoutContext, MouseButton, MouseEvent, MouseRegion, NavigationDirection,
PaintContext, SizeConstraint,
geometry::vector::Vector2F, presenter::MeasurementContext, CursorRegion, DebugContext, Element,
ElementBox, Event, EventContext, LayoutContext, MouseButton, MouseEvent, MouseRegion,
NavigationDirection, PaintContext, SizeConstraint,
};
use pathfinder_geometry::rect::RectF;
use serde_json::json;
use std::{any::TypeId, rc::Rc};
use std::{any::TypeId, ops::Range, rc::Rc};
pub struct EventHandler {
child: ElementBox,
@ -158,6 +158,18 @@ impl Element for EventHandler {
}
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -1,6 +1,10 @@
use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
json,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
use serde_json::json;
@ -74,6 +78,18 @@ impl Element for Expanded {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -1,7 +1,8 @@
use std::{any::Any, f32::INFINITY};
use std::{any::Any, f32::INFINITY, ops::Range};
use crate::{
json::{self, ToJson, Value},
presenter::MeasurementContext,
Axis, DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext,
LayoutContext, MouseMovedEvent, PaintContext, RenderContext, ScrollWheelEvent, SizeConstraint,
Vector2FExt, View,
@ -334,6 +335,20 @@ impl Element for Flex {
handled
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.children
.iter()
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), cx))
}
fn debug(
&self,
bounds: RectF,
@ -417,6 +432,18 @@ impl Element for FlexItem {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn metadata(&self) -> Option<&dyn Any> {
Some(&self.metadata)
}

View File

@ -1,6 +1,9 @@
use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::json,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
@ -65,6 +68,18 @@ impl Element for Hook {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -5,11 +5,12 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{json, ToJson},
presenter::MeasurementContext,
scene, Border, DebugContext, Element, Event, EventContext, ImageData, LayoutContext,
PaintContext, SizeConstraint,
};
use serde::Deserialize;
use std::sync::Arc;
use std::{ops::Range, sync::Arc};
pub struct Image {
data: Arc<ImageData>,
@ -89,6 +90,18 @@ impl Element for Image {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -76,6 +76,18 @@ impl Element for KeystrokeLabel {
element.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
_: RectF,

View File

@ -1,3 +1,5 @@
use std::ops::Range;
use crate::{
fonts::TextStyle,
geometry::{
@ -5,6 +7,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{ToJson, Value},
presenter::MeasurementContext,
text_layout::{Line, RunStyle},
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
@ -174,6 +177,18 @@ impl Element for Label {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -4,6 +4,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::json,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, ElementRc, Event, EventContext, LayoutContext, PaintContext,
RenderContext, ScrollWheelEvent, SizeConstraint, View, ViewContext,
};
@ -328,6 +329,39 @@ impl Element for List {
handled
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
bounds: RectF,
_: RectF,
scroll_top: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
let state = self.state.0.borrow();
let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
let mut cursor = state.items.cursor::<Count>();
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
while let Some(item) = cursor.item() {
if item_origin.y() > bounds.max_y() {
break;
}
if let ListItem::Rendered(element) = item {
if let Some(rect) = element.rect_for_text_range(range_utf16.clone(), cx) {
return Some(rect);
}
item_origin.set_y(item_origin.y() + element.size().y());
cursor.next(&());
} else {
unreachable!();
}
}
None
}
fn debug(
&self,
bounds: RectF,
@ -939,6 +973,18 @@ mod tests {
todo!()
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
todo!()
}
fn debug(&self, _: RectF, _: &(), _: &(), _: &DebugContext) -> serde_json::Value {
self.id.into()
}

View File

@ -1,4 +1,4 @@
use std::{any::TypeId, rc::Rc};
use std::{any::TypeId, ops::Range, rc::Rc};
use super::Padding;
use crate::{
@ -8,8 +8,8 @@ use crate::{
},
platform::CursorStyle,
scene::CursorRegion,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion, MouseState,
PaintContext, RenderContext, SizeConstraint, View,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
MouseState, PaintContext, RenderContext, SizeConstraint, View, presenter::MeasurementContext,
};
use serde_json::json;
@ -192,6 +192,18 @@ impl Element for MouseEventHandler {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -1,6 +1,9 @@
use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::ToJson,
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
PaintContext, SizeConstraint,
};
@ -126,6 +129,18 @@ impl Element for Overlay {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -1,6 +1,9 @@
use std::ops::Range;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
json::{self, json, ToJson},
presenter::MeasurementContext,
DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
SizeConstraint,
};
@ -64,6 +67,21 @@ impl Element for Stack {
false
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.children
.iter()
.rev()
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), cx))
}
fn debug(
&self,
bounds: RectF,

View File

@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, ops::Range};
use serde_json::json;
@ -8,6 +8,7 @@ use crate::{
rect::RectF,
vector::{vec2f, Vector2F},
},
presenter::MeasurementContext,
scene, DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
@ -84,6 +85,18 @@ impl Element for Svg {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -6,6 +6,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{ToJson, Value},
presenter::MeasurementContext,
text_layout::{Line, RunStyle, ShapedBoundary},
DebugContext, Element, Event, EventContext, FontCache, LayoutContext, PaintContext,
SizeConstraint, TextLayoutCache,
@ -63,7 +64,7 @@ impl Element for Text {
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
// Convert the string and highlight ranges into an iterator of highlighted chunks.
let mut offset = 0;
let mut highlight_ranges = self.highlights.iter().peekable();
let chunks = std::iter::from_fn(|| {
@ -81,7 +82,8 @@ impl Element for Text {
"Highlight out of text range. Text len: {}, Highlight range: {}..{}",
self.text.len(),
range.start,
range.end);
range.end
);
result = None;
}
} else if offset < self.text.len() {
@ -188,6 +190,18 @@ impl Element for Text {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: RectF,

View File

@ -6,12 +6,14 @@ use crate::{
fonts::TextStyle,
geometry::{rect::RectF, vector::Vector2F},
json::json,
presenter::MeasurementContext,
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, RenderContext, SizeConstraint,
Task, View,
};
use serde::Deserialize;
use std::{
cell::{Cell, RefCell},
ops::Range,
rc::Rc,
time::Duration,
};
@ -196,6 +198,18 @@ impl Element for Tooltip {
self.child.dispatch_event(event, cx)
}
fn rect_for_text_range(
&self,
range: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
self.child.rect_for_text_range(range, cx)
}
fn debug(
&self,
_: RectF,

View File

@ -5,6 +5,7 @@ use crate::{
vector::{vec2f, Vector2F},
},
json::{self, json},
presenter::MeasurementContext,
ElementBox, RenderContext, ScrollWheelEvent, View,
};
use json::ToJson;
@ -327,6 +328,21 @@ impl Element for UniformList {
handled
}
fn rect_for_text_range(
&self,
range: Range<usize>,
_: RectF,
_: RectF,
layout: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
layout
.items
.iter()
.find_map(|child| child.rect_for_text_range(range.clone(), cx))
}
fn debug(
&self,
bounds: RectF,

View File

@ -30,7 +30,8 @@ pub mod platform;
pub use gpui_macros::test;
pub use platform::*;
pub use presenter::{
Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
Axis, DebugContext, EventContext, LayoutContext, MeasurementContext, PaintContext,
SizeConstraint, Vector2FExt,
};
pub use anyhow;

View File

@ -91,17 +91,18 @@ pub trait Dispatcher: Send + Sync {
pub trait InputHandler {
fn selected_text_range(&self) -> Option<Range<usize>>;
fn set_selected_text_range(&mut self, range: Range<usize>);
fn text_for_range(&self, range: Range<usize>) -> Option<String>;
fn set_selected_text_range(&mut self, range_utf16: Range<usize>);
fn text_for_range(&self, range_utf16: Range<usize>) -> Option<String>;
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
range_utf16: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
);
fn marked_text_range(&self) -> Option<Range<usize>>;
fn unmark_text(&mut self);
fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF>;
}
pub trait Window: WindowContext {

View File

@ -1003,8 +1003,33 @@ extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
.map_or(NSRange::invalid(), |range| range.into())
}
extern "C" fn first_rect_for_character_range(_: &Object, _: Sel, _: NSRange, _: id) -> NSRect {
NSRect::new(NSPoint::new(0., 0.), NSSize::new(20., 20.))
extern "C" fn first_rect_for_character_range(
this: &Object,
_: Sel,
range: NSRange,
_: id,
) -> NSRect {
let frame = unsafe {
let window = get_window_state(this).borrow().native_window;
NSView::frame(window)
};
with_input_handler(this, |input_handler| {
input_handler.rect_for_range(range.to_range()?)
})
.flatten()
.map_or(
NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
|rect| {
NSRect::new(
NSPoint::new(
frame.origin.x + rect.origin_x() as f64,
frame.origin.y + frame.size.height - rect.origin_y() as f64,
),
NSSize::new(rect.width() as f64, rect.height() as f64),
)
},
)
}
extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {

View File

@ -19,7 +19,7 @@ use smallvec::SmallVec;
use std::{
collections::{HashMap, HashSet},
marker::PhantomData,
ops::{Deref, DerefMut},
ops::{Deref, DerefMut, Range},
sync::Arc,
};
@ -224,6 +224,17 @@ impl Presenter {
}
}
pub fn rect_for_text_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<RectF> {
cx.focused_view_id(self.window_id).and_then(|view_id| {
let cx = MeasurementContext {
app: cx,
rendered_views: &self.rendered_views,
window_id: self.window_id,
};
cx.rect_for_text_range(view_id, range_utf16)
})
}
pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) -> bool {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
let mut invalidated_views = Vec::new();
@ -777,6 +788,27 @@ impl<'a> DerefMut for EventContext<'a> {
}
}
pub struct MeasurementContext<'a> {
app: &'a AppContext,
rendered_views: &'a HashMap<usize, ElementBox>,
pub window_id: usize,
}
impl<'a> Deref for MeasurementContext<'a> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a> MeasurementContext<'a> {
fn rect_for_text_range(&self, view_id: usize, range_utf16: Range<usize>) -> Option<RectF> {
let element = self.rendered_views.get(&view_id)?;
element.rect_for_text_range(range_utf16, self)
}
}
pub struct DebugContext<'a> {
rendered_views: &'a HashMap<usize, ElementBox>,
pub font_cache: &'a FontCache,
@ -936,6 +968,18 @@ impl Element for ChildView {
cx.dispatch_event(self.view.id(), event)
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &MeasurementContext,
) -> Option<RectF> {
cx.rect_for_text_range(self.view.id(), range_utf16)
}
fn debug(
&self,
bounds: RectF,

View File

@ -395,6 +395,18 @@ impl Element for TerminalEl {
}
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &gpui::MeasurementContext,
) -> Option<RectF> {
todo!()
}
fn debug(
&self,
_bounds: gpui::geometry::rect::RectF,

View File

@ -43,6 +43,7 @@ use std::{
fmt,
future::Future,
mem,
ops::Range,
path::{Path, PathBuf},
rc::Rc,
sync::{
@ -2538,6 +2539,18 @@ impl Element for AvatarRibbon {
false
}
fn rect_for_text_range(
&self,
_: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
_: &gpui::MeasurementContext,
) -> Option<RectF> {
None
}
fn debug(
&self,
bounds: gpui::geometry::rect::RectF,