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

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

Issue 2747553002: [DevTools] Rework Popover API (Closed)
Patch Set: works! 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/sources/JavaScriptSourceFrame.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 {boolean} disableOnClick
38 * @param {function(!Event):?UI.PopoverContent} getContent
38 */ 39 */
39 constructor(panelElement, disableOnClick) { 40 constructor(container, disableOnClick, getContent) {
40 this._disableOnClick = !!disableOnClick; 41 this._disableOnClick = disableOnClick;
41 this._hasPadding = false; 42 this._hasPadding = false;
42 panelElement.addEventListener('mousedown', this._mouseDown.bind(this), false ); 43 this._getContent = getContent;
43 panelElement.addEventListener('mousemove', this._mouseMove.bind(this), false ); 44 this._scheduledContent = null;
44 panelElement.addEventListener('mouseout', this._mouseOut.bind(this), false); 45 /** @type {?function()} */
46 this._hidePopoverCallback = null;
47 container.addEventListener('mousedown', this._mouseDown.bind(this), false);
48 container.addEventListener('mousemove', this._mouseMove.bind(this), false);
49 container.addEventListener('mouseout', this._mouseOut.bind(this), false);
45 this.setTimeout(1000, 500); 50 this.setTimeout(1000, 500);
46 } 51 }
47 52
48 /** 53 /**
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 54 * @param {number} timeout
61 * @param {number=} hideTimeout 55 * @param {number=} hideTimeout
62 */ 56 */
63 setTimeout(timeout, hideTimeout) { 57 setTimeout(timeout, hideTimeout) {
64 this._timeout = timeout; 58 this._timeout = timeout;
65 if (typeof hideTimeout === 'number') 59 if (typeof hideTimeout === 'number')
66 this._hideTimeout = hideTimeout; 60 this._hideTimeout = hideTimeout;
67 else 61 else
68 this._hideTimeout = timeout / 2; 62 this._hideTimeout = timeout / 2;
69 } 63 }
70 64
71 /** 65 /**
72 * @param {boolean} hasPadding 66 * @param {boolean} hasPadding
73 */ 67 */
74 setHasPadding(hasPadding) { 68 setHasPadding(hasPadding) {
75 this._hasPadding = hasPadding; 69 this._hasPadding = hasPadding;
76 } 70 }
77 71
78 /** 72 /**
79 * @param {!MouseEvent} event 73 * @param {!Event} event
80 * @return {boolean} 74 * @return {boolean}
81 */ 75 */
82 _eventInHoverElement(event) { 76 _eventInScheduledContent(event) {
83 if (!this._hoverElement) 77 return this._scheduledContent ? this._scheduledContent.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 } 78 }
90 79
80 /**
81 * @param {!Event} event
82 */
91 _mouseDown(event) { 83 _mouseDown(event) {
92 if (this._disableOnClick || !this._eventInHoverElement(event)) { 84 if (this._disableOnClick || !this._eventInScheduledContent(event)) {
93 this.hidePopover(); 85 this.hidePopover();
94 } else { 86 } else {
95 this._killHidePopoverTimer(); 87 this._stopHidePopoverTimer();
96 this._handleMouseAction(event, true); 88 this._stopShowPopoverTimer();
89 this._startShowPopoverTimer(event, 0);
97 } 90 }
98 } 91 }
99 92
93 /**
94 * @param {!Event} event
95 */
100 _mouseMove(event) { 96 _mouseMove(event) {
101 // Pretend that nothing has happened. 97 // Pretend that nothing has happened.
102 if (this._eventInHoverElement(event)) 98 if (this._eventInScheduledContent(event))
103 return; 99 return;
104 100
105 this._startHidePopoverTimer(); 101 this._startHidePopoverTimer();
106 this._handleMouseAction(event, false); 102 this._stopShowPopoverTimer();
103 this._startShowPopoverTimer(
104 event, this.isPopoverVisible() ? Math.max(this._timeout * 0.6, this._hid eTimeout) : this._timeout);
107 } 105 }
108 106
109 _popoverMouseOut(event) { 107 /**
110 if (!this.isPopoverVisible()) 108 * @param {!Event} event
109 */
110 _popoverMouseMove(event) {
111 this._stopHidePopoverTimer();
112 }
113
114 /**
115 * @param {!UI.GlassPane} popover
116 * @param {!Event} event
117 */
118 _popoverMouseOut(popover, event) {
119 if (!popover.isShowing())
111 return; 120 return;
112 if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(this._pop over.contentElement)) 121 if (event.relatedTarget && !event.relatedTarget.isSelfOrDescendant(popover.c ontentElement))
113 this._startHidePopoverTimer(); 122 this._startHidePopoverTimer();
114 } 123 }
115 124
125 /**
126 * @param {!Event} event
127 */
116 _mouseOut(event) { 128 _mouseOut(event) {
117 if (!this.isPopoverVisible()) 129 if (!this.isPopoverVisible())
118 return; 130 return;
119 if (!this._eventInHoverElement(event)) 131 if (!this._eventInScheduledContent(event))
120 this._startHidePopoverTimer(); 132 this._startHidePopoverTimer();
121 } 133 }
122 134
123 _startHidePopoverTimer() { 135 _startHidePopoverTimer() {
124 // User has 500ms (this._hideTimeout) to reach the popup. 136 // User has this._hideTimeout to reach the popup.
125 if (!this._popover || this._hidePopoverTimer) 137 if (!this._hidePopoverCallback || this._hidePopoverTimer)
126 return; 138 return;
127 139
128 /** 140 this._hidePopoverTimer = setTimeout(() => {
129 * @this {UI.PopoverHelper}
130 */
131 function doHide() {
132 this._hidePopover(); 141 this._hidePopover();
133 delete this._hidePopoverTimer; 142 delete this._hidePopoverTimer;
134 } 143 }, this._hideTimeout);
135 this._hidePopoverTimer = setTimeout(doHide.bind(this), this._hideTimeout);
136 } 144 }
137 145
138 _handleMouseAction(event, isMouseDown) { 146 /**
139 this._resetHoverTimer(); 147 * @param {!Event} event
148 * @param {number} timeout
149 */
150 _startShowPopoverTimer(event, timeout) {
140 if (event.which && this._disableOnClick) 151 if (event.which && this._disableOnClick)
141 return; 152 return;
142 this._hoverElement = this._getAnchor(event.target, event); 153
143 if (!this._hoverElement) 154 this._scheduledContent = this._getContent.call(null, event);
155 if (!this._scheduledContent)
144 return; 156 return;
145 const toolTipDelay = isMouseDown ? 0 : (this._popup ? this._timeout * 0.6 : this._timeout); 157
146 this._hoverTimer = 158 this._showPopoverTimer = setTimeout(() => {
147 setTimeout(this._mouseHover.bind(this, this._hoverElement, event.target. ownerDocument), toolTipDelay); 159 delete this._showPopoverTimer;
160 this._showPopover(event.target.ownerDocument);
161 }, timeout);
148 } 162 }
149 163
150 _resetHoverTimer() { 164 _stopShowPopoverTimer() {
151 if (this._hoverTimer) { 165 if (!this._showPopoverTimer)
152 clearTimeout(this._hoverTimer); 166 return;
153 delete this._hoverTimer; 167 clearTimeout(this._showPopoverTimer);
154 } 168 delete this._showPopoverTimer;
155 } 169 }
156 170
157 /** 171 /**
158 * @return {boolean} 172 * @return {boolean}
159 */ 173 */
160 isPopoverVisible() { 174 isPopoverVisible() {
161 return !!this._popover; 175 return !!this._hidePopoverCallback;
162 } 176 }
163 177
164 hidePopover() { 178 hidePopover() {
165 this._resetHoverTimer(); 179 this._stopShowPopoverTimer();
166 this._hidePopover(); 180 this._hidePopover();
167 } 181 }
168 182
169 _hidePopover() { 183 _hidePopover() {
170 if (!this._popover) 184 if (!this._hidePopoverCallback)
171 return; 185 return;
172 186 this._hidePopoverCallback.call(null);
173 delete UI.PopoverHelper._popover; 187 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 } 188 }
182 189
183 _mouseHover(element, document) { 190 /**
184 delete this._hoverTimer; 191 * @param {!Document} document
185 this._hoverElement = element; 192 */
186 this._hidePopover(); 193 _showPopover(document) {
194 var popover = new UI.GlassPane();
195 popover.registerRequiredCSS('ui/popover.css');
196 popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
197 popover.setBlockPointerEvents(false);
198 popover.setShowArrow(true);
199 var content = this._scheduledContent;
200 content.show.call(null, popover).then(success => {
201 if (!success || this._scheduledContent !== content)
202 return;
187 203
188 this._popover = new UI.GlassPane(); 204 // This should not happen, but we hide previous popover to be on the safe side.
189 this._popover.registerRequiredCSS('ui/popover.css'); 205 if (UI.PopoverHelper._popoverHelper)
190 this._popover.setBlockPointerEvents(false); 206 UI.PopoverHelper._popoverHelper.hidePopover();
191 this._popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent); 207 UI.PopoverHelper._popoverHelper = this;
192 this._popover.setShowArrow(true);
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 208
199 // This should not happen, but we hide previous popover to be on the safe si de. 209 popover.contentElement.classList.toggle('has-padding', this._hasPadding);
200 if (UI.PopoverHelper._popover) { 210 popover.contentElement.addEventListener('mousemove', this._popoverMouseMov e.bind(this), true);
201 console.error('One popover is already visible'); 211 popover.contentElement.addEventListener('mouseout', this._popoverMouseOut. bind(this, popover), true);
202 UI.PopoverHelper._popover.hide(); 212 popover.setContentAnchorBox(content.box);
203 } 213 popover.show(document);
204 UI.PopoverHelper._popover = this._popover; 214
205 var popover = this._popover; 215 this._hidePopoverCallback = () => {
206 this._showPopover(element, this._popover).then(success => { 216 if (content.hide)
207 if (success && this._popover === popover && this._hoverElement === element ) 217 content.hide.call(null);
208 popover.show(document); 218 popover.hide();
219 delete UI.PopoverHelper._popoverHelper;
220 };
209 }); 221 });
210 } 222 }
211 223
212 _killHidePopoverTimer() { 224 _stopHidePopoverTimer() {
213 if (this._hidePopoverTimer) { 225 if (!this._hidePopoverTimer)
214 clearTimeout(this._hidePopoverTimer); 226 return;
215 delete this._hidePopoverTimer; 227 clearTimeout(this._hidePopoverTimer);
228 delete this._hidePopoverTimer;
216 229
217 // We know that we reached the popup, but we might have moved over other e lements. 230 // We know that we reached the popup, but we might have moved over other ele ments.
218 // Discard pending command. 231 // Discard pending command.
219 this._resetHoverTimer(); 232 this._stopShowPopoverTimer();
220 }
221 } 233 }
222 }; 234 };
235
236 /** @typedef {{box: !AnchorBox, show:(function(!UI.GlassPane):!Promise<boolean>) , hide:(function()|undefined)}} */
dgozman 2017/03/14 00:15:29 I'm thinking about onShowPopover and onHidePopover
237 UI.PopoverContent;
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/devtools/front_end/sources/JavaScriptSourceFrame.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698