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)
# Directory history stack
self.directory_history: list[str] = []
self.directory_history: list[Path] = []
# For overlay text timeout
self.overlay_hide_time = 0.0
@ -151,31 +151,7 @@ class MainWindow(Gtk.ApplicationWindow):
file_item = cast(FileItem, selected_item)
if file_item.file_type == FileType.DIRECTORY:
target_path = os.path.abspath(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()
self._navigate_to(file_item.full_path)
return
position = file_item.load_attribute("position", 0)
@ -287,6 +263,46 @@ class MainWindow(Gtk.ApplicationWindow):
item.connect("notify::attrs-changed", 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(
self,
selection_model: Gtk.SingleSelection,
@ -299,7 +315,7 @@ class MainWindow(Gtk.ApplicationWindow):
selected_item = selection_model.get_selected_item()
if 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:
"""Toggle between play and pause states"""
@ -396,20 +412,7 @@ class MainWindow(Gtk.ApplicationWindow):
return True
elif keyval == Gdk.keyval_from_name("BackSpace"):
if self.directory_history:
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
self._navigate_back()
return True
return False
@ -537,20 +540,15 @@ class MainWindow(Gtk.ApplicationWindow):
items: list[FileItem] = []
# Add parent directory
items.append(FileItem("..", FileType.DIRECTORY, "../"))
items.append(FileItem("..", FileType.DIRECTORY, Path("..").resolve()))
with os.scandir(".") as it:
for entry in it:
if entry.name != ".." and not entry.name.startswith("."):
if not entry.name.startswith("."):
try:
if entry.is_dir():
items.append(FileItem(entry.name, FileType.DIRECTORY, entry.name + "/"))
else:
file_item = FileItem.from_path(entry.name)
items.append(file_item)
items.append(FileItem.from_path(entry.name))
except ValueError:
# Skip unsupported file types
continue
continue # Skip unsupported file types
# Sort directories first, then files, both alphabetically
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)
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):

View file

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