Reputation: 539
I have a QTreeView object and the items of this tree view can hold one of three types of data, each require different handling. Therefore, for each of these types, I need a different context menu upon right clicking by the user. My tree object looks like this:
MyTreeView::MyTreeView(QWidget* parent): QTreeView(parent) {
// some code
m_init_item_model();
// some code
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(make_context_menu(const QPoint&)));
}
void MyTreeView::m_init_item_model() {
m_itemModel = new QStandardItemModel(this);
m_itemModel->setHorizontalHeaderLabels(QStringList()
<< "Item Name" << "Item Type");
this->setModel(m_itemModel);
// some code
}
How I currently deal with different context menus is, every time an item is right clicked, I look at the name of the QStandardItem
that falls under "Item Type"
for that tree item, and I pass it through a filter of if()
s and call different functions that create different context menus for different type of items. My approach is given below:
void MyTreeView::make_context_menu(const QPoint& pos) {
m_currentPos = pos;
QModelIndex firstIndex= this->indexAt(m_currentPos);
if(firstIndex.isValid()) {
if(firstIndex.parent() == QModelIndex()) { // if it's a top level item
int row = firstIndex.row();
m_currentIndex = firstIndex.sibling(row, 0);
m_currentItem = m_itemModel->itemFromIndex(m_currentIndex);
QModelIndex flTypeIndex = firstIndex.sibling(row, 1);
QStandardItem* fileTypeItem =
m_itemModel->itemFromIndex(flTypeIndex);
QString fileType = fileTypeItem->text();
if(fileType == "Type A") m_make_typeA_context_menu();
if(fileType == "Type B") m_make_typeB_context_menu();
}
}
}
This works, but I'm not sure if this is a rather good way of doing it. Now the thing is, as I add support for more types in my program, I'll have to get back to this and add more if()
s. Well, I'll need different context menus for those new types, so maybe there's no other way; but I'm not an experienced programmer so I would like to see different approaches at this problem.
Upvotes: 1
Views: 355
Reputation: 539
I came up with a not-so-terrible solution to this which is I think better than the approach I've mentioned in my original post. I wanted to share my solution as I thought it could help someone.
Firstly, I've created a base class for all tree items to inherit. The base class uses an enum to enumerate possible/registered tree item types.
// base_tree_item.h
#include <QStandardItem>
enum TREE_ITEM{
TYPE1,
TYPE2,
TYPE3,
// etc
TYPE_COUNT
};
class BaseTreeItem: public QStandardItem {
public:
BaseTreeItem() = delete;
BaseTreeItem(TREE_ITEM itemType): m_itemType(itemType) {}
const TREE_ITEM itemType() const { return m_itemType; }
virtual ~BaseTreeItem() {}
private:
TREE_ITEM m_itemType;
};
Now, every tree item will inherit this base class and have to construct the base with a type enum. For example,
// some_type1_item.h
#include "base_tree_item.h"
class SomeType1Item: public BaseTreeItem {
public:
SomeType1Item(): BaseTreeItem(TREE_ITEM::TYPE1) { //stuff }
};
And now, to construct the custom context menu exclusive to the item type:
void TreeView::make_context_menu(const QPoint& pos) {
m_currentPos = pos;
QModelIndex firstIndex= this->indexAt(m_currentPos);
if(firstIndex.isValid()) {
int row = firstIndex.row();
m_currentIndex = firstIndex.sibling(row, 0);
m_currentItem = m_itemModel->itemFromIndex(m_currentIndex);
BaseTreeItem* currentTreeItem =
static_cast<BaseTreeItem*>(m_currentItem);
TREE_ITEM itemType = currentTreeItem->itemType();
switch(itemType) {
case TYPE1:
make_type1_menu(pos);
break;
case TYPE2:
make_type2_menu(pos);
break;
// etc
default:
break;
}
}
}
To me, this seemed like a tidier solution. To make this better, classes deriving from QMenu could be constructed to make menu objects for each type like MenuType1, MenuType2 etc and then they could be used as below to further tidy things up:
void TreeView::make_type1_menu(pos) {
// stuff
MenuType1* newMenu = new MenuType1(//args);
newMenu->exec(mapToGlobal(pos));
}
Upvotes: 1