1 | /**************************************************************************/ |
2 | /* theme_editor_plugin.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
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 | #ifndef THEME_EDITOR_PLUGIN_H |
32 | #define THEME_EDITOR_PLUGIN_H |
33 | |
34 | #include "editor/editor_plugin.h" |
35 | #include "editor/plugins/theme_editor_preview.h" |
36 | #include "scene/gui/dialogs.h" |
37 | #include "scene/gui/margin_container.h" |
38 | #include "scene/gui/tree.h" |
39 | #include "scene/resources/theme.h" |
40 | |
41 | class Button; |
42 | class CheckButton; |
43 | class EditorFileDialog; |
44 | class ItemList; |
45 | class Label; |
46 | class OptionButton; |
47 | class PanelContainer; |
48 | class TabBar; |
49 | class TabContainer; |
50 | class TextureRect; |
51 | |
52 | class ThemeItemImportTree : public VBoxContainer { |
53 | GDCLASS(ThemeItemImportTree, VBoxContainer); |
54 | |
55 | Ref<Theme> edited_theme; |
56 | Ref<Theme> base_theme; |
57 | |
58 | struct ThemeItem { |
59 | String type_name; |
60 | Theme::DataType data_type; |
61 | String item_name; |
62 | |
63 | bool operator<(const ThemeItem &p_item) const { |
64 | if (type_name == p_item.type_name && data_type == p_item.data_type) { |
65 | return item_name < p_item.item_name; |
66 | } |
67 | if (type_name == p_item.type_name) { |
68 | return data_type < p_item.data_type; |
69 | } |
70 | return type_name < p_item.type_name; |
71 | } |
72 | }; |
73 | |
74 | enum ItemCheckedState { |
75 | SELECT_IMPORT_DEFINITION, |
76 | SELECT_IMPORT_FULL, |
77 | }; |
78 | |
79 | RBMap<ThemeItem, ItemCheckedState> selected_items; |
80 | |
81 | LineEdit *import_items_filter = nullptr; |
82 | |
83 | Tree *import_items_tree = nullptr; |
84 | List<TreeItem *> tree_color_items; |
85 | List<TreeItem *> tree_constant_items; |
86 | List<TreeItem *> tree_font_items; |
87 | List<TreeItem *> tree_font_size_items; |
88 | List<TreeItem *> tree_icon_items; |
89 | List<TreeItem *> tree_stylebox_items; |
90 | |
91 | bool updating_tree = false; |
92 | |
93 | enum ItemActionFlag { |
94 | IMPORT_ITEM = 1, |
95 | IMPORT_ITEM_DATA = 2, |
96 | }; |
97 | |
98 | TextureRect *select_colors_icon = nullptr; |
99 | Label *select_colors_label = nullptr; |
100 | Button *select_all_colors_button = nullptr; |
101 | Button *select_full_colors_button = nullptr; |
102 | Button *deselect_all_colors_button = nullptr; |
103 | Label *total_selected_colors_label = nullptr; |
104 | |
105 | TextureRect *select_constants_icon = nullptr; |
106 | Label *select_constants_label = nullptr; |
107 | Button *select_all_constants_button = nullptr; |
108 | Button *select_full_constants_button = nullptr; |
109 | Button *deselect_all_constants_button = nullptr; |
110 | Label *total_selected_constants_label = nullptr; |
111 | |
112 | TextureRect *select_fonts_icon = nullptr; |
113 | Label *select_fonts_label = nullptr; |
114 | Button *select_all_fonts_button = nullptr; |
115 | Button *select_full_fonts_button = nullptr; |
116 | Button *deselect_all_fonts_button = nullptr; |
117 | Label *total_selected_fonts_label = nullptr; |
118 | |
119 | TextureRect *select_font_sizes_icon = nullptr; |
120 | Label *select_font_sizes_label = nullptr; |
121 | Button *select_all_font_sizes_button = nullptr; |
122 | Button *select_full_font_sizes_button = nullptr; |
123 | Button *deselect_all_font_sizes_button = nullptr; |
124 | Label *total_selected_font_sizes_label = nullptr; |
125 | |
126 | TextureRect *select_icons_icon = nullptr; |
127 | Label *select_icons_label = nullptr; |
128 | Button *select_all_icons_button = nullptr; |
129 | Button *select_full_icons_button = nullptr; |
130 | Button *deselect_all_icons_button = nullptr; |
131 | Label *total_selected_icons_label = nullptr; |
132 | |
133 | TextureRect *select_styleboxes_icon = nullptr; |
134 | Label *select_styleboxes_label = nullptr; |
135 | Button *select_all_styleboxes_button = nullptr; |
136 | Button *select_full_styleboxes_button = nullptr; |
137 | Button *deselect_all_styleboxes_button = nullptr; |
138 | Label *total_selected_styleboxes_label = nullptr; |
139 | |
140 | HBoxContainer *select_icons_warning_hb = nullptr; |
141 | TextureRect *select_icons_warning_icon = nullptr; |
142 | Label *select_icons_warning = nullptr; |
143 | |
144 | Button *import_collapse_types_button = nullptr; |
145 | Button *import_expand_types_button = nullptr; |
146 | Button *import_select_all_button = nullptr; |
147 | Button *import_select_full_button = nullptr; |
148 | Button *import_deselect_all_button = nullptr; |
149 | |
150 | void _update_items_tree(); |
151 | void _toggle_type_items(bool p_collapse); |
152 | void _filter_text_changed(const String &p_value); |
153 | |
154 | void _store_selected_item(TreeItem *p_tree_item); |
155 | void _restore_selected_item(TreeItem *p_tree_item); |
156 | void _update_total_selected(Theme::DataType p_data_type); |
157 | |
158 | void _tree_item_edited(); |
159 | void _check_propagated_to_tree_item(Object *p_obj, int p_column); |
160 | void _select_all_subitems(TreeItem *p_root_item, bool p_select_with_data); |
161 | void _deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely); |
162 | |
163 | void _select_all_items_pressed(); |
164 | void _select_full_items_pressed(); |
165 | void _deselect_all_items_pressed(); |
166 | |
167 | void _select_all_data_type_pressed(int p_data_type); |
168 | void _select_full_data_type_pressed(int p_data_type); |
169 | void _deselect_all_data_type_pressed(int p_data_type); |
170 | |
171 | void _import_selected(); |
172 | |
173 | protected: |
174 | void _notification(int p_what); |
175 | static void _bind_methods(); |
176 | |
177 | public: |
178 | void set_edited_theme(const Ref<Theme> &p_theme); |
179 | void set_base_theme(const Ref<Theme> &p_theme); |
180 | void reset_item_tree(); |
181 | |
182 | bool has_selected_items() const; |
183 | |
184 | ThemeItemImportTree(); |
185 | }; |
186 | |
187 | class ThemeTypeEditor; |
188 | |
189 | class ThemeItemEditorDialog : public AcceptDialog { |
190 | GDCLASS(ThemeItemEditorDialog, AcceptDialog); |
191 | |
192 | ThemeTypeEditor *theme_type_editor = nullptr; |
193 | |
194 | Ref<Theme> edited_theme; |
195 | |
196 | TabContainer *tc = nullptr; |
197 | |
198 | enum TypesTreeAction { |
199 | TYPES_TREE_REMOVE_ITEM, |
200 | }; |
201 | |
202 | Tree *edit_type_list = nullptr; |
203 | LineEdit *edit_add_type_value = nullptr; |
204 | Button *edit_add_type_button = nullptr; |
205 | String edited_item_type; |
206 | |
207 | Button *edit_items_add_color = nullptr; |
208 | Button *edit_items_add_constant = nullptr; |
209 | Button *edit_items_add_font = nullptr; |
210 | Button *edit_items_add_font_size = nullptr; |
211 | Button *edit_items_add_icon = nullptr; |
212 | Button *edit_items_add_stylebox = nullptr; |
213 | Button *edit_items_remove_class = nullptr; |
214 | Button *edit_items_remove_custom = nullptr; |
215 | Button *edit_items_remove_all = nullptr; |
216 | Tree *edit_items_tree = nullptr; |
217 | Label *edit_items_message = nullptr; |
218 | |
219 | enum ItemsTreeAction { |
220 | ITEMS_TREE_RENAME_ITEM, |
221 | ITEMS_TREE_REMOVE_ITEM, |
222 | ITEMS_TREE_REMOVE_DATA_TYPE, |
223 | }; |
224 | |
225 | ConfirmationDialog *edit_theme_item_dialog = nullptr; |
226 | VBoxContainer *edit_theme_item_old_vb = nullptr; |
227 | Label *theme_item_old_name = nullptr; |
228 | LineEdit *theme_item_name = nullptr; |
229 | |
230 | enum { |
231 | CREATE_THEME_ITEM, |
232 | RENAME_THEME_ITEM, |
233 | |
234 | }; |
235 | |
236 | ItemPopupMode = ITEM_POPUP_MODE_MAX; |
237 | String edit_item_old_name; |
238 | Theme::DataType edit_item_data_type = Theme::DATA_TYPE_MAX; |
239 | |
240 | ThemeItemImportTree *import_default_theme_items = nullptr; |
241 | ThemeItemImportTree *import_editor_theme_items = nullptr; |
242 | ThemeItemImportTree *import_other_theme_items = nullptr; |
243 | |
244 | LineEdit *import_another_theme_value = nullptr; |
245 | Button *import_another_theme_button = nullptr; |
246 | EditorFileDialog *import_another_theme_dialog = nullptr; |
247 | |
248 | ConfirmationDialog *confirm_closing_dialog = nullptr; |
249 | |
250 | void ok_pressed() override; |
251 | void _close_dialog(); |
252 | |
253 | void _dialog_about_to_show(); |
254 | void _update_edit_types(); |
255 | void _edited_type_selected(); |
256 | void _edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); |
257 | |
258 | void _update_edit_item_tree(String p_item_type); |
259 | void _item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); |
260 | |
261 | void _add_theme_type(const String &p_new_text); |
262 | void _add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type); |
263 | void _remove_theme_type(const String &p_theme_type); |
264 | void _remove_data_type_items(Theme::DataType p_data_type, String p_item_type); |
265 | void _remove_class_items(); |
266 | void _remove_custom_items(); |
267 | void _remove_all_items(); |
268 | |
269 | void _open_add_theme_item_dialog(int p_data_type); |
270 | void _open_rename_theme_item_dialog(Theme::DataType p_data_type, String p_item_name); |
271 | void _confirm_edit_theme_item(); |
272 | void _edit_theme_item_gui_input(const Ref<InputEvent> &p_event); |
273 | |
274 | void _open_select_another_theme(); |
275 | void _select_another_theme_cbk(const String &p_path); |
276 | |
277 | protected: |
278 | void _notification(int p_what); |
279 | static void _bind_methods(); |
280 | |
281 | public: |
282 | void set_edited_theme(const Ref<Theme> &p_theme); |
283 | |
284 | ThemeItemEditorDialog(ThemeTypeEditor *p_theme_editor); |
285 | }; |
286 | |
287 | class ThemeTypeDialog : public ConfirmationDialog { |
288 | GDCLASS(ThemeTypeDialog, ConfirmationDialog); |
289 | |
290 | Ref<Theme> edited_theme; |
291 | bool include_own_types = false; |
292 | |
293 | String pre_submitted_value; |
294 | |
295 | LineEdit *add_type_filter = nullptr; |
296 | ItemList *add_type_options = nullptr; |
297 | ConfirmationDialog *add_type_confirmation = nullptr; |
298 | |
299 | void _dialog_about_to_show(); |
300 | void ok_pressed() override; |
301 | |
302 | void _update_add_type_options(const String &p_filter = "" ); |
303 | |
304 | void _add_type_filter_cbk(const String &p_value); |
305 | void _add_type_options_cbk(int p_index); |
306 | void _add_type_dialog_entered(const String &p_value); |
307 | void _add_type_dialog_activated(int p_index); |
308 | |
309 | void _add_type_selected(const String &p_type_name); |
310 | void _add_type_confirmed(); |
311 | |
312 | protected: |
313 | void _notification(int p_what); |
314 | static void _bind_methods(); |
315 | |
316 | public: |
317 | void set_edited_theme(const Ref<Theme> &p_theme); |
318 | void set_include_own_types(bool p_enable); |
319 | |
320 | ThemeTypeDialog(); |
321 | }; |
322 | |
323 | class ThemeTypeEditor : public MarginContainer { |
324 | GDCLASS(ThemeTypeEditor, MarginContainer); |
325 | |
326 | Ref<Theme> edited_theme; |
327 | String edited_type; |
328 | bool updating = false; |
329 | |
330 | struct LeadingStylebox { |
331 | bool pinned = false; |
332 | StringName item_name; |
333 | Ref<StyleBox> stylebox; |
334 | Ref<StyleBox> ref_stylebox; |
335 | }; |
336 | |
337 | LeadingStylebox leading_stylebox; |
338 | |
339 | OptionButton *theme_type_list = nullptr; |
340 | Button *add_type_button = nullptr; |
341 | |
342 | CheckButton *show_default_items_button = nullptr; |
343 | |
344 | TabContainer *data_type_tabs = nullptr; |
345 | VBoxContainer *color_items_list = nullptr; |
346 | VBoxContainer *constant_items_list = nullptr; |
347 | VBoxContainer *font_items_list = nullptr; |
348 | VBoxContainer *font_size_items_list = nullptr; |
349 | VBoxContainer *icon_items_list = nullptr; |
350 | VBoxContainer *stylebox_items_list = nullptr; |
351 | |
352 | LineEdit *type_variation_edit = nullptr; |
353 | Button *type_variation_button = nullptr; |
354 | Label *type_variation_locked = nullptr; |
355 | |
356 | enum TypeDialogMode { |
357 | ADD_THEME_TYPE, |
358 | ADD_VARIATION_BASE, |
359 | }; |
360 | |
361 | TypeDialogMode add_type_mode = ADD_THEME_TYPE; |
362 | ThemeTypeDialog *add_type_dialog = nullptr; |
363 | |
364 | Vector<Control *> focusables; |
365 | Timer *update_debounce_timer = nullptr; |
366 | |
367 | VBoxContainer *_create_item_list(Theme::DataType p_data_type); |
368 | void _update_type_list(); |
369 | void _update_type_list_debounced(); |
370 | HashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); |
371 | HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable); |
372 | void _add_focusable(Control *p_control); |
373 | void _update_type_items(); |
374 | |
375 | void _list_type_selected(int p_index); |
376 | void _add_type_button_cbk(); |
377 | void _add_default_type_items(); |
378 | |
379 | void _item_add_cbk(int p_data_type, Control *p_control); |
380 | void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control); |
381 | void _item_override_cbk(int p_data_type, String p_item_name); |
382 | void _item_remove_cbk(int p_data_type, String p_item_name); |
383 | void _item_rename_cbk(int p_data_type, String p_item_name, Control *p_control); |
384 | void _item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control); |
385 | void _item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control); |
386 | void _item_rename_canceled(int p_data_type, String p_item_name, Control *p_control); |
387 | |
388 | void _color_item_changed(Color p_value, String p_item_name); |
389 | void _constant_item_changed(float p_value, String p_item_name); |
390 | void _font_size_item_changed(float p_value, String p_item_name); |
391 | void _edit_resource_item(Ref<Resource> p_resource, bool p_edit); |
392 | void _font_item_changed(Ref<Font> p_value, String p_item_name); |
393 | void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name); |
394 | void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name); |
395 | void _change_pinned_stylebox(); |
396 | void _on_pin_leader_button_pressed(Control *p_editor, String p_item_name); |
397 | void _pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox); |
398 | void _on_unpin_leader_button_pressed(); |
399 | void _unpin_leading_stylebox(); |
400 | void _update_stylebox_from_leading(); |
401 | |
402 | void _type_variation_changed(const String p_value); |
403 | void _add_type_variation_cbk(); |
404 | |
405 | void _add_type_dialog_selected(const String p_type_name); |
406 | |
407 | protected: |
408 | void _notification(int p_what); |
409 | static void _bind_methods(); |
410 | |
411 | public: |
412 | void set_edited_theme(const Ref<Theme> &p_theme); |
413 | void select_type(String p_type_name); |
414 | bool is_stylebox_pinned(Ref<StyleBox> p_stylebox); |
415 | |
416 | ThemeTypeEditor(); |
417 | }; |
418 | |
419 | class ThemeEditor : public VBoxContainer { |
420 | GDCLASS(ThemeEditor, VBoxContainer); |
421 | |
422 | Ref<Theme> theme; |
423 | |
424 | TabBar *preview_tabs = nullptr; |
425 | PanelContainer *preview_tabs_content = nullptr; |
426 | Button *add_preview_button = nullptr; |
427 | EditorFileDialog *preview_scene_dialog = nullptr; |
428 | |
429 | ThemeTypeEditor *theme_type_editor = nullptr; |
430 | |
431 | Label *theme_name = nullptr; |
432 | ThemeItemEditorDialog *theme_edit_dialog = nullptr; |
433 | |
434 | void _theme_save_button_cbk(bool p_save_as); |
435 | void _theme_edit_button_cbk(); |
436 | |
437 | void _add_preview_button_cbk(); |
438 | void _preview_scene_dialog_cbk(const String &p_path); |
439 | void _add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon); |
440 | void _change_preview_tab(int p_tab); |
441 | void _remove_preview_tab(int p_tab); |
442 | void _remove_preview_tab_invalid(Node *p_tab_control); |
443 | void _update_preview_tab(Node *p_tab_control); |
444 | void _preview_control_picked(String p_class_name); |
445 | |
446 | protected: |
447 | void _notification(int p_what); |
448 | |
449 | public: |
450 | void edit(const Ref<Theme> &p_theme); |
451 | Ref<Theme> get_edited_theme(); |
452 | |
453 | ThemeEditor(); |
454 | }; |
455 | |
456 | class ThemeEditorPlugin : public EditorPlugin { |
457 | GDCLASS(ThemeEditorPlugin, EditorPlugin); |
458 | |
459 | ThemeEditor *theme_editor = nullptr; |
460 | Button *button = nullptr; |
461 | |
462 | public: |
463 | virtual String get_name() const override { return "Theme" ; } |
464 | bool has_main_screen() const override { return false; } |
465 | virtual void edit(Object *p_node) override; |
466 | virtual bool handles(Object *p_node) const override; |
467 | virtual void make_visible(bool p_visible) override; |
468 | |
469 | ThemeEditorPlugin(); |
470 | }; |
471 | |
472 | #endif // THEME_EDITOR_PLUGIN_H |
473 | |