Prevent Controls from stealing keyboard focus - qt

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.

Related

How to find subsequent (all) SLOT from a subclass

I need to block specific buttons on an MMI.
I implemented a button blocking function in a subclass of QPushButton.
For this, I used the clicked() signal and blocked the button with blockSignals(true).
This means that with each button clicked on my MMI, 2 SLOTS are always called.
But when calling the blocking of a specific button, I get the first SLOT (clicked()) of my subclass, in which I block the button, then I then arrive in the original SLOT linked to this button, which is still called despite the blocking (the first time only).
How can I in my QPushButton subclass know the subsequent SLOTs linked to this button and avoid them (delete them)?
void QbtnStandardButton::slotButtonClicked(void)
{
if (modeProtection)
{
// Special mode to protect/unprotect the button
if (isProtected())
{
// Reset the protection
this->blockSignals(false);
}
else
{
// Set the protection: button will be unclickable
this->blockSignals(true);
}
modeProtection = false;
}
if (isProtected())
{
QMessageBox *pMsgBox = new QMessageBox(QMessageBox::Information,
"Protection",
"This button is protected!",
QMessageBox::Ok);
pMsgBox->exec();
pMsgBox->deleteLater();
// Here: remove subsequent SLOT of this button ?
}
}
I think it's very difficult if not impossible to find SLOTS linked to a button.
I worked around the problem by using an eventFilter() instead of a SIGNAL() in my base class.
In this case, I can filter the "clicked()" event before it is reissued.

How to receive keyboard events in an unfocused QtQuick Item?

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.

Dragging a radio button performes the action but doesn't set the check

I am creating an application that should work on desktop and some mobile platforms.
The following example creates and connects my portrait/landscape buttons, in a group, to a slot, on the release signal.
m_landscapeRadio = new QRadioButton(QObject::tr("Landscape "));
m_portraitRadio = new QRadioButton(QObject::tr("Portrait "));
m_orientationGroup.addButton(m_landscapeRadio, 0);
m_orientationGroup.addButton(m_portraitRadio, 1);
m_orientationGroup.setExclusive(true);
m_landscapeRadio->setChecked(true);
connect(&m_orientationGroup, SIGNAL(buttonReleased(int)), this, SLOT(orientationSlot(int)));
But I found a weird situation:
Assume landscape button is checked. If I press and drag away from the portrait radio button, the slot action is performed (for the portrait option) but the portrait button is not checked.
I would like the action not to be performed.
For now...
In the orientationSlot I test the argument and set the checked value myself... Though I really expected the buttons to know to do that themselves.
But I think it is more expected by users that, if the press a button and change their mind, to be able to drag away from the button and not have the action be performed.
I can handle verifying if the check really happened in the action slot, and either check or discard the action depending on how I will think the user experience is better...
If I want the buttons to be checked and to perform the action as well:
void MyWidget::orientationSlot(int checked)
{
if(checked) m_portraitRadio->setChecked(true);
else m_landscapeRadio->setChecked(true);
.... actual actions
}
If I want the action not to be performed when the user drags away from the button (my preferred option):
void MyWidget::orientationSlot(int checked)
{
if(m_orientationGroup.checkedId() != checked) return;
.... actual actions
}
I use QRadioButton and handle mouse button being released event for reacting
on radio button being switched. It causes problems altogether with dragging event. I would like to either get the button
to be checked, or the action not to be performed.
http://doc.qt.io/qt-5/qradiobutton.html
Whenever a button is switched on or off, it emits the toggled()
signal. Connect to this signal if you want to trigger an action each
time the button changes state. Use isChecked() to see if a particular
button is selected.
Either you connect the radio button to the handler explicitly or the whole group: http://doc.qt.io/qt-5/qbuttongroup.html#buttonToggled
void QButtonGroup::buttonToggled(QAbstractButton *button, bool
checked)
This signal is emitted when the given button is toggled. checked is
true if the button is checked, or false if the button is unchecked.
Note: Signal buttonToggled is overloaded in this class. To connect to
this one using the function pointer syntax, you must specify the
signal type in a static cast, as shown in this example:
connect(buttonGroup, static_cast<void(QButtonGroup::*)
(QAbstractButton *, bool)>(&QButtonGroup::buttonToggled),
[=](QAbstractButton *button, bool checked) {
if (button == m_portraitRadio) {
// Portrait (un)checked
if (checked)
{
// checked!
}
}
/* ... */ });

WatchKit - Force Touch API Questions

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.

Flash Sprite loses focus on MOUSE_DOWN event

My Sprite class keeps losing focus when I click with the mouse - specifically after the MOUSE_DOWN event (before the click is complete).
I have set mouseEnabled to false on the children, no change. I added a listener for FOCUS_OUT and noticed that the FocusEvent.relatedObject property is NULL, which is confusing me - doesn't that mean there is no new focus target, the focus is just getting lost?
The exact sequence of events I get, by tracing them, as I click:
[FocusEvent type="focusOut" bubbles=true cancelable=false eventPhase=2 relatedObject=null shiftKey=false keyCode=0]
[MouseEvent type="mouseDown" bubbles=true cancelable=false eventPhase=2 localX=355 localY=362 stageX=360 stageY=367 relatedObject=null ctrlKey=false altKey=false shiftKey=false buttonDown=true delta=0])
[MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=355 localY=362 stageX=360 stageY=367 relatedObject=null ctrlKey=false altKey=false shiftKey=false buttonDown=false delta=0]
Try setting mouseChildren = false; on the sprite instead of mouseEnabled = false; on the children. If the sprite's children have mouseEnabled set to false, none of the visible elements contained in your sprite are clickable, except for the shapes drawn directly in its own graphics. You would then actually click "through" the children and on the stage.
[EDIT]:
I've created a test to reproduce your problem. You're right - the focus is mysteriously lost when a sprite is clicked on, even though one would expect this to actually set the focus on it. The same is true for MovieClips, but not for TextFields. Unpractical though that may be, you can work around it by adding a custom mouseDown handler:
private function onMouseDown (ev:Event) : void {
if (stage.focus != sprite) stage.focus = sprite;
}
If you extend any InteractiveObject you have to set tabEnabled = true; . This will make your clicked upon object gain focus.
Be careful though:
If tabEnabled is false, but mouseChildren is true, then the stage.focus will be set to null.

Resources