mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
Position IME input according to where the selection is rendered
This commit is contained in:
parent
3c5d7e001e
commit
97ce3998ec
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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"))]
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user