10#include <QtDesigner/abstractformeditor.h>
11#include <QtDesigner/taskmenu.h>
12#include <QtDesigner/qextensionmanager.h>
13#include <QtDesigner/abstractformwindowcursor.h>
14#include <QtDesigner/abstractformwindowmanager.h>
15#include <QtDesigner/container.h>
16#include <QtDesigner/abstractmetadatabase.h>
17#include <QtDesigner/abstractpropertyeditor.h>
20#include <qdesigner_utils_p.h>
21#include <formwindowbase_p.h>
22#include <qdesigner_dnditem_p.h>
23#include <textpropertyeditor_p.h>
24#include <qdesigner_command_p.h>
28#include <QtWidgets/qapplication.h>
29#include <QtWidgets/qheaderview.h>
30#include <QtWidgets/qlineedit.h>
31#include <QtWidgets/qscrollbar.h>
32#include <QtGui/qpainter.h>
33#include <QtWidgets/qboxlayout.h>
34#include <QtCore/qitemselectionmodel.h>
35#include <QtWidgets/qmenu.h>
36#include <QtWidgets/qtreeview.h>
37#include <QtWidgets/qstyleditemdelegate.h>
38#include <QtGui/qevent.h>
40#include <QtCore/qdebug.h>
41#include <QtCore/qlist.h>
42#include <QtCore/qpointer.h>
43#include <QtCore/qsortfilterproxymodel.h>
73 if (!o->isWidgetType())
86 if (!dropTarget || dropTarget->layout())
88 return QPoint(fw->designerGrid().deltaX(), fw->designerGrid().deltaY());
91namespace qdesigner_internal {
102 if (index.column() != ObjectInspectorModel::ObjectNameColumn)
103 return QStyledItemDelegate::createEditor(parent, option, index);
105 const bool isMainContainer = !index.parent().isValid();
106 return new TextPropertyEditor(parent, TextPropertyEditor::EmbeddingTreeView,
107 isMainContainer ? ValidationObjectNameScope : ValidationObjectName);
132 bool handled =
false;
133 switch (event->key()) {
136 if (event->modifiers() & Qt::ShiftModifier) {
141 case Qt::Key_Space: {
142 const QModelIndex index = currentIndex();
143 if (index.isValid() && index.column() == 0 && !model()->hasChildren(index) && model()->flags(index) & Qt::ItemIsEditable) {
154 QTreeView::keyPressEvent(event);
167 QDesignerFormEditorInterface *
core()
const {
return m_core; }
192 void setFormWindowBlocked(QDesignerFormWindowInterface *fwi);
193 void applyCursorSelection();
194 void synchronizeSelection(
const QItemSelection & selected,
const QItemSelection &deselected);
195 bool checkManagedWidgetSelection(
const QModelIndexList &selection);
196 void showContainersCurrentPage(
QWidget *widget);
198 enum SelectionFlags { AddToSelection = 1, MakeCurrent = 2};
199 void selectIndexRange(
const QModelIndexList &indexes,
unsigned flags);
201 QDesignerFormEditorInterface *m_core;
202 QLineEdit *m_filterLineEdit;
203 QTreeView *m_treeView;
205 QSortFilterProxyModel *m_filterModel;
206 QPointer<FormWindowBase> m_formWindow;
207 QPointer<QWidget> m_formFakeDropTarget;
208 bool m_withinClearSelection;
213 m_filterLineEdit(
new QLineEdit),
214 m_treeView(
new ObjectInspectorTreeView),
215 m_model(
new ObjectInspectorModel(m_treeView)),
216 m_filterModel(
new QSortFilterProxyModel(m_treeView)),
217 m_withinClearSelection(
false)
219 m_filterModel->setRecursiveFilteringEnabled(
true);
220 m_filterLineEdit->setPlaceholderText(ObjectInspector::tr(
"Filter"));
221 m_filterLineEdit->setClearButtonEnabled(
true);
222 connect(m_filterLineEdit, &QLineEdit::textChanged,
223 m_filterModel, &QSortFilterProxyModel::setFilterFixedString);
225 connect(m_filterLineEdit, &QLineEdit::textChanged,
226 m_core, [
this] (
const QString &text) {
228 this->m_treeView->expandAll();
230 m_filterModel->setSourceModel(m_model);
231 m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
232 m_treeView->setModel(m_filterModel);
233 m_treeView->setSortingEnabled(
true);
234 m_treeView->sortByColumn(0, Qt::AscendingOrder);
235 m_treeView->setItemDelegate(
new ObjectInspectorDelegate);
236 m_treeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
237 m_treeView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
238 m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
239 m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
240 m_treeView->setAlternatingRowColors(
true);
241 m_treeView->setTextElideMode (Qt::ElideMiddle);
243 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
246ObjectInspector::ObjectInspectorPrivate::~ObjectInspectorPrivate()
248 delete m_treeView->itemDelegate();
251void ObjectInspector::ObjectInspectorPrivate::clearSelection()
253 m_withinClearSelection =
true;
254 m_treeView->clearSelection();
255 m_withinClearSelection =
false;
258QWidget *ObjectInspector::ObjectInspectorPrivate::managedWidgetAt(
const QPoint &global_mouse_pos)
263 const QPoint pos = m_treeView->viewport()->mapFromGlobal(global_mouse_pos);
264 QObject *o = objectAt(m_treeView->indexAt(pos));
266 if (!o || !o->isWidgetType())
269 QWidget *rc = qobject_cast<QWidget *>(o);
270 if (!m_formWindow->isManaged(rc))
275void ObjectInspector::ObjectInspectorPrivate::showContainersCurrentPage(QWidget *widget)
280 FormWindow *fw = FormWindow::findFormWindow(widget);
284 QWidget *w = widget->parentWidget();
285 bool macroStarted =
false;
287 while (w !=
nullptr) {
288 if (fw->isManaged(w) && !qobject_cast<QMainWindow *>(w)) {
289 if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), w)) {
290 const int count = c->count();
291 if (count > 1 && !c->widget(c->currentIndex())->isAncestorOf(widget)) {
292 for (
int i = 0; i < count; i++)
293 if (c->widget(i)->isAncestorOf(widget)) {
296 fw->beginCommand(tr(
"Change Current Page"));
298 ChangeCurrentPageCommand *cmd =
new ChangeCurrentPageCommand(fw);
300 fw->commandHistory()->push(cmd);
306 w = w->parentWidget();
312void ObjectInspector::ObjectInspectorPrivate::restoreDropHighlighting()
314 if (m_formFakeDropTarget) {
316 m_formWindow->highlightWidget(m_formFakeDropTarget, QPoint(5, 5), FormWindow::Restore);
318 m_formFakeDropTarget =
nullptr;
322void ObjectInspector::ObjectInspectorPrivate::handleDragEnterMoveEvent(
const QWidget *objectInspectorWidget, QDragMoveEvent * event,
bool isDragEnter)
329 const QDesignerMimeData *mimeData = qobject_cast<
const QDesignerMimeData *>(event->mimeData());
335 QWidget *dropTarget =
nullptr;
336 QPoint fakeDropTargetOffset = QPoint(0, 0);
337 if (QWidget *managedWidget = managedWidgetAt(objectInspectorWidget->mapToGlobal(event->position().toPoint()))) {
338 fakeDropTargetOffset = dropPointOffset(m_formWindow, managedWidget);
340 const QPoint fakeFormPos = m_formWindow->mapFromGlobal(managedWidget->mapToGlobal(fakeDropTargetOffset));
341 const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget;
342 dropTarget = m_formWindow->widgetUnderMouse(fakeFormPos, wum);
345 if (m_formFakeDropTarget && dropTarget != m_formFakeDropTarget)
346 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Restore);
348 m_formFakeDropTarget = dropTarget;
349 if (m_formFakeDropTarget)
350 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Highlight);
353 if (isDragEnter || m_formFakeDropTarget)
354 mimeData->acceptEvent(event);
358void ObjectInspector::ObjectInspectorPrivate::dropEvent (QDropEvent * event)
360 if (!m_formWindow || !m_formFakeDropTarget) {
365 const QDesignerMimeData *mimeData = qobject_cast<
const QDesignerMimeData *>(event->mimeData());
370 const QPoint fakeGlobalDropFormPos = m_formFakeDropTarget->mapToGlobal(dropPointOffset(m_formWindow , m_formFakeDropTarget));
371 mimeData->moveDecoration(fakeGlobalDropFormPos + mimeData->hotSpot());
372 if (!m_formWindow->dropWidgets(mimeData->items(), m_formFakeDropTarget, fakeGlobalDropFormPos)) {
376 mimeData->acceptEvent(event);
379QModelIndexList ObjectInspector::ObjectInspectorPrivate::indexesOf(QObject *o)
const
381 QModelIndexList result;
382 const auto srcIndexes = m_model->indexesOf(o);
383 if (!srcIndexes.isEmpty()) {
384 result.reserve(srcIndexes.size());
385 for (
const auto &srcIndex : srcIndexes)
386 result.append(m_filterModel->mapFromSource(srcIndex));
391QObject *ObjectInspector::ObjectInspectorPrivate::objectAt(
const QModelIndex &index)
const
393 return m_model->objectAt(m_filterModel->mapToSource(index));
396bool ObjectInspector::ObjectInspectorPrivate::selectObject(QObject *o)
398 if (!m_core->metaDataBase()->item(o))
401 using ModelIndexSet = QSet<QModelIndex>;
403 const QModelIndexList objectIndexes = indexesOf(o);
404 if (objectIndexes.isEmpty())
407 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
408 const auto currentSelectedItemList = selectionModel->selectedRows(0);
409 const ModelIndexSet currentSelectedItems(currentSelectedItemList.cbegin(), currentSelectedItemList.cend());
412 if (!currentSelectedItems.isEmpty()
413 && currentSelectedItems == ModelIndexSet(objectIndexes.cbegin(), objectIndexes.cend())) {
418 selectIndexRange(objectIndexes, MakeCurrent);
422void ObjectInspector::ObjectInspectorPrivate::selectIndexRange(
const QModelIndexList &indexes,
unsigned flags)
424 if (indexes.isEmpty())
427 QItemSelectionModel::SelectionFlags selectFlags = QItemSelectionModel::Select|QItemSelectionModel::Rows;
428 if (!(flags & AddToSelection))
429 selectFlags |= QItemSelectionModel::Clear;
430 if (flags & MakeCurrent)
431 selectFlags |= QItemSelectionModel::Current;
433 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
434 for (
const auto &mi : indexes) {
435 if (mi.column() == 0) {
436 selectionModel->select(mi, selectFlags);
437 selectFlags &= ~(QItemSelectionModel::Clear|QItemSelectionModel::Current);
440 if (flags & MakeCurrent)
441 m_treeView->scrollTo(indexes.constFirst(), QAbstractItemView::EnsureVisible);
444void ObjectInspector::ObjectInspectorPrivate::clear()
446 m_formFakeDropTarget =
nullptr;
447 m_formWindow =
nullptr;
451static inline bool mainContainerIsCurrent(
const QDesignerFormWindowInterface *fw)
453 const QDesignerFormWindowCursorInterface *cursor = fw->cursor();
454 if (cursor->selectedWidgetCount() > 1)
456 const QWidget *current = cursor->current();
457 return current == fw || current == fw->mainContainer();
460void ObjectInspector::ObjectInspectorPrivate::setFormWindow(QDesignerFormWindowInterface *fwi)
462 const bool blocked = m_treeView->selectionModel()->blockSignals(
true);
464 UpdateBlocker ub(m_treeView);
465 setFormWindowBlocked(fwi);
468 m_treeView->update();
469 m_treeView->selectionModel()->blockSignals(blocked);
472void ObjectInspector::ObjectInspectorPrivate::setFormWindowBlocked(QDesignerFormWindowInterface *fwi)
474 FormWindowBase *fw = qobject_cast<FormWindowBase *>(fwi);
475 const bool formWindowChanged = m_formWindow != fw;
479 const int oldWidth = m_treeView->columnWidth(0);
480 const int xoffset = m_treeView->horizontalScrollBar()->value();
481 const int yoffset = m_treeView->verticalScrollBar()->value();
483 if (formWindowChanged)
484 m_formFakeDropTarget =
nullptr;
486 switch (m_model->update(m_formWindow)) {
487 case ObjectInspectorModel::NoForm:
490 case ObjectInspectorModel::Rebuilt:
491 applyCursorSelection();
492 m_treeView->expandAll();
493 if (formWindowChanged) {
494 m_treeView->resizeColumnToContents(0);
496 m_treeView->setColumnWidth(0, oldWidth);
497 m_treeView->horizontalScrollBar()->setValue(xoffset);
498 m_treeView->verticalScrollBar()->setValue(yoffset);
501 case ObjectInspectorModel::Updated: {
507 bool applySelection = !mainContainerIsCurrent(m_formWindow);
508 if (!applySelection) {
509 const QModelIndexList currentIndexes = m_treeView->selectionModel()->selectedRows(0);
510 if (currentIndexes.isEmpty()) {
511 applySelection =
true;
513 applySelection = selectionType(m_formWindow, objectAt(currentIndexes.constFirst())) == ManagedWidgetSelection;
517 applyCursorSelection();
524void ObjectInspector::ObjectInspectorPrivate::applyCursorSelection()
526 const QDesignerFormWindowCursorInterface *cursor = m_formWindow->cursor();
527 const int count = cursor->selectedWidgetCount();
532 QWidget *currentWidget = cursor->current();
534 selectIndexRange(indexesOf(currentWidget), MakeCurrent);
536 m_treeView->selectionModel()->clearSelection();
538 for (
int i = 0;i < count; i++) {
539 QWidget *widget = cursor->selectedWidget(i);
540 if (widget != currentWidget)
541 selectIndexRange(indexesOf(widget), AddToSelection);
546static int selectInCursor(FormWindowBase *fw,
const QObjectList &objects,
bool value)
549 const bool blocked = fw->blockSelectionChanged(
true);
550 for (
auto *o : objects) {
551 if (selectionType(fw, o) == ManagedWidgetSelection) {
552 fw->selectWidget(
static_cast<QWidget *>(o), value);
556 fw->blockSelectionChanged(blocked);
560void ObjectInspector::ObjectInspectorPrivate::slotSelectionChanged(
const QItemSelection &selected,
const QItemSelection &deselected)
563 synchronizeSelection(selected, deselected);
564 QMetaObject::invokeMethod(m_core->formWindowManager(),
"slotUpdateActions");
570QObjectList ObjectInspector::ObjectInspectorPrivate::indexesToObjects(
const QModelIndexList &indexes)
const
573 if (indexes.isEmpty())
575 rc.reserve(indexes.size());
576 for (
const auto &mi : indexes) {
577 if (mi.column() == 0)
578 rc.append(objectAt(mi));
585bool ObjectInspector::ObjectInspectorPrivate::checkManagedWidgetSelection(
const QModelIndexList &rowSelection)
587 bool isManagedWidgetSelection =
false;
588 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
589 for (
const auto &mi : rowSelection) {
590 QObject *object = objectAt(mi);
591 if (selectionType(m_formWindow, object) == ManagedWidgetSelection) {
592 isManagedWidgetSelection =
true;
597 if (!isManagedWidgetSelection)
600 const bool blocked = selectionModel->blockSignals(
true);
601 for (
const auto &mi : rowSelection) {
602 QObject *object = objectAt(mi);
603 if (selectionType(m_formWindow, object) != ManagedWidgetSelection)
604 selectionModel->select(mi, QItemSelectionModel::Deselect|QItemSelectionModel::Rows);
606 selectionModel->blockSignals(blocked);
610void ObjectInspector::ObjectInspectorPrivate::synchronizeSelection(
const QItemSelection & selectedSelection,
const QItemSelection &deselectedSelection)
613 const QObjectList deselected = indexesToObjects(deselectedSelection.indexes());
614 const QObjectList newlySelected = indexesToObjects(selectedSelection.indexes());
616 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
618 int deselectedManagedWidgetCount = 0;
619 if (!deselected.isEmpty())
620 deselectedManagedWidgetCount = selectInCursor(m_formWindow, deselected,
false);
622 if (newlySelected.isEmpty()) {
623 if (currentSelectedIndexes.isEmpty())
624 m_formWindow->clearSelection(!m_withinClearSelection);
628 const int selectManagedWidgetCount = selectInCursor(m_formWindow, newlySelected,
true);
632 if (selectManagedWidgetCount == 0) {
633 if (checkManagedWidgetSelection(currentSelectedIndexes)) {
635 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
636 m_formWindow->emitSelectionChanged();
640 m_formWindow->clearSelection(
false);
641 QObject *unmanagedObject = newlySelected.constFirst();
642 m_core->propertyEditor()->setObject(unmanagedObject);
643 m_core->propertyEditor()->setEnabled(
true);
645 if (newlySelected.size() == 1 && unmanagedObject->isWidgetType())
646 showContainersCurrentPage(
static_cast<QWidget*>(unmanagedObject));
650 if (newlySelected.size() == 1) {
651 QObject *object = newlySelected.constFirst();
652 if (object->isWidgetType())
653 showContainersCurrentPage(
static_cast<QWidget*>(object));
658 if (currentSelectedIndexes.size() > selectManagedWidgetCount)
659 checkManagedWidgetSelection(currentSelectedIndexes);
661 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
662 m_formWindow->emitSelectionChanged();
666void ObjectInspector::ObjectInspectorPrivate::getSelection(Selection &s)
const
673 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
674 if (currentSelectedIndexes.isEmpty())
678 for (
const QModelIndex &index : currentSelectedIndexes) {
679 if (QObject *object = objectAt(index)) {
680 switch (selectionType(m_formWindow, object)) {
683 case QObjectSelection:
686 if (!s.objects.contains(object))
687 s.objects.push_back(object);
689 case UnmanagedWidgetSelection:
690 s.unmanaged.push_back(qobject_cast<QWidget *>(object));
692 case ManagedWidgetSelection:
693 s.managed.push_back(qobject_cast<QWidget *>(object));
701static inline QMenu *createTaskMenu(QObject *object, QDesignerFormWindowInterface *fw)
704 if (!object->isWidgetType())
705 return FormWindowBase::createExtensionTaskMenu(fw, object,
false);
707 QWidget *w =
static_cast<QWidget *>(object);
708 if (!fw->isManaged(w))
709 return FormWindowBase::createExtensionTaskMenu(fw, w,
false);
711 if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase*>(fw))
712 return fwb->initializePopupMenu(w);
716void ObjectInspector::ObjectInspectorPrivate::slotPopupContextMenu(QWidget * ,
const QPoint &pos)
718 if (m_formWindow ==
nullptr || m_formWindow->currentTool() != 0)
721 if (QObject *object = objectAt(m_treeView->indexAt(pos))) {
722 if (QMenu *menu = createTaskMenu(object, m_formWindow)) {
723 menu->exec(m_treeView->viewport()->mapToGlobal(pos));
730ObjectInspector::ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) :
731 QDesignerObjectInspector(parent),
732 m_impl(
new ObjectInspectorPrivate(core))
734 QVBoxLayout *vbox =
new QVBoxLayout(
this);
735 vbox->setContentsMargins(QMargins());
737 vbox->addWidget(m_impl->filterLineEdit());
738 QTreeView *treeView = m_impl->treeView();
739 vbox->addWidget(treeView);
741 connect(treeView, &QWidget::customContextMenuRequested,
742 this, &ObjectInspector::slotPopupContextMenu);
744 connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
745 this, &ObjectInspector::slotSelectionChanged);
747 connect(treeView->header(), &QHeaderView::sectionDoubleClicked,
748 this, &ObjectInspector::slotHeaderDoubleClicked);
749 setAcceptDrops(
true);
752ObjectInspector::~ObjectInspector()
757QDesignerFormEditorInterface *ObjectInspector::core()
const
759 return m_impl->core();
762void ObjectInspector::slotPopupContextMenu(
const QPoint &pos)
764 m_impl->slotPopupContextMenu(
this, pos);
767void ObjectInspector::setFormWindow(QDesignerFormWindowInterface *fwi)
769 m_impl->setFormWindow(fwi);
772void ObjectInspector::slotSelectionChanged(
const QItemSelection & selected,
const QItemSelection &deselected)
774 m_impl->slotSelectionChanged(selected, deselected);
777void ObjectInspector::getSelection(Selection &s)
const
779 m_impl->getSelection(s);
782bool ObjectInspector::selectObject(QObject *o)
784 return m_impl->selectObject(o);
787void ObjectInspector::clearSelection()
789 m_impl->clearSelection();
792void ObjectInspector::slotHeaderDoubleClicked(
int column)
794 m_impl->slotHeaderDoubleClicked(column);
797void ObjectInspector::mainContainerChanged()
800 if (sender() == m_impl->formWindow())
801 setFormWindow(
nullptr);
804void ObjectInspector::dragEnterEvent (QDragEnterEvent * event)
806 m_impl->handleDragEnterMoveEvent(
this, event,
true);
809void ObjectInspector::dragMoveEvent(QDragMoveEvent * event)
811 m_impl->handleDragEnterMoveEvent(
this, event,
false);
814void ObjectInspector::dragLeaveEvent(QDragLeaveEvent * )
816 m_impl->restoreDropHighlighting();
819void ObjectInspector::dropEvent (QDropEvent * event)
821 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)