git mv Code\Sandbox\Editor Code/Editor
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHORTCUT_DISPATCHER_H
|
||||
#define SHORTCUT_DISPATCHER_H
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QObject>
|
||||
#endif
|
||||
|
||||
template<typename T> class QSet;
|
||||
template<typename T> class QList;
|
||||
template<typename T> class QPointer;
|
||||
|
||||
class QWidget;
|
||||
class QShortcutEvent;
|
||||
class QKeyEvent;
|
||||
|
||||
/* This class provides a workaround against Qt's buggy implementation of shortcut contexts when using dock widgets.
|
||||
|
||||
There are several problems with Qt's implementation:
|
||||
|
||||
- Qt::WindowShortcut doesn't work well with floating docking widgets.
|
||||
Qt gives a warning about ambiguous shortcut even though both actions are in different windows.
|
||||
- A Qt::WindowShortcut in a docked widget will conflict when the widget is embedded (not floating).
|
||||
- Qt::WidgetWithChildrenShortcut doesn't work well on menus since they can't have focus
|
||||
Implement our own conflict resolution and shortcut dispatch.
|
||||
Deliver to the action in the most inner scope, for our purposes a scope is either a window or a dockwidget (regardless of floating)
|
||||
|
||||
Beware that shortcut handling is complex and try not to change this class too much, as it's hard to test
|
||||
and hard to verify all edge cases.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
More documentation on Qt shortcuts
|
||||
-------------------------------------------
|
||||
|
||||
Here's some more detailed info regarding shortcuts in Qt. Not specific Open 3D Engine but
|
||||
useful as not explained in Qt docs much.
|
||||
|
||||
P.S.: The following text details the strategy used in an earlier Open 3D Engine version. Not sure which
|
||||
shortcut context type it uses nowadays, but eitherway, the following text is educational,
|
||||
and all the traps still exist in current Qt (5.11).
|
||||
|
||||
Some applications have a QMainWindow and also secondary main windows which can dock into the main QMainWindow.
|
||||
All these main windows have menu bars containing actions with shortcuts.
|
||||
|
||||
So, which shortcut context should be used ?
|
||||
|
||||
- Qt::ApplicationShortcut
|
||||
Obviously not, would create conflicting shortcuts and you only want local shortcuts anyway.
|
||||
|
||||
- Qt::WindowShortcut ?
|
||||
This is supposed to only work if the shortcut's parent is in the focused window. However,
|
||||
there a bug with floating dock widgets: doing a key sequence in the dock widget
|
||||
triggers the main QMainWindow's shortcut.
|
||||
|
||||
- Qt::WidgetShortcut
|
||||
docs say: "The shortcut is active when its parent widget has focus"
|
||||
The QAction's parent is a QMenuBar, which doesn't get focus, so this is useless.
|
||||
|
||||
- Qt::WidgetWithChildrenShortcut ?
|
||||
docs say: "The shortcut is active when its parent widget, or any of its children has focus"
|
||||
The QAction's parent is a QMenuBar, which doesn't get focus or has any focused children. Useless.
|
||||
|
||||
- Qt::WidgetWithChildrenShortcut (Round2!)
|
||||
Actually it's not the QAction's parent that counts but the associated widget! (Misleading docs)
|
||||
So if you also add the action to the window, it works:
|
||||
QAction *action = menu->addAction("Del");
|
||||
myWindow->addAction(action); // success!
|
||||
|
||||
Are we happy ?
|
||||
Not yet.
|
||||
|
||||
Qt::WidgetWithChildrenShortcut is working pretty well, but as soon as you dock your secondary QMainWindow into your main QMainWindow we get:
|
||||
"QAction::eventFilter: Ambiguous shortcut overload: Ctrl+O". Some widget inside main window 2 is focused, but it's also a child of main window 1 further up the hierarchy, so both QActions would apply.
|
||||
|
||||
So now what we need is simply a global event filter, catch QEvent::Shortcut, check if shortcut->isAmbiguous(), and if yes dispatch the shortcut
|
||||
manually (sendEvent), otherwise Qt would just bail out. To which QAction you send it to is up to you. I chose to imagine each dock widget was a scope and dispatch the shortcut to the most
|
||||
inner scope that contains the focused widget.
|
||||
|
||||
If you've read this far you can now press 'Ctrl+Q' and hope it closes your editor ;)
|
||||
*/
|
||||
|
||||
class ShortcutDispatcher
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ShortcutDispatcher(QObject* parent = nullptr);
|
||||
~ShortcutDispatcher();
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
static QWidget* focusWidget();
|
||||
|
||||
/// Assign the widget responsible for getting first attempt
|
||||
/// at every shortcut routed through the ShortcutDispatcher.
|
||||
void AttachOverride(QWidget* object);
|
||||
/// Detach the widget responsible for intercepting Actions
|
||||
/// routed through the ShortcutDispatcher.
|
||||
void DetachOverride();
|
||||
|
||||
private:
|
||||
bool shortcutFilter(QObject* obj, QShortcutEvent* ev);
|
||||
void setNewFocus(QObject* obj);
|
||||
|
||||
QWidget* FindParentScopeRoot(QWidget* w);
|
||||
bool IsAContainerForB(QWidget* a, QWidget* b);
|
||||
QList<QAction*> FindCandidateActions(QObject* scopeRoot, const QKeySequence& sequence, QSet<QObject*>& previouslyVisited, bool checkVisibility = true);
|
||||
bool FindCandidateActionAndFire(QObject* focusWidget, QShortcutEvent* shortcutEvent, QList<QAction*>& candidates, QSet<QObject*>& previouslyVisited);
|
||||
|
||||
static bool IsShortcutSearchBreak(QWidget* widget);
|
||||
|
||||
bool m_currentlyHandlingShortcut;
|
||||
|
||||
QString m_previousWidgetName;
|
||||
|
||||
QWidget* m_actionOverrideObject = nullptr; /**< (if set/not null) The widget responsible for getting first attempt
|
||||
* at every shortcut routed through the ShortcutDispatcher. */
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user