Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js

Issue 1696443002: Re-land: Implement ChromeVox Next menus. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview A drop-down menu in the ChromeVox panel.
7 */
8
9 goog.provide('PanelMenu');
10
11 goog.require('PanelMenuItem');
12
13 /**
14 * @param {string} menuTitle The title of the menu.
15 * @constructor
16 */
17 PanelMenu = function(menuTitle) {
18 // The item in the menu bar containing the menu's title.
19 this.menuBarItemElement = document.createElement('div');
20 this.menuBarItemElement.className = 'menu-bar-item';
21 this.menuBarItemElement.setAttribute('role', 'menu');
22 this.menuBarItemElement.textContent = menuTitle;
23
24 // The container for the menu. This part is fixed and scrolls its
25 // contents if necessary.
26 this.menuContainerElement = document.createElement('div');
27 this.menuContainerElement.className = 'menu-container';
28 this.menuContainerElement.style.visibility = 'hidden';
29
30 // The menu itself. It contains all of the items, and it scrolls within
31 // its container.
32 this.menuElement = document.createElement('table');
33 this.menuElement.className = 'menu';
34 this.menuElement.setAttribute('role', 'menu');
35 this.menuElement.setAttribute('aria-label', menuTitle);
36 this.menuContainerElement.appendChild(this.menuElement);
37
38 /**
39 * The items in the menu.
40 * @type {Array<PanelMenuItem>}
41 * @private
42 */
43 this.items_ = [];
44
45 /**
46 * The return value from window.setTimeout for a function to update the
47 * scroll bars after an item has been added to a menu. Used so that we
48 * don't re-layout too many times.
49 * @type {?number}
50 * @private
51 */
52 this.updateScrollbarsTimeout_ = null;
53
54 /**
55 * The current active menu item index, or -1 if none.
56 * @type {number}
57 * @private
58 */
59 this.activeIndex_ = -1;
60 };
61
62 PanelMenu.prototype = {
63 /**
64 * @param {string} menuItemTitle The title of the menu item.
65 * @param {string} menuItemShortcut The keystrokes to select this item.
66 * @param {Function} callback The function to call if this item is selected.
67 * @return {!PanelMenuItem} The menu item just created.
68 */
69 addMenuItem: function(menuItemTitle, menuItemShortcut, callback) {
70 var menuItem = new PanelMenuItem(menuItemTitle, menuItemShortcut, callback);
71 this.items_.push(menuItem);
72 this.menuElement.appendChild(menuItem.element);
73
74 // Sync the active index with focus.
75 menuItem.element.addEventListener('focus', (function(index, event) {
76 this.activeIndex_ = index;
77 }).bind(this, this.items_.length - 1), false);
78
79 // Update the container height, adding a scroll bar if necessary - but
80 // to avoid excessive layout, schedule this once per batch of adding
81 // menu items rather than after each add.
82 if (!this.updateScrollbarsTimeout_) {
83 this.updateScrollbarsTimeout_ = window.setTimeout((function() {
84 var menuBounds = this.menuElement.getBoundingClientRect();
85 var maxHeight = window.innerHeight - menuBounds.top;
86 this.menuContainerElement.style.maxHeight = maxHeight + 'px';
87 this.updateScrollbarsTimeout_ = null;
88 }).bind(this), 0);
89 }
90
91 return menuItem;
92 },
93
94 /**
95 * Activate this menu, which means showing it and positioning it on the
96 * screen underneath its title in the menu bar.
97 */
98 activate: function() {
99 this.menuContainerElement.style.visibility = 'visible';
100 this.menuContainerElement.style.opacity = 1;
101 this.menuBarItemElement.classList.add('active');
102 var barBounds =
103 this.menuBarItemElement.parentElement.getBoundingClientRect();
104 var titleBounds = this.menuBarItemElement.getBoundingClientRect();
105 var menuBounds = this.menuElement.getBoundingClientRect();
106
107 this.menuElement.style.minWidth = titleBounds.width + 'px';
108 this.menuContainerElement.style.minWidth = titleBounds.width + 'px';
109 if (titleBounds.left + menuBounds.width < barBounds.width) {
110 this.menuContainerElement.style.left = titleBounds.left + 'px';
111 } else {
112 this.menuContainerElement.style.left =
113 (titleBounds.right - menuBounds.width) + 'px';
114 }
115
116 // Make the first item active.
117 this.activateItem(0);
118 },
119
120 /**
121 * Hide this menu. Make it invisible first to minimize spurious
122 * accessibility events before the next menu activates.
123 */
124 deactivate: function() {
125 this.menuContainerElement.style.opacity = 0.001;
126 this.menuBarItemElement.classList.remove('active');
127 this.activeIndex_ = -1;
128
129 window.setTimeout((function() {
130 this.menuContainerElement.style.visibility = 'hidden';
131 }).bind(this), 0);
132 },
133
134 /**
135 * Make a specific menu item index active.
136 * @param {number} itemIndex The index of the menu item.
137 */
138 activateItem: function(itemIndex) {
139 this.activeIndex_ = itemIndex;
140 if (this.activeIndex_ >= 0 && this.activeIndex_ < this.items_.length)
141 this.items_[this.activeIndex_].element.focus();
142 },
143
144 /**
145 * Advanced the active menu item index by a given number.
146 * @param {number} delta The number to add to the active menu item index.
147 */
148 advanceItemBy: function(delta) {
149 if (this.activeIndex_ >= 0) {
150 this.activeIndex_ += delta;
151 this.activeIndex_ =
152 (this.activeIndex_ + this.items_.length) % this.items_.length;
153 } else {
154 if (delta >= 0)
155 this.activeIndex_ = 0;
156 else
157 this.activeIndex_ = this.menus_.length - 1;
158 }
159
160 this.items_[this.activeIndex_].element.focus();
161 },
162
163 /**
164 * Get the callback for the active menu item.
165 * @return {Function} The callback.
166 */
167 getCallbackForCurrentItem: function() {
168 if (this.activeIndex_ >= 0 && this.activeIndex_ < this.items_.length) {
169 return this.items_[this.activeIndex_].callback;
170 }
171 return null;
172 },
173
174 /**
175 * Get the callback for a menu item given its DOM element.
176 * @param {Element} element The DOM element.
177 * @return {Function} The callback.
178 */
179 getCallbackForElement: function(element) {
180 for (var i = 0; i < this.items_.length; i++) {
181 if (element == this.items_[i].element)
182 return this.items_[i].callback;
183 }
184 return null;
185 }
186 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698