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;
8 import creator.core;
9 import bindbc.imgui;
10 import creator.widgets;
11 import std.string;
12 import std.conv;
13 import i18n;
14 
15 public import creator.windows.about;
16 public import creator.windows.settings;
17 public import creator.windows.texviewer;
18 public import creator.windows.paramprop;
19 public import creator.windows.paramaxes;
20 public import creator.windows.paramsplit;
21 public import creator.windows.trkbind;
22 public import creator.windows.psdmerge;
23 public import creator.windows.welcome;
24 public import creator.windows.rename;
25 public import creator.windows.imgexport;
26 
27 version(NoUIScaling) private ImGuiWindowClass* windowClass;
28 
29 private uint spawnCount = 0;
30 
31 /**
32     A Widget
33 */
34 abstract class Window {
35 private:
36     string name_;
37     bool visible = true;
38     bool disabled;
39     int spawnedId;
40     const(char)* imName;
41 
42 protected:
43     bool onlyOne;
44     bool drewWindow;
45     ImGuiWindowFlags flags;
46 
47     abstract void onUpdate();
48 
49     void onBeginUpdate() {
50         if (imName is null) this.setTitle(name);
51         version(NoUIScaling) {
52             igSetNextWindowClass(windowClass);
53             drewWindow = incBegin(
54                 imName,
55                 &visible, 
56                 incIsWayland() ? flags : flags | ImGuiWindowFlags.NoDecoration
57             );
58         } else version(UseUIScaling) {
59             drewWindow = incBegin(
60                 imName,
61                 &visible, 
62                 flags
63             );
64         }
65     }
66     
67     void onEndUpdate() {
68         incEnd();
69     }
70 
71     void onClose() { }
72 
73     void unclose() {
74         this.visible = true;
75     }
76 
77 public:
78 
79 
80     /**
81         Constructs a frame
82     */
83     this(string name) {
84         this.name_ = name;
85         this.restore();
86     }
87 
88     final void close() {
89         this.visible = false;
90     }
91 
92     final string name() {
93         return name_;
94     }
95 
96     final void setTitle(string title) {
97         this.name_ = title;
98         imName = "%s###%s".format(name_, spawnedId).toStringz;
99     }
100 
101     /**
102         Draws the frame
103     */
104     final void update() {
105         if (!disabled && !visible) return;
106 
107         igPushItemFlag(ImGuiItemFlags.Disabled, disabled);
108             this.onBeginUpdate();
109                 this.onUpdate();
110             this.onEndUpdate();
111         igPopItemFlag();
112 
113         if (disabled && !visible) visible = true;
114     }
115 
116     ImVec2 getPosition() {
117         ImVec2 pos;
118         igGetWindowPos(&pos);
119         return pos;
120     }
121 
122     void disable() {
123         this.flags = ImGuiWindowFlags.NoDocking | 
124             ImGuiWindowFlags.NoCollapse | 
125             ImGuiWindowFlags.NoNav | 
126             ImGuiWindowFlags.NoMove |
127             ImGuiWindowFlags.NoScrollWithMouse |
128             ImGuiWindowFlags.NoScrollbar;
129         disabled = true;
130     }
131 
132     void restore() {
133         disabled = false;
134         this.flags = ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoSavedSettings;
135 
136         version(NoUIScaling) {
137             windowClass = ImGuiWindowClass_ImGuiWindowClass();
138             windowClass.ViewportFlagsOverrideClear = ImGuiViewportFlags.NoDecoration | ImGuiViewportFlags.NoTaskBarIcon;
139             windowClass.ViewportFlagsOverrideSet = ImGuiViewportFlags.NoAutoMerge;
140         }
141     }
142     
143     /**
144         Gets whether the window is visible.
145     */
146     final
147     bool isVisible() {
148         return visible;
149     }
150 }
151 
152 private {
153     Window[] windowStack;
154     Window[] windowList;
155 }
156 
157 /**
158     Pushes window to stack
159 */
160 void incPushWindow(Window window) {
161     window.spawnedId = spawnCount++;
162     
163     // Only allow one instance of the window
164     if (window.onlyOne) {
165         foreach(win; windowStack) {
166             if (win.name == window.name) return;
167         }
168     }
169 
170     if (windowStack.length > 0) {
171         windowStack[$-1].disable();
172     }
173 
174     windowStack ~= window;
175 }
176 
177 /**
178     Pushes window to stack
179 */
180 void incPushWindowList(Window window) {
181     window.spawnedId = spawnCount++;
182 
183     // Only allow one instance of the window
184     if (window.onlyOne) {
185         foreach(win; windowList) {
186             if (win.name == window.name) return;
187         }
188     }
189 
190     windowList ~= window;
191 }
192 
193 /**
194     Pop window from Window List
195 */
196 void incPopWindowList(Window window) {
197     import std.algorithm.searching : countUntil;
198     import std.algorithm.mutation : remove;
199 
200     ptrdiff_t i = windowList.countUntil(window);
201     if (i != -1) {
202         window.onClose();
203         if (windowList.length == 1) windowList.length = 0;
204         else windowList = windowList.remove(i);
205     }
206 }
207 
208 /**
209     Pop window from Window List
210 */
211 void incPopWindowListAll() {
212     foreach(window; windowList) {
213         window.onClose();
214         window.visible = false;
215     }
216     windowList.length = 0;
217 }
218 
219 /**
220     Pops a window
221 */
222 void incPopWindow() {
223     windowStack[$-1].onClose();
224     windowStack.length--;
225     if (windowStack.length > 0) windowStack[$-1].restore();
226 }
227 
228 /**
229     Update windows
230 */
231 void incUpdateWindows() {
232     int id = 0;
233     foreach(window; windowStack) {
234         window.update();
235         if (!window.visible) incPopWindow();
236     }
237     
238     Window[] closedWindows;
239     foreach(window; windowList) {
240         window.update();
241         if (!window.visible) closedWindows ~= window;
242     }
243 
244     foreach(window; closedWindows) {
245         incPopWindowList(window);
246     }
247 }
248 
249 /**
250     Gets top window
251 */
252 Window incGetTopWindow() {
253     return windowStack.length > 0 ? windowStack[$-1] : null;
254 }
255 
256 /**
257     Pops the welcome window.
258 */
259 void incPopWelcomeWindow() {
260     import std.algorithm.mutation : remove;
261     foreach(i; 0..windowStack.length) {
262         if (auto ww = cast(WelcomeWindow)windowStack[i]) {
263             windowStack = windowStack.remove(i);
264             return;
265         }
266     }
267 }