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
This commit is contained in:
parent
bffdc55d63
commit
b4f59284a9
3 changed files with 78 additions and 22 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -7421,6 +7421,7 @@ dependencies = [
|
||||||
"settings",
|
"settings",
|
||||||
"theme",
|
"theme",
|
||||||
"ui",
|
"ui",
|
||||||
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ pulldown-cmark.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -7,8 +7,8 @@ use crate::markdown_elements::{
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element,
|
div, img, px, rems, AbsoluteLength, AnyElement, ClipboardItem, DefiniteLength, Div, Element,
|
||||||
ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length,
|
ElementId, HighlightStyle, Hsla, ImageSource, InteractiveText, IntoElement, Keystroke, Length,
|
||||||
Modifiers, ParentElement, Resource, SharedString, Styled, StyledText, TextStyle, WeakView,
|
Modifiers, ParentElement, Render, Resource, SharedString, Styled, StyledText, TextStyle, View,
|
||||||
WindowContext,
|
WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -18,9 +18,10 @@ use std::{
|
||||||
};
|
};
|
||||||
use theme::{ActiveTheme, SyntaxTheme, ThemeSettings};
|
use theme::{ActiveTheme, SyntaxTheme, ThemeSettings};
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, relative, v_flex, Checkbox, Clickable, FluentBuilder, IconButton, IconName, IconSize,
|
h_flex, relative, tooltip_container, v_flex, Checkbox, Clickable, Color, FluentBuilder,
|
||||||
InteractiveElement, LinkPreview, Selection, StatefulInteractiveElement, StyledExt, StyledImage,
|
IconButton, IconName, IconSize, InteractiveElement, Label, LabelCommon, LabelSize, LinkPreview,
|
||||||
Tooltip, VisibleOnHover,
|
Selection, StatefulInteractiveElement, StyledExt, StyledImage, ViewContext, VisibleOnHover,
|
||||||
|
VisualContext as _,
|
||||||
};
|
};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
@ -206,15 +207,7 @@ fn render_markdown_list_item(
|
||||||
)
|
)
|
||||||
.hover(|s| s.cursor_pointer())
|
.hover(|s| s.cursor_pointer())
|
||||||
.tooltip(|cx| {
|
.tooltip(|cx| {
|
||||||
let secondary_modifier = Keystroke {
|
InteractiveMarkdownElementTooltip::new(None, "toggle checkbox", cx).into()
|
||||||
key: "".to_string(),
|
|
||||||
modifiers: Modifiers::secondary_key(),
|
|
||||||
key_char: None,
|
|
||||||
};
|
|
||||||
Tooltip::text(
|
|
||||||
format!("{}-click to toggle the checkbox", secondary_modifier),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
};
|
};
|
||||||
|
@ -513,6 +506,7 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
|
||||||
|
|
||||||
let image_element = div()
|
let image_element = div()
|
||||||
.id(element_id)
|
.id(element_id)
|
||||||
|
.cursor_pointer()
|
||||||
.child(img(ImageSource::Resource(image_resource)).with_fallback({
|
.child(img(ImageSource::Resource(image_resource)).with_fallback({
|
||||||
let alt_text = image.alt_text.clone();
|
let alt_text = image.alt_text.clone();
|
||||||
{
|
{
|
||||||
|
@ -521,18 +515,31 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
|
||||||
}))
|
}))
|
||||||
.tooltip({
|
.tooltip({
|
||||||
let link = image.link.clone();
|
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({
|
.on_click({
|
||||||
let workspace = workspace_clone.clone();
|
let workspace = workspace_clone.clone();
|
||||||
let link = image.link.clone();
|
let link = image.link.clone();
|
||||||
move |_event, window_cx| match &link {
|
move |_, cx| {
|
||||||
Link::Web { url } => window_cx.open_url(url),
|
if cx.modifiers().secondary() {
|
||||||
Link::Path { path, .. } => {
|
match &link {
|
||||||
if let Some(workspace) = &workspace {
|
Link::Web { url } => cx.open_url(url),
|
||||||
_ = workspace.update(window_cx, |workspace, cx| {
|
Link::Path { path, .. } => {
|
||||||
workspace.open_abs_path(path.clone(), false, cx).detach();
|
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);
|
let rule = div().w_full().h(px(2.)).bg(cx.border_color);
|
||||||
div().pt_3().pb_3().child(rule).into_any()
|
div().pt_3().pb_3().child(rule).into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct InteractiveMarkdownElementTooltip {
|
||||||
|
tooltip_text: Option<SharedString>,
|
||||||
|
action_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractiveMarkdownElementTooltip {
|
||||||
|
pub fn new(
|
||||||
|
tooltip_text: Option<String>,
|
||||||
|
action_text: &str,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> View<Self> {
|
||||||
|
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<Self>) -> 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),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue