mirror of https://github.com/godotengine/godot
Add support for "ordered" list properties.
Add support for "ordered" list properties.
This commit is contained in:
parent
15ff450680
commit
9cc6e4a1be
|
|
@ -678,6 +678,7 @@ void register_global_constants() {
|
|||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ONESHOT);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ORDERED_LIST);
|
||||
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
|
||||
|
||||
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ enum PropertyHint {
|
|||
PROPERTY_HINT_TOOL_BUTTON,
|
||||
PROPERTY_HINT_ONESHOT, ///< the property will be changed by self after setting, such as AudioStreamPlayer.playing, Particles.emitting.
|
||||
PROPERTY_HINT_NO_NODEPATH, /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock.
|
||||
PROPERTY_HINT_ORDERED_LIST,
|
||||
PROPERTY_HINT_MAX,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2954,7 +2954,11 @@
|
|||
<constant name="PROPERTY_HINT_ONESHOT" value="40" enum="PropertyHint">
|
||||
Hints that a property will be changed on its own after setting, such as [member AudioStreamPlayer.playing] or [member GPUParticles3D.emitting].
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint">
|
||||
<constant name="PROPERTY_HINT_ORDERED_LIST" value="42" enum="PropertyHint">
|
||||
Hints that a property is ordered list of values.
|
||||
The hint string is a comma separated list of display name/value pairs separated by [code]:[/code] such as [code]"Hello:hello,Something Else:else"[/code].
|
||||
</constant>
|
||||
<constant name="PROPERTY_HINT_MAX" value="43" enum="PropertyHint">
|
||||
Represents the size of the [enum PropertyHint] enum.
|
||||
</constant>
|
||||
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#include "scene/3d/gpu_particles_3d.h"
|
||||
#include "scene/gui/color_picker.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
|
@ -249,6 +250,225 @@ EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
|
|||
}
|
||||
}
|
||||
|
||||
///////////////////// ORDERED LIST /////////////////////////
|
||||
|
||||
void EditorPropertyOrderedList::_set_read_only(bool p_read_only) {
|
||||
read_only = p_read_only;
|
||||
_update_tree();
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::_update_tree() {
|
||||
tree->clear();
|
||||
|
||||
TreeItem *root = tree->create_item(nullptr);
|
||||
// Add missing keys to the value.
|
||||
for (const KeyValue<String, String> &E : names) {
|
||||
if (!values.has(E.key)) {
|
||||
values.push_back(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
// Update tree.
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
if (!names.has(values[i])) {
|
||||
WARN_PRINT(vformat("Invalid list item %s for property %s.", values[i], get_edited_property()));
|
||||
continue;
|
||||
}
|
||||
TreeItem *it = tree->create_item(root);
|
||||
it->set_text(0, names[values[i]]);
|
||||
it->set_metadata(0, values[i]);
|
||||
if (!read_only) {
|
||||
it->add_button(0, get_editor_theme_icon(SNAME("ArrowUp")), BUTTON_UP, (i == 0), TTR("Move Up"));
|
||||
it->add_button(0, get_editor_theme_icon(SNAME("ArrowDown")), BUTTON_DOWN, (i == values.size() - 1), TTR("Move Down"));
|
||||
}
|
||||
}
|
||||
|
||||
// Update size.
|
||||
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("TextEdit"));
|
||||
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("TextEdit"));
|
||||
tree->set_custom_minimum_size(tree->get_background_size() + Size2i(0, MIN(tree->get_internal_min_size().height, 6 * font->get_height(font_size))));
|
||||
}
|
||||
|
||||
Variant EditorPropertyOrderedList::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
|
||||
if (read_only) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
if (tree->get_button_id_at_position(p_point) != -1) {
|
||||
return Variant();
|
||||
}
|
||||
|
||||
TreeItem *item = tree->get_next_selected(nullptr);
|
||||
if (!item) {
|
||||
return Variant();
|
||||
}
|
||||
String value = item->get_metadata(0);
|
||||
if (!names.has(value)) {
|
||||
return Variant();
|
||||
}
|
||||
String name = names[value];
|
||||
|
||||
// Preview.
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
Label *label_prev = memnew(Label(vformat("%s (%s)", name, value)));
|
||||
label_prev->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
hb->add_child(label_prev);
|
||||
set_drag_preview(hb);
|
||||
|
||||
// Drag data.
|
||||
Dictionary drag_data;
|
||||
drag_data["type"] = "list_reorder";
|
||||
drag_data["id"] = get_instance_id();
|
||||
drag_data["value"] = value;
|
||||
|
||||
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
|
||||
|
||||
return drag_data;
|
||||
}
|
||||
|
||||
bool EditorPropertyOrderedList::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
|
||||
if (read_only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Dictionary d = p_data;
|
||||
if (!d.has("type") || !d.has("id") || !d.has("value")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d["type"] != "list_reorder" || d["id"] != get_instance_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TreeItem *item = tree->get_item_at_position(p_point);
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int section = tree->get_drop_section_at_position(p_point);
|
||||
if (section == -100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
|
||||
if (!can_drop_data_fw(p_point, p_data, p_from)) {
|
||||
return;
|
||||
}
|
||||
Dictionary d = p_data;
|
||||
String drop_value = d["value"];
|
||||
|
||||
TreeItem *item = tree->get_item_at_position(p_point);
|
||||
ERR_FAIL_COND(!item);
|
||||
String target_value = item->get_metadata(0);
|
||||
|
||||
int section = tree->get_drop_section_at_position(p_point);
|
||||
ERR_FAIL_COND(section == -100);
|
||||
|
||||
tree->set_drop_mode_flags(Tree::DROP_MODE_DISABLED);
|
||||
|
||||
Vector<String> new_values = values;
|
||||
new_values.erase(drop_value);
|
||||
if (section == 1) {
|
||||
int pos = new_values.find(target_value);
|
||||
if (pos == -1) {
|
||||
return;
|
||||
}
|
||||
new_values.insert(pos + 1, drop_value);
|
||||
} else {
|
||||
int pos = new_values.find(target_value);
|
||||
if (pos == -1) {
|
||||
return;
|
||||
}
|
||||
new_values.insert(pos, drop_value);
|
||||
}
|
||||
values = new_values;
|
||||
_update_tree();
|
||||
emit_changed(get_edited_property(), String(",").join(values));
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::_tree_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
|
||||
ERR_FAIL_COND(!p_item);
|
||||
if (p_button != MouseButton::LEFT) {
|
||||
return;
|
||||
}
|
||||
|
||||
String drop_value = p_item->get_metadata(0);
|
||||
Vector<String> new_values = values;
|
||||
if (p_id == BUTTON_DOWN) {
|
||||
int pos = new_values.find(drop_value);
|
||||
if (pos == -1) {
|
||||
return;
|
||||
}
|
||||
new_values.erase(drop_value);
|
||||
new_values.insert(pos + 1, drop_value);
|
||||
} else {
|
||||
int pos = new_values.find(drop_value);
|
||||
if (pos == -1) {
|
||||
return;
|
||||
}
|
||||
new_values.erase(drop_value);
|
||||
new_values.insert(pos - 1, drop_value);
|
||||
}
|
||||
values = new_values;
|
||||
_update_tree();
|
||||
emit_changed(get_edited_property(), String(",").join(values));
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::update_property() {
|
||||
String current_value = get_edited_property_value();
|
||||
values = current_value.split(",");
|
||||
print_line(values);
|
||||
_update_tree();
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::setup(const Vector<String> &p_options) {
|
||||
values.clear();
|
||||
names.clear();
|
||||
|
||||
for (const String &option : p_options) {
|
||||
Vector<String> text_split = option.split(":");
|
||||
if (text_split.size() != 1) {
|
||||
names[text_split[1]] = text_split[0];
|
||||
} else {
|
||||
names[text_split[0]] = text_split[0];
|
||||
}
|
||||
}
|
||||
_update_tree();
|
||||
}
|
||||
|
||||
void EditorPropertyOrderedList::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
tree->add_theme_style_override(SNAME("panel"), get_theme_stylebox(SNAME("normal"), SNAME("Button")));
|
||||
tree->add_theme_style_override(SNAME("focus"), get_theme_stylebox(SNAME("focus"), SNAME("Button")));
|
||||
_update_tree();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
EditorPropertyOrderedList::EditorPropertyOrderedList() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
add_child(hb);
|
||||
|
||||
default_layout = memnew(HBoxContainer);
|
||||
default_layout->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
hb->add_child(default_layout);
|
||||
|
||||
tree = memnew(Tree);
|
||||
tree->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
tree->set_hide_root(true);
|
||||
tree->connect("button_clicked", callable_mp(this, &EditorPropertyOrderedList::_tree_button_pressed));
|
||||
default_layout->add_child(tree);
|
||||
|
||||
SET_DRAG_FORWARDING_GCD(tree, EditorPropertyOrderedList);
|
||||
|
||||
add_focusable(tree);
|
||||
}
|
||||
|
||||
///////////////////// TEXT ENUM /////////////////////////
|
||||
|
||||
void EditorPropertyTextEnum::_set_read_only(bool p_read_only) {
|
||||
|
|
@ -3615,7 +3835,12 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
}
|
||||
} break;
|
||||
case Variant::STRING: {
|
||||
if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {
|
||||
if (p_hint == PROPERTY_HINT_ORDERED_LIST) {
|
||||
EditorPropertyOrderedList *editor = memnew(EditorPropertyOrderedList);
|
||||
Vector<String> options = p_hint_text.split(",", false);
|
||||
editor->setup(options);
|
||||
return editor;
|
||||
} else if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {
|
||||
EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
|
||||
Vector<String> options = p_hint_text.split(",", false);
|
||||
editor->setup(options, false, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ class PropertySelector;
|
|||
class SceneTreeDialog;
|
||||
class TextEdit;
|
||||
class TextureButton;
|
||||
class Tree;
|
||||
class TreeItem;
|
||||
|
||||
class EditorPropertyNil : public EditorProperty {
|
||||
GDCLASS(EditorPropertyNil, EditorProperty);
|
||||
|
|
@ -132,6 +134,39 @@ public:
|
|||
EditorPropertyTextEnum();
|
||||
};
|
||||
|
||||
class EditorPropertyOrderedList : public EditorProperty {
|
||||
GDCLASS(EditorPropertyOrderedList, EditorProperty);
|
||||
|
||||
enum EditorPropertyOrderedListButtonID {
|
||||
BUTTON_UP,
|
||||
BUTTON_DOWN,
|
||||
};
|
||||
|
||||
HBoxContainer *default_layout = nullptr;
|
||||
|
||||
bool read_only = false;
|
||||
Tree *tree = nullptr;
|
||||
|
||||
HashMap<String, String> names;
|
||||
Vector<String> values;
|
||||
|
||||
void _update_tree();
|
||||
void _tree_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
|
||||
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
|
||||
public:
|
||||
void setup(const Vector<String> &p_options);
|
||||
virtual void update_property() override;
|
||||
EditorPropertyOrderedList();
|
||||
};
|
||||
|
||||
class EditorPropertyPath : public EditorProperty {
|
||||
GDCLASS(EditorPropertyPath, EditorProperty);
|
||||
Vector<String> extensions;
|
||||
|
|
|
|||
|
|
@ -4196,10 +4196,20 @@ void Tree::set_editor_selection(int p_from_line, int p_to_line, int p_from_colum
|
|||
}
|
||||
}
|
||||
|
||||
Size2 Tree::get_background_size() const {
|
||||
const Ref<StyleBox> background = theme_cache.panel_style;
|
||||
|
||||
// This is the background stylebox's content rect.
|
||||
const real_t width = background->get_margin(SIDE_LEFT) + background->get_margin(SIDE_RIGHT);
|
||||
const real_t height = background->get_margin(SIDE_TOP) + background->get_margin(SIDE_BOTTOM);
|
||||
return Size2(width, height);
|
||||
}
|
||||
|
||||
Size2 Tree::get_internal_min_size() const {
|
||||
Size2i size;
|
||||
if (root) {
|
||||
size.height += get_item_height(root);
|
||||
size.height -= theme_cache.v_separation;
|
||||
}
|
||||
for (int i = 0; i < columns.size(); i++) {
|
||||
size.width += get_column_minimum_width(i);
|
||||
|
|
|
|||
|
|
@ -657,7 +657,6 @@ private:
|
|||
bool h_scroll_enabled = true;
|
||||
bool v_scroll_enabled = true;
|
||||
|
||||
Size2 get_internal_min_size() const;
|
||||
void update_scrollbars();
|
||||
|
||||
Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item);
|
||||
|
|
@ -798,6 +797,8 @@ public:
|
|||
void ensure_cursor_is_visible();
|
||||
|
||||
Rect2 get_custom_popup_rect() const;
|
||||
Size2 get_background_size() const;
|
||||
Size2 get_internal_min_size() const;
|
||||
|
||||
int get_item_offset(TreeItem *p_item) const;
|
||||
Rect2 get_item_rect(TreeItem *p_item, int p_column = -1, int p_button = -1) const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue