Detailed changes
@@ -14,6 +14,7 @@ pub struct Overlay {
anchor_position: Option<Vector2F>,
anchor_corner: AnchorCorner,
fit_mode: OverlayFitMode,
+ position_mode: OverlayPositionMode,
hoverable: bool,
}
@@ -24,6 +25,12 @@ pub enum OverlayFitMode {
None,
}
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum OverlayPositionMode {
+ Window,
+ Local,
+}
+
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum AnchorCorner {
TopLeft,
@@ -73,6 +80,7 @@ impl Overlay {
anchor_position: None,
anchor_corner: AnchorCorner::TopLeft,
fit_mode: OverlayFitMode::None,
+ position_mode: OverlayPositionMode::Window,
hoverable: false,
}
}
@@ -92,6 +100,11 @@ impl Overlay {
self
}
+ pub fn with_position_mode(mut self, position_mode: OverlayPositionMode) -> Self {
+ self.position_mode = position_mode;
+ self
+ }
+
pub fn with_hoverable(mut self, hoverable: bool) -> Self {
self.hoverable = hoverable;
self
@@ -123,8 +136,20 @@ impl Element for Overlay {
size: &mut Self::LayoutState,
cx: &mut PaintContext,
) {
- let anchor_position = self.anchor_position.unwrap_or_else(|| bounds.origin());
- let mut bounds = self.anchor_corner.get_bounds(anchor_position, *size);
+ let (anchor_position, mut bounds) = match self.position_mode {
+ OverlayPositionMode::Window => {
+ let anchor_position = self.anchor_position.unwrap_or_else(|| bounds.origin());
+ let bounds = self.anchor_corner.get_bounds(anchor_position, *size);
+ (anchor_position, bounds)
+ }
+ OverlayPositionMode::Local => {
+ let anchor_position = self.anchor_position.unwrap_or_default();
+ let bounds = self
+ .anchor_corner
+ .get_bounds(bounds.origin() + anchor_position, *size);
+ (anchor_position, bounds)
+ }
+ };
match self.fit_mode {
OverlayFitMode::SnapToWindow => {
@@ -385,6 +385,8 @@ impl TerminalBuilder {
breadcrumb_text: String::new(),
scroll_px: 0.,
last_mouse_position: None,
+ next_link_id: 0,
+ selection_phase: SelectionPhase::Ended,
};
Ok(TerminalBuilder {
@@ -471,7 +473,7 @@ pub struct TerminalContent {
cursor: RenderableCursor,
cursor_char: char,
size: TerminalSize,
- last_hovered_hyperlink: Option<(String, RangeInclusive<Point>)>,
+ last_hovered_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
}
impl Default for TerminalContent {
@@ -493,6 +495,12 @@ impl Default for TerminalContent {
}
}
+#[derive(PartialEq, Eq)]
+pub enum SelectionPhase {
+ Selecting,
+ Ended,
+}
+
pub struct Terminal {
pty_tx: Notifier,
term: Arc<FairMutex<Term<ZedListener>>>,
@@ -511,6 +519,8 @@ pub struct Terminal {
shell_fd: u32,
foreground_process_info: Option<LocalProcessInfo>,
scroll_px: f32,
+ next_link_id: usize,
+ selection_phase: SelectionPhase,
}
impl Terminal {
@@ -654,7 +664,7 @@ impl Terminal {
}
InternalEvent::ScrollToPoint(point) => term.scroll_to_point(*point),
InternalEvent::Hyperlink(position, open) => {
- self.last_content.last_hovered_hyperlink = None;
+ let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
let point = grid_point(
*position,
@@ -668,13 +678,37 @@ impl Terminal {
if *open {
open_uri(&url).log_err();
} else {
- self.last_content.last_hovered_hyperlink = Some((url, url_match));
+ self.update_hyperlink(prev_hyperlink, url, url_match);
}
}
}
}
}
+ fn update_hyperlink(
+ &mut self,
+ prev_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
+ url: String,
+ url_match: RangeInclusive<Point>,
+ ) {
+ if let Some(prev_hyperlink) = prev_hyperlink {
+ if prev_hyperlink.0 == url && prev_hyperlink.1 == url_match {
+ self.last_content.last_hovered_hyperlink = Some((url, url_match, prev_hyperlink.2));
+ } else {
+ self.last_content.last_hovered_hyperlink =
+ Some((url, url_match, self.next_link_id()));
+ }
+ } else {
+ self.last_content.last_hovered_hyperlink = Some((url, url_match, self.next_link_id()));
+ }
+ }
+
+ fn next_link_id(&mut self) -> usize {
+ let res = self.next_link_id;
+ self.next_link_id = self.next_link_id.wrapping_add(1);
+ res
+ }
+
pub fn last_content(&self) -> &TerminalContent {
&self.last_content
}
@@ -846,7 +880,8 @@ impl Terminal {
}
pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
- self.last_content.last_hovered_hyperlink = None;
+ let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
+
let position = e.position.sub(origin);
self.last_mouse_position = Some(position);
if self.mouse_mode(e.shift) {
@@ -862,19 +897,26 @@ impl Terminal {
self.pty_tx.notify(bytes);
}
}
- } else if e.cmd {
- self.fill_hyperlink(Some(position));
+ } else {
+ self.fill_hyperlink(Some(position), prev_hyperlink);
}
}
- fn fill_hyperlink(&mut self, position: Option<Vector2F>) {
- if let Some(position) = position {
+ fn fill_hyperlink(
+ &mut self,
+ position: Option<Vector2F>,
+ prev_hyperlink: Option<(String, RangeInclusive<Point>, usize)>,
+ ) {
+ if self.selection_phase == SelectionPhase::Selecting {
+ self.last_content.last_hovered_hyperlink = None;
+ } else if let Some(position) = position {
let content_index = content_index_for_mouse(position, &self.last_content);
let link = self.last_content.cells[content_index].hyperlink();
if link.is_some() {
let mut min_index = content_index;
loop {
- if self.last_content.cells[min_index - 1].hyperlink() == link {
+ if min_index >= 1 && self.last_content.cells[min_index - 1].hyperlink() == link
+ {
min_index = min_index - 1;
} else {
break;
@@ -882,21 +924,24 @@ impl Terminal {
}
let mut max_index = content_index;
+ let len = self.last_content.cells.len();
loop {
- if self.last_content.cells[max_index + 1].hyperlink() == link {
+ if max_index < len - 1
+ && self.last_content.cells[max_index + 1].hyperlink() == link
+ {
max_index = max_index + 1;
} else {
break;
}
}
- self.last_content.last_hovered_hyperlink = link.map(|link| {
- (
- link.uri().to_owned(),
- self.last_content.cells[min_index].point
- ..=self.last_content.cells[max_index].point,
- )
- });
+ if let Some(link) = link {
+ let url = link.uri().to_owned();
+ let url_match = self.last_content.cells[min_index].point
+ ..=self.last_content.cells[max_index].point;
+
+ self.update_hyperlink(prev_hyperlink, url, url_match);
+ };
} else {
self.events
.push_back(InternalEvent::Hyperlink(position, false));
@@ -909,6 +954,7 @@ impl Terminal {
self.last_mouse_position = Some(position);
if !self.mouse_mode(e.shift) {
+ self.selection_phase = SelectionPhase::Selecting;
// Alacritty has the same ordering, of first updating the selection
// then scrolling 15ms later
self.events
@@ -969,7 +1015,9 @@ impl Terminal {
pub fn left_click(&mut self, e: &ClickRegionEvent, origin: Vector2F) {
let position = e.position.sub(origin);
if !self.mouse_mode(e.shift) {
- if e.cmd {
+ if self.last_content.last_hovered_hyperlink.is_some()
+ && self.last_content.selection.is_none()
+ {
let mouse_cell_index = content_index_for_mouse(position, &self.last_content);
if let Some(link) = self.last_content.cells[mouse_cell_index].hyperlink() {
open_uri(link.uri()).log_err();
@@ -1021,6 +1069,7 @@ impl Terminal {
// so let's do that here
self.copy();
}
+ self.selection_phase = SelectionPhase::Ended;
self.last_mouse = None;
}
@@ -1061,10 +1110,10 @@ impl Terminal {
}
pub fn refresh_hyperlink(&mut self, cmd: bool) -> bool {
- self.last_content.last_hovered_hyperlink = None;
+ let prev_hyperlink = self.last_content.last_hovered_hyperlink.take();
if cmd {
- self.fill_hyperlink(self.last_mouse_position);
+ self.fill_hyperlink(self.last_mouse_position, prev_hyperlink);
true
} else {
false
@@ -7,7 +7,7 @@ use alacritty_terminal::{
use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
use gpui::{
color::Color,
- elements::{Overlay, Tooltip},
+ elements::{Empty, Overlay},
fonts::{HighlightStyle, Properties, Style::Italic, TextStyle, Underline, Weight},
geometry::{
rect::RectF,
@@ -15,7 +15,7 @@ use gpui::{
},
serde_json::json,
text_layout::{Line, RunStyle},
- Axis, Element, ElementBox, Event, EventContext, FontCache, KeyDownEvent, ModelContext,
+ Element, ElementBox, Event, EventContext, FontCache, KeyDownEvent, ModelContext,
ModifiersChangedEvent, MouseButton, MouseRegion, PaintContext, Quad, SizeConstraint,
TextLayoutCache, WeakModelHandle, WeakViewHandle,
};
@@ -324,7 +324,6 @@ impl TerminalElement {
.unwrap_or_default();
if indexed.cell.hyperlink().is_some() {
- underline.squiggly = true;
if underline.thickness == OrderedFloat(0.) {
underline.thickness = OrderedFloat(1.);
}
@@ -594,51 +593,34 @@ impl Element for TerminalElement {
};
let terminal_handle = self.terminal.upgrade(cx).unwrap();
- let (last_hovered_hyperlink, last_mouse) =
- terminal_handle.update(cx.app, |terminal, cx| {
- terminal.set_size(dimensions);
- terminal.try_sync(cx);
- (
- terminal.last_content.last_hovered_hyperlink.clone(),
- terminal.last_mouse_position,
- )
- });
+ let last_hovered_hyperlink = terminal_handle.update(cx.app, |terminal, cx| {
+ terminal.set_size(dimensions);
+ terminal.try_sync(cx);
+ terminal.last_content.last_hovered_hyperlink.clone()
+ });
let view_handle = self.view.clone();
- let hyperlink_tooltip = last_hovered_hyperlink.and_then(|(uri, _)| {
- last_mouse.and_then(|last_mouse| {
- view_handle.upgrade(cx).map(|handle| {
- let mut tooltip = cx.render(&handle, |_, cx| {
- // TODO: Use the correct dynamic line height
- // let mut collapsed_tooltip = Tooltip::render_tooltip(
- // uri.clone(),
- // tooltip_style.clone(),
- // None,
- // false,
- // )
- // .boxed();
-
- Overlay::new(
- Tooltip::render_tooltip(uri, tooltip_style, None, false)
- .constrained()
- .with_height(text_style.line_height(cx.font_cache()))
- // .dynamically(move |constraint, cx| {
- // SizeConstraint::strict_along(
- // Axis::Vertical,
- // collapsed_tooltip.layout(constraint, cx).y(),
- // )
- // })
- .boxed(),
- )
- .with_fit_mode(gpui::elements::OverlayFitMode::SwitchAnchor)
- .with_anchor_position(last_mouse)
- .boxed()
- });
+ let hyperlink_tooltip = last_hovered_hyperlink.and_then(|(uri, _, id)| {
+ // last_mouse.and_then(|_last_mouse| {
+ view_handle.upgrade(cx).map(|handle| {
+ let mut tooltip = cx.render(&handle, |_, cx| {
+ Overlay::new(
+ Empty::new()
+ .contained()
+ .constrained()
+ .with_width(dimensions.width())
+ .with_height(dimensions.height())
+ .with_tooltip::<TerminalElement, _>(id, uri, None, tooltip_style, cx)
+ .boxed(),
+ )
+ .with_position_mode(gpui::elements::OverlayPositionMode::Local)
+ .boxed()
+ });
- tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
- tooltip
- })
+ tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
+ tooltip
})
+ // })
});
let TerminalContent {
@@ -672,7 +654,7 @@ impl Element for TerminalElement {
self.modal,
last_hovered_hyperlink
.as_ref()
- .map(|(_, range)| (link_style, range)),
+ .map(|(_, range, _)| (link_style, range)),
);
//Layout cursor. Rectangle is used for IME, so we should lay it out even
@@ -822,12 +804,7 @@ impl Element for TerminalElement {
}
if let Some(element) = &mut layout.hyperlink_tooltip {
- element.paint(
- visible_bounds.lower_left()
- - vec2f(-layout.size.cell_width, layout.size.line_height),
- visible_bounds,
- cx,
- )
+ element.paint(origin, visible_bounds, cx)
}
});
}