mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 18:41:10 +00:00
WIP, almost done with tooltips
This commit is contained in:
parent
a686a9f1d2
commit
d2d49633f1
4 changed files with 138 additions and 18 deletions
|
@ -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>>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue