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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/Popover.js

Issue 2747553002: [DevTools] Rework Popover API (Closed)
Patch Set: rebased Created 3 years, 9 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
« no previous file with comments | « third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved. 2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 15 matching lines...) Expand all
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 /** 31 /**
32 * @unrestricted 32 * @unrestricted
33 */ 33 */
34 UI.PopoverHelper = class { 34 UI.PopoverHelper = class {
35 /** 35 /**
36 * @param {!Element} panelElement 36 * @param {!Element} container
37 * @param {boolean=} disableOnClick 37 * @param {function(!Event):?UI.PopoverRequest} getRequest
38 */ 38 */
39 constructor(panelElement, disableOnClick) { 39 constructor(container, getRequest) {
40 this._disableOnClick = !!disableOnClick; 40 this._disableOnClick = false;
41 this._hasPadding = false; 41 this._hasPadding = false;
42 panelElement.addEventListener('mousedown', this._mouseDown.bind(this), false ); 42 this._getRequest = getRequest;
43 panelElement.addEventListener('mousemove', this._mouseMove.bind(this), false ); 43 this._scheduledRequest = null;
44 panelElement.addEventListener('mouseout', this._mouseOut.bind(this), false); 44 /** @type {?function()} */
45 this._hidePopoverCallback = null;
46 container.addEventListener('mousedown', this._mouseDown.bind(this), false);
47 container.addEventListener('mousemove', this._mouseMove.bind(this), false);
48 container.addEventListener('mouseout', this._mouseOut.bind(this), false);
45 this.setTimeout(1000, 500); 49 this.setTimeout(1000, 500);
46 } 50 }
47 51
48 /** 52 /**
49 * @param {function(!Element, !Event):(!Element|!AnchorBox|undefined)} getAnch or
50 * @param {function((!Element|!AnchorBox), !UI.GlassPane):!Promise<boolean>} s howPopover
51 * @param {function()=} onHide
52 */
53 initializeCallbacks(getAnchor, showPopover, onHide) {
54 this._getAnchor = getAnchor;
55 this._showPopover = showPopover;
56 this._onHide = onHide;
57 }
58
59 /**
60 * @param {number} timeout 53 * @param {number} timeout
61 * @param {number=} hideTimeout 54 * @param {number=} hideTimeout
62 */ 55 */
63 setTimeout(timeout, hideTimeout) { 56 setTimeout(timeout, hideTimeout) {
64 this._timeout = timeout; 57 this._timeout = timeout;
65 if (typeof hideTimeout === 'number') 58 if (typeof hideTimeout === 'number')
66 this._hideTimeout = hideTimeout; 59 this._hideTimeout = hideTimeout;
67 else 60 else
68 this._hideTimeout = timeout / 2; 61 this._hideTimeout = timeout / 2;
69 } 62 }
70 63
71 /** 64 /**
72 * @param {boolean} hasPadding 65 * @param {boolean} hasPadding
73 */ 66 */
74 setHasPadding(hasPadding) { 67 setHasPadding(hasPadding) {
75 this._hasPadding = hasPadding; 68 this._hasPadding = hasPadding;
76 } 69 }
77 70
78 /** 71 /**
79 * @param {!MouseEvent} event 72 * @param {boolean} disableOnClick
73 */
74 setDisableOnClick(disableOnClick) {
75 this._disableOnClick = disableOnClick;
76 }
77
78 /**
79 * @param {!Event} event
80 * @return {boolean} 80 * @return {boolean}
81 */ 81 */
82 _eventInHoverElement(event) { 82 _eventInScheduledContent(event) {
83 if (!this._hoverElement) 83 return this._scheduledRequest ? this._scheduledRequest.box.contains(event.cl ientX, event.clientY) : false;
84 return false;
85 var box = this._hoverElement instanceof AnchorBox ? this._hoverElement : thi s._hoverElement.boxInWindow();
86 return (
87 box.x <= event.clientX && event.clientX <= box.x + box.width && box.y <= event.clientY &&
88 event.clientY <= box.y + box.height);
89 } 84 }
90 85
86 /**
87 * @param {!Event} event
88 */
91 _mouseDown(event) { 89 _mouseDown(event) {
92 if (this._disableOnClick || !this._eventInHoverElement(event)) { 90 if (this._disableOnClick || !this._eventInScheduledContent(event)) {
93 this.hidePopover(); 91 this.hidePopover();
94 } else { 92 } else {
95 this._killHidePopoverTimer(); 93 this._stopHidePopoverTimer();
96 this._handleMouseAction(event, true); 94 this._stopShowPopoverTimer();
95 this._startShowPopoverTimer(event, 0);
97 } 96 }
98 } 97 }
99 98
99 /**
100 * @param {!Event} event
101 */
100 _mouseMove(event) { 102 _mouseMove(event) {
101 // Pretend that nothing has happened. 103 // Pretend that nothing has happened.
102 if (this._eventInHoverElement(event)) 104 if (this._eventInScheduledContent(event))
103 return; 105 return;
104 106
105 this._startHidePopoverTimer(); 107 this._startHidePopoverTimer();
106 this._handleMouseAction(event, false); 108 this._stopShowPopoverTimer();
109 if (event.which && this._disableOnClick)
110 return;
111 this._startShowPopoverTimer(
112 event, this.isPopoverVisible() ? Math.max(this._timeout * 0.6, this._hid eTimeout) : this._timeout);
107 } 113 }
108 114
109 _popoverMouseOut(event) { 115 /**
110 if (!this.isPopoverVisible()) 116 * @param {!Event} event
117 */
118 _popoverMouseMove(event) {
119 this._stopHidePopoverTimer();
120 }
121
122 /**
123 * @param {!UI.GlassPane} popover
124 * @param {!Event} event
125 */
126 _popoverMouseOut(popover, event) {
127 if (!popover.isShowing())
111 return; 128 return;
112 if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(this._pop over.contentElement)) 129 if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(popover.c ontentElement))
113 this._startHidePopoverTimer(); 130 this._startHidePopoverTimer();
114 } 131 }
115 132
133 /**
134 * @param {!Event} event
135 */
116 _mouseOut(event) { 136 _mouseOut(event) {
117 if (!this.isPopoverVisible()) 137 if (!this.isPopoverVisible())
118 return; 138 return;
119 if (!this._eventInHoverElement(event)) 139 if (!this._eventInScheduledContent(event))
120 this._startHidePopoverTimer(); 140 this._startHidePopoverTimer();
121 } 141 }
122 142
123 _startHidePopoverTimer() { 143 _startHidePopoverTimer() {
124 // User has 500ms (this._hideTimeout) to reach the popup. 144 // User has this._hideTimeout to reach the popup.
125 if (!this._popover || this._hidePopoverTimer) 145 if (!this._hidePopoverCallback || this._hidePopoverTimer)
126 return; 146 return;
127 147
128 /** 148 this._hidePopoverTimer = setTimeout(() => {
129 * @this {UI.PopoverHelper}
130 */
131 function doHide() {
132 this._hidePopover(); 149 this._hidePopover();
133 delete this._hidePopoverTimer; 150 delete this._hidePopoverTimer;
134 } 151 }, this._hideTimeout);
135 this._hidePopoverTimer = setTimeout(doHide.bind(this), this._hideTimeout);
136 } 152 }
137 153
138 _handleMouseAction(event, isMouseDown) { 154 /**
139 this._resetHoverTimer(); 155 * @param {!Event} event
140 if (event.which && this._disableOnClick) 156 * @param {number} timeout
157 */
158 _startShowPopoverTimer(event, timeout) {
159 this._scheduledRequest = this._getRequest.call(null, event);
160 if (!this._scheduledRequest)
141 return; 161 return;
142 this._hoverElement = this._getAnchor(event.target, event); 162
143 if (!this._hoverElement) 163 this._showPopoverTimer = setTimeout(() => {
144 return; 164 delete this._showPopoverTimer;
145 const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout); 165 this._showPopover(event.target.ownerDocument);
146 this._hoverTimer = 166 }, timeout);
147 setTimeout(this._mouseHover.bind(this, this._hoverElement, event.target. ownerDocument), toolTipDelay);
148 } 167 }
149 168
150 _resetHoverTimer() { 169 _stopShowPopoverTimer() {
151 if (this._hoverTimer) { 170 if (!this._showPopoverTimer)
152 clearTimeout(this._hoverTimer); 171 return;
153 delete this._hoverTimer; 172 clearTimeout(this._showPopoverTimer);
154 } 173 delete this._showPopoverTimer;
155 } 174 }
156 175
157 /** 176 /**
158 * @return {boolean} 177 * @return {boolean}
159 */ 178 */
160 isPopoverVisible() { 179 isPopoverVisible() {
161 return !!this._popover; 180 return !!this._hidePopoverCallback;
162 } 181 }
163 182
164 hidePopover() { 183 hidePopover() {
165 this._resetHoverTimer(); 184 this._stopShowPopoverTimer();
166 this._hidePopover(); 185 this._hidePopover();
167 } 186 }
168 187
169 _hidePopover() { 188 _hidePopover() {
170 if (!this._popover) 189 if (!this._hidePopoverCallback)
171 return; 190 return;
172 191 this._hidePopoverCallback.call(null);
173 delete UI.PopoverHelper._popover; 192 this._hidePopoverCallback = null;
174 if (this._onHide)
175 this._onHide();
176
177 if (this._popover.isShowing())
178 this._popover.hide();
179 delete this._popover;
180 this._hoverElement = null;
181 } 193 }
182 194
183 _mouseHover(element, document) { 195 /**
184 delete this._hoverTimer; 196 * @param {!Document} document
185 this._hidePopover(); 197 */
186 this._hoverElement = element; 198 _showPopover(document) {
199 var popover = new UI.GlassPane();
200 popover.registerRequiredCSS('ui/popover.css');
201 popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
202 popover.setBlockPointerEvents(false);
203 popover.setShowArrow(true);
204 var request = this._scheduledRequest;
205 request.show.call(null, popover).then(success => {
206 if (!success)
207 return;
187 208
188 this._popover = new UI.GlassPane(); 209 if (this._scheduledRequest !== request) {
189 this._popover.registerRequiredCSS('ui/popover.css'); 210 if (request.hide)
190 this._popover.setBlockPointerEvents(false); 211 request.hide.call(null);
191 this._popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); 212 return;
192 this._popover.setShowArrow(true); 213 }
193 this._popover.contentElement.classList.toggle('has-padding', this._hasPaddin g);
194 this._popover.contentElement.addEventListener('mousemove', this._killHidePop overTimer.bind(this), true);
195 this._popover.contentElement.addEventListener('mouseout', this._popoverMouse Out.bind(this), true);
196 this._popover.setContentAnchorBox(
197 this._hoverElement instanceof AnchorBox ? this._hoverElement : this._hov erElement.boxInWindow());
198 214
199 // This should not happen, but we hide previous popover to be on the safe si de. 215 // This should not happen, but we hide previous popover to be on the safe side.
200 if (UI.PopoverHelper._popover) { 216 if (UI.PopoverHelper._popoverHelper) {
201 console.error('One popover is already visible'); 217 console.error('One popover is already visible');
202 UI.PopoverHelper._popover.hide(); 218 UI.PopoverHelper._popoverHelper.hidePopover();
203 } 219 }
204 UI.PopoverHelper._popover = this._popover; 220 UI.PopoverHelper._popoverHelper = this;
205 var popover = this._popover; 221
206 this._showPopover(element, this._popover).then(success => { 222 popover.contentElement.classList.toggle('has-padding', this._hasPadding);
207 if (success && this._popover === popover && this._hoverElement === element ) 223 popover.contentElement.addEventListener('mousemove', this._popoverMouseMov e.bind(this), true);
208 popover.show(document); 224 popover.contentElement.addEventListener('mouseout', this._popoverMouseOut. bind(this, popover), true);
225 popover.setContentAnchorBox(request.box);
226 popover.show(document);
227
228 this._hidePopoverCallback = () => {
229 if (request.hide)
230 request.hide.call(null);
231 popover.hide();
232 delete UI.PopoverHelper._popoverHelper;
233 };
209 }); 234 });
210 } 235 }
211 236
212 _killHidePopoverTimer() { 237 _stopHidePopoverTimer() {
213 if (this._hidePopoverTimer) { 238 if (!this._hidePopoverTimer)
214 clearTimeout(this._hidePopoverTimer); 239 return;
215 delete this._hidePopoverTimer; 240 clearTimeout(this._hidePopoverTimer);
241 delete this._hidePopoverTimer;
216 242
217 // We know that we reached the popup, but we might have moved over other e lements. 243 // We know that we reached the popup, but we might have moved over other ele ments.
218 // Discard pending command. 244 // Discard pending command.
219 this._resetHoverTimer(); 245 this._stopShowPopoverTimer();
220 }
221 } 246 }
222 }; 247 };
248
249 /** @typedef {{box: !AnchorBox, show:(function(!UI.GlassPane):!Promise<boolean>) , hide:(function()|undefined)}} */
250 UI.PopoverRequest;
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698