Skip to content

Commit

Permalink
Use columns for file name, directory, and state when files are shown …
Browse files Browse the repository at this point in the history
…as a list in TreeViews. Resolves Dense layout issue Murmele#547
  • Loading branch information
gh-devnull committed Aug 31, 2023
1 parent 820c0fd commit f28d290
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 66 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build
.cache
.DS_Store
.project
.vscode/
Expand All @@ -8,3 +9,5 @@ cmake-build-release/
build
.idea/
.venv
compile_commands.json

51 changes: 45 additions & 6 deletions src/ui/DiffTreeModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,30 @@
#include "git/Patch.h"
#include <QStringBuilder>
#include <QUrl>
#include <qobjectdefs.h>

namespace {

const QString kLinkFmt = "<a href='%1'>%2</a>";

const std::array<QString, 3> kModelHeaders = {QObject::tr("File Name"),
QObject::tr("Relative Path"),
QObject::tr("State")};

bool asList() {
return Settings::instance()
->value(Setting::Id::ShowChangedFilesAsList, false)
.toBool();
}

} // namespace

DiffTreeModel::DiffTreeModel(const git::Repository &repo, QObject *parent)
: QAbstractItemModel(parent), mRepo(repo) {}
: QStandardItemModel(0, kModelHeaders.size(), parent), mRepo(repo) {
for (int i = 0; i < kModelHeaders.size(); ++i) {
setHeaderData(i, Qt::Horizontal, kModelHeaders[i]);
}
}

DiffTreeModel::~DiffTreeModel() { delete mRoot; }

Expand Down Expand Up @@ -91,7 +106,9 @@ int DiffTreeModel::rowCount(const QModelIndex &parent) const {
return mDiff ? node(parent)->children().size() : 0;
}

int DiffTreeModel::columnCount(const QModelIndex &parent) const { return 1; }
int DiffTreeModel::columnCount(const QModelIndex &parent) const {
return asList() ? QStandardItemModel::columnCount(parent) : 1;
}

bool DiffTreeModel::hasChildren(const QModelIndex &parent) const {
return mRoot && node(parent)->hasChildren();
Expand Down Expand Up @@ -134,7 +151,7 @@ void DiffTreeModel::modelIndices(const QModelIndex &parent,
}

for (int i = 0; i < n->children().length(); i++) {
auto child = createIndex(i, 0, n->children()[i]);
auto child = createIndex(i, parent.column(), n->children()[i]);
if (recursive)
modelIndices(child, list);
else if (!node(child)->hasChildren())
Expand Down Expand Up @@ -195,9 +212,15 @@ QVariant DiffTreeModel::data(const QModelIndex &index, int role) const {
return QVariant();

Node *node = this->node(index);

// Skip intermediate path elements for trees showing file lists only.
if (node->hasChildren() && asList())
return QVariant();

switch (role) {
case Qt::DisplayRole:
return node->name();
case Qt::DisplayRole: {
return getDisplayRole(index);
}

// case Qt::DecorationRole: {
// QFileInfo info(node->path());
Expand All @@ -212,7 +235,7 @@ QVariant DiffTreeModel::data(const QModelIndex &index, int role) const {
return node->path();

case Qt::CheckStateRole: {
if (!mDiff.isValid() || !mDiff.isStatusDiff())
if (!mDiff.isValid() || !mDiff.isStatusDiff() || index.column() > 0)
return QVariant();

git::Index index = mDiff.index();
Expand Down Expand Up @@ -380,6 +403,22 @@ Node *DiffTreeModel::node(const QModelIndex &index) const {
return index.isValid() ? static_cast<Node *>(index.internalPointer()) : mRoot;
}

QVariant DiffTreeModel::getDisplayRole(const QModelIndex &index) const {
Node *node = this->node(index);
if (asList()) {
QFileInfo fileInfo(node->path(true));
switch (index.column()) {
case 0:
return fileInfo.fileName();
case 1:
return fileInfo.path();
default:
return "";
}
}
return node->name();
}

//#############################################################################
//###### DiffTreeModel::Node ##############################################
//#############################################################################
Expand Down
6 changes: 4 additions & 2 deletions src/ui/DiffTreeModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
#include "git/Index.h"
#include "git/Tree.h"
#include "git/Repository.h"
#include <QAbstractItemModel>
#include <QStandardItemModel>
#include <QAbstractListModel>
#include <QFileIconProvider>
#include "git/Index.h"

Expand Down Expand Up @@ -80,7 +81,7 @@ class Node : public QObject // item of the model
* This Treemodel is similar to the normal tree model, but handles only the
* files in the diff it self and not the complete tree
*/
class DiffTreeModel : public QAbstractItemModel {
class DiffTreeModel : public QStandardItemModel {
Q_OBJECT

public:
Expand Down Expand Up @@ -157,6 +158,7 @@ class DiffTreeModel : public QAbstractItemModel {

private:
Node *node(const QModelIndex &index) const;
QVariant getDisplayRole(const QModelIndex &index) const;

QFileIconProvider mIconProvider;

Expand Down
19 changes: 4 additions & 15 deletions src/ui/DoubleTreeWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "StatePushButton.h"
#include "TreeProxy.h"
#include "TreeView.h"
#include "ViewDelegate.h"
#include "Debug.h"
#include "conf/Settings.h"
#include "DiffView/DiffView.h"
Expand Down Expand Up @@ -98,6 +97,7 @@ DoubleTreeWidget::DoubleTreeWidget(const git::Repository &repo, QWidget *parent)
listView->setChecked(Settings::instance()
->value(Setting::Id::ShowChangedFilesAsList, false)
.toBool());
RepoView::parentView(this)->refresh();
connect(listView, &QAction::triggered, this, [this](bool checked) {
Settings::instance()->setValue(Setting::Id::ShowChangedFilesAsList,
checked);
Expand Down Expand Up @@ -160,13 +160,8 @@ DoubleTreeWidget::DoubleTreeWidget(const git::Repository &repo, QWidget *parent)
repoView->updateSubmodules(submodules, recursive, init,
force_checkout);
});
TreeProxy *treewrapperStaged = new TreeProxy(true, this);
treewrapperStaged->setSourceModel(mDiffTreeModel);
stagedFiles->setModel(treewrapperStaged);
stagedFiles->setHeaderHidden(true);
ViewDelegate *stagedDelegate = new ViewDelegate();
stagedDelegate->setDrawArrow(false);
stagedFiles->setItemDelegateForColumn(0, stagedDelegate);

stagedFiles->setModel(new TreeProxy(true, mDiffTreeModel, this));

QHBoxLayout *hBoxLayout = new QHBoxLayout();
QLabel *label = new QLabel(kStagedFiles);
Expand All @@ -192,13 +187,7 @@ DoubleTreeWidget::DoubleTreeWidget(const git::Repository &repo, QWidget *parent)
showFileContextMenu(pos, repoView, unstagedFiles, false);
});

TreeProxy *treewrapperUnstaged = new TreeProxy(false, this);
treewrapperUnstaged->setSourceModel(mDiffTreeModel);
unstagedFiles->setModel(treewrapperUnstaged);
unstagedFiles->setHeaderHidden(true);
ViewDelegate *unstagedDelegate = new ViewDelegate();
unstagedDelegate->setDrawArrow(false);
unstagedFiles->setItemDelegateForColumn(0, unstagedDelegate);
unstagedFiles->setModel(new TreeProxy(false, mDiffTreeModel, this));

hBoxLayout = new QHBoxLayout();
mUnstagedCommitedFiles = new QLabel(kUnstagedFiles);
Expand Down
6 changes: 4 additions & 2 deletions src/ui/TreeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ const QString kLinkFmt = "<a href='%1'>%2</a>";

} // namespace

TreeProxy::TreeProxy(bool staged, QObject *parent)
: QSortFilterProxyModel(parent), mStaged(staged) {}
TreeProxy::TreeProxy(bool staged, QAbstractItemModel *model, QObject *parent)
: mStaged(staged), QSortFilterProxyModel(parent) {
setSourceModel(model);
}

TreeProxy::~TreeProxy() {}

Expand Down
7 changes: 6 additions & 1 deletion src/ui/TreeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,25 @@
#include <QSortFilterProxyModel>
#include <QFileIconProvider>

class QAbstractItemModel;
class TreeModel;

class TreeProxy : public QSortFilterProxyModel {
Q_OBJECT

public:
TreeProxy(bool staged, QObject *parent = nullptr);
TreeProxy(bool staged, QAbstractItemModel *model, QObject *parent);
virtual ~TreeProxy();
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole, bool ignoreIndexChanges = false);
bool staged() { return mStaged; }

void enableFilter(bool enable) { mFilter = enable; }

int columnCount(const QModelIndex &parent = QModelIndex()) const override {
return sourceModel()->columnCount();
}

private:
using QSortFilterProxyModel::setData;
bool filterAcceptsRow(int source_row,
Expand Down
26 changes: 25 additions & 1 deletion src/ui/TreeView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "RepoView.h"
#include <QMessageBox>
#include <QPushButton>
#include "conf/Settings.h"
#include <QAbstractItemModel>
#include <memory>

#ifdef Q_OS_WIN
#define ICON_SIZE 48
Expand All @@ -41,8 +44,29 @@ const QString kLabelFmt = "<p style='color: gray; font-weight: bold'>%1</p>";
} // namespace

TreeView::TreeView(QWidget *parent, const QString &name)
: QTreeView(parent), mSharedDelegate(new ViewDelegate(this)), mName(name) {
: QTreeView(parent), mDelegateCol(0),
mFileListDelegatePtr(std::make_unique<ViewDelegate>(this, true)),
mFileTreeDelegatePtr(std::make_unique<ViewDelegate>(this)), mName(name) {
setObjectName(name);
connect(RepoView::parentView(this)->repo().notifier(),
&git::RepositoryNotifier::referenceUpdated, this,
&TreeView::updateView);
}

void TreeView::updateView() {
QAbstractItemModel *itemModel = model();
if (!itemModel)
return;

// Remove any previous delegate on the current column, get the new current
// column, and set the delegate on that.
setItemDelegateForColumn(mDelegateCol, nullptr);
mDelegateCol = itemModel->columnCount() - 1;
setItemDelegateForColumn(mDelegateCol, mDelegateCol
? mFileListDelegatePtr.get()
: mFileTreeDelegatePtr.get());

setHeaderHidden(mDelegateCol ? false : true);
}

void TreeView::setModel(QAbstractItemModel *model) {
Expand Down
7 changes: 6 additions & 1 deletion src/ui/TreeView.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#define TREEVIEW_H

#include <QTreeView>
#include <memory>
#include "ViewDelegate.h"

class QItemDelegate;
class DiffTreeModel;
Expand Down Expand Up @@ -96,9 +98,12 @@ public slots:
bool suppressDeselectionHandling{false};
int mCollapseCount; // Counts the number of collapsed folders.
bool mSupressItemExpandStateChanged{false};
void updateView();

QItemDelegate *mSharedDelegate;
QString mName;
std::unique_ptr<ViewDelegate> mFileListDelegatePtr;
std::unique_ptr<ViewDelegate> mFileTreeDelegatePtr;
int mDelegateCol = 0;
};

#endif // TREEVIEW_H
48 changes: 14 additions & 34 deletions src/ui/ViewDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,6 @@ void ViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
QStyleOptionViewItem opt = option;
drawBackground(painter, opt, index);

// Draw >.
if (mDrawArrow && index.model()->hasChildren(index)) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);

QColor color = opt.palette.color(QPalette::Active, QPalette::BrightText);
if (opt.state & QStyle::State_Selected)
color =
!opt.showDecorationSelected
? opt.palette.color(QPalette::Active, QPalette::WindowText)
: opt.palette.color(QPalette::Active, QPalette::HighlightedText);

painter->setPen(color);
painter->setBrush(color);

int x = opt.rect.x() + opt.rect.width() - 3;
int y = opt.rect.y() + (opt.rect.height() / 2);

QPainterPath path;
path.moveTo(x, y);
path.lineTo(x - 5, y - 3);
path.lineTo(x - 5, y + 3);
path.closeSubpath();
painter->drawPath(path);

painter->restore();

// Adjust rect to exclude the arrow.
opt.rect.adjust(0, 0, -11, 0);
}

// Draw badges.
QString status = index.data(TreeModel::StatusRole).toString();
if (!status.isEmpty()) {
Expand All @@ -49,19 +18,30 @@ void ViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
int width = size.width();
int height = size.height();

auto startIter = status.cbegin(), endIter = status.cend();
int leftAdjust = 0, rightAdjust = -3, leftWidth = 0, rightWidth = -width;
if (mMultiColumn) {
leftAdjust = 3;
rightAdjust = 0;
leftWidth = width;
rightWidth = 0;
std::reverse(status.begin(), status.end());
}

// Add extra space.
opt.rect.adjust(0, 0, -3, 0);
opt.rect.adjust(leftAdjust, 0, rightAdjust, 0);

for (int i = 0; i < status.count(); ++i) {
int x = opt.rect.x() + opt.rect.width();
int y = opt.rect.y() + (opt.rect.height() / 2);
QRect rect(x - width, y - (height / 2), width, height);
QRect rect(mMultiColumn ? opt.rect.x() : x - width, y - (height / 2),
width, height);
Badge::paint(painter,
{Badge::Label(Badge::Label::Type::Status, status.at(i))},
rect, &opt);

// Adjust rect.
opt.rect.adjust(0, 0, -width - 3, 0);
opt.rect.adjust(leftWidth + leftAdjust, 0, rightWidth + rightAdjust, 0);
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/ui/ViewDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
*/
class ViewDelegate : public QItemDelegate {
public:
ViewDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {}

void setDrawArrow(bool enable) { mDrawArrow = enable; }
ViewDelegate(QObject *parent, bool multiColumn = false)
: QItemDelegate(parent), mMultiColumn(multiColumn) {}

void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
Expand All @@ -20,7 +19,7 @@ class ViewDelegate : public QItemDelegate {
const QModelIndex &index) const override;

private:
bool mDrawArrow = true;
bool mMultiColumn = false;
};

#endif // VIEWDELEGATE_H
Loading

0 comments on commit f28d290

Please sign in to comment.