mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
More jump targets
This commit is contained in:
parent
ecb5543985
commit
e8435df5ec
@ -141,7 +141,7 @@ impl Layout for BoxNode {
|
|||||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||||
let outset = self.outset(styles);
|
let outset = self.outset(styles);
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles);
|
||||||
frame.fill_and_stroke(fill, stroke, outset, radius);
|
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply metadata.
|
// Apply metadata.
|
||||||
@ -383,7 +383,7 @@ impl Layout for BlockNode {
|
|||||||
let outset = self.outset(styles);
|
let outset = self.outset(styles);
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles);
|
||||||
for frame in frames.iter_mut().skip(skip as usize) {
|
for frame in frames.iter_mut().skip(skip as usize) {
|
||||||
frame.fill_and_stroke(fill, stroke, outset, radius);
|
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,14 +166,20 @@ impl Layout for TableNode {
|
|||||||
for offset in points(rows.iter().map(|piece| piece.height)) {
|
for offset in points(rows.iter().map(|piece| piece.height)) {
|
||||||
let target = Point::with_x(frame.width() + thickness);
|
let target = Point::with_x(frame.width() + thickness);
|
||||||
let hline = Geometry::Line(target).stroked(stroke);
|
let hline = Geometry::Line(target).stroked(stroke);
|
||||||
frame.prepend(Point::new(-half, offset), Element::Shape(hline));
|
frame.prepend(
|
||||||
|
Point::new(-half, offset),
|
||||||
|
Element::Shape(hline, self.span()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render vertical lines.
|
// Render vertical lines.
|
||||||
for offset in points(layout.cols.iter().copied()) {
|
for offset in points(layout.cols.iter().copied()) {
|
||||||
let target = Point::with_y(frame.height() + thickness);
|
let target = Point::with_y(frame.height() + thickness);
|
||||||
let vline = Geometry::Line(target).stroked(stroke);
|
let vline = Geometry::Line(target).stroked(stroke);
|
||||||
frame.prepend(Point::new(offset, -half), Element::Shape(vline));
|
frame.prepend(
|
||||||
|
Point::new(offset, -half),
|
||||||
|
Element::Shape(vline, self.span()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +192,7 @@ impl Layout for TableNode {
|
|||||||
let pos = Point::new(dx, dy);
|
let pos = Point::new(dx, dy);
|
||||||
let size = Size::new(col, row.height);
|
let size = Size::new(col, row.height);
|
||||||
let rect = Geometry::Rect(size).filled(fill);
|
let rect = Geometry::Rect(size).filled(fill);
|
||||||
frame.prepend(pos, Element::Shape(rect));
|
frame.prepend(pos, Element::Shape(rect, self.span()));
|
||||||
}
|
}
|
||||||
dy += row.height;
|
dy += row.height;
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ fn layout(
|
|||||||
paint: TextNode::fill_in(ctx.styles()),
|
paint: TextNode::fill_in(ctx.styles()),
|
||||||
thickness,
|
thickness,
|
||||||
}),
|
}),
|
||||||
|
span,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
ctx.push(FrameFragment::new(ctx, frame));
|
ctx.push(FrameFragment::new(ctx, frame));
|
||||||
|
@ -127,6 +127,7 @@ fn layout(
|
|||||||
Element::Shape(
|
Element::Shape(
|
||||||
Geometry::Line(Point::with_x(radicand.width()))
|
Geometry::Line(Point::with_x(radicand.width()))
|
||||||
.stroked(Stroke { paint: TextNode::fill_in(ctx.styles()), thickness }),
|
.stroked(Stroke { paint: TextNode::fill_in(ctx.styles()), thickness }),
|
||||||
|
span,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -285,6 +285,7 @@ fn create(
|
|||||||
bibliography: &BibliographyNode,
|
bibliography: &BibliographyNode,
|
||||||
citations: Vec<&CiteNode>,
|
citations: Vec<&CiteNode>,
|
||||||
) -> Arc<Works> {
|
) -> Arc<Works> {
|
||||||
|
let span = bibliography.span();
|
||||||
let entries = load(world, &bibliography.path()).unwrap();
|
let entries = load(world, &bibliography.path()).unwrap();
|
||||||
let style = bibliography.style(StyleChain::default());
|
let style = bibliography.style(StyleChain::default());
|
||||||
let bib_id = bibliography.0.stable_id().unwrap();
|
let bib_id = bibliography.0.stable_id().unwrap();
|
||||||
@ -369,7 +370,7 @@ fn create(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format and link to the reference entry.
|
// Format and link to the reference entry.
|
||||||
content += format_display_string(&display, supplement)
|
content += format_display_string(&display, supplement, citation.span())
|
||||||
.linked(Link::Node(ref_id(entry)));
|
.linked(Link::Node(ref_id(entry)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,12 +411,12 @@ fn create(
|
|||||||
let prefix = reference.prefix.map(|prefix| {
|
let prefix = reference.prefix.map(|prefix| {
|
||||||
// Format and link to first citation.
|
// Format and link to first citation.
|
||||||
let bracketed = prefix.with_default_brackets(&*citation_style);
|
let bracketed = prefix.with_default_brackets(&*citation_style);
|
||||||
format_display_string(&bracketed, None)
|
format_display_string(&bracketed, None, span)
|
||||||
.linked(Link::Node(ids[reference.entry.key()]))
|
.linked(Link::Node(ids[reference.entry.key()]))
|
||||||
.styled(backlink.clone())
|
.styled(backlink.clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut reference = format_display_string(&reference.display, None);
|
let mut reference = format_display_string(&reference.display, None, span);
|
||||||
if prefix.is_none() {
|
if prefix.is_none() {
|
||||||
reference = reference.styled(backlink);
|
reference = reference.styled(backlink);
|
||||||
}
|
}
|
||||||
@ -471,6 +472,7 @@ const SUPPLEMENT: &str = "cdc579c45cf3d648905c142c7082683f";
|
|||||||
fn format_display_string(
|
fn format_display_string(
|
||||||
string: &DisplayString,
|
string: &DisplayString,
|
||||||
mut supplement: Option<Content>,
|
mut supplement: Option<Content>,
|
||||||
|
span: Span,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
let mut stops: Vec<_> = string
|
let mut stops: Vec<_> = string
|
||||||
.formatting
|
.formatting
|
||||||
@ -498,7 +500,7 @@ fn format_display_string(
|
|||||||
let mut content = if segment == SUPPLEMENT && supplement.is_some() {
|
let mut content = if segment == SUPPLEMENT && supplement.is_some() {
|
||||||
supplement.take().unwrap_or_default()
|
supplement.take().unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
TextNode::packed(segment)
|
TextNode::packed(segment).spanned(span)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (range, fmt) in &string.formatting {
|
for (range, fmt) in &string.formatting {
|
||||||
|
@ -285,7 +285,7 @@ pub(super) fn decorate(
|
|||||||
|
|
||||||
if target.x >= min_width || !deco.evade {
|
if target.x >= min_width || !deco.evade {
|
||||||
let shape = Geometry::Line(target).stroked(stroke);
|
let shape = Geometry::Line(target).stroked(stroke);
|
||||||
frame.push(origin, Element::Shape(shape));
|
frame.push(origin, Element::Shape(shape, Span::detached()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ impl Layout for ImageNode {
|
|||||||
// the frame to the target size, center aligning the image in the
|
// the frame to the target size, center aligning the image in the
|
||||||
// process.
|
// process.
|
||||||
let mut frame = Frame::new(fitted);
|
let mut frame = Frame::new(fitted);
|
||||||
frame.push(Point::zero(), Element::Image(image, fitted));
|
frame.push(Point::zero(), Element::Image(image, fitted, self.span()));
|
||||||
frame.resize(target, Align::CENTER_HORIZON);
|
frame.resize(target, Align::CENTER_HORIZON);
|
||||||
|
|
||||||
// Create a clipping group if only part of the image should be visible.
|
// Create a clipping group if only part of the image should be visible.
|
||||||
|
@ -76,7 +76,7 @@ impl Layout for LineNode {
|
|||||||
|
|
||||||
let mut frame = Frame::new(target);
|
let mut frame = Frame::new(target);
|
||||||
let shape = Geometry::Line(delta.to_point()).stroked(stroke);
|
let shape = Geometry::Line(delta.to_point()).stroked(stroke);
|
||||||
frame.push(start.to_point(), Element::Shape(shape));
|
frame.push(start.to_point(), Element::Shape(shape, self.span()));
|
||||||
Ok(Fragment::frame(frame))
|
Ok(Fragment::frame(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,7 @@ impl Layout for RectNode {
|
|||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
self.radius(styles),
|
self.radius(styles),
|
||||||
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,6 +268,7 @@ impl Layout for SquareNode {
|
|||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
self.radius(styles),
|
self.radius(styles),
|
||||||
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,6 +350,7 @@ impl Layout for EllipseNode {
|
|||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
Corners::splat(Rel::zero()),
|
Corners::splat(Rel::zero()),
|
||||||
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,6 +457,7 @@ impl Layout for CircleNode {
|
|||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
Corners::splat(Rel::zero()),
|
Corners::splat(Rel::zero()),
|
||||||
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,6 +475,7 @@ fn layout(
|
|||||||
mut inset: Sides<Rel<Abs>>,
|
mut inset: Sides<Rel<Abs>>,
|
||||||
outset: Sides<Rel<Abs>>,
|
outset: Sides<Rel<Abs>>,
|
||||||
radius: Corners<Rel<Abs>>,
|
radius: Corners<Rel<Abs>>,
|
||||||
|
span: Span,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let resolved = sizing
|
let resolved = sizing
|
||||||
.zip(regions.base())
|
.zip(regions.base())
|
||||||
@ -524,9 +529,9 @@ fn layout(
|
|||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let pos = Point::new(-outset.left, -outset.top);
|
let pos = Point::new(-outset.left, -outset.top);
|
||||||
let shape = ellipse(size, fill, stroke.left);
|
let shape = ellipse(size, fill, stroke.left);
|
||||||
frame.prepend(pos, Element::Shape(shape));
|
frame.prepend(pos, Element::Shape(shape, span));
|
||||||
} else {
|
} else {
|
||||||
frame.fill_and_stroke(fill, stroke, outset, radius);
|
frame.fill_and_stroke(fill, stroke, outset, radius, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/doc.rs
35
src/doc.rs
@ -288,7 +288,7 @@ impl Frame {
|
|||||||
pub fn fill(&mut self, fill: Paint) {
|
pub fn fill(&mut self, fill: Paint) {
|
||||||
self.prepend(
|
self.prepend(
|
||||||
Point::zero(),
|
Point::zero(),
|
||||||
Element::Shape(Geometry::Rect(self.size()).filled(fill)),
|
Element::Shape(Geometry::Rect(self.size()).filled(fill), Span::detached()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +299,7 @@ impl Frame {
|
|||||||
stroke: Sides<Option<Stroke>>,
|
stroke: Sides<Option<Stroke>>,
|
||||||
outset: Sides<Rel<Abs>>,
|
outset: Sides<Rel<Abs>>,
|
||||||
radius: Corners<Rel<Abs>>,
|
radius: Corners<Rel<Abs>>,
|
||||||
|
span: Span,
|
||||||
) {
|
) {
|
||||||
let outset = outset.relative_to(self.size());
|
let outset = outset.relative_to(self.size());
|
||||||
let size = self.size() + outset.sum_by_axis();
|
let size = self.size() + outset.sum_by_axis();
|
||||||
@ -307,7 +308,7 @@ impl Frame {
|
|||||||
self.prepend_multiple(
|
self.prepend_multiple(
|
||||||
rounded_rect(size, radius, fill, stroke)
|
rounded_rect(size, radius, fill, stroke)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| (pos, Element::Shape(x))),
|
.map(|x| (pos, Element::Shape(x, span))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +350,7 @@ impl Frame {
|
|||||||
Element::Shape(
|
Element::Shape(
|
||||||
Geometry::Rect(self.size)
|
Geometry::Rect(self.size)
|
||||||
.filled(RgbaColor { a: 100, ..Color::TEAL.to_rgba() }.into()),
|
.filled(RgbaColor { a: 100, ..Color::TEAL.to_rgba() }.into()),
|
||||||
|
Span::detached(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self.insert(
|
self.insert(
|
||||||
@ -359,6 +361,7 @@ impl Frame {
|
|||||||
paint: Color::RED.into(),
|
paint: Color::RED.into(),
|
||||||
thickness: Abs::pt(1.0),
|
thickness: Abs::pt(1.0),
|
||||||
}),
|
}),
|
||||||
|
Span::detached(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
@ -369,11 +372,10 @@ impl Frame {
|
|||||||
let radius = Abs::pt(2.0);
|
let radius = Abs::pt(2.0);
|
||||||
self.push(
|
self.push(
|
||||||
pos - Point::splat(radius),
|
pos - Point::splat(radius),
|
||||||
Element::Shape(geom::ellipse(
|
Element::Shape(
|
||||||
Size::splat(2.0 * radius),
|
geom::ellipse(Size::splat(2.0 * radius), Some(Color::GREEN.into()), None),
|
||||||
Some(Color::GREEN.into()),
|
Span::detached(),
|
||||||
None,
|
),
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,10 +383,13 @@ impl Frame {
|
|||||||
pub fn mark_line(&mut self, y: Abs) {
|
pub fn mark_line(&mut self, y: Abs) {
|
||||||
self.push(
|
self.push(
|
||||||
Point::with_y(y),
|
Point::with_y(y),
|
||||||
Element::Shape(Geometry::Line(Point::with_x(self.size.x)).stroked(Stroke {
|
Element::Shape(
|
||||||
paint: Color::GREEN.into(),
|
Geometry::Line(Point::with_x(self.size.x)).stroked(Stroke {
|
||||||
thickness: Abs::pt(1.0),
|
paint: Color::GREEN.into(),
|
||||||
})),
|
thickness: Abs::pt(1.0),
|
||||||
|
}),
|
||||||
|
Span::detached(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,9 +411,9 @@ pub enum Element {
|
|||||||
/// A run of shaped text.
|
/// A run of shaped text.
|
||||||
Text(Text),
|
Text(Text),
|
||||||
/// A geometric shape with optional fill and stroke.
|
/// A geometric shape with optional fill and stroke.
|
||||||
Shape(Shape),
|
Shape(Shape, Span),
|
||||||
/// An image and its size.
|
/// An image and its size.
|
||||||
Image(Image, Size),
|
Image(Image, Size, Span),
|
||||||
/// Meta information and the region it applies to.
|
/// Meta information and the region it applies to.
|
||||||
Meta(Meta, Size),
|
Meta(Meta, Size),
|
||||||
}
|
}
|
||||||
@ -418,8 +423,8 @@ impl Debug for Element {
|
|||||||
match self {
|
match self {
|
||||||
Self::Group(group) => group.fmt(f),
|
Self::Group(group) => group.fmt(f),
|
||||||
Self::Text(text) => write!(f, "{text:?}"),
|
Self::Text(text) => write!(f, "{text:?}"),
|
||||||
Self::Shape(shape) => write!(f, "{shape:?}"),
|
Self::Shape(shape, _) => write!(f, "{shape:?}"),
|
||||||
Self::Image(image, _) => write!(f, "{image:?}"),
|
Self::Image(image, _, _) => write!(f, "{image:?}"),
|
||||||
Self::Meta(meta, _) => write!(f, "{meta:?}"),
|
Self::Meta(meta, _) => write!(f, "{meta:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,8 +293,8 @@ fn write_frame(ctx: &mut PageContext, frame: &Frame) {
|
|||||||
match element {
|
match element {
|
||||||
Element::Group(group) => write_group(ctx, pos, group),
|
Element::Group(group) => write_group(ctx, pos, group),
|
||||||
Element::Text(text) => write_text(ctx, x, y, text),
|
Element::Text(text) => write_text(ctx, x, y, text),
|
||||||
Element::Shape(shape) => write_shape(ctx, x, y, shape),
|
Element::Shape(shape, _) => write_shape(ctx, x, y, shape),
|
||||||
Element::Image(image, size) => write_image(ctx, x, y, image, *size),
|
Element::Image(image, size, _) => write_image(ctx, x, y, image, *size),
|
||||||
Element::Meta(meta, size) => match meta {
|
Element::Meta(meta, size) => match meta {
|
||||||
Meta::Link(link) => write_link(ctx, pos, link, *size),
|
Meta::Link(link) => write_link(ctx, pos, link, *size),
|
||||||
Meta::Node(_) => {}
|
Meta::Node(_) => {}
|
||||||
|
@ -52,10 +52,10 @@ fn render_frame(
|
|||||||
Element::Text(text) => {
|
Element::Text(text) => {
|
||||||
render_text(canvas, ts, mask, text);
|
render_text(canvas, ts, mask, text);
|
||||||
}
|
}
|
||||||
Element::Shape(shape) => {
|
Element::Shape(shape, _) => {
|
||||||
render_shape(canvas, ts, mask, shape);
|
render_shape(canvas, ts, mask, shape);
|
||||||
}
|
}
|
||||||
Element::Image(image, size) => {
|
Element::Image(image, size, _) => {
|
||||||
render_image(canvas, ts, mask, image, *size);
|
render_image(canvas, ts, mask, image, *size);
|
||||||
}
|
}
|
||||||
Element::Meta(meta, _) => match meta {
|
Element::Meta(meta, _) => match meta {
|
||||||
|
111
src/ide/jump.rs
111
src/ide/jump.rs
@ -1,7 +1,7 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use crate::doc::{Destination, Element, Frame, Location, Meta};
|
use crate::doc::{Destination, Element, Frame, Location, Meta};
|
||||||
use crate::geom::{Point, Size};
|
use crate::geom::{Geometry, Point, Size};
|
||||||
use crate::model::Introspector;
|
use crate::model::Introspector;
|
||||||
use crate::syntax::{LinkedNode, Source, SourceId, Span, SyntaxKind};
|
use crate::syntax::{LinkedNode, Source, SourceId, Span, SyntaxKind};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
@ -15,6 +15,14 @@ pub enum Jump {
|
|||||||
Dest(Destination),
|
Dest(Destination),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Jump {
|
||||||
|
fn from_span(world: &dyn World, span: Span) -> Self {
|
||||||
|
let source = world.source(span.source());
|
||||||
|
let node = source.find(span);
|
||||||
|
Self::Source(source.id(), node.offset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine where to jump to based on a click in a frame.
|
/// Determine where to jump to based on a click in a frame.
|
||||||
pub fn jump_from_click(
|
pub fn jump_from_click(
|
||||||
world: &dyn World,
|
world: &dyn World,
|
||||||
@ -24,48 +32,10 @@ pub fn jump_from_click(
|
|||||||
) -> Option<Jump> {
|
) -> Option<Jump> {
|
||||||
let mut introspector = None;
|
let mut introspector = None;
|
||||||
|
|
||||||
for (mut pos, element) in frame.elements() {
|
// Prefer metadata.
|
||||||
if let Element::Group(group) = element {
|
for (pos, element) in frame.elements() {
|
||||||
// TODO: Handle transformation.
|
|
||||||
if let Some(span) = jump_from_click(world, frames, &group.frame, click - pos)
|
|
||||||
{
|
|
||||||
return Some(span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Element::Text(text) = element {
|
|
||||||
for glyph in &text.glyphs {
|
|
||||||
if glyph.span.is_detached() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = glyph.x_advance.at(text.size);
|
|
||||||
if is_in_rect(
|
|
||||||
Point::new(pos.x, pos.y - text.size),
|
|
||||||
Size::new(width, text.size),
|
|
||||||
click,
|
|
||||||
) {
|
|
||||||
let source = world.source(glyph.span.source());
|
|
||||||
let node = source.find(glyph.span);
|
|
||||||
let pos = if node.kind() == SyntaxKind::Text {
|
|
||||||
let range = node.range();
|
|
||||||
let mut offset = range.start + usize::from(glyph.offset);
|
|
||||||
if (click.x - pos.x) > width / 2.0 {
|
|
||||||
offset += glyph.c.len_utf8();
|
|
||||||
}
|
|
||||||
offset.min(range.end)
|
|
||||||
} else {
|
|
||||||
node.offset()
|
|
||||||
};
|
|
||||||
return Some(Jump::Source(source.id(), pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
pos.x += width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Element::Meta(Meta::Link(link), size) = element {
|
if let Element::Meta(Meta::Link(link), size) = element {
|
||||||
if is_in_rect(pos, *size, click) {
|
if is_in_rect(*pos, *size, click) {
|
||||||
let dest = link.resolve(|| {
|
let dest = link.resolve(|| {
|
||||||
introspector.get_or_insert_with(|| Introspector::new(frames))
|
introspector.get_or_insert_with(|| Introspector::new(frames))
|
||||||
});
|
});
|
||||||
@ -76,6 +46,63 @@ pub fn jump_from_click(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (mut pos, element) in frame.elements().rev() {
|
||||||
|
match element {
|
||||||
|
Element::Group(group) => {
|
||||||
|
// TODO: Handle transformation.
|
||||||
|
if let Some(span) =
|
||||||
|
jump_from_click(world, frames, &group.frame, click - pos)
|
||||||
|
{
|
||||||
|
return Some(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element::Text(text) => {
|
||||||
|
for glyph in &text.glyphs {
|
||||||
|
if glyph.span.is_detached() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = glyph.x_advance.at(text.size);
|
||||||
|
if is_in_rect(
|
||||||
|
Point::new(pos.x, pos.y - text.size),
|
||||||
|
Size::new(width, text.size),
|
||||||
|
click,
|
||||||
|
) {
|
||||||
|
let source = world.source(glyph.span.source());
|
||||||
|
let node = source.find(glyph.span);
|
||||||
|
let pos = if node.kind() == SyntaxKind::Text {
|
||||||
|
let range = node.range();
|
||||||
|
let mut offset = range.start + usize::from(glyph.offset);
|
||||||
|
if (click.x - pos.x) > width / 2.0 {
|
||||||
|
offset += glyph.c.len_utf8();
|
||||||
|
}
|
||||||
|
offset.min(range.end)
|
||||||
|
} else {
|
||||||
|
node.offset()
|
||||||
|
};
|
||||||
|
return Some(Jump::Source(source.id(), pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.x += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element::Shape(shape, span) => {
|
||||||
|
let Geometry::Rect(size) = shape.geometry else { continue };
|
||||||
|
if is_in_rect(pos, size, click) {
|
||||||
|
return Some(Jump::from_span(world, *span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element::Image(_, size, span) if is_in_rect(pos, *size, click) => {
|
||||||
|
return Some(Jump::from_span(world, *span));
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user