1 ///
2 module nanogui.checkbox;
3 
4 /*
5 	NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
6 	The widget drawing code is based on the NanoVG demo application
7 	by Mikko Mononen.
8 
9 	All rights reserved. Use of this source code is governed by a
10 	BSD-style license that can be found in the LICENSE.txt file.
11 */
12 
13 import nanogui.widget;
14 import nanogui.common : Vector2i, Vector2f, MouseButton;
15 
16 /**
17  * Two-state check box widget.
18  *
19  * Remarks:
20  *     This class overrides `nanogui.Widget.mIconExtraScale` to be `1.2f`,
21  *     which affects all subclasses of this Widget.  Subclasses must explicitly
22  *     set a different value if needed (e.g., in their constructor).
23  */
24 class CheckBox : Widget
25 {
26 public:
27 	/**
28 	 * Adds a CheckBox to the specified `parent`.
29 	 *
30 	 * Params:
31 	 *     parent   = The Widget to add this CheckBox to.
32 	 *     caption  = The caption text of the CheckBox (default `"Untitled"`).
33 	 *     callback = If provided, the callback to execute when the CheckBox is 
34 	 *     checked or unchecked.  Default parameter function does nothing.  See
35 	 *     `nanogui.CheckBox.mPushed` for the difference between "pushed"
36 	 *     and "checked".
37 	 */
38 	this(Widget parent, const string caption, void delegate(bool) callback)
39 	{
40 		super(parent);
41 		mCaption = caption;
42 		mPushed = false;
43 		mChecked = false;
44 		mCallback = callback;
45 		mIconExtraScale = 1.2f;// widget override}
46 	}
47 
48 	/// The caption of this CheckBox.
49 	final string caption() const { return mCaption; }
50 
51 	/// Sets the caption of this CheckBox.
52 	final void caption(string caption) { mCaption = caption; }
53 
54 	/// Whether or not this CheckBox is currently checked.
55 	final bool checked() const { return mChecked; }
56 
57 	/// Sets whether or not this CheckBox is currently checked.
58 	final void checked(bool checked) { mChecked = checked; }
59 
60 	/// Whether or not this CheckBox is currently pushed.  See `nanogui.CheckBox.mPushed`.
61 	final bool pushed() const { return mPushed; }
62 
63 	/// Sets whether or not this CheckBox is currently pushed.  See `nanogui.CheckBox.mPushed`.
64 	final void pushed(bool pushed) { mPushed = pushed; }
65 
66 	/// Returns the current callback of this CheckBox.
67 	final void delegate(bool) callback() const { return mCallback; }
68 
69 	/// Sets the callback to be executed when this CheckBox is checked / unchecked.
70 	final void callback(void delegate(bool) callback) { mCallback = callback; }
71 
72 	/**
73 	 * The mouse button callback will return `true` when all three conditions are met:
74 	 *
75 	 * 1. This CheckBox is "enabled" (see `nanogui.Widget.mEnabled`).
76 	 * 2. `p` is inside this CheckBox.
77 	 * 3. `button` is `MouseButton.Left`.
78 	 *
79 	 * Since a mouse button event is issued for both when the mouse is pressed, as well
80 	 * as released, this function sets `nanogui.CheckBox.mPushed` to `true` when
81 	 * parameter `down == true`.  When the second event (`down == false`) is fired,
82 	 * `nanogui.CheckBox.mChecked` is inverted and `nanogui.CheckBox.mCallback`
83 	 * is called.
84 	 *
85 	 * That is, the callback provided is only called when the mouse button is released,
86 	 * **and** the click location remains within the CheckBox boundaries.  If the user
87 	 * clicks on the CheckBox and releases away from the bounds of the CheckBox,
88 	 * `nanogui.CheckBox.mPushed` is simply set back to `false`.
89 	 */
90 	override bool mouseButtonEvent(Vector2i p, MouseButton button, bool down, int modifiers)
91 	{
92 		super.mouseButtonEvent(p, button, down, modifiers);
93 		if (!mEnabled)
94 			return false;
95 
96 		if (button == MouseButton.Left)
97 		{
98 			if (down)
99 			{
100 				mPushed = true;
101 			}
102 			else if (mPushed)
103 			{
104 				if (contains(p))
105 				{
106 					mChecked = !mChecked;
107 					if (mCallback)
108 						mCallback(mChecked);
109 				}
110 				mPushed = false;
111 			}
112 			return true;
113 		}
114 		return false;
115 	}
116 
117 	/// The preferred size of this CheckBox.
118 	override Vector2i preferredSize(NanoContext ctx) const
119 	{
120 		if (mFixedSize != Vector2i())
121 			return mFixedSize;
122 		ctx.fontSize(fontSize());
123 		ctx.fontFace("sans");
124 		float[4] bounds;
125 		return cast(Vector2i) Vector2f(
126 			(ctx.textBounds(0, 0, mCaption, bounds[]) +
127 				1.8f * fontSize()),
128 			fontSize() * 1.3f);
129 	}
130 
131 	/// Draws this CheckBox.
132 	override void draw(ref NanoContext ctx)
133 	{
134 		super.draw(ctx);
135 
136 		ctx.fontSize(fontSize);
137 		ctx.fontFace("sans");
138 		ctx.fillColor(mEnabled ? mTheme.mTextColor : mTheme.mDisabledTextColor);
139 		NVGTextAlign algn;
140 		algn.left = true;
141 		algn.middle = true;
142 		ctx.textAlign(algn);
143 		ctx.text(mPos.x + 1.6f * fontSize, mPos.y + mSize.y * 0.5f,
144 				mCaption);
145 
146 		NVGPaint bg = ctx.boxGradient(mPos.x + 1.5f, mPos.y + 1.5f,
147 									 mSize.y - 2.0f, mSize.y - 2.0f, 3, 3,
148 									 mPushed ? Color(0, 0, 0, 100) : Color(0, 0, 0, 32),
149 									 Color(0, 0, 0, 180));
150 
151 		ctx.beginPath;
152 		ctx.roundedRect(mPos.x + 1.0f, mPos.y + 1.0f, mSize.y - 2.0f,
153 					   mSize.y - 2.0f, 3);
154 		ctx.fillPaint(bg);
155 		ctx.fill;
156 
157 		if (mChecked)
158 		{
159 			ctx.fontSize(mSize.y * icon_scale());
160 			ctx.fontFace("icons");
161 			ctx.fillColor(mEnabled ? mTheme.mIconColor
162 									   : mTheme.mDisabledTextColor);
163 			algn = NVGTextAlign();
164 			algn.center = true;
165 			algn.middle = true;
166 			ctx.textAlign(algn);
167 			ctx.text(mPos.x + mSize.y * 0.5f + 1,
168 					mPos.y + mSize.y * 0.5f, [mTheme.mCheckBoxIcon]);
169 		}
170 	}
171 
172 // // Saves this CheckBox to the specified Serializer.
173 //override void save(Serializer &s) const;
174 
175 // // Loads the state of the specified Serializer to this CheckBox.
176 //override bool load(Serializer &s);
177 
178 protected:
179 	/// The caption text of this CheckBox.
180 	string mCaption;
181 
182 	/**
183 	 * Internal tracking variable to distinguish between mouse click and release.
184 	 * `nanogui.CheckBox.mCallback` is only called upon release.  See
185 	 * `nanogui.CheckBox.mouseButtonEvent` for specific conditions.
186 	 */
187 	bool mPushed;
188 
189 	/// Whether or not this CheckBox is currently checked or unchecked.
190 	bool mChecked;
191 
192 	/// The function to execute when `nanogui.CheckBox.mChecked` is changed.
193 	void delegate(bool) mCallback;
194 }