probonopd / Filer

A clean rewrite of the Filer file manager for helloSystem, inspired by John Siracusa's descriptions of "Spatial Orientation"
BSD 2-Clause "Simplified" License
13 stars 1 forks source link

Miller Columns #23

Open probonopd opened 4 months ago

probonopd commented 4 months ago

POC:

#!/usr/bin/env python3

import sys
import os
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QHBoxLayout, QListView, 
    QWidget, QAbstractItemView, QMenuBar, QMenu, QToolBar, QMessageBox
)
from PyQt6.QtCore import QModelIndex, QSettings, QByteArray, Qt
from PyQt6.QtGui import QFileSystemModel, QIcon, QAction

class MillerColumns(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Miller Columns File Manager")
        self.resize(800, 600)  # Default size

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QHBoxLayout(self.central_widget)

        self.columns = []
        self.file_model = QFileSystemModel()
        self.file_model.setRootPath('')
        self.file_model.setOption(QFileSystemModel.Option.DontUseCustomDirectoryIcons, False)  # Enable color icons

        home_dir = os.path.expanduser('~')
        self.add_column(self.file_model.index(home_dir))

        self.create_menus()
        self.create_toolbar()
        self.read_settings()

    def create_menus(self):
        # Create a menubar
        menubar = self.menuBar()

        # File menu
        file_menu = menubar.addMenu("File")
        close_action = QAction("Close", self)
        close_action.triggered.connect(self.close)
        quit_action = QAction("Quit", self)
        quit_action.triggered.connect(QApplication.instance().quit)
        file_menu.addAction(close_action)
        file_menu.addSeparator()
        file_menu.addAction(quit_action)

        # Edit menu
        edit_menu = menubar.addMenu("Edit")
        undo_action = QAction("Undo", self)
        cut_action = QAction("Cut", self)
        copy_action = QAction("Copy", self)
        paste_action = QAction("Paste", self)
        delete_action = QAction("Delete", self)
        empty_trash_action = QAction("Empty Trash", self)

        edit_menu.addAction(undo_action)
        edit_menu.addSeparator()
        edit_menu.addAction(cut_action)
        edit_menu.addAction(copy_action)
        edit_menu.addAction(paste_action)
        edit_menu.addSeparator()
        edit_menu.addAction(delete_action)
        edit_menu.addSeparator()
        edit_menu.addAction(empty_trash_action)

        # Help menu
        help_menu = menubar.addMenu("Help")
        about_action = QAction("About", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_toolbar(self):
        # Create a toolbar
        toolbar = QToolBar("Navigation")
        self.addToolBar(Qt.ToolBarArea.TopToolBarArea, toolbar)

        up_action = QAction(QIcon.fromTheme("go-up"), "Up", self)
        up_action.triggered.connect(self.go_up)
        toolbar.addAction(up_action)

    def show_about(self):
        QMessageBox.about(self, "About", "Miller Columns File Manager\nVersion 1.0")

    def go_up(self):
        if self.columns:
            # Always target the first column
            first_view = self.columns[0]
            current_index = first_view.rootIndex()
            if current_index.isValid():
                parent_index = current_index.parent()
                if parent_index.isValid():
                    # Clear all columns to the right of the first column
                    for column_view in self.columns[1:]:
                        self.layout.removeWidget(column_view)
                        column_view.deleteLater()
                    self.columns = self.columns[:1]  # Keep only the first column

                    # Update the root index of the first column
                    first_view.setRootIndex(parent_index)

    def add_column(self, parent_index=None):
        column_view = QListView()
        column_view.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
        column_view.setUniformItemSizes(True)
        column_view.setAlternatingRowColors(True)  # Enable alternating row colors
        column_view.setModel(self.file_model)

        if parent_index:
            column_view.setRootIndex(parent_index)

        self.layout.addWidget(column_view)
        self.columns.append(column_view)

        column_view.selectionModel().currentChanged.connect(self.on_selection_changed)
        column_view.doubleClicked.connect(self.on_double_clicked)

    def on_selection_changed(self, current: QModelIndex, previous: QModelIndex):
        column_index = self.get_column_index(current)
        if column_index is not None:
            # Remove all columns to the right of the current column
            while len(self.columns) > column_index + 1:
                column_to_remove = self.columns.pop()
                self.layout.removeWidget(column_to_remove)
                column_to_remove.deleteLater()

            # Add a new column if the selected item is a directory
            if self.file_model.isDir(current):
                self.add_column(current)

    def on_double_clicked(self, index: QModelIndex):
        if not self.file_model.isDir(index):
            file_path = self.file_model.filePath(index)
            os.startfile(file_path)

    def get_column_index(self, index: QModelIndex):
        for i, column in enumerate(self.columns):
            if column.selectionModel() == self.sender():
                return i
        return None

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)

    def read_settings(self):
        settings = QSettings("MyCompany", "MillerColumnsFileManager")
        geometry = settings.value("geometry", QByteArray())
        if geometry:
            self.restoreGeometry(geometry)

    def write_settings(self):
        settings = QSettings("MyCompany", "MillerColumnsFileManager")
        settings.setValue("geometry", self.saveGeometry())

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MillerColumns()
    window.show()
    sys.exit(app.exec())
probonopd commented 3 months ago

This turned into https://github.com/probonopd/Miller, which currently already can run halfway decently on Windows 11...