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.windows.welcome; 8 import creator.widgets.label; 9 import creator.widgets.dummy; 10 import creator.windows; 11 import creator.core; 12 import creator.core.i18n; 13 import std.string; 14 import creator.utils.link; 15 import i18n; 16 import inochi2d; 17 import creator.ver; 18 import creator.io; 19 import creator; 20 import creator.config; 21 22 import creator.widgets.shadow; 23 24 class WelcomeWindow : Window { 25 private: 26 int step = 1; 27 Texture banner; 28 Texture bannerLogo; 29 ImVec2 origWindowPadding; 30 bool firstFrame = true; 31 bool changesRequiresRestart; 32 33 // Temporary variables for setup 34 int tmpUIScale; 35 36 ImVec2 uiSize; 37 ImDrawList* shadowDrawList; 38 39 protected: 40 override 41 void onBeginUpdate() { 42 flags |= ImGuiWindowFlags.NoResize; 43 flags |= ImGuiWindowFlags.NoDecoration; 44 45 ImVec2 wpos = ImVec2( 46 igGetMainViewport().Pos.x+(igGetMainViewport().Size.x/2), 47 igGetMainViewport().Pos.y+(igGetMainViewport().Size.y/2), 48 ); 49 50 igSetNextWindowPos(wpos, ImGuiCond.Always, ImVec2(0.5, 0.5)); 51 igSetNextWindowSize(uiSize, ImGuiCond.Appearing); 52 igSetNextWindowSizeConstraints(uiSize, uiSize); 53 origWindowPadding = igGetStyle().WindowPadding; 54 igPushStyleVar(ImGuiStyleVar.WindowPadding, ImVec2(0, 0)); 55 igPushStyleVar(ImGuiStyleVar.WindowRounding, 10); 56 super.onBeginUpdate(); 57 } 58 59 override 60 void onEndUpdate() { 61 igPopStyleVar(2); 62 super.onEndUpdate(); 63 } 64 65 override 66 void onUpdate() { 67 auto window = igGetCurrentWindow(); 68 incRenderWindowShadow( 69 shadowDrawList, 70 window.OuterRectClipped 71 ); 72 73 // Fix styling for subwindows 74 igPushStyleVar(ImGuiStyleVar.WindowPadding, origWindowPadding); 75 auto windowViewport = igGetWindowViewport(); 76 windowViewport.Flags |= ImGuiViewportFlags.TopMost; 77 windowViewport.Flags |= ImGuiViewportFlags.NoDecoration; 78 windowViewport.Flags |= ImGuiViewportFlags.NoTaskBarIcon; 79 80 81 ImVec2 origin; 82 igGetCursorStartPos(&origin); 83 if (igBeginChild("##BANNER", ImVec2(0, 200))) { 84 igPushStyleColor(ImGuiCol.Text, 0xFFFFFFFF); 85 ImVec2 spos; 86 igGetCursorScreenPos(&spos); 87 88 // Background 89 ImDrawList_AddImageRounded( 90 igGetWindowDrawList(), 91 cast(void*)banner.getTextureId(), 92 ImVec2(spos.x+1, spos.y+1), 93 ImVec2(spos.x+511, spos.y+199), 94 ImVec2(0, 0), 95 ImVec2(1, 1), 96 0xFFFFFFFF, 97 10, 98 ImDrawFlags.RoundCornersTop 99 ); 100 101 //Logo 102 igSetCursorPosY(origin.y+4); 103 version(InBranding) { 104 igImage(cast(void*)bannerLogo.getTextureId(), ImVec2(296, 100)); 105 } else { 106 igSetCursorPosY(origin.y+12); 107 igSetCursorPosX(origin.x+12); 108 igSetWindowFontScale(2); 109 incTextBordered("Inochi Creator"); 110 igSetWindowFontScale(1); 111 igSetCursorPosX(origin.x+12); 112 incTextBordered(_("Unsupported")); 113 } 114 115 // Version String 116 ImVec2 vsSize = incMeasureString(INC_VERSION); 117 igSetCursorPos(ImVec2(512-(vsSize.x+12), 12)); 118 incTextShadowed(INC_VERSION); 119 120 // Banner Artist Name 121 string artistString = INC_BANNER_ARTIST_NAME; 122 vsSize = incMeasureString(artistString); 123 igSetCursorPos(ImVec2(512-(vsSize.x+12), 200-(vsSize.y+8))); 124 incTextBordered(artistString); 125 126 if (igIsItemHovered()) { 127 igSetMouseCursor(ImGuiMouseCursor.Hand); 128 } 129 130 // Clicking artist link sends you to the artist's page 131 if (igIsItemClicked()) { 132 incOpenLink(INC_BANNER_ARTIST_PAGE); 133 } 134 igPopStyleColor(); 135 } 136 igEndChild(); 137 138 igIndent(); 139 if (igBeginChild("##CONFIG_AREA", ImVec2(-4, 0), false, ImGuiWindowFlags.NoScrollbar)) { 140 ImVec2 avail = incAvailableSpace(); 141 igPushTextWrapPos(avail.x); 142 switch(step) { 143 144 // SETUP PAGE 145 case 0: 146 incDummy(ImVec2(0, 4)); 147 148 incTextShadowed(_("Quick Setup")); 149 igNewLine(); 150 151 incDummy(ImVec2(avail.x/6, 64)); 152 igSameLine(0, 0); 153 igBeginGroup(); 154 igPushItemWidth(avail.x/3); 155 auto comboFlags = 156 ImGuiComboFlags.NoArrowButton | 157 ImGuiComboFlags.HeightLargest; 158 159 if(igBeginCombo(__("Language"), incLocaleCurrentName().toStringz, comboFlags)) { 160 if (igSelectable("English")) { 161 incLocaleSet(null); 162 changesRequiresRestart = true; 163 } 164 foreach(entry; incLocaleGetEntries()) { 165 if (igSelectable(entry.humanNameC)) { 166 incLocaleSet(entry.code); 167 changesRequiresRestart = true; 168 } 169 } 170 igEndCombo(); 171 } 172 173 if(igBeginCombo(__("Color Theme"), incGetDarkMode() ? __("Dark") : __("Light"), comboFlags)) { 174 if (igSelectable(__("Dark"), incGetDarkMode())) incSetDarkMode(true); 175 if (igSelectable(__("Light"), !incGetDarkMode())) incSetDarkMode(false); 176 177 igEndCombo(); 178 } 179 180 version (UseUIScaling) { 181 version(OSX) { 182 183 // macOS follows Retina scaling, skip showing this 184 } else { 185 if (igInputInt(__("UI Scale"), &tmpUIScale, 25, 50, ImGuiInputTextFlags.EnterReturnsTrue)) { 186 tmpUIScale = clamp(tmpUIScale, 100, 200); 187 incSetUIScale(cast(float)tmpUIScale/100.0); 188 } 189 } 190 } 191 192 if (changesRequiresRestart) { 193 igNewLine(); 194 igPushTextWrapPos(avail.x/1.15); 195 incTextColored( 196 ImVec4(0.8, 0.2, 0.2, 1), 197 _("Inochi Creator needs to be restarted for some changes to take effect.") 198 ); 199 igPopTextWrapPos(); 200 } 201 igPopItemWidth(); 202 igEndGroup(); 203 204 // Move down to where we want our button 205 incDummy(ImVec2(0, -32)); 206 207 // Move button to the right 208 incDummy(ImVec2(-64, 24)); 209 igSameLine(0, 0); 210 if (igButton(__("Next"), ImVec2(64, 24))) { 211 incSettingsSet!bool("hasDoneQuickSetup", true); 212 step++; 213 } 214 break; 215 216 // WELCOME PAGE 217 case 1: 218 incDummy(ImVec2(0, 4)); 219 220 // Left hand side 221 if (igBeginChild("##LHS", ImVec2((avail.x-8)/2, 0), false, ImGuiWindowFlags.NoScrollbar)) { 222 incTextShadowed(_("Create Project")); 223 incDummy(ImVec2(0, 2)); 224 igIndent(); 225 if (incTextLinkWithIcon("", _("New..."))) { 226 incNewProject(); 227 this.close(); 228 } 229 230 if (incTextLinkWithIcon("", _("Open..."))) { 231 const TFD_Filter[] filters = [ 232 { ["*.inx"], "Inochi Creator Project (*.inx)" } 233 ]; 234 235 string file = incShowOpenDialog(filters, _("Open...")); 236 if (file) { 237 incOpenProject(file); 238 this.close(); 239 } 240 } 241 242 243 if (incTextLinkWithIcon("", _("Import PSD..."))) { 244 if (incImportShowPSDDialog()) { 245 this.close(); 246 } 247 } 248 249 igUnindent(); 250 251 incDummy(ImVec2(0, 6)); 252 incTextShadowed(_("Recent Projects")); 253 incDummy(ImVec2(0, 2)); 254 igIndent(); 255 if (incGetPrevProjects().length > 0) { 256 foreach(i, recent; incGetPrevProjects()) { 257 if (i >= 4) break; 258 259 import std.path : baseName; 260 if (incTextLinkWithIcon("", recent.baseName)) { 261 incOpenProject(recent); 262 this.close(); 263 } 264 } 265 } else { 266 incTextShadowed("No recent projects..."); 267 } 268 igUnindent(); 269 } 270 igEndChild(); 271 272 igSameLine(0, 4); 273 274 // Right hand side 275 if (igBeginChild("##RHS", ImVec2((avail.x-8)/2, 0), false, ImGuiWindowFlags.NoScrollbar)) { 276 incTextShadowed(_("On the Web")); 277 incDummy(ImVec2(0, 2)); 278 igIndent(); 279 280 if (incTextLinkWithIcon("", _("Website"))) { 281 incOpenLink("https://inochi2d.com"); 282 } 283 284 if (incTextLinkWithIcon("", _("Documentation"))) { 285 incOpenLink("https://github.com/Inochi2D/Inochi-creator/wiki"); 286 } 287 288 if (incTextLinkWithIcon("", _("Join our Discord"))) { 289 incOpenLink("https://discord.com/invite/abnxwN6r9v"); 290 } 291 292 igNewLine(); 293 igNewLine(); 294 if (incTextLinkWithIcon("", _("Patreon"))) { 295 incOpenLink("https://www.patreon.com/LunaFoxgirlVT"); 296 } 297 if (incTextLinkWithIcon("", _("Github Sponsors"))) { 298 incOpenLink("https://github.com/sponsors/LunaTheFoxgirl/"); 299 } 300 igUnindent(); 301 } 302 igEndChild(); 303 break; 304 305 default: 306 this.close(); 307 break; 308 } 309 igPopTextWrapPos(); 310 } 311 igEndChild(); 312 igUnindent(); 313 igPopStyleVar(); 314 } 315 316 override 317 void onClose() { 318 if (step > 0) incSettingsSet!bool("hasDoneQuickSetup", true); 319 incDestroyWindowDrawList(shadowDrawList); 320 } 321 322 public: 323 this() { 324 super(_("Inochi Creator Start")); 325 326 auto bannerTex = ShallowTexture(cast(ubyte[])import("ui/banner.png")); 327 banner = new Texture(bannerTex); 328 banner.setAnisotropy(1.5); 329 330 version(InBranding) { 331 auto bannerLogoTex = ShallowTexture(cast(ubyte[])import("ui/banner-logo.png")); 332 inTexPremultiply(bannerLogoTex.data); 333 bannerLogo = new Texture(bannerLogoTex); 334 } 335 if (!incSettingsGet!bool("hasDoneQuickSetup", false)) step = 0; 336 337 // Load UI scale 338 tmpUIScale = cast(int)(incGetUIScale()*100); 339 340 uiSize = ImVec2( 341 512, 342 384 343 ); 344 345 shadowDrawList = incCreateWindowDrawList(); 346 } 347 }