Refactor directory navigation

This commit is contained in:
Jan Hamal Dvořák 2025-03-09 11:49:42 +01:00
parent 2fd45b556f
commit b0375c863e
2 changed files with 59 additions and 62 deletions

View file

@ -34,7 +34,7 @@ class MainWindow(Gtk.ApplicationWindow):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Directory history stack # Directory history stack
self.directory_history: list[str] = [] self.directory_history: list[Path] = []
# For overlay text timeout # For overlay text timeout
self.overlay_hide_time = 0.0 self.overlay_hide_time = 0.0
@ -151,31 +151,7 @@ class MainWindow(Gtk.ApplicationWindow):
file_item = cast(FileItem, selected_item) file_item = cast(FileItem, selected_item)
if file_item.file_type == FileType.DIRECTORY: if file_item.file_type == FileType.DIRECTORY:
target_path = os.path.abspath(file_item.full_path) self._navigate_to(file_item.full_path)
# Check if we have history and if target is where we came from
if self.directory_history and os.path.samefile(
os.path.dirname(target_path), self.directory_history[-1]
):
# Use history instead
prev_dir = self.directory_history.pop()
current_dir_name = Path(os.getcwd()).name
os.chdir(prev_dir)
self._populate_file_list()
# Find and select the directory we came from
for i in range(self.list_store.get_n_items()):
item = self.list_store.get_item(i)
if item and cast(FileItem, item).name == current_dir_name:
self.list_view.scroll_to(
i, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None
)
break
else:
# Regular directory navigation
self.directory_history.append(os.getcwd())
os.chdir(file_item.full_path)
self._populate_file_list()
return return
position = file_item.load_attribute("position", 0) position = file_item.load_attribute("position", 0)
@ -287,6 +263,46 @@ class MainWindow(Gtk.ApplicationWindow):
item.connect("notify::attrs-changed", update_icon) item.connect("notify::attrs-changed", update_icon)
update_icon() update_icon()
def _navigate_to(self, path: Path):
# Check if we have history and if target is where we came from
if self.directory_history:
print(path, "vs", self.directory_history[-1])
if path == self.directory_history[-1]:
print("navigate back")
self._navigate_back()
return
print("navigate to", path)
# Regular directory navigation
self.directory_history.append(Path(os.getcwd()))
os.chdir(path)
self._populate_file_list()
def _navigate_back(self):
if not self.directory_history:
return
prev_dir = self.directory_history.pop()
current_dir = Path(os.getcwd())
os.chdir(prev_dir)
self._populate_file_list()
# Find and select the directory we came from
for i in range(self.list_store.get_n_items()):
item = self.list_store.get_item(i)
if not item:
continue
file_item = cast(FileItem, item)
if current_dir == file_item.full_path:
self.list_view.scroll_to(
i, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None
)
break
return True
def _on_selection_changed( def _on_selection_changed(
self, self,
selection_model: Gtk.SingleSelection, selection_model: Gtk.SingleSelection,
@ -299,7 +315,7 @@ class MainWindow(Gtk.ApplicationWindow):
selected_item = selection_model.get_selected_item() selected_item = selection_model.get_selected_item()
if selected_item: if selected_item:
file_item = cast(FileItem, selected_item) file_item = cast(FileItem, selected_item)
self.file_info_label.set_text(file_item.full_path) self.file_info_label.set_text(file_item.name)
def _toggle_play_pause(self) -> None: def _toggle_play_pause(self) -> None:
"""Toggle between play and pause states""" """Toggle between play and pause states"""
@ -396,20 +412,7 @@ class MainWindow(Gtk.ApplicationWindow):
return True return True
elif keyval == Gdk.keyval_from_name("BackSpace"): elif keyval == Gdk.keyval_from_name("BackSpace"):
if self.directory_history: self._navigate_back()
prev_dir = self.directory_history.pop()
current_dir_name = Path(os.getcwd()).name
os.chdir(prev_dir)
self._populate_file_list()
# Find and select the directory we came from
for i in range(self.list_store.get_n_items()):
item = self.list_store.get_item(i)
if item and cast(FileItem, item).name == current_dir_name:
self.list_view.scroll_to(
i, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, None
)
break
return True return True
return False return False
@ -537,20 +540,15 @@ class MainWindow(Gtk.ApplicationWindow):
items: list[FileItem] = [] items: list[FileItem] = []
# Add parent directory # Add parent directory
items.append(FileItem("..", FileType.DIRECTORY, "../")) items.append(FileItem("..", FileType.DIRECTORY, Path("..").resolve()))
with os.scandir(".") as it: with os.scandir(".") as it:
for entry in it: for entry in it:
if entry.name != ".." and not entry.name.startswith("."): if not entry.name.startswith("."):
try: try:
if entry.is_dir(): items.append(FileItem.from_path(entry.name))
items.append(FileItem(entry.name, FileType.DIRECTORY, entry.name + "/"))
else:
file_item = FileItem.from_path(entry.name)
items.append(file_item)
except ValueError: except ValueError:
# Skip unsupported file types continue # Skip unsupported file types
continue
# Sort directories first, then files, both alphabetically # Sort directories first, then files, both alphabetically
items.sort(key=lambda x: (x.file_type != FileType.DIRECTORY, x.name.lower())) items.sort(key=lambda x: (x.file_type != FileType.DIRECTORY, x.name.lower()))
@ -562,7 +560,7 @@ class MainWindow(Gtk.ApplicationWindow):
self.list_store.append(item) self.list_store.append(item)
if items: if items:
self.file_info_label.set_text(items[0].full_path) self.file_info_label.set_text(items[0].name)
class App(Gtk.Application): class App(Gtk.Application):

View file

@ -2,6 +2,7 @@ from __future__ import annotations
import os import os
from enum import Enum, auto from enum import Enum, auto
from pathlib import Path
from typing import overload from typing import overload
import gi import gi
@ -17,13 +18,13 @@ class FileType(Enum):
class FileItem(GObject.Object): class FileItem(GObject.Object):
file_type: FileType file_type: FileType
full_path: str full_path: Path
__gtype_name__ = "FileItem" __gtype_name__ = "FileItem"
attrs_changed = GObject.Property(type=int, default=0) attrs_changed = GObject.Property(type=int, default=0)
def __init__(self, name: str, file_type: FileType, full_path: str): def __init__(self, name: str, file_type: FileType, full_path: Path):
super().__init__() super().__init__()
self.attrs_changed = 0 self.attrs_changed = 0
self.name = name self.name = name
@ -31,18 +32,16 @@ class FileItem(GObject.Object):
self.full_path = full_path self.full_path = full_path
@staticmethod @staticmethod
def from_path(path: str) -> FileItem: def from_path(path: str | Path) -> FileItem:
name = os.path.basename(path) full_path = Path(path).resolve()
if path.endswith("/"):
return FileItem(name, FileType.DIRECTORY, path)
parts = name.split(".") if full_path.is_dir():
suffix = parts[-1].lower() if len(parts) >= 2 else "" return FileItem(full_path.name, FileType.DIRECTORY, full_path)
if suffix in ("mkv", "mp4", "avi"): if full_path.suffix in (".mkv", ".mp4", ".avi"):
return FileItem(name, FileType.VIDEO, path) return FileItem(full_path.name, FileType.VIDEO, full_path)
raise ValueError(f"Unsupported file type: {path}") raise ValueError(f"Unsupported file type: {full_path}")
@overload @overload
def load_attribute(self, name: str, dfl: str) -> str: ... def load_attribute(self, name: str, dfl: str) -> str: ...