1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.panels.viewport;
8 import creator.viewport;
9 import creator.widgets;
10 import creator.core;
11 import creator.core.colorbleed;
12 import creator.panels;
13 import creator.actions;
14 import creator;
15 import inochi2d;
16 import inochi2d.core.dbg;
17 import bindbc.imgui;
18 import std.string;
19 import i18n;
20 
21 /**
22     A viewport
23 */
24 class ViewportPanel : Panel {
25 private:
26     ImVec2 lastSize;
27     bool actingInViewport;
28 
29 protected:
30     override
31     void onBeginUpdate() {
32         
33         ImGuiWindowClass wmclass;
34         wmclass.DockNodeFlagsOverrideSet = ImGuiDockNodeFlagsI.NoTabBar;
35         igSetNextWindowClass(&wmclass);
36         igPushStyleVar(ImGuiStyleVar.WindowPadding, ImVec2(1, 2));
37         igSetNextWindowDockID(incGetViewportDockSpace(), ImGuiCond.Always);
38         super.onBeginUpdate();
39     }
40 
41     override void onEndUpdate() {
42         super.onEndUpdate();
43         igPopStyleVar();
44     }
45 
46     override
47     void onUpdate() {
48 
49         auto io = igGetIO();
50         auto camera = inGetCamera();
51 
52         // Draw viewport itself
53         ImVec2 currSize;
54         igGetContentRegionAvail(&currSize);
55 
56         // We do not want the viewport to be NaN
57         // That will crash the app
58         if (currSize.x.isNaN || currSize.y.isNaN) {
59             currSize = ImVec2(0, 0);
60         }
61 
62         // Resize Inochi2D viewport according to frame
63         // Also viewport of 0 is too small, minimum 128.
64         currSize = ImVec2(clamp(currSize.x, 128, float.max), clamp(currSize.y, 128, float.max));
65         
66 
67         igBeginChild("##ViewportView", ImVec2(0, -30));
68             igGetContentRegionAvail(&currSize);
69             currSize = ImVec2(
70                 clamp(currSize.x, 128, float.max), 
71                 clamp(currSize.y, 128, float.max)-4
72             );
73 
74             if (currSize != lastSize) {
75                 inSetViewport(cast(int)currSize.x, cast(int)currSize.y);
76             }
77 
78             incViewportPoll();
79 
80             // Ignore events within child windows *unless* drag started within
81             // viewport.
82             ImGuiHoveredFlags winFlags = ImGuiHoveredFlags.None;
83             if (actingInViewport) winFlags |= ImGuiHoveredFlags.ChildWindows | ImGuiHoveredFlags.AllowWhenBlockedByActiveItem;
84             if (igIsWindowHovered(winFlags)) {
85                 actingInViewport = igIsMouseDown(ImGuiMouseButton.Left) ||
86                     igIsMouseDown(ImGuiMouseButton.Middle) ||
87                     igIsMouseDown(ImGuiMouseButton.Right);
88                 incViewportUpdate();
89             }
90 
91             auto style = igGetStyle();
92             inSetClearColor(style.Colors[ImGuiCol.WindowBg].x, style.Colors[ImGuiCol.WindowBg].y, style.Colors[ImGuiCol.WindowBg].z, 1);
93             incViewportDraw();
94 
95             int width, height;
96             inGetViewport(width, height);
97 
98             // Render our viewport
99             ImVec2 sPos;
100             ImVec2 sPosA;
101             igGetCursorScreenPos(&sPos);
102             
103             igImage(
104                 cast(void*)inGetRenderImage(), 
105                 ImVec2(width, height), 
106                 ImVec2(0, 1), 
107                 ImVec2(1, 0), 
108                 ImVec4(1, 1, 1, 1), ImVec4(0, 0, 0, 0)
109             );
110             igGetCursorScreenPos(&sPosA);
111 
112             // Render our fancy in-viewport buttons
113             igSetCursorScreenPos(ImVec2(sPos.x+8, sPos.y+8));
114                 igSetItemAllowOverlap();
115                 
116                 igPushStyleVar(ImGuiStyleVar.FrameRounding, 0);
117                     igBeginChild("##ViewportMainControls", ImVec2(200, 28 * incGetUIScale()));
118                         igPushStyleVar_Vec2(ImGuiStyleVar.FramePadding, ImVec2(6, 6));
119                             incViewportDrawOverlay();
120                         igPopStyleVar();
121                     igEndChild();
122                 igPopStyleVar();
123 
124             igSetCursorScreenPos(sPosA);
125 
126             lastSize = currSize;
127         igEndChild();
128 
129 
130         // FILE DRAG & DROP
131         if (igBeginDragDropTarget()) {
132             ImGuiPayload* payload = igAcceptDragDropPayload("__PARTS_DROP");
133             if (payload !is null) {
134                 string[] files = *cast(string[]*)payload.Data;
135                 import std.path : baseName, extension;
136                 import std.uni : toLower;
137                 mainLoop: foreach(file; files) {
138                     string fname = file.baseName;
139 
140                     switch(fname.extension.toLower) {
141                     case ".png", ".tga", ".jpeg", ".jpg":
142 
143                         auto tex = new ShallowTexture(file);
144                         incColorBleedPixels(tex);
145                         inTexPremultiply(tex.data);
146                         incAddChildWithHistory(
147                             inCreateSimplePart(*tex, null, fname), 
148                             incSelectedNode(), 
149                             fname
150                         );
151 
152                         // We've added new stuff, rescan nodes
153                         incActivePuppet().rescanNodes();
154                         break;
155 
156                     // Allow dragging PSD in to main window
157                     case ".psd":
158                         incImportPSD(file);
159                         break mainLoop;
160 
161                     default: break;
162                     }
163                 }
164 
165                 // Finish the file drag
166                 incFinishFileDrag();
167             }
168 
169             igEndDragDropTarget();
170         }
171 
172         // BOTTOM VIEWPORT CONTROLS
173         igGetContentRegionAvail(&currSize);
174         igBeginChild("##ViewportControls", ImVec2(0, currSize.y), false, flags.NoScrollbar);
175             igPushItemWidth(72);
176                 igSpacing();
177                 igSameLine(0, 8);
178                 if (igSliderFloat(
179                     "##Zoom", 
180                     &incViewportZoom, 
181                     incVIEWPORT_ZOOM_MIN, 
182                     incVIEWPORT_ZOOM_MAX, 
183                     "%s%%\0".format(cast(int)(incViewportZoom*100)).ptr, 
184                     ImGuiSliderFlags.NoRoundToFormat)
185                 ) {
186                     camera.scale = vec2(incViewportZoom);
187                     incViewportTargetZoom = incViewportZoom;
188                 }
189                 if (incViewportTargetZoom != 1) {
190                     igPushFont(incIconFont());
191                         igSameLine(0, 8);
192                         if (igButton("", ImVec2(0, 0))) {
193                             incViewportTargetZoom = 1;
194                         }
195                     igPopFont();
196                 }
197                 igSameLine(0, 8);
198                 igSeparatorEx(ImGuiSeparatorFlags.Vertical);
199 
200                 igSameLine(0, 8);
201                 igText("x = %.2f y = %.2f", incViewportTargetPosition.x, incViewportTargetPosition.y);
202                 if (incViewportTargetPosition != vec2(0)) {
203                     igSameLine(0, 8);
204                     igPushFont(incIconFont());
205                         if (igButton("##2", ImVec2(0, 0))) {
206                             incViewportTargetPosition = vec2(0, 0);
207                         }
208                     igPopFont();
209                 }
210 
211 
212             igPopItemWidth();
213         igEndChild();
214 
215         // Handle smooth move
216         incViewportZoom = dampen(incViewportZoom, incViewportTargetZoom, deltaTime, 1);
217         camera.scale = vec2(incViewportZoom, incViewportZoom);
218         camera.position = vec2(dampen(camera.position, incViewportTargetPosition, deltaTime, 1.5));
219     }
220 
221 public:
222     this() {
223         super("Viewport", _("Viewport"), true);
224         this.alwaysVisible = true;
225     }
226 
227 }
228 
229 mixin incPanel!ViewportPanel;