From 27e754b3f79f570ce36adda5d9647b8dd5f3e590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= <mordae@anilinux.org> Date: Sat, 8 Mar 2025 23:34:19 +0100 Subject: [PATCH] Add space in front of files --- lazy_player/__init__.py | 84 ++++++++++++++++++++++++--------------- lazy_player/file_model.py | 43 ++++++++++++++++++++ lazy_player/style.css | 8 +++- 3 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 lazy_player/file_model.py diff --git a/lazy_player/__init__.py b/lazy_player/__init__.py index 68645e1..f089dbc 100644 --- a/lazy_player/__init__.py +++ b/lazy_player/__init__.py @@ -7,18 +7,20 @@ from typing import Any, cast import gi +from .file_model import FileItem, FileType + gi.require_version("Gdk", "4.0") gi.require_version("Gtk", "4.0") gi.require_version("Gst", "1.0") gi.require_version("Pango", "1.0") -from gi.repository import Gdk, Gst, Gtk, Pango # NOQA: E402 +from gi.repository import Gdk, Gio, Gst, Gtk, Pango # NOQA: E402 class MainWindow(Gtk.ApplicationWindow): file_info_label: Gtk.Label stack: Gtk.Stack list_view: Gtk.ListView - list_store: Gtk.StringList + list_store: Gio.ListStore video_widget: Gtk.Picture pipeline: Gst.Pipeline overlay_tick_callback_id: int @@ -127,7 +129,7 @@ class MainWindow(Gtk.ApplicationWindow): main_box.append(grid) # Create list store and view - self.list_store = Gtk.StringList() + self.list_store = Gio.ListStore(item_type=FileItem) self.list_view = Gtk.ListView() selection_model = Gtk.SingleSelection.new(self.list_store) selection_model.connect("selection-changed", self._on_selection_changed) @@ -137,21 +139,20 @@ class MainWindow(Gtk.ApplicationWindow): def on_activate(widget: Gtk.ListView, index: int): selected_item = selection_model.get_item(index) if selected_item: - string_obj = cast(Gtk.StringObject, selected_item) - string = string_obj.get_string() + file_item = cast(FileItem, selected_item) - if string.endswith("/"): - os.chdir(string) + if file_item.file_type == FileType.DIRECTORY: + os.chdir(file_item.full_path) self._populate_file_list() return # Start playing the video playbin = self.pipeline.get_by_name("playbin") if playbin: - playbin.set_property("uri", f"file://{os.path.abspath(string)}") + playbin.set_property("uri", f"file://{os.path.abspath(file_item.full_path)}") self.pipeline.set_state(Gst.State.PLAYING) self.stack.set_visible_child_name("overlay") - self.show_overlay_text(string) + self.show_overlay_text(f"Playing: {file_item.name}") self.list_view.connect("activate", on_activate) @@ -172,14 +173,31 @@ class MainWindow(Gtk.ApplicationWindow): self.set_child(self.stack) def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem): + # Create horizontal box to hold icon and label + box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + box.set_spacing(8) + + # Create icon placeholder + icon = Gtk.Box() + icon.set_css_classes(["file-icon"]) + box.append(icon) + + # Create label label = Gtk.Label() label.set_halign(Gtk.Align.START) - list_item.set_child(label) + box.append(label) + + list_item.set_child(box) def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem): - label = cast(Gtk.Label, list_item.get_child()) - item = cast(Gtk.StringObject, list_item.get_item()) - label.set_text(item.get_string()) + box = cast(Gtk.Box, list_item.get_child()) + icon = cast(Gtk.Box, box.get_first_child()) + label = cast(Gtk.Label, box.get_last_child()) + item = cast(FileItem, list_item.get_item()) + + # Make icon transparent for directories + icon.set_opacity(0.0 if item.file_type == FileType.DIRECTORY else 1.0) + label.set_text(item.name) def _on_selection_changed( self, @@ -192,8 +210,8 @@ class MainWindow(Gtk.ApplicationWindow): else: selected_item = selection_model.get_selected_item() if selected_item: - string_obj = cast(Gtk.StringObject, selected_item) - self.file_info_label.set_text(string_obj.get_string()) + file_item = cast(FileItem, selected_item) + self.file_info_label.set_text(file_item.full_path) def _toggle_play_pause(self) -> None: """Toggle between play and pause states""" @@ -343,31 +361,35 @@ class MainWindow(Gtk.ApplicationWindow): def _populate_file_list(self) -> None: # TODO: Implement proper version sort (strverscmp equivalent) - directories: list[str] = ["../"] - files: list[str] = [] + items: list[FileItem] = [] + + # Add parent directory + items.append(FileItem("..", FileType.DIRECTORY, "../")) with os.scandir(".") as it: for entry in it: - if entry.name == ".." or not entry.name.startswith("."): - parts = entry.name.split(".") - suffix = parts[-1] if len(parts) >= 2 else "" + if entry.name != ".." and 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) + except ValueError: + # Skip unsupported file types + continue - if entry.is_dir(): - directories.append(entry.name + "/") - elif suffix in ("mkv", "mp4", "avi"): - files.append(entry.name) - - directories.sort(key=lambda x: x.lower()) - files.sort(key=lambda x: x.lower()) + # Sort directories first, then files, both alphabetically + items.sort(key=lambda x: (x.file_type != FileType.DIRECTORY, x.name.lower())) while self.list_store.get_n_items(): self.list_store.remove(0) - for name in directories + files: - self.list_store.append(name) + for item in items: + self.list_store.append(item) - all = directories + files - self.file_info_label.set_text(all[0] if all else "") + if items: + self.file_info_label.set_text(items[0].full_path) class App(Gtk.Application): diff --git a/lazy_player/file_model.py b/lazy_player/file_model.py new file mode 100644 index 0000000..f7f6b6b --- /dev/null +++ b/lazy_player/file_model.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import os +from enum import Enum, auto + +import gi + +gi.require_version("GObject", "2.0") +from gi.repository import GObject # NOQA: E402 + + +class FileType(Enum): + DIRECTORY = auto() + VIDEO = auto() + + +class FileItem(GObject.Object): + __gtype_name__ = "FileItem" + + def __init__( + self, + name: str, + file_type: FileType, + full_path: str, + ): + super().__init__() + self.name = name + self.file_type = file_type + 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) + + parts = name.split(".") + suffix = parts[-1].lower() if len(parts) >= 2 else "" + + if suffix in ("mkv", "mp4", "avi"): + return FileItem(name, FileType.VIDEO, path) + + raise ValueError(f"Unsupported file type: {path}") diff --git a/lazy_player/style.css b/lazy_player/style.css index 51e6b3d..db079dd 100644 --- a/lazy_player/style.css +++ b/lazy_player/style.css @@ -4,6 +4,13 @@ listview > row { font-family: monospace; } +.file-icon { + min-width: 32px; + min-height: 32px; + margin-right: 8px; + border: 1px solid #666; +} + #black-overlay { background-color: black; } @@ -16,5 +23,4 @@ listview > row { padding: 24px; border-radius: 8px; margin: 32px; - width: 80%; }