Path: blob/master/editor/scene/gui/theme_editor_preview.cpp
9902 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_ENTER_TREE: {204if (is_visible_in_tree()) {205set_process(true);206}207208connect(SceneStringName(visibility_changed), callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed));209} break;210211case NOTIFICATION_READY: {212Vector<Ref<Theme>> preview_themes;213preview_themes.push_back(ThemeDB::get_singleton()->get_default_theme());214ThemeDB::get_singleton()->create_theme_context(preview_root, preview_themes);215} break;216217case NOTIFICATION_THEME_CHANGED: {218picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick")));219220theme_cache.preview_picker_overlay = get_theme_stylebox(SNAME("preview_picker_overlay"), SNAME("ThemeEditor"));221theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor"));222theme_cache.preview_picker_label = get_theme_stylebox(SNAME("preview_picker_label"), SNAME("ThemeEditor"));223theme_cache.preview_picker_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));224theme_cache.font_size = get_theme_default_font_size();225} break;226227case NOTIFICATION_PROCESS: {228time_left -= get_process_delta_time();229if (time_left < 0) {230time_left = REFRESH_TIMER;231_refresh_interval();232}233} break;234}235}236237void ThemeEditorPreview::_bind_methods() {238ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name")));239}240241ThemeEditorPreview::ThemeEditorPreview() {242preview_toolbar = memnew(HBoxContainer);243add_child(preview_toolbar);244245picker_button = memnew(Button);246preview_toolbar->add_child(picker_button);247picker_button->set_theme_type_variation(SceneStringName(FlatButton));248picker_button->set_toggle_mode(true);249picker_button->set_tooltip_text(TTR("Toggle the control picker, allowing to visually select control types for edit."));250picker_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditorPreview::_picker_button_cbk));251252MarginContainer *preview_body = memnew(MarginContainer);253preview_body->set_custom_minimum_size(Size2(200, 0) * EDSCALE);254preview_body->set_v_size_flags(SIZE_EXPAND_FILL);255add_child(preview_body);256257preview_container = memnew(ScrollContainer);258preview_body->add_child(preview_container);259260preview_root = memnew(MarginContainer);261preview_container->add_child(preview_root);262preview_root->set_clip_contents(true);263preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE);264preview_root->set_v_size_flags(SIZE_EXPAND_FILL);265preview_root->set_h_size_flags(SIZE_EXPAND_FILL);266267preview_bg = memnew(ColorRect);268preview_bg->set_anchors_and_offsets_preset(PRESET_FULL_RECT);269preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));270preview_root->add_child(preview_bg);271272preview_content = memnew(MarginContainer);273preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE);274preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE);275preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE);276preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE);277preview_root->add_child(preview_content);278279preview_overlay = memnew(MarginContainer);280preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE);281preview_overlay->set_clip_contents(true);282preview_body->add_child(preview_overlay);283284picker_overlay = memnew(Control);285add_preview_overlay(picker_overlay);286picker_overlay->connect(SceneStringName(draw), callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay));287picker_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay));288picker_overlay->connect(SceneStringName(mouse_exited), callable_mp(this, &ThemeEditorPreview::_reset_picker_overlay));289}290291void DefaultThemeEditorPreview::_notification(int p_what) {292switch (p_what) {293case NOTIFICATION_THEME_CHANGED: {294test_color_picker_button->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), EditorStringName(Editor))));295} break;296}297}298299DefaultThemeEditorPreview::DefaultThemeEditorPreview() {300Panel *main_panel = memnew(Panel);301preview_content->add_child(main_panel);302303MarginContainer *main_mc = memnew(MarginContainer);304main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE);305main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE);306main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE);307main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE);308preview_content->add_child(main_mc);309310HBoxContainer *main_hb = memnew(HBoxContainer);311main_mc->add_child(main_hb);312main_hb->add_theme_constant_override("separation", 20 * EDSCALE);313314VBoxContainer *first_vb = memnew(VBoxContainer);315main_hb->add_child(first_vb);316first_vb->set_h_size_flags(SIZE_EXPAND_FILL);317first_vb->add_theme_constant_override("separation", 10 * EDSCALE);318319first_vb->add_child(memnew(Label("Label")));320321first_vb->add_child(memnew(Button("Button")));322Button *bt = memnew(Button);323bt->set_text(TTR("Toggle Button"));324bt->set_toggle_mode(true);325bt->set_pressed(true);326first_vb->add_child(bt);327bt = memnew(Button);328bt->set_text(TTR("Disabled Button"));329bt->set_disabled(true);330first_vb->add_child(bt);331Button *tb = memnew(Button);332tb->set_flat(true);333tb->set_text("Flat Button");334first_vb->add_child(tb);335336CheckButton *cb = memnew(CheckButton);337cb->set_text("CheckButton");338first_vb->add_child(cb);339CheckBox *cbx = memnew(CheckBox);340cbx->set_text("CheckBox");341first_vb->add_child(cbx);342343MenuButton *test_menu_button = memnew(MenuButton);344test_menu_button->set_text("MenuButton");345test_menu_button->get_popup()->add_item(TTR("Item"));346test_menu_button->get_popup()->add_item(TTR("Disabled Item"));347test_menu_button->get_popup()->set_item_disabled(1, true);348test_menu_button->get_popup()->add_separator();349test_menu_button->get_popup()->add_check_item(TTR("Check Item"));350test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));351test_menu_button->get_popup()->set_item_checked(4, true);352test_menu_button->get_popup()->add_separator();353test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));354test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));355test_menu_button->get_popup()->set_item_checked(7, true);356test_menu_button->get_popup()->add_separator(TTR("Named Separator"));357358PopupMenu *test_submenu = memnew(PopupMenu);359test_menu_button->get_popup()->add_submenu_node_item(TTR("Submenu"), test_submenu);360test_submenu->add_item(TTR("Subitem 1"));361test_submenu->add_item(TTR("Subitem 2"));362first_vb->add_child(test_menu_button);363364OptionButton *test_option_button = memnew(OptionButton);365test_option_button->add_item("OptionButton");366test_option_button->add_separator();367test_option_button->add_item(TTR("Has"));368test_option_button->add_item(TTR("Many"));369test_option_button->add_item(TTR("Options"));370first_vb->add_child(test_option_button);371test_color_picker_button = memnew(ColorPickerButton);372first_vb->add_child(test_color_picker_button);373374VBoxContainer *second_vb = memnew(VBoxContainer);375second_vb->set_h_size_flags(SIZE_EXPAND_FILL);376main_hb->add_child(second_vb);377second_vb->add_theme_constant_override("separation", 10 * EDSCALE);378LineEdit *le = memnew(LineEdit);379le->set_text("LineEdit");380second_vb->add_child(le);381le = memnew(LineEdit);382le->set_text(TTR("Disabled LineEdit"));383le->set_editable(false);384second_vb->add_child(le);385TextEdit *te = memnew(TextEdit);386te->set_text("TextEdit");387te->set_custom_minimum_size(Size2(0, 100) * EDSCALE);388second_vb->add_child(te);389second_vb->add_child(memnew(SpinBox));390391HBoxContainer *vhb = memnew(HBoxContainer);392second_vb->add_child(vhb);393vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE);394vhb->add_child(memnew(VSlider));395VScrollBar *vsb = memnew(VScrollBar);396vsb->set_page(25);397vhb->add_child(vsb);398vhb->add_child(memnew(VSeparator));399VBoxContainer *hvb = memnew(VBoxContainer);400vhb->add_child(hvb);401hvb->set_alignment(BoxContainer::ALIGNMENT_CENTER);402hvb->set_h_size_flags(SIZE_EXPAND_FILL);403hvb->add_child(memnew(HSlider));404HScrollBar *hsb = memnew(HScrollBar);405hsb->set_page(25);406hvb->add_child(hsb);407HSlider *hs = memnew(HSlider);408hs->set_editable(false);409hvb->add_child(hs);410hvb->add_child(memnew(HSeparator));411ProgressBar *pb = memnew(ProgressBar);412pb->set_value(50);413hvb->add_child(pb);414415VBoxContainer *third_vb = memnew(VBoxContainer);416third_vb->set_h_size_flags(SIZE_EXPAND_FILL);417third_vb->add_theme_constant_override("separation", 10 * EDSCALE);418main_hb->add_child(third_vb);419420TabContainer *tc = memnew(TabContainer);421third_vb->add_child(tc);422tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE);423Control *tcc = memnew(Control);424tcc->set_name(TTR("Tab 1"));425tc->add_child(tcc);426tcc = memnew(Control);427tcc->set_name(TTR("Tab 2"));428tc->add_child(tcc);429tcc = memnew(Control);430tcc->set_name(TTR("Tab 3"));431tc->add_child(tcc);432tc->set_tab_disabled(2, true);433434Tree *test_tree = memnew(Tree);435third_vb->add_child(test_tree);436test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE);437438TreeItem *item = test_tree->create_item();439item->set_text(0, "Tree");440item = test_tree->create_item(test_tree->get_root());441item->set_text(0, "Item");442item = test_tree->create_item(test_tree->get_root());443item->set_editable(0, true);444item->set_text(0, TTR("Editable Item"));445TreeItem *sub_tree = test_tree->create_item(test_tree->get_root());446sub_tree->set_text(0, TTR("Subtree"));447item = test_tree->create_item(sub_tree);448item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);449item->set_editable(0, true);450item->set_text(0, "Check Item");451item = test_tree->create_item(sub_tree);452item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);453item->set_editable(0, true);454item->set_range_config(0, 0, 20, 0.1);455item->set_range(0, 2);456item = test_tree->create_item(sub_tree);457item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);458item->set_editable(0, true);459item->set_text(0, TTR("Has,Many,Options"));460item->set_range(0, 2);461}462463void SceneThemeEditorPreview::_reload_scene() {464if (loaded_scene.is_null()) {465return;466}467468if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) {469EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed."));470emit_signal(SNAME("scene_invalidated"));471return;472}473474for (int i = preview_content->get_child_count() - 1; i >= 0; i--) {475Node *node = preview_content->get_child(i);476node->queue_free();477preview_content->remove_child(node);478}479480Node *instance = loaded_scene->instantiate();481if (!instance || !Object::cast_to<Control>(instance)) {482EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));483emit_signal(SNAME("scene_invalidated"));484return;485}486487preview_content->add_child(instance);488emit_signal(SNAME("scene_reloaded"));489}490491void SceneThemeEditorPreview::_notification(int p_what) {492switch (p_what) {493case NOTIFICATION_THEME_CHANGED: {494reload_scene_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));495} break;496}497}498499void SceneThemeEditorPreview::_bind_methods() {500ADD_SIGNAL(MethodInfo("scene_invalidated"));501ADD_SIGNAL(MethodInfo("scene_reloaded"));502}503504bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) {505loaded_scene = ResourceLoader::load(p_path);506if (loaded_scene.is_null()) {507EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource."));508return false;509}510511Node *instance = loaded_scene->instantiate();512513if (!instance) {514EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, could not instantiate it."));515return false;516}517518if (!Object::cast_to<Control>(instance)) {519EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));520memdelete(instance);521return false;522}523524preview_content->add_child(instance);525return true;526}527528String SceneThemeEditorPreview::get_preview_scene_path() const {529if (loaded_scene.is_null()) {530return "";531}532533return loaded_scene->get_path();534}535536SceneThemeEditorPreview::SceneThemeEditorPreview() {537preview_toolbar->add_child(memnew(VSeparator));538539reload_scene_button = memnew(Button);540reload_scene_button->set_flat(true);541reload_scene_button->set_tooltip_text(TTR("Reload the scene to reflect its most actual state."));542preview_toolbar->add_child(reload_scene_button);543reload_scene_button->connect(SceneStringName(pressed), callable_mp(this, &SceneThemeEditorPreview::_reload_scene));544}545546547