96952a9d8adbfd47067270e43ee1ace819fafd7f
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ShaderProgramContentView.js
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
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 WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentView
27 {
28     constructor(shaderProgram)
29     {
30         console.assert(shaderProgram instanceof WI.ShaderProgram);
31
32         super(shaderProgram);
33
34         let isWebGPU = this.representedObject.contextType === WI.Canvas.ContextType.WebGPU;
35
36         this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh", WI.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
37         this._refreshButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
38         this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshContent, this);
39
40         let contentDidChangeDebouncer = new Debouncer((event) => {
41             this._contentDidChange(event);
42         });
43
44         this.element.classList.add("shader-program", this.representedObject.programType);
45
46         let createEditor = (shaderType) => {
47             let container = this.element.appendChild(document.createElement("div"));
48
49             let header = container.appendChild(document.createElement("header"));
50
51             let shaderTypeContainer = header.appendChild(document.createElement("div"));
52             shaderTypeContainer.classList.add("shader-type");
53
54             let textEditor = new WI.TextEditor;
55             textEditor.readOnly = false;
56             textEditor.addEventListener(WI.TextEditor.Event.Focused, this._editorFocused, this);
57             textEditor.addEventListener(WI.TextEditor.Event.NumberOfSearchResultsDidChange, this._numberOfSearchResultsDidChange, this);
58             textEditor.addEventListener(WI.TextEditor.Event.ContentDidChange, (event) => {
59                 contentDidChangeDebouncer.delayForTime(250, event);
60             }, this);
61
62             switch (shaderType) {
63             case WI.ShaderProgram.ShaderType.Compute:
64                 shaderTypeContainer.textContent = WI.UIString("Compute Shader");
65                 textEditor.mimeType = isWebGPU ? "x-pipeline/x-compute" : "x-shader/x-compute";
66                 break;
67
68             case WI.ShaderProgram.ShaderType.Fragment:
69                 shaderTypeContainer.textContent = WI.UIString("Fragment Shader");
70                 textEditor.mimeType = isWebGPU ? "x-pipeline/x-fragment" : "x-shader/x-fragment";
71                 break;
72
73             case WI.ShaderProgram.ShaderType.Vertex:
74                 shaderTypeContainer.textContent = WI.UIString("Vertex Shader");
75                 textEditor.mimeType = isWebGPU ? "x-pipeline/x-vertex" : "x-shader/x-vertex";
76                 break;
77             }
78
79             this.addSubview(textEditor);
80             container.appendChild(textEditor.element);
81             container.classList.add("shader", shaderType);
82
83             return {container, textEditor};
84         };
85
86         switch (this.representedObject.programType) {
87         case WI.ShaderProgram.ProgramType.Compute: {
88             let computeEditor = createEditor(WI.ShaderProgram.ShaderType.Compute);
89             this._computeContainer = computeEditor.container;
90             this._computeEditor = computeEditor.textEditor;
91
92             this._lastActiveEditor = this._computeEditor;
93             break;
94         }
95
96         case WI.ShaderProgram.ProgramType.Render: {
97             let vertexEditor = createEditor(WI.ShaderProgram.ShaderType.Vertex);
98             this._vertexContainer = vertexEditor.container;
99             this._vertexEditor = vertexEditor.textEditor;
100
101             let fragmentEditor = createEditor(WI.ShaderProgram.ShaderType.Fragment);
102             this._fragmentContainer = fragmentEditor.container;
103             this._fragmentEditor = fragmentEditor.textEditor;
104
105             this._lastActiveEditor = this._vertexEditor;
106             break;
107         }
108         }
109     }
110
111     // Public
112
113     get navigationItems()
114     {
115         return [this._refreshButtonNavigationItem];
116     }
117
118     // Protected
119
120     shown()
121     {
122         super.shown();
123
124         switch (this.representedObject.programType) {
125         case WI.ShaderProgram.ProgramType.Compute:
126             this._computeEditor.shown();
127             break;
128
129         case WI.ShaderProgram.ProgramType.Render:
130             this._vertexEditor.shown();
131             this._fragmentEditor.shown();
132             break;
133         }
134
135         this._refreshContent();
136     }
137
138     hidden()
139     {
140         switch (this.representedObject.programType) {
141         case WI.ShaderProgram.ProgramType.Compute:
142             this._computeEditor.hidden();
143             break;
144
145         case WI.ShaderProgram.ProgramType.Render:
146             this._vertexEditor.hidden();
147             this._fragmentEditor.hidden();
148             break;
149         }
150
151         super.hidden();
152     }
153
154     get supportsSave()
155     {
156         return true;
157     }
158
159     get saveData()
160     {
161         let filename = "";
162         switch (this._lastActiveEditor) {
163         case this._computeEditor:
164             filename = WI.UIString("Compute");
165             break;
166         case this._fragmentEditor:
167             filename = WI.UIString("Fragment");
168             break;
169         case this._vertexEditor:
170             filename = WI.UIString("Vertex");
171             break;
172         }
173         console.assert(filename);
174
175         let extension = "";
176         switch (this.representedObject.canvas.contextType) {
177         case WI.Canvas.ContextType.WebGL:
178         case WI.Canvas.ContextType.WebGL2:
179             extension = WI.unlocalizedString(".glsl");
180             break;
181         case WI.Canvas.ContextType.WebGPU:
182             extension = WI.unlocalizedString(".wsl");
183             break;
184         }
185         console.assert(extension);
186
187         return {
188             url: WI.FileUtilities.inspectorURLForFilename(filename + extension),
189             content: this._lastActiveEditor.string,
190             forceSaveAs: true,
191         };
192     }
193
194     get supportsSearch()
195     {
196         return true;
197     }
198
199     get numberOfSearchResults()
200     {
201         return this._lastActiveEditor.numberOfSearchResults;
202     }
203
204     get hasPerformedSearch()
205     {
206         return this._lastActiveEditor.currentSearchQuery !== null;
207     }
208
209     set automaticallyRevealFirstSearchResult(reveal)
210     {
211         this._lastActiveEditor.automaticallyRevealFirstSearchResult = reveal;
212     }
213
214     performSearch(query)
215     {
216         this._lastActiveEditor.performSearch(query);
217     }
218
219     searchCleared()
220     {
221         this._lastActiveEditor.searchCleared();
222     }
223
224     searchQueryWithSelection()
225     {
226         return this._lastActiveEditor.searchQueryWithSelection();
227     }
228
229     revealPreviousSearchResult(changeFocus)
230     {
231         this._lastActiveEditor.revealPreviousSearchResult(changeFocus);
232     }
233
234     revealNextSearchResult(changeFocus)
235     {
236         this._lastActiveEditor.revealNextSearchResult(changeFocus);
237     }
238
239     revealPosition(position, textRangeToSelect, forceUnformatted)
240     {
241         this._lastActiveEditor.revealPosition(position, textRangeToSelect, forceUnformatted);
242     }
243
244     // Private
245
246     _refreshContent()
247     {
248         let createCallback = (container, textEditor) => {
249             return (source, entryPoint) => {
250                 if (source === null) {
251                     container.remove();
252                     return;
253                 }
254
255                 if (!container.parentNode) {
256                     switch (container) {
257                     case this._computeContainer:
258                     case this._vertexContainer:
259                         this.element.insertAdjacentElement("afterbegin", container);
260                         break;
261
262                     case this._fragmentContainer:
263                         this.element.insertAdjacentElement("beforeend", container);
264                         break;
265                     }
266                 }
267
268                 textEditor.string = source || "";
269             };
270         };
271
272         switch (this.representedObject.programType) {
273         case WI.ShaderProgram.ProgramType.Compute:
274             this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Compute, createCallback(this._computeContainer, this._computeEditor));
275             return;
276
277         case WI.ShaderProgram.ProgramType.Render:
278             this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Vertex, createCallback(this._vertexContainer, this._vertexEditor));
279             this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Fragment, createCallback(this._fragmentContainer, this._fragmentEditor));
280             return;
281         }
282
283         console.assert();
284     }
285
286     _updateShader(shaderType)
287     {
288         switch (shaderType) {
289         case WI.ShaderProgram.ShaderType.Compute:
290             this.representedObject.updateShader(shaderType, this._computeEditor.string);
291             return;
292
293         case WI.ShaderProgram.ShaderType.Fragment:
294             this.representedObject.updateShader(shaderType, this._fragmentEditor.string);
295             return;
296
297         case WI.ShaderProgram.ShaderType.Vertex:
298             this.representedObject.updateShader(shaderType, this._vertexEditor.string);
299             return;
300         }
301
302         console.assert();
303     }
304
305     _editorFocused(event)
306     {
307         if (this._lastActiveEditor === event.target)
308             return;
309
310         let currentSearchQuery = null;
311
312         if (this._lastActiveEditor) {
313             currentSearchQuery = this._lastActiveEditor.currentSearchQuery;
314
315             this._lastActiveEditor.searchCleared();
316         }
317
318         this._lastActiveEditor = event.target;
319
320         if (currentSearchQuery)
321             this._lastActiveEditor.performSearch(currentSearchQuery);
322     }
323
324     _numberOfSearchResultsDidChange(event)
325     {
326         this.dispatchEventToListeners(WI.ContentView.Event.NumberOfSearchResultsDidChange);
327     }
328
329     _contentDidChange(event)
330     {
331         switch (event.target) {
332         case this._computeEditor:
333             this._updateShader(WI.ShaderProgram.ShaderType.Compute);
334             return;
335
336         case this._fragmentEditor:
337             this._updateShader(WI.ShaderProgram.ShaderType.Fragment);
338             return;
339
340         case this._vertexEditor:
341             this._updateShader(WI.ShaderProgram.ShaderType.Vertex);
342             return;
343         }
344
345         console.assert();
346     }
347 };