From bbe83e74b2c1335a0a726c2f993bb2d8c47ffedc Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Mon, 14 Oct 2024 09:54:47 -0300 Subject: Functioning context drawer --- kodereviewer/models/file.py | 75 ++++++---- kodereviewer/qml/FilesDrawer.qml | 70 +++++++++ kodereviewer/qml/KRContextDrawer.qml | 36 ----- kodereviewer/qml/Main.qml | 27 +++- kodereviewer/qml/PullRequestDescription.qml | 225 +++++++++++++++------------- kodereviewer/qml/PullRequestFilesDrawer.qml | 57 +++++++ kodereviewer/qml/PullRequestPage.qml | 74 +++++---- 7 files changed, 364 insertions(+), 200 deletions(-) create mode 100644 kodereviewer/qml/FilesDrawer.qml delete mode 100644 kodereviewer/qml/KRContextDrawer.qml create mode 100644 kodereviewer/qml/PullRequestFilesDrawer.qml diff --git a/kodereviewer/models/file.py b/kodereviewer/models/file.py index 4f0d139..43fcbe4 100644 --- a/kodereviewer/models/file.py +++ b/kodereviewer/models/file.py @@ -22,8 +22,11 @@ logger = logging.getLogger(__name__) @dataclass class FileItem: - filename: str + path: str + icon: str + name: str parent: Self | None + patch: str | None = None children: list[Self] = field(default_factory=list) def append(self, child: Self): @@ -48,8 +51,11 @@ class FileItem: """Only the filename.""" return 1 + def is_file(self) -> bool: + return self.patch is not None + def __str__(self) -> str: - return f'{self.filename} | {id(self.parent)} | {[x.filename for x in self.children]}' + return f'{self.name} | {id(self.parent)} | {[x.name for x in self.children]}' @QmlElement @@ -60,18 +66,23 @@ class TreeFileModel(QAbstractItemModel): root_node: FileItem _pull_request: Optional[PullRequest] + class Roles(IntEnum): + Filename = Qt.ItemDataRole.UserRole + 1 + Path = auto() + IconName = auto() + Patch = auto() + IsFile = auto() + def __init__(self): super().__init__() - self.root_node = FileItem('./', None) + self.root_node = FileItem('./', '', './', parent=None) self._pull_request = None - def load_files(self, filenames: list[str]): - logger.info(f'Loading {filenames}') - self.filenames = filenames + def load_files(self, files: list[ChangedFile]): self.dir_mapping: dict[str, FileItem] = {} - root_node = FileItem('./', None) - for file in self.filenames: - p = Path(file) + root_node = FileItem('./', '', './', parent=None) + for file in files: + p = Path(file.filename) directories = p.parts[:-1] fname = p.name current_path = Path('') @@ -79,15 +90,13 @@ class TreeFileModel(QAbstractItemModel): 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 + path=str(current_path), name=current_path.name, icon='inode-directory-symbolic', parent=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) + file_item = FileItem(path=str(p), name=fname, icon='document-open-symbolic', patch=file.patch, parent=parent) parent.append(file_item) self.root_node = root_node logger.info(self.root_node) @@ -116,20 +125,19 @@ class TreeFileModel(QAbstractItemModel): 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.load_files(self._pull_request.files) self.endResetModel() def index(self, row: int, column: int, - index: QModelIndex = QModelIndex()) -> QModelIndex: + parent: 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): + if not self.hasIndex(row, column, parent): return QModelIndex() item: FileItem - if not index.isValid(): + if not parent.isValid(): item = self.root_node else: - item = index.internalPointer() + item = parent.internalPointer() if item.child(row): return self.createIndex(row, column, item.child(row)) @@ -166,25 +174,32 @@ class TreeFileModel(QAbstractItemModel): 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}') + item: FileItem = index.internalPointer() + if role == Qt.ItemDataRole.DisplayRole: - return name - if role == self.FilenameRole: - logger.info(name) - return name + return item.name + if role == self.Roles.Filename: + return item.name + if role == self.Roles.Path: + return item.path + if role == self.Roles.IconName: + return item.icon + if role == self.Roles.Patch: + return item.patch + if role == self.Roles.IsFile: + return item.is_file() return None def roleNames(self) -> dict[int, QByteArray]: return { - self.FilenameRole: QByteArray(b'filename'), + self.Roles.Filename: QByteArray(b'filename'), + self.Roles.IconName: QByteArray(b'iconName'), + self.Roles.Path: QByteArray(b'path'), + self.Roles.Patch: QByteArray(b'patch'), + self.Roles.IsFile: QByteArray(b'isFile'), } diff --git a/kodereviewer/qml/FilesDrawer.qml b/kodereviewer/qml/FilesDrawer.qml new file mode 100644 index 0000000..3f98290 --- /dev/null +++ b/kodereviewer/qml/FilesDrawer.qml @@ -0,0 +1,70 @@ +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts +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 + width: Kirigami.Units.gridUnit * 20 + + signal fileSelected(string filename, string text) + + contentItem: ColumnLayout { + id: mainLayout + implicitWidth: Kirigami.Units.gridUnit * 20 + QQC2.ToolBar { + id: toolbar + Layout.fillWidth: true + + Layout.preferredHeight: pageStack.globalToolBar.preferredHeight + + contentItem: RowLayout { + Kirigami.Heading { + Layout.fillWidth: true + text: "Files" + } + } + } + + QQC2.ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + + ListView { + anchors.fill: parent + clip: true + id: menu + model: KDescendantsProxyModel { + id: descendantsModel + } + + delegate: Delegates.RoundedTreeDelegate { + padding: 4 + required property string filename + required property string iconName + required property string path + required property string patch + required property bool isFile + text: filename + icon.name: iconName + + highlighted: menu.currentItem ? menu.currentItem.path == path : false + onClicked: { + menu.currentIndex = index + if (isFile) { + contextDrawer.fileSelected(filename, patch) + } + } + } + } + } + } +} diff --git a/kodereviewer/qml/KRContextDrawer.qml b/kodereviewer/qml/KRContextDrawer.qml deleted file mode 100644 index c3923fa..0000000 --- a/kodereviewer/qml/KRContextDrawer.qml +++ /dev/null @@ -1,36 +0,0 @@ -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 - } - } - } - } -} diff --git a/kodereviewer/qml/Main.qml b/kodereviewer/qml/Main.qml index 56248d7..1c5e951 100644 --- a/kodereviewer/qml/Main.qml +++ b/kodereviewer/qml/Main.qml @@ -46,6 +46,29 @@ Kirigami.ApplicationWindow { } } + + + Loader { + id: treeFileModelLoader + active: !!pullRequest + property var pullRequest: undefined + + sourceComponent: TreeFileModel { + pullRequest: treeFileModelLoader.pullRequest + } + } + + contextDrawer: FilesDrawer { + id: contextDrawer + enabled: treeFileModelLoader.active + model: treeFileModelLoader.item + + onEnabledChanged: { + print("Enabled? " + enabled) + } + } + + Loader { id: projectListPageLoader active: false @@ -55,7 +78,9 @@ Kirigami.ApplicationWindow { project: root.project onPullRequestSelected: number => { - pullRequestPageLoader.item.pullRequest = project.pullRequest(number) + const pullRequest = project.pullRequest(number) + pullRequestPageLoader.item.pullRequest = pullRequest + treeFileModelLoader.pullRequest = pullRequest } } } diff --git a/kodereviewer/qml/PullRequestDescription.qml b/kodereviewer/qml/PullRequestDescription.qml index de5a679..075b210 100644 --- a/kodereviewer/qml/PullRequestDescription.qml +++ b/kodereviewer/qml/PullRequestDescription.qml @@ -1,137 +1,158 @@ -pragma ComponentBehavior: Bound -import QtQuick import QtCore +//pragma ComponentBehavior: Bound +import QtQuick import QtQuick.Controls as QQC2 import QtQuick.Layouts - +import org.deprecated.kodereviewer 1.0 import org.kde.kirigami as Kirigami import org.kde.kirigamiaddons.formcard as FormCard -import org.deprecated.kodereviewer 1.0 - -ScrollView { +Kirigami.FormLayout { id: root - property var pullRequest: undefined - anchors.fill: parent + property var pullRequest + implicitWidth: parent.width - Kirigami.FormLayout { - anchors.fill: parent - implicitWidth: parent.width + Kirigami.Heading { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + leftPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + level: 1 + text: root.pullRequest ? root.pullRequest.title : "" + wrapMode: Text.WordWrap + } - Kirigami.Heading { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - leftPadding: Kirigami.Units.largeSpacing - rightPadding: Kirigami.Units.largeSpacing - level: 1 - text: root.pullRequest ? root.pullRequest.title : "" - wrapMode: Text.WordWrap - } + Kirigami.Separator { + Kirigami.FormData.isSection: true + } - Kirigami.Separator { - Kirigami.FormData.isSection: true - } + RowLayout { + Layout.fillWidth: true - RowLayout { + ColumnLayout { Layout.fillWidth: true - ColumnLayout { - Layout.fillWidth: true - RowLayout { - QQC2.Label { - text: "Author" - elide: Text.ElideRight - } - QQC2.Label { - text: root.pullRequest ? root.pullRequest.username : "" - elide: Text.ElideLeft - } + + RowLayout { + QQC2.Label { + text: "Author" + elide: Text.ElideRight } - RowLayout { - QQC2.Label { - text: "State: " - elide: Text.ElideRight - } - QQC2.Label { - text: root.pullRequest ? root.pullRequest.state : "" - elide: Text.ElideLeft - } + + QQC2.Label { + text: root.pullRequest ? root.pullRequest.username : "" + elide: Text.ElideLeft } - RowLayout { - QQC2.Label { - text: "Draft?: " - elide: Text.ElideRight - } - QQC2.Label { - text: root.pullRequest ? root.pullRequest.draft ? i18n("Yes") : i18n("No") : "" - elide: Text.ElideLeft - } + + } + + RowLayout { + QQC2.Label { + text: "State: " + elide: Text.ElideRight } - RowLayout { - QQC2.Label { - text: "Last commit: " - elide: Text.ElideRight - } - QQC2.Label { - text: root.pullRequest ? root.pullRequest.last_commit : "" - elide: Text.ElideLeft - } + QQC2.Label { + text: root.pullRequest ? root.pullRequest.state : "" + elide: Text.ElideLeft } + } - ColumnLayout { - Layout.fillWidth: false - Layout.fillHeight: true - Repeater { - model: 0 - delegate: QQC2.Label { - text: "#Faa" - } + RowLayout { + QQC2.Label { + text: "Draft?: " + elide: Text.ElideRight } + + QQC2.Label { + text: root.pullRequest ? root.pullRequest.draft ? i18n("Yes") : i18n("No") : "" + elide: Text.ElideLeft + } + } - } - Loader { - id: labelModelLoader - active: !!root.pullRequest - sourceComponent: LabelModel { - pullRequest: root.pullRequest + RowLayout { + QQC2.Label { + text: "Last commit: " + elide: Text.ElideRight + } + + QQC2.Label { + text: root.pullRequest ? root.pullRequest.last_commit : "" + elide: Text.ElideLeft + } + } + } - RowLayout { + ColumnLayout { + Layout.fillWidth: false + Layout.fillHeight: true + Repeater { - model: labelModelLoader.item - delegate: Rectangle { - required property string name - required property string labelColor - required property string textColor - color: labelColor - width: thelabel.implicitWidth - height: thelabel.implicitHeight - radius: 5 - QQC2.Label { - id: thelabel - padding: Kirigami.Units.smallSpacing - text: name - color: textColor - } + model: 0 + + delegate: QQC2.Label { + text: "#Faa" } + } + } - Kirigami.Separator { - Kirigami.FormData.isSection: true - Kirigami.FormData.label: "Description" + } + + Loader { + id: labelModelLoader + + active: !!root.pullRequest + + sourceComponent: LabelModel { + pullRequest: root.pullRequest } - MarkdownLabel { - Layout.fillWidth: true - Layout.fillHeight: false - leftPadding: Kirigami.Units.largeSpacing - rightPadding: Kirigami.Units.largeSpacing - text: root.pullRequest ? - (root.pullRequest.body != "" ? root.pullRequest.body : "*No description provided.*") : "" + } + + RowLayout { + Repeater { + model: labelModelLoader.item + + delegate: Rectangle { + required property string name + required property string labelColor + required property string textColor + + color: labelColor + width: thelabel.implicitWidth + height: thelabel.implicitHeight + radius: 5 + + QQC2.Label { + id: thelabel + + padding: Kirigami.Units.smallSpacing + text: name + color: textColor + } + + } + } + + } + + Kirigami.Separator { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Description" } + + MarkdownLabel { + Layout.fillWidth: true + Layout.fillHeight: false + leftPadding: Kirigami.Units.largeSpacing + rightPadding: Kirigami.Units.largeSpacing + text: root.pullRequest ? (root.pullRequest.body != "" ? root.pullRequest.body : "*No description provided.*") : "" + } + } diff --git a/kodereviewer/qml/PullRequestFilesDrawer.qml b/kodereviewer/qml/PullRequestFilesDrawer.qml new file mode 100644 index 0000000..e54fc7b --- /dev/null +++ b/kodereviewer/qml/PullRequestFilesDrawer.qml @@ -0,0 +1,57 @@ +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami + +import org.kde.kirigamiaddons.delegates as Delegates +import org.deprecated.kodereviewer 1.0 + +Kirigami.OverlayDrawer { + id: root + + required property FileModel fileModel + + width: actualWidth + + readonly property int minWidth: Kirigami.Units.gridUnit * 15 + readonly property int maxWidth: Kirigami.Units.gridUnit * 25 + readonly property int defaultWidth: Kirigami.Units.gridUnit * 20 + property int actualWidth: { + return Kirigami.Units.gridUnit * 20; + } + + onOpened: forceActiveFocus() + + edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge + + topPadding: 0 + bottomPadding: 0 + leftPadding: 0 + rightPadding: 0 + + Kirigami.Theme.colorSet: Kirigami.Theme.View + + contentItem: Loader { + id: loader + active: root.drawerOpen + + sourceComponent: QQC2.ScrollView { + ListView { + id: listView + model: root.fileModel + delegate: Delegates.RoundedItemDelegate { + required property string filename + required property string patch + highlighted: ListView.isCurrentItem + text: filename + + onClicked: { + textArea.text = patch + textArea.file = filename + } + } + } + } + } +} diff --git a/kodereviewer/qml/PullRequestPage.qml b/kodereviewer/qml/PullRequestPage.qml index 860bfcb..cdcc1ec 100644 --- a/kodereviewer/qml/PullRequestPage.qml +++ b/kodereviewer/qml/PullRequestPage.qml @@ -34,7 +34,11 @@ Kirigami.ScrollablePage { icon.name: "file-catalog-symbolic" enabled: !!root.pullRequest onTriggered: { - contextDrawer.open() + if(contextDrawer.opened) { + contextDrawer.close() + } else { + contextDrawer.open() + } } } ] @@ -72,21 +76,6 @@ Kirigami.ScrollablePage { } } - Loader { - id: treeFileModelLoader - active: !!root.pullRequest - sourceComponent: TreeFileModel { - pullRequest: root.pullRequest - } - } - - KRContextDrawer { - id: contextDrawer - enabled: !!root.pullRequest - modal: true - model: treeFileModelLoader.item - } - Kirigami.PlaceholderMessage { visible: !root.pullRequest anchors.centerIn: parent @@ -94,25 +83,48 @@ Kirigami.ScrollablePage { text: "Select a pull request" } - PullRequestDescription { - visible: !!root.pullRequest && root.currentView == "info" - pullRequest: root.pullRequest + ColumnLayout { + id: mainLayout anchors.fill: parent - } + PullRequestDescription { + visible: !!root.pullRequest && root.currentView == "info" + pullRequest: root.pullRequest - Kirigami.CardsListView { - visible: !!root.pullRequest && root.currentView == "comments" - anchors.fill: parent - // Layout.fillWidth: true - // Layout.fillHeight: true - model: commentModelLoader.item - delegate: CommentDelegate {} - footerPositioning: ListView.OverlayFooter + Layout.fillWidth: true + Layout.fillHeight: true + //anchors.fill: parent + } + + Kirigami.CardsListView { + visible: !!root.pullRequest && root.currentView == "comments" + //anchors.fill: parent + Layout.fillWidth: true + Layout.fillHeight: true + model: commentModelLoader.item + delegate: CommentDelegate {} + footerPositioning: ListView.OverlayFooter + } + + Editor { + visible: !!root.pullRequest && root.currentView == "files" + + Layout.fillWidth: true + Layout.fillHeight: true + + id: editor + text: "" + file: "" + //fileModel: fileModelLoader.item + } } - FilesView { - visible: !!root.pullRequest && root.currentView == "files" - fileModel: fileModelLoader.item + Connections { + target: contextDrawer + function onFileSelected(filename, text) { + print("ASDF") + editor.filename = filename + '.diff' + editor.text = text + } } footer: Kirigami.NavigationTabBar { -- cgit v1.2.3-70-g09d2