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