1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module w4d.widget.base; 8 import w4d.element.box, 9 w4d.layout.base, 10 w4d.layout.fill, 11 w4d.parser.colorset, 12 w4d.style.color, 13 w4d.style.widget, 14 w4d.task.window, 15 w4d.widget.base.context, 16 w4d.widget.base.keyboard, 17 w4d.widget.base.mouse, 18 w4d.exception; 19 import g4d.glfw.cursor, 20 g4d.shader.base; 21 import gl3n.linalg; 22 import std.algorithm, 23 std.array, 24 std.conv; 25 26 mixin Context; 27 28 /// An empty widget. 29 class Widget : WindowContent, Layoutable 30 { 31 protected uint _status; 32 /// Current status. 33 @property status () { return _status; } 34 35 protected WindowContext _context; 36 37 protected WidgetStyle _style; 38 /// Style of the widget. 39 @property WidgetStyle style () { return _style; } 40 41 protected ColorSet _colorset; 42 /// Current colorset. 43 @property colorset () { return _colorset; } 44 45 protected Layout _layout; 46 protected BoxElement _box; 47 48 protected bool _needLayout; 49 50 protected Widget findChildAt ( vec2 pt ) 51 { 52 // Use reversed foreach to match the order with drawing. 53 foreach_reverse ( c; calcedChildren ) { 54 if ( c.style.isPointInside(pt) ) { 55 return c; 56 } 57 } 58 return null; 59 } 60 61 mixin Mouse; 62 mixin Keyboard; 63 64 /// 65 void handlePopup ( bool, WindowContext ) 66 { 67 } 68 69 /// 70 this () 71 { 72 _status = WidgetState.None; 73 _style = new WidgetStyle; 74 75 _context = null; 76 _box = new BoxElement; 77 78 _needLayout = true; 79 80 parseColorSetsFromFile!"colorset/widget.yaml"( style ); 81 setLayout!FillLayout(); 82 } 83 84 /// 85 @property vec2 wantedSize () 86 { 87 return vec2(-1,-1); 88 } 89 /// Child widgets. 90 @property Widget[] children () 91 { 92 return []; 93 } 94 /// Child widgets that have no uncalculated style properties. 95 @property Widget[] calcedChildren () 96 { 97 return children.filter!"a.style.isCalced".array; 98 } 99 /// Child widgets that are converted to Layoutables. 100 @property Layoutable[] childLayoutables () 101 { 102 return children.to!( Layoutable[] ); 103 } 104 105 protected void infectWindowContext () 106 { 107 enforce( _context, "WindowContext is null." ); 108 children.each!( x => x._context = _context ); 109 } 110 111 /// Enables the state. 112 void enableState ( WidgetState state ) 113 { 114 _status |= state; 115 requestRedraw(); 116 } 117 /// Disables the state. 118 void disableState ( WidgetState state ) 119 { 120 _status &= ~state; 121 requestRedraw(); 122 } 123 124 /// Changes Layout object. 125 void setLayout (L,Args...) ( Args args ) 126 { 127 _layout = new L(this,args); 128 } 129 /// Checks if the widget needs re-layout. 130 @property bool needLayout () 131 { 132 return _needLayout || children.canFind!"a.needLayout" || !style.isCalced; 133 } 134 /// Set needLayout true. 135 void requestLayout () 136 { 137 _needLayout = true; 138 } 139 /// Re-layouts the widget. 140 /// Be called only by Window. 141 void layout ( vec2i size ) 142 { 143 layout( vec2(0,0), vec2(size) ); 144 } 145 /// Re-layouts the widget. 146 vec2 layout ( vec2 pos, vec2 size ) 147 { 148 infectWindowContext(); 149 150 enforce( _layout, "Layout is null." ); 151 _layout.place( pos, size ); 152 _needLayout = false; 153 154 _box.resize( _style.box ); 155 requestRedraw(); 156 157 return style.box.collisionSize; 158 } 159 /// Moves all children. 160 void shiftChildren ( vec2 size ) 161 { 162 foreach ( c; calcedChildren ) { 163 c.style.shift( size ); 164 c.shiftChildren( size ); 165 } 166 requestRedraw(); 167 } 168 169 /// Checks if the widget needs redrawing. 170 @property bool needRedraw () 171 { 172 return _context && _context.needRedraw; 173 } 174 /// Sets needRedraw true. 175 void requestRedraw () 176 { 177 if ( _context ) { 178 _context.requestRedraw(); 179 } 180 } 181 protected void drawBox ( Window win ) 182 { 183 auto shader = win.shaders.fill3; 184 const saver = ShaderStateSaver( shader ); 185 186 shader.use(); 187 shader.matrix.late = vec3( style.translate, 0 ); 188 189 _box.setColor( colorset ); 190 _box.draw( shader ); 191 } 192 protected void drawChildren ( Window win ) 193 { 194 //children.each!( x => x.draw(win,colorset) ); 195 foreach ( child; children ) { 196 child.draw( win, colorset ); 197 } 198 } 199 /// Draws the widget. 200 /// Be called only by Window. 201 void draw ( Window win ) 202 { 203 draw( win, ColorSet() ); 204 } 205 /// Draws the widget. 206 void draw ( Window win, ColorSet parent ) 207 { 208 _colorset = style.getColorSet(status); 209 _colorset.inherit( parent ); 210 211 drawBox( win ); 212 drawChildren( win ); 213 _context.setNoNeedRedraw(); 214 } 215 }