summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kodereviewer/app.py6
-rw-r--r--kodereviewer/models/__init__.py3
-rw-r--r--kodereviewer/models/file.py180
-rw-r--r--kodereviewer/qml/KRContextDrawer.qml36
4 files changed, 221 insertions, 4 deletions
diff --git a/kodereviewer/app.py b/kodereviewer/app.py
index 204b73e..f7c5b08 100644
--- a/kodereviewer/app.py
+++ b/kodereviewer/app.py
@@ -12,7 +12,10 @@ from PySide6.QtQuickControls2 import QQuickStyle
from kodereviewer.models.file import FileModel
from kodereviewer.network_manager import NetworkManager
-from kodereviewer.models import CommentModel, LabelModel, LineModel, PullRequestModel, ProjectModel
+from kodereviewer.models import (
+ CommentModel, TreeFileModel, LabelModel, LineModel, PullRequestModel,
+ ProjectModel
+)
import logging
from rich.logging import RichHandler
@@ -46,6 +49,7 @@ def main():
qmlRegisterType(CommentModel, "org.deprecated.kodereviewer", 1, 0, "CommentModel")
qmlRegisterType(FileModel, "org.deprecated.kodereviewer", 1, 0, "FileModel")
+ qmlRegisterType(TreeFileModel, "org.deprecated.kodereviewer", 1, 0, "TreeFileModel")
qmlRegisterType(LabelModel, "org.deprecated.kodereviewer", 1, 0, "LabelModel")
qmlRegisterType(LineModel, "org.deprecated.kodereviewer", 1, 0, "LineModel")
qmlRegisterType(PullRequestModel, "org.deprecated.kodereviewer", 1, 0, "PullRequestModel")
diff --git a/kodereviewer/models/__init__.py b/kodereviewer/models/__init__.py
index b9082d1..ad675bf 100644
--- a/kodereviewer/models/__init__.py
+++ b/kodereviewer/models/__init__.py
@@ -1,5 +1,5 @@
from kodereviewer.models.comments import CommentModel
-from kodereviewer.models.file import FileModel
+from kodereviewer.models.file import FileModel, TreeFileModel
from kodereviewer.models.line_model import LineModel
from kodereviewer.models.project import ProjectModel
from kodereviewer.models.pull_request import PullRequestModel
@@ -8,6 +8,7 @@ from kodereviewer.models.label import LabelModel
__all__ = [
'CommentModel',
'FileModel',
+ 'TreeFileModel',
'LabelModel'
'LineModel',
'ProjectModel',
diff --git a/kodereviewer/models/file.py b/kodereviewer/models/file.py
index 246bd5b..4f0d139 100644
--- a/kodereviewer/models/file.py
+++ b/kodereviewer/models/file.py
@@ -1,8 +1,14 @@
+import logging
from copy import copy
+from dataclasses import dataclass, field
from enum import IntEnum, auto
-from typing import Any, Optional
+from pathlib import Path
+from typing import Any, Optional, Self
-from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, QObject, QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property
+from PySide6.QtCore import (
+ QAbstractListModel, QAbstractItemModel, QByteArray, QModelIndex, QObject,
+ QPersistentModelIndex, QStandardPaths, QUrl, Qt, Signal, Slot, Property
+)
from PySide6.QtQml import QmlElement
from kodereviewer.project import Project
@@ -11,6 +17,176 @@ from kodereviewer.data import ChangedFile, Label, PullRequest
QML_IMPORT_NAME = "org.deprecated.kodereviewer"
QML_IMPORT_MAJOR_VERSION = 1
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class FileItem:
+ filename: str
+ parent: Self | None
+ children: list[Self] = field(default_factory=list)
+
+ def append(self, child: Self):
+ self.children.append(child)
+
+ def child(self, row: int) -> Self | None:
+ if row >= 0 and row < len(self.children):
+ return self.children[row]
+ return None
+
+ def row(self) -> int:
+ """Reports the item's location within it's parent."""
+ if self.parent is None:
+ return 0
+ for i, child in enumerate(self.parent.children):
+ if self is child:
+ return i
+
+ raise Exception('Should not happen!')
+
+ def column_count(self) -> int:
+ """Only the filename."""
+ return 1
+
+ def __str__(self) -> str:
+ return f'{self.filename} | {id(self.parent)} | {[x.filename for x in self.children]}'
+
+
+@QmlElement
+class TreeFileModel(QAbstractItemModel):
+
+ filenames: list[str] = field(default_factory=list)
+ dir_mapping: dict[str, FileItem] = field(default_factory=dict)
+ root_node: FileItem
+ _pull_request: Optional[PullRequest]
+
+ def __init__(self):
+ super().__init__()
+ self.root_node = FileItem('./', None)
+ self._pull_request = None
+
+ def load_files(self, filenames: list[str]):
+ logger.info(f'Loading {filenames}')
+ self.filenames = filenames
+ self.dir_mapping: dict[str, FileItem] = {}
+ root_node = FileItem('./', None)
+ for file in self.filenames:
+ p = Path(file)
+ directories = p.parts[:-1]
+ fname = p.name
+ current_path = Path('')
+ parent: FileItem = root_node
+ for dir in directories:
+ current_path = current_path / dir
+ if str(current_path) not in self.dir_mapping:
+ logger.info(f'Creating {current_path}')
+ self.dir_mapping[str(current_path)] = FileItem(
+ str(current_path), parent
+ )
+ logger.info(f'Appending to {parent}')
+ parent.append(self.dir_mapping[str(current_path)])
+
+ parent = self.dir_mapping[str(current_path)]
+ file_item = FileItem(fname, parent)
+ parent.append(file_item)
+ self.root_node = root_node
+ logger.info(self.root_node)
+
+ pullRequestChanged = Signal()
+
+ def get_pull_request(self) -> Optional[PullRequest]:
+ return self._pull_request
+
+ def set_pull_request(self, pull_request: Optional[PullRequest]) -> None:
+ if pull_request is None:
+ return
+
+ if self._pull_request is not None and self._pull_request == pull_request:
+ return
+
+ self.beginResetModel()
+ self._pull_request = pull_request
+ self._pull_request.filesLoaded.connect(self._reset_model)
+ self.endResetModel()
+ self.pullRequestChanged.emit()
+
+ pullRequest = Property(PullRequest, fget=get_pull_request, fset=set_pull_request,
+ notify=pullRequestChanged)
+
+ def _reset_model(self) -> None:
+ logger.info('reseting model')
+ self.beginResetModel()
+ filenames = [f.filename for f in self._pull_request.files]
+ self.load_files(filenames)
+ self.endResetModel()
+
+ def index(self, row: int, column: int,
+ index: QModelIndex = QModelIndex()) -> QModelIndex:
+ """Returns the index of the item in the model specified by the given row, column and parent index."""
+ if not self.hasIndex(row, column, index):
+ return QModelIndex()
+ item: FileItem
+ if not index.isValid():
+ item = self.root_node
+ else:
+ item = index.internalPointer()
+
+ if item.child(row):
+ return self.createIndex(row, column, item.child(row))
+ return QModelIndex()
+
+ def parent(self, index: QModelIndex) -> QModelIndex:
+ """Returns the parent of the model item with the given index
+
+ If the item has no parent, an invalid QModelIndex is returned.
+ """
+ if not index.isValid():
+ return QModelIndex()
+
+ item: FileItem = index.internalPointer()
+ parent: FileItem = item.parent
+ if parent == self.root_node:
+ return QModelIndex()
+ return self.createIndex(parent.row(), 0, parent)
+
+ def rowCount(self, index: QModelIndex = QModelIndex()) -> int:
+ """Returns the number of rows under the given parent
+
+ When the parent is valid it means that rowCount is returning
+ the number of children of parent. """
+
+ parent: FileItem
+ if index.isValid():
+ parent = index.internalPointer()
+ else:
+ parent = self.root_node
+
+ return len(parent.children)
+
+ def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
+ return 1
+
+ FilenameRole = Qt.ItemDataRole.UserRole + 1
+
+ def data(self, index: QModelIndex, role: int) -> Any:
+ if not index.isValid():
+ return ''
+ filename = index.internalPointer().filename
+ name = Path(filename).name
+ logger.info(index)
+ logger.info(f'Role: {role} | {self.FilenameRole}')
+ if role == Qt.ItemDataRole.DisplayRole:
+ return name
+ if role == self.FilenameRole:
+ logger.info(name)
+ return name
+ return None
+
+ def roleNames(self) -> dict[int, QByteArray]:
+ return {
+ self.FilenameRole: QByteArray(b'filename'),
+ }
+
@QmlElement
class FileModel(QAbstractListModel):
diff --git a/kodereviewer/qml/KRContextDrawer.qml b/kodereviewer/qml/KRContextDrawer.qml
new file mode 100644
index 0000000..c3923fa
--- /dev/null
+++ b/kodereviewer/qml/KRContextDrawer.qml
@@ -0,0 +1,36 @@
+import QtQuick
+import QtQuick.Controls as QQC2
+import org.kde.kirigami as Kirigami
+import org.kde.kirigamiaddons.delegates as Delegates
+import org.kde.kitemmodels
+
+import org.deprecated.kodereviewer 1.0
+
+
+Kirigami.ContextDrawer {
+ id: contextDrawer
+ // modal: false
+ handleVisible: false
+ property alias model: descendantsModel.model
+ contentItem: QQC2.ScrollView {
+ implicitWidth: Kirigami.Units.gridUnit * 20
+ ListView {
+ anchors.fill: parent
+ clip: true
+ id: menu
+ model: KDescendantsProxyModel {
+ id: descendantsModel
+ }
+
+ delegate: Delegates.RoundedTreeDelegate {
+ required property string filename
+ text: filename
+
+ highlighted: menu.currentItem ? menu.currentItem.filename == filename : false
+ onClicked: {
+ menu.currentIndex = index
+ }
+ }
+ }
+ }
+}