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.widgets.titlebar;
8 import bindbc.sdl;
9 import bindbc.imgui;
10 import creator.core;
11 import creator.widgets;
12 import creator.utils.link;
13 import std.string;
14 import app : incUpdateNoEv;
15 import i18n;
16 
17 private {
18     bool incUseNativeTitlebar;
19 
20     extern(C) SDL_HitTestResult _incHitTestCallback(SDL_Window* win, const(SDL_Point)* area, void* data) nothrow {
21         int winWidth, winHeight;
22         SDL_GetWindowSize(win, &winWidth, &winHeight);
23         
24         // GET PROPER RESIZE AREA
25         enum RESIZE_AREA = 4;
26         enum RESIZE_AREAC = RESIZE_AREA*2;
27 
28         // Resize top
29         if (area.x < RESIZE_AREAC && area.y < RESIZE_AREAC) return SDL_HitTestResult.SDL_HITTEST_RESIZE_TOPLEFT;
30         if (area.x > winWidth-RESIZE_AREAC && area.y < RESIZE_AREAC) return SDL_HitTestResult.SDL_HITTEST_RESIZE_TOPRIGHT;
31         if (area.x < RESIZE_AREA) return SDL_HitTestResult.SDL_HITTEST_RESIZE_LEFT;
32         if (area.y < RESIZE_AREA) return SDL_HitTestResult.SDL_HITTEST_RESIZE_TOP;
33 
34         // Title bar
35         if (area.y < 22 && area.x < winWidth-128) return SDL_HitTestResult.SDL_HITTEST_DRAGGABLE;
36 
37         if (area.x < RESIZE_AREAC && area.y > winHeight-RESIZE_AREAC) return SDL_HitTestResult.SDL_HITTEST_RESIZE_BOTTOMLEFT;
38         if (area.x > winWidth-RESIZE_AREAC && area.y > winHeight-RESIZE_AREAC) return SDL_HitTestResult.SDL_HITTEST_RESIZE_BOTTOMRIGHT;
39         if (area.x > winWidth-RESIZE_AREA) return SDL_HitTestResult.SDL_HITTEST_RESIZE_RIGHT;
40         if (area.y > winHeight-RESIZE_AREA) return SDL_HitTestResult.SDL_HITTEST_RESIZE_BOTTOM;
41 
42         return SDL_HitTestResult.SDL_HITTEST_NORMAL;
43     }
44 }
45 
46 /**
47     Whether the native titlebar can be used
48 */
49 bool incCanUseAppTitlebar = true;
50 
51 /**
52     Gets whether to use the native titlebar
53 */
54 bool incGetUseNativeTitlebar() {
55     return incCanUseAppTitlebar && incUseNativeTitlebar;
56 }
57 
58 /**
59     Set whether to use the native titlebar
60 */
61 void incSetUseNativeTitlebar(bool value) {
62     if (!incCanUseAppTitlebar) return;
63     incUseNativeTitlebar = value;
64     incApplyTitlebar(incGetWindowPtr());
65 }
66 
67 /**
68     Applies titlebar settings to a window
69 */
70 void incApplyTitlebar(void* window) {
71     if (!incUseNativeTitlebar) {
72         SDL_SetWindowBordered(cast(SDL_Window*)window, cast(SDL_bool)false);
73         SDL_SetWindowHitTest(cast(SDL_Window*)window, &_incHitTestCallback, null);
74     } else {
75         SDL_SetWindowBordered(cast(SDL_Window*)window, cast(SDL_bool)true);
76         SDL_SetWindowHitTest(cast(SDL_Window*)window, null, null);
77     }
78 }
79 
80 /**
81     Draws the custom titlebar
82 */
83 void incTitlebar(string title) {
84     auto flags = 
85         ImGuiWindowFlags.NoSavedSettings |
86         ImGuiWindowFlags.NoScrollbar |
87         ImGuiWindowFlags.MenuBar;
88     
89     SDL_Window* window = incGetWindowPtr();
90 
91     if (incGetDarkMode()) igPushStyleColor(ImGuiCol.MenuBarBg, ImVec4(0.1, 0.1, 0.1, 1));
92     else  igPushStyleColor(ImGuiCol.MenuBarBg, ImVec4(0.9, 0.9, 0.9, 1));
93     igPushStyleVar(ImGuiStyleVar.FramePadding, ImVec2(0, 4*incGetUIScale()));
94         if (igBeginViewportSideBar("##Titlebar", igGetMainViewport(), ImGuiDir.Up, 22*incGetUIScale(), flags)) {
95             if (igBeginMenuBar()) {
96                 ImVec2 avail;
97                 igGetContentRegionAvail(&avail);
98 
99                 version (InBranding) {
100                     igImage(
101                         cast(void*)incGetLogo(), 
102                         ImVec2(avail.y*2, avail.y*2), 
103                         ImVec2(0, 0), ImVec2(1, 1), 
104                         ImVec4(1, 1, 1, 1), 
105                         ImVec4(0, 0, 0, 0)
106                     );
107 
108                     debug {
109                         igText((title~_(" (Debug Mode)")).toStringz);
110                     } else {
111                         igText(title.toStringz);
112                     }
113                 } else {
114                     igText((title~_(" (Unsupported)")).toStringz);
115                 }
116 
117                 // :)
118                 if (isTransMonthOfVisibility) {
119                     igSeparator();
120                     ImVec4 a = ImVec4(85.0/255.0, 205.0/255.0, 252.0/255.0, 255);
121                     ImVec4 b;
122                     ImVec4 c;
123                     igColorConvertU32ToFloat4(&b, 0xF7A8B8FF);
124                     igColorConvertU32ToFloat4(&c, 0xFFFFFFFF);
125                     ImVec4[] transColors = [a, b, c, b];
126                     
127                     // A call for the human rights of transgender people to be respected
128                     // it is shown in the custom titlebar every transgender awareness month.
129                     foreach(i, ic; _("Trans Rights!")) {
130                         igTextColored(transColors[i%transColors.length], [ic, '\0'].ptr);
131                         igSameLine(0, 0);
132                     }
133                 }
134                 
135                 incDummy(ImVec2(-((16*4*incGetUIScale())+8), 0));
136                 igPushFont(incIconFont());
137                     auto state = igGetStateStorage();
138 
139                     igTextColored(
140                         ImGuiStorage_GetBool(state, igGetID("##MINIMIZE")) ? 
141                             (incGetDarkMode() ? ImVec4(1, 1, 1, 1) : ImVec4(.3, .3, .3, 1)) : 
142                             ImVec4(.5, .5, .5, 1), 
143                         ""
144                     );
145                     if (igIsItemClicked()) {
146                         SDL_MinimizeWindow(window);
147                     }
148                     if(igIsItemHovered()) {
149                         ImGuiStorage_SetBool(state, igGetID("##MINIMIZE"), true);
150                     } else {
151                         ImGuiStorage_SetBool(state, igGetID("##MINIMIZE"), false);
152                     }
153 
154                     bool isMaximized = (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) > 0;
155                     
156                     igTextColored(
157                         ImGuiStorage_GetBool(state, igGetID("##MAXIMIZE")) ? 
158                             (incGetDarkMode() ? ImVec4(1, 1, 1, 1) : ImVec4(.3, .3, .3, 1)) : 
159                             ImVec4(.5, .5, .5, 1), 
160                         isMaximized ? "" : ""
161                     );
162                     if (igIsItemClicked()) {
163                         if (!isMaximized) SDL_MaximizeWindow(window);
164                         else SDL_RestoreWindow(window);
165                     }
166                     if(igIsItemHovered()) {
167                         ImGuiStorage_SetBool(state, igGetID("##MAXIMIZE"), true);
168                     } else {
169                         ImGuiStorage_SetBool(state, igGetID("##MAXIMIZE"), false);
170                     }
171                     
172                     igTextColored(
173                         ImGuiStorage_GetBool(state, igGetID("##EXIT")) ? 
174                             ImVec4(1, .1, .1, 1) : 
175                             ImVec4(.5, .5, .5, 1), 
176                         ""
177                     );
178                     if (igIsItemClicked()) {
179                         incExit();
180                     }
181                     if(igIsItemHovered()) {
182                         ImGuiStorage_SetBool(state, igGetID("##EXIT"), true);
183                     } else {
184                         ImGuiStorage_SetBool(state, igGetID("##EXIT"), false);
185                     }
186                 igPopFont();
187 
188                 igEndMenuBar();
189             }
190                 
191             igEnd();
192         }
193     igPopStyleColor();
194     igPopStyleVar();
195 }