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