Module: Lich::Common::GUI::Accessibility

Defined in:
documented/common/gui/accessibility.rb

Class Method Summary collapse

Class Method Details

.add_keyboard_navigation(widget, can_focus = true, tab_order = nil) ⇒ void

This method returns an undefined value.

Adds keyboard navigation properties to a widget.

Parameters:

  • widget (Object)

    the widget to add keyboard navigation to

  • can_focus (Boolean) (defaults to: true)

    whether the widget can receive focus

  • tab_order (Integer, nil) (defaults to: nil)

    the tab order for the widget



141
142
143
144
145
146
147
148
149
150
151
152
# File 'documented/common/gui/accessibility.rb', line 141

def self.add_keyboard_navigation(widget, can_focus = true, tab_order = nil)
  begin
    widget.can_focus = can_focus

    # In GTK3, we need to check if the property exists before setting it
    if tab_order && widget.class.property?('tab-position')
      widget.set_property('tab-position', tab_order)
    end
  rescue StandardError => e
    Lich.log "warning: Could not set keyboard navigation: #{e.message}"
  end
end

.add_keyboard_shortcut(widget, key, modifiers = []) ⇒ void

This method returns an undefined value.

Adds a keyboard shortcut to a widget.

Parameters:

  • widget (Object)

    the widget to add the shortcut to

  • key (String)

    the key for the shortcut

  • modifiers (Array<Symbol>) (defaults to: [])

    an array of modifiers for the shortcut



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'documented/common/gui/accessibility.rb', line 160

def self.add_keyboard_shortcut(widget, key, modifiers = [])
  return unless widget.respond_to?(:add_accelerator)

  begin
    # Convert modifiers to Gdk::ModifierType
    modifier_mask = 0
    modifiers.each do |mod|
      case mod
      when :control, :ctrl
        modifier_mask |= Gdk::ModifierType::CONTROL_MASK
      when :shift
        modifier_mask |= Gdk::ModifierType::SHIFT_MASK
      when :alt
        modifier_mask |= Gdk::ModifierType::MOD1_MASK
      end
    end

    # Find or create accelerator group
    if widget.parent.is_a?(Gtk::Window)
      # Get the first accel group (replacing the unreachable loop)
      accel_group = widget.parent.accel_groups.first

      # Create new accel group if none found
      if accel_group.nil?
        accel_group = Gtk::AccelGroup.new
        widget.parent.add_accel_group(accel_group)
      end

      # Add accelerator
      widget.add_accelerator(
        "activate",
        accel_group,
        Gdk::Keyval.from_name(key),
        modifier_mask,
        Gtk::AccelFlags::VISIBLE
      )
    end
  rescue StandardError => e
    Lich.log "warning: Could not add keyboard shortcut: #{e.message}"
  end
end

.announce(widget, message, _priority = :medium) ⇒ void

This method returns an undefined value.

Announces a message through a widget for assistive technologies.

This method temporarily changes the accessible name to the message.

Parameters:

  • widget (Object)

    the widget to announce the message through

  • message (String)

    the message to announce

  • priority (Symbol)

    the priority of the announcement (default: :medium)



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'documented/common/gui/accessibility.rb', line 209

def self.announce(widget, message, _priority = :medium)
  return unless widget.respond_to?(:get_accessible)

  begin
    accessible = widget.get_accessible
    return unless accessible

    # In GTK3/ATK, we can use state changes to trigger screen reader announcements
    if accessible.respond_to?(:notify_state_change)
      # Toggle state to trigger announcement
      accessible.notify_state_change(Atk::StateType::SHOWING, true)

      # Set name to message temporarily
      original_name = nil
      if accessible.respond_to?(:get_name) && accessible.respond_to?(:set_name)
        original_name = accessible.get_name
        accessible.set_name(message)
      end

      # Restore original name after a short delay
      if original_name
        # Using one-shot timeout (returns false to prevent repetition)
        GLib::Timeout.add(1000) do
          accessible.set_name(original_name)
          false # Intentionally return false to run only once
        end
      end
    end
  rescue StandardError => e
    Lich.log "warning: Could not announce message: #{e.message}"
  end
end

.create_accessible_label(container, input, text, position = :left) ⇒ Gtk::Label

Creates a label that is accessible and connects it to an input widget.

Parameters:

  • container (Gtk::Box, Gtk::Grid)

    the container to add the label and input to

  • input (Gtk::Widget)

    the input widget to connect to the label

  • text (String)

    the text for the label

  • position (Symbol) (defaults to: :left)

    the position of the label relative to the input (default: :left)

Returns:

  • (Gtk::Label)

    the created label



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'documented/common/gui/accessibility.rb', line 249

def self.create_accessible_label(container, input, text, position = :left)
  label = Gtk::Label.new(text)
  label.set_alignment(position == :left ? 1 : 0, 0.5)

  # Connect label to input for screen readers
  if input.respond_to?(:get_accessible) && label.respond_to?(:get_accessible)
    input_accessible = input.get_accessible
    label_accessible = label.get_accessible

    if input_accessible && label_accessible &&
       input_accessible.respond_to?(:add_relationship) &&
       defined?(Atk::RelationType::LABEL_FOR)
      label_accessible.add_relationship(Atk::RelationType::LABEL_FOR, input_accessible)
    end
  end

  # Add to container based on position
  case container
  when Gtk::Box
    case position
    when :left
      container.pack_start(label, expand: false, fill: false, padding: 5)
      container.pack_start(input, expand: true, fill: true, padding: 5)
    when :right
      container.pack_start(input, expand: true, fill: true, padding: 5)
      container.pack_start(label, expand: false, fill: false, padding: 5)
    when :top, :bottom
      # For top/bottom, we need to change the box orientation or create a vertical box
      vbox = if container.orientation == :vertical
               container
             else
               vbox = Gtk::Box.new(:vertical, 5)
               container.add(vbox)
               vbox
             end

      if position == :top
        vbox.pack_start(label, expand: false, fill: false, padding: 2)
        vbox.pack_start(input, expand: true, fill: true, padding: 2)
      else
        vbox.pack_start(input, expand: true, fill: true, padding: 2)
        vbox.pack_start(label, expand: false, fill: false, padding: 2)
      end
    end
  when Gtk::Grid
    # For Grid, we need row and column information which isn't provided
    # This is a simplified version
    container.add(label)
    container.add(input)
  else
    # For other containers, just add both
    container.add(label)
    container.add(input)
  end

  label
end

.get_atk_role(role_symbol) ⇒ Object?

Retrieves the ATK role corresponding to a given symbol.

Parameters:

  • role_symbol (Symbol)

    the symbol representing the role

Returns:

  • (Object, nil)

    the corresponding ATK role or nil if not found



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'documented/common/gui/accessibility.rb', line 311

def self.get_atk_role(role_symbol)
  return nil unless defined?(Atk::Role)

  begin
    case role_symbol
    when :button then Atk::Role::PUSH_BUTTON
    when :text then Atk::Role::TEXT
    when :combo_box then Atk::Role::COMBO_BOX
    when :page_tab then Atk::Role::PAGE_TAB
    when :panel then Atk::Role::PANEL
    when :window then Atk::Role::FRAME
    when :label then Atk::Role::LABEL
    when :list then Atk::Role::LIST
    when :list_item then Atk::Role::LIST_ITEM
    when :menu then Atk::Role::MENU
    when :menu_item then Atk::Role::MENU_ITEM
    when :check_box then Atk::Role::CHECK_BOX
    when :radio_button then Atk::Role::RADIO_BUTTON
    when :dialog then Atk::Role::DIALOG
    when :separator then Atk::Role::SEPARATOR
    when :scroll_bar then Atk::Role::SCROLL_BAR
    when :slider then Atk::Role::SLIDER
    when :spin_button then Atk::Role::SPIN_BUTTON
    when :table then Atk::Role::TABLE
    when :tree then Atk::Role::TREE
    when :tree_item then Atk::Role::TREE_ITEM
    else nil
    end
  rescue StandardError => e
    Lich.log "warning: Could not get ATK role: #{e.message}"
    nil
  end
end

.initialize_accessibilityvoid

This method returns an undefined value.

Initializes accessibility features for the GUI.

This method ensures that accessibility is enabled by referencing a Gail widget type. Accessibility is not available on any non-linux platform.



11
12
13
14
15
16
17
18
19
20
# File 'documented/common/gui/accessibility.rb', line 11

def self.initialize_accessibility
  # In GTK3, accessibility is enabled by default through ATK
  # Ensure ATK is loaded by referencing a Gail widget type
  # accessibility is not available on any non-linux platform
  begin
    GLib::Object.type_from_name('GailWidget')
  rescue NoMethodError => e
    Lich.log "warning: Could not initialize accessibility: #{e.message}" if OS.linux?
  end
end

.make_accessible(widget, label, description = nil, role = nil) ⇒ void

This method returns an undefined value.

Makes a widget accessible for assistive technologies.

Parameters:

  • widget (Object)

    the widget to make accessible

  • label (String)

    the accessible name for the widget

  • description (String, nil) (defaults to: nil)

    an optional description for the widget

  • role (Symbol, nil) (defaults to: nil)

    an optional role for the widget



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'documented/common/gui/accessibility.rb', line 29

def self.make_accessible(widget, label, description = nil, role = nil)
  return unless widget.respond_to?(:get_accessible)

  begin
    accessible = widget.get_accessible
    return unless accessible

    # Set accessible name
    accessible.set_name(label) if accessible.respond_to?(:set_name)

    # Set accessible description
    accessible.set_description(description) if description && accessible.respond_to?(:set_description)

    # Set accessible role
    if role && accessible.respond_to?(:set_role)
      role_value = get_atk_role(role)
      accessible.set_role(role_value) if role_value
    end
  rescue StandardError => e
    Lich.log "warning: Could not make widget accessible: #{e.message}"
  end
end

.make_button_accessible(button, label, description = nil) ⇒ void

This method returns an undefined value.

Makes a button accessible for assistive technologies.

This method ensures that the button has a visible label for screen readers.

Parameters:

  • button (Gtk::Button)

    the button to make accessible

  • label (String)

    the accessible name for the button

  • description (String, nil) (defaults to: nil)

    an optional description for the button



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'documented/common/gui/accessibility.rb', line 59

def self.make_button_accessible(button, label, description = nil)
  make_accessible(button, label, description, :button)

  # Ensure button has a visible label for screen readers
  begin
    if button.child.is_a?(Gtk::Label)
      button.child.set_text(label) if button.child.text.empty?
    elsif !button.label.nil? && button.label.empty?
      button.label = label
    end
  rescue StandardError => e
    Lich.log "warning: Could not set button label: #{e.message}"
  end
end

.make_combo_accessible(combo, label, description = nil) ⇒ void

This method returns an undefined value.

Makes a combo box accessible for assistive technologies.

Parameters:

  • combo (Gtk::ComboBox)

    the combo box to make accessible

  • label (String)

    the accessible name for the combo box

  • description (String, nil) (defaults to: nil)

    an optional description for the combo box



90
91
92
# File 'documented/common/gui/accessibility.rb', line 90

def self.make_combo_accessible(combo, label, description = nil)
  make_accessible(combo, label, description, :combo_box)
end

.make_entry_accessible(entry, label, description = nil) ⇒ void

This method returns an undefined value.

Makes an entry widget accessible for assistive technologies.

Parameters:

  • entry (Gtk::Entry)

    the entry to make accessible

  • label (String)

    the accessible name for the entry

  • description (String, nil) (defaults to: nil)

    an optional description for the entry



80
81
82
# File 'documented/common/gui/accessibility.rb', line 80

def self.make_entry_accessible(entry, label, description = nil)
  make_accessible(entry, label, description, :text)
end

.make_tab_accessible(notebook, page, tab_label, description = nil) ⇒ void

This method returns an undefined value.

Makes a tab in a notebook accessible for assistive technologies.

This method also makes the page itself accessible.

Parameters:

  • notebook (Gtk::Notebook)

    the notebook containing the tab

  • page (Gtk::Widget)

    the page to make accessible

  • tab_label (String)

    the accessible name for the tab

  • description (String, nil) (defaults to: nil)

    an optional description for the tab



102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'documented/common/gui/accessibility.rb', line 102

def self.make_tab_accessible(notebook, page, tab_label, description = nil)
  begin
    page_num = notebook.page_num(page)
    return if page_num == -1

    tab = notebook.get_tab_label(page)
    make_accessible(tab, tab_label, description, :page_tab)

    # Also make the page itself accessible
    make_accessible(page, tab_label, description, :panel)
  rescue StandardError => e
    Lich.log "warning: Could not make tab accessible: #{e.message}"
  end
end

.make_window_accessible(window, title, description = nil) ⇒ void

This method returns an undefined value.

Makes a window accessible for assistive technologies.

This method ensures that the window has a title for screen readers.

Parameters:

  • window (Gtk::Window)

    the window to make accessible

  • title (String)

    the accessible title for the window

  • description (String, nil) (defaults to: nil)

    an optional description for the window



124
125
126
127
128
129
130
131
132
133
# File 'documented/common/gui/accessibility.rb', line 124

def self.make_window_accessible(window, title, description = nil)
  make_accessible(window, title, description, :window)

  # Ensure window has a title for screen readers
  begin
    window.title = title if window.title.nil? || window.title.empty?
  rescue StandardError => e
    Lich.log "warning: Could not set window title: #{e.message}"
  end
end