1 /*************************************************************************/
2 /* item_list_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 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
31 #include "item_list_editor_plugin.h"
32
33 #include "core/io/resource_loader.h"
34 #include "editor/editor_scale.h"
35
_set(const StringName & p_name,const Variant & p_value)36 bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) {
37
38 String name = p_name;
39 int idx = name.get_slice("/", 0).to_int();
40 String what = name.get_slice("/", 1);
41
42 if (what == "text")
43 set_item_text(idx, p_value);
44 else if (what == "icon")
45 set_item_icon(idx, p_value);
46 else if (what == "checkable") {
47 // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
48 switch ((int)p_value) {
49 case 0:
50 case 1:
51 set_item_checkable(idx, p_value);
52 break;
53 case 2:
54 set_item_radio_checkable(idx, true);
55 break;
56 }
57 } else if (what == "checked")
58 set_item_checked(idx, p_value);
59 else if (what == "id")
60 set_item_id(idx, p_value);
61 else if (what == "enabled")
62 set_item_enabled(idx, p_value);
63 else if (what == "separator")
64 set_item_separator(idx, p_value);
65 else
66 return false;
67
68 return true;
69 }
70
_get(const StringName & p_name,Variant & r_ret) const71 bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const {
72
73 String name = p_name;
74 int idx = name.get_slice("/", 0).to_int();
75 String what = name.get_slice("/", 1);
76
77 if (what == "text")
78 r_ret = get_item_text(idx);
79 else if (what == "icon")
80 r_ret = get_item_icon(idx);
81 else if (what == "checkable") {
82 // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
83 if (!is_item_checkable(idx)) {
84 r_ret = 0;
85 } else {
86 r_ret = is_item_radio_checkable(idx) ? 2 : 1;
87 }
88 } else if (what == "checked")
89 r_ret = is_item_checked(idx);
90 else if (what == "id")
91 r_ret = get_item_id(idx);
92 else if (what == "enabled")
93 r_ret = is_item_enabled(idx);
94 else if (what == "separator")
95 r_ret = is_item_separator(idx);
96 else
97 return false;
98
99 return true;
100 }
_get_property_list(List<PropertyInfo> * p_list) const101 void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const {
102
103 for (int i = 0; i < get_item_count(); i++) {
104
105 String base = itos(i) + "/";
106
107 p_list->push_back(PropertyInfo(Variant::STRING, base + "text"));
108 p_list->push_back(PropertyInfo(Variant::OBJECT, base + "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"));
109
110 int flags = get_flags();
111
112 if (flags & FLAG_CHECKABLE) {
113 p_list->push_back(PropertyInfo(Variant::INT, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"));
114 p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked"));
115 }
116
117 if (flags & FLAG_ID)
118 p_list->push_back(PropertyInfo(Variant::INT, base + "id", PROPERTY_HINT_RANGE, "-1,4096"));
119
120 if (flags & FLAG_ENABLE)
121 p_list->push_back(PropertyInfo(Variant::BOOL, base + "enabled"));
122
123 if (flags & FLAG_SEPARATOR)
124 p_list->push_back(PropertyInfo(Variant::BOOL, base + "separator"));
125 }
126 }
127
128 ///////////////////////////////////////////////////////////////
129 ///////////////////////// PLUGINS /////////////////////////////
130 ///////////////////////////////////////////////////////////////
131
set_object(Object * p_object)132 void ItemListOptionButtonPlugin::set_object(Object *p_object) {
133
134 ob = Object::cast_to<OptionButton>(p_object);
135 }
136
handles(Object * p_object) const137 bool ItemListOptionButtonPlugin::handles(Object *p_object) const {
138
139 return p_object->is_class("OptionButton");
140 }
141
get_flags() const142 int ItemListOptionButtonPlugin::get_flags() const {
143
144 return FLAG_ICON | FLAG_ID | FLAG_ENABLE;
145 }
146
add_item()147 void ItemListOptionButtonPlugin::add_item() {
148
149 ob->add_item(vformat(TTR("Item %d"), ob->get_item_count()));
150 _change_notify();
151 }
152
get_item_count() const153 int ItemListOptionButtonPlugin::get_item_count() const {
154
155 return ob->get_item_count();
156 }
157
erase(int p_idx)158 void ItemListOptionButtonPlugin::erase(int p_idx) {
159
160 ob->remove_item(p_idx);
161 _change_notify();
162 }
163
ItemListOptionButtonPlugin()164 ItemListOptionButtonPlugin::ItemListOptionButtonPlugin() {
165
166 ob = NULL;
167 }
168
169 ///////////////////////////////////////////////////////////////
170
set_object(Object * p_object)171 void ItemListPopupMenuPlugin::set_object(Object *p_object) {
172
173 if (p_object->is_class("MenuButton"))
174 pp = Object::cast_to<MenuButton>(p_object)->get_popup();
175 else
176 pp = Object::cast_to<PopupMenu>(p_object);
177 }
178
handles(Object * p_object) const179 bool ItemListPopupMenuPlugin::handles(Object *p_object) const {
180
181 return p_object->is_class("PopupMenu") || p_object->is_class("MenuButton");
182 }
183
get_flags() const184 int ItemListPopupMenuPlugin::get_flags() const {
185
186 return FLAG_ICON | FLAG_CHECKABLE | FLAG_ID | FLAG_ENABLE | FLAG_SEPARATOR;
187 }
188
add_item()189 void ItemListPopupMenuPlugin::add_item() {
190
191 pp->add_item(vformat(TTR("Item %d"), pp->get_item_count()));
192 _change_notify();
193 }
194
get_item_count() const195 int ItemListPopupMenuPlugin::get_item_count() const {
196
197 return pp->get_item_count();
198 }
199
erase(int p_idx)200 void ItemListPopupMenuPlugin::erase(int p_idx) {
201
202 pp->remove_item(p_idx);
203 _change_notify();
204 }
205
ItemListPopupMenuPlugin()206 ItemListPopupMenuPlugin::ItemListPopupMenuPlugin() {
207
208 pp = NULL;
209 }
210
211 ///////////////////////////////////////////////////////////////
212
set_object(Object * p_object)213 void ItemListItemListPlugin::set_object(Object *p_object) {
214
215 pp = Object::cast_to<ItemList>(p_object);
216 }
217
handles(Object * p_object) const218 bool ItemListItemListPlugin::handles(Object *p_object) const {
219
220 return p_object->is_class("ItemList");
221 }
222
get_flags() const223 int ItemListItemListPlugin::get_flags() const {
224
225 return FLAG_ICON | FLAG_ENABLE;
226 }
227
add_item()228 void ItemListItemListPlugin::add_item() {
229
230 pp->add_item(vformat(TTR("Item %d"), pp->get_item_count()));
231 _change_notify();
232 }
233
get_item_count() const234 int ItemListItemListPlugin::get_item_count() const {
235
236 return pp->get_item_count();
237 }
238
erase(int p_idx)239 void ItemListItemListPlugin::erase(int p_idx) {
240
241 pp->remove_item(p_idx);
242 _change_notify();
243 }
244
ItemListItemListPlugin()245 ItemListItemListPlugin::ItemListItemListPlugin() {
246
247 pp = NULL;
248 }
249
250 ///////////////////////////////////////////////////////////////
251 ///////////////////////////////////////////////////////////////
252 ///////////////////////////////////////////////////////////////
253
_node_removed(Node * p_node)254 void ItemListEditor::_node_removed(Node *p_node) {
255
256 if (p_node == item_list) {
257 item_list = NULL;
258 hide();
259 dialog->hide();
260 }
261 }
262
_notification(int p_notification)263 void ItemListEditor::_notification(int p_notification) {
264
265 if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) {
266
267 add_button->set_icon(get_icon("Add", "EditorIcons"));
268 del_button->set_icon(get_icon("Remove", "EditorIcons"));
269 } else if (p_notification == NOTIFICATION_READY) {
270
271 get_tree()->connect("node_removed", this, "_node_removed");
272 }
273 }
274
_add_pressed()275 void ItemListEditor::_add_pressed() {
276
277 if (selected_idx == -1)
278 return;
279
280 item_plugins[selected_idx]->add_item();
281 }
282
_delete_pressed()283 void ItemListEditor::_delete_pressed() {
284
285 if (selected_idx == -1)
286 return;
287
288 String current_selected = (String)property_editor->get_selected_path();
289
290 if (current_selected == "")
291 return;
292
293 // FIXME: Currently relying on selecting a *property* to derive what item to delete
294 // e.g. you select "1/enabled" to delete item 1.
295 // This should be fixed so that you can delete by selecting the item section header,
296 // or a delete button on that header.
297
298 int idx = current_selected.get_slice("/", 0).to_int();
299
300 item_plugins[selected_idx]->erase(idx);
301 }
302
_edit_items()303 void ItemListEditor::_edit_items() {
304
305 dialog->popup_centered_clamped(Vector2(425, 1200) * EDSCALE, 0.8);
306 }
307
edit(Node * p_item_list)308 void ItemListEditor::edit(Node *p_item_list) {
309
310 item_list = p_item_list;
311
312 if (!item_list) {
313 selected_idx = -1;
314 property_editor->edit(NULL);
315 return;
316 }
317
318 for (int i = 0; i < item_plugins.size(); i++) {
319 if (item_plugins[i]->handles(p_item_list)) {
320
321 item_plugins[i]->set_object(p_item_list);
322 property_editor->edit(item_plugins[i]);
323
324 toolbar_button->set_icon(EditorNode::get_singleton()->get_object_icon(item_list, ""));
325
326 selected_idx = i;
327 return;
328 }
329 }
330
331 selected_idx = -1;
332 property_editor->edit(NULL);
333 }
334
handles(Object * p_object) const335 bool ItemListEditor::handles(Object *p_object) const {
336
337 for (int i = 0; i < item_plugins.size(); i++) {
338 if (item_plugins[i]->handles(p_object)) {
339 return true;
340 }
341 }
342
343 return false;
344 }
345
_bind_methods()346 void ItemListEditor::_bind_methods() {
347
348 ClassDB::bind_method("_node_removed", &ItemListEditor::_node_removed);
349 ClassDB::bind_method("_edit_items", &ItemListEditor::_edit_items);
350 ClassDB::bind_method("_add_button", &ItemListEditor::_add_pressed);
351 ClassDB::bind_method("_delete_button", &ItemListEditor::_delete_pressed);
352 }
353
ItemListEditor()354 ItemListEditor::ItemListEditor() {
355
356 selected_idx = -1;
357 item_list = NULL;
358
359 toolbar_button = memnew(ToolButton);
360 toolbar_button->set_text(TTR("Items"));
361 add_child(toolbar_button);
362 toolbar_button->connect("pressed", this, "_edit_items");
363
364 dialog = memnew(AcceptDialog);
365 dialog->set_title(TTR("Item List Editor"));
366 add_child(dialog);
367
368 VBoxContainer *vbc = memnew(VBoxContainer);
369 dialog->add_child(vbc);
370 //dialog->set_child_rect(vbc);
371
372 HBoxContainer *hbc = memnew(HBoxContainer);
373 hbc->set_h_size_flags(SIZE_EXPAND_FILL);
374 vbc->add_child(hbc);
375
376 add_button = memnew(Button);
377 add_button->set_text(TTR("Add"));
378 hbc->add_child(add_button);
379 add_button->connect("pressed", this, "_add_button");
380
381 hbc->add_spacer();
382
383 del_button = memnew(Button);
384 del_button->set_text(TTR("Delete"));
385 hbc->add_child(del_button);
386 del_button->connect("pressed", this, "_delete_button");
387
388 property_editor = memnew(EditorInspector);
389 vbc->add_child(property_editor);
390 property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
391 }
392
~ItemListEditor()393 ItemListEditor::~ItemListEditor() {
394
395 for (int i = 0; i < item_plugins.size(); i++)
396 memdelete(item_plugins[i]);
397 }
398
edit(Object * p_object)399 void ItemListEditorPlugin::edit(Object *p_object) {
400
401 item_list_editor->edit(Object::cast_to<Node>(p_object));
402 }
403
handles(Object * p_object) const404 bool ItemListEditorPlugin::handles(Object *p_object) const {
405
406 return item_list_editor->handles(p_object);
407 }
408
make_visible(bool p_visible)409 void ItemListEditorPlugin::make_visible(bool p_visible) {
410
411 if (p_visible) {
412 item_list_editor->show();
413 } else {
414
415 item_list_editor->hide();
416 item_list_editor->edit(NULL);
417 }
418 }
419
ItemListEditorPlugin(EditorNode * p_node)420 ItemListEditorPlugin::ItemListEditorPlugin(EditorNode *p_node) {
421
422 editor = p_node;
423 item_list_editor = memnew(ItemListEditor);
424 CanvasItemEditor::get_singleton()->add_control_to_menu_panel(item_list_editor);
425
426 item_list_editor->hide();
427 item_list_editor->add_plugin(memnew(ItemListOptionButtonPlugin));
428 item_list_editor->add_plugin(memnew(ItemListPopupMenuPlugin));
429 item_list_editor->add_plugin(memnew(ItemListItemListPlugin));
430 }
431
~ItemListEditorPlugin()432 ItemListEditorPlugin::~ItemListEditorPlugin() {
433 }
434