1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module w4d.task.window; 8 import w4d.style.scalar, 9 w4d.util.clipping, 10 w4d.util.tuple, 11 w4d.app, 12 w4d.event, 13 w4d.exception; 14 import g4d.glfw.lib; 15 static import g4d; 16 import gl3n.linalg; 17 18 /// 19 alias WindowHint = g4d.WindowHint; 20 /// 21 alias MouseButton = g4d.MouseButton; 22 /// 23 alias Key = g4d.Key; 24 /// 25 alias KeyState = g4d.KeyState; 26 27 /// A window to place widgets. 28 class Window : g4d.Window, Task 29 { 30 protected static void retrieveDisplayConfig () 31 { 32 auto mon = enforce!glfwGetPrimaryMonitor(); 33 const mod = enforce!glfwGetVideoMode( mon ); 34 35 int mm_x = 0, mm_y = 0; // millimetres 36 enforce!glfwGetMonitorPhysicalSize( mon, &mm_x, &mm_y ); 37 38 ScalarUnitBase.mm = mod.width*1f / mm_x; 39 ScalarUnitBase.inch = mod.width*1f / mm_x / 25.4f; 40 } 41 42 protected __gshared Window _aliveWindow; 43 44 static dstring getClipboard () 45 { 46 if ( _aliveWindow ) { 47 return _aliveWindow.clipboard; 48 } 49 return ""d; 50 } 51 static void setClipboard ( dstring v ) 52 { 53 if ( _aliveWindow ) { 54 _aliveWindow.clipboard = v; 55 } 56 } 57 58 protected Shaders _shaders; 59 /// A collection of shader. 60 @property shaders () { return _shaders; } 61 62 protected ClipRect _clip; 63 /// ClipRect utility object. 64 @property clip () { return _clip; } 65 66 protected WindowContent _root; 67 protected vec2 _cursorPos; 68 69 /// 70 this ( vec2i size, string text, WindowHint hint = WindowHint.None ) 71 { 72 enforce( size.x > 0 && size.y > 0, "Window size is invalid." ); 73 74 super( size, text, hint ); 75 retrieveDisplayConfig(); 76 77 _shaders = new Shaders; 78 _clip = new ClipRect( shaders.fill3 ); 79 _root = null; 80 _cursorPos = vec2(0,0); 81 82 g4d.ColorBuffer.enableBlend(); 83 g4d.DepthBuffer.disable(); 84 85 handler.onWindowResize = delegate ( vec2i sz ) 86 { 87 enforce( _root, "Content is null." ); 88 recalcMatrix(); 89 _root.layout( sz ); 90 }; 91 handler.onWindowRefresh = delegate () 92 { 93 _root.requestRedraw(); 94 }; 95 handler.onMouseEnter = delegate ( bool entered ) 96 { 97 _root.handleMouseEnter( entered, _cursorPos ); 98 }; 99 handler.onMouseMove = delegate ( vec2 pos ) 100 { 101 _cursorPos = pos; 102 _root.handleMouseMove( pos ); 103 }; 104 handler.onMouseButton = delegate ( MouseButton btn, bool status ) 105 { 106 _root.handleMouseButton( btn, status, _cursorPos ); 107 }; 108 handler.onMouseScroll = delegate ( vec2 amount ) 109 { 110 _root.handleMouseScroll( amount, _cursorPos ); 111 }; 112 handler.onKey = delegate ( Key key, KeyState state ) 113 { 114 _root.handleKey( key, state ); 115 }; 116 handler.onCharacter = delegate ( dchar c ) 117 { 118 _root.handleTextInput( c ); 119 }; 120 } 121 122 /// Sets contents(widget). 123 /// This method can be called only once. 124 void setContent ( WindowContent content ) 125 { 126 enforce( !_root, "Content is already setted." ); 127 _root = content; 128 handler.onWindowResize( size ); 129 } 130 131 protected void recalcMatrix () 132 { 133 auto half = vec2( size ) / 2; 134 auto late = half * -1; 135 136 const orth = mat4.orthographic( -half.x,half.x, 137 half.y,-half.y, short.min,short.max ); 138 139 const proj = orth * mat4.translation( vec3( late, 0 ) ); 140 141 foreach ( s; shaders.list ) { 142 s.matrix.projection = proj; 143 } 144 } 145 146 /// Makes contexts current, 147 /// And clears color and depth buffer. 148 /// Warnings: Stencil buffer won't be cleared. 149 override void resetFrame () 150 { 151 super.resetFrame(); 152 g4d.ColorBuffer.clear(); 153 g4d.DepthBuffer.clear(); 154 } 155 156 /// Updates window. 157 /// This method must be called by App object. 158 override bool exec ( App app ) 159 { 160 enforce( _root, "Content is null." ); 161 162 if ( alive ) { 163 _aliveWindow = this; 164 165 if ( _root.needLayout ) { 166 _root.layout( size ); 167 } 168 if ( _root.needRedraw ) { 169 resetFrame(); 170 _root.draw( this ); 171 applyFrame(); 172 } 173 cursor = _root.cursor; 174 return false; 175 } 176 return true; 177 } 178 } 179 180 /// A struct of shader collection. 181 /// One Shaders class is for only one Window. 182 class Shaders 183 { 184 @("shader") { 185 protected g4d.RGBA3DShader _rgba3; 186 protected g4d.Alpha3DShader _alpha3; 187 protected g4d.Fill3DShader _fill3; 188 } 189 190 /// FIXME: IDK how to fix these fucking codes. 191 192 static foreach ( name; __traits(allMembers,typeof(this)) ) { 193 static if ( "shader".isIn( __traits(getAttributes,mixin(name)) ) ) { 194 mixin( "@property "~name[1..$]~"() { return "~name~"; }" ); 195 } 196 } 197 198 /// 199 this () 200 { 201 static foreach ( name; __traits(allMembers,typeof(this)) ) { 202 static if ( "shader".isIn( __traits(getAttributes,mixin(name)) ) ) { 203 mixin( name~"= new typeof("~name~");" ); 204 } 205 } 206 } 207 208 /// List of all shaders. 209 @property list () 210 { 211 import g4d.shader.base: Shader; 212 Shader[] result; 213 214 static foreach ( name; __traits(allMembers,typeof(this)) ) { 215 static if ( "shader".isIn( __traits(getAttributes,mixin(name)) ) ) { 216 mixin( "result ~= "~name~";" ); 217 } 218 } 219 return result; 220 } 221 } 222 223 /// An interface that objects placed in Window must inherit. 224 /// Some handlers return true if event was handled. 225 interface WindowContent 226 { 227 228 /// Be called with true when cursor is entered. 229 bool handleMouseEnter ( bool, vec2 ); 230 /// Be called when cursor is moved. 231 bool handleMouseMove ( vec2 ); 232 /// Be called when mouse button is clicked. 233 bool handleMouseButton ( MouseButton, bool, vec2 ); 234 /// Be called when mouse wheel is rotated. 235 bool handleMouseScroll ( vec2, vec2 ); 236 237 /// Be called when key is pressed, repeated or released. 238 bool handleKey ( Key, KeyState ); 239 /// Be called when focused and text was inputted. 240 bool handleTextInput ( dchar ); 241 242 /// Current cursor. 243 @property const(g4d.Cursor) cursor (); 244 245 /// Checks if the contents need window size. 246 @property bool needLayout (); 247 /// Checks if the contents need redrawing. 248 @property bool needRedraw (); 249 /// Sets the content redraw next frame. 250 void requestRedraw (); 251 252 /// Be called with size of Window when needLayout is true. 253 void layout ( vec2i ); 254 /// Be called when needRedraw is true. 255 void draw ( Window ); 256 }