Gui: Add support for selecting multiple objects without keyboard

Adds checkboxes to the document tree which makes it possible to
select multiple document objects on devices without physical keyboard.

These checkboxes are opt-in and can be enabled/disabled from:
Edit -> Preferences -> Display -> Navigation -> Selection

Forum thread for feature the request:
https://forum.freecadweb.org/viewtopic.php?f=34&t=53065

We need to iterate the tree to add/remove the selection boxes when
the enable-setting has been changed.

The size for the first column in the tree is adjusted to fit both
name and optional checkbox.
This commit is contained in:
Benjamin Nauck
2021-01-02 20:31:41 +01:00
committed by wmayer
parent 78158a058b
commit 67f21d663d
5 changed files with 89 additions and 14 deletions

View File

@@ -897,7 +897,7 @@ QTreeWidgetItem *DlgPropertyLink::createItem(
if(allowSubObject) {
item->setChildIndicatorPolicy(obj->getLinkedObject(true)->getOutList().size()?
QTreeWidgetItem::ShowIndicator:QTreeWidgetItem::DontShowIndicator);
item->setFlags(item->flags() | Qt::ItemIsEditable);
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsUserCheckable);
}
const char *typeName = obj->getTypeId().getName();

View File

@@ -52,6 +52,7 @@ void DlgSettingsSelection::saveSettings()
handle->SetBool("SyncSelection", ui->checkBoxAutoExpand->isChecked());
handle->SetBool("PreSelection", ui->checkBoxPreselect->isChecked());
handle->SetBool("RecordSelection", ui->checkBoxRecord->isChecked());
handle->SetBool("CheckBoxesSelection", ui->checkBoxSelectionCheckBoxes->isChecked());
}
void DlgSettingsSelection::loadSettings()
@@ -61,6 +62,7 @@ void DlgSettingsSelection::loadSettings()
ui->checkBoxAutoExpand->setChecked(handle->GetBool("SyncSelection"));
ui->checkBoxPreselect->setChecked(handle->GetBool("PreSelection"));
ui->checkBoxRecord->setChecked(handle->GetBool("RecordSelection"));
ui->checkBoxSelectionCheckBoxes->setChecked(handle->GetBool("CheckBoxesSelection"));
}
void DlgSettingsSelection::changeEvent(QEvent *e)

View File

@@ -14,13 +14,6 @@
<string>Selection</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxAutoSwitch">
<property name="text">
<string>Auto switch to the 3D view containing the selected item</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxAutoExpand">
<property name="text">
@@ -42,7 +35,7 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -50,11 +43,25 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>274</height>
<height>245</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxAutoSwitch">
<property name="text">
<string>Auto switch to the 3D view containing the selected item</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxSelectionCheckBoxes">
<property name="text">
<string>Add checkboxes for selection in document tree</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@@ -139,6 +139,11 @@ void TreeParams::onSyncSelectionChanged() {
TreeWidget::scrollItemToTop();
}
void TreeParams::onCheckBoxesSelectionChanged()
{
TreeWidget::instance()->synchronizeSelectionCheckBoxes();
}
void TreeParams::onDocumentModeChanged() {
App::GetApplication().setActiveDocument(App::GetApplication().getActiveDocument());
}
@@ -549,6 +554,8 @@ TreeWidget::TreeWidget(const char *name, QWidget* parent)
this, SLOT(onItemExpanded(QTreeWidgetItem*)));
connect(this, SIGNAL(itemSelectionChanged()),
this, SLOT(onItemSelectionChanged()));
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(onItemChanged(QTreeWidgetItem*, int)));
connect(this->preselectTimer, SIGNAL(timeout()),
this, SLOT(onPreSelectTimer()));
connect(this->selectTimer, SIGNAL(timeout()),
@@ -2800,6 +2807,33 @@ void TreeWidget::onItemSelectionChanged ()
this->blockConnection(lock);
}
static bool isSelectionCheckBoxesEnabled() {
return TreeParams::Instance()->CheckBoxesSelection();
}
void TreeWidget::synchronizeSelectionCheckBoxes() {
const bool useCheckBoxes = isSelectionCheckBoxesEnabled();
for (QTreeWidgetItemIterator it(this); *it; ++it) {
if (const auto item = dynamic_cast<DocumentObjectItem*>(*it)) {
if (useCheckBoxes)
item->QTreeWidgetItem::setCheckState(0, item->isSelected() ? Qt::Checked : Qt::Unchecked);
else
item->setData(0, Qt::CheckStateRole, QVariant());
}
}
resizeColumnToContents(0);
}
void TreeWidget::onItemChanged(QTreeWidgetItem *item, int column) {
if (column == 0 && isSelectionCheckBoxesEnabled()) {
bool selected = item->isSelected();
bool checked = item->checkState(0) == Qt::Checked;
if (checked != selected) {
item->setSelected(checked);
}
}
}
void TreeWidget::onSelectTimer() {
_updateStatus(false);
@@ -3923,6 +3957,7 @@ void DocumentItem::clearSelection(DocumentObjectItem *exclude)
item->selected = 0;
item->mySubs.clear();
item->setSelected(false);
item->setCheckState(false);
}
END_FOREACH_ITEM;
treeWidget()->blockSignals(ok);
@@ -3933,8 +3968,10 @@ void DocumentItem::updateSelection(QTreeWidgetItem *ti, bool unselect) {
auto child = ti->child(i);
if(child && child->type()==TreeWidget::ObjectType) {
auto childItem = static_cast<DocumentObjectItem*>(child);
if(unselect)
if (unselect) {
childItem->setSelected(false);
childItem->setCheckState(false);
}
updateItemSelection(childItem);
if(unselect && childItem->isGroup()) {
// If the child item being force unselected by its group parent
@@ -3952,8 +3989,17 @@ void DocumentItem::updateSelection(QTreeWidgetItem *ti, bool unselect) {
void DocumentItem::updateItemSelection(DocumentObjectItem *item) {
bool selected = item->isSelected();
if((selected && item->selected>0) || (!selected && !item->selected))
bool checked = item->checkState(0) == Qt::Checked;
if(selected && !checked)
item->setCheckState(true);
if(!selected && checked)
item->setCheckState(false);
if((selected && item->selected>0) || (!selected && !item->selected)) {
return;
}
if(item->selected != -1)
item->mySubs.clear();
item->selected = selected;
@@ -4026,6 +4072,7 @@ void DocumentItem::updateItemSelection(DocumentObjectItem *item) {
if(!Gui::Selection().addSelection(docname,objname,subname.c_str())) {
item->selected = 0;
item->setSelected(false);
item->setCheckState(false);
return;
}
}
@@ -4221,6 +4268,7 @@ void DocumentItem::selectItems(SelectionReason reason) {
item->selected = 0;
item->mySubs.clear();
item->setSelected(false);
item->setCheckState(false);
}else if(item->selected) {
if(sync) {
if(item->selected==2 && showItem(item,false,reason==SR_FORCE_EXPAND)) {
@@ -4242,6 +4290,7 @@ void DocumentItem::selectItems(SelectionReason reason) {
}
item->selected = 1;
item->setSelected(true);
item->setCheckState(true);
}
END_FOREACH_ITEM;
@@ -4338,8 +4387,10 @@ bool DocumentItem::showItem(DocumentObjectItem *item, bool select, bool force) {
}else
parent->setExpanded(true);
if(select)
if(select) {
item->setSelected(true);
item->setCheckState(true);
}
return true;
}
@@ -4366,7 +4417,9 @@ DocumentObjectItem::DocumentObjectItem(DocumentItem *ownerDocItem, DocumentObjec
: QTreeWidgetItem(TreeWidget::ObjectType)
, myOwner(ownerDocItem), myData(data), previousStatus(-1),selected(0),populated(false)
{
setFlags(flags()|Qt::ItemIsEditable);
setFlags(flags() | Qt::ItemIsEditable | Qt::ItemIsUserCheckable);
setCheckState(false);
myData->items.insert(this);
++countItems;
TREE_LOG("Create item: " << countItems << ", " << object()->getObject()->getFullName());
@@ -4948,6 +5001,13 @@ App::DocumentObject *DocumentObjectItem::getRelativeParent(
return 0;
}
void DocumentObjectItem::setCheckState(bool checked) {
if (isSelectionCheckBoxesEnabled())
QTreeWidgetItem::setCheckState(0, checked ? Qt::Checked : Qt::Unchecked);
else
setData(0, Qt::CheckStateRole, QVariant());
}
DocumentItem *DocumentObjectItem::getParentDocument() const {
return getTree()->getDocumentItem(object()->getDocument());
}

View File

@@ -129,6 +129,8 @@ public:
void startItemSearch(QLineEdit*);
void itemSearch(const QString &text, bool select);
void synchronizeSelectionCheckBoxes();
protected:
/// Observer message from the Selection
void onSelectionChanged(const SelectionChanges& msg) override;
@@ -176,6 +178,7 @@ protected Q_SLOTS:
private Q_SLOTS:
void onItemSelectionChanged(void);
void onItemChanged(QTreeWidgetItem*, int);
void onItemEntered(QTreeWidgetItem * item);
void onItemCollapsed(QTreeWidgetItem * item);
void onItemExpanded(QTreeWidgetItem * item);
@@ -436,6 +439,8 @@ public:
TreeWidget *getTree() const;
private:
void setCheckState(bool checked);
QBrush bgBrush;
DocumentItem *myOwner;
DocumentObjectDataPtr myData;
@@ -521,6 +526,7 @@ public:
#define FC_TREEPARAM_DEFS \
FC_TREEPARAM_DEF2(SyncSelection,bool,Bool,true) \
FC_TREEPARAM_DEF2(CheckBoxesSelection,bool,Bool,false) \
FC_TREEPARAM_DEF(SyncView,bool,Bool,true) \
FC_TREEPARAM_DEF(PreSelection,bool,Bool,true) \
FC_TREEPARAM_DEF(SyncPlacement,bool,Bool,false) \