implement anchor auto-scroll behavior (on page load)

This commit is contained in:
yggverse 2026-03-10 02:36:51 +02:00
parent 36f5d29fa4
commit 7d8bce152b
2 changed files with 38 additions and 3 deletions

View file

@ -4,11 +4,11 @@ mod tags;
use super::{ItemAction, WindowAction}; use super::{ItemAction, WindowAction};
use crate::app::browser::window::action::Position; use crate::app::browser::window::action::Position;
use gtk::{ use gtk::{
EventControllerMotion, GestureClick, TextBuffer, TextTag, TextTagTable, TextView, EventControllerMotion, GestureClick, TextBuffer, TextSearchFlags, TextTag, TextTagTable,
TextWindowType, UriLauncher, Window, WrapMode, TextView, TextWindowType, UriLauncher, Window, WrapMode,
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, RGBA}, gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, RGBA},
gio::{Cancellable, SimpleAction, SimpleActionGroup}, gio::{Cancellable, SimpleAction, SimpleActionGroup},
glib::{Uri, uuid_string_random}, glib::{ControlFlow, Uri, idle_add_local, uri_unescape_string, uuid_string_random},
prelude::{PopoverExt, TextBufferExt, TextTagExt, TextViewExt, WidgetExt}, prelude::{PopoverExt, TextBufferExt, TextTagExt, TextViewExt, WidgetExt},
}; };
use gutter::Gutter; use gutter::Gutter;
@ -308,6 +308,34 @@ impl Markdown {
} }
}); // @TODO may be expensive for CPU, add timeout? }); // @TODO may be expensive for CPU, add timeout?
// Anchor auto-scroll behavior (@TODO navigate without page reload)
idle_add_local({
let base = base.clone();
let text_view = text_view.clone();
move || {
if let Some(fragment) = base.fragment() {
let query = uri_unescape_string(&fragment, None::<&str>)
.unwrap_or(fragment)
.replace("-", " ");
let mut cursor = text_view.buffer().start_iter();
while let Some((mut match_start, match_end)) =
cursor.forward_search(&query, TextSearchFlags::CASE_INSENSITIVE, None)
{
if match_start
.tags()
.iter()
.any(|t| t.name().is_some_and(|n| n.starts_with("h")))
{
text_view.scroll_to_iter(&mut match_start, 0.0, true, 0.0, 0.0);
break;
}
cursor = match_end;
}
}
ControlFlow::Break
}
});
Self { text_view, title } Self { text_view, title }
} }
} }

View file

@ -17,9 +17,11 @@ pub struct Header {
impl Header { impl Header {
pub fn new() -> Self { pub fn new() -> Self {
// * important to give the tag name here as used in the fragment search
Self { Self {
h1: TextTag::builder() h1: TextTag::builder()
.foreground("#2190a4") // @TODO optional .foreground("#2190a4") // @TODO optional
.name("h1")
.scale(1.6) .scale(1.6)
.sentence(true) .sentence(true)
.weight(500) .weight(500)
@ -27,6 +29,7 @@ impl Header {
.build(), .build(),
h2: TextTag::builder() h2: TextTag::builder()
.foreground("#d56199") // @TODO optional .foreground("#d56199") // @TODO optional
.name("h2")
.scale(1.4) .scale(1.4)
.sentence(true) .sentence(true)
.weight(400) .weight(400)
@ -34,6 +37,7 @@ impl Header {
.build(), .build(),
h3: TextTag::builder() h3: TextTag::builder()
.foreground("#c88800") // @TODO optional .foreground("#c88800") // @TODO optional
.name("h3")
.scale(1.2) .scale(1.2)
.sentence(true) .sentence(true)
.weight(400) .weight(400)
@ -41,6 +45,7 @@ impl Header {
.build(), .build(),
h4: TextTag::builder() h4: TextTag::builder()
.foreground("#c88800") // @TODO optional .foreground("#c88800") // @TODO optional
.name("h4")
.scale(1.1) .scale(1.1)
.sentence(true) .sentence(true)
.weight(400) .weight(400)
@ -48,6 +53,7 @@ impl Header {
.build(), .build(),
h5: TextTag::builder() h5: TextTag::builder()
.foreground("#c88800") // @TODO optional .foreground("#c88800") // @TODO optional
.name("h5")
.scale(1.0) .scale(1.0)
.sentence(true) .sentence(true)
.weight(400) .weight(400)
@ -55,6 +61,7 @@ impl Header {
.build(), .build(),
h6: TextTag::builder() h6: TextTag::builder()
.foreground("#c88800") // @TODO optional .foreground("#c88800") // @TODO optional
.name("h6")
.scale(1.0) .scale(1.0)
.sentence(true) .sentence(true)
.weight(300) .weight(300)