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;