ui: overhaul sidebar, add content filters and state improvements

Sidebar layout:
- Replace AdwNavigationSplitView with GtkPaned for a resizable sidebar
  with a persistent width stored in GSettings.
- Apply navigation-sidebar CSS class to the content Stack only (not the
  ToolbarView) so both header bars share the same colour and height.
- Override Adwaita's automatic paned-first-child header tint and gap via
  application-level CSS.
- Remove the gap between the sidebar header and the first list item.
- Add toggle-sidebar button and F9 shortcut; sidebar visibility and width
  are persisted across restarts.

Loading indicator:
- Replace the large AdwSpinner status page + header Stack with a small
  Gtk.Spinner (16×16) in the header Stack so the header height never
  changes during loading.

Article row:
- Add hexpand to title and excerpt labels so text reflows when the
  sidebar is resized.

Content:
- Inline CSS into the HTML template at load time (/*INJECT_CSS*/
  placeholder) so WebKit does not need a custom URI scheme handler.
- Fix max-width centering and padding for article body and header.
- Fix embedded video/iframe auto-opening in browser by checking
  NavigationType::LinkClicked instead of is_user_gesture().

Content filters:
- Add Preferences dialog with a TextView for content-rewrite rules
  stored in GSettings (content-filters key).
- Rule format: "domain find replace [find replace …]" one per line.
- Rules are applied to article HTML before display and reloaded on
  every refresh.

Shortcuts:
- Add Ctrl+W to close, Ctrl+Q to quit, F1 for keyboard shortcuts
  overlay, j/k and arrow-key navigation via a capture-phase controller
  so keys work regardless of which widget has focus.

Misc:
- Set window title to "FeedTheMonkey" (fixes Hyprland title bar).
- Update About dialog website URL.
This commit is contained in:
Jeena 2026-03-21 01:13:01 +00:00
parent 141f9ee32d
commit 8fd52dd8a0
16 changed files with 680 additions and 320 deletions

View file

@ -51,6 +51,33 @@ mod imp {
.expect("failed to load GResource");
gio::resources_register(&resource);
// Apply application-level CSS tweaks
let css = gtk4::CssProvider::new();
css.load_from_string(
"paned > :first-child .top-bar headerbar {
background-color: @headerbar_bg_color;
box-shadow: none;
}
paned > :first-child > toolbarview > .content {
padding-top: 0;
margin-top: 0;
}
.sidebar-content row:not(:selected) {
background-color: alpha(@window_fg_color, 0.07);
}
.sidebar-content row:not(:selected):hover {
background-color: alpha(@window_fg_color, 0.14);
}
.sidebar-content row:selected {
background-color: alpha(@window_fg_color, 0.22);
}"
);
gtk4::style_context_add_provider_for_display(
&gtk4::gdk::Display::default().unwrap(),
&css,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
let window = FeedTheMonkeyWindow::new(app.upcast_ref());
// Shortcuts overlay
@ -74,7 +101,7 @@ mod imp {
.version("3.0.0")
.copyright("© Jeena Paradies")
.license_type(gtk4::License::Gpl30)
.website("https://github.com/jeena/FeedTheMonkey")
.website("https://git.jeena.net/jeena/FeedTheMonkey")
.developer_name("Jeena Paradies")
.build();
dialog.present(win.as_ref().map(|w| w.upcast_ref::<gtk4::Widget>()));
@ -116,10 +143,8 @@ fn setup_shortcuts(window: &FeedTheMonkeyWindow) {
controller.add_shortcut(shortcut);
};
add(&controller, Key::j, ModifierType::empty(), "win.next-article");
add(&controller, Key::Right, ModifierType::empty(), "win.next-article");
add(&controller, Key::k, ModifierType::empty(), "win.prev-article");
add(&controller, Key::Left, ModifierType::empty(), "win.prev-article");
// j/k/Left/Right are handled by a capture-phase key controller in window.rs
// so they work regardless of which widget has focus.
add(&controller, Key::r, ModifierType::empty(), "win.reload");
add(&controller, Key::u, ModifierType::empty(), "win.mark-unread");
add(&controller, Key::Return, ModifierType::empty(), "win.open-in-browser");
@ -128,6 +153,7 @@ fn setup_shortcuts(window: &FeedTheMonkeyWindow) {
add(&controller, Key::equal, ModifierType::CONTROL_MASK, "win.zoom-in");
add(&controller, Key::minus, ModifierType::CONTROL_MASK, "win.zoom-out");
add(&controller, Key::_0, ModifierType::CONTROL_MASK, "win.zoom-reset");
add(&controller, Key::F9, ModifierType::empty(), "win.toggle-sidebar");
add(&controller, Key::F11, ModifierType::empty(), "win.toggle-fullscreen");
add(&controller, Key::w, ModifierType::CONTROL_MASK, "window.close");
add(&controller, Key::q, ModifierType::CONTROL_MASK, "app.quit");