1 /*************************************************************************/
2 /*  option_button.cpp                                                    */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "option_button.h"
31 #include "print_string.h"
32 
get_minimum_size() const33 Size2 OptionButton::get_minimum_size() const {
34 
35 	Size2 minsize = Button::get_minimum_size();
36 
37 	if (has_icon("arrow"))
38 		minsize.width += Control::get_icon("arrow")->get_width();
39 
40 	return minsize;
41 }
42 
_notification(int p_what)43 void OptionButton::_notification(int p_what) {
44 
45 	switch (p_what) {
46 
47 		case NOTIFICATION_DRAW: {
48 
49 			if (!has_icon("arrow"))
50 				return;
51 
52 			RID ci = get_canvas_item();
53 			Ref<Texture> arrow = Control::get_icon("arrow");
54 			Ref<StyleBox> normal = get_stylebox("normal");
55 
56 			Size2 size = get_size();
57 
58 			Point2 ofs(size.width - arrow->get_width() - get_constant("arrow_margin"), int(Math::abs((size.height - arrow->get_height()) / 2)));
59 			arrow->draw(ci, ofs);
60 
61 		} break;
62 	}
63 }
64 
_selected(int p_which)65 void OptionButton::_selected(int p_which) {
66 
67 	int selid = -1;
68 	for (int i = 0; i < popup->get_item_count(); i++) {
69 
70 		bool is_clicked = popup->get_item_ID(i) == p_which;
71 		if (is_clicked) {
72 			selid = i;
73 			break;
74 		}
75 	}
76 
77 	if (selid == -1 && p_which >= 0 && p_which < popup->get_item_count()) {
78 		_select(p_which, true);
79 	} else {
80 
81 		ERR_FAIL_COND(selid == -1);
82 
83 		_select(selid, true);
84 	}
85 }
86 
pressed()87 void OptionButton::pressed() {
88 
89 	Size2 size = get_size();
90 	popup->set_global_pos(get_global_pos() + Size2(0, size.height));
91 	popup->set_size(Size2(size.width, 0));
92 
93 	popup->popup();
94 }
95 
add_icon_item(const Ref<Texture> & p_icon,const String & p_label,int p_ID)96 void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
97 
98 	popup->add_icon_check_item(p_icon, p_label, p_ID);
99 	if (popup->get_item_count() == 1)
100 		select(0);
101 }
add_item(const String & p_label,int p_ID)102 void OptionButton::add_item(const String &p_label, int p_ID) {
103 
104 	popup->add_check_item(p_label, p_ID);
105 	if (popup->get_item_count() == 1)
106 		select(0);
107 }
108 
set_item_text(int p_idx,const String & p_text)109 void OptionButton::set_item_text(int p_idx, const String &p_text) {
110 
111 	popup->set_item_text(p_idx, p_text);
112 }
set_item_icon(int p_idx,const Ref<Texture> & p_icon)113 void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
114 
115 	popup->set_item_icon(p_idx, p_icon);
116 }
set_item_ID(int p_idx,int p_ID)117 void OptionButton::set_item_ID(int p_idx, int p_ID) {
118 
119 	popup->set_item_ID(p_idx, p_ID);
120 }
121 
set_item_metadata(int p_idx,const Variant & p_metadata)122 void OptionButton::set_item_metadata(int p_idx, const Variant &p_metadata) {
123 
124 	popup->set_item_metadata(p_idx, p_metadata);
125 }
126 
set_item_disabled(int p_idx,bool p_disabled)127 void OptionButton::set_item_disabled(int p_idx, bool p_disabled) {
128 
129 	popup->set_item_disabled(p_idx, p_disabled);
130 }
131 
get_item_text(int p_idx) const132 String OptionButton::get_item_text(int p_idx) const {
133 
134 	return popup->get_item_text(p_idx);
135 }
136 
get_item_icon(int p_idx) const137 Ref<Texture> OptionButton::get_item_icon(int p_idx) const {
138 
139 	return popup->get_item_icon(p_idx);
140 }
141 
get_item_ID(int p_idx) const142 int OptionButton::get_item_ID(int p_idx) const {
143 
144 	return popup->get_item_ID(p_idx);
145 }
get_item_metadata(int p_idx) const146 Variant OptionButton::get_item_metadata(int p_idx) const {
147 
148 	return popup->get_item_metadata(p_idx);
149 }
150 
is_item_disabled(int p_idx) const151 bool OptionButton::is_item_disabled(int p_idx) const {
152 
153 	return popup->is_item_disabled(p_idx);
154 }
155 
get_item_count() const156 int OptionButton::get_item_count() const {
157 
158 	return popup->get_item_count();
159 }
160 
add_separator()161 void OptionButton::add_separator() {
162 
163 	popup->add_separator();
164 }
165 
clear()166 void OptionButton::clear() {
167 
168 	popup->clear();
169 	set_text("");
170 	current = -1;
171 }
172 
_select(int p_idx,bool p_emit)173 void OptionButton::_select(int p_idx, bool p_emit) {
174 
175 	if (p_idx < 0)
176 		return;
177 	if (p_idx == current)
178 		return;
179 
180 	ERR_FAIL_INDEX(p_idx, popup->get_item_count());
181 
182 	for (int i = 0; i < popup->get_item_count(); i++) {
183 
184 		popup->set_item_checked(i, i == p_idx);
185 	}
186 
187 	current = p_idx;
188 	set_text(popup->get_item_text(current));
189 	set_icon(popup->get_item_icon(current));
190 
191 	if (is_inside_tree() && p_emit)
192 		emit_signal("item_selected", current);
193 }
194 
_select_int(int p_which)195 void OptionButton::_select_int(int p_which) {
196 
197 	if (p_which < 0 || p_which >= popup->get_item_count())
198 		return;
199 	_select(p_which, false);
200 }
201 
select(int p_idx)202 void OptionButton::select(int p_idx) {
203 
204 	_select(p_idx, false);
205 }
206 
get_selected() const207 int OptionButton::get_selected() const {
208 
209 	return current;
210 }
211 
get_selected_ID() const212 int OptionButton::get_selected_ID() const {
213 
214 	int idx = get_selected();
215 	if (idx < 0)
216 		return 0;
217 	return get_item_ID(current);
218 }
get_selected_metadata() const219 Variant OptionButton::get_selected_metadata() const {
220 
221 	int idx = get_selected();
222 	if (idx < 0)
223 		return Variant();
224 	return get_item_metadata(current);
225 }
226 
remove_item(int p_idx)227 void OptionButton::remove_item(int p_idx) {
228 
229 	popup->remove_item(p_idx);
230 }
231 
_get_items() const232 Array OptionButton::_get_items() const {
233 
234 	Array items;
235 	for (int i = 0; i < get_item_count(); i++) {
236 
237 		items.push_back(get_item_text(i));
238 		items.push_back(get_item_icon(i));
239 		items.push_back(is_item_disabled(i));
240 		items.push_back(get_item_ID(i));
241 		items.push_back(get_item_metadata(i));
242 	}
243 
244 	return items;
245 }
_set_items(const Array & p_items)246 void OptionButton::_set_items(const Array &p_items) {
247 
248 	ERR_FAIL_COND(p_items.size() % 5);
249 	clear();
250 
251 	for (int i = 0; i < p_items.size(); i += 5) {
252 
253 		String text = p_items[i + 0];
254 		Ref<Texture> icon = p_items[i + 1];
255 		bool disabled = p_items[i + 2];
256 		int id = p_items[i + 3];
257 		Variant meta = p_items[i + 4];
258 
259 		int idx = get_item_count();
260 		add_item(text, id);
261 		set_item_icon(idx, icon);
262 		set_item_disabled(idx, disabled);
263 		set_item_metadata(idx, meta);
264 	}
265 }
266 
get_translatable_strings(List<String> * p_strings) const267 void OptionButton::get_translatable_strings(List<String> *p_strings) const {
268 
269 	return popup->get_translatable_strings(p_strings);
270 }
271 
_bind_methods()272 void OptionButton::_bind_methods() {
273 
274 	ObjectTypeDB::bind_method(_MD("_selected"), &OptionButton::_selected);
275 
276 	ObjectTypeDB::bind_method(_MD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1));
277 	ObjectTypeDB::bind_method(_MD("add_icon_item", "texture:Texture", "label", "id"), &OptionButton::add_icon_item);
278 	ObjectTypeDB::bind_method(_MD("set_item_text", "idx", "text"), &OptionButton::set_item_text);
279 	ObjectTypeDB::bind_method(_MD("set_item_icon", "idx", "texture:Texture"), &OptionButton::set_item_icon);
280 	ObjectTypeDB::bind_method(_MD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled);
281 	ObjectTypeDB::bind_method(_MD("set_item_ID", "idx", "id"), &OptionButton::set_item_ID);
282 	ObjectTypeDB::bind_method(_MD("set_item_metadata", "idx", "metadata"), &OptionButton::set_item_metadata);
283 	ObjectTypeDB::bind_method(_MD("get_item_text", "idx"), &OptionButton::get_item_text);
284 	ObjectTypeDB::bind_method(_MD("get_item_icon:Texture", "idx"), &OptionButton::get_item_icon);
285 	ObjectTypeDB::bind_method(_MD("get_item_ID", "idx"), &OptionButton::get_item_ID);
286 	ObjectTypeDB::bind_method(_MD("get_item_metadata", "idx"), &OptionButton::get_item_metadata);
287 	ObjectTypeDB::bind_method(_MD("is_item_disabled", "idx"), &OptionButton::is_item_disabled);
288 	ObjectTypeDB::bind_method(_MD("get_item_count"), &OptionButton::get_item_count);
289 	ObjectTypeDB::bind_method(_MD("add_separator"), &OptionButton::add_separator);
290 	ObjectTypeDB::bind_method(_MD("clear"), &OptionButton::clear);
291 	ObjectTypeDB::bind_method(_MD("select", "idx"), &OptionButton::select);
292 	ObjectTypeDB::bind_method(_MD("get_selected"), &OptionButton::get_selected);
293 	ObjectTypeDB::bind_method(_MD("get_selected_ID"), &OptionButton::get_selected_ID);
294 	ObjectTypeDB::bind_method(_MD("get_selected_metadata"), &OptionButton::get_selected_metadata);
295 	ObjectTypeDB::bind_method(_MD("remove_item", "idx"), &OptionButton::remove_item);
296 	ObjectTypeDB::bind_method(_MD("_select_int"), &OptionButton::_select_int);
297 
298 	ObjectTypeDB::bind_method(_MD("_set_items"), &OptionButton::_set_items);
299 	ObjectTypeDB::bind_method(_MD("_get_items"), &OptionButton::_get_items);
300 
301 	ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), _SCS("_select_int"), _SCS("get_selected"));
302 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), _SCS("_set_items"), _SCS("_get_items"));
303 	ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID")));
304 }
305 
OptionButton()306 OptionButton::OptionButton() {
307 
308 	popup = memnew(PopupMenu);
309 	popup->hide();
310 	popup->set_as_toplevel(true);
311 	add_child(popup);
312 	popup->connect("item_pressed", this, "_selected");
313 
314 	current = -1;
315 	set_text_align(ALIGN_LEFT);
316 }
317 
~OptionButton()318 OptionButton::~OptionButton() {
319 }
320