Commit 7cbe577d authored by Lefebvre, Jordan's avatar Lefebvre, Jordan
Browse files

WIP: Starting on navigationmodel to integration navigationwidget and navigationitem together.

parent 8aa41aea
Pipeline #14510 passed with stages
in 7 minutes and 29 seconds
......@@ -45,18 +45,22 @@ ELSE()
SET(HEADERS
${HEADERS}
numberpadwidget.hh
navigationitem.hh
navigationmodel.hh
navigationwidget.hh
)
SET(SOURCES
${SOURCES}
numberpadwidget.cc
navigationitem.cc
navigationmodel.cc
navigationwidget.cc
)
QT5_WRAP_CPP(MOC_FILES
${HEADERS}
)
SET(HEADERS ${HEADERS}
navigationitem.hh
)
#
# Process qt resource files
#QT5_ADD_RESOURCES(RESOURCE_RESULT
......
#include "radixwidgets/navigationitem.hh"
#include "radixbug/bug.hh"
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QMenu>
#include <QSignalMapper>
#include <QList>
namespace radix
{
class NavigationItem::PImpl : public QObject
class NavigationItem::PImpl
{
public:
NavigationItem *parent = nullptr;
QList<NavigationItem *> children;
QList<QVariant> data;
~PImpl();
};
NavigationItem::NavigationItem()
: QObject()
, QStandardItem()
, p(new PImpl(), [](PImpl *impl) { delete impl; })
NavigationItem::PImpl::~PImpl()
{
qDeleteAll(children);
if (parent) delete parent;
}
void NavigationItem::addActions(QMenu &m) const {}
NavigationItem::NavigationItem(QVariant data, NavigationItem *parentItem)
: p(new PImpl(), [](PImpl *impl) { delete impl; })
{
p->parent = parentItem;
p->data.append(data);
}
NavigationItem::NavigationItem(QList<QVariant> data, NavigationItem *parentItem)
: p(new PImpl(), [](PImpl *impl) { delete impl; })
{
p->parent = parentItem;
p->data = data;
}
NavigationItem::NavigationItem(NavigationItem *parentItem)
: p(new PImpl(), [](PImpl *impl) { delete impl; })
{
p->parent = parentItem;
}
void NavigationItem::addChild(NavigationItem *child)
{
p->children.append(child);
}
int NavigationItem::childCount() const { return p->children.size(); }
int NavigationItem::columnCount() const { return p->data.size(); }
QVariant NavigationItem::data(int column) const
{
radix_tagged_line("data(" << column << ") data size (" << p->data.size()
<< ")");
if (column >= p->data.size()) return QVariant();
p->data.value(column);
}
void NavigationItem::setData(int column, QVariant value)
{
int size = p->data.size();
for (int i = size; i <= column; ++i)
{
p->data.append(QVariant());
}
p->data[column] = value;
}
int NavigationItem::row() const
{
if (p->parent)
{
return p->parent->p->children.indexOf(const_cast<NavigationItem *>(this));
}
}
NavigationItem *NavigationItem::parentItem() { return p->parent; }
NavigationItem *NavigationItem::child(int row)
{
return p->children.value(row);
}
} // namespace radix
#ifndef RADIX_RADIXWIDGETS_NAVIGATIONITEM_HH_
#define RADIX_RADIXWIDGETS_NAVIGATIONITEM_HH_
#include <QStandardItem>
#include <QVariant>
#include <memory>
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
QT_END_NAMESPACE
namespace radix
{
class NavigationItem : public QObject, public QStandardItem
class NavigationItem
{
Q_OBJECT
public:
virtual void addActions(QMenu &m) const;
public slots:
virtual void activate()
{ /**empty*/
}
virtual void select()
{ /**empty*/
}
protected:
NavigationItem();
NavigationItem(QVariant data, NavigationItem *parentItem = nullptr);
NavigationItem(QList<QVariant> data, NavigationItem *parentItem = nullptr);
NavigationItem(NavigationItem *parentItem = nullptr);
void addChild(NavigationItem *child);
NavigationItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
void setData(int column, QVariant value);
int row() const;
NavigationItem *parentItem();
private:
class PImpl;
......
#include "radixwidgets/navigationmodel.hh"
#include "radixwidgets/navigationitem.hh"
#include "radixbug/bug.hh"
#include <QList>
namespace radix
{
class NavigationModel::PImpl
{
public:
NavigationItem* root = nullptr;
~PImpl();
};
NavigationModel::PImpl::~PImpl()
{
if (root) delete root;
}
NavigationModel::NavigationModel(QObject* parent)
: QAbstractItemModel(parent)
, p(new PImpl(), [](PImpl* impl) { delete impl; })
{
radix_tagged_line("Navigation Model");
p->root = new NavigationItem();
p->root->addChild(new NavigationItem("Hello", p->root));
NavigationItem* world = new NavigationItem("World", p->root);
p->root->addChild(world);
NavigationItem* child = new NavigationItem("My name is edward", world);
world->addChild(child);
}
QVariant NavigationModel::data(const QModelIndex& index, int role) const
{
radix_tagged_line("data(" << index.row() << ", " << index.column() << ")");
if (!index.isValid()) return QVariant();
if (role != Qt::DisplayRole) return QVariant();
NavigationItem* item = static_cast<NavigationItem*>(index.internalPointer());
return item->data(index.column());
}
bool NavigationModel::setData(const QModelIndex& index, const QVariant& value,
int role)
{
return false;
}
Qt::ItemFlags NavigationModel::flags(const QModelIndex& index) const
{
if (!index.isValid()) return QAbstractItemModel::flags(index);
return QAbstractItemModel::flags(index);
}
QVariant NavigationModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return p->root->data(section);
return QVariant();
}
QModelIndex NavigationModel::index(int row, int column,
const QModelIndex& parent) const
{
radix_tagged_line("index(" << row << "," << column << ")");
if (!hasIndex(row, column, parent)) return QModelIndex();
NavigationItem* parentItem;
if (!parent.isValid())
parentItem = p->root;
else
parentItem = static_cast<NavigationItem*>(parent.internalPointer());
NavigationItem* childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex NavigationModel::parent(const QModelIndex& index) const
{
radix_tagged_line("parent(" << index.row() << "," << index.column() << ")");
if (!index.isValid()) return QModelIndex();
NavigationItem* childItem =
static_cast<NavigationItem*>(index.internalPointer());
NavigationItem* parentItem = childItem->parentItem();
if (parentItem == p->root) return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int NavigationModel::rowCount(const QModelIndex& parent) const
{
radix_tagged_line("rowCount(" << parent.row() << "," << parent.column()
<< ")");
NavigationItem* parentItem;
if (parent.column() > 0) return 0;
if (!parent.isValid())
parentItem = p->root;
else
parentItem = static_cast<NavigationItem*>(parent.internalPointer());
return parentItem->childCount();
}
int NavigationModel::columnCount(const QModelIndex& parent) const
{
radix_tagged_line("columnCount(" << parent.row() << "," << parent.column()
<< ")");
if (parent.isValid())
return static_cast<NavigationItem*>(parent.internalPointer())
->columnCount();
else
return p->root->columnCount();
}
} // namespace radix
#ifndef RADIX_RADIXWIDGETS_NAVIGATIONMODEL_HH_
#define RADIX_RADIXWIDGETS_NAVIGATIONMODEL_HH_
#include <QAbstractItemModel>
#include <QObject>
#include <memory>
namespace radix
{
class NavigationModel : public QAbstractItemModel
{
Q_OBJECT
public:
NavigationModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
private:
class PImpl;
std::unique_ptr<PImpl, void (*)(PImpl *)> p;
};
} // namespace radix
#endif
#include "radixwidgets/navigationwidget.hh"
#include "radixbug/bug.hh"
#include "radixwidgets/navigationitem.hh"
#include "radixwidgets/navigationmodel.hh"
#include "radixbug/bug.hh"
#include <QClipboard>
#include <QEvent>
......@@ -22,142 +24,12 @@
namespace radix
{
ProNavDelegate::ProNavDelegate(QObject* parent)
: QStyledItemDelegate(parent)
{
}
bool ProNavDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view,
const QStyleOptionViewItem& option,
const QModelIndex& index)
{
// event, view, and index must be valid
if (event == nullptr || view == nullptr || !index.isValid())
{
return false;
}
// determine what to do based on the event type
switch (event->type())
{
// displaying/hiding a tooltip
case QEvent::ToolTip:
{
// index's visual rect, and view's size
auto rect = view->visualRect(index);
auto size = view->size();
// move rect and resize based on size hint
rect.moveTo(view->viewport()->mapTo(view, rect.topLeft()));
rect.setSize(sizeHint(option, index));
// size hint rect is cut off by the view, show tooltip
if (rect.right() > size.width())
{
// item text is cut off, so start with the item text as the tooltip
auto tooltip = index.data(Qt::DisplayRole).toString();
// get specified tooltip
auto original = index.data(Qt::ToolTipRole);
// tooltip specified, tack it on the end
if (original.isValid())
{
tooltip.append(QString(" (%1)").arg(original.toString()));
}
QToolTip::showText(event->globalPos(), tooltip, view);
return true;
}
// full item is visible, so call base implementation
if (QStyledItemDelegate::helpEvent(event, view, option, index))
{
return true;
}
QToolTip::hideText();
return false;
}
default:
break;
}
return QStyledItemDelegate::helpEvent(event, view, option, index);
}
void ProNavDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
// default implementation
QStyledItemDelegate::paint(painter, option, index);
}
QSize ProNavDelegate::sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
// default size
auto s = QStyledItemDelegate::sizeHint(option, index);
return s;
}
class NavigationItemSortFilterProxyModel::PImpl
{
public:
QHash<QModelIndex, bool> seen;
};
NavigationItemSortFilterProxyModel::NavigationItemSortFilterProxyModel(
QObject* parent)
: QSortFilterProxyModel(parent)
, p(new PImpl(), [](PImpl* impl) { delete impl; })
{
}
bool NavigationItemSortFilterProxyModel::accepts(const QModelIndex& index) const
{
const QString& text = index.data().toString();
const QRegExp& regex = filterRegExp();
bool accept = text.contains(regex);
for (int i = 0, ie = sourceModel()->rowCount(index); i < ie; i++)
{
if (accepts(sourceModel()->index(i, 0, index)))
{
accept = true;
break;
}
}
p->seen[index] = accept;
return accept;
}
void NavigationItemSortFilterProxyModel::clearCache() { p->seen.clear(); }
bool NavigationItemSortFilterProxyModel::filterAcceptsRow(
int source_row, const QModelIndex& source_parent) const
{
const QModelIndex& index = sourceModel()->index(source_row, 0, source_parent);
if (p->seen.contains(index))
{
return p->seen.value(index);
}
return accepts(index);
}
class NavigationWidget::PImpl
{
public:
QLineEdit* filter;
QStandardItemModel* navModel;
QTreeView* navTree;
NavigationItemSortFilterProxyModel* proxyModel;
NavigationModel* model;
QTreeView* view;
};
NavigationWidget::NavigationWidget(QWidget* parent)
......@@ -168,159 +40,35 @@ NavigationWidget::NavigationWidget(QWidget* parent)
initLayout();
}
void NavigationWidget::activateItem()
{
// selected indices
const auto& selected = p->navTree->selectionModel()->selectedIndexes();
// selected at least one index
if (selected.isEmpty())
{
return;
}
// properly map selected items
}
void NavigationWidget::addItem(QStandardItem* item)
{
// perform linear search to determine where to insert the item (alphabetized)
for (auto i = 0, ie = p->navModel->rowCount(); i < ie; i++)
{
// current item and text comparison result
auto si = p->navModel->item(i);
auto c = item->text().compare(si->text(), Qt::CaseInsensitive);
// new item should be inserted before the current item
if (c < 0)
{
p->navModel->insertRow(i, item);
return;
}
}
// just append new item to model
p->navModel->appendRow(item);
}
void NavigationWidget::closeTriggered()
{
// selected item
auto ani = selectedRoot();
radix_tagged_line("closeTriggered");
}
void NavigationWidget::collapse(QStandardItem* item)
{
p->navTree->collapse(p->proxyModel->mapFromSource(item->index()));
}
void NavigationWidget::collapse()
{
// selected item
auto ani = selectedItem();
// no-op
if (ani == nullptr)
{
return;
}
// item's index
const auto& index = ani->index();
const auto& mapped = p->proxyModel->mapFromSource(index);
// current index is expanded, so collapse it
if (p->navTree->isExpanded(mapped))
{
p->navTree->collapse(mapped);
}
// current index is collapsed (or a leaf), so move to its parent
else
{
p->navTree->selectionModel()->setCurrentIndex(
mapped.parent(), QItemSelectionModel::ClearAndSelect);
}
}
bool NavigationWidget::eventFilter(QObject* o, QEvent* e)
{
radix_tagged_line("eventFilter");
return QWidget::eventFilter(o, e);
}
void NavigationWidget::expand(QStandardItem* item)
{
p->navTree->expand(p->proxyModel->mapFromSource(item->index()));
}
void NavigationWidget::filterModel(const QString& pattern)
{
// regex used to filter the model
QRegExp regex(pattern, Qt::CaseInsensitive, QRegExp::RegExp2);
// update the model
p->proxyModel->clearCache();
p->proxyModel->setFilterRegExp(regex);
// update the tree
if (pattern.isEmpty())
{
p->navTree->collapseAll();
for (int i = 0, ie = p->navModel->rowCount(); i < ie; i++)
{
p->navTree->expand(
p->proxyModel->mapFromSource(p->navModel->index(i, 0)));
}
}
else
{
p->navTree->expandAll();
}
}
void NavigationWidget::filterSelectionChanged()
{
radix_tagged_line("filterSelectionChanged");
}
void NavigationWidget::initLayout()