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(128, 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 
155                         foreach(Part part; incActivePuppet().getRootParts()) {
156                             import std.stdio : writeln;
157                             debug writeln(part);
158                         }
159                         break;
160 
161                     // Allow dragging PSD in to main window
162                     case ".psd":
163                         incImportPSD(file);
164                         break mainLoop;
165 
166                     default: break;
167                     }
168                 }
169 
170                 // Finish the file drag
171                 incFinishFileDrag();
172             }
173 
174             igEndDragDropTarget();
175         }
176 
177         // BOTTOM VIEWPORT CONTROLS
178         igGetContentRegionAvail(&currSize);
179         igBeginChild("##ViewportControls", ImVec2(0, currSize.y), false, flags.NoScrollbar);
180             igPushItemWidth(72);
181                 igSpacing();
182                 igSameLine(0, 8);
183                 if (igSliderFloat(
184                     "##Zoom", 
185                     &incViewportZoom, 
186                     incVIEWPORT_ZOOM_MIN, 
187                     incVIEWPORT_ZOOM_MAX, 
188                     "%s%%\0".format(cast(int)(incViewportZoom*100)).ptr, 
189                     ImGuiSliderFlags.NoRoundToFormat)
190                 ) {
191                     camera.scale = vec2(incViewportZoom);
192                     incViewportTargetZoom = incViewportZoom;
193                 }
194                 if (incViewportTargetZoom != 1) {
195                     igPushFont(incIconFont());
196                         igSameLine(0, 8);
197                         if (igButton("", ImVec2(0, 0))) {
198                             incViewportTargetZoom = 1;
199                         }
200                     igPopFont();
201                 }
202                 igSameLine(0, 8);
203                 igSeparatorEx(ImGuiSeparatorFlags.Vertical);
204 
205                 igSameLine(0, 8);
206                 igText("x = %.2f y = %.2f", incViewportTargetPosition.x, incViewportTargetPosition.y);
207                 if (incViewportTargetPosition != vec2(0)) {
208                     igSameLine(0, 8);
209                     igPushFont(incIconFont());
210                         if (igButton("##2", ImVec2(0, 0))) {
211                             incViewportTargetPosition = vec2(0, 0);
212                         }
213                     igPopFont();
214                 }
215 
216 
217             igPopItemWidth();
218         igEndChild();
219 
220         // Handle smooth move
221         incViewportZoom = dampen(incViewportZoom, incViewportTargetZoom, deltaTime, 1);
222         camera.scale = vec2(incViewportZoom, incViewportZoom);
223         camera.position = vec2(dampen(camera.position, incViewportTargetPosition, deltaTime, 1.5));
224     }
225 
226 public:
227     this() {
228         super("Viewport", _("Viewport"), true);
229         this.alwaysVisible = true;
230     }
231 
232 }
233 
234 mixin incPanel!ViewportPanel;