WIP, almost done with tooltips

This commit is contained in:
Mikayla Maki 2022-09-24 08:32:06 -07:00
parent a686a9f1d2
commit d2d49633f1
4 changed files with 138 additions and 18 deletions

View file

@ -36,10 +36,10 @@ struct TooltipState {
#[derive(Clone, Deserialize, Default)]
pub struct TooltipStyle {
#[serde(flatten)]
container: ContainerStyle,
text: TextStyle,
pub container: ContainerStyle,
pub text: TextStyle,
keystroke: KeystrokeStyle,
max_text_width: f32,
pub max_text_width: f32,
}
#[derive(Clone, Deserialize, Default)]
@ -126,7 +126,7 @@ impl Tooltip {
}
}
fn render_tooltip(
pub fn render_tooltip(
text: String,
style: TooltipStyle,
action: Option<Box<dyn Action>>,

View file

@ -384,6 +384,7 @@ impl TerminalBuilder {
foreground_process_info: None,
breadcrumb_text: String::new(),
scroll_px: 0.,
last_mouse_position: None,
};
Ok(TerminalBuilder {
@ -496,7 +497,10 @@ pub struct Terminal {
pty_tx: Notifier,
term: Arc<FairMutex<Term<ZedListener>>>,
events: VecDeque<InternalEvent>,
/// This is only used for mouse mode cell change detection
last_mouse: Option<(Point, AlacDirection)>,
/// This is only used for terminal hyperlink checking
last_mouse_position: Option<Vector2F>,
pub matches: Vec<RangeInclusive<Point>>,
last_content: TerminalContent,
last_synced: Instant,
@ -813,7 +817,8 @@ impl Terminal {
}
}
pub fn focus_out(&self) {
pub fn focus_out(&mut self) {
self.last_mouse_position = None;
if self.last_content.mode.contains(TermMode::FOCUS_IN_OUT) {
self.write_to_pty("\x1b[O".to_string());
}
@ -843,6 +848,7 @@ impl Terminal {
pub fn mouse_move(&mut self, e: &MouseMovedEvent, origin: Vector2F) {
self.last_content.last_hovered_hyperlink = None;
let position = e.position.sub(origin);
self.last_mouse_position = Some(position);
if self.mouse_mode(e.shift) {
let point = grid_point(
position,
@ -857,9 +863,14 @@ impl Terminal {
}
}
} else if e.cmd {
self.fill_hyperlink(Some(position));
}
}
fn fill_hyperlink(&mut self, position: Option<Vector2F>) {
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 {
@ -895,6 +906,7 @@ impl Terminal {
pub fn mouse_drag(&mut self, e: DragRegionEvent, origin: Vector2F) {
let position = e.position.sub(origin);
self.last_mouse_position = Some(position);
if !self.mouse_mode(e.shift) {
// Alacritty has the same ordering, of first updating the selection
@ -1048,6 +1060,17 @@ impl Terminal {
}
}
pub fn refresh_hyperlink(&mut self, cmd: bool) -> bool {
self.last_content.last_hovered_hyperlink = None;
if cmd {
self.fill_hyperlink(self.last_mouse_position);
true
} else {
false
}
}
fn determine_scroll_lines(
&mut self,
e: &ScrollWheelRegionEvent,

View file

@ -7,15 +7,17 @@ use alacritty_terminal::{
use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
use gpui::{
color::Color,
fonts::{Properties, Style::Italic, TextStyle, Underline, Weight},
elements::{Overlay, Tooltip},
fonts::{HighlightStyle, Properties, Style::Italic, TextStyle, Underline, Weight},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
serde_json::json,
text_layout::{Line, RunStyle},
Element, Event, EventContext, FontCache, KeyDownEvent, ModelContext, MouseButton, MouseRegion,
PaintContext, Quad, TextLayoutCache, WeakModelHandle, WeakViewHandle,
Axis, Element, ElementBox, Event, EventContext, FontCache, KeyDownEvent, ModelContext,
ModifiersChangedEvent, MouseButton, MouseRegion, PaintContext, Quad, SizeConstraint,
TextLayoutCache, WeakModelHandle, WeakViewHandle,
};
use itertools::Itertools;
use ordered_float::OrderedFloat;
@ -42,6 +44,7 @@ pub struct LayoutState {
size: TerminalSize,
mode: TermMode,
display_offset: usize,
hyperlink_tooltip: Option<ElementBox>,
}
///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
@ -180,6 +183,7 @@ impl TerminalElement {
text_layout_cache: &TextLayoutCache,
font_cache: &FontCache,
modal: bool,
hyperlink: Option<(HighlightStyle, &RangeInclusive<Point>)>,
) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
let mut cells = vec![];
let mut rects = vec![];
@ -245,6 +249,7 @@ impl TerminalElement {
text_style,
font_cache,
modal,
hyperlink,
);
let layout_cell = text_layout_cache.layout_str(
@ -304,6 +309,7 @@ impl TerminalElement {
text_style: &TextStyle,
font_cache: &FontCache,
modal: bool,
hyperlink: Option<(HighlightStyle, &RangeInclusive<Point>)>,
) -> RunStyle {
let flags = indexed.cell.flags;
let fg = convert_color(&fg, &style.colors, modal);
@ -339,11 +345,25 @@ impl TerminalElement {
.select_font(text_style.font_family_id, &properties)
.unwrap_or(text_style.font_id);
RunStyle {
let mut result = RunStyle {
color: fg,
font_id,
underline,
};
if let Some((style, range)) = hyperlink {
if range.contains(&indexed.point) {
if let Some(underline) = style.underline {
result.underline = underline;
}
if let Some(color) = style.color {
result.color = color;
}
}
}
result
}
fn generic_button_handler<E>(
@ -373,7 +393,7 @@ impl TerminalElement {
) {
let connection = self.terminal;
let mut region = MouseRegion::new::<Self>(view_id, view_id, visible_bounds);
let mut region = MouseRegion::new::<Self>(view_id, 0, visible_bounds);
// Terminal Emulator controlled behavior:
region = region
@ -549,6 +569,9 @@ impl Element for TerminalElement {
//Setup layout information
let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
let link_style = settings.theme.editor.link_definition;
let tooltip_style = settings.theme.tooltip.clone();
let text_style = TerminalElement::make_text_style(font_cache, settings);
let selection_color = settings.theme.editor.selection.selection;
let match_color = settings.theme.search.match_background;
@ -571,9 +594,51 @@ impl Element for TerminalElement {
};
let terminal_handle = self.terminal.upgrade(cx).unwrap();
terminal_handle.update(cx.app, |terminal, cx| {
terminal.set_size(dimensions);
terminal.try_sync(cx)
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 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()
});
tooltip.layout(SizeConstraint::new(Vector2F::zero(), cx.window_size), cx);
tooltip
})
})
});
let TerminalContent {
@ -585,7 +650,7 @@ impl Element for TerminalElement {
cursor,
last_hovered_hyperlink,
..
} = &terminal_handle.read(cx).last_content;
} = { &terminal_handle.read(cx).last_content };
// searches, highlights to a single range representations
let mut relative_highlighted_ranges = Vec::new();
@ -605,6 +670,9 @@ impl Element for TerminalElement {
cx.text_layout_cache,
cx.font_cache(),
self.modal,
last_hovered_hyperlink
.as_ref()
.map(|(_, range)| (link_style, range)),
);
//Layout cursor. Rectangle is used for IME, so we should lay it out even
@ -636,10 +704,11 @@ impl Element for TerminalElement {
)
};
let focused = self.focused;
TerminalElement::shape_cursor(cursor_point, dimensions, &cursor_text).map(
move |(cursor_position, block_width)| {
let shape = match cursor.shape {
AlacCursorShape::Block if !self.focused => CursorShape::Hollow,
AlacCursorShape::Block if !focused => CursorShape::Hollow,
AlacCursorShape::Block => CursorShape::Block,
AlacCursorShape::Underline => CursorShape::Underscore,
AlacCursorShape::Beam => CursorShape::Bar,
@ -672,6 +741,7 @@ impl Element for TerminalElement {
relative_highlighted_ranges,
mode: *mode,
display_offset: *display_offset,
hyperlink_tooltip,
},
)
}
@ -694,7 +764,11 @@ impl Element for TerminalElement {
cx.scene.push_cursor_region(gpui::CursorRegion {
bounds,
style: gpui::CursorStyle::IBeam,
style: if layout.hyperlink_tooltip.is_some() {
gpui::CursorStyle::PointingHand
} else {
gpui::CursorStyle::IBeam
},
});
cx.paint_layer(clip_bounds, |cx| {
@ -746,6 +820,15 @@ 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,
)
}
});
}
@ -784,6 +867,18 @@ impl Element for TerminalElement {
})
})
.unwrap_or(false)
} else if let Event::ModifiersChanged(ModifiersChangedEvent { cmd, .. }) = event {
self.terminal
.upgrade(cx.app)
.map(|model_handle| {
if model_handle.update(cx.app, |term, _| term.refresh_hyperlink(*cmd)) {
cx.notify();
true
} else {
false
}
})
.unwrap_or(false)
} else {
false
}

View file

@ -362,7 +362,9 @@ impl View for TerminalView {
}
fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.terminal.read(cx).focus_out();
self.terminal.update(cx, |terminal, _| {
terminal.focus_out();
});
cx.notify();
}