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:
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
Reference in New Issue
Block a user