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.input.checkbox; 8 import w4d.layout.placer.lineup, 9 w4d.layout.fill, 10 w4d.parser.colorset, 11 w4d.style.color, 12 w4d.style.rect, 13 w4d.style.scalar, 14 w4d.style.widget, 15 w4d.task.window, 16 w4d.widget.base, 17 w4d.widget.text, 18 w4d.event, 19 w4d.exception; 20 import g4d.element.shape.border, 21 g4d.element.shape.regular, 22 g4d.ft.font, 23 g4d.glfw.cursor, 24 g4d.shader.base; 25 import gl3n.linalg; 26 import std.math; 27 28 /// A handler that handles changing checked. 29 alias CheckHandler = EventHandler!( void, bool ); 30 31 /// A widget of checkbox. 32 class CheckBoxWidget : Widget 33 { 34 protected class CheckMarkWidget : Widget 35 { 36 protected RegularNgonBorderElement!4 _border; 37 protected RegularNgonElement!4 _mark; 38 39 override @property vec2 wantedSize () 40 { 41 auto lineheight = _text.font.size.y; 42 return vec2( lineheight, lineheight ); 43 } 44 override @property const(Cursor) cursor () 45 { 46 return Cursor.Hand; 47 } 48 this () 49 { 50 super(); 51 52 _border = new RegularNgonBorderElement!4; 53 _mark = new RegularNgonElement!4; 54 55 style.box.size.width = Scalar.Auto; 56 style.box.size.height = Scalar.Auto; 57 } 58 override vec2 layout ( vec2 pos, vec2 size ) 59 { 60 scope(success) { 61 auto sz = style.box.clientSize.x; 62 _border.resize( sz/2, 1.5f ); 63 _mark .resize( sz/3 ); 64 } 65 return super.layout( pos, size ); 66 } 67 override void draw ( Window w, in ColorSet parent ) 68 { 69 super.draw( w, parent ); 70 71 auto shader = w.shaders.fill3; 72 const saver = ShaderStateSaver( shader ); 73 auto late = style.box.clientSize/2; 74 late += style.clientLeftTop; 75 76 shader.use(); 77 shader.matrix.late = vec3( late, 0 ); 78 shader.matrix.rota = vec3( 0, 0, PI/4 ); 79 shader.color = colorset.foreground; 80 _border.draw( shader ); 81 82 if ( checked ) { 83 _mark.draw( shader ); 84 } 85 } 86 override @property bool trackable () { return false; } 87 override @property bool focusable () { return false; } 88 } 89 90 protected CheckMarkWidget _mark; 91 protected TextWidget _text; 92 93 /// 94 override @property Widget[] children () 95 { 96 return [ _mark, _text ]; 97 } 98 99 /// Checks if the checkbox is checked. 100 const @property checked () { return status.selected; } 101 102 /// 103 CheckHandler onCheck; 104 105 /// 106 override bool handleMouseButton ( MouseButton btn, bool status, vec2 pos ) 107 { 108 if ( super.handleMouseButton( btn, status, pos ) ) return true; 109 110 if ( !style.isPointInside(pos) ) return false; 111 112 if ( btn == MouseButton.Left && !status ) { 113 setChecked( !checked ); 114 return true; 115 } 116 return false; 117 } 118 119 /// 120 this () 121 { 122 super(); 123 setLayout!( FillLayout, HorizontalLineupPlacer ); 124 125 _mark = new CheckMarkWidget; 126 _text = new TextWidget; 127 128 parseColorSetsFromFile!"colorset/checkbox.yaml"( style ); 129 style.box.margins = Rect(1.mm); 130 style.box.borderWidth = Rect(1.pixel); 131 } 132 133 /// Changes checked. 134 void setChecked ( bool b ) 135 { 136 if ( status.locked ) return; 137 138 const temp = status.selected; 139 status.selected = b; 140 141 if ( b != temp ) { 142 onCheck.call( b ); 143 } 144 } 145 146 /// Changes text. 147 void loadText ( dstring text, FontFace face = null ) 148 { 149 _text.loadText( text, face ); 150 } 151 152 /// 153 override const @property bool trackable () { return true; } 154 /// 155 override const @property bool focusable () { return true; } 156 }