aboutsummaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
author Hop311 <Hop3114@gmail.com>2024-03-23 10:46:13 +0100
committer GitHub <noreply@github.com>2024-03-23 10:46:13 +0100
commit9e99050cc93f4f482e70c218eb99c004d74a66d1 (patch)
treebb1e7fbe1e9f2c082ca82318911a09191455fd04 /extension
parent8088824d7b78311c0613b3876ba9555fbbe96c17 (diff)
parent0cd976341792ea30ca41e09d9c238e4e342402cd (diff)
Merge pull request #214 from OpenVicProject/pop-menu
Assorted UI Changes (in preparation for Population Menu)
Diffstat (limited to 'extension')
m---------extension/deps/openvic-simulation0
-rw-r--r--extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp44
-rw-r--r--extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp5
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.cpp40
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.hpp10
-rw-r--r--extension/src/openvic-extension/classes/GUIListBox.cpp169
-rw-r--r--extension/src/openvic-extension/classes/GUIListBox.hpp25
-rw-r--r--extension/src/openvic-extension/classes/GUIScrollbar.cpp12
-rw-r--r--extension/src/openvic-extension/classes/GUIScrollbar.hpp2
-rw-r--r--extension/src/openvic-extension/utility/UITools.cpp18
10 files changed, 232 insertions, 93 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation
-Subproject 2c892c99a6647be15ef23cabf6cc40f08769283
+Subproject c11d262a4d2c987c8cf8e0d4b24929cbe56bb28
diff --git a/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp b/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
index 7cbc0a6..f6f3c22 100644
--- a/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
@@ -39,14 +39,15 @@ void GFXButtonStateTexture::_bind_methods() {
OV_BIND_METHOD(GFXButtonStateTexture::set_button_state, { "new_button_state" });
OV_BIND_METHOD(GFXButtonStateTexture::get_button_state);
- OV_BIND_SMETHOD(button_state_to_theme_name, { "button_state" });
- OV_BIND_METHOD(GFXButtonStateTexture::get_button_state_theme);
+ OV_BIND_SMETHOD(button_state_to_name, { "button_state" });
+ OV_BIND_METHOD(GFXButtonStateTexture::get_button_state_name);
OV_BIND_METHOD(GFXButtonStateTexture::generate_state_image, { "source_image" });
BIND_ENUM_CONSTANT(HOVER);
BIND_ENUM_CONSTANT(PRESSED);
BIND_ENUM_CONSTANT(DISABLED);
+ BIND_ENUM_CONSTANT(SELECTED);
}
GFXButtonStateTexture::GFXButtonStateTexture() : button_state { HOVER }, state_image {}, state_texture {} {}
@@ -67,7 +68,10 @@ Ref<GFXButtonStateTexture> GFXButtonStateTexture::make_gfx_button_state_texture(
}
void GFXButtonStateTexture::set_button_state(ButtonState new_button_state) {
- ERR_FAIL_COND(new_button_state != HOVER && new_button_state != PRESSED && new_button_state != DISABLED);
+ ERR_FAIL_COND(
+ new_button_state != HOVER && new_button_state != PRESSED &&
+ new_button_state != DISABLED && new_button_state != SELECTED
+ );
button_state = new_button_state;
}
@@ -106,8 +110,15 @@ Error GFXButtonStateTexture::generate_state_image(
const float luma = colour.get_luminance();
return { luma, luma, luma, colour.a };
};
+ static constexpr auto selected_colour = [](Color const& colour) -> Color {
+ return { std::max(colour.r - 0.3f, 0.0f), std::max(colour.g - 0.3f, 0.0f), std::max(colour.b - 0.3f, 0.0f), colour.a };
+ };
- const auto colour_func = button_state == HOVER ? hover_colour : button_state == PRESSED ? pressed_colour : disabled_colour;
+ const auto colour_func =
+ button_state == HOVER ? hover_colour :
+ button_state == PRESSED ? pressed_colour :
+ button_state == DISABLED ? disabled_colour :
+ selected_colour;
for (Vector2i point { 0, 0 }; point.y < state_image->get_height(); ++point.y) {
for (point.x = 0; point.x < state_image->get_width(); ++point.x) {
@@ -123,25 +134,28 @@ Error GFXButtonStateTexture::generate_state_image(
return OK;
}
-StringName const& GFXButtonStateTexture::button_state_to_theme_name(ButtonState button_state) {
- static const StringName theme_name_hover = "hover";
- static const StringName theme_name_pressed = "pressed";
- static const StringName theme_name_disabled = "disabled";
- static const StringName theme_name_error = "INVALID BUTTON STATE";
+StringName const& GFXButtonStateTexture::button_state_to_name(ButtonState button_state) {
+ static const StringName name_hover = "hover";
+ static const StringName name_pressed = "pressed";
+ static const StringName name_disabled = "disabled";
+ static const StringName name_selected = "selected";
+ static const StringName name_error = "INVALID BUTTON STATE";
switch (button_state) {
case HOVER:
- return theme_name_hover;
+ return name_hover;
case PRESSED:
- return theme_name_pressed;
+ return name_pressed;
case DISABLED:
- return theme_name_disabled;
+ return name_disabled;
+ case SELECTED:
+ return name_selected;
default:
- return theme_name_error;
+ return name_error;
}
}
-StringName const& GFXButtonStateTexture::get_button_state_theme() const {
- return button_state_to_theme_name(button_state);
+StringName const& GFXButtonStateTexture::get_button_state_name() const {
+ return button_state_to_name(button_state);
}
void GFXButtonStateHavingTexture::_bind_methods() {
diff --git a/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp b/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
index b57ea46..d97ca2b 100644
--- a/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
@@ -32,6 +32,7 @@ namespace OpenVic {
HOVER,
PRESSED,
DISABLED,
+ SELECTED,
BUTTON_STATE_COUNT
};
@@ -62,8 +63,8 @@ namespace OpenVic {
godot::Vector2i const& new_cornered_tile_border_size
);
- static godot::StringName const& button_state_to_theme_name(ButtonState button_state);
- godot::StringName const& get_button_state_theme() const;
+ static godot::StringName const& button_state_to_name(ButtonState button_state);
+ godot::StringName const& get_button_state_name() const;
};
class GFXButtonStateHavingTexture : public GFXCorneredTileSupportingTexture {
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
index f4c7851..10a2cb5 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
@@ -23,7 +23,7 @@ StringName const& GFXPieChartTexture::_slice_weight_key() {
return slice_weight_key;
}
-static constexpr float PI = std::numbers::pi_v<float>;
+static constexpr float TWO_PI = 2.0f * std::numbers::pi_v<float>;
Error GFXPieChartTexture::_generate_pie_chart_image() {
ERR_FAIL_NULL_V(gfx_pie_chart, FAILED);
@@ -31,40 +31,57 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
gfx_pie_chart->get_size() <= 0, FAILED,
vformat("Invalid GFX::PieChart size for GFXPieChartTexture - %d", gfx_pie_chart->get_size())
);
+
const int32_t pie_chart_size = 2 * gfx_pie_chart->get_size();
+
/* Whether we've already set the ImageTexture to an image of the right dimensions,
* and so can update it without creating and setting a new image, or not. */
const bool can_update = pie_chart_image.is_valid() && pie_chart_image->get_width() == pie_chart_size
&& pie_chart_image->get_height() == pie_chart_size;
+
if (!can_update) {
pie_chart_image = Image::create(pie_chart_size, pie_chart_size, false, Image::FORMAT_RGBA8);
ERR_FAIL_NULL_V(pie_chart_image, FAILED);
}
static const Color background_colour { 0.0f, 0.0f, 0.0f, 0.0f };
+
if (!slices.empty()) {
const float pie_chart_radius = gfx_pie_chart->get_size();
+
const Vector2 centre_translation = Vector2 { 0.5f, 0.5f } - static_cast<Vector2>(pie_chart_image->get_size()) * 0.5f;
+
for (Vector2i point { 0, 0 }; point.y < pie_chart_image->get_height(); ++point.y) {
+
for (point.x = 0; point.x < pie_chart_image->get_width(); ++point.x) {
+
const Vector2 offset = centre_translation + point;
+
if (offset.length() <= pie_chart_radius) {
- float theta = 0.5f * PI + atan2(offset.y, offset.x);
+
+ /* Calculate the anti-clockwise angle between the point and the centre of the image.
+ * The y coordinate is negated as the image coordinate system's y increases downwards. */
+ float theta = atan2(-offset.y, offset.x);
if (theta < 0.0f) {
- theta += 2.0f * PI;
+ theta += TWO_PI;
}
+
/* Rescale angle so that total_weight is a full rotation. */
- theta *= total_weight / (2.0f * PI);
+ theta *= total_weight / TWO_PI;
+
+ /* Default to the first colour in case theta never reaches 0 due to floating point inaccuracy. */
Color colour = slices.front().first;
+
/* Find the slice theta lies in. */
for (slice_t const& slice : slices) {
- if (theta <= slice.second) {
+ theta -= slice.second;
+
+ if (theta <= 0.0f) {
colour = slice.first;
break;
- } else {
- theta -= slice.second;
}
}
+
pie_chart_image->set_pixelv(point, colour);
} else {
pie_chart_image->set_pixelv(point, background_colour);
@@ -72,7 +89,9 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
}
}
} else {
+
pie_chart_image->fill(background_colour);
+
}
if (can_update) {
@@ -80,10 +99,11 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
} else {
set_image(pie_chart_image);
}
+
return OK;
}
-Error GFXPieChartTexture::set_slices_array(TypedArray<Dictionary> const& new_slices) {
+Error GFXPieChartTexture::set_slices_array(godot_pie_chart_data_t const& new_slices) {
slices.clear();
total_weight = 0.0f;
for (int32_t i = 0; i < new_slices.size(); ++i) {
@@ -92,7 +112,9 @@ Error GFXPieChartTexture::set_slices_array(TypedArray<Dictionary> const& new_sli
!slice_dict.has(_slice_colour_key()) || !slice_dict.has(_slice_weight_key()), vformat("Invalid slice keys at index %d", i)
);
slice_t slice = std::make_pair(slice_dict[_slice_colour_key()], slice_dict[_slice_weight_key()]);
- ERR_CONTINUE_MSG(slice.second <= 0.0f, vformat("Invalid slice values at index %d", i));
+ ERR_CONTINUE_MSG(
+ slice.second <= 0.0f, vformat("Invalid slice values at index %d \"%s\"", i, slice_dict[_slice_identifier_key()])
+ );
total_weight += slice.second;
slices.emplace_back(std::move(slice));
}
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
index f8279e4..abeca1e 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
@@ -29,15 +29,17 @@ namespace OpenVic {
public:
GFXPieChartTexture();
+ using godot_pie_chart_data_t = godot::TypedArray<godot::Dictionary>;
+
/* Set slices given an Array of Dictionaries, each with the following key-value entries:
* - colour: Color
* - weight: float */
- godot::Error set_slices_array(godot::TypedArray<godot::Dictionary> const& new_slices);
+ godot::Error set_slices_array(godot_pie_chart_data_t const& new_slices);
/* Generate slice data from a distribution of HasIdentifierAndColour derived objects, sorted by their weight.
* The resulting Array of Dictionaries can be used as an argument for set_slices_array. */
template<std::derived_from<HasIdentifierAndColour> T>
- static godot::TypedArray<godot::Dictionary> distribution_to_slices_array(fixed_point_map_t<T const*> const& dist) {
+ static godot_pie_chart_data_t distribution_to_slices_array(fixed_point_map_t<T const*> const& dist) {
using namespace godot;
using entry_t = std::pair<T const*, fixed_point_t>;
std::vector<entry_t> sorted_dist;
@@ -49,9 +51,9 @@ namespace OpenVic {
sorted_dist.push_back(entry);
}
std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool {
- return lhs.second < rhs.second;
+ return lhs.first < rhs.first;
});
- TypedArray<Dictionary> array;
+ godot_pie_chart_data_t array;
for (auto const& [key, val] : sorted_dist) {
Dictionary sub_dict;
sub_dict[_slice_identifier_key()] = Utilities::std_view_to_godot_string(key->get_identifier());
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 93eb00b..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);
@@ -394,7 +396,7 @@ Error GUIScrollbar::set_gui_scrollbar(GUI::Scrollbar const* new_gui_scrollbar) {
for (GFXButtonStateTexture::ButtonState state : { HOVER, PRESSED }) {
ERR_FAIL_NULL_V_MSG(texture->get_button_state_texture(state), false, vformat(
"Failed to generate %s texture for %s element %s for GUIScrollbar %s!",
- GFXButtonStateTexture::button_state_to_theme_name(state), target, element_name, gui_scrollbar_name
+ GFXButtonStateTexture::button_state_to_name(state), target, element_name, gui_scrollbar_name
));
}
}
@@ -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);
diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp
index c00b64d..4af2b74 100644
--- a/extension/src/openvic-extension/utility/UITools.cpp
+++ b/extension/src/openvic-extension/utility/UITools.cpp
@@ -177,8 +177,13 @@ static bool generate_icon(generate_gui_args_t&& args) {
godot_progress_bar, false, vformat("Failed to create TextureProgressBar for GUI icon %s", icon_name)
);
+ static constexpr double MIN_VALUE = 0.0, MAX_VALUE = 1.0;
+ static constexpr uint32_t STEPS = 100;
+
godot_progress_bar->set_nine_patch_stretch(true);
- godot_progress_bar->set_max(1.0);
+ godot_progress_bar->set_step((MAX_VALUE - MIN_VALUE) / STEPS);
+ godot_progress_bar->set_min(MIN_VALUE);
+ godot_progress_bar->set_max(MAX_VALUE);
GFX::ProgressBar const* progress_bar = icon.get_sprite()->cast_to<GFX::ProgressBar>();
@@ -342,11 +347,11 @@ static bool generate_button(generate_gui_args_t&& args) {
Ref<GFXButtonStateTexture> button_state_texture = texture->get_button_state_texture(button_state);
if (button_state_texture.is_valid()) {
ret &= add_theme_stylebox(
- godot_button, button_state_texture->get_button_state_theme(), button_state_texture
+ godot_button, button_state_texture->get_button_state_name(), button_state_texture
);
} else {
UtilityFunctions::push_error(
- "Failed to make ", GFXButtonStateTexture::button_state_to_theme_name(button_state),
+ "Failed to make ", GFXButtonStateTexture::button_state_to_name(button_state),
" GFXButtonStateTexture for GUI button ", button_name
);
ret = false;
@@ -435,7 +440,12 @@ static bool generate_text(generate_gui_args_t&& args) {
ERR_FAIL_NULL_V_MSG(godot_label, false, vformat("Failed to create Label for GUI text %s", text_name));
godot_label->set_text(std_view_to_godot_string(text.get_text()));
- godot_label->set_custom_minimum_size(Utilities::to_godot_fvec2(text.get_max_size()));
+
+ static const Vector2 default_padding { 1.0f, 0.0f };
+ const Vector2 border_size = Utilities::to_godot_fvec2(text.get_border_size()) + default_padding;
+ const Vector2 max_size = Utilities::to_godot_fvec2(text.get_max_size());
+ godot_label->set_position(godot_label->get_position() + border_size);
+ godot_label->set_custom_minimum_size(max_size - 2 * border_size);
using enum GUI::AlignedElement::format_t;
static const ordered_map<GUI::AlignedElement::format_t, HorizontalAlignment> format_map {