Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accesibility Change One: Show Acessible Info #99

Merged
merged 2 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 65 additions & 0 deletions melatonin/component_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ namespace melatonin
juce::Value lookAndFeelValue, typeValue, fontValue, alphaValue;
juce::Value pickedColor;
juce::Value timing1, timing2, timing3, timingMax, hasChildren;

struct AccessiblityDetail
{
juce::Value title, value, role, handlerType;
} accessiblityDetail;

double timingWithChildren1, timingWithChildren2, timingWithChildren3, timingWithChildrenMax;

ComponentModel() = default;
Expand Down Expand Up @@ -145,6 +151,65 @@ namespace melatonin
interceptsMouseValue.addListener (this);
childrenInterceptsMouseValue.addListener (this);

if (selectedComponent->isAccessible() && selectedComponent->getAccessibilityHandler())
{
auto* accH = selectedComponent->getAccessibilityHandler();
accessiblityDetail.handlerType = type (*accH);
if (accH->getValueInterface())
{
accessiblityDetail.value = accH->getValueInterface()->getCurrentValueAsString();
}
else
{
accessiblityDetail.value = "no value interface";
}
accessiblityDetail.title = accH->getTitle();
auto role = accH->getRole();
switch (role)
{
// Amazingly juce doesn' thave a display name fn for these
#define DN(x) \
case juce::AccessibilityRole::x: \
accessiblityDetail.role = #x; \
break;
DN (button)
DN (toggleButton)
DN (radioButton)
DN (comboBox)
DN (image)
DN (slider)
DN (label)
DN (staticText)
DN (editableText)
DN (menuItem)
DN (menuBar)
DN (popupMenu)
DN (table)
DN (tableHeader)
DN (column)
DN (row)
DN (cell)
DN (hyperlink)
DN (list)
DN (listItem)
DN (tree)
DN (treeItem)
DN (progressBar)
DN (group)
DN (dialogWindow)
DN (window)
DN (scrollBar)
DN (tooltip)
DN (splashScreen)
DN (ignored)
DN (unspecified)
#undef DN
default:
accessiblityDetail.role = juce::String ("Unknown ") + juce::String ((int) role);
break;
}
}

{
bool interceptsMouse = false;
bool childrenInterceptsMouse = false;
Expand Down
16 changes: 16 additions & 0 deletions melatonin/components/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ namespace melatonin
}
panel.addProperties (props, padding);

if (model.accessibilityHandledValue.getValue())
{
auto& ad = model.accessiblityDetail;
auto aprops = juce::Array<juce::PropertyComponent*> {
new juce::TextPropertyComponent (ad.title, "Title", 200, false, false),
new juce::TextPropertyComponent (ad.value, "Value", 200, false, false),
new juce::TextPropertyComponent (ad.role, "Role", 200, false, false),
new juce::TextPropertyComponent (ad.handlerType, "Handler", 200, false, false),
};
for (auto* p : aprops)
{
p->setLookAndFeel (&getLookAndFeel());
}
panel.addSection ("Accessibility", aprops);
}

resized();
}

Expand Down
13 changes: 13 additions & 0 deletions melatonin/helpers/component_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ namespace melatonin
return juce::String ("Editor: ") + editor->getAudioProcessor()->getName();
}
#endif
else if (c && c->isAccessible() && c->getAccessibilityHandler() && !c->getAccessibilityHandler()->getTitle().isEmpty())
{
// If a widget has an accessible name, prefer that to the internal
// name since it is user facing in a screen reader
auto acctitle = c->getAccessibilityHandler()->getTitle();
auto nm = c->getName();
if (nm != acctitle && !nm.isEmpty())
{
// but if i also have an internal name, dont' suppress it
acctitle += "(name=" + nm + ")";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check this out locally and toy with it. From reading the code, I think will lead to ugly names in the tree like like Pitch Stiffness(pitchStiffness) for people like me who use component's setName widely. I think it's good thinking to prefer the accessible name for tree display but perhaps we should always display a component's name as a discrete property (like we do with Class).

Sidebar: I can't help but feel like this is in part a JUCE issue / historical artifact that component has both name / title. There's some conventions missing so I'm guessing devs are all over the place in terms of their implementations. In my setups, a component's name is a "key" that's used to look up its "UI friendly name" from data which is then used in the UI, can be used for accessibility, can be swapped out with translations, etc....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree with your sidebar

Putting the name after the class in the properties list and backing out this change is a good idea

And yes having an accessibility top level area would also solve a lot of the other things we want to do but was a bigger change so wasn't sure if you were up for it. Then we could do things like 'next- and previous- focus target' and the action list also.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: I found out from using inspector that I don't have a name on any of my components!

One of the things I think juce wanted to avoid with name/title by the way is if users had given their components names like 'dumbSwitchToUnbreakStuff' in code, exposing that as user display for screen reader users only was a bit risky. That's also why I chose the screen reader name to beat the accesible name since it is in the end user UX.

Copy link
Owner

@sudara sudara Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the things I think juce wanted to avoid

Amazingly get/setName seem to be... 17 years old according to git blame. I'm guessing they are mainly used as internal identifiers by devs?

Then we could do things like 'next- and previous- focus target' and the action list also.

I'm into it! Let's make it its own panel.

Some of the panel / header logic is a bit messy if I remember (the headers overlay the panels as they can contain action buttons when collapsed. And I think PROPERTIES is setup to be the last panel, would need some work if we wanted to plop accessibility afterwards. In short, feel free to uhh.. nope out of that work if it gets finicky, I don't mind getting in there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha I'll take a peek today and if its impossible i'll revert but otherwise will update this PR once I have it in

}
return acctitle;
}
else if (c && !c->getName().isEmpty())
{
return c->getName();
Expand Down