aboutsummaryrefslogtreecommitdiff
path: root/extension/src
diff options
context:
space:
mode:
author Spartan322 <Megacake1234@gmail.com>2023-12-30 06:32:13 +0100
committer Spartan322 <Megacake1234@gmail.com>2024-05-07 00:07:50 +0200
commit7def4dd2e7987c20163c6a419bcc0506b5a670d9 (patch)
tree671f5b94ab2f0d9d1a59b7fa11ac393e93101958 /extension/src
parentbf3df0ee900f406a5a2aa56609ecb89c67055351 (diff)
Improve map view and game panel user experience
Minimizes panel mouse obstruction to scripted panel images Prevents map view from hovering inside UI elements Unsets province hover when not over provinces Add `GUINode.click_mask` Prevents mouse interactions not within click_mask Add `GUINode.set_click_mask_from_nodepaths` Generates click_mask from paths relating to GUINode textures Sets nodepaths to MOUSE_FILTER_IGNORE Add CanvasLayer parent to GameSession UI nodes Set mouse_force_pass_scroll_events to GameSession UI nodes Set MapControlPanel mouse_filter to default (MOUSE_FILTER_STOP) Move MapView mouse viewport changes to _input Move MapView _action_drag released check to _input Move MapView processing to _process Remove viewport and window notifications Disable if window is not focused or input is handled: MapView mouse interactions (including edge scrolling and drag panning) MapView province hover Set mouse_filter to MOUSE_FILTER_IGNORE for ProvinceOverviewPanel province_view panel Set ProvinceOverviewPanel click_mask path to `province_view/background` Set mouse_filter to MOUSE_FILTER_IGNORE for Topbar topbar panel Set Topbar click_mask path to `topbar/topbar_bg` and `topbar/topbar_paper`
Diffstat (limited to 'extension/src')
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp159
-rw-r--r--extension/src/openvic-extension/classes/GUINode.hpp30
2 files changed, 185 insertions, 4 deletions
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
index 73ebb0c..c9af7e2 100644
--- a/extension/src/openvic-extension/classes/GUINode.cpp
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -1,7 +1,35 @@
#include "GUINode.hpp"
+#include <limits>
+
+#include <godot_cpp/classes/bit_map.hpp>
+#include <godot_cpp/classes/button.hpp>
+#include <godot_cpp/classes/canvas_item.hpp>
+#include <godot_cpp/classes/check_box.hpp>
+#include <godot_cpp/classes/control.hpp>
+#include <godot_cpp/classes/image.hpp>
+#include <godot_cpp/classes/label.hpp>
+#include <godot_cpp/classes/node.hpp>
+#include <godot_cpp/classes/object.hpp>
+#include <godot_cpp/classes/panel.hpp>
+#include <godot_cpp/classes/ref.hpp>
+#include <godot_cpp/classes/style_box.hpp>
#include <godot_cpp/classes/style_box_texture.hpp>
+#include <godot_cpp/classes/texture2d.hpp>
+#include <godot_cpp/classes/texture_progress_bar.hpp>
+#include <godot_cpp/classes/texture_rect.hpp>
+#include <godot_cpp/core/defs.hpp>
+#include <godot_cpp/core/error_macros.hpp>
+#include <godot_cpp/core/object.hpp>
+#include <godot_cpp/core/property_info.hpp>
+#include <godot_cpp/variant/node_path.hpp>
+#include <godot_cpp/variant/rect2.hpp>
+#include <godot_cpp/variant/string.hpp>
+#include <godot_cpp/variant/string_name.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
+#include <godot_cpp/variant/variant.hpp>
+#include <godot_cpp/variant/vector2.hpp>
+#include <godot_cpp/variant/vector2i.hpp>
#include "openvic-extension/utility/ClassBindings.hpp"
#include "openvic-extension/utility/UITools.hpp"
@@ -31,6 +59,15 @@ void GUINode::_bind_methods() {
OV_BIND_METHOD(GUINode::add_gui_element, { "gui_scene", "gui_element", "name" }, DEFVAL(String {}));
OV_BIND_SMETHOD(get_gui_position, { "gui_scene", "gui_position" });
+ OV_BIND_METHOD(GUINode::get_click_mask);
+ OV_BIND_METHOD(GUINode::set_click_mask, { "mask" });
+ ADD_PROPERTY(
+ PropertyInfo(Variant::OBJECT, "click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask"
+ );
+
+ OV_BIND_METHOD(GUINode::set_click_mask_from_nodepaths, { "paths" });
+ OV_BIND_METHOD(GUINode::update_click_mask);
+
#define GET_BINDINGS(type, name) \
OV_BIND_SMETHOD(get_##name##_from_node, { "node" }); \
OV_BIND_METHOD(GUINode::get_##name##_from_nodepath, { "path" });
@@ -56,7 +93,7 @@ GUINode::GUINode() {
set_anchors_and_offsets_preset(PRESET_FULL_RECT);
set_h_grow_direction(GROW_DIRECTION_BOTH);
set_v_grow_direction(GROW_DIRECTION_BOTH);
- set_mouse_filter(MOUSE_FILTER_IGNORE);
+ set_mouse_filter(MOUSE_FILTER_STOP);
}
Control* GUINode::generate_gui_element(String const& gui_scene, String const& gui_element, String const& name) {
@@ -196,3 +233,123 @@ String GUINode::format_province_name(String const& province_identifier) {
static const String province_prefix = "PROV";
return province_prefix + province_identifier;
}
+
+Ref<BitMap> GUINode::get_click_mask() const {
+ return _click_mask;
+}
+
+void GUINode::set_click_mask(Ref<BitMap> const& mask) {
+ if (_click_mask == mask) {
+ return;
+ }
+ _click_mask = mask;
+ queue_redraw();
+ update_minimum_size();
+}
+
+bool GUINode::_update_click_mask_for(Ref<Image> const& img, int index) {
+ ERR_FAIL_INDEX_V(index, _mask_controls.size(), false);
+ Control* control = _mask_controls[index];
+ if (!UtilityFunctions::is_instance_valid(control) && !control->is_inside_tree()) {
+ _mask_controls.remove_at(index);
+ return false;
+ }
+ ERR_FAIL_COND_V(img.is_null(), false);
+ Ref<Texture2D> texture = get_texture_from_node(control);
+ ERR_FAIL_COND_V(texture.is_null(), false);
+ Ref<Image> texture_img = texture->get_image();
+ if (img->is_empty()) {
+ img->copy_from(texture_img);
+ } else {
+ if (img->get_format() != texture_img->get_format()) {
+ img->convert(texture_img->get_format());
+ }
+ Vector2i img_size = img->get_size();
+ Vector2i total_size = control->get_screen_position() + texture_img->get_size();
+ Vector2i new_img_size = img_size.max(total_size);
+ if (new_img_size != img_size) {
+ img->crop(new_img_size.x, new_img_size.y);
+ }
+ img->blend_rect(texture_img, texture_img->get_used_rect(), control->get_position());
+ }
+ ERR_FAIL_COND_V(img->is_empty(), false);
+ return true;
+}
+
+void GUINode::update_click_mask() {
+ static constexpr real_t max_real = std::numeric_limits<real_t>::max();
+ static const Point2 max_point { max_real, max_real };
+ if (_mask_controls.size() == 0) {
+ return;
+ }
+
+ if (_click_mask.is_null()) {
+ _click_mask.instantiate();
+ }
+ Ref<Image> img;
+ img.instantiate();
+ Vector2 size = get_size();
+ img->create(size.x, size.y, false, Image::Format::FORMAT_RGBA8);
+ Point2 highest_position = { max_real, max_real };
+ for (int index = 0; index < _mask_controls.size(); index++) {
+ if (!_update_click_mask_for(img, index)) {
+ continue;
+ }
+ Vector2 screen_pos = _mask_controls[index]->get_screen_position();
+ highest_position = highest_position.min(screen_pos);
+ }
+ ERR_FAIL_COND(img.is_null());
+ ERR_FAIL_COND(highest_position == max_point);
+ _texture_region = Rect2(Point2(), img->get_size());
+ _position_rect = Rect2(highest_position, _texture_region.get_size());
+ _click_mask->create_from_image_alpha(img);
+ queue_redraw();
+ update_minimum_size();
+}
+
+void GUINode::set_click_mask_from_nodepaths(TypedArray<NodePath> const& paths) {
+ // TODO: Update to use https://github.com/godotengine/godot/pull/90916
+ // for(godot::Control* control : _mask_controls) {
+ // control->set_mouse_filter(Control::MouseFilter::MOUSE_FILTER_STOP);
+ // }
+ _mask_controls.clear();
+ for (int index = 0; index < paths.size(); index++) {
+ Control* control = _cast_node<Control>(get_node_internal(paths[index]));
+ ERR_CONTINUE(control == nullptr);
+ control->set_mouse_filter(Control::MouseFilter::MOUSE_FILTER_IGNORE);
+ _mask_controls.push_back(control);
+ }
+ update_click_mask();
+}
+
+bool GUINode::_has_point(godot::Vector2 const& p_point) const {
+ if (!_click_mask.is_valid()) {
+ return Control::_has_point(p_point);
+ }
+
+ Point2 point = p_point;
+ Rect2 rect;
+ Size2 mask_size = _click_mask->get_size();
+
+ if (!_position_rect.has_area()) {
+ rect.size = mask_size;
+ } else {
+ // we need to transform the point from our scaled / translated image back to our mask image
+ Point2 ofs = _position_rect.position;
+ Size2 scale = mask_size / _position_rect.size;
+
+ // offset and scale the new point position to adjust it to the bitmask size
+ point -= ofs;
+ point *= scale;
+
+ // finally, we need to check if the point is inside a rectangle with a position >= 0,0 and a size <= mask_size
+ rect.position = _texture_region.position.min(Point2 {});
+ rect.size = mask_size.min(_texture_region.size);
+ }
+
+ if (!rect.has_point(point)) {
+ return false;
+ }
+
+ return _click_mask->get_bitv(point);
+}
diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp
index 3dbe403..8d926cc 100644
--- a/extension/src/openvic-extension/classes/GUINode.hpp
+++ b/extension/src/openvic-extension/classes/GUINode.hpp
@@ -1,17 +1,27 @@
#pragma once
+#include <godot_cpp/classes/bit_map.hpp>
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/check_box.hpp>
+#include <godot_cpp/classes/control.hpp>
+#include <godot_cpp/classes/image.hpp>
+#include <godot_cpp/classes/input_event.hpp>
#include <godot_cpp/classes/label.hpp>
+#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/panel.hpp>
+#include <godot_cpp/classes/ref.hpp>
+#include <godot_cpp/classes/texture2d.hpp>
#include <godot_cpp/classes/texture_progress_bar.hpp>
#include <godot_cpp/classes/texture_rect.hpp>
+#include <godot_cpp/templates/vector.hpp>
+#include <godot_cpp/variant/node_path.hpp>
+#include <godot_cpp/variant/rect2.hpp>
+#include <godot_cpp/variant/string.hpp>
+#include <godot_cpp/variant/vector2.hpp>
-#include <openvic-simulation/interface/GUI.hpp>
-
-#include "openvic-extension/classes/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GFXSpriteTexture.hpp"
#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
@@ -20,6 +30,11 @@ namespace OpenVic {
class GUINode : public godot::Control {
GDCLASS(GUINode, godot::Control)
+ godot::Ref<godot::BitMap> _click_mask;
+ godot::Vector<Control*> _mask_controls;
+ godot::Rect2 _texture_region;
+ godot::Rect2 _position_rect;
+
protected:
static void _bind_methods();
@@ -73,5 +88,14 @@ namespace OpenVic {
static godot::String int_to_formatted_string(int64_t val);
static godot::String float_to_formatted_string(float val, int32_t decimal_places);
static godot::String format_province_name(godot::String const& province_identifier);
+
+ godot::Ref<godot::BitMap> get_click_mask() const;
+ void set_click_mask(godot::Ref<godot::BitMap> const& mask);
+
+ void set_click_mask_from_nodepaths(godot::TypedArray<godot::NodePath> const& paths);
+ bool _update_click_mask_for(godot::Ref<godot::Image> const& img, int index);
+ void update_click_mask();
+
+ bool _has_point(godot::Vector2 const& point) const override;
};
}