diff --git a/hyperplane/editable_row.py b/hyperplane/editable_row.py
index bc499f9..7640e6b 100644
--- a/hyperplane/editable_row.py
+++ b/hyperplane/editable_row.py
@@ -74,7 +74,7 @@ def start_edit(self) -> None:
self.check_revealer.set_reveal_child(True)
def end_edit(self) -> None:
- """Saves the edits and updates self accordingly."""
+ """Saves the edits and updates the row accordingly."""
self.check_revealer.set_reveal_child(False)
GLib.timeout_add(
self.check_revealer.get_transition_duration(),
@@ -136,7 +136,7 @@ def set_active(self) -> None:
@GObject.Property(type=str)
def identifier(self) -> str:
- """The identifier for self used in dconf."""
+ """The identifier for the row used in dconf."""
return self._identifier
@identifier.setter
diff --git a/hyperplane/gtk/path-entry.blp b/hyperplane/gtk/path-entry.blp
new file mode 100644
index 0000000..230fef2
--- /dev/null
+++ b/hyperplane/gtk/path-entry.blp
@@ -0,0 +1,12 @@
+using Gtk 4.0;
+
+template $HypPathEntry : Entry {
+ hexpand: true;
+
+ ShortcutController {
+ Shortcut {
+ trigger: "Escape";
+ action: "action(win.hide-path-entry)";
+ }
+ }
+}
\ No newline at end of file
diff --git a/hyperplane/gtk/style.css b/hyperplane/gtk/style.css
index e8d7d89..4c8b738 100644
--- a/hyperplane/gtk/style.css
+++ b/hyperplane/gtk/style.css
@@ -66,6 +66,10 @@ and that looks ugly.
filter: contrast(50%) brightness(130%);
}
+.sidebar-drop-target {
+ border-bottom: @accent_bg_color 3px solid;
+}
+
.sidebar-check-button > check {
color: @sidebar_fg_color;
background-color: @sidebar_shade_color;
diff --git a/hyperplane/gtk/window.blp b/hyperplane/gtk/window.blp
index 20609a3..fbe7fd8 100644
--- a/hyperplane/gtk/window.blp
+++ b/hyperplane/gtk/window.blp
@@ -226,16 +226,7 @@ template $HypWindow : Adw.ApplicationWindow {
}
Adw.Clamp path_entry_clamp {
- Entry path_entry {
- hexpand: true;
-
- ShortcutController {
- Shortcut {
- trigger: "Escape";
- action: "action(win.hide-path-entry)";
- }
- }
- }
+ $HypPathEntry path_entry {}
}
Adw.Clamp search_entry_clamp {
diff --git a/hyperplane/hyperplane.gresource.xml.in b/hyperplane/hyperplane.gresource.xml.in
index c556ce5..ba19750 100644
--- a/hyperplane/hyperplane.gresource.xml.in
+++ b/hyperplane/hyperplane.gresource.xml.in
@@ -5,6 +5,7 @@
gtk/item.ui
gtk/items-page.ui
gtk/path-bar.ui
+ gtk/path-entry.ui
gtk/path-segment.ui
gtk/preferences.ui
gtk/volumes-box.ui
diff --git a/hyperplane/item_sorter.py b/hyperplane/item_sorter.py
index 9af6da4..e9a931c 100644
--- a/hyperplane/item_sorter.py
+++ b/hyperplane/item_sorter.py
@@ -99,9 +99,5 @@ def do_compare(
def __ordering_from_cmpfunc(self, cmpfunc_result: int) -> Gtk.Ordering:
"""Converts the result of a `GCompareFunc` like `strcmp()` to a `GtkOrdering` value."""
- # HACK: https://gitlab.gnome.org/GNOME/gtk/-/issues/6298
- if cmpfunc_result > 0:
- return Gtk.Ordering.LARGER
- if cmpfunc_result < 0:
- return Gtk.Ordering.SMALLER
- return Gtk.Ordering.EQUAL
+ # https://gitlab.gnome.org/GNOME/gtk/-/issues/6298
+ return Gtk.Ordering((cmpfunc_result > 0) - (cmpfunc_result < 0))
diff --git a/hyperplane/items_page.py b/hyperplane/items_page.py
index 8564e48..f3e8ce9 100644
--- a/hyperplane/items_page.py
+++ b/hyperplane/items_page.py
@@ -446,7 +446,7 @@ def __right_click(self, _gesture, _n, x, y) -> None:
GLib.timeout_add(10, self.__popup_menu)
def __drop_file(
- self, _drop_target: Gtk.DropTarget, file_list: GObject.Value, _x, _y
+ self, _drop_target: Gtk.DropTarget, file_list: Gdk.FileList, _x, _y
) -> None:
# TODO: This is copy-paste from __paste()
for src in file_list:
@@ -516,7 +516,7 @@ def __drop_texture(
output.write_bytes(texture_bytes)
def __drop_text(
- self, _drop_target: Gtk.DropTarget, text: GObject.Value, _x, _y
+ self, _drop_target: Gtk.DropTarget, text: str, _x, _y
) -> None:
# TODO: Again again, copy-paste from __paste()
if not text: # If text is an empty string
@@ -895,7 +895,6 @@ def delete():
case 0:
return
case 1:
- # TODO: Blocking I/O for this? Really?
msg = _("Are you sure you want to permanently delete {}?").format(
f'"{get_gfile_display_name(gfiles[0])}"'
)
diff --git a/hyperplane/meson.build b/hyperplane/meson.build
index 96fbc39..bdd8561 100644
--- a/hyperplane/meson.build
+++ b/hyperplane/meson.build
@@ -6,6 +6,7 @@ blueprints = custom_target('blueprints',
'gtk/item.blp',
'gtk/items-page.blp',
'gtk/path-bar.blp',
+ 'gtk/path-entry.blp',
'gtk/path-segment.blp',
'gtk/preferences.blp',
'gtk/volumes-box.blp',
@@ -54,6 +55,7 @@ hyperplane_sources = [
'main.py',
'navigation_bin.py',
'path_bar.py',
+ 'path_entry.py',
'path_segment.py',
'postmaster_general.py',
'preferences.py',
diff --git a/hyperplane/path_bar.py b/hyperplane/path_bar.py
index c210caf..bb6cc5b 100644
--- a/hyperplane/path_bar.py
+++ b/hyperplane/path_bar.py
@@ -18,9 +18,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later
"""The path bar in a HypWindow."""
-from typing import Optional
+from os import sep
+from pathlib import Path
+from typing import Iterable, Optional
+from urllib.parse import unquote, urlparse
-from gi.repository import GLib, Gtk
+from gi.repository import Gio, GLib, Gtk
from hyperplane import shared
from hyperplane.path_segment import HypPathSegment
@@ -57,15 +60,15 @@ def remove(self, n: int) -> None:
child,
)
- if not (sep := self.separators[child]):
+ if not (separator := self.separators[child]):
return
- sep.set_reveal_child(False)
+ separator.set_reveal_child(False)
GLib.timeout_add(
- sep.get_transition_duration(),
+ separator.get_transition_duration(),
self.__remove_child,
self.segments_box,
- sep,
+ separator,
)
self.separators.pop(child)
@@ -97,13 +100,13 @@ def append(
sep_label = Gtk.Label.new("+" if self.tags else "/")
sep_label.add_css_class("heading" if self.tags else "dim-label")
- sep = Gtk.Revealer(
+ separator = Gtk.Revealer(
child=sep_label, transition_type=Gtk.RevealerTransitionType.SLIDE_RIGHT
)
- self.segments_box.append(sep)
- sep.set_reveal_child(True)
+ self.segments_box.append(separator)
+ separator.set_reveal_child(True)
else:
- sep = None
+ separator = None
segment = HypPathSegment(label, icon_name, uri, tag)
self.segments_box.append(segment)
@@ -111,7 +114,7 @@ def append(
segment.set_transition_type(Gtk.RevealerTransitionType.SLIDE_RIGHT)
segment.set_reveal_child(True)
- self.separators[segment] = sep
+ self.separators[segment] = separator
self.segments.append(segment)
last_segment = self.segments[-1]
@@ -140,6 +143,109 @@ def purge(self) -> None:
self.segments = []
self.separators = {}
+ def update(self, gfile: Optional[Gio.File], tags: Optional[Iterable[str]]) -> None:
+ """Updates the bar according to a new `gfile` or new `tags`."""
+ if gfile:
+ if self.tags:
+ self.purge()
+
+ self.tags = False
+
+ uri = gfile.get_uri()
+ parse = urlparse(uri)
+ segments = []
+
+ # Do these automatically is shceme != "file"
+ if parse.scheme != "file":
+ scheme_uri = f"{parse.scheme}://"
+ try:
+ file_info = Gio.File.new_for_uri(scheme_uri).query_info(
+ ",".join(
+ (
+ Gio.FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON,
+ Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ )
+ ),
+ Gio.FileQueryInfoFlags.NONE,
+ )
+ except GLib.Error:
+ pass
+ else:
+ display_name = file_info.get_display_name()
+ symbolic = file_info.get_symbolic_icon()
+
+ segments.insert(
+ 0,
+ (
+ display_name,
+ symbolic.get_names()[0] if symbolic else None,
+ scheme_uri,
+ None,
+ ),
+ )
+
+ parts = unquote(parse.path).split(sep)
+
+ for index, part in enumerate(parts):
+ if not part:
+ continue
+
+ segments.append((part, "", f"file://{sep.join(parts[:index+1])}", None))
+
+ if (path := gfile.get_path()) and (
+ (path := Path(path)) == shared.home_path
+ or path.is_relative_to(shared.home_path)
+ ):
+ segments = segments[len(shared.home_path.parts) - 1 :]
+ segments.insert(
+ 0,
+ (_("Home"), "user-home-symbolic", shared.home_path.as_uri(), None),
+ )
+ elif parse.scheme == "file":
+ # Not relative to home, so add a root segment
+ segments.insert(
+ 0,
+ (
+ "",
+ "drive-harddisk-symbolic",
+ # Fall back to sep if the GFile doesn't have a path
+ Path(path.anchor if path else sep).as_uri(),
+ None,
+ ),
+ )
+
+ elif tags:
+ if not self.tags:
+ self.purge()
+
+ self.tags = True
+
+ segments = tuple((tag, "", None, tag) for tag in tags)
+
+ if (old_len := len(self.segments)) > (new_len := len(segments)):
+ self.remove(old_len - new_len)
+
+ append = False
+ for index, new_segment in enumerate(segments):
+ try:
+ old_segment = self.segments[index]
+ except IndexError:
+ old_segment = None
+
+ if (
+ not append
+ and old_segment
+ and new_segment[2] == old_segment.uri
+ and new_segment[3] == old_segment.tag
+ ):
+ continue
+
+ if not append:
+ self.remove(len(self.segments) - index)
+ append = True
+
+ self.append(*new_segment)
+
def __remove_child(self, parent: Gtk.Box, child: Gtk.Widget) -> None:
# This is so GTK doesn't freak out when the child isn't in the parent anymore
if child.get_parent == parent:
diff --git a/hyperplane/path_entry.py b/hyperplane/path_entry.py
new file mode 100644
index 0000000..f31bba5
--- /dev/null
+++ b/hyperplane/path_entry.py
@@ -0,0 +1,78 @@
+# path_entry.py
+#
+# Copyright 2023 kramo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+"""An entry for navigating to paths or tags."""
+from typing import Any
+
+from gi.repository import Gio, GObject, Gtk
+
+from hyperplane import shared
+
+
+@Gtk.Template(resource_path=shared.PREFIX + "/gtk/path-entry.ui")
+class HypPathEntry(Gtk.Entry):
+ """An entry for navigating to paths or tags."""
+
+ __gtype_name__ = "HypPathEntry"
+
+ def __init__(self, **kwargs) -> None:
+ super().__init__(**kwargs)
+ self.connect("activate", self.__activate)
+
+ def __activate(self, entry, *_args: Any) -> None:
+ text = entry.get_text().strip()
+
+ if text.startswith("//"):
+ tags = list(
+ tag
+ for tag in shared.tags
+ if tag in text.lstrip("/").rstrip("/").split("//")
+ )
+
+ if not tags:
+ self.get_root().send_toast(_("No such tags"))
+ return
+
+ self.emit("hide-entry")
+ self.get_root().new_page(tags=tags)
+ return
+
+ if "://" in text:
+ gfile = Gio.File.new_for_uri(text)
+ else:
+ gfile = Gio.File.new_for_path(text)
+
+ if (
+ not gfile.query_file_type(Gio.FileQueryInfoFlags.NONE)
+ == Gio.FileType.DIRECTORY
+ ):
+ self.get_root().send_toast(_("Unable to find path"))
+ return
+
+ self.emit("hide-entry")
+
+ self.get_root().new_page(gfile)
+
+ @GObject.Signal(name="hide-entry")
+ def hide(self) -> None:
+ """
+ Emitted to indicate that the entry is done and should be hidden.
+
+ Containers of this widget should connect to it and react accordingly.
+ """
diff --git a/hyperplane/postmaster_general.py b/hyperplane/postmaster_general.py
index fd777ec..aac245b 100644
--- a/hyperplane/postmaster_general.py
+++ b/hyperplane/postmaster_general.py
@@ -46,7 +46,7 @@ def tags_changed(self, change: Gtk.FilterChange) -> None:
All objects that keep an internal list of tags should connect to it
and update their list accordingly.
- `change` represents whether tags were added, removed or just reordered.
+ `change` indicates whether tags were added, removed or just reordered.
This is only relevant for item filters.
"""
diff --git a/hyperplane/tag_row.py b/hyperplane/tag_row.py
index b1cae31..9350f77 100644
--- a/hyperplane/tag_row.py
+++ b/hyperplane/tag_row.py
@@ -18,12 +18,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later
"""A row in the sidebar representing a tag."""
-from typing import Any
-
from gi.repository import Gdk, Gtk
from hyperplane import shared
from hyperplane.editable_row import HypEditableRow
+from hyperplane.utils.tags import update_tags
class HypTagRow(HypEditableRow):
@@ -49,6 +48,48 @@ def __init__(self, tag: str, icon_name: str, **kwargs) -> None:
)
self.add_controller(middle_click)
+ # Drag and drop
+ drag_source = Gtk.DragSource.new()
+ drag_source.connect("prepare", self.__drag_prepare)
+ drag_source.connect("drag-begin", self.__drag_begin)
+ drag_source.set_actions(Gdk.DragAction.MOVE)
+ self.box.add_controller(drag_source)
+
+ drop_target = Gtk.DropTarget.new(str, Gdk.DragAction.MOVE)
+ drop_target.connect("enter", self.__drop_enter)
+ drop_target.connect("leave", self.__drop_leave)
+ drop_target.connect("drop", self.__drop)
+ self.add_controller(drop_target)
+
+ def __drag_prepare(self, _src: Gtk.DragSource, _x: float, _y: float) -> None:
+ return Gdk.ContentProvider.new_for_value(self.tag)
+
+ def __drag_begin(self, src: Gtk.DragSource, _drag: Gdk.Drag) -> None:
+ src.set_icon(Gtk.WidgetPaintable.new(self), 0, 0)
+
+ def __drop_enter(self, _target: Gtk.DropTarget, _x: float, _y: float) -> None:
+ self.add_css_class("sidebar-drop-target")
+
+ return Gdk.DragAction.MOVE
+
+ def __drop_leave(self, _target: Gtk.DropTarget) -> None:
+ self.remove_css_class("sidebar-drop-target")
+
+ def __drop(
+ self, _target: Gtk.DropTarget, string: str, _x: float, _y: float
+ ) -> None:
+ if not string in shared.tags:
+ return
+
+ self_index = shared.tags.index(self.tag)
+ str_index = shared.tags.index(string)
+
+ shared.tags.insert(
+ self_index + int(self_index < str_index), shared.tags.pop(str_index)
+ )
+
+ update_tags()
+
def __right_click(
self, _gesture: Gtk.GestureClick, _n: int, x: float, y: float
) -> None:
diff --git a/hyperplane/utils/tags.py b/hyperplane/utils/tags.py
index a66e6fa..5adef35 100644
--- a/hyperplane/utils/tags.py
+++ b/hyperplane/utils/tags.py
@@ -26,11 +26,19 @@
from hyperplane import shared
-def __update_tags() -> None:
+def update_tags(change: Gtk.FilterChange = Gtk.FilterChange.DIFFERENT) -> None:
+ """
+ Writes the list of tags from `shared.tags` to disk and notifies widgets.
+
+ `change` indicates whether tags were
+ added (more strict), removed (less strict) or just reordered (different).
+ """
(shared.home_path / ".hyperplane").write_text(
"\n".join(shared.tags), encoding="utf-8"
)
+ shared.postmaster.emit("tags-changed", change)
+
def path_represents_tags(path: PathLike | str) -> bool:
"""Checks whether a given `path` represents tags or not."""
@@ -53,9 +61,7 @@ def add_tags(*tags: str) -> None:
"""
for tag in tags:
shared.tags.append(tag)
- __update_tags()
-
- shared.postmaster.emit("tags-changed", Gtk.FilterChange.MORE_STRICT)
+ update_tags(Gtk.FilterChange.MORE_STRICT)
def remove_tags(*tags: str) -> None:
@@ -63,9 +69,7 @@ def remove_tags(*tags: str) -> None:
for tag in tags:
if tag in shared.tags:
shared.tags.remove(tag)
- __update_tags()
-
- shared.postmaster.emit("tags-changed", Gtk.FilterChange.LESS_STRICT)
+ update_tags(Gtk.FilterChange.LESS_STRICT)
def move_tag(tag: str, up: bool) -> None:
@@ -83,9 +87,7 @@ def move_tag(tag: str, up: bool) -> None:
shared.tags[index - 1],
shared.tags[index],
)
- __update_tags()
-
- shared.postmaster.emit("tags-changed", Gtk.FilterChange.DIFFERENT)
+ update_tags()
return
# Moving down
@@ -100,6 +102,4 @@ def move_tag(tag: str, up: bool) -> None:
shared.tags[index],
)
- __update_tags()
-
- shared.postmaster.emit("tags-changed", Gtk.FilterChange.DIFFERENT)
+ update_tags()
diff --git a/hyperplane/window.py b/hyperplane/window.py
index 85cdc21..81d538a 100644
--- a/hyperplane/window.py
+++ b/hyperplane/window.py
@@ -20,10 +20,8 @@
"""The main application window."""
from itertools import chain
from os import sep
-from pathlib import Path
from time import time
from typing import Any, Callable, Iterable, Optional, Self
-from urllib.parse import unquote, urlparse
from gi.repository import Adw, Gdk, Gio, GLib, Gtk
@@ -32,6 +30,7 @@
from hyperplane.items_page import HypItemsPage
from hyperplane.navigation_bin import HypNavigationBin
from hyperplane.path_bar import HypPathBar
+from hyperplane.path_entry import HypPathEntry
from hyperplane.properties import HypPropertiesWindow
from hyperplane.tag_row import HypTagRow
from hyperplane.utils.create_message_dialog import create_message_dialog
@@ -51,11 +50,14 @@ class HypWindow(Adw.ApplicationWindow):
__gtype_name__ = "HypWindow"
+ # Main view
tab_overview: Adw.TabOverview = Gtk.Template.Child()
toast_overlay: Adw.ToastOverlay = Gtk.Template.Child()
overlay_split_view: Adw.OverlaySplitView = Gtk.Template.Child()
tab_view: Adw.TabView = Gtk.Template.Child()
toolbar_view: Adw.ToolbarView = Gtk.Template.Child()
+
+ # Sidebar
sidebar: Gtk.ListBox = Gtk.Template.Child()
sidebar_action_bar: Gtk.ActionBar = Gtk.Template.Child()
home_row: Gtk.Box = Gtk.Template.Child()
@@ -65,15 +67,17 @@ class HypWindow(Adw.ApplicationWindow):
trash_row: HypEditableRow = Gtk.Template.Child()
volumes_box: HypVolumesBox = Gtk.Template.Child()
+ # Header bar
title_stack: Gtk.Stack = Gtk.Template.Child()
path_bar_clamp: Adw.Clamp = Gtk.Template.Child()
path_bar: HypPathBar = Gtk.Template.Child()
path_entry_clamp: Adw.Clamp = Gtk.Template.Child()
- path_entry: Gtk.Entry = Gtk.Template.Child()
+ path_entry: HypPathEntry = Gtk.Template.Child()
search_entry_clamp: Adw.Clamp = Gtk.Template.Child()
search_entry: Gtk.SearchEntry = Gtk.Template.Child()
search_button: Gtk.ToggleButton = Gtk.Template.Child()
+ # Rename popover
rename_popover: Gtk.Popover = Gtk.Template.Child()
rename_label: Gtk.Label = Gtk.Template.Child()
rename_entry: Adw.EntryRow = Gtk.Template.Child()
@@ -81,11 +85,12 @@ class HypWindow(Adw.ApplicationWindow):
rename_revealer_label: Gtk.Label = Gtk.Template.Child()
rename_button: Gtk.Button = Gtk.Template.Child()
+ # Right-click menus
right_click_menu: Gtk.PopoverMenu = Gtk.Template.Child()
tag_right_click_menu: Gtk.PopoverMenu = Gtk.Template.Child()
file_right_click_menu: Gtk.PopoverMenu = Gtk.Template.Child()
- path_bar_connection: int
+ path_entry_connection: int
sidebar_tag_rows: set
right_clicked_tag: str
@@ -122,10 +127,12 @@ def __init__(self, **kwargs) -> None:
# Create actions
navigation_view = HypNavigationBin(initial_gfile=shared.home)
+
self.tab_view.append(navigation_view).set_title(
- title := self.get_visible_page().get_title()
+ title := (page := self.get_visible_page()).get_title()
)
- self.__update_path_bar()
+
+ self.path_bar.update(page.gfile, page.tags)
self.set_title(title)
self.create_action(
@@ -196,13 +203,14 @@ def __init__(self, **kwargs) -> None:
self.tab_view.connect("create-window", self.__create_window)
self.tab_overview.connect("create-tab", self.__create_tab)
- self.path_entry.connect("activate", self.__path_entry_activated)
self.search_entry.connect("search-started", self.__show_search_entry)
self.search_entry.connect("search-changed", self.__search_changed)
self.search_entry.connect("stop-search", self.__hide_search_entry)
self.search_entry.connect("activate", self.__search_activate)
self.search_button.connect("clicked", self.__toggle_search_entry)
+ self.path_entry.connect("hide-entry", self.__hide_path_entry)
+
shared.postmaster.connect("tags-changed", self.__update_tags)
shared.postmaster.connect("sidebar-edited", self.__sidebar_edited)
shared.postmaster.connect(
@@ -496,110 +504,6 @@ def __navigation_changed(self, view: Adw.NavigationView, *_args: Any) -> None:
self.__nav_stack_changed()
- def __update_path_bar(self) -> None:
- page = self.get_visible_page()
-
- if page.gfile:
- if self.path_bar.tags:
- self.path_bar.purge()
-
- self.path_bar.tags = False
-
- uri = page.gfile.get_uri()
- parse = urlparse(uri)
- segments = []
-
- # Do these automatically is shceme != "file"
- if parse.scheme != "file":
- scheme_uri = f"{parse.scheme}://"
- try:
- file_info = Gio.File.new_for_uri(scheme_uri).query_info(
- ",".join(
- (
- Gio.FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON,
- Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
- )
- ),
- Gio.FileQueryInfoFlags.NONE,
- )
- except GLib.Error:
- pass
- else:
- display_name = file_info.get_display_name()
- symbolic = file_info.get_symbolic_icon()
-
- segments.insert(
- 0,
- (
- display_name,
- symbolic.get_names()[0] if symbolic else None,
- scheme_uri,
- None,
- ),
- )
-
- parts = unquote(parse.path).split(sep)
-
- for index, part in enumerate(parts):
- if not part:
- continue
-
- segments.append((part, "", f"file://{sep.join(parts[:index+1])}", None))
-
- if (path := page.gfile.get_path()) and (
- (path := Path(path)) == shared.home_path
- or path.is_relative_to(shared.home_path)
- ):
- segments = segments[len(shared.home_path.parts) - 1 :]
- segments.insert(
- 0,
- (_("Home"), "user-home-symbolic", shared.home_path.as_uri(), None),
- )
- elif parse.scheme == "file":
- # Not relative to home, so add a root segment
- segments.insert(
- 0,
- (
- "",
- "drive-harddisk-symbolic",
- # Fall back to sep if the GFile doesn't have a path
- Path(path.anchor if path else sep).as_uri(),
- None,
- ),
- )
-
- elif page.tags:
- if not self.path_bar.tags:
- self.path_bar.purge()
-
- self.path_bar.tags = True
-
- segments = tuple((tag, "", None, tag) for tag in page.tags)
-
- if (old_len := len(self.path_bar.segments)) > (new_len := len(segments)):
- self.path_bar.remove(old_len - new_len)
-
- append = False
- for index, new_segment in enumerate(segments):
- try:
- old_segment = self.path_bar.segments[index]
- except IndexError:
- old_segment = None
-
- if (
- not append
- and old_segment
- and new_segment[2] == old_segment.uri
- and new_segment[3] == old_segment.tag
- ):
- continue
-
- if not append:
- self.path_bar.remove(len(self.path_bar.segments) - index)
- append = True
-
- self.path_bar.append(*new_segment)
-
def __page_attached(self, _view: Adw.TabView, page: Adw.TabPage, _pos: int) -> None:
page.get_child().view.connect("popped", self.__navigation_changed)
page.get_child().view.connect("pushed", self.__navigation_changed)
@@ -619,39 +523,6 @@ def __reopen_tab(self, *_args: Any) -> None:
return
self.tab_view.append(page).set_title(title)
- def __path_entry_activated(self, entry, *_args: Any) -> None:
- text = entry.get_text().strip()
-
- if text.startswith("//"):
- self.__hide_path_entry()
- tags = list(
- tag
- for tag in shared.tags
- if tag in text.lstrip("/").rstrip("/").split("//")
- )
-
- if not tags:
- self.send_toast(_("No such tags"))
-
- self.new_page(tags=tags)
- return
-
- if "://" in text:
- gfile = Gio.File.new_for_uri(text)
- else:
- gfile = Gio.File.new_for_path(text)
-
- if (
- not gfile.query_file_type(Gio.FileQueryInfoFlags.NONE)
- == Gio.FileType.DIRECTORY
- ):
- self.send_toast(_("Unable to find path"))
- return
-
- self.__hide_path_entry()
-
- self.new_page(gfile)
-
def __title_stack_set_child(self, new: Gtk.Widget) -> None:
old = self.title_stack.get_visible_child()
if old == new:
@@ -666,9 +537,9 @@ def __title_stack_set_child(self, new: Gtk.Widget) -> None:
shared.search = ""
self.searched_page.item_filter.changed(Gtk.FilterChange.LESS_STRICT)
case self.path_entry_clamp:
- if self.path_bar_connection:
- self.path_entry.disconnect(self.path_bar_connection)
- self.path_bar_connection = None
+ if self.path_entry_connection:
+ self.path_entry.disconnect(self.path_entry_connection)
+ self.path_entry_connection = None
match new:
case self.search_entry_clamp:
@@ -697,7 +568,7 @@ def __title_stack_set_child(self, new: Gtk.Widget) -> None:
self.set_focus(self.path_entry)
self.path_entry.select_region(-1, -1)
- self.path_bar_connection = self.path_entry.connect(
+ self.path_entry_connection = self.path_entry.connect(
"notify::has-focus", self.__path_entry_focus
)
case self.path_bar_clamp:
@@ -854,7 +725,9 @@ def __set_actions(self, *_args: Any) -> None:
)
def __nav_stack_changed(self) -> None:
- self.__update_path_bar()
+ page = self.get_visible_page()
+
+ self.path_bar.update(page.gfile, page.tags)
self.lookup_action("back").set_enabled(
bool(