1 /** 2 * Copyright: Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved 3 * Authors: $(Gabriel Gheorghe) 4 * License: $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007) 5 * Source: $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/framework/gui/impl.d) 6 * Documentation: 7 * Coverage: 8 * TODO: 9 * - Change action delegate and objEvList dinamically. 10 **/ 11 module liberty.framework.gui.impl; 12 13 import liberty.framework.gui.data; 14 import liberty.graphics.shader.impl; 15 import liberty.math.matrix; 16 import liberty.scene.constants; 17 import liberty.scene.entity; 18 19 import std.typecons : Tuple; 20 21 import liberty.logger; 22 import liberty.math.matrix; 23 import liberty.math.util; 24 import liberty.scene.entity; 25 import liberty.core.platform; 26 import liberty.scene.impl; 27 import liberty.framework.gui.event; 28 import liberty.framework.gui.constants; 29 import liberty.framework.gui.widget; 30 import liberty.scene.services; 31 import liberty.framework.gui.controls; 32 import liberty.scene.action; 33 import liberty.graphics.shader.program; 34 35 import liberty.math.transform; 36 37 38 /** 39 * A gui represents a 2-dimensional view containting graphical user interface elements. 40 * Inheriths $(D Entity) class and implements $(D IUpdateable) service. 41 **/ 42 abstract class Gui : Entity { 43 private { 44 Shader shader; 45 // getProjection, setProjection 46 GuiProjection projection; 47 // isFixedProjectionEnabled, setFixedProjectionEnabled 48 bool fixedProjectionEnabled; 49 // getProjectionMatrix 50 Matrix4F projectionMatrix = Matrix4F.identity; 51 52 Canvas rootCanvas; 53 Action!Widget[string] actionMap; 54 } 55 56 /** 57 * Create a new gui using an id and a parent. 58 **/ 59 this(string id) { 60 super(id); 61 62 shader = Shader 63 .getOrCreate("Gui", (shader) { 64 shader 65 .setViewMatrixEnabled(false) 66 .addCustomRenderMethod((program, self) { 67 program.bind; 68 69 foreach (node; self.getMap) 70 if (node.visibility == Visibility.Visible) { 71 (cast(Gui)node).updateProjection(program); 72 foreach (widget; (cast(Gui)node).getRootCanvas.getWidgets) { 73 if (widget.getZIndex == 0) { 74 if (widget.visibility == Visibility.Visible) { 75 if (widget.model !is null) 76 program 77 .loadUniform("uZIndex", 0) 78 .loadUniform("uModelMatrix", widget.component!Transform.getModelMatrix) 79 .render(widget.model); 80 } 81 } 82 } 83 // FILTER Z INDEX FOR NOW WITH ONLY 0 AND 1 --> BUG 84 foreach (widget; (cast(Gui)node).getRootCanvas.getWidgets) { 85 if (widget.getZIndex == 1) { 86 if (widget.visibility == Visibility.Visible) { 87 if (widget.model !is null) 88 program 89 .loadUniform("uZIndex", 1) 90 .loadUniform("uModelMatrix", widget.component!Transform.getModelMatrix) 91 .render(widget.model); 92 } 93 } 94 } 95 } 96 97 program.unbind; 98 }); 99 }); 100 101 shader.registerEntity(this); 102 scene.shaderMap[shader.id] = shader; 103 104 rootCanvas = new Canvas("RootCanvas" ~ id, this); 105 updateProjection(shader.getProgram); 106 } 107 108 private void updateProjection(ShaderProgram program) { 109 if (!fixedProjectionEnabled) { 110 const tempWidth = Platform.getWindow.getWidth; 111 const tempHeight = Platform.getWindow.getHeight; 112 113 if (projection.width != tempWidth || projection.height != tempHeight) { 114 projection.width = tempWidth; 115 projection.height = tempHeight; 116 117 projectionMatrix = MathUtils.getOrthographicMatrixFrom( 118 cast(float)projection.xStart, cast(float)projection.width, 119 cast(float)projection.height, cast(float)projection.yStart, 120 cast(float)projection.zNear, cast(float)projection.zFar 121 ); 122 123 program 124 .bind 125 .loadUniform("uProjectionMatrix", projectionMatrix) 126 .unbind; 127 } 128 } 129 } 130 131 /** 132 * Returns the projection matrix. 133 **/ 134 Matrix4F getProjectionMatrix() const { 135 return projectionMatrix; 136 } 137 138 /** 139 * 140 **/ 141 override void update() { 142 rootCanvas.update(); 143 } 144 145 /** 146 * 147 **/ 148 final Canvas getRootCanvas() { 149 return rootCanvas; 150 } 151 152 /** 153 * Add a new action for the current gui using an id, an event, 154 * an array of tuple containing the wanted widget and its event and a priority. 155 * The priority param is optional, its default value is 0. 156 * Returns reference to this so it can be used in a stream. 157 **/ 158 Gui addAction(T)(string id, void delegate(Widget, Event) action, 159 Tuple!(T, Event)[] objEvList = null, ubyte priority = 0) 160 do { 161 import std.array : split; 162 import std.traits : EnumMembers; 163 164 bool possible = false; 165 166 if (objEvList !is null) 167 static foreach (s; EnumMembers!WidgetType) 168 static if (s == T.stringof.split("!")[0]) { 169 foreach (e; objEvList) { 170 switch (e[1]) with (Event) { 171 static foreach (member; mixin(T.stringof ~ ".getEventArrayString")) 172 mixin("case " ~ member ~ ": (cast(" ~ s ~ ")e[0]).setOn" ~ member ~ 173 "(action); possible = true; goto END_SWITCH;"); 174 default: break; 175 } 176 END_SWITCH: 177 } 178 } 179 180 possible 181 ? actionMap[id] = new Action!Widget(id, action, priority) 182 : Logger.error("Action with id: " ~ id ~ " can't be created.", typeof(this).stringof); 183 184 return this; 185 } 186 187 /** 188 * Simulate an action by starting it right now. 189 * Returns reference to this so it can be used in a stream. 190 **/ 191 final Gui simulateAction(string id, Widget sender, Event e) { 192 actionMap[id].callEvent(sender, e); 193 return this; 194 } 195 196 /** 197 * Remove an user interface action from the memory. 198 * Returns reference to this so it can be used in a stream. 199 **/ 200 final Gui removeAction(string id) { 201 actionMap[id].destroy(); 202 actionMap[id] = null; 203 actionMap.remove(id); 204 return this; 205 } 206 207 /** 208 * Returns the action map for user interface elements. 209 **/ 210 final Action!Widget[string] getActionMap() { 211 return actionMap; 212 } 213 214 /** 215 * Returns an action by given id for user interface elements. 216 **/ 217 final Action!Widget getAction(string name) { 218 return actionMap[name]; 219 } 220 221 /** 222 * Keep window aspect ratio the same. 223 * Returns reference to this so it can be used in a stream. 224 **/ 225 final typeof(this) setFixedProjectionEnabled(bool enabled = true) { 226 fixedProjectionEnabled = enabled; 227 return this; 228 } 229 230 /** 231 * Returns true if fixed projection is enabled. 232 **/ 233 final bool isFixedProjectionEnabled() const { 234 return fixedProjectionEnabled; 235 } 236 }