initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

6
editor/scene/gui/SCsub Normal file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env.add_source_files(env.editor_sources, "*.cpp")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,257 @@
/**************************************************************************/
/* control_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_inspector.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/margin_container.h"
class CheckBox;
class CheckButton;
class EditorSelection;
class GridContainer;
class Label;
class OptionButton;
class PanelContainer;
class PopupPanel;
class Separator;
class TextureRect;
// Inspector controls.
class ControlPositioningWarning : public MarginContainer {
GDCLASS(ControlPositioningWarning, MarginContainer);
Control *control_node = nullptr;
PanelContainer *bg_panel = nullptr;
GridContainer *grid = nullptr;
TextureRect *title_icon = nullptr;
TextureRect *hint_icon = nullptr;
Label *title_label = nullptr;
Label *hint_label = nullptr;
Control *hint_filler_left = nullptr;
Control *hint_filler_right = nullptr;
void _update_warning();
void _update_toggler();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
protected:
void _notification(int p_notification);
public:
void set_control(Control *p_node);
ControlPositioningWarning();
};
class EditorPropertyAnchorsPreset : public EditorProperty {
GDCLASS(EditorPropertyAnchorsPreset, EditorProperty);
OptionButton *options = nullptr;
void _option_selected(int p_which);
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
public:
void setup(const Vector<String> &p_options);
virtual void update_property() override;
EditorPropertyAnchorsPreset();
};
class EditorPropertySizeFlags : public EditorProperty {
GDCLASS(EditorPropertySizeFlags, EditorProperty);
enum FlagPreset {
SIZE_FLAGS_PRESET_FILL,
SIZE_FLAGS_PRESET_SHRINK_BEGIN,
SIZE_FLAGS_PRESET_SHRINK_CENTER,
SIZE_FLAGS_PRESET_SHRINK_END,
SIZE_FLAGS_PRESET_CUSTOM,
};
OptionButton *flag_presets = nullptr;
CheckBox *flag_expand = nullptr;
VBoxContainer *flag_options = nullptr;
Vector<CheckBox *> flag_checks;
bool vertical = false;
bool keep_selected_preset = false;
void _preset_selected(int p_which);
void _expand_toggled();
void _flag_toggled();
protected:
virtual void _set_read_only(bool p_read_only) override;
public:
void setup(const Vector<String> &p_options, bool p_vertical);
virtual void update_property() override;
EditorPropertySizeFlags();
};
class EditorInspectorPluginControl : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginControl, EditorInspectorPlugin);
bool inside_control_category = false;
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_category(Object *p_object, const String &p_category) override;
virtual void parse_group(Object *p_object, const String &p_group) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
// Toolbar controls.
class ControlEditorPopupButton : public Button {
GDCLASS(ControlEditorPopupButton, Button);
Ref<Texture2D> arrow_icon;
PopupPanel *popup_panel = nullptr;
VBoxContainer *popup_vbox = nullptr;
void _popup_visibility_changed(bool p_visible);
protected:
void _notification(int p_what);
public:
virtual Size2 get_minimum_size() const override;
virtual void toggled(bool p_pressed) override;
VBoxContainer *get_popup_hbox() const { return popup_vbox; }
ControlEditorPopupButton();
};
class ControlEditorPresetPicker : public MarginContainer {
GDCLASS(ControlEditorPresetPicker, MarginContainer);
virtual void _preset_button_pressed(const int p_preset) {}
protected:
static constexpr int grid_separation = 0;
HashMap<int, Button *> preset_buttons;
void _add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name);
void _add_separator(BoxContainer *p_box, Separator *p_separator);
};
class AnchorPresetPicker : public ControlEditorPresetPicker {
GDCLASS(AnchorPresetPicker, ControlEditorPresetPicker);
virtual void _preset_button_pressed(const int p_preset) override;
protected:
void _notification(int p_notification);
static void _bind_methods();
public:
AnchorPresetPicker();
};
class SizeFlagPresetPicker : public ControlEditorPresetPicker {
GDCLASS(SizeFlagPresetPicker, ControlEditorPresetPicker);
CheckButton *expand_button = nullptr;
bool vertical = false;
virtual void _preset_button_pressed(const int p_preset) override;
void _expand_button_pressed();
protected:
void _notification(int p_notification);
static void _bind_methods();
public:
void set_allowed_flags(Vector<SizeFlags> &p_flags);
void set_expand_flag(bool p_expand);
SizeFlagPresetPicker(bool p_vertical);
};
class ControlEditorToolbar : public HBoxContainer {
GDCLASS(ControlEditorToolbar, HBoxContainer);
EditorSelection *editor_selection = nullptr;
ControlEditorPopupButton *anchors_button = nullptr;
ControlEditorPopupButton *containers_button = nullptr;
Button *anchor_mode_button = nullptr;
SizeFlagPresetPicker *container_h_picker = nullptr;
SizeFlagPresetPicker *container_v_picker = nullptr;
bool anchors_mode = false;
void _anchors_preset_selected(int p_preset);
void _anchors_to_current_ratio();
void _anchor_mode_toggled(bool p_status);
void _container_flags_selected(int p_flags, bool p_vertical);
void _expand_flag_toggled(bool p_expand, bool p_vertical);
Vector2 _position_to_anchor(const Control *p_control, Vector2 position);
bool _is_node_locked(const Node *p_node);
List<Control *> _get_edited_controls();
void _selection_changed();
protected:
void _notification(int p_notification);
static ControlEditorToolbar *singleton;
public:
bool is_anchors_mode_enabled() { return anchors_mode; }
static ControlEditorToolbar *get_singleton() { return singleton; }
ControlEditorToolbar();
};
// Editor plugin.
class ControlEditorPlugin : public EditorPlugin {
GDCLASS(ControlEditorPlugin, EditorPlugin);
ControlEditorToolbar *toolbar = nullptr;
public:
virtual String get_plugin_name() const override { return "Control"; }
ControlEditorPlugin();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,268 @@
/**************************************************************************/
/* font_config_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_properties.h"
#include "editor/inspector/editor_properties_array_dict.h"
#include "editor/plugins/editor_plugin.h"
/*************************************************************************/
class EditorPropertyFontMetaObject : public RefCounted {
GDCLASS(EditorPropertyFontMetaObject, RefCounted);
Dictionary dict;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
public:
void set_dict(const Dictionary &p_dict);
Dictionary get_dict();
};
/*************************************************************************/
class EditorPropertyFontOTObject : public RefCounted {
GDCLASS(EditorPropertyFontOTObject, RefCounted);
Dictionary dict;
Dictionary defaults_dict;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
public:
void set_dict(const Dictionary &p_dict);
Dictionary get_dict();
void set_defaults(const Dictionary &p_dict);
Dictionary get_defaults();
};
/*************************************************************************/
class EditorPropertyFontMetaOverride : public EditorProperty {
GDCLASS(EditorPropertyFontMetaOverride, EditorProperty);
Ref<EditorPropertyFontMetaObject> object;
MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
Button *edit = nullptr;
PopupMenu *menu = nullptr;
EditorLocaleDialog *locale_select = nullptr;
Vector<String> script_codes;
bool script_editor = false;
bool updating = false;
int page_length = 20;
int page_index = 0;
EditorPaginator *paginator = nullptr;
protected:
void _edit_pressed();
void _page_changed(int p_page);
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
void _remove(Object *p_button, const String &p_key);
void _add_menu();
void _add_script(int p_option);
void _add_lang(const String &p_locale);
void _object_id_selected(const StringName &p_property, ObjectID p_id);
public:
virtual void update_property() override;
EditorPropertyFontMetaOverride(bool p_script);
};
/*************************************************************************/
class EditorPropertyOTVariation : public EditorProperty {
GDCLASS(EditorPropertyOTVariation, EditorProperty);
Ref<EditorPropertyFontOTObject> object;
MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
Button *edit = nullptr;
bool updating = false;
int page_length = 20;
int page_index = 0;
EditorPaginator *paginator = nullptr;
protected:
static void _bind_methods() {}
void _edit_pressed();
void _page_changed(int p_page);
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
void _object_id_selected(const StringName &p_property, ObjectID p_id);
public:
virtual void update_property() override;
EditorPropertyOTVariation();
};
/*************************************************************************/
class EditorPropertyOTFeatures : public EditorProperty {
GDCLASS(EditorPropertyOTFeatures, EditorProperty);
enum FeatureGroups {
FGRP_STYLISTIC_SET,
FGRP_CHARACTER_VARIANT,
FGRP_CAPITLS,
FGRP_LIGATURES,
FGRP_ALTERNATES,
FGRP_EAL,
FGRP_EAW,
FGRP_NUMAL,
FGRP_CUSTOM,
FGRP_MAX,
};
Ref<EditorPropertyFontOTObject> object;
MarginContainer *container = nullptr;
VBoxContainer *property_vbox = nullptr;
Button *edit = nullptr;
PopupMenu *menu = nullptr;
PopupMenu *menu_sub[FGRP_MAX];
String group_names[FGRP_MAX];
bool updating = false;
int page_length = 20;
int page_index = 0;
EditorPaginator *paginator = nullptr;
protected:
void _edit_pressed();
void _page_changed(int p_page);
void _property_changed(const String &p_property, const Variant &p_value, const String &p_name = "", bool p_changing = false);
void _remove(Object *p_button, int p_key);
void _add_menu();
void _add_feature(int p_option);
void _object_id_selected(const StringName &p_property, ObjectID p_id);
public:
virtual void update_property() override;
EditorPropertyOTFeatures();
};
/*************************************************************************/
class EditorInspectorPluginFontVariation : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginFontVariation, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
/*************************************************************************/
class FontPreview : public Control {
GDCLASS(FontPreview, Control);
protected:
void _notification(int p_what);
static void _bind_methods();
Ref<Font> prev_font;
void _preview_changed();
public:
virtual Size2 get_minimum_size() const override;
void set_data(const Ref<Font> &p_f);
};
/*************************************************************************/
class EditorInspectorPluginFontPreview : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginFontPreview, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
/*************************************************************************/
class EditorPropertyFontNamesArray : public EditorPropertyArray {
GDCLASS(EditorPropertyFontNamesArray, EditorPropertyArray);
PopupMenu *menu = nullptr;
protected:
virtual void _add_element() override;
void _add_font(int p_option);
static void _bind_methods() {}
public:
EditorPropertyFontNamesArray();
};
/*************************************************************************/
class EditorInspectorPluginSystemFont : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginSystemFont, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
};
/*************************************************************************/
class FontEditorPlugin : public EditorPlugin {
GDCLASS(FontEditorPlugin, EditorPlugin);
public:
FontEditorPlugin();
virtual String get_plugin_name() const override { return "Font"; }
};

View File

@@ -0,0 +1,138 @@
/**************************************************************************/
/* style_box_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "style_box_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/resources/style_box_texture.h"
bool StyleBoxPreview::grid_preview_enabled = true;
void StyleBoxPreview::_grid_preview_toggled(bool p_active) {
grid_preview_enabled = p_active;
queue_redraw();
}
void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) {
if (stylebox.is_valid()) {
stylebox->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
stylebox = p_stylebox;
if (stylebox.is_valid()) {
stylebox->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
}
Ref<StyleBoxTexture> sbt = stylebox;
grid_preview->set_visible(sbt.is_valid());
queue_redraw();
}
void StyleBoxPreview::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
set_texture(get_editor_theme_icon(SNAME("Checkerboard")));
grid_preview->set_button_icon(get_editor_theme_icon(SNAME("StyleBoxGrid")));
} break;
case NOTIFICATION_DRAW: {
_redraw();
} break;
}
}
void StyleBoxPreview::_redraw() {
if (stylebox.is_valid()) {
float grid_button_width = get_editor_theme_icon(SNAME("StyleBoxGrid"))->get_size().x;
Rect2 preview_rect = get_rect();
preview_rect = preview_rect.grow(-grid_button_width);
// Re-adjust preview panel to fit all drawn content.
Rect2 drawing_rect = stylebox->get_draw_rect(preview_rect);
preview_rect.size -= drawing_rect.size - preview_rect.size;
preview_rect.position -= drawing_rect.position - preview_rect.position;
draw_style_box(stylebox, preview_rect);
Ref<StyleBoxTexture> sbt = stylebox;
// Draw the "grid". Use white lines, as well as subtle black lines to ensure contrast.
if (sbt.is_valid() && grid_preview->is_pressed()) {
const Color dark_color = Color(0, 0, 0, 0.4);
const Color bright_color = Color(1, 1, 1, 0.8);
int x_left = drawing_rect.position.x + sbt->get_margin(SIDE_LEFT);
int x_right = drawing_rect.position.x + drawing_rect.size.width - sbt->get_margin(SIDE_RIGHT);
int y_top = drawing_rect.position.y + sbt->get_margin(SIDE_TOP);
int y_bottom = drawing_rect.position.y + drawing_rect.size.height - sbt->get_margin(SIDE_BOTTOM);
draw_line(Point2(x_left + 2, 0), Point2(x_left + 2, get_size().height), dark_color);
draw_line(Point2(x_right + 1, 0), Point2(x_right + 1, get_size().height), dark_color);
draw_line(Point2(0, y_top + 2), Point2(get_size().width, y_top + 2), dark_color);
draw_line(Point2(0, y_bottom + 1), Point2(get_size().width, y_bottom + 1), dark_color);
draw_line(Point2(x_left + 1, 0), Point2(x_left + 1, get_size().height), bright_color);
draw_line(Point2(x_right, 0), Point2(x_right, get_size().height), bright_color);
draw_line(Point2(0, y_top + 1), Point2(get_size().width, y_top + 1), bright_color);
draw_line(Point2(0, y_bottom), Point2(get_size().width, y_bottom), bright_color);
}
}
}
StyleBoxPreview::StyleBoxPreview() {
set_clip_contents(true);
set_custom_minimum_size(Size2(0, 150) * EDSCALE);
set_stretch_mode(TextureRect::STRETCH_TILE);
set_texture_repeat(CanvasItem::TEXTURE_REPEAT_ENABLED);
set_anchors_and_offsets_preset(PRESET_FULL_RECT);
grid_preview = memnew(Button);
// This theme variation works better than the normal theme because there's no focus highlight.
grid_preview->set_theme_type_variation("PreviewLightButton");
grid_preview->set_tooltip_text(TTRC("Toggle margins preview grid."));
grid_preview->set_toggle_mode(true);
grid_preview->connect(SceneStringName(toggled), callable_mp(this, &StyleBoxPreview::_grid_preview_toggled));
grid_preview->set_pressed(grid_preview_enabled);
add_child(grid_preview);
}
bool EditorInspectorPluginStyleBox::can_handle(Object *p_object) {
return Object::cast_to<StyleBox>(p_object) != nullptr;
}
void EditorInspectorPluginStyleBox::parse_begin(Object *p_object) {
Ref<StyleBox> sb = Ref<StyleBox>(Object::cast_to<StyleBox>(p_object));
StyleBoxPreview *preview = memnew(StyleBoxPreview);
preview->edit(sb);
add_custom_control(preview);
}
StyleBoxEditorPlugin::StyleBoxEditorPlugin() {
Ref<EditorInspectorPluginStyleBox> inspector_plugin;
inspector_plugin.instantiate();
add_inspector_plugin(inspector_plugin);
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************/
/* style_box_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/inspector/editor_inspector.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/texture_rect.h"
class Button;
class StyleBox;
class StyleBoxPreview : public TextureRect {
GDCLASS(StyleBoxPreview, TextureRect);
Button *grid_preview = nullptr;
Ref<StyleBox> stylebox;
void _sb_changed();
void _redraw();
static bool grid_preview_enabled;
void _grid_preview_toggled(bool p_active);
protected:
void _notification(int p_what);
public:
void edit(const Ref<StyleBox> &p_stylebox);
StyleBoxPreview();
};
class EditorInspectorPluginStyleBox : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginStyleBox, EditorInspectorPlugin);
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
};
class StyleBoxEditorPlugin : public EditorPlugin {
GDCLASS(StyleBoxEditorPlugin, EditorPlugin);
public:
virtual String get_plugin_name() const override { return "StyleBox"; }
StyleBoxEditorPlugin();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,498 @@
/**************************************************************************/
/* theme_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "editor/scene/gui/theme_editor_preview.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/tree.h"
#include "scene/resources/theme.h"
class Button;
class CheckButton;
class EditorFileDialog;
class ItemList;
class Label;
class LineEdit;
class OptionButton;
class PanelContainer;
class TabBar;
class TabContainer;
class ThemeEditorPlugin;
class TextureRect;
class ThemeItemImportTree : public VBoxContainer {
GDCLASS(ThemeItemImportTree, VBoxContainer);
Ref<Theme> edited_theme;
Ref<Theme> base_theme;
struct ThemeItem {
String type_name;
Theme::DataType data_type;
String item_name;
bool operator<(const ThemeItem &p_item) const {
if (type_name == p_item.type_name && data_type == p_item.data_type) {
return item_name < p_item.item_name;
}
if (type_name == p_item.type_name) {
return data_type < p_item.data_type;
}
return type_name < p_item.type_name;
}
};
enum ItemCheckedState {
SELECT_IMPORT_DEFINITION,
SELECT_IMPORT_FULL,
};
RBMap<ThemeItem, ItemCheckedState> selected_items;
LineEdit *import_items_filter = nullptr;
Tree *import_items_tree = nullptr;
List<TreeItem *> tree_color_items;
List<TreeItem *> tree_constant_items;
List<TreeItem *> tree_font_items;
List<TreeItem *> tree_font_size_items;
List<TreeItem *> tree_icon_items;
List<TreeItem *> tree_stylebox_items;
bool updating_tree = false;
enum ItemActionFlag {
IMPORT_ITEM = 1,
IMPORT_ITEM_DATA = 2,
};
TextureRect *select_colors_icon = nullptr;
Label *select_colors_label = nullptr;
Button *select_all_colors_button = nullptr;
Button *select_full_colors_button = nullptr;
Button *deselect_all_colors_button = nullptr;
Label *total_selected_colors_label = nullptr;
TextureRect *select_constants_icon = nullptr;
Label *select_constants_label = nullptr;
Button *select_all_constants_button = nullptr;
Button *select_full_constants_button = nullptr;
Button *deselect_all_constants_button = nullptr;
Label *total_selected_constants_label = nullptr;
TextureRect *select_fonts_icon = nullptr;
Label *select_fonts_label = nullptr;
Button *select_all_fonts_button = nullptr;
Button *select_full_fonts_button = nullptr;
Button *deselect_all_fonts_button = nullptr;
Label *total_selected_fonts_label = nullptr;
TextureRect *select_font_sizes_icon = nullptr;
Label *select_font_sizes_label = nullptr;
Button *select_all_font_sizes_button = nullptr;
Button *select_full_font_sizes_button = nullptr;
Button *deselect_all_font_sizes_button = nullptr;
Label *total_selected_font_sizes_label = nullptr;
TextureRect *select_icons_icon = nullptr;
Label *select_icons_label = nullptr;
Button *select_all_icons_button = nullptr;
Button *select_full_icons_button = nullptr;
Button *deselect_all_icons_button = nullptr;
Label *total_selected_icons_label = nullptr;
TextureRect *select_styleboxes_icon = nullptr;
Label *select_styleboxes_label = nullptr;
Button *select_all_styleboxes_button = nullptr;
Button *select_full_styleboxes_button = nullptr;
Button *deselect_all_styleboxes_button = nullptr;
Label *total_selected_styleboxes_label = nullptr;
HBoxContainer *select_icons_warning_hb = nullptr;
TextureRect *select_icons_warning_icon = nullptr;
Label *select_icons_warning = nullptr;
Button *import_collapse_types_button = nullptr;
Button *import_expand_types_button = nullptr;
Button *import_select_all_button = nullptr;
Button *import_select_full_button = nullptr;
Button *import_deselect_all_button = nullptr;
void _update_items_tree();
void _toggle_type_items(bool p_collapse);
void _filter_text_changed(const String &p_value);
void _store_selected_item(TreeItem *p_tree_item);
void _restore_selected_item(TreeItem *p_tree_item);
void _update_total_selected(Theme::DataType p_data_type);
void _tree_item_edited();
void _check_propagated_to_tree_item(Object *p_obj, int p_column);
void _select_all_subitems(TreeItem *p_root_item, bool p_select_with_data);
void _deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely);
void _select_all_items_pressed();
void _select_full_items_pressed();
void _deselect_all_items_pressed();
void _select_all_data_type_pressed(int p_data_type);
void _select_full_data_type_pressed(int p_data_type);
void _deselect_all_data_type_pressed(int p_data_type);
void _import_selected();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
void set_base_theme(const Ref<Theme> &p_theme);
void reset_item_tree();
bool has_selected_items() const;
ThemeItemImportTree();
};
class ThemeTypeEditor;
class ThemeItemEditorDialog : public AcceptDialog {
GDCLASS(ThemeItemEditorDialog, AcceptDialog);
ThemeTypeEditor *theme_type_editor = nullptr;
Ref<Theme> edited_theme;
TabContainer *tc = nullptr;
enum TypesTreeAction {
TYPES_TREE_REMOVE_ITEM,
};
Tree *edit_type_list = nullptr;
LineEdit *edit_add_type_value = nullptr;
Button *edit_add_type_button = nullptr;
String edited_item_type;
Button *edit_items_add_color = nullptr;
Button *edit_items_add_constant = nullptr;
Button *edit_items_add_font = nullptr;
Button *edit_items_add_font_size = nullptr;
Button *edit_items_add_icon = nullptr;
Button *edit_items_add_stylebox = nullptr;
Button *edit_items_remove_class = nullptr;
Button *edit_items_remove_custom = nullptr;
Button *edit_items_remove_all = nullptr;
Tree *edit_items_tree = nullptr;
Label *edit_items_message = nullptr;
enum ItemsTreeAction {
ITEMS_TREE_RENAME_ITEM,
ITEMS_TREE_REMOVE_ITEM,
ITEMS_TREE_REMOVE_DATA_TYPE,
};
ConfirmationDialog *edit_theme_item_dialog = nullptr;
VBoxContainer *edit_theme_item_old_vb = nullptr;
Label *theme_item_old_name = nullptr;
LineEdit *theme_item_name = nullptr;
enum ItemPopupMode {
CREATE_THEME_ITEM,
RENAME_THEME_ITEM,
ITEM_POPUP_MODE_MAX
};
ItemPopupMode item_popup_mode = ITEM_POPUP_MODE_MAX;
String edit_item_old_name;
Theme::DataType edit_item_data_type = Theme::DATA_TYPE_MAX;
ThemeItemImportTree *import_default_theme_items = nullptr;
ThemeItemImportTree *import_editor_theme_items = nullptr;
ThemeItemImportTree *import_other_theme_items = nullptr;
LineEdit *import_another_theme_value = nullptr;
Button *import_another_theme_button = nullptr;
EditorFileDialog *import_another_theme_dialog = nullptr;
ConfirmationDialog *confirm_closing_dialog = nullptr;
void ok_pressed() override;
void _close_dialog();
void _dialog_about_to_show();
void _update_edit_types();
void _edited_type_selected();
void _edited_type_edited();
void _edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _update_edit_item_tree(String p_item_type);
void _item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _add_theme_type();
void _add_theme_item(Theme::DataType p_data_type, const String &p_item_name, const String &p_item_type);
void _remove_theme_type(const String &p_theme_type);
void _remove_data_type_items(Theme::DataType p_data_type, String p_item_type);
void _remove_class_items();
void _remove_custom_items();
void _remove_all_items();
void _open_add_theme_item_dialog(int p_data_type);
void _open_rename_theme_item_dialog(Theme::DataType p_data_type, String p_item_name);
void _confirm_edit_theme_item();
void _edit_theme_item_gui_input(const Ref<InputEvent> &p_event);
void _open_select_another_theme();
void _select_another_theme_cbk(const String &p_path);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
ThemeItemEditorDialog(ThemeTypeEditor *p_theme_editor);
};
class ThemeTypeDialog : public ConfirmationDialog {
GDCLASS(ThemeTypeDialog, ConfirmationDialog);
Ref<Theme> edited_theme;
bool include_own_types = false;
String pre_submitted_value;
LineEdit *add_type_filter = nullptr;
ItemList *add_type_options = nullptr;
ConfirmationDialog *add_type_confirmation = nullptr;
void _dialog_about_to_show();
void ok_pressed() override;
void _update_add_type_options(const String &p_filter = "");
void _add_type_filter_cbk(const String &p_value);
void _type_filter_input(const Ref<InputEvent> &p_event);
void _add_type_options_cbk(int p_index);
void _add_type_dialog_entered(const String &p_value);
void _add_type_dialog_activated(int p_index);
void _add_type_selected(const String &p_type_name);
void _add_type_confirmed();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
void set_include_own_types(bool p_enable);
ThemeTypeDialog();
};
// Custom `Label` needed to use `EditorHelpBit` to display theme item documentation.
class ThemeItemLabel : public Label {
virtual Control *make_custom_tooltip(const String &p_text) const;
};
class ThemeTypeEditor : public MarginContainer {
GDCLASS(ThemeTypeEditor, MarginContainer);
Ref<Theme> edited_theme;
String edited_type;
bool updating = false;
struct LeadingStylebox {
bool pinned = false;
StringName item_name;
Ref<StyleBox> stylebox;
Ref<StyleBox> ref_stylebox;
};
LeadingStylebox leading_stylebox;
OptionButton *theme_type_list = nullptr;
Button *add_type_button = nullptr;
Button *rename_type_button = nullptr;
ConfirmationDialog *theme_type_rename_dialog = nullptr;
LineEdit *theme_type_rename_line_edit = nullptr;
Button *remove_type_button = nullptr;
CheckButton *show_default_items_button = nullptr;
TabContainer *data_type_tabs = nullptr;
VBoxContainer *color_items_list = nullptr;
VBoxContainer *constant_items_list = nullptr;
VBoxContainer *font_items_list = nullptr;
VBoxContainer *font_size_items_list = nullptr;
VBoxContainer *icon_items_list = nullptr;
VBoxContainer *stylebox_items_list = nullptr;
LineEdit *type_variation_edit = nullptr;
Button *type_variation_button = nullptr;
Label *type_variation_locked = nullptr;
enum TypeDialogMode {
ADD_THEME_TYPE,
ADD_VARIATION_BASE,
};
TypeDialogMode add_type_mode = ADD_THEME_TYPE;
ThemeTypeDialog *add_type_dialog = nullptr;
Vector<Control *> focusables;
Timer *update_debounce_timer = nullptr;
VBoxContainer *_create_item_list(Theme::DataType p_data_type);
void _update_type_list();
void _update_type_list_debounced();
HashMap<StringName, bool> _get_type_items(String p_type_name, Theme::DataType p_type, bool p_include_default);
HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable);
void _add_focusable(Control *p_control);
void _update_type_items();
void _list_type_selected(int p_index);
void _add_type_button_cbk();
void _rename_type_button_cbk();
void _theme_type_rename_dialog_confirmed();
void _remove_type_button_cbk();
void _add_default_type_items();
void _update_add_button(const String &p_text, LineEdit *p_for_edit);
void _item_add_cbk(int p_data_type, Control *p_control);
void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control);
void _item_override_cbk(int p_data_type, String p_item_name);
void _item_remove_cbk(int p_data_type, String p_item_name);
void _item_rename_cbk(int p_data_type, String p_item_name, Control *p_control);
void _item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control);
void _item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control);
void _item_rename_canceled(int p_data_type, String p_item_name, Control *p_control);
void _color_item_changed(Color p_value, String p_item_name);
void _constant_item_changed(float p_value, String p_item_name);
void _font_size_item_changed(float p_value, String p_item_name);
void _edit_resource_item(Ref<Resource> p_resource, bool p_edit);
void _font_item_changed(Ref<Font> p_value, String p_item_name);
void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name);
void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name);
void _change_pinned_stylebox();
void _on_pin_leader_button_pressed(Control *p_editor, String p_item_name);
void _pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox);
void _on_unpin_leader_button_pressed();
void _unpin_leading_stylebox();
void _update_stylebox_from_leading();
void _type_variation_changed(const String p_value);
void _add_type_variation_cbk();
void _add_type_dialog_selected(const String p_type_name);
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void set_edited_theme(const Ref<Theme> &p_theme);
void select_type(String p_type_name);
bool is_stylebox_pinned(Ref<StyleBox> p_stylebox);
ThemeTypeEditor();
};
class ThemeEditor : public VBoxContainer {
GDCLASS(ThemeEditor, VBoxContainer);
friend class ThemeEditorPlugin;
ThemeEditorPlugin *plugin = nullptr;
Ref<Theme> theme;
TabBar *preview_tabs = nullptr;
PanelContainer *preview_tabs_content = nullptr;
Button *add_preview_button = nullptr;
EditorFileDialog *preview_scene_dialog = nullptr;
ThemeTypeEditor *theme_type_editor = nullptr;
Label *theme_name = nullptr;
ThemeItemEditorDialog *theme_edit_dialog = nullptr;
void _theme_save_button_cbk(bool p_save_as);
void _theme_edit_button_cbk();
void _theme_close_button_cbk();
void _scene_closed(const String &p_path);
void _resource_saved(const Ref<Resource> &p_resource);
void _files_moved(const String &p_old_path, const String &p_new_path);
void _update_theme_name(const String &p_name);
void _add_preview_button_cbk();
void _preview_scene_dialog_cbk(const String &p_path);
void _add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon);
void _change_preview_tab(int p_tab);
void _remove_preview_tab(int p_tab);
void _remove_preview_tab_invalid(Node *p_tab_control);
void _update_preview_tab(Node *p_tab_control);
void _preview_control_picked(String p_class_name);
protected:
void _notification(int p_what);
public:
void edit(const Ref<Theme> &p_theme);
Ref<Theme> get_edited_theme();
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
ThemeEditor();
};
class ThemeEditorPlugin : public EditorPlugin {
GDCLASS(ThemeEditorPlugin, EditorPlugin);
ThemeEditor *theme_editor = nullptr;
Button *button = nullptr;
public:
virtual String get_plugin_name() const override { return "Theme"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
virtual bool can_auto_hide() const override;
ThemeEditorPlugin();
};

View File

@@ -0,0 +1,545 @@
/**************************************************************************/
/* theme_editor_preview.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "theme_editor_preview.h"
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/separator.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/text_edit.h"
#include "scene/gui/tree.h"
#include "scene/resources/packed_scene.h"
#include "scene/theme/theme_db.h"
constexpr double REFRESH_TIMER = 1.5;
void ThemeEditorPreview::set_preview_theme(const Ref<Theme> &p_theme) {
preview_content->set_theme(p_theme);
}
void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) {
preview_overlay->add_child(p_overlay);
p_overlay->hide();
}
void ThemeEditorPreview::_propagate_redraw(Control *p_at) {
p_at->notification(NOTIFICATION_THEME_CHANGED);
p_at->update_minimum_size();
p_at->queue_redraw();
for (int i = 0; i < p_at->get_child_count(); i++) {
Control *a = Object::cast_to<Control>(p_at->get_child(i));
if (a) {
_propagate_redraw(a);
}
}
}
void ThemeEditorPreview::_refresh_interval() {
// In case the project settings have changed.
preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
_propagate_redraw(preview_bg);
_propagate_redraw(preview_content);
}
void ThemeEditorPreview::_preview_visibility_changed() {
set_process(is_visible_in_tree());
}
void ThemeEditorPreview::_picker_button_cbk() {
picker_overlay->set_visible(picker_button->is_pressed());
if (picker_button->is_pressed()) {
_reset_picker_overlay();
}
}
Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) {
Control *found = nullptr;
for (int i = p_parent->get_child_count() - 1; i >= 0; i--) {
Control *cc = Object::cast_to<Control>(p_parent->get_child(i));
if (!cc || !cc->is_visible()) {
continue;
}
Rect2 crect = cc->get_rect();
if (crect.has_point(p_mouse_position)) {
// Check if there is a child control under mouse.
if (cc->get_child_count() > 0) {
found = _find_hovered_control(cc, p_mouse_position - cc->get_position());
}
// If there are no applicable children, use the control itself.
if (!found) {
found = cc;
}
break;
}
}
return found;
}
void ThemeEditorPreview::_draw_picker_overlay() {
if (!picker_button->is_pressed()) {
return;
}
picker_overlay->draw_rect(Rect2(Vector2(0.0, 0.0), picker_overlay->get_size()), theme_cache.preview_picker_overlay_color);
if (hovered_control) {
Rect2 highlight_rect = hovered_control->get_global_rect();
highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position);
picker_overlay->draw_style_box(theme_cache.preview_picker_overlay, highlight_rect);
String highlight_name = hovered_control->get_theme_type_variation();
if (highlight_name == StringName()) {
highlight_name = hovered_control->get_class_name();
}
Rect2 highlight_label_rect = highlight_rect;
highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size);
int margin_top = theme_cache.preview_picker_label->get_margin(SIDE_TOP);
int margin_left = theme_cache.preview_picker_label->get_margin(SIDE_LEFT);
int margin_bottom = theme_cache.preview_picker_label->get_margin(SIDE_BOTTOM);
int margin_right = theme_cache.preview_picker_label->get_margin(SIDE_RIGHT);
highlight_label_rect.size.x += margin_left + margin_right;
highlight_label_rect.size.y += margin_top + margin_bottom;
highlight_label_rect.position = highlight_label_rect.position.clamp(Vector2(), picker_overlay->get_size());
picker_overlay->draw_style_box(theme_cache.preview_picker_label, highlight_label_rect);
Point2 label_pos = highlight_label_rect.position;
label_pos.y += highlight_label_rect.size.y - margin_bottom;
label_pos.x += margin_left;
picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size);
}
}
void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_event) {
if (!picker_button->is_pressed()) {
return;
}
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (hovered_control) {
StringName theme_type = hovered_control->get_theme_type_variation();
if (theme_type == StringName()) {
theme_type = hovered_control->get_class_name();
}
emit_signal(SNAME("control_picked"), theme_type);
picker_button->set_pressed(false);
picker_overlay->set_visible(false);
return;
}
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
Vector2 mp = preview_content->get_local_mouse_position();
hovered_control = _find_hovered_control(preview_content, mp);
picker_overlay->queue_redraw();
}
// Forward input to the scroll container underneath to allow scrolling.
preview_container->gui_input(p_event);
}
void ThemeEditorPreview::_reset_picker_overlay() {
hovered_control = nullptr;
picker_overlay->queue_redraw();
}
void ThemeEditorPreview::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (is_visible_in_tree()) {
set_process(true);
}
connect(SceneStringName(visibility_changed), callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed));
} break;
case NOTIFICATION_READY: {
Vector<Ref<Theme>> preview_themes;
preview_themes.push_back(ThemeDB::get_singleton()->get_default_theme());
ThemeDB::get_singleton()->create_theme_context(preview_root, preview_themes);
} break;
case NOTIFICATION_THEME_CHANGED: {
picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick")));
theme_cache.preview_picker_overlay = get_theme_stylebox(SNAME("preview_picker_overlay"), SNAME("ThemeEditor"));
theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor"));
theme_cache.preview_picker_label = get_theme_stylebox(SNAME("preview_picker_label"), SNAME("ThemeEditor"));
theme_cache.preview_picker_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
theme_cache.font_size = get_theme_default_font_size();
} break;
case NOTIFICATION_PROCESS: {
time_left -= get_process_delta_time();
if (time_left < 0) {
time_left = REFRESH_TIMER;
_refresh_interval();
}
} break;
}
}
void ThemeEditorPreview::_bind_methods() {
ADD_SIGNAL(MethodInfo("control_picked", PropertyInfo(Variant::STRING, "class_name")));
}
ThemeEditorPreview::ThemeEditorPreview() {
preview_toolbar = memnew(HBoxContainer);
add_child(preview_toolbar);
picker_button = memnew(Button);
preview_toolbar->add_child(picker_button);
picker_button->set_theme_type_variation(SceneStringName(FlatButton));
picker_button->set_toggle_mode(true);
picker_button->set_tooltip_text(TTR("Toggle the control picker, allowing to visually select control types for edit."));
picker_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditorPreview::_picker_button_cbk));
MarginContainer *preview_body = memnew(MarginContainer);
preview_body->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
preview_body->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(preview_body);
preview_container = memnew(ScrollContainer);
preview_body->add_child(preview_container);
preview_root = memnew(MarginContainer);
preview_container->add_child(preview_root);
preview_root->set_clip_contents(true);
preview_root->set_custom_minimum_size(Size2(450, 0) * EDSCALE);
preview_root->set_v_size_flags(SIZE_EXPAND_FILL);
preview_root->set_h_size_flags(SIZE_EXPAND_FILL);
preview_bg = memnew(ColorRect);
preview_bg->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
preview_bg->set_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
preview_root->add_child(preview_bg);
preview_content = memnew(MarginContainer);
preview_content->add_theme_constant_override("margin_right", 4 * EDSCALE);
preview_content->add_theme_constant_override("margin_top", 4 * EDSCALE);
preview_content->add_theme_constant_override("margin_left", 4 * EDSCALE);
preview_content->add_theme_constant_override("margin_bottom", 4 * EDSCALE);
preview_root->add_child(preview_content);
preview_overlay = memnew(MarginContainer);
preview_overlay->set_mouse_filter(MOUSE_FILTER_IGNORE);
preview_overlay->set_clip_contents(true);
preview_body->add_child(preview_overlay);
picker_overlay = memnew(Control);
add_preview_overlay(picker_overlay);
picker_overlay->connect(SceneStringName(draw), callable_mp(this, &ThemeEditorPreview::_draw_picker_overlay));
picker_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ThemeEditorPreview::_gui_input_picker_overlay));
picker_overlay->connect(SceneStringName(mouse_exited), callable_mp(this, &ThemeEditorPreview::_reset_picker_overlay));
}
void DefaultThemeEditorPreview::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
test_color_picker_button->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), EditorStringName(Editor))));
} break;
}
}
DefaultThemeEditorPreview::DefaultThemeEditorPreview() {
Panel *main_panel = memnew(Panel);
preview_content->add_child(main_panel);
MarginContainer *main_mc = memnew(MarginContainer);
main_mc->add_theme_constant_override("margin_right", 4 * EDSCALE);
main_mc->add_theme_constant_override("margin_top", 4 * EDSCALE);
main_mc->add_theme_constant_override("margin_left", 4 * EDSCALE);
main_mc->add_theme_constant_override("margin_bottom", 4 * EDSCALE);
preview_content->add_child(main_mc);
HBoxContainer *main_hb = memnew(HBoxContainer);
main_mc->add_child(main_hb);
main_hb->add_theme_constant_override("separation", 20 * EDSCALE);
VBoxContainer *first_vb = memnew(VBoxContainer);
main_hb->add_child(first_vb);
first_vb->set_h_size_flags(SIZE_EXPAND_FILL);
first_vb->add_theme_constant_override("separation", 10 * EDSCALE);
first_vb->add_child(memnew(Label("Label")));
first_vb->add_child(memnew(Button("Button")));
Button *bt = memnew(Button);
bt->set_text(TTR("Toggle Button"));
bt->set_toggle_mode(true);
bt->set_pressed(true);
first_vb->add_child(bt);
bt = memnew(Button);
bt->set_text(TTR("Disabled Button"));
bt->set_disabled(true);
first_vb->add_child(bt);
Button *tb = memnew(Button);
tb->set_flat(true);
tb->set_text("Flat Button");
first_vb->add_child(tb);
CheckButton *cb = memnew(CheckButton);
cb->set_text("CheckButton");
first_vb->add_child(cb);
CheckBox *cbx = memnew(CheckBox);
cbx->set_text("CheckBox");
first_vb->add_child(cbx);
MenuButton *test_menu_button = memnew(MenuButton);
test_menu_button->set_text("MenuButton");
test_menu_button->get_popup()->add_item(TTR("Item"));
test_menu_button->get_popup()->add_item(TTR("Disabled Item"));
test_menu_button->get_popup()->set_item_disabled(1, true);
test_menu_button->get_popup()->add_separator();
test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
test_menu_button->get_popup()->set_item_checked(4, true);
test_menu_button->get_popup()->add_separator();
test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));
test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
test_menu_button->get_popup()->set_item_checked(7, true);
test_menu_button->get_popup()->add_separator(TTR("Named Separator"));
PopupMenu *test_submenu = memnew(PopupMenu);
test_menu_button->get_popup()->add_submenu_node_item(TTR("Submenu"), test_submenu);
test_submenu->add_item(TTR("Subitem 1"));
test_submenu->add_item(TTR("Subitem 2"));
first_vb->add_child(test_menu_button);
OptionButton *test_option_button = memnew(OptionButton);
test_option_button->add_item("OptionButton");
test_option_button->add_separator();
test_option_button->add_item(TTR("Has"));
test_option_button->add_item(TTR("Many"));
test_option_button->add_item(TTR("Options"));
first_vb->add_child(test_option_button);
test_color_picker_button = memnew(ColorPickerButton);
first_vb->add_child(test_color_picker_button);
VBoxContainer *second_vb = memnew(VBoxContainer);
second_vb->set_h_size_flags(SIZE_EXPAND_FILL);
main_hb->add_child(second_vb);
second_vb->add_theme_constant_override("separation", 10 * EDSCALE);
LineEdit *le = memnew(LineEdit);
le->set_text("LineEdit");
second_vb->add_child(le);
le = memnew(LineEdit);
le->set_text(TTR("Disabled LineEdit"));
le->set_editable(false);
second_vb->add_child(le);
TextEdit *te = memnew(TextEdit);
te->set_text("TextEdit");
te->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
second_vb->add_child(te);
second_vb->add_child(memnew(SpinBox));
HBoxContainer *vhb = memnew(HBoxContainer);
second_vb->add_child(vhb);
vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
vhb->add_child(memnew(VSlider));
VScrollBar *vsb = memnew(VScrollBar);
vsb->set_page(25);
vhb->add_child(vsb);
vhb->add_child(memnew(VSeparator));
VBoxContainer *hvb = memnew(VBoxContainer);
vhb->add_child(hvb);
hvb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
hvb->set_h_size_flags(SIZE_EXPAND_FILL);
hvb->add_child(memnew(HSlider));
HScrollBar *hsb = memnew(HScrollBar);
hsb->set_page(25);
hvb->add_child(hsb);
HSlider *hs = memnew(HSlider);
hs->set_editable(false);
hvb->add_child(hs);
hvb->add_child(memnew(HSeparator));
ProgressBar *pb = memnew(ProgressBar);
pb->set_value(50);
hvb->add_child(pb);
VBoxContainer *third_vb = memnew(VBoxContainer);
third_vb->set_h_size_flags(SIZE_EXPAND_FILL);
third_vb->add_theme_constant_override("separation", 10 * EDSCALE);
main_hb->add_child(third_vb);
TabContainer *tc = memnew(TabContainer);
third_vb->add_child(tc);
tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE);
Control *tcc = memnew(Control);
tcc->set_name(TTR("Tab 1"));
tc->add_child(tcc);
tcc = memnew(Control);
tcc->set_name(TTR("Tab 2"));
tc->add_child(tcc);
tcc = memnew(Control);
tcc->set_name(TTR("Tab 3"));
tc->add_child(tcc);
tc->set_tab_disabled(2, true);
Tree *test_tree = memnew(Tree);
third_vb->add_child(test_tree);
test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE);
TreeItem *item = test_tree->create_item();
item->set_text(0, "Tree");
item = test_tree->create_item(test_tree->get_root());
item->set_text(0, "Item");
item = test_tree->create_item(test_tree->get_root());
item->set_editable(0, true);
item->set_text(0, TTR("Editable Item"));
TreeItem *sub_tree = test_tree->create_item(test_tree->get_root());
sub_tree->set_text(0, TTR("Subtree"));
item = test_tree->create_item(sub_tree);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_editable(0, true);
item->set_text(0, "Check Item");
item = test_tree->create_item(sub_tree);
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
item->set_range_config(0, 0, 20, 0.1);
item->set_range(0, 2);
item = test_tree->create_item(sub_tree);
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
item->set_text(0, TTR("Has,Many,Options"));
item->set_range(0, 2);
}
void SceneThemeEditorPreview::_reload_scene() {
if (loaded_scene.is_null()) {
return;
}
if (loaded_scene->get_path().is_empty() || !ResourceLoader::exists(loaded_scene->get_path())) {
EditorNode::get_singleton()->show_warning(TTR("Invalid path, the PackedScene resource was probably moved or removed."));
emit_signal(SNAME("scene_invalidated"));
return;
}
for (int i = preview_content->get_child_count() - 1; i >= 0; i--) {
Node *node = preview_content->get_child(i);
node->queue_free();
preview_content->remove_child(node);
}
Node *instance = loaded_scene->instantiate();
if (!instance || !Object::cast_to<Control>(instance)) {
EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));
emit_signal(SNAME("scene_invalidated"));
return;
}
preview_content->add_child(instance);
emit_signal(SNAME("scene_reloaded"));
}
void SceneThemeEditorPreview::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
reload_scene_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
} break;
}
}
void SceneThemeEditorPreview::_bind_methods() {
ADD_SIGNAL(MethodInfo("scene_invalidated"));
ADD_SIGNAL(MethodInfo("scene_reloaded"));
}
bool SceneThemeEditorPreview::set_preview_scene(const String &p_path) {
loaded_scene = ResourceLoader::load(p_path);
if (loaded_scene.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a PackedScene resource."));
return false;
}
Node *instance = loaded_scene->instantiate();
if (!instance) {
EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, could not instantiate it."));
return false;
}
if (!Object::cast_to<Control>(instance)) {
EditorNode::get_singleton()->show_warning(TTR("Invalid PackedScene resource, must have a Control node at its root."));
memdelete(instance);
return false;
}
preview_content->add_child(instance);
return true;
}
String SceneThemeEditorPreview::get_preview_scene_path() const {
if (loaded_scene.is_null()) {
return "";
}
return loaded_scene->get_path();
}
SceneThemeEditorPreview::SceneThemeEditorPreview() {
preview_toolbar->add_child(memnew(VSeparator));
reload_scene_button = memnew(Button);
reload_scene_button->set_flat(true);
reload_scene_button->set_tooltip_text(TTR("Reload the scene to reflect its most actual state."));
preview_toolbar->add_child(reload_scene_button);
reload_scene_button->connect(SceneStringName(pressed), callable_mp(this, &SceneThemeEditorPreview::_reload_scene));
}

View File

@@ -0,0 +1,119 @@
/**************************************************************************/
/* theme_editor_preview.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#pragma once
#include "scene/gui/box_container.h"
#include "scene/resources/theme.h"
class Button;
class ColorPickerButton;
class ColorRect;
class MarginContainer;
class ScrollContainer;
class ThemeEditorPreview : public VBoxContainer {
GDCLASS(ThemeEditorPreview, VBoxContainer);
ScrollContainer *preview_container = nullptr;
MarginContainer *preview_root = nullptr;
ColorRect *preview_bg = nullptr;
MarginContainer *preview_overlay = nullptr;
Control *picker_overlay = nullptr;
Control *hovered_control = nullptr;
struct ThemeCache {
Ref<StyleBox> preview_picker_overlay;
Color preview_picker_overlay_color;
Ref<StyleBox> preview_picker_label;
Ref<Font> preview_picker_font;
int font_size = 16;
} theme_cache;
double time_left = 0;
void _propagate_redraw(Control *p_at);
void _refresh_interval();
void _preview_visibility_changed();
void _picker_button_cbk();
Control *_find_hovered_control(Control *p_parent, Vector2 p_mouse_position);
void _draw_picker_overlay();
void _gui_input_picker_overlay(const Ref<InputEvent> &p_event);
void _reset_picker_overlay();
protected:
HBoxContainer *preview_toolbar = nullptr;
MarginContainer *preview_content = nullptr;
Button *picker_button = nullptr;
void add_preview_overlay(Control *p_overlay);
void _notification(int p_what);
static void _bind_methods();
public:
void set_preview_theme(const Ref<Theme> &p_theme);
ThemeEditorPreview();
};
class DefaultThemeEditorPreview : public ThemeEditorPreview {
GDCLASS(DefaultThemeEditorPreview, ThemeEditorPreview);
ColorPickerButton *test_color_picker_button = nullptr;
protected:
void _notification(int p_what);
public:
DefaultThemeEditorPreview();
};
class SceneThemeEditorPreview : public ThemeEditorPreview {
GDCLASS(SceneThemeEditorPreview, ThemeEditorPreview);
Ref<PackedScene> loaded_scene;
Button *reload_scene_button = nullptr;
void _reload_scene();
protected:
void _notification(int p_what);
static void _bind_methods();
public:
bool set_preview_scene(const String &p_path);
String get_preview_scene_path() const;
SceneThemeEditorPreview();
};