1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-09-10 12:03:18 +02:00

accessibility: fix call action bar

Make the call action bar usable with only a keyboard and a
screen-reader by makint it visible using tab and inplementing
proper labels.

GitLab: #2049
Change-Id: Ifa1f1463f27b30bcfffae228058223729f00e51d
This commit is contained in:
pmagnier-slimani 2025-06-02 10:40:15 -04:00
parent e0f939318e
commit 869aef8929
6 changed files with 103 additions and 9 deletions

View file

@ -401,6 +401,16 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
}
}
}
// Tab or BackTab key events should trigger a signal that we can use to
// prevent the overlay from fading and to allow the user to navigate
// through the controls.
if (event->type() == QEvent::KeyPress && (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Tab)
|| (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Backtab)) {
Q_EMIT focusKeyPressed();
// Don't absorb the event so that the focus can be changed
// to the next or previous control.
return false;
}
#ifndef HAVE_GLOBAL_PTT
else if (event->type() == QEvent::KeyPress && listener_->getPttState()) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);

View file

@ -140,6 +140,7 @@ public:
Q_SIGNALS:
void mouseMoved(QQuickItem* item);
void focusKeyPressed();
void pttKeyPressed();
void pttKeyReleased();

View file

@ -55,15 +55,30 @@ Control {
signal fullScreenClicked
signal swarmDetailsClicked
// For Keyboard naviguation
property bool isInternalNavigation: false
function exitBarNavigation() {
isInternalNavigation = false;
// Let the parent control take over focus handling
parent.forceActiveFocus();
}
Component {
id: buttonDelegate
CallButtonDelegate {
id: delegateItem
width: root.height
height: width
barWidth: root.width
onSubMenuVisibleChanged: subMenuOpen = subMenuVisible
onHoveredChanged: root.barHovered = hovered
focusPolicy: Qt.StrongFocus
focus: false
Keys.onEscapePressed: root.exitBarNavigation()
}
}
@ -567,6 +582,34 @@ Control {
ComboBox {
id: overflowButton
Accessible.role: Accessible.Button
Accessible.name: JamiStrings.more
Accessible.description: JamiStrings.moreOptions
KeyNavigation.tab: {
if (popup.opened) {
return popup.contentItem.itemAtIndex(0);
}
// Exit bar navigation if we've reached the end
root.exitBarNavigation();
return null;
}
KeyNavigation.backtab: {
if (overflowItemListView.count > 0) {
return overflowItemListView.itemAtIndex(overflowItemListView.count - 1);
}
return itemListView.itemAtIndex(itemListView.count - 1);
}
Keys.onEscapePressed: {
if (popup.opened) {
popup.close();
} else {
root.exitBarNavigation();
}
}
visible: CallOverlayModel.overflowIndex < overflowItemCount - 2
width: root.height
height: width

View file

@ -42,8 +42,18 @@ ItemDelegate {
text: ""
Accessible.role: Accessible.Button
Accessible.name: text
Accessible.description: text
Accessible.name: ItemAction.text
Accessible.description: {
if (!ItemAction?.text) return ""
if (ItemAction.checkable) {
return JamiStrings.pressToToggle.arg(ItemAction.text)
.arg(ItemAction.checked ? JamiStrings.active : JamiStrings.inactive)
}
return JamiStrings.pressToAction.arg(ItemAction.text)
}
Accessible.pressed: pressed
Accessible.checkable: ItemAction ? ItemAction.checkable : false
Accessible.checked: ItemAction ? ItemAction.checked : false
z: index

View file

@ -26,6 +26,17 @@ Item {
property string timeText: "00:00"
property string remoteRecordingLabel
property bool isKeyboardSelectionActive: {
if (!appWindow || !appWindow.activeFocusItem)
return false;
let parent = appWindow.activeFocusItem.parent;
while (parent && parent !== appWindow && parent !== root && parent !== null) {
if (parent.objectName === "callActionBar")
return true;
parent = parent.parent;
}
return false;
}
Connections {
target: CurrentCall
@ -42,7 +53,11 @@ Item {
property alias callActionBar: __callActionBar
property bool frozen: callActionBar.overflowOpen || callActionBar.barHovered || callActionBar.subMenuOpen || participantCallInStatusView.visible
property bool frozen: callActionBar.overflowOpen ||
callActionBar.barHovered ||
callActionBar.subMenuOpen ||
participantCallInStatusView.visible ||
isKeyboardSelectionActive
property string muteAlertMessage: ""
property bool muteAlertActive: false
@ -59,15 +74,27 @@ Item {
Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
function kickOverlay() {
root.opacity = 1;
fadeOutTimer.restart();
}
Connections {
target: CallOverlayModel
function onMouseMoved(item) {
if (item === root) {
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}
// This is part of a mechanism used to show the overlay when a focus key is pressed
// and keep it open in the case that the user is navigating with the keyboard over
// the call action bar.
function onFocusKeyPressed() {
// Always show the overlay when a focus key (Tab/BackTab) is pressed
kickOverlay();
}
}
Shortcut {
@ -76,8 +103,7 @@ Item {
context: Qt.ApplicationShortcut
onActivated: {
CallAdapter.muteAudioToggle();
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}
@ -87,8 +113,7 @@ Item {
context: Qt.ApplicationShortcut
onActivated: {
CallAdapter.muteCameraToggle();
root.opacity = 1;
fadeOutTimer.restart();
kickOverlay();
}
}

View file

@ -935,4 +935,9 @@ Item {
property string adviceBox: qsTr("Advice Box")
property string backButton: qsTr("Back button")
property string adviceBoxExplanation: qsTr("Open the advice popup that contains information about Jami")
property string more: qsTr("More")
property string pressToAction: qsTr("Press to %1")
property string pressToToggle: qsTr("Press to toggle %1 (%2)")
property string active: qsTr("active")
property string inactive: qsTr("inactive")
}