2025-03-08 19:39:55 +01:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2025-03-08 19:55:49 +01:00
|
|
|
import os
|
2025-03-08 20:02:44 +01:00
|
|
|
from pathlib import Path
|
2025-03-08 19:55:49 +01:00
|
|
|
from typing import Any, cast
|
2025-03-08 19:39:55 +01:00
|
|
|
|
|
|
|
import gi
|
|
|
|
|
|
|
|
gi.require_version("Gtk", "4.0")
|
2025-03-08 20:02:44 +01:00
|
|
|
from gi.repository import Gdk, Gtk # NOQA: E402
|
2025-03-08 19:39:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
class MainWindow(Gtk.ApplicationWindow):
|
2025-03-08 20:42:04 +01:00
|
|
|
file_info_label: Gtk.Label
|
|
|
|
|
2025-03-08 19:39:55 +01:00
|
|
|
def __init__(self, *args: Any, **kwargs: Any):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
# Make window fullscreen and borderless
|
|
|
|
self.set_decorated(False)
|
|
|
|
self.fullscreen()
|
|
|
|
|
|
|
|
# Main horizontal box to split the screen
|
|
|
|
main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
2025-03-08 20:33:46 +01:00
|
|
|
# Create a grid to handle the 1:2 ratio
|
|
|
|
grid = Gtk.Grid()
|
|
|
|
grid.set_column_homogeneous(True)
|
|
|
|
grid.set_hexpand(True)
|
|
|
|
|
|
|
|
# Left third (1/3 width)
|
2025-03-08 19:39:55 +01:00
|
|
|
left_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
2025-03-08 20:18:43 +01:00
|
|
|
left_box.set_valign(Gtk.Align.CENTER)
|
2025-03-08 20:33:46 +01:00
|
|
|
left_box.set_halign(Gtk.Align.FILL)
|
2025-03-08 20:42:04 +01:00
|
|
|
self.file_info_label = Gtk.Label(label="")
|
|
|
|
left_box.append(self.file_info_label)
|
2025-03-08 19:55:49 +01:00
|
|
|
|
2025-03-08 20:33:46 +01:00
|
|
|
# Right two-thirds (2/3 width)
|
2025-03-08 20:18:43 +01:00
|
|
|
right_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
|
|
right_box.set_hexpand(True)
|
|
|
|
|
2025-03-08 20:33:46 +01:00
|
|
|
# Attach boxes to grid with specific column spans
|
|
|
|
grid.attach(left_box, 0, 0, 1, 1)
|
|
|
|
grid.attach(right_box, 1, 0, 2, 1)
|
|
|
|
|
|
|
|
main_box.append(grid)
|
|
|
|
|
2025-03-08 19:55:49 +01:00
|
|
|
# Create list store and view
|
|
|
|
list_store = Gtk.StringList()
|
|
|
|
list_view = Gtk.ListView()
|
2025-03-08 20:42:04 +01:00
|
|
|
selection_model = Gtk.SingleSelection.new(list_store)
|
|
|
|
selection_model.connect("selection-changed", self._on_selection_changed)
|
|
|
|
list_view.set_model(selection_model)
|
2025-03-08 19:55:49 +01:00
|
|
|
list_view.set_vexpand(True)
|
|
|
|
|
2025-03-08 20:49:36 +01:00
|
|
|
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()
|
|
|
|
print("activated", string)
|
|
|
|
|
|
|
|
list_view.connect("activate", on_activate)
|
|
|
|
|
2025-03-08 19:55:49 +01:00
|
|
|
# Factory for list items
|
|
|
|
factory = Gtk.SignalListItemFactory()
|
|
|
|
factory.connect("setup", self._setup_list_item)
|
|
|
|
factory.connect("bind", self._bind_list_item)
|
|
|
|
list_view.set_factory(factory)
|
|
|
|
|
|
|
|
# Populate the list store
|
|
|
|
self._populate_file_list(list_store)
|
|
|
|
|
|
|
|
# Add list view to a scrolled window
|
|
|
|
scrolled = Gtk.ScrolledWindow()
|
|
|
|
scrolled.set_child(list_view)
|
2025-03-08 20:18:43 +01:00
|
|
|
right_box.append(scrolled)
|
2025-03-08 19:39:55 +01:00
|
|
|
|
|
|
|
self.set_child(main_box)
|
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
def _setup_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
2025-03-08 19:55:49 +01:00
|
|
|
label = Gtk.Label()
|
|
|
|
label.set_halign(Gtk.Align.START)
|
|
|
|
list_item.set_child(label)
|
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
def _bind_list_item(self, factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem):
|
2025-03-08 19:55:49 +01:00
|
|
|
label = cast(Gtk.Label, list_item.get_child())
|
|
|
|
item = cast(Gtk.StringObject, list_item.get_item())
|
|
|
|
label.set_text(item.get_string())
|
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
def _on_selection_changed(
|
|
|
|
self,
|
|
|
|
selection_model: Gtk.SingleSelection,
|
|
|
|
position: int,
|
|
|
|
n_items: int,
|
|
|
|
):
|
|
|
|
if selection_model.get_selected() == Gtk.INVALID_LIST_POSITION:
|
|
|
|
self.file_info_label.set_text("")
|
|
|
|
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())
|
|
|
|
|
2025-03-08 19:55:49 +01:00
|
|
|
def _populate_file_list(self, list_store: Gtk.StringList) -> None:
|
|
|
|
# TODO: Implement proper version sort (strverscmp equivalent)
|
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
directories: list[str] = ["../"]
|
2025-03-08 19:55:49 +01:00
|
|
|
files: list[str] = []
|
|
|
|
|
|
|
|
with os.scandir(".") as it:
|
|
|
|
for entry in it:
|
|
|
|
if entry.name == ".." or not entry.name.startswith("."):
|
|
|
|
if entry.is_dir():
|
2025-03-08 20:42:04 +01:00
|
|
|
directories.append(entry.name + "/")
|
2025-03-08 19:55:49 +01:00
|
|
|
else:
|
|
|
|
files.append(entry.name)
|
|
|
|
|
2025-03-08 20:02:44 +01:00
|
|
|
directories.sort(key=lambda x: x.lower())
|
|
|
|
files.sort(key=lambda x: x.lower())
|
2025-03-08 19:55:49 +01:00
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
while list_store.get_n_items():
|
|
|
|
list_store.remove(0)
|
2025-03-08 19:55:49 +01:00
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
for name in directories + files:
|
2025-03-08 19:55:49 +01:00
|
|
|
list_store.append(name)
|
|
|
|
|
2025-03-08 20:42:04 +01:00
|
|
|
all = directories + files
|
|
|
|
self.file_info_label.set_text(all[0] if all else "")
|
|
|
|
|
2025-03-08 19:39:55 +01:00
|
|
|
|
|
|
|
class App(Gtk.Application):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
|
|
|
|
2025-03-08 20:02:44 +01:00
|
|
|
# Load CSS
|
|
|
|
css_provider = Gtk.CssProvider()
|
|
|
|
css_file = Path(__file__).parent / "style.css"
|
|
|
|
css_provider.load_from_path(str(css_file))
|
|
|
|
|
|
|
|
display = Gdk.Display.get_default()
|
|
|
|
if display is None:
|
|
|
|
raise RuntimeError("No display available")
|
|
|
|
|
|
|
|
Gtk.StyleContext.add_provider_for_display(
|
|
|
|
display,
|
|
|
|
css_provider,
|
|
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
|
|
)
|
|
|
|
|
2025-03-08 19:39:55 +01:00
|
|
|
def do_activate(self):
|
|
|
|
win = MainWindow(application=self)
|
|
|
|
win.present()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
app = App()
|
|
|
|
app.run(None)
|