17#include <QtDesigner/abstractformwindow.h>
18#include <QtDesigner/abstractformeditor.h>
19#include <QtDesigner/abstractwidgetfactory.h>
20#include <QtDesigner/qextensionmanager.h>
22#include <QtCore/qmimedata.h>
24#include <QtCore/qdebug.h>
26#include <QtWidgets/qapplication.h>
27#include <QtGui/qdrag.h>
28#include <QtWidgets/qlineedit.h>
29#include <QtGui/qpainter.h>
30#include <QtGui/qevent.h>
34using namespace Qt::StringLiterals;
36using ActionList = QList<QAction *>;
60QDesignerMenuBar::QDesignerMenuBar(QWidget *parent) :
62 m_addMenu(
new qdesigner_internal::SpecialMenuAction(
this)),
63 m_editor(
new qdesigner_internal::MenuActionLineEdit(
this)),
64 m_promotionTaskMenu(
new qdesigner_internal::PromotionTaskMenu(
this, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget,
this))
66 setContextMenuPolicy(Qt::DefaultContextMenu);
70 setNativeMenuBar(
false);
72 m_addMenu->setText(tr(
"Type Here"));
76 italic.setItalic(
true);
77 m_addMenu->setFont(italic);
79 m_editor->setObjectName(u"__qt__passive_editor"_s);
81 connect(m_editor, &qdesigner_internal::MenuActionLineEdit::focusOut,
82 this, &QDesignerMenuBar::stopInlineEditing);
83 installEventFilter(
new qdesigner_internal::MenuBarEventFilter(
this));
86QDesignerMenuBar::~QDesignerMenuBar() =
default;
88void QDesignerMenuBar::paintEvent(QPaintEvent *event)
90 QMenuBar::paintEvent(event);
94 const auto &actionList = actions();
95 for (QAction *a : actionList) {
96 if (qobject_cast<qdesigner_internal::SpecialMenuAction*>(a)) {
97 const QRect g = actionGeometry(a);
98 QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom());
99 lg.setColorAt(0.0, Qt::transparent);
100 lg.setColorAt(0.7, QColor(0, 0, 0, 32));
101 lg.setColorAt(1.0, Qt::transparent);
107 QAction *action = currentAction();
109 if (m_dragging || !action)
113 const QRect g = actionGeometry(action);
114 QDesignerMenu::drawSelection(&p, g.adjusted(1, 1, -1, -1));
115 }
else if (action->menu() && action->menu()->isVisible()) {
116 const QRect g = actionGeometry(action);
117 p.drawRect(g.adjusted(1, 1, -1, -1));
121bool QDesignerMenuBar::handleEvent(QEvent *event)
126 switch (event->type()) {
127 case QEvent::MouseButtonDblClick:
128 return handleMouseDoubleClickEvent(
static_cast<QMouseEvent*>(event));
129 case QEvent::MouseButtonPress:
130 return handleMousePressEvent(
static_cast<QMouseEvent*>(event));
131 case QEvent::MouseButtonRelease:
132 return handleMouseReleaseEvent(
static_cast<QMouseEvent*>(event));
133 case QEvent::MouseMove:
134 return handleMouseMoveEvent(
static_cast<QMouseEvent*>(event));
135 case QEvent::ContextMenu:
136 return handleContextMenuEvent(
static_cast<QContextMenuEvent*>(event));
137 case QEvent::KeyPress:
138 return handleKeyPressEvent(
static_cast<QKeyEvent*>(event));
139 case QEvent::FocusIn:
140 case QEvent::FocusOut:
150bool QDesignerMenuBar::handleMouseDoubleClickEvent(QMouseEvent *event)
152 if (!rect().contains(event->position().toPoint()))
155 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
160 m_startPosition = QPoint();
162 m_currentIndex = actionIndexAt(
this, event->position().toPoint(), Qt::Horizontal);
163 if (m_currentIndex != -1) {
170bool QDesignerMenuBar::handleKeyPressEvent(QKeyEvent *e)
172 if (m_editor->isHidden()) {
176 if (m_currentIndex == -1 || m_currentIndex >= realActionCount())
184 moveLeft(e->modifiers() & Qt::ControlModifier);
189 moveRight(e->modifiers() & Qt::ControlModifier);
206 case Qt::Key_PageDown:
207 m_currentIndex = actions().size() - 1;
218 case Qt::Key_Control:
225 if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) {
227 QApplication::sendEvent(m_editor, e);
239 case Qt::Key_Control:
245 if (!m_editor->text().isEmpty()) {
246 leaveEditMode(ForceAccept);
247 if (m_lastFocusWidget)
248 m_lastFocusWidget->setFocus();
269void QDesignerMenuBar::startDrag(
const QPoint &pos)
271 using namespace qdesigner_internal;
273 const int index = findAction(pos);
274 if (m_currentIndex == -1 || index >= realActionCount())
277 QAction *action = safeActionAt(index);
279 QDesignerFormWindowInterface *fw = formWindow();
280 auto *cmd =
new qdesigner_internal::RemoveActionFromCommand(fw);
281 cmd->init(
this, action, actions().at(index + 1));
282 fw->commandHistory()->push(cmd);
288 QDrag *drag =
new QDrag(
this);
289 drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action));
290 drag->setMimeData(
new ActionRepositoryMimeData(action, Qt::MoveAction));
292 const int old_index = m_currentIndex;
295 if (drag->exec(Qt::MoveAction) == Qt::IgnoreAction) {
296 auto *cmd =
new qdesigner_internal::InsertActionIntoCommand(fw);
297 cmd->init(
this, action, safeActionAt(index));
298 fw->commandHistory()->push(cmd);
300 m_currentIndex = old_index;
305bool QDesignerMenuBar::handleMousePressEvent(QMouseEvent *event)
307 m_startPosition = QPoint();
310 if (event->button() != Qt::LeftButton)
313 m_startPosition = event->position().toPoint();
314 const int newIndex = actionIndexAt(
this, m_startPosition, Qt::Horizontal);
315 const bool changed = newIndex != m_currentIndex;
316 m_currentIndex = newIndex;
317 updateCurrentAction(changed);
322bool QDesignerMenuBar::handleMouseReleaseEvent(QMouseEvent *event)
324 m_startPosition = QPoint();
326 if (event->button() != Qt::LeftButton)
330 m_currentIndex = actionIndexAt(
this, event->position().toPoint(), Qt::Horizontal);
331 if (!m_editor->isVisible() && m_currentIndex != -1 && m_currentIndex < realActionCount())
337bool QDesignerMenuBar::handleMouseMoveEvent(QMouseEvent *event)
339 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
342 if (m_startPosition.isNull())
345 const QPoint pos = mapFromGlobal(event->globalPosition().toPoint());
347 if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance())
350 const int index = actionIndexAt(
this, m_startPosition, Qt::Horizontal);
351 if (index < actions().size()) {
356 startDrag(m_startPosition);
357 m_startPosition = QPoint();
362ActionList QDesignerMenuBar::contextMenuActions()
364 using namespace qdesigner_internal;
367 if (QAction *action = safeActionAt(m_currentIndex)) {
368 if (!qobject_cast<SpecialMenuAction*>(action)) {
370 itemData.setValue(action);
372 QAction *remove_action =
new QAction(tr(
"Remove Menu '%1'").arg(action->menu()->objectName()),
nullptr);
373 remove_action->setData(itemData);
374 connect(remove_action, &QAction::triggered,
this, &QDesignerMenuBar::deleteMenu);
375 rc.push_back(remove_action);
376 QAction *sep =
new QAction(
nullptr);
377 sep->setSeparator(
true);
382 m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::TrailingSeparator, rc);
384 QAction *remove_menubar =
new QAction(tr(
"Remove Menu Bar"),
nullptr);
385 connect(remove_menubar, &QAction::triggered,
this, &QDesignerMenuBar::slotRemoveMenuBar);
386 rc.push_back(remove_menubar);
390bool QDesignerMenuBar::handleContextMenuEvent(QContextMenuEvent *event)
394 m_currentIndex = actionIndexAt(
this, mapFromGlobal(event->globalPos()), Qt::Horizontal);
399 const ActionList al = contextMenuActions();
402 menu.exec(event->globalPos());
406void QDesignerMenuBar::slotRemoveMenuBar()
408 Q_ASSERT(formWindow() !=
nullptr);
410 QDesignerFormWindowInterface *fw = formWindow();
412 auto *cmd =
new qdesigner_internal::DeleteMenuBarCommand(fw);
414 fw->commandHistory()->push(cmd);
417void QDesignerMenuBar::stopInlineEditing()
419 if (!m_editor->isHidden()) {
420 leaveEditMode(Default);
426void QDesignerMenuBar::focusOutEvent(QFocusEvent *event)
428 QMenuBar::focusOutEvent(event);
431void QDesignerMenuBar::enterEditMode()
433 if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) {
438void QDesignerMenuBar::leaveEditMode(LeaveEditMode mode)
440 using namespace qdesigner_internal;
442 m_editor->releaseKeyboard();
447 if (m_editor->text().isEmpty())
450 QAction *action =
nullptr;
452 QDesignerFormWindowInterface *fw = formWindow();
455 if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) {
456 action = safeActionAt(m_currentIndex);
457 fw->beginCommand(QApplication::translate(
"Command",
"Change Title"));
459 fw->beginCommand(QApplication::translate(
"Command",
"Insert Menu"));
460 const QString niceObjectName = ActionEditor::actionTextToName(m_editor->text(), u"menu"_s);
461 QMenu *menu = qobject_cast<QMenu*>(fw->core()->widgetFactory()->createWidget(u"QMenu"_s,
this));
462 fw->core()->widgetFactory()->initialize(menu);
463 menu->setObjectName(niceObjectName);
464 menu->setTitle(tr(
"Menu"));
465 fw->ensureUniqueObjectName(menu);
466 action = menu->menuAction();
467 auto *cmd =
new qdesigner_internal::AddMenuActionCommand(fw);
468 cmd->init(action, m_addMenu,
this,
this);
469 fw->commandHistory()->push(cmd);
472 auto *cmd =
new qdesigner_internal::SetPropertyCommand(fw);
473 cmd->init(action, u"text"_s, m_editor->text());
474 fw->commandHistory()->push(cmd);
478void QDesignerMenuBar::showLineEdit()
480 QAction *action =
nullptr;
482 if (m_currentIndex >= 0 && m_currentIndex < realActionCount())
483 action = safeActionAt(m_currentIndex);
487 if (action->isSeparator())
492 m_lastFocusWidget = qApp->focusWidget();
495 const QString text = action != m_addMenu ? action->text() : QString();
497 m_editor->setText(text);
498 m_editor->selectAll();
499 m_editor->setGeometry(actionGeometry(action));
501 m_editor->activateWindow();
502 m_editor->setFocus();
503 m_editor->grabKeyboard();
508 switch (event->type()) {
509 case QEvent::KeyPress:
510 case QEvent::KeyRelease:
511 case QEvent::ContextMenu:
512 case QEvent::MouseMove:
513 case QEvent::MouseButtonPress:
514 case QEvent::MouseButtonRelease:
515 case QEvent::MouseButtonDblClick:
518 case QEvent::FocusIn:
519 case QEvent::FocusOut:
520 if (
auto *mb = qobject_cast<QDesignerMenuBar *>(object))
521 return mb->handleEvent(event);
523 case QEvent::Shortcut:
530 return QObject::eventFilter(object, event);
533int QDesignerMenuBar::findAction(
const QPoint &pos)
const
535 const int index = actionIndexAt(
this, pos, Qt::Horizontal);
537 return realActionCount();
542void QDesignerMenuBar::adjustIndicator(
const QPoint &pos)
544 const int index = findAction(pos);
545 QAction *action = safeActionAt(index);
546 Q_ASSERT(action !=
nullptr);
548 if (pos != QPoint(-1, -1)) {
549 QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
550 if (!m || m->parentMenu()) {
551 m_currentIndex = index;
556 if (QDesignerActionProviderExtension *a = actionProvider()) {
557 a->adjustIndicator(pos);
561QDesignerMenuBar::ActionDragCheck QDesignerMenuBar::checkAction(QAction *action)
const
564 if (!action || !qdesigner_internal::Utils::isObjectAncestorOf(formWindow()->mainContainer(), action))
568 return ActionDragOnSubMenu;
570 QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
571 if (m && m->parentMenu())
572 return ActionDragOnSubMenu;
574 if (actions().contains(action))
575 return ActionDragOnSubMenu;
577 return AcceptActionDrag;
580void QDesignerMenuBar::dragEnterEvent(QDragEnterEvent *event)
582 auto *d = qobject_cast<
const qdesigner_internal::ActionRepositoryMimeData*>(event->mimeData());
583 if (!d || d->actionList().isEmpty()) {
588 QAction *action = d->actionList().first();
589 switch (checkAction(action)) {
593 case ActionDragOnSubMenu:
597 case AcceptActionDrag:
600 adjustIndicator(event->position().toPoint());
605void QDesignerMenuBar::dragMoveEvent(QDragMoveEvent *event)
607 auto *d = qobject_cast<
const qdesigner_internal::ActionRepositoryMimeData*>(event->mimeData());
608 if (!d || d->actionList().isEmpty()) {
612 QAction *action = d->actionList().first();
614 switch (checkAction(action)) {
618 case ActionDragOnSubMenu:
620 showMenu(findAction(event->position().toPoint()));
622 case AcceptActionDrag:
624 adjustIndicator(event->position().toPoint());
629void QDesignerMenuBar::dragLeaveEvent(QDragLeaveEvent *)
633 adjustIndicator(QPoint(-1, -1));
636void QDesignerMenuBar::dropEvent(QDropEvent *event)
640 if (
auto *d = qobject_cast<
const qdesigner_internal::ActionRepositoryMimeData*>(event->mimeData())) {
642 QAction *action = d->actionList().first();
643 if (checkAction(action) == AcceptActionDrag) {
644 event->acceptProposedAction();
645 int index = findAction(event->position().toPoint());
646 index = qMin(index, actions().size() - 1);
648 QDesignerFormWindowInterface *fw = formWindow();
649 auto *cmd =
new qdesigner_internal::InsertActionIntoCommand(fw);
650 cmd->init(
this, action, safeActionAt(index));
651 fw->commandHistory()->push(cmd);
653 m_currentIndex = index;
655 adjustIndicator(QPoint(-1, -1));
662void QDesignerMenuBar::actionEvent(QActionEvent *event)
664 QMenuBar::actionEvent(event);
667QDesignerFormWindowInterface *QDesignerMenuBar::formWindow()
const
669 return QDesignerFormWindowInterface::findFormWindow(
const_cast<QDesignerMenuBar*>(
this));
672QDesignerActionProviderExtension *QDesignerMenuBar::actionProvider()
674 if (QDesignerFormWindowInterface *fw = formWindow()) {
675 QDesignerFormEditorInterface *core = fw->core();
676 return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(),
this);
682QAction *QDesignerMenuBar::currentAction()
const
684 if (m_currentIndex < 0 || m_currentIndex >= actions().size())
687 return safeActionAt(m_currentIndex);
690int QDesignerMenuBar::realActionCount()
const
692 return actions().size() - 1;
695bool QDesignerMenuBar::dragging()
const
700void QDesignerMenuBar::moveLeft(
bool ctrl)
702 if (layoutDirection() == Qt::LeftToRight) {
709void QDesignerMenuBar::moveRight(
bool ctrl)
711 if (layoutDirection() == Qt::LeftToRight) {
718void QDesignerMenuBar::movePrevious(
bool ctrl)
720 const bool swapped = ctrl && swapActions(m_currentIndex, m_currentIndex - 1);
721 const int newIndex = qMax(0, m_currentIndex - 1);
723 if (swapped || newIndex != m_currentIndex) {
724 m_currentIndex = newIndex;
725 updateCurrentAction(
true);
729void QDesignerMenuBar::moveNext(
bool ctrl)
731 const bool swapped = ctrl && swapActions(m_currentIndex + 1, m_currentIndex);
732 const int newIndex = qMin(actions().size() - 1, m_currentIndex + 1);
733 if (swapped || newIndex != m_currentIndex) {
734 m_currentIndex = newIndex;
735 updateCurrentAction(!ctrl);
739void QDesignerMenuBar::moveUp()
744void QDesignerMenuBar::moveDown()
749void QDesignerMenuBar::adjustSpecialActions()
751 removeAction(m_addMenu);
752 addAction(m_addMenu);
755void QDesignerMenuBar::hideMenu(
int index)
757 if (index < 0 && m_currentIndex >= 0)
758 index = m_currentIndex;
760 if (index < 0 || index >= realActionCount())
763 QAction *action = safeActionAt(index);
765 if (action && action->menu()) {
766 action->menu()->hide();
768 if (QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu())) {
769 menu->closeMenuChain();
774void QDesignerMenuBar::deleteMenu()
776 deleteMenuAction(currentAction());
779void QDesignerMenuBar::deleteMenuAction(QAction *action)
781 if (action && !qobject_cast<qdesigner_internal::SpecialMenuAction*>(action)) {
782 const int pos = actions().indexOf(action);
783 QAction *action_before =
nullptr;
785 action_before = safeActionAt(pos + 1);
787 QDesignerFormWindowInterface *fw = formWindow();
788 auto *cmd =
new qdesigner_internal::RemoveMenuActionCommand(fw);
789 cmd->init(action, action_before,
this,
this);
790 fw->commandHistory()->push(cmd);
794void QDesignerMenuBar::showMenu(
int index)
796 if (index < 0 && m_currentIndex >= 0)
797 index = m_currentIndex;
799 if (index < 0 || index >= realActionCount())
802 m_currentIndex = index;
803 QAction *action = currentAction();
805 if (action && action->menu()) {
806 if (m_lastMenuActionIndex != -1 && m_lastMenuActionIndex != index) {
807 hideMenu(m_lastMenuActionIndex);
810 m_lastMenuActionIndex = index;
811 QMenu *menu = action->menu();
812 const QRect g = actionGeometry(action);
814 if (!menu->isVisible()) {
815 if ((menu->windowFlags() & Qt::Popup) != Qt::Popup)
816 menu->setWindowFlags(Qt::Popup);
818 if (layoutDirection() == Qt::LeftToRight) {
819 menu->move(mapToGlobal(g.bottomLeft()));
823 QPoint point = g.bottomRight() - QPoint(menu->width(), 0);
824 menu->move(mapToGlobal(point));
826 menu->setFocus(Qt::MouseFocusReason);
835QAction *QDesignerMenuBar::safeActionAt(
int index)
const
837 if (index < 0 || index >= actions().size())
840 return actions().at(index);
843bool QDesignerMenuBar::swapActions(
int a,
int b)
845 using namespace qdesigner_internal;
847 const int left = qMin(a, b);
848 int right = qMax(a, b);
850 QAction *action_a = safeActionAt(left);
851 QAction *action_b = safeActionAt(right);
853 if (action_a == action_b
856 || qobject_cast<SpecialMenuAction*>(action_a)
857 || qobject_cast<SpecialMenuAction*>(action_b))
860 right = qMin(right, realActionCount());
864 formWindow()->beginCommand(QApplication::translate(
"Command",
"Move action"));
866 QAction *action_b_before = safeActionAt(right + 1);
868 QDesignerFormWindowInterface *fw = formWindow();
869 auto *cmd1 =
new qdesigner_internal::RemoveActionFromCommand(fw);
870 cmd1->init(
this, action_b, action_b_before,
false);
871 fw->commandHistory()->push(cmd1);
873 QAction *action_a_before = safeActionAt(left + 1);
875 auto *cmd2 =
new qdesigner_internal::InsertActionIntoCommand(fw);
876 cmd2->init(
this, action_b, action_a_before,
false);
877 fw->commandHistory()->push(cmd2);
879 auto *cmd3 =
new qdesigner_internal::RemoveActionFromCommand(fw);
880 cmd3->init(
this, action_a, action_b,
false);
881 fw->commandHistory()->push(cmd3);
883 auto *cmd4 =
new qdesigner_internal::InsertActionIntoCommand(fw);
884 cmd4->init(
this, action_a, action_b_before,
true);
885 fw->commandHistory()->push(cmd4);
892void QDesignerMenuBar::keyPressEvent(QKeyEvent *event)
897void QDesignerMenuBar::keyReleaseEvent(QKeyEvent *event)
902void QDesignerMenuBar::updateCurrentAction(
bool selectAction)
904 using namespace qdesigner_internal;
911 QAction *action = currentAction();
912 if (!action || action == m_addMenu)
915 QMenu *menu = action->menu();
919 QDesignerObjectInspector *oi =
nullptr;
920 if (QDesignerFormWindowInterface *fw = formWindow())
921 oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector());
926 oi->clearSelection();
927 oi->selectObject(menu);
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.