diff options
Diffstat (limited to 'extension')
4 files changed, 148 insertions, 58 deletions
diff --git a/extension/src/openvic-extension/classes/GUIListBox.cpp b/extension/src/openvic-extension/classes/GUIListBox.cpp index f7b8d88..d04ab59 100644 --- a/extension/src/openvic-extension/classes/GUIListBox.cpp +++ b/extension/src/openvic-extension/classes/GUIListBox.cpp @@ -12,28 +12,52 @@ using namespace godot; using OpenVic::Utilities::std_view_to_godot_string; -Error GUIListBox::_calculate_child_arrangement() { - ERR_FAIL_NULL_V(gui_listbox, FAILED); +/* StringNames cannot be constructed until Godot has called StringName::setup(), + * so we must use wrapper functions to delay their initialisation. */ +StringName const& GUIListBox::_signal_scroll_index_changed() { + static const StringName signal_scroll_index_changed = "scroll_index_changed"; + return signal_scroll_index_changed; +} - const int32_t child_count = get_child_count(); - const real_t max_height = get_size().height; +Error GUIListBox::_calculate_max_scroll_index(bool signal) { + if (fixed) { + if (fixed_item_count <= 0) { + max_scroll_index = 0; + fixed_visible_items = 0; + } else if (fixed_item_height <= 0.0f) { + max_scroll_index = fixed_item_count - 1; + fixed_visible_items = max_scroll_index; + } else { + const real_t max_height = get_size().height; - real_t height = 0.0f, height_under_max_scroll_index = 0.0f; + fixed_visible_items = max_height / fixed_item_height; + max_scroll_index = fixed_item_count - std::max(fixed_visible_items, 1); + } + } else { + const int32_t child_count = get_child_count(); - children_data.clear(); - max_scroll_index = 0; + if (child_count <= 0) { + max_scroll_index = 0; + } else { + const real_t max_height = get_size().height; - for (int32_t index = 0; index < child_count; ++index) { - Control* child = Object::cast_to<Control>(get_child(index)); - if (child != nullptr && child != scrollbar && child->is_visible()) { - const real_t child_height = child->get_size().height; /* Spacing is ignored */ - children_data.push_back({ child, height, child_height }); + real_t height_under_max_scroll_index = 0.0f; - height += child_height; - height_under_max_scroll_index += child_height; + max_scroll_index = child_count; - while (height_under_max_scroll_index > max_height && max_scroll_index + 1 < children_data.size()) { - height_under_max_scroll_index -= children_data[max_scroll_index++].height; + while (max_scroll_index > 0) { + max_scroll_index--; + Control* child = Object::cast_to<Control>(get_child(max_scroll_index)); + if (child != nullptr) { + height_under_max_scroll_index += child->get_size().height; /* Spacing is ignored */ + + if (height_under_max_scroll_index > max_height) { + if (max_scroll_index + 1 < child_count) { + max_scroll_index++; + } + break; + } + } } } } @@ -41,28 +65,34 @@ Error GUIListBox::_calculate_child_arrangement() { ERR_FAIL_NULL_V(scrollbar, FAILED); scrollbar->set_limits(0, max_scroll_index, false); - scrollbar->emit_value_changed(); scrollbar->set_visible(max_scroll_index > 0); + set_scroll_index(scrollbar->get_value(), signal); + return OK; } Error GUIListBox::_update_child_positions() { - ERR_FAIL_NULL_V(gui_listbox, FAILED); + const int32_t child_count = get_child_count(); + const real_t max_height = get_size().height; - if (children_data.empty()) { - return OK; - } + real_t height = 0.0f; - const real_t max_height = get_size().height; - const real_t scroll_pos = children_data[scroll_index].start_pos; + const int32_t child_scroll_index = fixed ? 0 : scroll_index; - for (int32_t index = 0; index < children_data.size(); ++index) { - child_data_t const& data = children_data[index]; - if (index < scroll_index || data.start_pos + data.height > scroll_pos + max_height) { - data.child->set_position({ 0.0f, max_height + 10.0f }); - } else { - data.child->set_position({ 0.0f, data.start_pos - scroll_pos }); + for (int32_t index = 0; index < child_count; ++index) { + Control* child = Object::cast_to<Control>(get_child(index)); + + if (child != nullptr) { + if (index < child_scroll_index) { + child->hide(); + } else { + child->set_position({ 0.0f, height }); + + height += child->get_size().height; /* Spacing is ignored */ + + child->set_visible(height <= max_height); + } } } @@ -71,28 +101,36 @@ Error GUIListBox::_update_child_positions() { void GUIListBox::_bind_methods() { OV_BIND_METHOD(GUIListBox::clear); - OV_BIND_METHOD(GUIListBox::clear_children); + OV_BIND_METHOD(GUIListBox::clear_children, { "remaining_child_count" }, DEFVAL(0)); OV_BIND_METHOD(GUIListBox::get_scroll_index); - OV_BIND_METHOD(GUIListBox::set_scroll_index, { "new_scroll_index" }); + OV_BIND_METHOD(GUIListBox::set_scroll_index, { "new_scroll_index", "signal" }, DEFVAL(true)); OV_BIND_METHOD(GUIListBox::get_max_scroll_index); + OV_BIND_METHOD(GUIListBox::is_fixed); + OV_BIND_METHOD(GUIListBox::get_fixed_item_count); + OV_BIND_METHOD(GUIListBox::get_fixed_visible_items); + OV_BIND_METHOD(GUIListBox::get_fixed_item_height); + OV_BIND_METHOD(GUIListBox::set_fixed, { "item_count", "item_height", "signal" }, DEFVAL(true)); + OV_BIND_METHOD(GUIListBox::unset_fixed, { "signal" }, DEFVAL(true)); + OV_BIND_METHOD(GUIListBox::get_gui_listbox_name); OV_BIND_METHOD(GUIListBox::get_scrollbar); + + ADD_SIGNAL(MethodInfo(_signal_scroll_index_changed(), PropertyInfo(Variant::INT, "value"))); } void GUIListBox::_notification(int what) { switch (what) { case NOTIFICATION_SORT_CHILDREN: { - _calculate_child_arrangement(); + _calculate_max_scroll_index(!fixed); } break; } } GUIListBox::GUIListBox() - : gui_listbox { nullptr }, scrollbar { nullptr }, children_data {}, scroll_index { 0 }, max_scroll_index { 0 } { - set_clip_contents(true); -} + : gui_listbox { nullptr }, scrollbar { nullptr }, scroll_index { 0 }, max_scroll_index { 0 }, + fixed { false }, fixed_item_count { 0 }, fixed_visible_items { 0 }, fixed_item_height { 0.0f } {} Vector2 GUIListBox::_get_minimum_size() const { if (gui_listbox != nullptr) { @@ -111,14 +149,18 @@ Vector2 GUIListBox::_get_minimum_size() const { void GUIListBox::_gui_input(godot::Ref<godot::InputEvent> const& event) { ERR_FAIL_NULL(event); + if (scrollbar == nullptr) { + return; + } + Ref<InputEventMouseButton> mb = event; if (mb.is_valid()) { if (mb->is_pressed()) { if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_UP) { - set_scroll_index(scroll_index - 1); + scrollbar->decrement_value(); } else if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_DOWN) { - set_scroll_index(scroll_index + 1); + scrollbar->increment_value(); } else { return; } @@ -129,9 +171,14 @@ void GUIListBox::_gui_input(godot::Ref<godot::InputEvent> const& event) { void GUIListBox::clear() { gui_listbox = nullptr; - children_data.clear(); scroll_index = 0; max_scroll_index = 0; + + fixed = false; + fixed_item_count = 0; + fixed_visible_items = 0; + fixed_item_height = 0.0f; + clear_children(); if (scrollbar != nullptr) { remove_child(scrollbar); @@ -139,27 +186,55 @@ void GUIListBox::clear() { } } -void GUIListBox::clear_children() { - int32_t child_count = get_child_count(); - while (child_count > 0) { - Node* child = get_child(--child_count); - if (child != scrollbar) { - remove_child(child); - } +void GUIListBox::clear_children(int32_t remaining_child_count) { + ERR_FAIL_COND(remaining_child_count < 0); + + int32_t child_index = get_child_count(); + + while (child_index > remaining_child_count) { + remove_child(get_child(--child_index)); } + if (scrollbar != nullptr) { scrollbar->set_value(0); } } -void GUIListBox::set_scroll_index(int32_t new_scroll_index) { +void GUIListBox::set_scroll_index(int32_t new_scroll_index, bool signal) { + const int32_t old_scroll_index = scroll_index; + scroll_index = std::clamp(new_scroll_index, 0, max_scroll_index); + if (scrollbar != nullptr && scrollbar->get_value() != scroll_index) { scrollbar->set_value(scroll_index, false); } + + if (signal && scroll_index != old_scroll_index) { + emit_signal(_signal_scroll_index_changed(), scroll_index); + } + _update_child_positions(); } +Error GUIListBox::set_fixed(int32_t item_count, real_t item_height, bool signal) { + fixed = true; + + fixed_item_count = item_count; + fixed_item_height = item_height; + + return _calculate_max_scroll_index(signal); +} + +Error GUIListBox::unset_fixed(bool signal) { + ERR_FAIL_COND_V(!fixed, FAILED); + + fixed = false; + fixed_item_count = 0; + fixed_item_height = 0.0f; + + return _calculate_max_scroll_index(signal); +} + Error GUIListBox::set_gui_listbox(GUI::ListBox const* new_gui_listbox) { if (gui_listbox == new_gui_listbox) { return OK; @@ -192,7 +267,7 @@ Error GUIListBox::set_gui_listbox(GUI::ListBox const* new_gui_listbox) { if (scrollbar_control != nullptr) { scrollbar = Object::cast_to<GUIScrollbar>(scrollbar_control); if (scrollbar != nullptr) { - add_child(scrollbar); + add_child(scrollbar, false, INTERNAL_MODE_FRONT); const Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size()); scrollbar->set_position({ size.width, 0.0f }); diff --git a/extension/src/openvic-extension/classes/GUIListBox.hpp b/extension/src/openvic-extension/classes/GUIListBox.hpp index 9d49840..775feae 100644 --- a/extension/src/openvic-extension/classes/GUIListBox.hpp +++ b/extension/src/openvic-extension/classes/GUIListBox.hpp @@ -14,18 +14,18 @@ namespace OpenVic { GUIScrollbar* scrollbar; - struct child_data_t { - Control* child; - real_t start_pos, height; - }; - - std::vector<child_data_t> children_data; - /* The children_data index of the topmost visible child element. */ int32_t PROPERTY(scroll_index); int32_t PROPERTY(max_scroll_index); - godot::Error _calculate_child_arrangement(); + bool PROPERTY_CUSTOM_PREFIX(fixed, is); + int32_t PROPERTY(fixed_item_count); + int32_t PROPERTY(fixed_visible_items); + real_t PROPERTY(fixed_item_height); + + static godot::StringName const& _signal_scroll_index_changed(); + + godot::Error _calculate_max_scroll_index(bool signal); godot::Error _update_child_positions(); protected: @@ -42,10 +42,13 @@ namespace OpenVic { /* Reset gui_listbox to nullptr, and remove all child elements. */ void clear(); - /* Remove all child elements except for the scrollbar. */ - void clear_children(); + /* Remove child elements until there are remaining_child_count left excluding the scrollbar. */ + void clear_children(int32_t remaining_child_count = 0); + + void set_scroll_index(int32_t new_scroll_index, bool signal = true); - void set_scroll_index(int32_t new_scroll_index); + godot::Error set_fixed(int32_t item_count, real_t item_height, bool signal = true); + godot::Error unset_fixed(bool signal = true); /* Set the GUI::ListBox. This does not affect any existing child elements. */ godot::Error set_gui_listbox(GUI::ListBox const* new_gui_listbox); diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.cpp b/extension/src/openvic-extension/classes/GUIScrollbar.cpp index cdfa2fd..dab74d8 100644 --- a/extension/src/openvic-extension/classes/GUIScrollbar.cpp +++ b/extension/src/openvic-extension/classes/GUIScrollbar.cpp @@ -36,6 +36,8 @@ void GUIScrollbar::_bind_methods() { OV_BIND_METHOD(GUIScrollbar::get_min_value); OV_BIND_METHOD(GUIScrollbar::get_max_value); OV_BIND_METHOD(GUIScrollbar::set_value, { "new_value", "signal" }, DEFVAL(true)); + OV_BIND_METHOD(GUIScrollbar::increment_value, { "signal" }, DEFVAL(true)); + OV_BIND_METHOD(GUIScrollbar::decrement_value, { "signal" }, DEFVAL(true)); OV_BIND_METHOD(GUIScrollbar::set_value_as_ratio, { "new_ratio", "signal" }, DEFVAL(true)); OV_BIND_METHOD(GUIScrollbar::is_range_limited); @@ -466,6 +468,14 @@ void GUIScrollbar::set_value(int32_t new_value, bool signal) { } } +void GUIScrollbar::increment_value(bool signal) { + set_value(value + 1, signal); +} + +void GUIScrollbar::decrement_value(bool signal) { + set_value(value - 1, signal); +} + float GUIScrollbar::get_value_as_ratio() const { return _value_to_ratio(value); } diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.hpp b/extension/src/openvic-extension/classes/GUIScrollbar.hpp index c5b476a..16b2e00 100644 --- a/extension/src/openvic-extension/classes/GUIScrollbar.hpp +++ b/extension/src/openvic-extension/classes/GUIScrollbar.hpp @@ -93,6 +93,8 @@ namespace OpenVic { godot::String get_gui_scrollbar_name() const; void set_value(int32_t new_value, bool signal = true); + void increment_value(bool signal = true); + void decrement_value(bool signal = true); float get_value_as_ratio() const; void set_value_as_ratio(float new_ratio, bool signal = true); |