Automatically populate "Edit" menu in menubar with current focus widget context menu
I've been looking for ways to implement the "Edit" menu of Qt application. The "Edit" menu contains items such as "Copy", "Cut", "Paste", etc. and which need to forward to the currently active widget.
I can't seem to find a standard or elegant way to do this. According to this question, it's not possible:
How to implement the "Edit" menu with "Undo", "Cut", "Paste" and "Copy"?
I recently had the idea to trigger a context menu event on the current active widget when the "Edit" menu is shown, via:
// create menus in MainWindow con开发者_开发知识库structor
...
edit_menu = menuBar()->addMenu(tr("&Edit"));
connect(edit_menu, SIGNAL(aboutToShow()), this, SLOT(showEditMenu()));
...
// custom slot to handle the edit menu
void MainWindow::showEditMenu()
{
QWidget* w = QApplication::focusWidget();
// show the context menu of current focus widget in the menubar spot
QPoint global_pos = edit_menu->mapToGlobal(edit_menu->rect().bottomLeft());
QPoint pos = w->mapFromGlobal(global_pos);
QApplication::sendEvent(w, new QContextMenuEvent(QContextMenuEvent::Keyboard, pos, global_pos));
}
This shows the a context menu for the current widget great, but has some problems. For example, it takes focus away from the menubar, or if you click a different menubar item first, the menubar has focus, etc.
One partial solution would be to grab the context menu from the widget and copy it's items into the edit menu dynamically. Is there a way to do this?
Is there a better way build the edit menu in Qt?
Thanks for your help.
well, if you just need to create menu, you can always take actions from actions
of a widget. To create edit actions for widgets, you can do something like this:
void MainWindow::addActions (QWidget* widget)
{
QAction * copyAction = new QAction("copy",widget);
if(connect(copyAction,SIGNAL(triggered()),widget,SLOT(copy())))
{
widget->addAction(copyAction);
qDebug()<<"success connection";
}
}
and
foreach (QObject * obj, centralWidget()->children())
{
QWidget * w = dynamic_cast<QWidget*>(obj);
if (w)
addActions(w);
}
then you always can update actions of edit menu with focused widget's actions
This may be not elegant, but it better, than imporssible. The main bad assumption in example is that copy slot is named copy
An elegant solution I guess would be to have a base class for the widgets you need to have copy/paste/... functionality, and have them register themselves with some parent class upon becoming active and unregistering when being deactivated. The actions can then just be connected to slots in the main window, which forwards them to the registered widget. You could even gray out the menu items if no widget is currently registered (e.g. because the active widget does not have the required functionality).
An example for registering/unregistering (untested):
class ActionWidget;
class ActionWidgetManager
{
public:
ActionWidgetManager() : actionWidget_(0){}
void registerWidget(ActionWidget* widget){ actionWidget_ = widget; }
void unregisterWidget(ActionWidget* widget)
{ if (actionWidget_ == widget) actionWidget_ = 0; }
bool hasActiveWidget() const{ return actionWidget_ != 0; }
ActionWidget* getActiveWidget(){ return actionWidget_; }
private:
ActionWidget* actionWidget_;
};
class ActionWidget : public QWidget
{
public:
ActionWidget(ActionWidgetManager* manager, QWidget* parent=0)
: manager_(manager), QWidget(parent) {}
~ActionWidget(){ manager_->unregisterWidget(this); }
void Widget::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if(event->type() == QEvent::ActivationChange){
if(isActiveWindow()) {
manager_->registerWidget(this);
}
else {
manager_->unregisterWidget(this);
}
}
}
virtual void doCopy() = 0;
virtual void doPaste() = 0;
virtual void doUndo() = 0;
virtual void doCut() = 0;
private:
ActionWidgetManager* manager_;
};
Or something equivalent using signals and slots.
精彩评论