Path: blob/master/editor/scene/gui/theme_editor_preview.cpp
20987 views
/**************************************************************************/1/* theme_editor_preview.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "theme_editor_preview.h"3132#include "core/config/project_settings.h"33#include "editor/editor_node.h"34#include "editor/editor_string_names.h"35#include "editor/themes/editor_scale.h"36#include "scene/gui/button.h"37#include "scene/gui/check_box.h"38#include "scene/gui/check_button.h"39#include "scene/gui/color_picker.h"40#include "scene/gui/color_rect.h"41#include "scene/gui/label.h"42#include "scene/gui/margin_container.h"43#include "scene/gui/menu_button.h"44#include "scene/gui/option_button.h"45#include "scene/gui/panel.h"46#include "scene/gui/progress_bar.h"47#include "scene/gui/scroll_container.h"48#include "scene/gui/separator.h"49#include "scene/gui/slider.h"50#include "scene/gui/spin_box.h"51#include "scene/gui/tab_container.h"52#include "scene/gui/text_edit.h"53#include "scene/gui/tree.h"54#include "scene/resources/packed_scene.h"55#include "scene/theme/theme_db.h"5657constexpr double REFRESH_TIMER = 1.5;5859void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) {60preview_content->set_theme(p_theme);61}6263void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) {64preview_overlay->add_child(p_overlay);65p_overlay->hide();66}6768void ThemeEditorPreview::_propagate_redraw(Control *p_at) {69p_at->notification(NOTIFICATION_THEME_CHANGED);70p_at->update_minimum_size();71p_at->queue_redraw();72for (int i = 0; i < p_at->get_child_count(); i++) {73Control *a = Object::cast_to<Control>(p_at->get_child(i));74if (a) {75_propagate_redraw(a);76}77}78}7980void ThemeEditorPreview::_refresh_interval() {81// In case the project settings have changed.82preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));8384_propagate_redraw(preview_bg);85_propagate_redraw(preview_content);86}8788void ThemeEditorPreview::_preview_visibility_changed() {89set_process(is_visible_in_tree());90}9192void ThemeEditorPreview::_picker_button_cbk() {93picker_overlay->set_visible(picker_button->is_pressed());94if (picker_button->is_pressed()) {95_reset_picker_overlay();96}97}9899Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) {100Control *found = nullptr;101102for (int i = p_parent->get_child_count() - 1; i >= 0; i--) {103Control *cc = Object::cast_to<Control>(p_parent->get_child(i));104if (!cc || !cc->is_visible()) {105continue;106}107108Rect2 crect = cc->get_rect();109if (crect.has_point(p_mouse_position)) {110// Check if there is a child control under mouse.111if (cc->get_child_count() > 0) {112found = _find_hovered_control(cc, p_mouse_position - cc->get_position());113}114115// If there are no applicable children, use the control itself.116if (!found) {117found = cc;118}119break;120}121}122123return found;124}125126void ThemeEditorPreview::_draw_picker_overlay() {127if (!picker_button->is_pressed()) {128return;129}130131picker_overlay->draw_rect(Rect2(Vector2(0.0, 0.0), picker_overlay->get_size()), theme_cache.preview_picker_overlay_color);132if (hovered_control) {133Rect2 highlight_rect = hovered_control->get_global_rect();134highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position);135picker_overlay->draw_style_box(theme_cache.preview_picker_overlay, highlight_rect);136137String highlight_name = hovered_control->get_theme_type_variation();138if (highlight_name == StringName()) {139highlight_name = hovered_control->get_class_name();140}141142Rect2 highlight_label_rect = highlight_rect;143highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size);144145int margin_top = theme_cache.preview_picker_label->get_margin(SIDE_TOP);146int margin_left = theme_cache.preview_picker_label->get_margin(SIDE_LEFT);147int margin_bottom = theme_cache.preview_picker_label->get_margin(SIDE_BOTTOM);148int margin_right = theme_cache.preview_picker_label->get_margin(SIDE_RIGHT);149highlight_label_rect.size.x += margin_left + margin_right;150highlight_label_rect.size.y += margin_top + margin_bottom;151152highlight_label_rect.position = highlight_label_rect.position.clamp(Vector2(), picker_overlay->get_size());153154picker_overlay->draw_style_box(theme_cache.preview_picker_label, highlight_label_rect);155156Point2 label_pos = highlight_label_rect.position;157label_pos.y += highlight_label_rect.size.y - margin_bottom;158label_pos.x += margin_left;159picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size);160}161}162163void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_event) {164if (!picker_button->is_pressed()) {165return;166}167168Ref<InputEventMouseButton> mb = p_event;169170if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {171if (hovered_control) {172StringName theme_type = hovered_control->get_theme_type_variation();173if (theme_type == StringName()) {174theme_type = hovered_control->get_class_name();175}176177emit_signal(SNAME("control_picked"), theme_type);178picker_button->set_pressed(false);179picker_overlay->set_visible(false);180return;181}182}183184Ref<InputEventMouseMotion> mm = p_event;185186if (mm.is_valid()) {187Vector2 mp = preview_content->get_local_mouse_position();188hovered_control = _find_hovered_control(preview_content, mp);189picker_overlay->queue_redraw();190}191192// Forward input to the scroll container underneath to allow scrolling.193preview_container->gui_input(p_event);194}195196void ThemeEditorPreview::_reset_picker_overlay() {197hovered_control = nullptr;198picker_overlay->queue_redraw();199}200201void ThemeEditorPreview::_notification(int p_what) {202switch (p_what) {203case NOTIFICATION_POSTINITIALIZE: {204connect(SceneStringName(visibility_changed), callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed));205} break;206207case NOTIFICATION_ENTER_TREE: {208if (is_visible_in_tree()) {209set_process(true);210}211} break;212213case NOTIFICATION_READY: {214Vector<Ref<Theme>> preview_themes;215preview_themes.push_back(ThemeDB::get_singleton()->get_default_theme());216ThemeDB::get_singleton()->create_theme_context(preview_root, preview_themes);217} break;218219case NOTIFICATION_THEME_CHANGED: {220picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick")));221222theme_cache.preview_picker_overlay = get_theme_stylebox(SNAME("preview_picker_overlay"), SNAME("ThemeEditor"));223theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor"));224theme_cache.preview_picker_label = get_theme_stylebox(SNAME("preview_picker_label"), SNAME("ThemeEditor"));225theme_cache.preview_picker_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));226theme_cache.font_size = get_theme_default_font_size();227} break;228229case NOTIFICATION_PROCESS: {230time_left -= get_process_delta_time();231if (time_left < 0) {232time_left = REFRESH_TIMER;233_refresh_interval();234}235} break;236}237}238239void ThemeEditorPreview::_bind_methods() {240ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name")));241}242243ThemeEditorPreview::ThemeEditorPreview() {244preview_toolbar = memnew(HBoxContainer);245add_child(preview_toolbar);246247picker_button = memnew(Button);248preview_toolbar->add_child(picker_button);249picker_button->set_theme_type_variation(SceneStringName(FlatButton));250picker_button->set_toggle_mode(true);251picker_button->set_tooltip_text(TTR("Toggle the control picker, allowing to visually select control types for edit."));252picker_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditorPreview::_picker_button_cbk));253254MarginContainer *preview_body = memnew(MarginContainer);255preview_body->set_v_size_flags(SIZE_EXPAND_FILL);256add_child(preview_body);257258preview_container = memnew(ScrollContainer);259preview_body->add_child(preview_container);260261preview_root = memnew(MarginContainer);262preview_container->add_child(preview_root);263preview_root->set_clip_contents(true);264preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE);265preview_root->set_v_size_flags(SIZE_EXPAND_FILL);266preview_root->set_h_size_flags(SIZE_EXPAND_FILL);267268preview_bg = memnew(ColorRect);269preview_bg->set_anchors_and_offsets_preset(PRESET_FULL_RECT);270preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));271preview_root->add_child(preview_bg);272273preview_content = memnew(MarginContainer);274preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE);275preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE);276preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE);277preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE);278preview_root->add_child(preview_content);279280preview_overlay = memnew(MarginContainer);281preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE);282preview_overlay->set_clip_contents(true);283preview_body->add_child(preview_overlay);284285picker_overlay = memnew(Control);286add_preview_overlay(picker_overlay);287picker_overlay->connect(SceneStringName(draw), callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay));288picker_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay));289picker_overlay->connect(SceneStringName(mouse_exited), callable_mp(this, &ThemeEditorPreview::_reset_picker_overlay));290}291292void DefaultThemeEditorPreview::_notification(int p_what) {293switch (p_what) {294case NOTIFICATION_THEME_CHANGED: {295test_color_picker_button->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("inspector_property_height"), EditorStringName(Editor))));296} break;297}298}299300DefaultThemeEditorPreview::DefaultThemeEditorPreview() {301Panel *main_panel = memnew(Panel);302preview_content->add_child(main_panel);303304MarginContainer *main_mc = memnew(MarginContainer);305main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE);306main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE);307main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE);308main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE);309preview_content->add_child(main_mc);310311HBoxContainer *main_hb = memnew(HBoxContainer);312main_mc->add_child(main_hb);313main_hb->add_theme_constant_override("separation", 20 * EDSCALE);314315VBoxContainer *first_vb = memnew(VBoxContainer);316main_hb->add_child(first_vb);317first_vb->set_h_size_flags(SIZE_EXPAND_FILL);318first_vb->add_theme_constant_override("separation", 10 * EDSCALE);319320first_vb->add_child(memnew(Label("Label")));321322first_vb->add_child(memnew(Button("Button")));323Button *bt = memnew(Button);324bt->set_text(TTR("Toggle Button"));325bt->set_toggle_mode(true);326bt->set_pressed(true);327first_vb->add_child(bt);328bt = memnew(Button);329bt->set_text(TTR("Disabled Button"));330bt->set_disabled(true);331first_vb->add_child(bt);332Button *tb = memnew(Button);333tb->set_flat(true);334tb->set_text("Flat Button");335first_vb->add_child(tb);336337CheckButton *cb = memnew(CheckButton);338cb->set_text("CheckButton");339first_vb->add_child(cb);340CheckBox *cbx = memnew(CheckBox);341cbx->set_text("CheckBox");342first_vb->add_child(cbx);343344MenuButton *test_menu_button = memnew(MenuButton);345test_menu_button->set_text("MenuButton");346test_menu_button->get_popup()->add_item(TTR("Item"));347test_menu_button->get_popup()->add_item(TTR("Disabled Item"));348test_menu_button->get_popup()->set_item_disabled(1, true);349test_menu_button->get_popup()->add_separator();350test_menu_button->get_popup()->add_check_item(TTR("Check Item"));351test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));352test_menu_button->get_popup()->set_item_checked(4, true);353test_menu_button->get_popup()->add_separator();354test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));355test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));356test_menu_button->get_popup()->set_item_checked(7, true);357test_menu_button->get_popup()->add_separator(TTR("Named Separator"));358359PopupMenu *test_submenu = memnew(PopupMenu);360test_menu_button->get_popup()->add_submenu_node_item(TTR("Submenu"), test_submenu);361test_submenu->add_item(TTR("Subitem 1"));362test_submenu->add_item(TTR("Subitem 2"));363first_vb->add_child(test_menu_button);364365OptionButton *test_option_button = memnew(OptionButton);366test_option_button->add_item("OptionButton");367test_option_button->add_separator();368test_option_button->add_item(TTR("Has"));369test_option_button->add_item(TTR("Many"));370test_option_button->add_item(TTR("Options"));371first_vb->add_child(test_option_button);372test_color_picker_button = memnew(ColorPickerButton);373first_vb->add_child(test_color_picker_button);374375VBoxContainer *second_vb = memnew(VBoxContainer);376second_vb->set_h_size_flags(SIZE_EXPAND_FILL);377main_hb->add_child(second_vb);378second_vb->add_theme_constant_override("separation", 10 * EDSCALE);379LineEdit *le = memnew(LineEdit);380le->set_text("LineEdit");381second_vb->add_child(le);382le = memnew(LineEdit);383le->set_text(TTR("Disabled LineEdit"));384le->set_editable(false);385second_vb->add_child(le);386TextEdit *te = memnew(TextEdit);387te->set_text("TextEdit");388te->set_custom_minimum_size(Size2(0, 100) * EDSCALE);389second_vb->add_child(te);390second_vb->add_child(memnew(SpinBox));391392HBoxContainer *vhb = memnew(HBoxContainer);393second_vb->add_child(vhb);394vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE);395vhb->add_child(memnew(VSlider));396VScrollBar *vsb = memnew(VScrollBar);397vsb->set_page(25);398vhb->add_child(vsb);399vhb->add_child(memnew(VSeparator));400VBoxContainer *hvb = memnew(VBoxContainer);401vhb->add_child(hvb);402hvb->set_alignment(BoxContainer::ALIGNMENT_CENTER);403hvb->set_h_size_flags(SIZE_EXPAND_FILL);404hvb->add_child(memnew(HSlider));405HScrollBar *hsb = memnew(HScrollBar);406hsb->set_page(25);407hvb->add_child(hsb);408HSlider *hs = memnew(HSlider);409hs->set_editable(false);410hvb->add_child(hs);411hvb->add_child(memnew(HSeparator));412ProgressBar *pb = memnew(ProgressBar);413pb->set_value(50);414hvb->add_child(pb);415416VBoxContainer *third_vb = memnew(VBoxContainer);417third_vb->set_h_size_flags(SIZE_EXPAND_FILL);418third_vb->add_theme_constant_override("separation", 10 * EDSCALE);419main_hb->add_child(third_vb);420421TabContainer *tc = memnew(TabContainer);422third_vb->add_child(tc);423tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE);424Control *tcc = memnew(Control);425tcc->set_name(TTR("Tab 1"));426tc->add_child(tcc);427tcc = memnew(Control);428tcc->set_name(TTR("Tab 2"));429tc->add_child(tcc);430tcc = memnew(Control);431tcc->set_name(TTR("Tab 3"));432tc->add_child(tcc);433tc->set_tab_disabled(2, true);434435Tree *test_tree = memnew(Tree);436third_vb->add_child(test_tree);437test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE);438439TreeItem *item = test_tree->create_item();440item->set_text(0, "Tree");441item = test_tree->create_item(test_tree->get_root());442item->set_text(0, "Item");443item = test_tree->create_item(test_tree->get_root());444item->set_editable(0, true);445item->set_text(0, TTR("Editable Item"));446TreeItem *sub_tree = test_tree->create_item(test_tree->get_root());447sub_tree->set_text(0, TTR("Subtree"));448item = test_tree->create_item(sub_tree);449item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);450item->set_editable(0, true);451item->set_text(0, "Check Item");452item = test_tree->create_item(sub_tree);453item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);454item->set_editable(0, true);455item->set_range_config(0, 0, 20, 0.1);456item->set_range(0, 2);457item = test_tree->create_item(sub_tree);458item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);459item->set_editable(0, true);460item->set_text(0, TTR("Has,Many,Options"));461item->set_range(0, 2);462}463464void SceneThemeEditorPreview::_reload_scene() {465if (loaded_scene.is_null()) {466return;467}468469if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) {470EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed."));471emit_signal(SNAME("scene_invalidated"));472return;473}474475for (int i = preview_content->get_child_count() - 1; i >= 0; i--) {476Node *node = preview_content->get_child(i);477node->queue_free();478preview_content->remove_child(node);479}480481Node *instance = loaded_scene->instantiate();482if (!instance || !Object::cast_to<Control>(instance)) {483EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));484emit_signal(SNAME("scene_invalidated"));485return;486}487488preview_content->add_child(instance);489emit_signal(SNAME("scene_reloaded"));490}491492void SceneThemeEditorPreview::_notification(int p_what) {493switch (p_what) {494case NOTIFICATION_THEME_CHANGED: {495reload_scene_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));496} break;497}498}499500void SceneThemeEditorPreview::_bind_methods() {501ADD_SIGNAL(MethodInfo("scene_invalidated"));502ADD_SIGNAL(MethodInfo("scene_reloaded"));503}504505bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) {506loaded_scene = ResourceLoader::load(p_path);507if (loaded_scene.is_null()) {508EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource."));509return false;510}511512Node *instance = loaded_scene->instantiate();513514if (!instance) {515EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, could not instantiate it."));516return false;517}518519if (!Object::cast_to<Control>(instance)) {520EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));521memdelete(instance);522return false;523}524525preview_content->add_child(instance);526return true;527}528529String SceneThemeEditorPreview::get_preview_scene_path() const {530if (loaded_scene.is_null()) {531return "";532}533534return loaded_scene->get_path();535}536537SceneThemeEditorPreview::SceneThemeEditorPreview() {538preview_toolbar->add_child(memnew(VSeparator));539540reload_scene_button = memnew(Button);541reload_scene_button->set_flat(true);542reload_scene_button->set_tooltip_text(TTR("Reload the scene to reflect its most actual state."));543preview_toolbar->add_child(reload_scene_button);544reload_scene_button->connect(SceneStringName(pressed), callable_mp(this, &SceneThemeEditorPreview::_reload_scene));545}546547548