From b4f59284a94d3af414f62824331e2641e9b055e8 Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner Date: Fri, 6 Dec 2024 18:31:58 +0100 Subject: [PATCH] markdown preview: Allow clicking on image to navigate to source location (#21630) Follow up to #21082 Similar to checkboxes, you can now click on the image to navigate to the source location, cmd-clicking opens the url in the browser. https://github.com/user-attachments/assets/edaaa580-9d8f-490b-a4b3-d6ffb21f197c Release Notes: - N/A --- Cargo.lock | 1 + crates/markdown_preview/Cargo.toml | 1 + .../markdown_preview/src/markdown_renderer.rs | 98 ++++++++++++++----- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b93ebce571..477a7b83e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7421,6 +7421,7 @@ dependencies = [ "settings", "theme", "ui", + "util", "workspace", ] diff --git a/crates/markdown_preview/Cargo.toml b/crates/markdown_preview/Cargo.toml index 46a33966f2..f1409c23a4 100644 --- a/crates/markdown_preview/Cargo.toml +++ b/crates/markdown_preview/Cargo.toml @@ -28,6 +28,7 @@ pulldown-cmark.workspace = true settings.workspace = true theme.workspace = true ui.workspace = true +util.workspace = true workspace.workspace = true [dev-dependencies] diff --git a/crates/markdown_preview/src/markdown_renderer.rs b/crates/markdown_preview/src/markdown_renderer.rs index 7a13077194..5183f361b6 100644 --- a/crates/markdown_preview/src/markdown_renderer.rs +++ b/crates/markdown_preview/src/markdown_renderer.rs @@ -7,8 +7,8 @@ use crate::markdown_elements::{ use gpui::{ div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element, ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length, - Modifiers, ParentElement, Resource, SharedString, Styled, StyledText, TextStyle, WeakView, - WindowContext, + Modifiers, ParentElement, Render, Resource, SharedString, Styled, StyledText, TextStyle, View, + WeakView, WindowContext, }; use settings::Settings; use std::{ @@ -18,9 +18,10 @@ use std::{ }; use theme::{ActiveTheme, SyntaxTheme, ThemeSettings}; use ui::{ - h_flex, relative, v_flex, Checkbox, Clickable, FluentBuilder, IconButton, IconName, IconSize, - InteractiveElement, LinkPreview, Selection, StatefulInteractiveElement, StyledExt, StyledImage, - Tooltip, VisibleOnHover, + h_flex, relative, tooltip_container, v_flex, Checkbox, Clickable, Color, FluentBuilder, + IconButton, IconName, IconSize, InteractiveElement, Label, LabelCommon, LabelSize, LinkPreview, + Selection, StatefulInteractiveElement, StyledExt, StyledImage, ViewContext, VisibleOnHover, + VisualContext as _, }; use workspace::Workspace; @@ -206,15 +207,7 @@ fn render_markdown_list_item( ) .hover(|s| s.cursor_pointer()) .tooltip(|cx| { - let secondary_modifier = Keystroke { - key: "".to_string(), - modifiers: Modifiers::secondary_key(), - key_char: None, - }; - Tooltip::text( - format!("{}-click to toggle the checkbox", secondary_modifier), - cx, - ) + InteractiveMarkdownElementTooltip::new(None, "toggle checkbox", cx).into() }) .into_any_element(), }; @@ -513,6 +506,7 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext) let image_element = div() .id(element_id) + .cursor_pointer() .child(img(ImageSource::Resource(image_resource)).with_fallback({ let alt_text = image.alt_text.clone(); { @@ -521,18 +515,31 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext) })) .tooltip({ let link = image.link.clone(); - move |cx| LinkPreview::new(&link.to_string(), cx) + move |cx| { + InteractiveMarkdownElementTooltip::new( + Some(link.to_string()), + "open image", + cx, + ) + .into() + } }) .on_click({ let workspace = workspace_clone.clone(); let link = image.link.clone(); - move |_event, window_cx| match &link { - Link::Web { url } => window_cx.open_url(url), - Link::Path { path, .. } => { - if let Some(workspace) = &workspace { - _ = workspace.update(window_cx, |workspace, cx| { - workspace.open_abs_path(path.clone(), false, cx).detach(); - }); + move |_, cx| { + if cx.modifiers().secondary() { + match &link { + Link::Web { url } => cx.open_url(url), + Link::Path { path, .. } => { + if let Some(workspace) = &workspace { + _ = workspace.update(cx, |workspace, cx| { + workspace + .open_abs_path(path.clone(), false, cx) + .detach(); + }); + } + } } } } @@ -550,3 +557,50 @@ fn render_markdown_rule(cx: &mut RenderContext) -> AnyElement { let rule = div().w_full().h(px(2.)).bg(cx.border_color); div().pt_3().pb_3().child(rule).into_any() } + +struct InteractiveMarkdownElementTooltip { + tooltip_text: Option, + action_text: String, +} + +impl InteractiveMarkdownElementTooltip { + pub fn new( + tooltip_text: Option, + action_text: &str, + cx: &mut WindowContext, + ) -> View { + let tooltip_text = tooltip_text.map(|t| util::truncate_and_trailoff(&t, 50).into()); + + cx.new_view(|_| Self { + tooltip_text, + action_text: action_text.to_string(), + }) + } +} + +impl Render for InteractiveMarkdownElementTooltip { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + tooltip_container(cx, |el, _| { + let secondary_modifier = Keystroke { + modifiers: Modifiers::secondary_key(), + ..Default::default() + }; + + el.child( + v_flex() + .gap_1() + .when_some(self.tooltip_text.clone(), |this, text| { + this.child(Label::new(text).size(LabelSize::Small)) + }) + .child( + Label::new(format!( + "{}-click to {}", + secondary_modifier, self.action_text + )) + .size(LabelSize::Small) + .color(Color::Muted), + ), + ) + }) + } +}