Merge pull request #12340 from shoogle/accessibility-element-info

Accessibility: Improve speech output for notes and common elements
This commit is contained in:
Elnur Ismailzada 2022-07-21 11:45:57 +03:00 committed by GitHub
commit 222a8b6d6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 8 deletions

View file

@ -25,14 +25,27 @@
#include "../libmscore/score.h"
#include "../libmscore/measure.h"
#include "translation.h"
#include "log.h"
using namespace mu;
using namespace mu::engraving;
using namespace mu::accessibility;
using namespace mu::engraving;
bool AccessibleItem::enabled = true;
static QString readable(QString s)
{
// Remove undesired pauses in screen reader speech output
s.remove(u':');
// Handle desired pauses
s.replace(u';', u','); // NVDA doesn't pause for semicolon
return s;
}
AccessibleItem::AccessibleItem(EngravingItem* e, Role role)
: m_element(e), m_role(role)
{
@ -169,9 +182,15 @@ QString AccessibleItem::accessibleName() const
QString staffInfo = root ? root->staffInfo() : "";
QString barsAndBeats = m_element->formatBarsAndBeats();
return QString("%1%2%3").arg(!staffInfo.isEmpty() ? (staffInfo + "; ") : "")
.arg(m_element->accessibleInfo().toQString())
.arg(!barsAndBeats.isEmpty() ? ("; " + barsAndBeats) : "");
barsAndBeats.remove(u';'); // Too many pauses in speech
QString name = QString("%1%2%3%4")
.arg(!staffInfo.isEmpty() ? (staffInfo + "; ") : "")
.arg(m_element->screenReaderInfo().toQString())
.arg(m_element->visible() ? "" : " " + qtrc("engraving", "invisible"))
.arg(!barsAndBeats.isEmpty() ? ("; " + barsAndBeats) : "");
return readable(name);
}
QString AccessibleItem::accessibleDescription() const
@ -185,7 +204,7 @@ QString AccessibleItem::accessibleDescription() const
result = accessibleName() + " ";
#endif
result += m_element->accessibleExtraInfo();
result += readable(m_element->accessibleExtraInfo());
return result;
}

View file

@ -683,7 +683,8 @@ String ChordRest::durationUserName() const
tupletType = mtrc("engraving", "Nonuplet");
break;
default:
tupletType = mtrc("engraving", "Custom tuplet");
//: %1 is tuplet ratio numerator (i.e. the number of notes in the tuplet)
tupletType = mtrc("engraving", "%1 note tuplet").arg(tuplet()->ratio().numerator());
}
}
String dotString;

View file

@ -3332,7 +3332,15 @@ String Note::screenReaderInfo() const
pitchName = mtrc("engraving", "%1; String: %2; Fret: %3")
.arg(tpcUserName(true), String::number(string() + 1), String::number(fret()));
} else {
pitchName = tpcUserName(true);
pitchName = _headGroup == NoteHeadGroup::HEAD_NORMAL
? tpcUserName(true)
//: head as in note head. %1 is head type (circle, cross, etc.). %2 is pitch (e.g. Db4).
: mtrc("engraving", "%1 head %2").arg(subtypeName()).arg(tpcUserName(true));
if (chord()->staffMove() < 0) {
duration += u"; " + mtrc("engraving", "Cross-staff above");
} else if (chord()->staffMove() > 0) {
duration += u"; " + mtrc("engraving", "Cross-staff below");
}
}
return String(u"%1 %2 %3%4").arg(noteTypeUserName(), pitchName, duration, (chord()->isGrace() ? u"" : String(u"; %1").arg(voice)));
}

View file

@ -863,8 +863,14 @@ String Rest::screenReaderInfo() const
{
Measure* m = measure();
bool voices = m ? m->hasVoices(staffIdx()) : false;
String voice = voices ? mtrc("engraving", "Voice: %1").arg(track() % VOICES + 1) : u"";
return String(u"%1 %2 %3").arg(EngravingItem::accessibleInfo(), durationUserName(), voice);
String voice = voices ? (u"; " + mtrc("engraving", "Voice: %1").arg(track() % VOICES + 1)) : u"";
String crossStaff;
if (staffMove() < 0) {
crossStaff = u"; " + mtrc("engraving", "Cross-staff above");
} else if (staffMove() > 0) {
crossStaff = u"; " + mtrc("engraving", "Cross-staff below");
}
return String(u"%1 %2%3%4").arg(EngravingItem::accessibleInfo(), durationUserName(), crossStaff, voice);
}
//---------------------------------------------------------