Skip to content

Commit

Permalink
[Editor] Add support for displaying unsigned integers in the inspector
Browse files Browse the repository at this point in the history
Added these hints to shader uniforms, which is what inspired this
improvement

Also made improvements to handling of vector inspector
  • Loading branch information
AThousandShips committed Sep 22, 2024
1 parent e4e024a commit 7fbfde7
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 29 deletions.
2 changes: 1 addition & 1 deletion core/object/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class TypedArray;

enum PropertyHint {
PROPERTY_HINT_NONE, ///< no hint provided.
PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians_as_degrees][,degrees][,exp][,suffix:<keyword>] range.
PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians_as_degrees][,degrees][,exp][,display_unsigned][,suffix:<keyword>] range.
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc"
PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only")
Expand Down
2 changes: 1 addition & 1 deletion doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2763,7 +2763,7 @@
<constant name="PROPERTY_HINT_RANGE" value="1" enum="PropertyHint">
Hints that an [int] or [float] property should be within a range specified via the hint string [code]"min,max"[/code] or [code]"min,max,step"[/code]. The hint string can optionally include [code]"or_greater"[/code] and/or [code]"or_less"[/code] to allow manual input going respectively above the max or below the min values.
[b]Example:[/b] [code]"-360,360,1,or_greater,or_less"[/code].
Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians_as_degrees"[/code] for editing radian angles in degrees (the range values are also in degrees), [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider.
Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians_as_degrees"[/code] for editing radian angles in degrees (the range values are also in degrees), [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider, and [code]"display_unsigned"[/code] to allow signed integers to be displayed as unsigned (this is especially useful with [PackedInt32Array]).
</constant>
<constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint">
Hints that an [int] or [String] property is an enumerated value to pick in a list specified via a hint string.
Expand Down
52 changes: 38 additions & 14 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1294,17 +1294,30 @@ void EditorPropertyInteger::_value_changed(int64_t val) {
}

void EditorPropertyInteger::update_property() {
int64_t val = get_edited_property_value();
spin->set_value_no_signal(val);
if (display_unsigned) {
// Fix range to handle casting.
uint64_t val = (uint64_t)get_edited_property_value() % ((uint64_t)spin->get_max() + 1);
spin->set_value_no_signal(val);
#ifdef DEBUG_ENABLED
// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.
if (val != (int64_t)(double)(val)) {
WARN_PRINT("Cannot reliably represent '" + itos(val) + "' in the inspector, value is too large.");
}
// If spin (currently EditorSplinSlider : Range) is changed so that it can use uint64_t, then the below warning wouldn't be a problem.
if (val != (uint64_t)(double)(val)) {
WARN_PRINT("Cannot reliably represent '" + uitos(val) + "' in the inspector, value is too large.");
}
#endif
} else {
int64_t val = get_edited_property_value();
spin->set_value_no_signal(val);
#ifdef DEBUG_ENABLED
// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.
if (val != (int64_t)(double)(val)) {
WARN_PRINT("Cannot reliably represent '" + itos(val) + "' in the inspector, value is too large.");
}
#endif
}
}

void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix, bool p_display_unsigned) {
display_unsigned = p_display_unsigned;
spin->set_min(p_min);
spin->set_max(p_max);
spin->set_step(p_step);
Expand Down Expand Up @@ -3386,6 +3399,7 @@ struct EditorPropertyRangeHint {
bool exp_range = false;
bool hide_slider = true;
bool radians_as_degrees = false;
bool display_unsigned = false;
};

static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const String &p_hint_text, double p_default_step, bool is_int = false) {
Expand Down Expand Up @@ -3420,8 +3434,18 @@ static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const Stri
hint.hide_slider = true;
} else if (slice == "exp") {
hint.exp_range = true;
} else if (slice == "display_unsigned") {
hint.display_unsigned = true;
}
}

// display_unsigned requires a non-negative min and no or_less/or_greater.
ERR_FAIL_COND_V_MSG(hint.display_unsigned && hint.or_less, hint,
vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": display_unsigned cannot be combined with or_less.", p_hint_text));
ERR_FAIL_COND_V_MSG(hint.display_unsigned && hint.or_greater, hint,
vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": display_unsigned cannot be combined with or_greater.", p_hint_text));
ERR_FAIL_COND_V_MSG(hint.display_unsigned && hint.min < 0, hint,
vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": display_unsigned requires a non-negative minimum value.", p_hint_text));
}
bool degrees = false;
for (int i = 0; i < slices.size(); i++) {
Expand Down Expand Up @@ -3520,7 +3544,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);

EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix, hint.display_unsigned);

return editor;
}
Expand Down Expand Up @@ -3602,14 +3626,14 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide));

EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, hint.or_greater, hint.or_less);
return editor;

} break;
case Variant::VECTOR2I: {
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, hint.or_greater, hint.or_less, hint.display_unsigned);
return editor;

} break;
Expand All @@ -3629,28 +3653,28 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
case Variant::VECTOR3: {
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees, hint.or_greater, hint.or_less);
return editor;

} break;
case Variant::VECTOR3I: {
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, hint.or_greater, hint.or_less, hint.display_unsigned);
return editor;

} break;
case Variant::VECTOR4: {
EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, hint.or_greater, hint.or_less);
return editor;

} break;
case Variant::VECTOR4I: {
EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, hint.or_greater, hint.or_less, hint.display_unsigned);
return editor;

} break;
Expand Down
4 changes: 3 additions & 1 deletion editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,14 @@ class EditorPropertyInteger : public EditorProperty {
EditorSpinSlider *spin = nullptr;
void _value_changed(int64_t p_val);

bool display_unsigned = false;

protected:
virtual void _set_read_only(bool p_read_only) override;

public:
virtual void update_property() override;
void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String());
void setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix = String(), bool p_display_unsigned = false);
EditorPropertyInteger();
};

Expand Down
16 changes: 11 additions & 5 deletions editor/editor_properties_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ void EditorPropertyVectorN::_value_changed(double val, const String &p_name) {
Variant::construct(vector_type, v, nullptr, 0, cerror);

for (int i = 0; i < component_count; i++) {
if (radians_as_degrees) {
if (display_unsigned) {
v.set(i, (int64_t)spin_sliders[i]->get_value());
} else if (radians_as_degrees) {
v.set(i, Math::deg_to_rad(spin_sliders[i]->get_value()));
} else {
v.set(i, spin_sliders[i]->get_value());
Expand All @@ -85,7 +87,10 @@ void EditorPropertyVectorN::_value_changed(double val, const String &p_name) {
void EditorPropertyVectorN::update_property() {
Variant val = get_edited_property_value();
for (int i = 0; i < component_count; i++) {
if (radians_as_degrees) {
if (display_unsigned) {
// Fix range to handle casting signed to unsigned.
spin_sliders[i]->set_value_no_signal((uint64_t)val.get(i) % ((uint64_t)spin_sliders[i]->get_max() + 1));
} else if (radians_as_degrees) {
spin_sliders[i]->set_value_no_signal(Math::rad_to_deg((real_t)val.get(i)));
} else {
spin_sliders[i]->set_value_no_signal(val.get(i));
Expand Down Expand Up @@ -153,16 +158,17 @@ void EditorPropertyVectorN::_notification(int p_what) {
}
}

void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_radians_as_degrees) {
void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_radians_as_degrees, bool p_allow_greater, bool p_allow_lesser, bool p_display_unsigned) {
radians_as_degrees = p_radians_as_degrees;
display_unsigned = p_display_unsigned;

for (EditorSpinSlider *spin : spin_sliders) {
spin->set_min(p_min);
spin->set_max(p_max);
spin->set_step(p_step);
spin->set_hide_slider(p_hide_slider);
spin->set_allow_greater(true);
spin->set_allow_lesser(true);
spin->set_allow_greater(p_allow_greater);
spin->set_allow_lesser(p_allow_lesser);
spin->set_suffix(p_suffix);
}

Expand Down
3 changes: 2 additions & 1 deletion editor/editor_properties_vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class EditorPropertyVectorN : public EditorProperty {
bool is_grabbed = false;

bool radians_as_degrees = false;
bool display_unsigned = false;

void _update_ratio();
void _store_link(bool p_linked);
Expand All @@ -62,7 +63,7 @@ class EditorPropertyVectorN : public EditorProperty {

public:
virtual void update_property() override;
void setup(double p_min, double p_max, double p_step = 1.0, bool p_hide_slider = true, bool p_link = false, const String &p_suffix = String(), bool p_radians_as_degrees = false);
void setup(double p_min, double p_max, double p_step = 1.0, bool p_hide_slider = true, bool p_link = false, const String &p_suffix = String(), bool p_radians_as_degrees = false, bool p_allow_greater = true, bool p_allow_lesser = true, bool p_display_unsigned = false);
EditorPropertyVectorN(Variant::Type p_type, bool p_force_wide, bool p_horizontal);
};

Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@
<description>
Export an [int], [float], [Array][lb][int][rb], [Array][lb][float][rb], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], or [PackedFloat64Array] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [member EditorSettings.interface/inspector/default_float_step] setting.
If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget.
Hints also allow to indicate the units for the edited value. Using [code]"radians_as_degrees"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock (the range values are also in degrees). [code]"degrees"[/code] allows to add a degree sign as a unit suffix (the value is unchanged). Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string.
Hints also allow to indicate the units for the edited value. Using [code]"radians_as_degrees"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock (the range values are also in degrees). [code]"degrees"[/code] allows to add a degree sign as a unit suffix (the value is unchanged). [code]"display_unsigned"[/code] allows signed integers to be displayed as unsigned, this is especially useful with [PackedInt32Array]. Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string.
See also [constant PROPERTY_HINT_RANGE].
[codeblock]
@export_range(0, 20) var number
Expand Down
5 changes: 4 additions & 1 deletion modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<St

static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (p_annotation->name == SNAME("@export_range")) {
if (p_argument == 3 || p_argument == 4 || p_argument == 5) {
if (p_argument == 3 || p_argument == 4 || p_argument == 5 || p_argument == 6) {
// Slider hint.
ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider1.insert_text = slider1.display.quote(p_quote_style);
Expand All @@ -877,6 +877,9 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption slider3("hide_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider3.insert_text = slider3.display.quote(p_quote_style);
r_result.insert(slider3.display, slider3);
ScriptLanguage::CodeCompletionOption slider4("display_unsigned", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider4.insert_text = slider4.display.quote(p_quote_style);
r_result.insert(slider4.display, slider4);
}
} else if (p_annotation->name == SNAME("@export_exp_easing")) {
if (p_argument == 0 || p_argument == 1) {
Expand Down
49 changes: 45 additions & 4 deletions servers/rendering/shader_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4557,7 +4557,12 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
case ShaderLanguage::TYPE_INT: {
if (p_uniform.array_size > 0) {
pi.type = Variant::PACKED_INT32_ARRAY;
// TODO: Handle range and encoding for for unsigned values.
pi.hint = PROPERTY_HINT_TYPE_STRING;
if (p_uniform.type == ShaderLanguage::TYPE_INT) {
pi.hint_string = vformat("%d/%d:%d,%d", Variant::INT, PROPERTY_HINT_RANGE, INT32_MIN, INT32_MAX);
} else {
pi.hint_string = vformat("%d/%d:0,%d,display_unsigned", Variant::INT, PROPERTY_HINT_RANGE, UINT32_MAX);
}
} else if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_ENUM) {
pi.type = Variant::INT;
pi.hint = PROPERTY_HINT_ENUM;
Expand All @@ -4568,10 +4573,10 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
pi.hint = PROPERTY_HINT_RANGE;
if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) {
pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]);
} else if (p_uniform.type == ShaderLanguage::TYPE_UINT) {
pi.hint_string = "0," + itos(UINT32_MAX);
} else {
} else if (p_uniform.type == ShaderLanguage::TYPE_INT) {
pi.hint_string = itos(INT32_MIN) + "," + itos(INT32_MAX);
} else {
pi.hint_string = "0," + itos(UINT32_MAX) + ",display_unsigned";
}
}
} break;
Expand All @@ -4580,26 +4585,62 @@ PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform
if (p_uniform.array_size > 0) {
pi.type = Variant::PACKED_INT32_ARRAY;
// TODO: Handle vector pairs?
pi.hint = PROPERTY_HINT_TYPE_STRING;
if (p_uniform.type == ShaderLanguage::TYPE_VEC2) {
pi.hint_string = vformat("%d/%d:%d,%d", Variant::INT, PROPERTY_HINT_RANGE, INT32_MIN, INT32_MAX);
} else {
pi.hint_string = vformat("%d/%d:0,%d,display_unsigned", Variant::INT, PROPERTY_HINT_RANGE, UINT32_MAX);
}
} else {
pi.type = Variant::VECTOR2I;
pi.hint = PROPERTY_HINT_RANGE;
if (p_uniform.type == ShaderLanguage::TYPE_IVEC2) {
pi.hint_string = itos(INT32_MIN) + "," + itos(INT32_MAX);
} else {
pi.hint_string = "0," + itos(UINT32_MAX) + ",display_unsigned";
}
}
} break;
case ShaderLanguage::TYPE_UVEC3:
case ShaderLanguage::TYPE_IVEC3: {
if (p_uniform.array_size > 0) {
pi.type = Variant::PACKED_INT32_ARRAY;
// TODO: Handle vector pairs?
pi.hint = PROPERTY_HINT_TYPE_STRING;
if (p_uniform.type == ShaderLanguage::TYPE_IVEC3) {
pi.hint_string = vformat("%d/%d:%d,%d", Variant::INT, PROPERTY_HINT_RANGE, INT32_MIN, INT32_MAX);
} else {
pi.hint_string = vformat("%d/%d:0,%d,display_unsigned", Variant::INT, PROPERTY_HINT_RANGE, UINT32_MAX);
}
} else {
pi.type = Variant::VECTOR3I;
pi.hint = PROPERTY_HINT_RANGE;
if (p_uniform.type == ShaderLanguage::TYPE_IVEC3) {
pi.hint_string = itos(INT32_MIN) + "," + itos(INT32_MAX);
} else {
pi.hint_string = "0," + itos(UINT32_MAX) + ",display_unsigned";
}
}
} break;
case ShaderLanguage::TYPE_UVEC4:
case ShaderLanguage::TYPE_IVEC4: {
if (p_uniform.array_size > 0) {
pi.type = Variant::PACKED_INT32_ARRAY;
// TODO: Handle vector pairs?
pi.hint = PROPERTY_HINT_TYPE_STRING;
if (p_uniform.type == ShaderLanguage::TYPE_IVEC4) {
pi.hint_string = vformat("%d/%d:%d,%d", Variant::INT, PROPERTY_HINT_RANGE, INT32_MIN, INT32_MAX);
} else {
pi.hint_string = vformat("%d/%d:0,%d,display_unsigned", Variant::INT, PROPERTY_HINT_RANGE, UINT32_MAX);
}
} else {
pi.type = Variant::VECTOR4I;
pi.hint = PROPERTY_HINT_RANGE;
if (p_uniform.type == ShaderLanguage::TYPE_IVEC4) {
pi.hint_string = itos(INT32_MIN) + "," + itos(INT32_MAX);
} else {
pi.hint_string = "0," + itos(UINT32_MAX) + ",display_unsigned";
}
}
} break;
case ShaderLanguage::TYPE_FLOAT: {
Expand Down

0 comments on commit 7fbfde7

Please sign in to comment.