Web Inspector: unify resizer implementations used by DataGrid and Sidebar
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / Resizer.js
1 /*
2  * Copyright (C) 2015 University of Washington.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.Resizer = function(ruleOrientation, delegate) {
27     // FIXME: Convert this to a WebInspector.Object subclass, and call super().
28     // WebInspector.Object.call(this);
29
30     console.assert(delegate);
31
32     this._delegate = delegate;
33     this._orientation = ruleOrientation;
34     this._element = document.createElement("div");
35     this._element.classList.add(WebInspector.Resizer.StyleClassName);
36
37     if (this._orientation === WebInspector.Resizer.RuleOrientation.Horizontal)
38         this._element.classList.add(WebInspector.Resizer.HorizontalRuleStyleClassName);
39     else if (this._orientation === WebInspector.Resizer.RuleOrientation.Vertical)
40         this._element.classList.add(WebInspector.Resizer.VerticalRuleStyleClassName);
41
42     this._element.addEventListener("mousedown", this._resizerMouseDown.bind(this), false);
43     this._resizerMouseMovedEventListener = this._resizerMouseMoved.bind(this);
44     this._resizerMouseUpEventListener = this._resizerMouseUp.bind(this);
45 };
46
47 WebInspector.Resizer.RuleOrientation = {
48     Horizontal: Symbol("resizer-rule-orientation-horizontal"),
49     Vertical: Symbol("resizer-rule-orientation-vertical"),
50 };
51
52 WebInspector.Resizer.StyleClassName = "resizer";
53 WebInspector.Resizer.HorizontalRuleStyleClassName = "horizontal-rule";
54 WebInspector.Resizer.VerticalRuleStyleClassName = "vertical-rule";
55 WebInspector.Resizer.GlassPaneStyleClassName = "glass-pane-for-drag";
56
57 WebInspector.Resizer.prototype = {
58     constructor: WebInspector.Resizer,
59     __proto__: WebInspector.Object.prototype,
60
61     // Public
62
63     get element()
64     {
65         return this._element;
66     },
67
68     get orientation()
69     {
70         return this._orientation;
71     },
72
73     get initialPosition()
74     {
75         return this._resizerMouseDownPosition || NaN;
76     },
77
78     // Private
79
80     _currentPosition: function()
81     {
82         if (this._orientation === WebInspector.Resizer.RuleOrientation.Vertical)
83             return event.pageX;
84         if (this._orientation === WebInspector.Resizer.RuleOrientation.Horizontal)
85             return event.pageY;
86
87         console.assert(false, "Should not be reached!");
88     },
89
90     _resizerMouseDown: function(event)
91     {
92         if (event.button !== 0 || event.ctrlKey)
93             return;
94
95         this._resizerMouseDownPosition = this._currentPosition();
96
97         var delegateRequestedAbort = false;
98         if (typeof this._delegate.resizerDragStarted === "function")
99             delegateRequestedAbort = this._delegate.resizerDragStarted(this, event.target);
100
101         if (delegateRequestedAbort) {
102             delete this._resizerMouseDownPosition;
103             return;
104         }
105
106         if (this._orientation === WebInspector.Resizer.RuleOrientation.Vertical)
107             document.body.style.cursor = "col-resize";
108         else {
109             console.assert(this._orientation === WebInspector.Resizer.RuleOrientation.Horizontal);
110             document.body.style.cursor = "row-resize";
111         }
112
113         // Register these listeners on the document so we can track the mouse if it leaves the resizer.
114         document.addEventListener("mousemove", this._resizerMouseMovedEventListener, false);
115         document.addEventListener("mouseup", this._resizerMouseUpEventListener, false);
116
117         event.preventDefault();
118         event.stopPropagation();
119
120         // Install a global "glass pane" which prevents cursor from changing during the drag interaction.
121         // The cursor could change when hovering over links, text, or other elements with cursor cues.
122         // FIXME: when Pointer Events support is available this could be implemented by drawing the cursor ourselves.
123         if (WebInspector._elementDraggingGlassPane)
124             WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
125
126         var glassPaneElement = document.createElement("div");
127         glassPaneElement.className = WebInspector.Resizer.GlassPaneStyleClassName;
128         document.body.appendChild(glassPaneElement);
129         WebInspector._elementDraggingGlassPane = glassPaneElement;
130     },
131
132     _resizerMouseMoved: function(event)
133     {
134         event.preventDefault();
135         event.stopPropagation();
136
137         if (typeof this._delegate.resizerDragging === "function")
138             this._delegate.resizerDragging(this, this._resizerMouseDownPosition - this._currentPosition());
139     },
140
141     _resizerMouseUp: function(event)
142     {
143         if (event.button !== 0 || event.ctrlKey)
144             return;
145
146         document.body.style.removeProperty("cursor");
147
148         if (WebInspector._elementDraggingGlassPane) {
149             WebInspector._elementDraggingGlassPane.parentElement.removeChild(WebInspector._elementDraggingGlassPane);
150             delete WebInspector._elementDraggingGlassPane;
151         }
152
153         document.removeEventListener("mousemove", this._resizerMouseMovedEventListener, false);
154         document.removeEventListener("mouseup", this._resizerMouseUpEventListener, false);
155
156         event.preventDefault();
157         event.stopPropagation();
158
159         if (typeof this._delegate.resizerDragEnded === "function")
160             this._delegate.resizerDragEnded(this);
161
162         delete this._resizerMouseDownPosition;
163     }
164 };