I'm having an issue with my QML program. In the top-level main.qml file, I have some keyboard Shortcuts like so:
Shortcut {
sequence: "Up"
onActivated: {
// yadda yadda
}
}
In another file, I have several Keys.onPressed calls like this:
Keys.onPressed: {
if (event.key == Qt.Key_Up) {
// yadda yadda
}
}
Apparently, the Shortcuts are interfering with the OnPressed calls. When I comment out the Shortcuts, the OnPressed's work fine. But when they're both active, it seems the Shortcut intercepts the keyboard press and prevents the OnPressed from activating.
I know that with mouse events, Qt has the "accepted" variable. If you want an event to continue propagating down the stack, you can just set "accepted = false" in the OnActivated function in order to accomplish this. However, I am not seeing any equivalent "accepted" variable in the Shortcuts API. Is there some other way I can ensure that the event is propagated correctly?
In order to act like a shortcut, Shortcut must have a higher priority than key handlers in active focus items. Otherwise it would be no different to a normal key handler. However, sometimes there is a need to override a shortcut, like you do.
In Qt 5.8 and earlier, you can disable a Shortcut to prevent it processing shortcut events under certain conditions. For example:
Shortcut {
enabled: !someItem.activeFocus
}
In Qt 5.9, a better mechanism has been introduced for this. Active focus items with key handlers can now override shortcuts by accepting shortcut override events using Keys.shortcutOverride. For example:
Item {
focus: true
Keys.onShortcutOverride: {
if (event.key == Qt.Key_Up)
event.accepted = true
}
Keys.onUpPressed: ...
}
Related
I am trying to add a keyboard shortcut to my QML but I am having a hard time getting it working without repeating logic. Below is the code,
Controls.Button {
id:sendAction
Shortcut {
sequence: "Ctrl+Return"
onActivated: parent.trigger()
}
onPressed: {call function}
}
If I repeat the calling of the function, the keyboard shortcut works but with parent.trigger() it fails with trigger isn't a property of the button. I have tried looking up what functions can be called in a shortcut to trigger the parent but the documentation is quite light. Essentially what I need though is what to add in to onActivated to trigger the onPressed without repeating the function call.
have you tried the pressed signal?
onActivated: parent.pressed()
https://doc.qt.io/qt-5/qml-qtquick-controls2-abstractbutton.html#pressed-signal
Solved with the code below.
Item {
Shortcut{
id: sendShortcut
sequences: ["Ctrl+Enter", "Ctrl+Return"]
onActivated: sendAction.action.trigger()
}
}
Controls.Button {
id:sendAction
action: Controls.Action {
onTriggered{
call function
}
}
onPressed: action.trigger()
}
In QML only a single object can have keyboard focus (per window). In my application, I need the option of having multiple objects with keyboard focus, thus I use a custom event dispatcher in combination with a custom multiple selection implementation.
The problem is however that every time any of the stock Control elements are clicked, they automatically steal the focus, breaking the custom event dispatcher.
In addition to that, it still needs to be possible to explicitly set another focus item, in the case of overlay popups and such.
I'm not sure how it fits in with your custom event stuff, but this answer might also help others who have found your question but are simply looking to prevent a control from getting focus.
You can prevent controls from getting focus with the focusPolicy enum:
Button {
focusPolicy: Qt.NoFocus
// Other options:
// focusPolicy: Qt.TabFocus - The control accepts focus by tabbing.
// focusPolicy: Qt.ClickFocus - The control accepts focus by clicking.
// focusPolicy: Qt.StrongFocus - The control accepts focus by both tabbing and clicking.
// focusPolicy: Qt.WheelFocus - The control accepts focus by tabbing, clicking, and using the mouse wheel.
}
I ended up with this interface, applied to all focus-able items:
Item {
onFocusChanged: if (keepFocus) focus = true
property bool keepFocus: false
property Item prevFocus: null
function getFocus() {
if (prevFocus) {
prevFocus.keepFocus = false
keepFocus = true
focus = true
}
}
function restoreFocus() {
if (prevFocus) {
keepFocus = false
prevFocus.keepFocus = true
prevFocus.focus = true
}
}
}
Since only overlay dialogs are supposed to take focus from the event dispatcher, the dialog base type automatically handles the acquiring and restoring of focus on dialog show and hide respectively.
So from "one item may have focus" I move to a "one item may have explicit focus", causing the focus to be re-enabled for that item whenever a Control element might steal it.
I want to use a press-and-hold behaviour to switch states of a gui Item.
I use a FocusScope(below) to recieve keyboard events.
FocusScope{
id:pageFocus
property var pedalKey//a key id
Keys.enabled: true
Keys.onPressed: {
if(event.key===pedalKey && !event.isAutoRepeat)
{
state="a"
}
}
Keys.onReleased: {
if(event.key===pedalKey && !event.isAutoRepeat)
{
state="b"
}
}
}
It works, but when FocusScope loses the focus.
The most terrible thing is that I don't know which Item got the focus.
Is there any way to enable the Item to receive keyboard events without focus?
You can forward key events to other objects (even multiple objects). Here is the example from Qt's documentation:
Item {
ListView {
id: list1
// ...
}
ListView {
id: list2
// ...
}
Keys.forwardTo: [list1, list2]
focus: true
}
It works, but when FocusScope lost the focus.
Yes, key events are only delivered to the items with activeFocus. The event will be sent to the inner-most item first, proceeding up the chain of parents until one of them accepts the events (using e.g. the handlers you're using here).
The most terrible thing is that I don't know which Item got the focus.
You can use the Window.activeFocusItem attached property to see where the focus is currently.
Is there any way to enable the Item to receive keyboard events without focus?
Not easily or directly. You could use event filtering to intercept events before they get to the window, but I would consider that absolutely an option of last resort. Shortcuts are another possibility, depending on what presses you are trying to intercept.
Some questions to the Force Touch Menu:
Now when I perform a Force Touch and press one of my menu Items, the whole InterfaceController, where my Menu is implemented is loading new.
Is this avoidable?
I've implemented a menu with 4 menuItems for my InterfaceController.
With one of this menuItems, I want to enable/disable the haptic Feedback of my Buttons.
My button methods are like this:
- (IBAction) but1Pressed {
[[WKInterfaceDevice currentDevice] playHaptic:WKHapticType.Click];
// Do something
}
How can I disable the TapticEngine, if the user disables it ind the ForceTouchMenu?
No, as by today that's not possible.
If I understand you properly, you need a BOOL which defines whether the app should perform haptic feedback when a button in your interface controller is being pressed.
To realise that, implement the following:
BOOL shouldGiveTapticFeedback = YES
Then you need a method to change this BOOL when the corresponding menu item is being pressed:
- (IBAction) tapticFeedbackChangeButtonPressed {
//change BOOL value
shouldGiveTapticFeedback = !shouldGiveTapticFeedback
}
Finally, you need to check whether the taptic feedback should be played, when a button in your interface controller is being pressed:
- (IBAction) interfaceButtonPressed {
if(shouldGiveTapticFeedback) {
//play sound
[[WKInterfaceDevice currentDevice] playHaptic:WKHapticType.Click];
}
}
No, that's not possible either.
In Qt, either implementing keyPressEvent or creating a QAction and assigning it a key combination allow me to act based on the keyboard.
Which of these methods is generally preferred?
You should use QAction whenever the same event that is triggered by the key sequence you want may be triggered through other ways like from a menu, toolbar or other buttons. This way you can use the same action on several widgets that should do the same trick.
Excerpt from QAction doc:
The QAction class provides an abstract
user interface action that can be
inserted into widgets.
In applications many common commands
can be invoked via menus, toolbar buttons, and
keyboard shortcuts. Since the user
expects each command to be performed
in the same way, regardless of the
user interface used, it is useful to
represent each command as an action.
I'd prefer to overwrite the keyPressEvent. I don't like the idea of a QAction "lying around somewhere". Just overwrite the keyPressedEvent. I usually do it with a switch-case in which I check the pressed key. Just don't forget to call the keyPressEvent of the base class if you don't want to disable the standard behaviour of a key. Additionally you can check if a "modifier" is pressed while a keyPressEvent occurs. (e.g. Shift or Ctrl). IMHO for general purposes overwriting the keyPressEvent is better than creating invisible, secret actions, unless you want your application to contain all those actions visible for the user.
void my_widget::keyPressEvent( QKeyEvent* p_event )
{
bool ctrl_pressed = false;
if( p_event->modifiers() == Qt::ControlModifier )
{
ctrl_pressed = true;
}
switch( p_event->key() )
{
case Qt::Key_F:
focus_view();
break;
case Qt::Key_I:
if( ctrl_pressed )
{
toggle_interface();
}
else
{
QWidget::keyPressEvent( p_event );
}
break;
case Qt::Key_Return: // return key
case Qt::Key_Enter: // numpad enter key
update_something();
break;
default:
QSpinBox::keyPressEvent( p_event );
}
}
Would depend on what you need it for.
Is it for a menu like action that may be triggered by a menu, button, toolbar too, then go for the QAction. Especially if this action should work all over your program, not only in a single widget.
Is it more like a local activity in a single widget (say for example controlling movement in a game), I would use the keypress event.