diff options
Diffstat (limited to '')
-rw-r--r-- | js/ui/status/accessibility.js | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js new file mode 100644 index 0000000..04406e2 --- /dev/null +++ b/js/ui/status/accessibility.js @@ -0,0 +1,200 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported ATIndicator */ + +const { Gio, GLib, GObject, St } = imports.gi; + +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; + +const A11Y_SCHEMA = 'org.gnome.desktop.a11y'; +const KEY_ALWAYS_SHOW = 'always-show-universal-access-status'; + +const A11Y_KEYBOARD_SCHEMA = 'org.gnome.desktop.a11y.keyboard'; +const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable'; +const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable'; +const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable'; +const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable'; + +const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; + +var DPI_FACTOR_LARGE = 1.25; + +const WM_SCHEMA = 'org.gnome.desktop.wm.preferences'; +const KEY_VISUAL_BELL = 'visual-bell'; + +const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface'; +const KEY_GTK_THEME = 'gtk-theme'; +const KEY_ICON_THEME = 'icon-theme'; +const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor'; + +const HIGH_CONTRAST_THEME = 'HighContrast'; + +var ATIndicator = GObject.registerClass( +class ATIndicator extends PanelMenu.Button { + _init() { + super._init(0.5, _("Accessibility")); + + this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); + this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon', + icon_name: 'preferences-desktop-accessibility-symbolic' })); + this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); + + this.add_child(this._hbox); + + this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA }); + this._a11ySettings.connect('changed::%s'.format(KEY_ALWAYS_SHOW), this._queueSyncMenuVisibility.bind(this)); + + let highContrast = this._buildHCItem(); + this.menu.addMenuItem(highContrast); + + let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA, + 'screen-magnifier-enabled'); + this.menu.addMenuItem(magnifier); + + let textZoom = this._buildFontItem(); + this.menu.addMenuItem(textZoom); + + let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA, + 'screen-reader-enabled'); + this.menu.addMenuItem(screenReader); + + let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA, + 'screen-keyboard-enabled'); + this.menu.addMenuItem(screenKeyboard); + + let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL); + this.menu.addMenuItem(visualBell); + + let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_KEYBOARD_SCHEMA, KEY_STICKY_KEYS_ENABLED); + this.menu.addMenuItem(stickyKeys); + + let slowKeys = this._buildItem(_("Slow Keys"), A11Y_KEYBOARD_SCHEMA, KEY_SLOW_KEYS_ENABLED); + this.menu.addMenuItem(slowKeys); + + let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_KEYBOARD_SCHEMA, KEY_BOUNCE_KEYS_ENABLED); + this.menu.addMenuItem(bounceKeys); + + let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_KEYBOARD_SCHEMA, KEY_MOUSE_KEYS_ENABLED); + this.menu.addMenuItem(mouseKeys); + + this._syncMenuVisibility(); + } + + _syncMenuVisibility() { + this._syncMenuVisibilityIdle = 0; + + let alwaysShow = this._a11ySettings.get_boolean(KEY_ALWAYS_SHOW); + let items = this.menu._getMenuItems(); + + this.visible = alwaysShow || items.some(f => !!f.state); + + return GLib.SOURCE_REMOVE; + } + + _queueSyncMenuVisibility() { + if (this._syncMenuVisibilityIdle) + return; + + this._syncMenuVisibilityIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, this._syncMenuVisibility.bind(this)); + GLib.Source.set_name_by_id(this._syncMenuVisibilityIdle, '[gnome-shell] this._syncMenuVisibility'); + } + + _buildItemExtended(string, initialValue, writable, onSet) { + let widget = new PopupMenu.PopupSwitchMenuItem(string, initialValue); + if (!writable) { + widget.reactive = false; + } else { + widget.connect('toggled', item => { + onSet(item.state); + }); + } + return widget; + } + + _buildItem(string, schema, key) { + let settings = new Gio.Settings({ schema_id: schema }); + let widget = this._buildItemExtended(string, + settings.get_boolean(key), + settings.is_writable(key), + enabled => settings.set_boolean(key, enabled)); + + settings.connect('changed::%s'.format(key), () => { + widget.setToggleState(settings.get_boolean(key)); + + this._queueSyncMenuVisibility(); + }); + + return widget; + } + + _buildHCItem() { + let interfaceSettings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); + let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME); + let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME); + let hasHC = gtkTheme == HIGH_CONTRAST_THEME; + let highContrast = this._buildItemExtended( + _("High Contrast"), + hasHC, + interfaceSettings.is_writable(KEY_GTK_THEME) && + interfaceSettings.is_writable(KEY_ICON_THEME), + enabled => { + if (enabled) { + interfaceSettings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME); + interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME); + } else if (!hasHC) { + interfaceSettings.set_string(KEY_ICON_THEME, iconTheme); + interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme); + } else { + interfaceSettings.reset(KEY_ICON_THEME); + interfaceSettings.reset(KEY_GTK_THEME); + } + }); + + interfaceSettings.connect('changed::%s'.format(KEY_GTK_THEME), () => { + let value = interfaceSettings.get_string(KEY_GTK_THEME); + if (value == HIGH_CONTRAST_THEME) { + highContrast.setToggleState(true); + } else { + highContrast.setToggleState(false); + gtkTheme = value; + } + + this._queueSyncMenuVisibility(); + }); + + interfaceSettings.connect('changed::%s'.format(KEY_ICON_THEME), () => { + let value = interfaceSettings.get_string(KEY_ICON_THEME); + if (value != HIGH_CONTRAST_THEME) + iconTheme = value; + }); + + return highContrast; + } + + _buildFontItem() { + let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); + let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); + let initialSetting = factor > 1.0; + let widget = this._buildItemExtended(_("Large Text"), + initialSetting, + settings.is_writable(KEY_TEXT_SCALING_FACTOR), + enabled => { + if (enabled) { + settings.set_double( + KEY_TEXT_SCALING_FACTOR, DPI_FACTOR_LARGE); + } else { + settings.reset(KEY_TEXT_SCALING_FACTOR); + } + }); + + settings.connect('changed::%s'.format(KEY_TEXT_SCALING_FACTOR), () => { + factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); + let active = factor > 1.0; + widget.setToggleState(active); + + this._queueSyncMenuVisibility(); + }); + + return widget; + } +}); |