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 }