Web Inspector: Allow user to change dock side by dragging toolbar
[WebKit-https.git] / Source / WebCore / inspector / front-end / Toolbar.js
1  /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
3  * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
4  * Copyright (C) 2009 Joseph Pecoraro
5  * Copyright (C) 2011 Google Inc. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution.
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /**
33  * @constructor
34  */
35 WebInspector.Toolbar = function()
36 {
37     this.element = document.getElementById("toolbar");
38     WebInspector.installDragHandle(this.element, this._toolbarDragStart.bind(this), this._toolbarDrag.bind(this), this._toolbarDragEnd.bind(this), "default");
39
40     this._dropdownButton = document.getElementById("toolbar-dropdown-arrow");
41     this._dropdownButton.addEventListener("click", this._toggleDropdown.bind(this), false);
42
43     document.getElementById("close-button-left").addEventListener("click", this._onClose, true);
44     document.getElementById("close-button-right").addEventListener("click", this._onClose, true);
45
46     this._isWindowMoveSupported = WebInspector.isMac() && !Preferences.showDockToRight;
47 }
48
49 WebInspector.Toolbar.prototype = {
50     resize: function()
51     {
52         this._updateDropdownButtonAndHideDropdown();
53     },
54
55     /**
56      * @param {WebInspector.PanelDescriptor} panelDescriptor
57      */
58     addPanel: function(panelDescriptor)
59     {
60         this.element.appendChild(this._createPanelToolbarItem(panelDescriptor));
61         this.resize();
62     },
63
64     /**
65      * @param {WebInspector.PanelDescriptor} panelDescriptor
66      * @return {Element}
67      */
68     _createPanelToolbarItem: function(panelDescriptor)
69     {
70         var toolbarItem = document.createElement("button");
71         toolbarItem.className = "toolbar-item toggleable";
72         toolbarItem.panelDescriptor = panelDescriptor;
73         toolbarItem.addStyleClass(panelDescriptor.name());
74
75         function onToolbarItemClicked()
76         {
77             this._updateDropdownButtonAndHideDropdown();
78             WebInspector.inspectorView.setCurrentPanel(panelDescriptor.panel());
79         }
80         toolbarItem.addEventListener("click", onToolbarItemClicked.bind(this), false);
81
82         function panelSelected()
83         {
84             if (WebInspector.inspectorView.currentPanel() && panelDescriptor.name() === WebInspector.inspectorView.currentPanel().name)
85                 toolbarItem.addStyleClass("toggled-on");
86             else
87                 toolbarItem.removeStyleClass("toggled-on");
88         }
89         WebInspector.inspectorView.addEventListener(WebInspector.InspectorView.Events.PanelSelected, panelSelected);
90
91         var iconElement = toolbarItem.createChild("div", "toolbar-icon");
92         toolbarItem.createChild("div", "toolbar-label").textContent = panelDescriptor.title();
93         if (panelDescriptor.iconURL()) {
94             iconElement.addStyleClass("custom-toolbar-icon");
95             iconElement.style.backgroundImage = "url(" + panelDescriptor.iconURL() + ")";
96         }
97         panelSelected();
98         return toolbarItem;
99     },
100
101     /**
102      * @return {boolean}
103      */
104     _isDockedToBottom: function()
105     {
106         return !!WebInspector.dockController && WebInspector.dockController.dockSide() == WebInspector.DockController.State.DockedToBottom;
107     },
108
109     /**
110      * @return {boolean}
111      */
112     _isUndocked: function()
113     {
114         return !!WebInspector.dockController && WebInspector.dockController.dockSide() == WebInspector.DockController.State.Undocked;
115     },
116
117     /**
118      * @return {boolean}
119      */
120     _toolbarDragStart: function(event)
121     {
122         if (this._isUndocked() && !this._isWindowMoveSupported)
123             return false;
124
125         var target = event.target;
126         if (target.hasStyleClass("toolbar-item") && target.hasStyleClass("toggleable"))
127             return false;
128
129         if (target !== this.element && !target.hasStyleClass("toolbar-item"))
130             return false;
131
132         this._lastScreenX = event.screenX;
133         this._lastScreenY = event.screenY;
134         this._lastHeightDuringDrag = window.innerHeight;
135         this._startDistanceToRight = window.innerWidth - event.clientX;
136         this._startDinstanceToBottom = window.innerHeight - event.clientY;
137         return true;
138     },
139
140     _toolbarDragEnd: function(event)
141     {
142         // We may not get the drag event at the end.
143         // Apply last changes manually.
144         this._toolbarDrag(event);
145         delete this._lastScreenX;
146         delete this._lastScreenY;
147         delete this._lastHeightDuringDrag;
148         delete this._startDistanceToRight;
149         delete this._startDinstanceToBottom;
150     },
151
152     _toolbarDrag: function(event)
153     {
154         event.preventDefault();
155
156         if (this._isUndocked())
157             return this._toolbarDragMoveWindow(event);
158
159         if (Preferences.showDockToRight)
160             return this._toolbarDragChangeDocking(event);
161
162         return this._toolbarDragChangeHeight(event);
163     },
164
165     _toolbarDragMoveWindow: function(event)
166     {
167         var x = event.screenX - this._lastScreenX;
168         var y = event.screenY - this._lastScreenY;
169         this._lastScreenX = event.screenX;
170         this._lastScreenY = event.screenY;
171         InspectorFrontendHost.moveWindowBy(x, y);
172     },
173
174     _toolbarDragChangeDocking: function(event)
175     {
176         if (this._isDockedToBottom()) {
177             var distanceToRight = window.innerWidth - event.clientX;
178             if (distanceToRight < this._startDistanceToRight * 2 / 3) {
179                 InspectorFrontendHost.requestSetDockSide(WebInspector.DockController.State.DockedToRight);
180                 return true;
181             }
182         } else {
183             var distanceToBottom = window.innerHeight - event.clientY;
184             if (distanceToBottom < this._startDinstanceToBottom * 2 / 3) {
185                 InspectorFrontendHost.requestSetDockSide(WebInspector.DockController.State.DockedToBottom);
186                 return true;
187             }
188         }
189     },
190
191     _toolbarDragChangeHeight: function(event)
192     {
193         // Change the inspector window height for dock-to-bottom only mode.
194         var height = this._lastHeightDuringDrag - (event.screenY - this._lastScreenY);
195         this._lastHeightDuringDrag = height;
196         this._lastScreenY = event.screenY;
197         InspectorFrontendHost.setAttachedWindowHeight(height);
198     },
199
200     _onClose: function()
201     {
202         WebInspector.close();
203     },
204
205     _setDropdownVisible: function(visible)
206     {
207         if (!this._dropdown) {
208             if (!visible)
209                 return;
210             this._dropdown = new WebInspector.ToolbarDropdown(this);
211         }
212         if (visible)
213             this._dropdown.show();
214         else
215             this._dropdown.hide();
216     },
217
218     _toggleDropdown: function()
219     {
220         this._setDropdownVisible(!this._dropdown || !this._dropdown.visible);
221     },
222
223     _updateDropdownButtonAndHideDropdown: function()
224     {
225         WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateDropdownButtonAndHideDropdown);
226     },
227
228     _innerUpdateDropdownButtonAndHideDropdown: function()
229     {
230         this._setDropdownVisible(false);
231
232         if (this.element.scrollHeight > this.element.offsetHeight)
233             this._dropdownButton.removeStyleClass("hidden");
234         else
235             this._dropdownButton.addStyleClass("hidden");
236     }
237 }
238
239 /**
240  * @constructor
241  * @param {WebInspector.Toolbar} toolbar
242  */
243 WebInspector.ToolbarDropdown = function(toolbar)
244 {
245     this._toolbar = toolbar;
246     this._arrow = document.getElementById("toolbar-dropdown-arrow");
247     this.element = document.createElement("div");
248     this.element.id = "toolbar-dropdown";
249     this.element.className = "toolbar-small";
250     this._contentElement = this.element.createChild("div", "scrollable-content");
251     this._contentElement.tabIndex = 0;
252     this._contentElement.addEventListener("keydown", this._onKeyDown.bind(this), true);
253 }
254
255 WebInspector.ToolbarDropdown.prototype = {
256     show: function()
257     {
258         if (this.visible)
259             return;
260         var style = this.element.style;
261         this._populate();
262         var top = this._arrow.totalOffsetTop() + this._arrow.clientHeight;
263         this._arrow.addStyleClass("dropdown-visible");
264         this.element.style.top = top + "px";
265         this.element.style.right = window.innerWidth - this._arrow.totalOffsetLeft() - this._arrow.clientWidth + "px";
266         this._contentElement.style.maxHeight = window.innerHeight - top - 20 + "px";
267         this._toolbar.element.appendChild(this.element);
268     },
269
270     hide: function()
271     {
272         if (!this.visible)
273             return;
274         this._arrow.removeStyleClass("dropdown-visible");
275         this.element.parentNode.removeChild(this.element);
276         this._contentElement.removeChildren();
277     },
278
279     get visible()
280     {
281         return !!this.element.parentNode;
282     },
283
284     _populate: function()
285     {
286         var toolbarItems = this._toolbar.element.querySelectorAll(".toolbar-item.toggleable");
287
288         for (var i = 0; i < toolbarItems.length; ++i) {
289             if (toolbarItems[i].offsetTop > 1)
290                 this._contentElement.appendChild(this._toolbar._createPanelToolbarItem(toolbarItems[i].panelDescriptor));
291         }
292     },
293
294     _onKeyDown: function(event)
295     {
296         if (event.keyCode !== WebInspector.KeyboardShortcut.Keys.Esc.code)
297             return;
298         event.consume();
299         this.hide();
300     }
301 }
302
303 /**
304  * @type {?WebInspector.Toolbar}
305  */
306 WebInspector.toolbar = null;