9#include <QtDesigner/abstractformeditor.h>
10#include <QtDesigner/taskmenu.h>
11#include <QtDesigner/qextensionmanager.h>
12#include <QtDesigner/abstractformwindowcursor.h>
13#include <QtDesigner/abstractformwindowmanager.h>
14#include <QtDesigner/container.h>
15#include <QtDesigner/abstractmetadatabase.h>
16#include <QtDesigner/abstractpropertyeditor.h>
19#include <qdesigner_utils_p.h>
20#include <formwindowbase_p.h>
21#include <qdesigner_dnditem_p.h>
22#include <textpropertyeditor_p.h>
23#include <qdesigner_command_p.h>
27#include <QtWidgets/qapplication.h>
28#include <QtWidgets/qheaderview.h>
29#include <QtWidgets/qlineedit.h>
30#include <QtWidgets/qscrollbar.h>
31#include <QtGui/qpainter.h>
32#include <QtWidgets/qboxlayout.h>
33#include <QtCore/qitemselectionmodel.h>
34#include <QtWidgets/qmenu.h>
35#include <QtWidgets/qtreeview.h>
36#include <QtWidgets/qstyleditemdelegate.h>
37#include <QtGui/qevent.h>
39#include <QtCore/qdebug.h>
40#include <QtCore/qlist.h>
41#include <QtCore/qpointer.h>
42#include <QtCore/qsortfilterproxymodel.h>
72 if (!o->isWidgetType())
85 if (!dropTarget || dropTarget->layout())
87 return QPoint(fw->designerGrid().deltaX(), fw->designerGrid().deltaY());
90namespace qdesigner_internal {
101 if (index.column() != ObjectInspectorModel::ObjectNameColumn)
102 return QStyledItemDelegate::createEditor(parent, option, index);
104 const bool isMainContainer = !index.parent().isValid();
105 return new TextPropertyEditor(parent, TextPropertyEditor::EmbeddingTreeView,
106 isMainContainer ? ValidationObjectNameScope : ValidationObjectName);
131 bool handled =
false;
132 switch (event->key()) {
135 if (event->modifiers() & Qt::ShiftModifier) {
140 case Qt::Key_Space: {
141 const QModelIndex index = currentIndex();
142 if (index.isValid() && index.column() == 0 && !model()->hasChildren(index) && model()->flags(index) & Qt::ItemIsEditable) {
153 QTreeView::keyPressEvent(event);
166 QDesignerFormEditorInterface *
core()
const {
return m_core; }
191 void setFormWindowBlocked(QDesignerFormWindowInterface *fwi);
192 void applyCursorSelection();
193 void synchronizeSelection(
const QItemSelection & selected,
const QItemSelection &deselected);
194 bool checkManagedWidgetSelection(
const QModelIndexList &selection);
195 void showContainersCurrentPage(
QWidget *widget);
197 enum SelectionFlags { AddToSelection = 1, MakeCurrent = 2};
198 void selectIndexRange(
const QModelIndexList &indexes,
unsigned flags);
200 QDesignerFormEditorInterface *m_core;
201 QLineEdit *m_filterLineEdit;
202 QTreeView *m_treeView;
204 QSortFilterProxyModel *m_filterModel;
205 QPointer<FormWindowBase> m_formWindow;
206 QPointer<QWidget> m_formFakeDropTarget;
207 bool m_withinClearSelection;
212 m_filterLineEdit(
new QLineEdit),
213 m_treeView(
new ObjectInspectorTreeView),
214 m_model(
new ObjectInspectorModel(m_treeView)),
215 m_filterModel(
new QSortFilterProxyModel(m_treeView)),
216 m_withinClearSelection(
false)
218 m_filterModel->setRecursiveFilteringEnabled(
true);
219 m_filterLineEdit->setPlaceholderText(ObjectInspector::tr(
"Filter"));
220 m_filterLineEdit->setClearButtonEnabled(
true);
221 connect(m_filterLineEdit, &QLineEdit::textChanged,
222 m_filterModel, &QSortFilterProxyModel::setFilterFixedString);
224 connect(m_filterLineEdit, &QLineEdit::textChanged,
225 m_core, [
this] (
const QString &text) {
227 this->m_treeView->expandAll();
229 m_filterModel->setSourceModel(m_model);
230 m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
231 m_treeView->setModel(m_filterModel);
232 m_treeView->setSortingEnabled(
true);
233 m_treeView->sortByColumn(0, Qt::AscendingOrder);
234 m_treeView->setItemDelegate(
new ObjectInspectorDelegate);
235 m_treeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
236 m_treeView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
237 m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
238 m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
239 m_treeView->setAlternatingRowColors(
true);
240 m_treeView->setTextElideMode (Qt::ElideMiddle);
242 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
245ObjectInspector::ObjectInspectorPrivate::~ObjectInspectorPrivate()
247 delete m_treeView->itemDelegate();
250void ObjectInspector::ObjectInspectorPrivate::clearSelection()
252 m_withinClearSelection =
true;
253 m_treeView->clearSelection();
254 m_withinClearSelection =
false;
257QWidget *ObjectInspector::ObjectInspectorPrivate::managedWidgetAt(
const QPoint &global_mouse_pos)
262 const QPoint pos = m_treeView->viewport()->mapFromGlobal(global_mouse_pos);
263 QObject *o = objectAt(m_treeView->indexAt(pos));
265 if (!o || !o->isWidgetType())
268 QWidget *rc = qobject_cast<QWidget *>(o);
269 if (!m_formWindow->isManaged(rc))
274void ObjectInspector::ObjectInspectorPrivate::showContainersCurrentPage(QWidget *widget)
279 FormWindow *fw = FormWindow::findFormWindow(widget);
283 QWidget *w = widget->parentWidget();
284 bool macroStarted =
false;
286 while (w !=
nullptr) {
287 if (fw->isManaged(w) && !qobject_cast<QMainWindow *>(w)) {
288 if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), w)) {
289 const int count = c->count();
290 if (count > 1 && !c->widget(c->currentIndex())->isAncestorOf(widget)) {
291 for (
int i = 0; i < count; i++)
292 if (c->widget(i)->isAncestorOf(widget)) {
295 fw->beginCommand(tr(
"Change Current Page"));
297 ChangeCurrentPageCommand *cmd =
new ChangeCurrentPageCommand(fw);
299 fw->commandHistory()->push(cmd);
305 w = w->parentWidget();
311void ObjectInspector::ObjectInspectorPrivate::restoreDropHighlighting()
313 if (m_formFakeDropTarget) {
315 m_formWindow->highlightWidget(m_formFakeDropTarget, QPoint(5, 5), FormWindow::Restore);
317 m_formFakeDropTarget =
nullptr;
321void ObjectInspector::ObjectInspectorPrivate::handleDragEnterMoveEvent(
const QWidget *objectInspectorWidget, QDragMoveEvent * event,
bool isDragEnter)
328 const QDesignerMimeData *mimeData = qobject_cast<
const QDesignerMimeData *>(event->mimeData());
334 QWidget *dropTarget =
nullptr;
335 QPoint fakeDropTargetOffset = QPoint(0, 0);
336 if (QWidget *managedWidget = managedWidgetAt(objectInspectorWidget->mapToGlobal(event->position().toPoint()))) {
337 fakeDropTargetOffset = dropPointOffset(m_formWindow, managedWidget);
339 const QPoint fakeFormPos = m_formWindow->mapFromGlobal(managedWidget->mapToGlobal(fakeDropTargetOffset));
340 const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget;
341 dropTarget = m_formWindow->widgetUnderMouse(fakeFormPos, wum);
344 if (m_formFakeDropTarget && dropTarget != m_formFakeDropTarget)
345 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Restore);
347 m_formFakeDropTarget = dropTarget;
348 if (m_formFakeDropTarget)
349 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Highlight);
352 if (isDragEnter || m_formFakeDropTarget)
353 mimeData->acceptEvent(event);
357void ObjectInspector::ObjectInspectorPrivate::dropEvent (QDropEvent * event)
359 if (!m_formWindow || !m_formFakeDropTarget) {
364 const QDesignerMimeData *mimeData = qobject_cast<
const QDesignerMimeData *>(event->mimeData());
369 const QPoint fakeGlobalDropFormPos = m_formFakeDropTarget->mapToGlobal(dropPointOffset(m_formWindow , m_formFakeDropTarget));
370 mimeData->moveDecoration(fakeGlobalDropFormPos + mimeData->hotSpot());
371 if (!m_formWindow->dropWidgets(mimeData->items(), m_formFakeDropTarget, fakeGlobalDropFormPos)) {
375 mimeData->acceptEvent(event);
378QModelIndexList ObjectInspector::ObjectInspectorPrivate::indexesOf(QObject *o)
const
380 QModelIndexList result;
381 const auto srcIndexes = m_model->indexesOf(o);
382 if (!srcIndexes.isEmpty()) {
383 result.reserve(srcIndexes.size());
384 for (
const auto &srcIndex : srcIndexes)
385 result.append(m_filterModel->mapFromSource(srcIndex));
390QObject *ObjectInspector::ObjectInspectorPrivate::objectAt(
const QModelIndex &index)
const
392 return m_model->objectAt(m_filterModel->mapToSource(index));
395bool ObjectInspector::ObjectInspectorPrivate::selectObject(QObject *o)
397 if (!m_core->metaDataBase()->item(o))
400 using ModelIndexSet = QSet<QModelIndex>;
402 const QModelIndexList objectIndexes = indexesOf(o);
403 if (objectIndexes.isEmpty())
406 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
407 const auto currentSelectedItemList = selectionModel->selectedRows(0);
408 const ModelIndexSet currentSelectedItems(currentSelectedItemList.cbegin(), currentSelectedItemList.cend());
411 if (!currentSelectedItems.isEmpty()
412 && currentSelectedItems == ModelIndexSet(objectIndexes.cbegin(), objectIndexes.cend())) {
417 selectIndexRange(objectIndexes, MakeCurrent);
421void ObjectInspector::ObjectInspectorPrivate::selectIndexRange(
const QModelIndexList &indexes,
unsigned flags)
423 if (indexes.isEmpty())
426 QItemSelectionModel::SelectionFlags selectFlags = QItemSelectionModel::Select|QItemSelectionModel::Rows;
427 if (!(flags & AddToSelection))
428 selectFlags |= QItemSelectionModel::Clear;
429 if (flags & MakeCurrent)
430 selectFlags |= QItemSelectionModel::Current;
432 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
433 for (
const auto &mi : indexes) {
434 if (mi.column() == 0) {
435 selectionModel->select(mi, selectFlags);
436 selectFlags &= ~(QItemSelectionModel::Clear|QItemSelectionModel::Current);
439 if (flags & MakeCurrent)
440 m_treeView->scrollTo(indexes.constFirst(), QAbstractItemView::EnsureVisible);
443void ObjectInspector::ObjectInspectorPrivate::clear()
445 m_formFakeDropTarget =
nullptr;
446 m_formWindow =
nullptr;
450static inline bool mainContainerIsCurrent(
const QDesignerFormWindowInterface *fw)
452 const QDesignerFormWindowCursorInterface *cursor = fw->cursor();
453 if (cursor->selectedWidgetCount() > 1)
455 const QWidget *current = cursor->current();
456 return current == fw || current == fw->mainContainer();
459void ObjectInspector::ObjectInspectorPrivate::setFormWindow(QDesignerFormWindowInterface *fwi)
461 const bool blocked = m_treeView->selectionModel()->blockSignals(
true);
463 UpdateBlocker ub(m_treeView);
464 setFormWindowBlocked(fwi);
467 m_treeView->update();
468 m_treeView->selectionModel()->blockSignals(blocked);
471void ObjectInspector::ObjectInspectorPrivate::setFormWindowBlocked(QDesignerFormWindowInterface *fwi)
473 FormWindowBase *fw = qobject_cast<FormWindowBase *>(fwi);
474 const bool formWindowChanged = m_formWindow != fw;
478 const int oldWidth = m_treeView->columnWidth(0);
479 const int xoffset = m_treeView->horizontalScrollBar()->value();
480 const int yoffset = m_treeView->verticalScrollBar()->value();
482 if (formWindowChanged)
483 m_formFakeDropTarget =
nullptr;
485 switch (m_model->update(m_formWindow)) {
486 case ObjectInspectorModel::NoForm:
489 case ObjectInspectorModel::Rebuilt:
490 applyCursorSelection();
491 m_treeView->expandAll();
492 if (formWindowChanged) {
493 m_treeView->resizeColumnToContents(0);
495 m_treeView->setColumnWidth(0, oldWidth);
496 m_treeView->horizontalScrollBar()->setValue(xoffset);
497 m_treeView->verticalScrollBar()->setValue(yoffset);
500 case ObjectInspectorModel::Updated: {
506 bool applySelection = !mainContainerIsCurrent(m_formWindow);
507 if (!applySelection) {
508 const QModelIndexList currentIndexes = m_treeView->selectionModel()->selectedRows(0);
509 if (currentIndexes.isEmpty()) {
510 applySelection =
true;
512 applySelection = selectionType(m_formWindow, objectAt(currentIndexes.constFirst())) == ManagedWidgetSelection;
516 applyCursorSelection();
523void ObjectInspector::ObjectInspectorPrivate::applyCursorSelection()
525 const QDesignerFormWindowCursorInterface *cursor = m_formWindow->cursor();
526 const int count = cursor->selectedWidgetCount();
531 QWidget *currentWidget = cursor->current();
533 selectIndexRange(indexesOf(currentWidget), MakeCurrent);
535 m_treeView->selectionModel()->clearSelection();
537 for (
int i = 0;i < count; i++) {
538 QWidget *widget = cursor->selectedWidget(i);
539 if (widget != currentWidget)
540 selectIndexRange(indexesOf(widget), AddToSelection);
545static int selectInCursor(FormWindowBase *fw,
const QObjectList &objects,
bool value)
548 const bool blocked = fw->blockSelectionChanged(
true);
549 for (
auto *o : objects) {
550 if (selectionType(fw, o) == ManagedWidgetSelection) {
551 fw->selectWidget(
static_cast<QWidget *>(o), value);
555 fw->blockSelectionChanged(blocked);
559void ObjectInspector::ObjectInspectorPrivate::slotSelectionChanged(
const QItemSelection &selected,
const QItemSelection &deselected)
562 synchronizeSelection(selected, deselected);
563 QMetaObject::invokeMethod(m_core->formWindowManager(),
"slotUpdateActions");
569QObjectList ObjectInspector::ObjectInspectorPrivate::indexesToObjects(
const QModelIndexList &indexes)
const
572 if (indexes.isEmpty())
574 rc.reserve(indexes.size());
575 for (
const auto &mi : indexes) {
576 if (mi.column() == 0)
577 rc.append(objectAt(mi));
584bool ObjectInspector::ObjectInspectorPrivate::checkManagedWidgetSelection(
const QModelIndexList &rowSelection)
586 bool isManagedWidgetSelection =
false;
587 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
588 for (
const auto &mi : rowSelection) {
589 QObject *object = objectAt(mi);
590 if (selectionType(m_formWindow, object) == ManagedWidgetSelection) {
591 isManagedWidgetSelection =
true;
596 if (!isManagedWidgetSelection)
599 const bool blocked = selectionModel->blockSignals(
true);
600 for (
const auto &mi : rowSelection) {
601 QObject *object = objectAt(mi);
602 if (selectionType(m_formWindow, object) != ManagedWidgetSelection)
603 selectionModel->select(mi, QItemSelectionModel::Deselect|QItemSelectionModel::Rows);
605 selectionModel->blockSignals(blocked);
609void ObjectInspector::ObjectInspectorPrivate::synchronizeSelection(
const QItemSelection & selectedSelection,
const QItemSelection &deselectedSelection)
612 const QObjectList deselected = indexesToObjects(deselectedSelection.indexes());
613 const QObjectList newlySelected = indexesToObjects(selectedSelection.indexes());
615 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
617 int deselectedManagedWidgetCount = 0;
618 if (!deselected.isEmpty())
619 deselectedManagedWidgetCount = selectInCursor(m_formWindow, deselected,
false);
621 if (newlySelected.isEmpty()) {
622 if (currentSelectedIndexes.isEmpty())
623 m_formWindow->clearSelection(!m_withinClearSelection);
627 const int selectManagedWidgetCount = selectInCursor(m_formWindow, newlySelected,
true);
631 if (selectManagedWidgetCount == 0) {
632 if (checkManagedWidgetSelection(currentSelectedIndexes)) {
634 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
635 m_formWindow->emitSelectionChanged();
639 m_formWindow->clearSelection(
false);
640 QObject *unmanagedObject = newlySelected.constFirst();
641 m_core->propertyEditor()->setObject(unmanagedObject);
642 m_core->propertyEditor()->setEnabled(
true);
644 if (newlySelected.size() == 1 && unmanagedObject->isWidgetType())
645 showContainersCurrentPage(
static_cast<QWidget*>(unmanagedObject));
649 if (newlySelected.size() == 1) {
650 QObject *object = newlySelected.constFirst();
651 if (object->isWidgetType())
652 showContainersCurrentPage(
static_cast<QWidget*>(object));
657 if (currentSelectedIndexes.size() > selectManagedWidgetCount)
658 checkManagedWidgetSelection(currentSelectedIndexes);
660 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
661 m_formWindow->emitSelectionChanged();
665void ObjectInspector::ObjectInspectorPrivate::getSelection(Selection &s)
const
672 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
673 if (currentSelectedIndexes.isEmpty())
677 for (
const QModelIndex &index : currentSelectedIndexes) {
678 if (QObject *object = objectAt(index)) {
679 switch (selectionType(m_formWindow, object)) {
682 case QObjectSelection:
685 if (!s.objects.contains(object))
686 s.objects.push_back(object);
688 case UnmanagedWidgetSelection:
689 s.unmanaged.push_back(qobject_cast<QWidget *>(object));
691 case ManagedWidgetSelection:
692 s.managed.push_back(qobject_cast<QWidget *>(object));
700static inline QMenu *createTaskMenu(QObject *object, QDesignerFormWindowInterface *fw)
703 if (!object->isWidgetType())
704 return FormWindowBase::createExtensionTaskMenu(fw, object,
false);
706 QWidget *w =
static_cast<QWidget *>(object);
707 if (!fw->isManaged(w))
708 return FormWindowBase::createExtensionTaskMenu(fw, w,
false);
710 if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase*>(fw))
711 return fwb->initializePopupMenu(w);
715void ObjectInspector::ObjectInspectorPrivate::slotPopupContextMenu(QWidget * ,
const QPoint &pos)
717 if (m_formWindow ==
nullptr || m_formWindow->currentTool() != 0)
720 if (QObject *object = objectAt(m_treeView->indexAt(pos))) {
721 if (QMenu *menu = createTaskMenu(object, m_formWindow)) {
722 menu->exec(m_treeView->viewport()->mapToGlobal(pos));
729ObjectInspector::ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) :
730 QDesignerObjectInspector(parent),
731 m_impl(
new ObjectInspectorPrivate(core))
733 QVBoxLayout *vbox =
new QVBoxLayout(
this);
734 vbox->setContentsMargins(QMargins());
736 vbox->addWidget(m_impl->filterLineEdit());
737 QTreeView *treeView = m_impl->treeView();
738 vbox->addWidget(treeView);
740 connect(treeView, &QWidget::customContextMenuRequested,
741 this, &ObjectInspector::slotPopupContextMenu);
743 connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
744 this, &ObjectInspector::slotSelectionChanged);
746 connect(treeView->header(), &QHeaderView::sectionDoubleClicked,
747 this, &ObjectInspector::slotHeaderDoubleClicked);
748 setAcceptDrops(
true);
751ObjectInspector::~ObjectInspector()
756QDesignerFormEditorInterface *ObjectInspector::core()
const
758 return m_impl->core();
761void ObjectInspector::slotPopupContextMenu(
const QPoint &pos)
763 m_impl->slotPopupContextMenu(
this, pos);
766void ObjectInspector::setFormWindow(QDesignerFormWindowInterface *fwi)
768 m_impl->setFormWindow(fwi);
771void ObjectInspector::slotSelectionChanged(
const QItemSelection & selected,
const QItemSelection &deselected)
773 m_impl->slotSelectionChanged(selected, deselected);
776void ObjectInspector::getSelection(Selection &s)
const
778 m_impl->getSelection(s);
781bool ObjectInspector::selectObject(QObject *o)
783 return m_impl->selectObject(o);
786void ObjectInspector::clearSelection()
788 m_impl->clearSelection();
791void ObjectInspector::slotHeaderDoubleClicked(
int column)
793 m_impl->slotHeaderDoubleClicked(column);
796void ObjectInspector::mainContainerChanged()
799 if (sender() == m_impl->formWindow())
800 setFormWindow(
nullptr);
803void ObjectInspector::dragEnterEvent (QDragEnterEvent * event)
805 m_impl->handleDragEnterMoveEvent(
this, event,
true);
808void ObjectInspector::dragMoveEvent(QDragMoveEvent * event)
810 m_impl->handleDragEnterMoveEvent(
this, event,
false);
813void ObjectInspector::dragLeaveEvent(QDragLeaveEvent * )
815 m_impl->restoreDropHighlighting();
818void ObjectInspector::dropEvent (QDropEvent * event)
820 m_impl->dropEvent(event);
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the editor to be used for editing the data item with the given index.
void mouseMoveEvent(QMouseEvent *event) override
void keyPressEvent(QKeyEvent *event) override
QObjectList indexesToObjects(const QModelIndexList &indexes) const
QWidget * managedWidgetAt(const QPoint &global_mouse_pos)
QModelIndexList indexesOf(QObject *o) const
void setFormWindow(QDesignerFormWindowInterface *fwi)
void slotPopupContextMenu(QWidget *parent, const QPoint &pos)
~ObjectInspectorPrivate()
void dropEvent(QDropEvent *event)
const QPointer< FormWindowBase > & formWindow() const
void getSelection(Selection &s) const
QLineEdit * filterLineEdit() const
void slotHeaderDoubleClicked(int column)
void handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent *event, bool isDragEnter)
void restoreDropHighlighting()
QDesignerFormEditorInterface * core() const
QObject * objectAt(const QModelIndex &index) const
void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
bool selectObject(QObject *o)
QTreeView * treeView() const
Combined button and popup list for selecting options.
@ UnmanagedWidgetSelection
Auxiliary methods to store/retrieve settings.
static SelectionType selectionType(const QDesignerFormWindowInterface *fw, QObject *o)
static QPoint dropPointOffset(const qdesigner_internal::FormWindowBase *fw, const QWidget *dropTarget)