diff options
author | Spartan322 <Megacake1234@gmail.com> | 2023-12-30 06:32:13 +0100 |
---|---|---|
committer | Spartan322 <Megacake1234@gmail.com> | 2024-05-07 00:07:50 +0200 |
commit | 7def4dd2e7987c20163c6a419bcc0506b5a670d9 (patch) | |
tree | 671f5b94ab2f0d9d1a59b7fa11ac393e93101958 /extension | |
parent | bf3df0ee900f406a5a2aa56609ecb89c67055351 (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')
-rw-r--r-- | extension/src/openvic-extension/classes/GUINode.cpp | 159 | ||||
-rw-r--r-- | extension/src/openvic-extension/classes/GUINode.hpp | 30 |
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; }; } |