1 ///
2 module nanogui.combobox;
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 import std.container.array : Array;
13 import nanogui.popupbutton : PopupButton;
14 import nanogui.button : Button;
15 import nanogui.widget : Widget;
16 import nanogui.common : Vector2i, Vector2f;
17 
18 /**
19  * Simple combo box widget based on a popup button.
20  */
21 class ComboBox : PopupButton
22 {
23 public:
24 	/// Create an empty combo box
25 	this(Widget parent)
26 	{
27 		super(parent);
28 		mSelectedIndex = 0;
29 	}
30 
31 	/// Create a new combo box with the given items
32 	this(Widget parent, const string[] items)
33 	{
34 		this(parent);
35 		this.items(items);
36 	}
37 
38 	/**
39 	 * Create a new combo box with the given items, providing both short and
40 	 * long descriptive labels for each item
41 	 */
42 	this(Widget parent, const string[] items,
43 			 const string[] itemsShort)
44 	{
45 		this(parent);
46 		this.items(items, itemsShort);
47 	}
48 
49 	/// The callback to execute for this ComboBox.
50 	final void delegate(int) callback() const { return mCallback; }
51 
52 	/// Sets the callback to execute for this ComboBox.
53 	final void callback(void delegate(int) callback) { mCallback = callback; }
54 
55 	/// The current index this ComboBox has selected.
56 	final int selectedIndex() const { return mSelectedIndex; }
57 
58 	/// Sets the current index this ComboBox has selected.
59 	final void selectedIndex(int idx)
60 	{
61 		if (mItemsShort.empty)
62 			return;
63 		auto children = popup.children;
64 		(cast(Button) children[mSelectedIndex]).pushed = false;
65 		(cast(Button) children[idx]).pushed = true;
66 		mSelectedIndex = idx;
67 		caption = mItemsShort[idx];
68 	}
69 
70 	/// Sets the items for this ComboBox, providing both short and long descriptive lables for each item.
71 	final void items(const string[] items, const string[] itemsShort)
72 	{
73 		import std.exception : enforce;
74 
75 		enforce(items.length == itemsShort.length);
76 		mItems.clear;
77 		mItems.insertBack(items);
78 		mItemsShort.clear;
79 		mItemsShort.insertBack(itemsShort);
80 		if (mSelectedIndex < 0 || mSelectedIndex >= cast(int) items.length)
81 			mSelectedIndex = 0;
82 		while (mPopup.childCount != 0)
83 			mPopup.removeChild(mPopup.childCount-1);
84 
85 		import nanogui.layout : GroupLayout;
86 		mPopup.layout = new GroupLayout(2, 2);
87 		int index;
88 		foreach (const ref str; items)
89 		{
90 			Button button = new Button(mPopup, str);
91 			button.flags = Button.Flags.RadioButton;
92 			button.callback = ((int idx) => () {
93 				mSelectedIndex = idx;
94 				caption = mItemsShort[idx];
95 				pushed = false;
96 				popup.visible = false;
97 				if (mCallback)
98 					mCallback(idx);
99 			})(index);
100 			index++;
101 		}
102 		selectedIndex = mSelectedIndex;
103 	}
104 
105 	/// Sets the items for this ComboBox.
106 	final void items(const string[] i) { items(i, i); }
107 
108 	/// The items associated with this ComboBox.
109 	final auto items() const { return mItems; }
110 
111 	/// The short descriptions associated with this ComboBox.
112 	final auto itemsShort() const { return mItemsShort; }
113 
114 	/// Handles mouse scrolling events for this ComboBox.
115 	override bool scrollEvent(Vector2i p, Vector2f rel)
116 	{
117 		import std.algorithm : min, max;
118 
119 		if (rel.y < 0)
120 		{
121 			selectedIndex = min(mSelectedIndex+1, cast(int)(items.length-1));
122 			if (mCallback)
123 				mCallback(mSelectedIndex);
124 			return true;
125 		} else if (rel.y > 0)
126 		{
127 			selectedIndex(max(mSelectedIndex-1, 0));
128 			if (mCallback)
129 				mCallback(mSelectedIndex);
130 			return true;
131 		}
132 		return Widget.scrollEvent(p, rel);
133 	}
134 
135 // /// Saves the state of this ComboBox to the specified Serializer.
136 // virtual void save(Serializer &s) const override;
137 
138 // /// Sets the state of this ComboBox from the specified Serializer.
139 // virtual bool load(Serializer &s) override;
140 
141 protected:
142 	/// The items associated with this ComboBox.
143 	Array!string mItems;
144 
145 	/// The short descriptions of items associated with this ComboBox.
146 	Array!string mItemsShort;
147 
148 	/// The callback for this ComboBox.
149 	void delegate(int) mCallback;
150 
151 	/// The current index this ComboBox has selected.
152 	int mSelectedIndex;
153 }