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         glfwGetWindowSize(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         glViewport(x, y, width, height);
222 
223         windowWidth = width;
224         windowHeight = height;
225     }
226 
227     void onScroll(double hOffset, double vOffset)
228     {
229         mouseScroll = -cast(int)vOffset;
230     }
231 
232 private:
233     Window window;
234     int windowWidth;
235     int windowHeight;
236 
237     bool checkState1 = false;
238     bool checkState2 = false;
239     bool checkState3 = true;
240     bool collapseState1 = true;
241     bool collapseState2 = false;
242 
243     RGBAF color;
244     float brightness = 0;
245 
246     float disabledSliderValue = 30.0;
247     int scrollArea1 = 0;
248     int scrollArea2 = 0;
249     int scrollArea3 = 0;
250     int mouseScroll = 0;
251 
252     char[] lastInfo;  // last clicked element information
253     char[1024] buffer;  // buffer to hold our text
254 }
255 
256 int main(string[] args)
257 {
258     int width = 1024, height = 768;
259 
260     auto window = createWindow("imgui", WindowMode.windowed, width, height);
261 
262     GUI gui = GUI(window);
263 
264     glfwSwapInterval(1);
265 
266     string fontPath = thisExePath().dirName().buildPath("../").buildPath("DroidSans.ttf");
267 
268     enforce(imguiInit(fontPath));
269 
270     glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
271     glEnable(GL_BLEND);
272     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
273     glDisable(GL_DEPTH_TEST);
274 
275     while (!glfwWindowShouldClose(window.window))
276     {
277         gui.render();
278 
279         /* Swap front and back buffers. */
280         window.swap_buffers();
281 
282         /* Poll for and process events. */
283         glfwPollEvents();
284 
285         if (window.is_key_down(GLFW_KEY_ESCAPE))
286             glfwSetWindowShouldClose(window.window, true);
287     }
288 
289     // Clean UI
290     imguiDestroy();
291 
292     return 0;
293 }