Skip to content

Commit

Permalink
GH-187 Add BitField input/default value support
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Mar 23, 2024
1 parent fdec82f commit 9b06c06
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/editor/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ void register_editor_classes()
ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorSceneTreeDialog)

// Graph Pin Types
ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinBitField)
ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinBool)
ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinColor)
ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinEnum)
Expand Down
3 changes: 3 additions & 0 deletions src/editor/graph/factories/graph_node_pin_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ OrchestratorGraphNodePin* OrchestratorGraphNodePinFactory::create_pin(Orchestrat
else if (p_pin->get_flags().has_flag(OScriptNodePin::Flags::ENUM))
return memnew(OrchestratorGraphNodePinEnum(p_node, p_pin));

else if (p_pin->get_flags().has_flag(OScriptNodePin::Flags::BITFIELD))
return memnew(OrchestratorGraphNodePinBitField(p_node, p_pin));

switch (p_pin->get_type())
{
case Variant::STRING:
Expand Down
115 changes: 115 additions & 0 deletions src/editor/graph/pins/graph_node_pin_bitfield.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "editor/graph/pins/graph_node_pin_bitfield.h"

#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/check_box.hpp>
#include <godot_cpp/classes/grid_container.hpp>
#include <godot_cpp/classes/popup_panel.hpp>

OrchestratorGraphNodePinBitField::OrchestratorGraphNodePinBitField(OrchestratorGraphNode* p_node,
const Ref<OScriptNodePin>& p_pin)
: OrchestratorGraphNodePin(p_node, p_pin)
{
}

void OrchestratorGraphNodePinBitField::_bind_methods()
{
}

void OrchestratorGraphNodePinBitField::_on_bit_toggle(bool p_state, int64_t p_enum_value)
{
int64_t current_value = _pin->get_effective_default_value();
if (p_state)
current_value |= p_enum_value;
else
current_value &= p_enum_value;

_pin->set_default_value(current_value);

if (_button)
_button->set_text(vformat("%d", _pin->get_effective_default_value()));
}

void OrchestratorGraphNodePinBitField::_on_hide_flags(PopupPanel* p_panel)
{
p_panel->queue_free();
}

void OrchestratorGraphNodePinBitField::_on_show_flags()
{
if (!_button)
return;

PopupPanel* panel = memnew(PopupPanel);
panel->set_size(Vector2(100, 100));
panel->set_position(_button->get_screen_position() + Vector2(0, _button->get_size().height));
panel->connect("popup_hide", callable_mp(this, &OrchestratorGraphNodePinBitField::_on_hide_flags).bind(panel));

const String target_class = _pin->get_target_class();
if (!target_class.is_empty() && target_class.contains("."))
{
const int64_t dot = target_class.find(".");
const String class_name = target_class.substr(0, dot);
const String enum_name = target_class.substr(dot + 1);

GridContainer* grid = memnew(GridContainer);
grid->set_columns(2);
panel->add_child(grid);

const int default_value = _pin->get_effective_default_value();

const PackedStringArray bitfield_values = ClassDB::class_get_enum_constants(class_name, enum_name, true);
for (const String& bitfield : bitfield_values)
{
const int64_t enum_value = ClassDB::class_get_integer_constant(class_name, bitfield);

CheckBox* cb = memnew(CheckBox);
grid->add_child(cb);

if (default_value & enum_value)
cb->set_pressed(true);

Label* label = memnew(Label);
label->set_text(bitfield);
grid->add_child(label);

cb->connect("toggled", callable_mp(this, &OrchestratorGraphNodePinBitField::_on_bit_toggle).bind(enum_value));
}
}

panel->reset_size();

// Position panel centered until the button widget
Vector2 new_position = panel->get_position()
- Vector2i(panel->get_size().width / 2, 0)
+ Vector2(_button->get_size().width / 2, 0);
panel->set_position(new_position);

_button->add_child(panel);
panel->popup();
}

Control* OrchestratorGraphNodePinBitField::_get_default_value_widget()
{
_button = memnew(Button);
_button->set_text(vformat("%d", _pin->get_effective_default_value()));
_button->set_h_size_flags(SIZE_SHRINK_BEGIN);
_button->connect("pressed", callable_mp(this, &OrchestratorGraphNodePinBitField::_on_show_flags));
return _button;
}

62 changes: 62 additions & 0 deletions src/editor/graph/pins/graph_node_pin_bitfield.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This file is part of the Godot Orchestrator project.
//
// Copyright (c) 2023-present Vahera Studios LLC and its contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef ORCHESTRATOR_GRAPH_NODE_PIN_BITFIELD_H
#define ORCHESTRATOR_GRAPH_NODE_PIN_BITFIELD_H

#include "editor/graph/graph_node_pin.h"

/// Forward declaration
namespace godot
{
class Button;
class PopupPanel;
}

/// An implementation of OrchestratorGraphNodePin for bitfield pin types, which renders a
/// drop down multi-selection box for choices.
class OrchestratorGraphNodePinBitField : public OrchestratorGraphNodePin
{
GDCLASS(OrchestratorGraphNodePinBitField, OrchestratorGraphNodePin);

static void _bind_methods();

Button* _button{ nullptr }; //! The button that shows the pop-up

protected:
OrchestratorGraphNodePinBitField() = default;

//~ Begin OrchestratorGraphNodePin Interface
Control* _get_default_value_widget() override;
//~ End OrchestratorGraphNodePin Interface

/// Dispatched when a bitfield checkbox is toggled
/// @param p_state true if the checkbox is toggled, false otherwise
/// @param p_value the value to be adjusted for the bitfield
void _on_bit_toggle(bool p_state, int64_t p_value);

/// Dispatched when the popup panel is hidden.
/// @param p_panel the panel being hidden
void _on_hide_flags(PopupPanel* p_panel);

/// Displays the flag choices for user selection
void _on_show_flags();

public:
OrchestratorGraphNodePinBitField(OrchestratorGraphNode* p_node, const Ref<OScriptNodePin>& p_pin);
};

#endif // ORCHESTRATOR_GRAPH_NODE_PIN_BITFIELD_H
40 changes: 31 additions & 9 deletions src/editor/graph/pins/graph_node_pin_enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include "graph_node_pin_enum.h"

#include "api/extension_db.h"

#include <godot_cpp/classes/option_button.hpp>


OrchestratorGraphNodePinEnum::OrchestratorGraphNodePinEnum(OrchestratorGraphNode* p_node, const Ref<OScriptNodePin>& p_pin)
: OrchestratorGraphNodePin(p_node, p_pin)
{
Expand All @@ -44,19 +46,39 @@ Control* OrchestratorGraphNodePinEnum::_get_default_value_widget()
OptionButton* button = memnew(OptionButton);
button->connect("item_selected", callable_mp(this, &OrchestratorGraphNodePinEnum::_on_item_selected).bind(button));

const String enum_class = _pin->get_target_class();
if (!enum_class.is_empty() && ExtensionDB::get_global_enum_names().has(enum_class))
{
int effective_default = _pin->get_effective_default_value();
int effective_default = _pin->get_effective_default_value();

const EnumInfo &ei = ExtensionDB::get_global_enum(enum_class);
for (const EnumValue& value : ei.values)
const String target_enum_class = _pin->get_target_class();
if (!target_enum_class.is_empty())
{
if (target_enum_class.contains("."))
{
if (!value.friendly_name.is_empty())
const int dot = target_enum_class.find(".");
const String class_name = target_enum_class.substr(0, dot);
const String enum_name = target_enum_class.substr(dot + 1);
const PackedStringArray enum_values = ClassDB::class_get_enum_constants(class_name, enum_name, true);

int index = 0;
for (const String& enum_value : enum_values)
{
button->add_item(value.friendly_name);
if (effective_default == value.value)
button->add_item(enum_value);
if (effective_default == index)
button->select(button->get_item_count() - 1);

index++;
}
}
else if (ExtensionDB::get_global_enum_names().has(target_enum_class))
{
const EnumInfo &ei = ExtensionDB::get_global_enum(target_enum_class);
for (const EnumValue& value : ei.values)
{
if (!value.friendly_name.is_empty())
{
button->add_item(value.friendly_name);
if (effective_default == value.value)
button->select(button->get_item_count() - 1);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/editor/graph/pins/graph_node_pins.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef ORCHESTRATOR_GRAPH_NODE_PINS_H
#define ORCHESTRATOR_GRAPH_NODE_PINS_H

#include "editor/graph/pins/graph_node_pin_bitfield.h"
#include "editor/graph/pins/graph_node_pin_bool.h"
#include "editor/graph/pins/graph_node_pin_color.h"
#include "editor/graph/pins/graph_node_pin_enum.h"
Expand Down
1 change: 1 addition & 0 deletions src/script/node_pin.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class OScriptNodePin : public Resource
FILE = 1 << 23, //! Should allow the user to select a file
MULTILINE = 1 << 24, //! Text should be rendered using a TextEdit rather than LineEdit
ENUM = 1 << 25, //! Target class holds the name of the enum
BITFIELD = 1 << 26, //! Target class holds a bitfield
};

private:
Expand Down
17 changes: 16 additions & 1 deletion src/script/nodes/functions/call_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,22 @@ void OScriptNodeCallFunction::_create_pins_for_method(const MethodInfo& p_method
{
Ref<OScriptNodePin> pin = create_pin(PD_Input, pi.name, pi.type);
if (pin.is_valid())
pin->set_flags(OScriptNodePin::Flags::DATA | OScriptNodePin::Flags::NO_CAPITALIZE);
{
BitField<OScriptNodePin::Flags> flags(OScriptNodePin::Flags::DATA | OScriptNodePin::NO_CAPITALIZE);
if (pi.usage & PROPERTY_USAGE_CLASS_IS_ENUM)
{
flags.set_flag(OScriptNodePin::Flags::ENUM);
pin->set_target_class(pi.class_name);
pin->set_type(pi.type);
}
else if (pi.usage & PROPERTY_USAGE_CLASS_IS_BITFIELD)
{
flags.set_flag(OScriptNodePin::Flags::BITFIELD);
pin->set_target_class(pi.class_name);
pin->set_type(pi.type);
}
pin->set_flags(flags);
}
}

if (_has_return_value(p_method))
Expand Down

0 comments on commit 9b06c06

Please sign in to comment.