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 }