@qooxdoo/framework 7.0.0-beta.2 → 7.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/Manifest.json +2 -3
  3. package/bin/deploy/qx +0 -0
  4. package/lib/compiler/compile-info.json +63 -64
  5. package/lib/compiler/index.js +614 -7793
  6. package/lib/resource/qx/tool/cli/templates/loader/loader-node.tmpl.js +3 -1
  7. package/lib/resource/qx/tool/cli/templates/skeleton/mobile/source/theme/custom/css/custom.css +1 -1
  8. package/lib/resource/qx/tool/cli/templates/skeleton/mobile/source/theme/custom/css/custom.css.map +1 -1
  9. package/lib/resource/qx/tool/cli/templates/template_vars.js +1 -1
  10. package/lib/resource/qx/tool/loadsass.js +2 -10
  11. package/lib/resource/qx/tool/schema/Manifest-1-0-0.json +1 -2
  12. package/lib/resource/qx/tool/schema/Manifest-2-0-0.json +1 -2
  13. package/lib/resource/qx/tool/schema/compile-1-0-0.json +3 -7
  14. package/package.json +11 -11
  15. package/source/class/qx/test/ui/embed/Iframe.js +1 -0
  16. package/source/class/qx/test/ui/form/ComboBox.js +0 -42
  17. package/source/class/qx/theme/manager/Decoration.js +0 -0
  18. package/source/class/qx/theme/tangible/ColorDark.js +0 -0
  19. package/source/class/qx/tool/cli/Cli.js +5 -3
  20. package/source/class/qx/tool/cli/api/CompilerApi.js +15 -5
  21. package/source/class/qx/tool/cli/commands/Command.js +7 -0
  22. package/source/class/qx/tool/cli/commands/Compile.js +1 -1
  23. package/source/class/qx/tool/cli/commands/Lint.js +30 -11
  24. package/source/class/qx/tool/cli/commands/Package.js +1 -2
  25. package/source/class/qx/tool/cli/commands/package/Publish.js +19 -10
  26. package/source/class/qx/tool/compiler/Analyser.js +21 -22
  27. package/source/class/qx/tool/compiler/app/WebFont.js +1 -1
  28. package/source/class/qx/tool/compiler/makers/AppMaker.js +13 -13
  29. package/source/class/qx/tool/compiler/targets/TypeScriptWriter.js +1 -2
  30. package/source/class/qx/tool/compiler/targets/meta/PolyfillJs.js +7 -7
  31. package/source/class/qx/tool/config/Abstract.js +3 -3
  32. package/source/class/qx/tool/config/Utils.js +10 -1
  33. package/source/class/qx/tool/utils/Json.js +1 -1
  34. package/source/class/qx/ui/container/SlideBar.js +3 -0
  35. package/source/class/qx/ui/core/scroll/NativeScrollBar.js +3 -0
  36. package/source/class/qx/ui/core/scroll/ScrollBar.js +3 -0
  37. package/source/class/qx/ui/form/AbstractSelectBox.js +38 -6
  38. package/source/class/qx/ui/form/Button.js +3 -0
  39. package/source/class/qx/ui/form/CheckBox.js +25 -1
  40. package/source/class/qx/ui/form/ComboBox.js +36 -27
  41. package/source/class/qx/ui/form/List.js +3 -0
  42. package/source/class/qx/ui/form/MenuButton.js +24 -2
  43. package/source/class/qx/ui/form/RadioButton.js +7 -0
  44. package/source/class/qx/ui/form/RadioButtonGroup.js +3 -0
  45. package/source/class/qx/ui/form/RadioGroup.js +19 -0
  46. package/source/class/qx/ui/form/SelectBox.js +23 -1
  47. package/source/class/qx/ui/form/Slider.js +15 -0
  48. package/source/class/qx/ui/form/SplitButton.js +3 -0
  49. package/source/class/qx/ui/form/ToggleButton.js +8 -0
  50. package/source/class/qx/ui/menu/AbstractButton.js +24 -0
  51. package/source/class/qx/ui/menu/Button.js +3 -0
  52. package/source/class/qx/ui/menu/CheckBox.js +8 -0
  53. package/source/class/qx/ui/menu/Manager.js +2 -0
  54. package/source/class/qx/ui/menu/Menu.js +63 -1
  55. package/source/class/qx/ui/menu/RadioButton.js +10 -1
  56. package/source/class/qx/ui/menubar/Button.js +0 -27
  57. package/source/class/qx/ui/menubar/MenuBar.js +12 -0
  58. package/source/class/qx/ui/splitpane/Blocker.js +3 -0
  59. package/source/class/qx/ui/splitpane/Pane.js +3 -0
  60. package/source/class/qx/ui/table/Table.js +24 -2
  61. package/source/class/qx/ui/table/cellrenderer/Abstract.js +3 -1
  62. package/source/class/qx/ui/table/cellrenderer/AbstractImage.js +7 -3
  63. package/source/class/qx/ui/table/headerrenderer/HeaderCell.js +3 -0
  64. package/source/class/qx/ui/table/pane/Header.js +3 -0
  65. package/source/class/qx/ui/table/pane/Scroller.js +3 -7
  66. package/source/class/qx/ui/table/rowrenderer/Default.js +1 -1
  67. package/source/class/qx/ui/tabview/Page.js +26 -0
  68. package/source/class/qx/ui/tabview/TabView.js +3 -0
  69. package/source/class/qx/ui/toolbar/Button.js +2 -27
  70. package/source/class/qx/ui/toolbar/CheckBox.js +0 -27
  71. package/source/class/qx/ui/toolbar/RadioButton.js +21 -0
  72. package/source/class/qx/ui/toolbar/SplitButton.js +0 -28
  73. package/source/class/qx/ui/toolbar/ToolBar.js +3 -0
  74. package/source/class/qxWeb.js +2 -0
  75. package/source/resource/qx/decoration/Indigo/font/JosefinSlab-SemiBold.ttf +0 -0
  76. package/source/resource/qx/decoration/Indigo/font/SIL Open Font License 1.1.txt +0 -0
  77. package/source/resource/qx/iconfont/MaterialIcons/fetch-fonts.sh +0 -0
  78. package/source/resource/qx/tool/bin/build-devtools +0 -0
  79. package/source/resource/qx/tool/bin/build-website +0 -0
  80. package/source/resource/qx/tool/bin/download-assets +0 -0
  81. package/source/resource/qx/tool/cli/templates/loader/loader-node.tmpl.js +3 -1
  82. package/source/resource/qx/tool/cli/templates/template_vars.js +1 -1
  83. package/source/resource/qx/tool/loadsass.js +2 -10
  84. package/source/resource/qx/tool/schema/Manifest-1-0-0.json +1 -2
  85. package/source/resource/qx/tool/schema/Manifest-2-0-0.json +1 -2
  86. package/source/resource/qx/tool/schema/compile-1-0-0.json +3 -7
  87. package/lib/resource/qx/tool/website/.gitignore +0 -2
  88. package/source/class/qx/io/request/auth/.gitignore +0 -0
  89. package/source/class/qx/test/bom/client/.gitignore +0 -0
  90. package/source/class/qx/test/ui/control/.gitignore +0 -0
  91. package/source/class/qx/tool/compiler/Version.js +0 -20
  92. package/source/resource/qx/decoration/Modern/treevirtual/.gitignore +0 -0
  93. package/source/resource/qx/mobile/css/.gitignore +0 -3
  94. package/source/resource/qx/tool/website/.gitignore +0 -2
  95. package/source/resource/qx/website/.gitignore +0 -1
  96. package/source/resource/qx/website/scss/.gitignore +0 -1
@@ -191,7 +191,7 @@ qx.Class.define("qx.tool.config.Utils", {
191
191
 
192
192
  throw new qx.tool.utils.Utils.UserError(`Path to the qx library cannot be determined.`);
193
193
  };
194
-
194
+
195
195
  this.__qxPathPromise = getQxPathImpl();
196
196
  return await this.__qxPathPromise;
197
197
  },
@@ -217,6 +217,15 @@ qx.Class.define("qx.tool.config.Utils", {
217
217
  return qx.tool.config.Utils.getLibraryVersion(qxpath);
218
218
  },
219
219
 
220
+ /**
221
+ * returns the compiler version.
222
+ * The version is written during compiler compile into the enviroment
223
+ * @return {String}
224
+ */
225
+ getCompilerVersion() {
226
+ return qx.core.Environment.get("qx.compiler.version");
227
+ },
228
+
220
229
  /**
221
230
  * Returns the qooxdoo version used in the application in the current or given
222
231
  * directory. Throws if no such version can be determined
@@ -59,7 +59,7 @@ qx.Class.define("qx.tool.utils.Json", {
59
59
  validate(json, schema, warnOnly=false) {
60
60
  let ajv = new Ajv({
61
61
  allErrors: true,
62
- jsonPointers: true
62
+ strict: false
63
63
  });
64
64
  if (qx.lang.Type.isArray(schema)) {
65
65
  ajv.addSchema(schema);
@@ -272,6 +272,9 @@ qx.Class.define("qx.ui.container.SlideBar",
272
272
  // property apply
273
273
  _applyOrientation : function(value, old)
274
274
  {
275
+ // ARIA attrs
276
+ this.getContentElement().setAttribute("aria-orientation", value);
277
+
275
278
  var oldLayouts = [this.getLayout(), this._getLayout()];
276
279
  var buttonForward = this.getChildControl("button-forward");
277
280
  var buttonBackward = this.getChildControl("button-backward");
@@ -234,6 +234,9 @@ qx.Class.define("qx.ui.core.scroll.NativeScrollBar",
234
234
  // property apply
235
235
  _applyOrientation : function(value, old)
236
236
  {
237
+ // ARIA attrs
238
+ this.getContentElement().setAttribute("aria-orientation", value);
239
+
237
240
  var isHorizontal = this.__isHorizontal = value === "horizontal";
238
241
 
239
242
  this.set({
@@ -301,6 +301,9 @@ qx.Class.define("qx.ui.core.scroll.ScrollBar",
301
301
  // property apply
302
302
  _applyOrientation : function(value, old)
303
303
  {
304
+ // ARIA attrs
305
+ this.getContentElement().setAttribute("aria-orientation", value);
306
+
304
307
  // Dispose old layout
305
308
  var oldLayout = this._getLayout();
306
309
  if (oldLayout) {
@@ -55,6 +55,12 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
55
55
  this._setLayout(layout);
56
56
  layout.setAlignY("middle");
57
57
 
58
+ // ARIA attrs
59
+ const contentEl = this.getContentElement();
60
+ contentEl.setAttribute("role", "button");
61
+ contentEl.setAttribute("aria-haspopup", "listbox");
62
+ contentEl.setAttribute("aria-expanded", false);
63
+
58
64
  // Register listeners
59
65
  this.addListener("keypress", this._onKeyPress);
60
66
  this.addListener("blur", this._onBlur, this);
@@ -130,7 +136,7 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
130
136
 
131
137
  switch(id)
132
138
  {
133
- case "list":
139
+ case "list": {
134
140
  control = new qx.ui.form.List().set({
135
141
  focusable: false,
136
142
  keepFocus: true,
@@ -140,12 +146,18 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
140
146
  selectionMode: "one",
141
147
  quickSelection: true
142
148
  });
149
+ const listId = "list-" + control.toHashCode();
150
+ const childrenContainerEl = control.getChildrenContainer().getContentElement();
151
+ childrenContainerEl.setAttribute("id", listId);
152
+ childrenContainerEl.setAttribute("role", "listbox");
153
+ this.getContentElement().setAttribute("aria-owns", listId);
143
154
 
155
+ control.addListener("addItem", this._onListAddItem, this);
144
156
  control.addListener("changeSelection", this._onListChangeSelection, this);
145
157
  control.addListener("pointerdown", this._onListPointerDown, this);
146
158
  control.getChildControl("pane").addListener("tap", this.close, this);
147
159
  break;
148
-
160
+ }
149
161
  case "popup":
150
162
  control = new qx.ui.popup.Popup(new qx.ui.layout.VBox());
151
163
  control.setAutoHide(false);
@@ -292,8 +304,8 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
292
304
  e.stopPropagation();
293
305
  }
294
306
 
295
- // hide the list always on escape
296
- else if (!listPopup.isHidden() && identifier == "Escape")
307
+ // hide the list always on escape and tab
308
+ else if (!listPopup.isHidden() && (identifier == "Escape" || identifier == "Tab"))
297
309
  {
298
310
  this.close();
299
311
  e.stop();
@@ -315,7 +327,23 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
315
327
  _onResize : function(e){
316
328
  this.getChildControl("popup").setMinWidth(e.getData().width);
317
329
  },
318
-
330
+
331
+ /**
332
+ * Sets ARIA attributes on the item
333
+ *
334
+ * @param e {qx.event.type.Data} Data Event
335
+ */
336
+ _onListAddItem : function(e) {
337
+ const item = e.getData();
338
+ const contentEl = item.getContentElement();
339
+ contentEl.setAttribute("id", "list-item-" + item.toHashCode());
340
+ contentEl.setAttribute("role", "option");
341
+ const ariaSelected = contentEl.getAttribute("aria-selected");
342
+ // aria-selected may be already set from changeSelection listener
343
+ if (ariaSelected === null || ariaSelected === undefined) {
344
+ contentEl.setAttribute("aria-selected", false);
345
+ }
346
+ },
319
347
 
320
348
  /**
321
349
  * Syncs the own property from the list change
@@ -343,7 +371,11 @@ qx.Class.define("qx.ui.form.AbstractSelectBox",
343
371
  * @param e {qx.event.type.Data} Property change event
344
372
  */
345
373
  _onPopupChangeVisibility : function(e) {
346
- e.getData() == "visible" ? this.addState("popupOpen") : this.removeState("popupOpen");
374
+ const visible = e.getData() == "visible";
375
+ visible ? this.addState("popupOpen") : this.removeState("popupOpen");
376
+
377
+ // ARIA attrs
378
+ this.getContentElement().setAttribute("aria-expanded", visible);
347
379
  }
348
380
  }
349
381
  });
@@ -75,6 +75,9 @@ qx.Class.define("qx.ui.form.Button",
75
75
  this.setCommand(command);
76
76
  }
77
77
 
78
+ // ARIA attrs
79
+ this.getContentElement().setAttribute("role", "button");
80
+
78
81
  // Add listeners
79
82
  this.addListener("pointerover", this._onPointerOver);
80
83
  this.addListener("pointerout", this._onPointerOut);
@@ -54,6 +54,12 @@ qx.Class.define("qx.ui.form.CheckBox",
54
54
  // Initialize the checkbox to a valid value (the default is null which
55
55
  // is invalid)
56
56
  this.setValue(false);
57
+
58
+ // ARIA attrs
59
+ const contentEl = this.getContentElement();
60
+ contentEl.setAttribute("role", "checkbox");
61
+ contentEl.removeAttribute("aria-pressed");
62
+ contentEl.setAttribute("aria-checked", false);
57
63
  },
58
64
 
59
65
 
@@ -107,6 +113,24 @@ qx.Class.define("qx.ui.form.CheckBox",
107
113
  "toolTipText",
108
114
  "value",
109
115
  "menu"
110
- ]
116
+ ],
117
+
118
+ // overridden
119
+ _applyValue : function(value, old) {
120
+ value ? this.addState("checked") : this.removeState("checked");
121
+
122
+ let ariaChecked = Boolean(value);
123
+ if (this.isTriState()) {
124
+ if (value === null) {
125
+ ariaChecked = "mixed";
126
+ this.addState("undetermined");
127
+ } else if (old === null) {
128
+ this.removeState("undetermined");
129
+ }
130
+ }
131
+
132
+ // ARIA attrs
133
+ this.getContentElement().setAttribute("aria-checked", ariaChecked);
134
+ },
111
135
  }
112
136
  });
@@ -50,6 +50,9 @@ qx.Class.define("qx.ui.form.ComboBox",
50
50
  var textField = this._createChildControl("textfield");
51
51
  this._createChildControl("button");
52
52
 
53
+ // ARIA attrs
54
+ this.getContentElement().setAttribute("role", "combobox");
55
+
53
56
  this.addListener("tap", this._onTap);
54
57
 
55
58
  // forward the focusin and focusout events to the textfield. The textfield
@@ -242,26 +245,25 @@ qx.Class.define("qx.ui.form.ComboBox",
242
245
  var popup = this.getChildControl("popup");
243
246
  var iden = e.getKeyIdentifier();
244
247
 
245
- if (iden == "Down" && e.isAltPressed())
246
- {
247
- this.getChildControl("button").addState("selected");
248
- this.toggle();
249
- e.stopPropagation();
250
- }
251
- else if (iden == "Enter")
252
- {
253
- if (popup.isVisible())
254
- {
248
+ if (popup.isVisible()) {
249
+ const listIdentifier = ["Up", "Down", "PageUp", "PageDown", "Escape", "Tab"];
250
+ if (iden == "Enter") {
255
251
  this._setPreselectedItem();
256
252
  this.resetAllTextSelection();
257
253
  this.close();
258
254
  e.stop();
255
+ } else if (listIdentifier.includes(iden)) {
256
+ this.base(arguments, e);
257
+ } else {
258
+ this.close();
259
+ }
260
+ } else {
261
+ if (iden == "Down") {
262
+ this.getChildControl("button").addState("selected");
263
+ this.open();
264
+ e.stop();
259
265
  }
260
266
  }
261
- else if (popup.isVisible())
262
- {
263
- this.base(arguments, e);
264
- }
265
267
  },
266
268
 
267
269
 
@@ -333,6 +335,13 @@ qx.Class.define("qx.ui.form.ComboBox",
333
335
  this.__preSelectedItem = null;
334
336
  }
335
337
  }
338
+
339
+ // Set aria-activedescendant
340
+ if (current && current[0]) {
341
+ this.getChildControl("textfield").getContentElement().setAttribute("aria-activedescendant", current[0].getContentElement().getAttribute("id"));
342
+ } else {
343
+ this.getChildControl("textfield").getContentElement().removeAttribute("aria-activedescendant");
344
+ }
336
345
  },
337
346
 
338
347
 
@@ -363,16 +372,6 @@ qx.Class.define("qx.ui.form.ComboBox",
363
372
  list.resetSelection();
364
373
  }
365
374
  }
366
- else
367
- {
368
- // When closing the popup text should selected and field should
369
- // have the focus. Identical to when reaching the field using the TAB key.
370
- //
371
- // Only focus if popup was visible before. Fixes [BUG #4453].
372
- if (e.getOldData() == "visible") {
373
- this.tabFocus();
374
- }
375
- }
376
375
 
377
376
  // In all cases: Remove focused state from button
378
377
  this.getChildControl("button").removeState("selected");
@@ -390,11 +389,12 @@ qx.Class.define("qx.ui.form.ComboBox",
390
389
  var value = e.getData();
391
390
 
392
391
  var list = this.getChildControl("list");
392
+ let current = null;
393
393
  if (value != null) {
394
394
  // Select item when possible
395
- var item = list.findItem(value, false);
396
- if (item) {
397
- list.setSelection([item]);
395
+ current = list.findItem(value, false);
396
+ if (current) {
397
+ list.setSelection([current]);
398
398
  } else {
399
399
  list.resetSelection();
400
400
  }
@@ -402,6 +402,15 @@ qx.Class.define("qx.ui.form.ComboBox",
402
402
  list.resetSelection();
403
403
  }
404
404
 
405
+ // ARIA attrs
406
+ const old = e.getOldData() ? list.findItem(e.getOldData(), false) : null;
407
+ if (old && old !== current) {
408
+ old.getContentElement().setAttribute("aria-selected", false);
409
+ }
410
+ if (current) {
411
+ current.getContentElement().setAttribute("aria-selected", true);
412
+ }
413
+
405
414
  // Fire event
406
415
  this.fireDataEvent("changeValue", value, e.getOldData());
407
416
  },
@@ -277,6 +277,9 @@ qx.Class.define("qx.ui.form.List",
277
277
  // property apply
278
278
  _applyOrientation : function(value, old)
279
279
  {
280
+ // ARIA attrs
281
+ this.getContentElement().setAttribute("aria-orientation", value);
282
+
280
283
  var content = this.__content;
281
284
 
282
285
  // save old layout for disposal
@@ -45,6 +45,9 @@ qx.Class.define("qx.ui.form.MenuButton",
45
45
  if (menu != null) {
46
46
  this.setMenu(menu);
47
47
  }
48
+
49
+ // ARIA attrs
50
+ this.getContentElement().setAttribute("role", "button");
48
51
  },
49
52
 
50
53
 
@@ -112,6 +115,7 @@ qx.Class.define("qx.ui.form.MenuButton",
112
115
  old.resetOpener();
113
116
  }
114
117
 
118
+ const contentEl = this.getContentElement();
115
119
  if (value)
116
120
  {
117
121
  value.addListener("changeVisibility", this._onMenuChange, this);
@@ -119,6 +123,16 @@ qx.Class.define("qx.ui.form.MenuButton",
119
123
 
120
124
  value.removeState("submenu");
121
125
  value.removeState("contextmenu");
126
+
127
+ // ARIA attrs
128
+ contentEl.setAttribute("aria-haspopup", "menu");
129
+ contentEl.setAttribute("aria-expanded", value.isVisible());
130
+ contentEl.setAttribute("aria-controls", value.getContentElement().getAttribute("id"));
131
+ } else {
132
+ // ARIA attrs
133
+ contentEl.removeAttribute("aria-haspopup");
134
+ contentEl.removeAttribute("aria-expanded");
135
+ contentEl.removeAttribute("aria-controls");
122
136
  }
123
137
  },
124
138
 
@@ -142,6 +156,10 @@ qx.Class.define("qx.ui.form.MenuButton",
142
156
 
143
157
  if (menu)
144
158
  {
159
+ // Focus this button when the menu opens
160
+ if (this.isFocusable() && !qx.ui.core.FocusHandler.getInstance().isFocused(this)) {
161
+ this.focus();
162
+ }
145
163
  // Hide all menus first
146
164
  qx.ui.menu.Manager.getInstance().hideAll();
147
165
 
@@ -177,12 +195,15 @@ qx.Class.define("qx.ui.form.MenuButton",
177
195
  _onMenuChange : function(e)
178
196
  {
179
197
  var menu = this.getMenu();
180
-
181
- if (menu.isVisible()) {
198
+ const menuVisible = menu.isVisible()
199
+ if (menuVisible) {
182
200
  this.addState("pressed");
183
201
  } else {
184
202
  this.removeState("pressed");
185
203
  }
204
+
205
+ // ARIA attrs
206
+ this.getContentElement().setAttribute("aria-expanded", menuVisible);
186
207
  },
187
208
 
188
209
 
@@ -241,6 +262,7 @@ qx.Class.define("qx.ui.form.MenuButton",
241
262
  {
242
263
  switch(e.getKeyIdentifier())
243
264
  {
265
+ case "Space":
244
266
  case "Enter":
245
267
  this.removeState("abandoned");
246
268
  this.addState("pressed");
@@ -68,6 +68,12 @@ qx.Class.define("qx.ui.form.RadioButton",
68
68
 
69
69
  this.base(arguments, label);
70
70
 
71
+ // ARIA attrs
72
+ // Important: (Grouped) radio btns should be children of a div with role 'radiogroup'
73
+ const contentEl = this.getContentElement();
74
+ contentEl.setAttribute("role", "radio");
75
+ contentEl.setAttribute("aria-checked", false);
76
+
71
77
  // Add listeners
72
78
  this.addListener("execute", this._onExecute);
73
79
  this.addListener("keypress", this._onKeyPress);
@@ -164,6 +170,7 @@ qx.Class.define("qx.ui.form.RadioButton",
164
170
  value ?
165
171
  this.addState("checked") :
166
172
  this.removeState("checked");
173
+ this.getContentElement().setAttribute("aria-checked", Boolean(value));
167
174
  },
168
175
 
169
176
 
@@ -55,6 +55,9 @@ qx.Class.define("qx.ui.form.RadioButtonGroup",
55
55
  this.setLayout(layout);
56
56
  }
57
57
 
58
+ // ARIA attrs
59
+ this.getContentElement().setAttribute("role", "radiogroup");
60
+
58
61
  // create the radio group
59
62
  this.__radioGroup = new qx.ui.form.RadioGroup();
60
63
 
@@ -522,7 +522,26 @@ qx.Class.define("qx.ui.form.RadioGroup",
522
522
 
523
523
  if (value) {
524
524
  value.set(groupedProperty, true);
525
+
526
+ // If Group is focused, the selection was changed by keyboard. Switch focus to new value
527
+ if (this.__isGroupFocused() && value.isFocusable()) {
528
+ value.focus();
529
+ }
530
+ }
531
+ },
532
+
533
+ /**
534
+ * Checks if this group is focused by checking focused state of each item
535
+ * @returns {Boolean} result
536
+ */
537
+ __isGroupFocused: function () {
538
+ const focusHandler = qx.ui.core.FocusHandler.getInstance();
539
+ for (const item of this._getItems()) {
540
+ if (focusHandler.isFocused(item)) {
541
+ return true;
542
+ }
525
543
  }
544
+ return false;
526
545
  }
527
546
  },
528
547
 
@@ -221,6 +221,16 @@ qx.Class.define("qx.ui.form.SelectBox",
221
221
 
222
222
  this.__updateIcon();
223
223
  this.__updateLabel();
224
+
225
+ // ARIA attrs
226
+ const old = e.getOldData() ? e.getOldData()[0] : null;
227
+ const current = this.getSelection()[0];
228
+ if (old && old !== current) {
229
+ old.getContentElement().setAttribute("aria-selected", false);
230
+ }
231
+ if (current) {
232
+ current.getContentElement().setAttribute("aria-selected", true);
233
+ }
224
234
  },
225
235
 
226
236
 
@@ -327,7 +337,11 @@ qx.Class.define("qx.ui.form.SelectBox",
327
337
  _onKeyPress : function(e)
328
338
  {
329
339
  var iden = e.getKeyIdentifier();
330
- if(iden == "Enter" || iden == "Space")
340
+ if ((iden == "Down" || iden == "Up") && e.isAltPressed()) {
341
+ this.toggle();
342
+ e.stop();
343
+ }
344
+ else if (iden == "Enter" || iden == "Space")
331
345
  {
332
346
  // Apply pre-selected item (translate quick selection to real selection)
333
347
  if (this.__preSelectedItem)
@@ -337,6 +351,7 @@ qx.Class.define("qx.ui.form.SelectBox",
337
351
  }
338
352
 
339
353
  this.toggle();
354
+ e.stop();
340
355
  }
341
356
  else
342
357
  {
@@ -413,6 +428,13 @@ qx.Class.define("qx.ui.form.SelectBox",
413
428
  {
414
429
  this.resetSelection();
415
430
  }
431
+
432
+ // Set aria-activedescendant
433
+ if (current && current[0]) {
434
+ this.getContentElement().setAttribute("aria-activedescendant", current[0].getContentElement().getAttribute("id"));
435
+ } else {
436
+ this.getContentElement().removeAttribute("aria-activedescendant");
437
+ }
416
438
  },
417
439
 
418
440
  // overridden
@@ -82,6 +82,9 @@ qx.Class.define("qx.ui.form.Slider",
82
82
  // Force canvas layout
83
83
  this._setLayout(new qx.ui.layout.Canvas());
84
84
 
85
+ // ARIA attrs
86
+ this.getContentElement().setAttribute("role", "slider");
87
+
85
88
  // Add listeners
86
89
  this.addListener("keypress", this._onKeyPress);
87
90
  this.addListener("roll", this._onRoll);
@@ -1024,6 +1027,9 @@ qx.Class.define("qx.ui.form.Slider",
1024
1027
  // property apply
1025
1028
  _applyOrientation : function(value, old)
1026
1029
  {
1030
+ // ARIA attrs
1031
+ this.getContentElement().setAttribute("aria-orientation", value);
1032
+
1027
1033
  var knob = this.getChildControl("knob");
1028
1034
 
1029
1035
  // Update private flag for faster access
@@ -1077,6 +1083,9 @@ qx.Class.define("qx.ui.form.Slider",
1077
1083
  // property apply
1078
1084
  _applyValue : function(value, old) {
1079
1085
  if (value != null) {
1086
+ // ARIA attrs
1087
+ this.getContentElement().setAttribute("aria-valuenow", value);
1088
+
1080
1089
  this._updateKnobPosition();
1081
1090
  if (this.__dragMode) {
1082
1091
  this.__dragValue = [value,old];
@@ -1105,6 +1114,9 @@ qx.Class.define("qx.ui.form.Slider",
1105
1114
  // property apply
1106
1115
  _applyMinimum : function(value, old)
1107
1116
  {
1117
+ // ARIA attrs
1118
+ this.getContentElement().setAttribute("aria-valuemin", value);
1119
+
1108
1120
  if (this.getValue() < value) {
1109
1121
  this.setValue(value);
1110
1122
  }
@@ -1116,6 +1128,9 @@ qx.Class.define("qx.ui.form.Slider",
1116
1128
  // property apply
1117
1129
  _applyMaximum : function(value, old)
1118
1130
  {
1131
+ // ARIA attrs
1132
+ this.getContentElement().setAttribute("aria-valuemax", value);
1133
+
1119
1134
  if (this.getValue() > value) {
1120
1135
  this.setValue(value);
1121
1136
  }
@@ -48,6 +48,9 @@ qx.Class.define("qx.ui.form.SplitButton",
48
48
  {
49
49
  this.base(arguments);
50
50
 
51
+ // ARIA attrs
52
+ this.getContentElement().setAttribute("role", "button");
53
+
51
54
  this._setLayout(new qx.ui.layout.HBox);
52
55
 
53
56
  // Force arrow creation
@@ -65,6 +65,10 @@ qx.Class.define("qx.ui.form.ToggleButton",
65
65
  // register execute event
66
66
  this.addListener("execute", this._onExecute, this);
67
67
 
68
+ // ARIA attrs
69
+ const contentEl = this.getContentElement();
70
+ contentEl.setAttribute("role", "button");
71
+ contentEl.setAttribute("aria-pressed", false);
68
72
  },
69
73
 
70
74
 
@@ -159,13 +163,17 @@ qx.Class.define("qx.ui.form.ToggleButton",
159
163
  _applyValue : function(value, old) {
160
164
  value ? this.addState("checked") : this.removeState("checked");
161
165
 
166
+ let ariaPressed = Boolean(value);
162
167
  if (this.isTriState()) {
163
168
  if (value === null) {
169
+ ariaPressed = "mixed";
164
170
  this.addState("undetermined");
165
171
  } else if (old === null) {
166
172
  this.removeState("undetermined");
167
173
  }
168
174
  }
175
+
176
+ this.getContentElement().setAttribute("aria-pressed", ariaPressed);
169
177
  },
170
178
 
171
179
  /**
@@ -335,23 +335,47 @@ qx.Class.define("qx.ui.menu.AbstractButton",
335
335
  {
336
336
  if (old)
337
337
  {
338
+ old.removeListener("changeVisibility", this._onMenuChange, this);
338
339
  old.resetOpener();
339
340
  old.removeState("submenu");
340
341
  }
341
342
 
343
+ const contentEl = this.getContentElement();
342
344
  if (value)
343
345
  {
344
346
  this._showChildControl("arrow");
345
347
 
348
+ value.addListener("changeVisibility", this._onMenuChange, this);
346
349
  value.setOpener(this);
347
350
  value.addState("submenu");
351
+
352
+ // ARIA attrs
353
+ contentEl.setAttribute("aria-haspopup", "menu");
354
+ contentEl.setAttribute("aria-expanded", value.isVisible());
355
+ contentEl.setAttribute("aria-controls", value.getContentElement().getAttribute("id"));
348
356
  }
349
357
  else
350
358
  {
351
359
  this._excludeChildControl("arrow");
360
+
361
+ // ARIA attrs
362
+ contentEl.removeAttribute("aria-haspopup");
363
+ contentEl.removeAttribute("aria-expanded");
364
+ contentEl.removeAttribute("aria-controls");
352
365
  }
353
366
  },
354
367
 
368
+ /**
369
+ * Listener for visibility property changes of the attached menu
370
+ *
371
+ * @param e {qx.event.type.Data} Property change event
372
+ */
373
+ _onMenuChange : function(e)
374
+ {
375
+ // ARIA attrs
376
+ this.getContentElement().setAttribute("aria-expanded", this.getMenu().isVisible());
377
+ },
378
+
355
379
  // property apply
356
380
  _applyShowCommandLabel : function(value, old)
357
381
  {
@@ -43,6 +43,9 @@ qx.Class.define("qx.ui.menu.Button",
43
43
  {
44
44
  this.base(arguments);
45
45
 
46
+ // ARIA attrs
47
+ this.getContentElement().setAttribute("role", "button");
48
+
46
49
  // Initialize with incoming arguments
47
50
  if (label != null) {
48
51
  this.setLabel(label);