mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Jump to destination
This commit is contained in:
parent
ad05ba5925
commit
a735bb9db3
@ -280,7 +280,7 @@ impl Frame {
|
||||
self.clear();
|
||||
break;
|
||||
}
|
||||
self.push(Point::zero(), Element::Meta(meta, self.size));
|
||||
self.prepend(Point::zero(), Element::Meta(meta, self.size));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,29 @@
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use crate::doc::{Element, Frame, Location};
|
||||
use crate::geom::Point;
|
||||
use crate::syntax::{LinkedNode, Source, Span, SyntaxKind};
|
||||
use crate::doc::{Destination, Element, Frame, Location, Meta};
|
||||
use crate::geom::{Point, Size};
|
||||
use crate::syntax::{LinkedNode, Source, SourceId, Span, SyntaxKind};
|
||||
use crate::World;
|
||||
|
||||
/// Find the source file and byte offset for a click position.
|
||||
pub fn jump_to_source<'a>(
|
||||
world: &'a dyn World,
|
||||
frame: &Frame,
|
||||
click: Point,
|
||||
) -> Option<(&'a Source, usize)> {
|
||||
/// Where to [jump](jump_from_click) to.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Jump {
|
||||
/// Jump to a position in a source file.
|
||||
Source(SourceId, usize),
|
||||
/// Jump to position in the output or to an external URL.
|
||||
Dest(Destination),
|
||||
}
|
||||
|
||||
/// Determine where to jump to based on a click in a frame.
|
||||
pub fn jump_from_click(world: &dyn World, frame: &Frame, click: Point) -> Option<Jump> {
|
||||
for (mut pos, element) in frame.elements() {
|
||||
if let Element::Group(group) = element {
|
||||
// TODO: Handle transformation.
|
||||
if let Some(span) = jump_from_click(world, &group.frame, click - pos) {
|
||||
return Some(span);
|
||||
}
|
||||
}
|
||||
|
||||
if let Element::Text(text) = element {
|
||||
for glyph in &text.glyphs {
|
||||
if glyph.span.is_detached() {
|
||||
@ -19,11 +31,11 @@ pub fn jump_to_source<'a>(
|
||||
}
|
||||
|
||||
let width = glyph.x_advance.at(text.size);
|
||||
if pos.x <= click.x
|
||||
&& pos.x + width >= click.x
|
||||
&& pos.y >= click.y
|
||||
&& pos.y - text.size <= click.y
|
||||
{
|
||||
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 {
|
||||
@ -32,16 +44,16 @@ pub fn jump_to_source<'a>(
|
||||
} else {
|
||||
node.offset()
|
||||
};
|
||||
return Some((source, pos));
|
||||
return Some(Jump::Source(source.id(), pos));
|
||||
}
|
||||
|
||||
pos.x += width;
|
||||
}
|
||||
}
|
||||
|
||||
if let Element::Group(group) = element {
|
||||
if let Some(span) = jump_to_source(world, &group.frame, click - pos) {
|
||||
return Some(span);
|
||||
if let Element::Meta(Meta::Link(dest), size) = element {
|
||||
if is_in_rect(pos, *size, click) {
|
||||
return Some(Jump::Dest(dest.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,8 +61,8 @@ pub fn jump_to_source<'a>(
|
||||
None
|
||||
}
|
||||
|
||||
/// Find the output location for a cursor position.
|
||||
pub fn jump_to_preview(
|
||||
/// Find the output location in the document for a cursor position.
|
||||
pub fn jump_from_cursor(
|
||||
frames: &[Frame],
|
||||
source: &Source,
|
||||
cursor: usize,
|
||||
@ -73,6 +85,13 @@ pub fn jump_to_preview(
|
||||
/// Find the position of a span in a frame.
|
||||
fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> {
|
||||
for (mut pos, element) in frame.elements() {
|
||||
if let Element::Group(group) = element {
|
||||
// TODO: Handle transformation.
|
||||
if let Some(point) = find_in_frame(&group.frame, span) {
|
||||
return Some(point + pos);
|
||||
}
|
||||
}
|
||||
|
||||
if let Element::Text(text) = element {
|
||||
for glyph in &text.glyphs {
|
||||
if glyph.span == span {
|
||||
@ -81,13 +100,16 @@ fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> {
|
||||
pos.x += glyph.x_advance.at(text.size);
|
||||
}
|
||||
}
|
||||
|
||||
if let Element::Group(group) = element {
|
||||
if let Some(point) = find_in_frame(&group.frame, span) {
|
||||
return Some(point + pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether a rectangle with the given size at the given position contains the
|
||||
/// click position.
|
||||
fn is_in_rect(pos: Point, size: Size, click: Point) -> bool {
|
||||
pos.x <= click.x
|
||||
&& pos.x + size.x >= click.x
|
||||
&& pos.y <= click.y
|
||||
&& pos.y + size.y >= click.y
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user