robbert-vdh / nih-plug

Rust VST3 and CLAP plugin framework and plugins - because everything is better when you do it yourself
ISC License
1.81k stars 152 forks source link

nih_plug_vizia: accessibility not working #174

Open Timtam opened 6 days ago

Timtam commented 6 days ago

Hello,

the Vizia UI framework does have support for screen reader accessibility via the AccessKit crate. That is why I opted for this one in the hope of being able to write accessible VST plugins. I noticed that nih_plug_vizia currently uses a custom patched version of vizia from here: https://github.com/robbert-vdh/vizia I thus checked out the tag that is currently used in nih_plug_vizia and built the accesskit example

cargo run --example accesskit

As you can see when running either Narrator (the windows built-in screen reader) or NVDA (freely available), accessibility support works just fine in standalone mode and all controls are clearly labeled and visible. However when building a VST3 or CLAP and loading it in a DAW, e.g. REAPER (test version freely available), after focusing the window, the only feedback you get is "Vizia application". All controls are inaccessible. Does that have something to do with the baseview implementation used or something? I'm waay to unexperienced with this, could someone more experienced take a look? It'd be really awesome to have a working plugin framework that can generate accessible VST/CLAP plugins from scratch available in Rust!

Thank you.

DataTriny commented 6 days ago

The Vizia example uses winit and accesskit_winit to install accessibility on the window. There is currently no AccessKit adapter for the baseview crate, so no accessibility tree is exposed. Writing one would hopefully be similar to how we did it for winit. I see that baseview uses raw-window-handle so you should be able to use AccessKit's subclassing adapters.

If you go this far, I'd recommend using egui instead because we ensure the AccessKit implementation isn't broken there.

Timtam commented 5 days ago

@DataTriny I'd love to switch to egui, however I don't know anything about it, yet. I wanted to learn about it, but well... the web version of egui seems to be entirely inaccessible for screen readers, all I get on egui.rs is an unlabelled graphic and an empty input field. Its nice to demonstrate that this cool website is built with egui and all, but AccessKit doesn't work on the web :(. That puts the entry level much higher, as I'll have to learn by going through examples and stuff. Not impossible, but will take much more time, plus the frustration that the Vizia stuff I've learned already doesn't get me anywhere. I however still appreciate your feedback. Maybe checking out raw-window-handle and baseview and all this stuff will get me into a position where I can kind of add the AccessKit support into baseview to make Vizia work again. I'll also keep an eye on egui, as that will get me further within Bevy too, its just so sad that all the documentation and presentational work on the website just isn't accessible.

DataTriny commented 5 days ago

To install accessibility on a window using subclassing, you have to first create the window hidden, install AccessKit hooks then show the window. This is fine for regular apps but I don't know if that would be an issue in the context of VST plugins where the window is a children of the DAW software. Also, baseview currently has no support for creating hidden windows and showing them later. Testing would have to be performed here to see if this path is worth taking.

Otherwise AccessKit can directly be wired into baseview, but this would require significant architectural changes to the crate.

Finally, nih_plug's widgets would have to be modified because they currently don't take accessibility into account.

Timtam commented 4 days ago

@DataTriny thank you. Itried switching to egui, but no success, same result as with Vizia. Here's my editor fn:

    fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
        let params = self.params.clone();

        create_egui_editor(
            self.params.editor_state.clone(),
            (),
            |_, _| {},
            move |egui_ctx, setter, _state| {
                egui::CentralPanel::default().show(egui_ctx, |ui| {
                    ui.label("Application");
                    ui.button("Application: ");
                });
            },
        )
    }

As you can see, really basic, but no accessibility. I then noticed that egui-baseview doesn't enable the accesskit feature by default, so I tried adding this to my cargo.toml:

[dependencies.egui]
version = "*"
features = ["bytemuck", "accesskit"]

It seems to work, as it now compiles accesskit into the bundle too, but still no success. Anything I missed?

DataTriny commented 4 days ago

@Timtam baseview is the one responsible for managing the window and accessibility is tied to the window (on Windows and macOS). No matter which UI toolkit you use, if the windowing library doesn't cooperate nothing will happen.

On Windows, accessibility should be activated when a window receives the WM_GETOBJECT message. As you can see in baseview/src/win/window.rs in the function wnd_proc (starting at line 121), this message is simply ignored. To directly integrate AccessKit, one must call accesskit_windows::Adapter::handle_wm_getobject whenever this message arrives.

As stated above, there is a dirtier way to install accessibility by using accesskit_windows::SubclassingAdapter, but again, baseview currently offer no way to create the window hidden, which is a requirement for this hack to work.

Timtam commented 4 days ago

@DataTriny thank you for this inforomation. Your initial comment "If you go this far, I'd recommend using egui instead because we ensure the AccessKit implementation isn't broken there." made me think that using Egui over Vizia would result in an accessible GUI. Does that mean that it'd be more helpful to open an issue over at baseview to ask for AccessKit support?

DataTriny commented 4 days ago

@Timtam opening an issue towards the baseview repository would indeed be a good way to start the discussion. Feel free to mention me over there so I can add the necessary technical details.