1 /*
2  *             Copyright Andrej Mitrovic 2014.
3  *  Distributed under the Boost Software License, Version 1.0.
4  *     (See accompanying file LICENSE_1_0.txt or copy at
5  *           http://www.boost.org/LICENSE_1_0.txt)
6  */
7 module colors;
8 
9 import std.exception;
10 import std.file;
11 import std.path;
12 import std.range;
13 import std.stdio;
14 import std..string;
15 
16 import deimos.glfw.glfw3;
17 
18 import glad.gl.enums;
19 import glad.gl.ext;
20 import glad.gl.funcs;
21 import glad.gl.loader;
22 import glad.gl.types;
23 
24 import glwtf.input;
25 import glwtf.window;
26 
27 import imgui;
28 
29 import window;
30 
31 struct RGBAF
32 {
33     float r = 0.0, g = 0.0, b = 0.0, a = 0.0;
34 
35     RGBAF opBinary(string op)(RGBAF rgba)
36     {
37         RGBAF res = this;
38 
39         mixin("res.r = res.r " ~ op ~ " rgba.r;");
40         mixin("res.g = res.g " ~ op ~ " rgba.g;");
41         mixin("res.b = res.b " ~ op ~ " rgba.b;");
42         mixin("res.a = res.a " ~ op ~ " rgba.a;");
43 
44         return res;
45     }
46 }
47 
48 auto clamp(T1, T2, T3)(T1 value, T2 min, T3 max)
49 {
50     return (((value) >(max)) ? (max) : (((value) <(min)) ? (min) : (value)));
51 }
52 
53 RGBA toRGBA(RGBAF c)
54 {
55     return RGBA(cast(ubyte)(255.0f * clamp(c.r, 0.0, 1.0)),
56                 cast(ubyte)(255.0f * clamp(c.g, 0.0, 1.0)),
57                 cast(ubyte)(255.0f * clamp(c.b, 0.0, 1.0)),
58                 cast(ubyte)(255.0f * clamp(c.a, 0.0, 1.0)));
59 }
60 
61 RGBAF toRGBAF(RGBA c)
62 {
63     return RGBAF(clamp((cast(float)c.r) / 255.0, 0.0, 1.0),
64                  clamp((cast(float)c.g) / 255.0, 0.0, 1.0),
65                  clamp((cast(float)c.b) / 255.0, 0.0, 1.0),
66                  clamp((cast(float)c.a) / 255.0, 0.0, 1.0));
67 }
68 
69 struct GUI
70 {
71     this(Window window)
72     {
73         this.window = window;
74 
75         window.on_scroll.strongConnect(&onScroll);
76 
77         int width;
78         int height;
79         glfwGetFramebufferSize(window.window, &width, &height);
80 
81         // trigger initial viewport transform.
82         onWindowResize(width, height);
83 
84         window.on_resize.strongConnect(&onWindowResize);
85 
86         oldColorScheme = defaultColorScheme;
87         updateColorScheme();
88     }
89 
90     ColorScheme oldColorScheme;
91 
92     void updateColorScheme()
93     {
94         auto rgbaBright = RGBAF(brightness, brightness, brightness, 0);
95 
96         foreach (ref outColor, oldColor; zip(defaultColorScheme.walkColors, oldColorScheme.walkColors))
97         {
98             auto oldRGBAF = toRGBAF(*oldColor);
99             auto res = oldRGBAF + color + rgbaBright;
100             *outColor = res.toRGBA();
101         }
102     }
103 
104     void render()
105     {
106         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
107 
108         // Mouse states
109         ubyte mousebutton = 0;
110         double mouseX;
111         double mouseY;
112         glfwGetCursorPos(window.window, &mouseX, &mouseY);
113 
114         const scrollAreaWidth = windowWidth / 4;
115         const scrollAreaHeight = windowHeight - 20;
116 
117         int mousex = cast(int)mouseX;
118         int mousey = cast(int)mouseY;
119 
120         mousey = windowHeight - mousey;
121         int leftButton   = glfwGetMouseButton(window.window, GLFW_MOUSE_BUTTON_LEFT);
122         int rightButton  = glfwGetMouseButton(window.window, GLFW_MOUSE_BUTTON_RIGHT);
123         int middleButton = glfwGetMouseButton(window.window, GLFW_MOUSE_BUTTON_MIDDLE);
124 
125         if (leftButton == GLFW_PRESS)
126             mousebutton |= MouseButton.left;
127 
128         imguiBeginFrame(mousex, mousey, mousebutton, mouseScroll);
129 
130         if (mouseScroll != 0)
131             mouseScroll = 0;
132 
133         imguiBeginScrollArea("Scroll area 1", 10, 10, scrollAreaWidth, scrollAreaHeight, &scrollArea1);
134 
135         imguiSeparatorLine();
136         imguiSeparator();
137 
138         if (imguiSlider("Transparency Alpha", &color.a, 0.0, 1.0, 0.01f))
139             updateColorScheme();
140 
141         if (imguiSlider("Brightness", &brightness, -1.0, 1.0, 0.01f))
142             updateColorScheme();
143 
144         if (imguiSlider("Red Channel", &color.r, 0.0, 1.0, 0.01f))
145             updateColorScheme();
146 
147         if (imguiSlider("Green Channel", &color.g, 0.0, 1.0, 0.01f))
148             updateColorScheme();
149 
150         if (imguiSlider("Blue Channel", &color.b, 0.0, 1.0, 0.01f))
151             updateColorScheme();
152 
153         // should not be clickable
154         enforce(!imguiSlider("Disabled slider", &disabledSliderValue, 0.0, 100.0, 1.0f, Enabled.no));
155 
156         imguiIndent();
157         imguiLabel("Indented");
158         imguiUnindent();
159         imguiLabel("Unindented");
160 
161         imguiEndScrollArea();
162 
163         imguiBeginScrollArea("Scroll area 2", 20 + (1 * scrollAreaWidth), 10, scrollAreaWidth, scrollAreaHeight, &scrollArea2);
164         imguiSeparatorLine();
165         imguiSeparator();
166 
167         foreach (i; 0 .. 100)
168             imguiLabel("A wall of text");
169 
170         imguiEndScrollArea();
171 
172         imguiBeginScrollArea("Scroll area 3", 30 + (2 * scrollAreaWidth), 10, scrollAreaWidth, scrollAreaHeight, &scrollArea3);
173         imguiLabel(lastInfo);
174         imguiEndScrollArea();
175 
176         imguiEndFrame();
177 
178         const graphicsXPos = 40 + (3 * scrollAreaWidth);
179 
180         imguiDrawText(graphicsXPos, scrollAreaHeight, TextAlign.left, "Free text", RGBA(32, 192, 32, 192));
181         imguiDrawText(graphicsXPos + 100, windowHeight - 40, TextAlign.right, "Free text", RGBA(32, 32, 192, 192));
182         imguiDrawText(graphicsXPos + 50, windowHeight - 60, TextAlign.center, "Free text", RGBA(192, 32, 32, 192));
183 
184         imguiDrawLine(graphicsXPos, windowHeight - 80, graphicsXPos + 100, windowHeight - 60, 1.0f, RGBA(32, 192, 32, 192));
185         imguiDrawLine(graphicsXPos, windowHeight - 100, graphicsXPos + 100, windowHeight - 80, 2.0, RGBA(32, 32, 192, 192));
186         imguiDrawLine(graphicsXPos, windowHeight - 120, graphicsXPos + 100, windowHeight - 100, 3.0, RGBA(192, 32, 32, 192));
187 
188         imguiDrawRoundedRect(graphicsXPos, windowHeight - 240, 100, 100, 5.0, RGBA(32, 192, 32, 192));
189         imguiDrawRoundedRect(graphicsXPos, windowHeight - 350, 100, 100, 10.0, RGBA(32, 32, 192, 192));
190         imguiDrawRoundedRect(graphicsXPos, windowHeight - 470, 100, 100, 20.0, RGBA(192, 32, 32, 192));
191 
192         imguiDrawRect(graphicsXPos, windowHeight - 590, 100, 100, RGBA(32, 192, 32, 192));
193         imguiDrawRect(graphicsXPos, windowHeight - 710, 100, 100, RGBA(32, 32, 192, 192));
194         imguiDrawRect(graphicsXPos, windowHeight - 830, 100, 100, RGBA(192, 32, 32, 192));
195 
196         imguiRender(windowWidth, windowHeight);
197     }
198 
199     /**
200         This tells OpenGL what area of the available area we are
201         rendering to. In this case, we change it to match the
202         full available area. Without this function call resizing
203         the window would have no effect on the rendering.
204     */
205     void onWindowResize(int width, int height)
206     {
207         // bottom-left position.
208         enum int x = 0;
209         enum int y = 0;
210 
211         /**
212             This function defines the current viewport transform.
213             It defines as a region of the window, specified by the
214             bottom-left position and a width/height.
215 
216             Note about the viewport transform:
217             It is the process of transforming vertex data from normalized
218             device coordinate space to window space. It specifies the
219             viewable region of a window.
220         */
221         glfwGetFramebufferSize(window.window, &width, &height);
222         glViewport(x, y, width, height);
223 
224         windowWidth = width;
225         windowHeight = height;
226     }
227 
228     void onScroll(double hOffset, double vOffset)
229     {
230         mouseScroll = -cast(int)vOffset;
231     }
232 
233 private:
234     Window window;
235     int windowWidth;
236     int windowHeight;
237 
238     bool checkState1 = false;
239     bool checkState2 = false;
240     bool checkState3 = true;
241     bool collapseState1 = true;
242     bool collapseState2 = false;
243 
244     RGBAF color;
245     float brightness = 0;
246 
247     float disabledSliderValue = 30.0;
248     int scrollArea1 = 0;
249     int scrollArea2 = 0;
250     int scrollArea3 = 0;
251     int mouseScroll = 0;
252 
253     char[] lastInfo;  // last clicked element information
254     char[1024] buffer;  // buffer to hold our text
255 }
256 
257 int main(string[] args)
258 {
259     int width = 1024, height = 768;
260 
261     auto window = createWindow("imgui", WindowMode.windowed, width, height);
262 
263     GUI gui = GUI(window);
264 
265     glfwSwapInterval(1);
266 
267     string fontPath = thisExePath().dirName().buildPath("../").buildPath("DroidSans.ttf");
268 
269     enforce(imguiInit(fontPath));
270 
271     glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
272     glEnable(GL_BLEND);
273     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
274     glDisable(GL_DEPTH_TEST);
275 
276     while (!glfwWindowShouldClose(window.window))
277     {
278         gui.render();
279 
280         /* Swap front and back buffers. */
281         window.swap_buffers();
282 
283         /* Poll for and process events. */
284         glfwPollEvents();
285 
286         if (window.is_key_down(GLFW_KEY_ESCAPE))
287             glfwSetWindowShouldClose(window.window, true);
288     }
289 
290     // Clean UI
291     imguiDestroy();
292 
293     return 0;
294 }