diff options
36 files changed, 585 insertions, 119 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ac70a4367..c101a94c88 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,56 +2,168 @@ # Each line is a file pattern followed by one or more owners. # Owners can be @users, @org/teams or emails -/doc/ @godotengine/documentation -doc_classes/* @godotengine/documentation - -# Rendering -/drivers/gl_context/ @reduz -/drivers/gles2/ @reduz -/drivers/gles3/ @reduz - -# Audio -/drivers/alsa/ @marcelofg55 -/drivers/alsamidi/ @marcelofg55 -/drivers/coreaudio/ @marcelofg55 -/drivers/coremidi/ @marcelofg55 -/drivers/pulseaudio/ @marcelofg55 -/drivers/wasapi/ @marcelofg55 -/drivers/winmidi/ @marcelofg55 -/drivers/xaudio2/ @marcelofg55 - -/drivers/unix/ @hpvb -/drivers/windows/ @hpvb - -/editor/icons/ @djrm - -/misc/ @akien-mga - -/modules/bullet/ @AndreaCatania -/modules/csg/ @BastiaanOlij -/modules/enet/ @godotengine/network -/modules/gdnative/*arvr/ @BastiaanOlij -/modules/gdnative/text/ @bruvzg -/modules/gdscript/ @vnen -/modules/mbedtls/ @godotengine/network -/modules/mobile_vr/ @BastiaanOlij -/modules/mono/ @neikeq -/modules/mono/glue/GodotSharp @aaronfranke -/modules/opensimplex/ @JFonS -/modules/regex/ @LeeZH -/modules/text_server_*/ @bruvzg -/modules/upnp/ @godotengine/network -/modules/websocket/ @godotengine/network - -/platform/javascript/ @eska014 -/platform/uwp/ @vnen - -/scene/resources/font.* @bruvzg -/scene/resources/text_line.* @bruvzg -/scene/resources/text_paragraph.* @bruvzg - -/servers/physics*/ @reduz @AndreaCatania -/servers/text_server.* @bruvzg -/servers/visual*/ @reduz - -/thirdparty/ @akien-mga +# Core + +/core/ @godotengine/core +/core/crypto/ @godotengine/network +/core/debugger/ @godotengine/debugger +/core/input/ @godotengine/input + +# Doc + +/doc/ @godotengine/documentation +doc_classes/* @godotengine/documentation + +# Drivers + +## Audio +/drivers/alsa/ @godotengine/audio +/drivers/alsamidi/ @godotengine/audio +/drivers/coreaudio/ @godotengine/audio +/drivers/coremidi/ @godotengine/audio +/drivers/pulseaudio/ @godotengine/audio +/drivers/wasapi/ @godotengine/audio +/drivers/winmidi/ @godotengine/audio +/drivers/xaudio2/ @godotengine/audio + +## Rendering +/drivers/dummy/ @godotengine/rendering +/drivers/spirv-reflect/ @godotengine/rendering +/drivers/vulkan/ @godotengine/rendering + +## OS +/drivers/unix/ @godotengine/_platforms +/drivers/windows/ @godotengine/windows + +# Editor + +/editor/*debugger* @godotengine/debugger +/editor/icons/ @godotengine/usability +/editor/import/ @godotengine/import +/editor/plugins/*2d_*.* @godotengine/2d-editor +/editor/plugins/*3d_*.* @godotengine/3d-editor +/editor/plugins/script_*.* @godotengine/script-editor +/editor/plugins/*shader*.* @godotengine/shaders +/editor/code_editor.* @godotengine/script-editor +/editor/*dock*.* @godotengine/docks +/editor/*shader*.* @godotengine/shaders + +# Main + +/main/ @godotengine/core + +# Misc + +/misc/ @godotengine/buildsystem + +# Modules + +## Audio (+ video) +/modules/minimp3/ @godotengine/audio +/modules/ogg/ @godotengine/audio +/modules/opus/ @godotengine/audio +/modules/stb_vorbis/ @godotengine/audio +/modules/theora/ @godotengine/audio +/modules/vorbis/ @godotengine/audio +/modules/webm/ @godotengine/audio + +## Import +/modules/basis_universal/ @godotengine/import +/modules/bmp/ @godotengine/import +/modules/cvtt/ @godotengine/import +/modules/dds/ @godotengine/import +/modules/etc/ @godotengine/import +/modules/fbx/ @godotengine/import +/modules/gltf/ @godotengine/import +/modules/hdr/ @godotengine/import +/modules/jpg/ @godotengine/import +/modules/pvr/ @godotengine/import +/modules/squish/ @godotengine/import +/modules/svg/ @godotengine/import +/modules/tga/ @godotengine/import +/modules/tinyexr/ @godotengine/import +/modules/webp/ @godotengine/import + +## Network +/modules/enet/ @godotengine/network +/modules/mbedtls/ @godotengine/network +/modules/upnp/ @godotengine/network +/modules/webrtc/ @godotengine/network +/modules/websocket/ @godotengine/network + +## Rendering +/modules/denoise/ @godotengine/rendering +/modules/glslang/ @godotengine/rendering +/modules/lightmapper_rd/ @godotengine/rendering +/modules/meshoptimizer/ @godotengine/rendering +/modules/vhacd/ @godotengine/rendering +/modules/xatlas_unwrap/ @godotengine/rendering + +## Scripting +/modules/gdnative/ @godotengine/gdnative +/modules/gdscript/ @godotengine/gdscript +/modules/jsonrpc/ @godotengine/gdscript +/modules/mono/ @godotengine/mono +/modules/visual_script/ @godotengine/visualscript + +## Text +/modules/freetype/ @godotengine/buildsystem +/modules/gdnative/text/ @godotengine/gui-nodes +/modules/text_server_adv/ @godotengine/gui-nodes +/modules/text_server_fb/ @godotengine/gui-nodes + +## XR +/modules/camera/ @godotengine/xr +/modules/gdnative/xr/ @godotengine/xr +/modules/mobile_vr/ @godotengine/xr +/modules/webxr/ @godotengine/xr + +## Misc +/modules/bullet/ @godotengine/physics +/modules/csg/ @godotengine/3d-nodes +/modules/gdnavigation/ @godotengine/navigation +/modules/gridmap/ @godotengine/3d-nodes +/modules/opensimplex/ @godotengine/3d-nodes +/modules/regex/ @godotengine/core + +# Platform + +/platform/android/ @godotengine/android +/platform/iphone/ @godotengine/iphone +/platform/javascript/ @godotengine/html5 +/platform/linuxbsd/ @godotengine/linuxbsd +/platform/osx/ @godotengine/macos +/platform/uwp/ @godotengine/uwp +/platform/windows/ @godotengine/windows + +# Scene + +/scene/2d/ @godotengine/2d-nodes +/scene/3d/ @godotengine/3d-nodes +/scene/animation/ @godotengine/animation +/scene/audio/ @godotengine/audio +/scene/debugger/ @godotengine/debugger +/scene/gui/ @godotengine/gui-nodes +/scene/main/ @godotengine/core +/scene/resources/font.* @godotengine/gui-nodes +/scene/resources/text_line.* @godotengine/gui-nodes +/scene/resources/text_paragraph.* @godotengine/gui-nodes + +# Servers + +/servers/audio* @godotengine/audio +/servers/camera* @godotengine/xr +/servers/display_server.* @godotengine/_platforms +/servers/navigation_server*.* @godotengine/navigation +/servers/physics* @godotengine/physics +/servers/rendering* @godotengine/rendering +/servers/text_server.* @godotengine/gui-nodes +/servers/xr* @godotengine/xr + +# Tests + +/tests/ @godotengine/tests + +# Thirdparty + +/thirdparty/ @godotengine/buildsystem diff --git a/core/input/input.cpp b/core/input/input.cpp index 047aeb47fd..48e573626d 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -97,11 +97,11 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "exact"), &Input::is_action_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact"), &Input::is_action_just_pressed, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact"), &Input::is_action_just_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact"), &Input::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); @@ -240,16 +240,20 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const { return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } -bool Input::is_action_pressed(const StringName &p_action) const { - return action_state.has(p_action) && action_state[p_action].pressed; +bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { + return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); } -bool Input::is_action_just_pressed(const StringName &p_action) const { +bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -257,12 +261,16 @@ bool Input::is_action_just_pressed(const StringName &p_action) const { } } -bool Input::is_action_just_released(const StringName &p_action) const { +bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; } + if (p_exact && E->get().exact == false) { + return false; + } + if (Engine::get_singleton()->is_in_physics_frame()) { return !E->get().pressed && E->get().physics_frame == Engine::get_singleton()->get_physics_frames(); } else { @@ -270,21 +278,29 @@ bool Input::is_action_just_released(const StringName &p_action) const { } } -float Input::get_action_strength(const StringName &p_action) const { +float Input::get_action_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().strength; } -float Input::get_action_raw_strength(const StringName &p_action) const { +float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; } + if (p_exact && E->get().exact == false) { + return 0.0f; + } + return E->get().raw_strength; } @@ -590,14 +606,15 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) { if (InputMap::get_singleton()->event_is_action(p_event, E->key())) { - // Save the action's state - if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) { + // If not echo and action pressed state has changed + if (!p_event->is_echo() && is_action_pressed(E->key(), false) != p_event->is_action_pressed(E->key())) { Action action; action.physics_frame = Engine::get_singleton()->get_physics_frames(); action.process_frame = Engine::get_singleton()->get_process_frames(); action.pressed = p_event->is_action_pressed(E->key()); action.strength = 0.0f; action.raw_strength = 0.0f; + action.exact = InputMap::get_singleton()->event_is_action(p_event, E->key(), true); action_state[E->key()] = action; } action_state[E->key()].strength = p_event->get_action_strength(E->key()); diff --git a/core/input/input.h b/core/input/input.h index 445b7ff0cf..0e3af42381 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -116,6 +116,7 @@ private: uint64_t physics_frame; uint64_t process_frame; bool pressed; + bool exact; float strength; float raw_strength; }; @@ -261,11 +262,11 @@ public: bool is_key_pressed(int p_keycode) const; bool is_mouse_button_pressed(int p_button) const; bool is_joy_button_pressed(int p_device, int p_button) const; - bool is_action_pressed(const StringName &p_action) const; - bool is_action_just_pressed(const StringName &p_action) const; - bool is_action_just_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; + bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; + float get_action_strength(const StringName &p_action, bool p_exact = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index c91dc4d067..b1c8f1ad66 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -44,31 +44,31 @@ int InputEvent::get_device() const { return device; } -bool InputEvent::is_action(const StringName &p_action) const { - return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action); +bool InputEvent::is_action(const StringName &p_action, bool p_exact_match) const { + return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match); } -bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo) const { +bool InputEvent::is_action_pressed(const StringName &p_action, bool p_allow_echo, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && pressed && (p_allow_echo || !is_echo()); } -bool InputEvent::is_action_released(const StringName &p_action) const { +bool InputEvent::is_action_released(const StringName &p_action, bool p_exact_match) const { bool pressed; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, &pressed, nullptr, nullptr); return valid && !pressed; } -float InputEvent::get_action_strength(const StringName &p_action) const { +float InputEvent::get_action_strength(const StringName &p_action, bool p_exact_match) const { float strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, &strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, &strength, nullptr); return valid ? strength : 0.0f; } -float InputEvent::get_action_raw_strength(const StringName &p_action) const { +float InputEvent::get_action_raw_strength(const StringName &p_action, bool p_exact_match) const { float raw_strength; - bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, nullptr, nullptr, &raw_strength); + bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, p_exact_match, nullptr, nullptr, &raw_strength); return valid ? raw_strength : 0.0f; } @@ -100,10 +100,10 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device); ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device); - ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo"), &InputEvent::is_action_pressed, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength); + ClassDB::bind_method(D_METHOD("is_action", "action", "exact_match"), &InputEvent::is_action, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action", "allow_echo", "exact_match"), &InputEvent::is_action_pressed, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("is_action_released", "action", "exact_match"), &InputEvent::is_action_released, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &InputEvent::get_action_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed); ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo); diff --git a/core/input/input_event.h b/core/input/input_event.h index a354119cf9..1ce1fad9c2 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -128,11 +128,11 @@ public: void set_device(int p_device); int get_device() const; - bool is_action(const StringName &p_action) const; - bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false) const; - bool is_action_released(const StringName &p_action) const; - float get_action_strength(const StringName &p_action) const; - float get_action_raw_strength(const StringName &p_action) const; + bool is_action(const StringName &p_action, bool p_exact_match = false) const; + bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const; + bool is_action_released(const StringName &p_action, bool p_exact_match = false) const; + float get_action_strength(const StringName &p_action, bool p_exact_match = false) const; + float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const; // To be removed someday, since they do not make sense for all events virtual bool is_pressed() const; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 53ed925c1f..c03e64eaf4 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -50,7 +50,7 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events); - ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action); + ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); } @@ -95,7 +95,7 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { @@ -106,7 +106,9 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re int device = e->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + if (p_exact_match && e->shortcut_match(p_event)) { + return E; + } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -134,7 +136,7 @@ void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - if (_find_event(input_map[p_action], p_event)) { + if (_find_event(input_map[p_action], p_event, true)) { return; // Already addded. } @@ -143,13 +145,13 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); - return (_find_event(input_map[p_action], p_event) != nullptr); + return (_find_event(input_map[p_action], p_event, true) != nullptr); } void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { ERR_FAIL_COND_MSG(!input_map.has(p_action), "Request for nonexistent InputMap action '" + String(p_action) + "'."); - List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event); + List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); if (Input::get_singleton()->is_action_pressed(p_action)) { @@ -185,11 +187,11 @@ const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_act return &E->get().inputs; } -bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const { - return event_get_action_status(p_event, p_action); +bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { + return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength, float *p_raw_strength) const { +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *p_pressed, float *p_strength, float *p_raw_strength) const { Map<StringName, Action>::Element *E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); @@ -207,7 +209,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str bool pressed; float strength; float raw_strength; - List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, &pressed, &strength, &raw_strength); + List<Ref<InputEvent>>::Element *event = _find_event(E->get(), p_event, p_exact_match, &pressed, &strength, &raw_strength); if (event != nullptr) { if (p_pressed != nullptr) { *p_pressed = pressed; diff --git a/core/input/input_map.h b/core/input/input_map.h index 1646e7fadb..28c692c7ba 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -54,7 +54,7 @@ private: mutable Map<StringName, Action> input_map; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; Array _action_get_events(const StringName &p_action); Array _get_actions(); @@ -78,8 +78,8 @@ public: void action_erase_events(const StringName &p_action); const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); - bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; + bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *p_pressed = nullptr, float *p_strength = nullptr, float *p_raw_strength = nullptr) const; const Map<StringName, Action> &get_action_map() const; void load_from_project_settings(); diff --git a/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index dd3c57d1d0..38f4c5fe5c 100644 --- a/doc/classes/CollisionPolygon3D.xml +++ b/doc/classes/CollisionPolygon3D.xml @@ -17,6 +17,9 @@ <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false"> If [code]true[/code], no collision will be produced. </member> + <member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.04"> + The collision margin for the generated [Shape3D]. See [member Shape3D.margin] for more details. + </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array( )"> Array of vertices which define the polygon. [b]Note:[/b] The returned value is a copy of the original. Methods which mutate the size or properties of the return value will not impact the original polygon. To change properties of the polygon, assign it to a temporary variable and make changes before reassigning the [code]polygon[/code] member. diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index edd2bd137f..409e405551 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -329,10 +329,10 @@ </methods> <members> <member name="extra_spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0"> - Extra spacing at the bottom in pixels. + Extra spacing at the bottom of the line in pixels. </member> <member name="extra_spacing_top" type="int" setter="set_spacing" getter="get_spacing" default="0"> - Extra character spacing in pixels. + Extra spacing at the top of the line in pixels. </member> </members> <constants> diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml index e2c35f9ce7..7e99510124 100644 --- a/doc/classes/FontData.xml +++ b/doc/classes/FontData.xml @@ -154,6 +154,15 @@ Returns list of script support overrides. </description> </method> + <method name="get_spacing" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <description> + Returns the spacing for the given [code]type[/code] (see [enum SpacingType]). + </description> + </method> <method name="get_supported_chars" qualifiers="const"> <return type="String"> </return> @@ -296,6 +305,17 @@ Adds override for [method is_script_supported]. </description> </method> + <method name="set_spacing"> + <return type="void"> + </return> + <argument index="0" name="type" type="int"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + Sets the spacing for [code]type[/code] (see [enum SpacingType]) to [code]value[/code] in pixels (not relative to the font size). + </description> + </method> <method name="set_variation"> <return type="void"> </return> @@ -318,6 +338,14 @@ <member name="distance_field_hint" type="bool" setter="set_distance_field_hint" getter="get_distance_field_hint" default="false"> If [code]true[/code], distance field hint is enabled. </member> + <member name="extra_spacing_glyph" type="int" setter="set_spacing" getter="get_spacing" default="0"> + Extra spacing for each glyphs in pixels. + This can be a negative number to make the distance between glyphs smaller. + </member> + <member name="extra_spacing_space" type="int" setter="set_spacing" getter="get_spacing" default="0"> + Extra spacing for the space character in pixels. + This can be a negative number to make the distance between words smaller. + </member> <member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="get_force_autohinter" default="false"> If [code]true[/code], default autohinter is used for font hinting. </member> @@ -326,5 +354,11 @@ </member> </members> <constants> + <constant name="SPACING_GLYPH" value="0" enum="SpacingType"> + Spacing for each glyph. + </constant> + <constant name="SPACING_SPACE" value="1" enum="SpacingType"> + Spacing for the space character. + </constant> </constants> </class> diff --git a/doc/classes/Shape3D.xml b/doc/classes/Shape3D.xml index 2d8bb5d051..f3e62175c6 100644 --- a/doc/classes/Shape3D.xml +++ b/doc/classes/Shape3D.xml @@ -13,7 +13,8 @@ </methods> <members> <member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.04"> - The collision margin for the shape. + The collision margin for the shape. Used in Bullet Physics only. + Collision margins allows collision detection to be more efficient by adding an extra shell around shapes. Collision algorithms are more expensive when objects overlap by more than their margin, so a higher value for margins is better for performance, at the cost of accuracy around edges as it makes them less sharp. </member> </members> <constants> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 791646000b..413a77a0e9 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -295,6 +295,24 @@ Returns list of script support overrides. </description> </method> + <method name="font_get_spacing_glyph" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <description> + Returns extra spacing for each glyphs in pixels. + </description> + </method> + <method name="font_get_spacing_space" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <description> + Sets extra spacing for each glyphs in pixels. + </description> + </method> <method name="font_get_supported_chars" qualifiers="const"> <return type="String"> </return> @@ -490,6 +508,28 @@ Adds override for [method font_is_script_supported]. </description> </method> + <method name="font_set_spacing_glyph"> + <return type="void"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + Returns extra spacing for the space character in pixels. + </description> + </method> + <method name="font_set_spacing_space"> + <return type="void"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <argument index="1" name="value" type="int"> + </argument> + <description> + Sets extra spacing for the space character in pixels. + </description> + </method> <method name="font_set_variation"> <return type="void"> </return> diff --git a/doc/classes/TouchScreenButton.xml b/doc/classes/TouchScreenButton.xml index 355804f2a3..9833f0dc23 100644 --- a/doc/classes/TouchScreenButton.xml +++ b/doc/classes/TouchScreenButton.xml @@ -30,7 +30,8 @@ The button's texture for the normal state. </member> <member name="passby_press" type="bool" setter="set_passby_press" getter="is_passby_press_enabled" default="false"> - If [code]true[/code], pass-by presses are enabled. + If [code]true[/code], the [signal pressed] and [signal released] signals are emitted whenever a pressed finger goes in and out of the button, even if the pressure started outside the active area of the button. + [b]Note:[/b] this is a "pass-by" (not "bypass") press mode. </member> <member name="pressed" type="Texture2D" setter="set_texture_pressed" getter="get_texture_pressed"> The button's texture for the pressed state. diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index 5335116c8a..ae3cc73098 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -110,6 +110,9 @@ class ClassDef: self.theme_items = None # type: Optional[OrderedDict[str, List[ThemeItemDef]]] self.tutorials = [] # type: List[str] + # Used to match the class with XML source for output filtering purposes. + self.filepath = "" # type: str + class State: def __init__(self): # type: () -> None @@ -118,11 +121,12 @@ class State: self.classes = OrderedDict() # type: OrderedDict[str, ClassDef] self.current_class = "" # type: str - def parse_class(self, class_root): # type: (ET.Element) -> None + def parse_class(self, class_root, filepath): # type: (ET.Element, str) -> None class_name = class_root.attrib["name"] class_def = ClassDef(class_name) self.classes[class_name] = class_def + class_def.filepath = filepath inherits = class_root.get("inherits") if inherits is not None: @@ -278,6 +282,7 @@ def parse_arguments(root): # type: (ET.Element) -> List[ParameterDef] def main(): # type: () -> None parser = argparse.ArgumentParser() parser.add_argument("path", nargs="+", help="A path to an XML file or a directory containing XML files to parse.") + parser.add_argument("--filter", default="", help="The filepath pattern for XML files to filter.") group = parser.add_mutually_exclusive_group() group.add_argument("--output", "-o", default=".", help="The directory to save output .rst files in.") group.add_argument( @@ -333,17 +338,21 @@ def main(): # type: () -> None print_error("Duplicate class '{}'".format(name), state) continue - classes[name] = doc + classes[name] = (doc, cur_file) for name, data in classes.items(): try: - state.parse_class(data) + state.parse_class(data[0], data[1]) except Exception as e: print_error("Exception while parsing class '{}': {}".format(name, e), state) state.sort_classes() + pattern = re.compile(args.filter) + for class_name, class_def in state.classes.items(): + if args.filter and not pattern.search(class_def.filepath): + continue state.current_class = class_name make_rst_class(class_def, state, args.dry_run, args.output) diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index aef069331b..3d40c145f2 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -710,9 +710,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // PopupMenu const int popup_menu_margin_size = default_margin_size * 1.5 * EDSCALE; Ref<StyleBoxFlat> style_popup_menu = style_popup->duplicate(); - style_popup_menu->set_default_margin(SIDE_LEFT, popup_menu_margin_size); + // Use 1 pixel for the sides, since if 0 is used, the highlight of hovered items is drawn + // on top of the popup border. This causes a 'gap' in the panel border when an item is highlighted, + // and it looks weird. 1px solves this. + style_popup_menu->set_default_margin(SIDE_LEFT, 1 * EDSCALE); style_popup_menu->set_default_margin(SIDE_TOP, popup_menu_margin_size); - style_popup_menu->set_default_margin(SIDE_RIGHT, popup_menu_margin_size); + style_popup_menu->set_default_margin(SIDE_RIGHT, 1 * EDSCALE); style_popup_menu->set_default_margin(SIDE_BOTTOM, popup_menu_margin_size); theme->set_stylebox("panel", "PopupMenu", style_popup_menu); @@ -734,7 +737,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons")); theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons")); theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons")); + theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size + 1) * EDSCALE); + theme->set_constant("item_start_padding", "PopupMenu", popup_menu_margin_size * EDSCALE); + theme->set_constant("item_end_padding", "PopupMenu", popup_menu_margin_size * EDSCALE); for (int i = 0; i < 16; i++) { Color si_base_color = accent_color; diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h index 44fac3c190..ef60633a3d 100644 --- a/modules/gdnative/include/text/godot_text.h +++ b/modules/gdnative/include/text/godot_text.h @@ -79,6 +79,10 @@ typedef struct { float (*font_get_descent)(void *, godot_rid *, int); float (*font_get_underline_position)(void *, godot_rid *, int); float (*font_get_underline_thickness)(void *, godot_rid *, int); + int (*font_get_spacing_space)(void *, godot_rid *); + void (*font_set_spacing_space)(void *, godot_rid *, int); + int (*font_get_spacing_glyph)(void *, godot_rid *); + void (*font_set_spacing_glyph)(void *, godot_rid *, int); void (*font_set_antialiased)(void *, godot_rid *, bool); bool (*font_get_antialiased)(void *, godot_rid *); godot_dictionary (*font_get_feature_list)(void *, godot_rid *); diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index f7a3cb8135..7584568dd8 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -138,6 +138,26 @@ float TextServerGDNative::font_get_underline_thickness(RID p_font, int p_size) c return interface->font_get_underline_thickness(data, (godot_rid *)&p_font, p_size); } +int TextServerGDNative::font_get_spacing_space(RID p_font) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_spacing_space(data, (godot_rid *)&p_font); +} + +void TextServerGDNative::font_set_spacing_space(RID p_font, int p_value) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_spacing_space(data, (godot_rid *)&p_font, p_value); +} + +int TextServerGDNative::font_get_spacing_glyph(RID p_font) const { + ERR_FAIL_COND_V(interface == nullptr, 0); + return interface->font_get_spacing_glyph(data, (godot_rid *)&p_font); +} + +void TextServerGDNative::font_set_spacing_glyph(RID p_font, int p_value) { + ERR_FAIL_COND(interface == nullptr); + interface->font_set_spacing_glyph(data, (godot_rid *)&p_font, p_value); +} + void TextServerGDNative::font_set_antialiased(RID p_font, bool p_antialiased) { ERR_FAIL_COND(interface == nullptr); interface->font_set_antialiased(data, (godot_rid *)&p_font, p_antialiased); diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index 9cbb94217e..9783b65431 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -72,6 +72,12 @@ public: virtual float font_get_underline_position(RID p_font, int p_size) const override; virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual int font_get_spacing_space(RID p_font) const override; + virtual void font_set_spacing_space(RID p_font, int p_value) override; + + virtual int font_get_spacing_glyph(RID p_font) const override; + virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; virtual bool font_get_antialiased(RID p_font) const override; diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h index 4bbd2dd4bf..2bb09c11ad 100644 --- a/modules/text_server_adv/font_adv.h +++ b/modules/text_server_adv/font_adv.h @@ -39,6 +39,8 @@ struct FontDataAdvanced { Map<String, bool> lang_support_overrides; Map<String, bool> script_support_overrides; bool valid = false; + int spacing_space = 0; + int spacing_glyph = 0; virtual void clear_cache() = 0; @@ -58,6 +60,18 @@ struct FontDataAdvanced { virtual float get_underline_position(int p_size) const = 0; virtual float get_underline_thickness(int p_size) const = 0; + virtual int get_spacing_space() const { return spacing_space; }; + virtual void set_spacing_space(int p_value) { + spacing_space = p_value; + clear_cache(); + }; + + virtual int get_spacing_glyph() const { return spacing_glyph; }; + virtual void set_spacing_glyph(int p_value) { + spacing_glyph = p_value; + clear_cache(); + }; + virtual void set_antialiased(bool p_antialiased) = 0; virtual bool get_antialiased() const = 0; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 8e4771685d..1e015db210 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -601,6 +601,34 @@ float TextServerAdvanced::font_get_underline_thickness(RID p_font, int p_size) c return fd->get_underline_thickness(p_size); } +int TextServerAdvanced::font_get_spacing_space(RID p_font) const { + _THREAD_SAFE_METHOD_ + const FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, 0); + return fd->get_spacing_space(); +} + +void TextServerAdvanced::font_set_spacing_space(RID p_font, int p_value) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->set_spacing_space(p_value); +} + +int TextServerAdvanced::font_get_spacing_glyph(RID p_font) const { + _THREAD_SAFE_METHOD_ + const FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, 0); + return fd->get_spacing_glyph(); +} + +void TextServerAdvanced::font_set_spacing_glyph(RID p_font, int p_value) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->set_spacing_glyph(p_value); +} + void TextServerAdvanced::font_set_antialiased(RID p_font, bool p_antialiased) { _THREAD_SAFE_METHOD_ FontDataAdvanced *fd = font_owner.getornull(p_font); @@ -2049,6 +2077,11 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / fd->get_font_scale(fs))); gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / fd->get_font_scale(fs))); } + if (fd->get_spacing_space() && is_whitespace(p_sd->text[glyph_info[i].cluster])) { + gl.advance += fd->get_spacing_space(); + } else { + gl.advance += fd->get_spacing_glyph(); + } if (p_sd->preserve_control) { last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || is_whitespace(p_sd->text[glyph_info[i].cluster]) || is_linebreak(p_sd->text[glyph_info[i].cluster])); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 89fae477f9..c5ebe61bc3 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -134,6 +134,12 @@ public: virtual float font_get_underline_position(RID p_font, int p_size) const override; virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual int font_get_spacing_space(RID p_font) const override; + virtual void font_set_spacing_space(RID p_font, int p_value) override; + + virtual int font_get_spacing_glyph(RID p_font) const override; + virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; virtual bool font_get_antialiased(RID p_font) const override; diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h index cc72919542..7fccbe06b5 100644 --- a/modules/text_server_fb/font_fb.h +++ b/modules/text_server_fb/font_fb.h @@ -37,6 +37,8 @@ struct FontDataFallback { Map<String, bool> lang_support_overrides; Map<String, bool> script_support_overrides; bool valid = false; + int spacing_space = 0; + int spacing_glyph = 0; virtual void clear_cache() = 0; @@ -50,6 +52,18 @@ struct FontDataFallback { virtual float get_underline_position(int p_size) const = 0; virtual float get_underline_thickness(int p_size) const = 0; + virtual int get_spacing_space() const { return spacing_space; }; + virtual void set_spacing_space(int p_value) { + spacing_space = p_value; + clear_cache(); + }; + + virtual int get_spacing_glyph() const { return spacing_glyph; }; + virtual void set_spacing_glyph(int p_value) { + spacing_glyph = p_value; + clear_cache(); + }; + virtual void set_antialiased(bool p_antialiased) = 0; virtual bool get_antialiased() const = 0; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index e60d269408..5cbe0f8277 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -179,6 +179,34 @@ float TextServerFallback::font_get_underline_thickness(RID p_font, int p_size) c return fd->get_underline_thickness(p_size); } +int TextServerFallback::font_get_spacing_space(RID p_font) const { + _THREAD_SAFE_METHOD_ + const FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, 0); + return fd->get_spacing_space(); +} + +void TextServerFallback::font_set_spacing_space(RID p_font, int p_value) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->set_spacing_space(p_value); +} + +int TextServerFallback::font_get_spacing_glyph(RID p_font) const { + _THREAD_SAFE_METHOD_ + const FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND_V(!fd, 0); + return fd->get_spacing_glyph(); +} + +void TextServerFallback::font_set_spacing_glyph(RID p_font, int p_value) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->set_spacing_glyph(p_value); +} + void TextServerFallback::font_set_antialiased(RID p_font, bool p_antialiased) { _THREAD_SAFE_METHOD_ FontDataFallback *fd = font_owner.getornull(p_font); @@ -1184,6 +1212,11 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { sd->descent = MAX(sd->descent, Math::round(fd->get_advance(gl.index, gl.font_size).x * 0.5)); } } + if (fd->get_spacing_space() && is_whitespace(sd->text[j])) { + gl.advance += fd->get_spacing_space(); + } else { + gl.advance += fd->get_spacing_glyph(); + } sd->upos = MAX(sd->upos, fd->get_underline_position(gl.font_size)); sd->uthk = MAX(sd->uthk, fd->get_underline_thickness(gl.font_size)); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index d142b320e4..c14a444872 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -89,6 +89,12 @@ public: virtual float font_get_underline_position(RID p_font, int p_size) const override; virtual float font_get_underline_thickness(RID p_font, int p_size) const override; + virtual int font_get_spacing_space(RID p_font) const override; + virtual void font_set_spacing_space(RID p_font, int p_value) override; + + virtual int font_get_spacing_glyph(RID p_font) const override; + virtual void font_set_spacing_glyph(RID p_font, int p_value) override; + virtual void font_set_antialiased(RID p_font, bool p_antialiased) override; virtual bool font_get_antialiased(RID p_font) const override; diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index 597ebce6ff..9fac99810b 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -93,6 +93,7 @@ public: List<WarpEvent> warp_events; NSTimeInterval last_warp = 0; + bool ignore_warp = false; Vector<KeyEvent> key_event_buffer; int key_event_pos; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 2d43454501..ed7d89009f 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -871,6 +871,15 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i NSPoint delta = NSMakePoint([event deltaX], [event deltaY]); NSPoint mpos = [event locationInWindow]; + if (DS_OSX->ignore_warp) { + // Discard late events, before warp + if (([event timestamp]) < DS_OSX->last_warp) { + return; + } + DS_OSX->ignore_warp = false; + return; + } + if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED) { // Discard late events if (([event timestamp]) < DS_OSX->last_warp) { @@ -2098,6 +2107,8 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) { CGAssociateMouseAndMouseCursorPosition(true); } + last_warp = [[NSProcessInfo processInfo] systemUptime]; + ignore_warp = true; warp_events.clear(); mouse_mode = p_mode; } diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 4d117f02d3..e3e2eb4669 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -70,6 +70,7 @@ void CollisionPolygon3D::_build_polygon() { } convex->set_points(cp); + convex->set_margin(margin); parent->shape_owner_add_shape(owner_id, convex); parent->shape_owner_set_disabled(owner_id, disabled); } @@ -155,6 +156,17 @@ bool CollisionPolygon3D::is_disabled() const { return disabled; } +real_t CollisionPolygon3D::get_margin() const { + return margin; +} + +void CollisionPolygon3D::set_margin(real_t p_margin) { + margin = p_margin; + if (parent) { + _build_polygon(); + } +} + String CollisionPolygon3D::get_configuration_warning() const { String warning = Node3D::get_configuration_warning(); @@ -189,11 +201,15 @@ void CollisionPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon3D::is_disabled); + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &CollisionPolygon3D::set_margin); + ClassDB::bind_method(D_METHOD("get_margin"), &CollisionPolygon3D::get_margin); + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon3D::_is_editable_3d_polygon); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin"); } CollisionPolygon3D::CollisionPolygon3D() { diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index cb0aba67b1..750751b509 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -37,6 +37,7 @@ class CollisionObject3D; class CollisionPolygon3D : public Node3D { GDCLASS(CollisionPolygon3D, Node3D); + real_t margin = 0.04; protected: real_t depth = 1.0; @@ -70,6 +71,9 @@ public: virtual AABB get_item_rect() const; + real_t get_margin() const; + void set_margin(real_t p_margin); + String get_configuration_warning() const override; CollisionPolygon3D(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index c5ff4b37ae..d68a3206b6 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -89,7 +89,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const { minsize.height += size.height; } - minsize.width += max_w + icon_w + accel_max_w; + int item_side_padding = get_theme_constant("item_start_padding") + get_theme_constant("item_end_padding"); + minsize.width += max_w + icon_w + accel_max_w + item_side_padding; + if (has_check) { minsize.width += check_w; } @@ -451,6 +453,10 @@ void PopupMenu::_draw_items() { margin_size.width = margin_container->get_theme_constant("margin_right") + margin_container->get_theme_constant("margin_left"); margin_size.height = margin_container->get_theme_constant("margin_top") + margin_container->get_theme_constant("margin_bottom"); + // Space between the item content and the sides of popup menu. + int item_start_padding = get_theme_constant("item_start_padding"); + int item_end_padding = get_theme_constant("item_end_padding"); + bool rtl = control->is_layout_rtl(); Ref<StyleBox> style = get_theme_stylebox("panel"); Ref<StyleBox> hover = get_theme_stylebox("hover"); @@ -537,12 +543,15 @@ void PopupMenu::_draw_items() { labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, display_width - text_right), sep_h))); } } else { - separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(display_width, sep_h))); + separator->draw(ci, Rect2(item_ofs, Size2(display_width, sep_h))); } } Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1); + // For non-separator items, add some padding for the content. + item_ofs.x += item_start_padding; + // Checkboxes if (items[i].checkable_type) { Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); @@ -565,9 +574,9 @@ void PopupMenu::_draw_items() { // Submenu arrow on right hand side if (items[i].submenu != "") { if (rtl) { - submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } else { - submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); + submenu->draw(ci, Point2(display_width - style->get_margin(SIDE_RIGHT) - submenu->get_width() - item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); } } @@ -603,9 +612,9 @@ void PopupMenu::_draw_items() { // Accelerator / Shortcut if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) { if (rtl) { - item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT); + item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding; } else { - item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x; + item_ofs.x = display_width - style->get_margin(SIDE_RIGHT) - items[i].accel_text_buf->get_size().x - item_end_padding; } Vector2 text_pos = item_ofs + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); if (outline_size > 0 && font_outline_color.a > 0) { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index e217bc94b0..943176537b 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -644,6 +644,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("hseparation", "PopupMenu", 4 * scale); theme->set_constant("vseparation", "PopupMenu", 4 * scale); theme->set_constant("outline_size", "PopupMenu", 0); + theme->set_constant("item_start_padding", "PopupMenu", 2 * scale); + theme->set_constant("item_end_padding", "PopupMenu", 2 * scale); // GraphNode diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 06a15e36d7..702f2ed1c8 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -50,6 +50,9 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &FontData::get_underline_position); ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &FontData::get_underline_thickness); + ClassDB::bind_method(D_METHOD("get_spacing", "type"), &FontData::get_spacing); + ClassDB::bind_method(D_METHOD("set_spacing", "type", "value"), &FontData::set_spacing); + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); ClassDB::bind_method(D_METHOD("get_antialiased"), &FontData::get_antialiased); @@ -100,6 +103,13 @@ void FontData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distance_field_hint"), "set_distance_field_hint", "get_distance_field_hint"); ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + + ADD_GROUP("Extra Spacing", "extra_spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_glyph"), "set_spacing", "get_spacing", SPACING_GLYPH); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "extra_spacing_space"), "set_spacing", "get_spacing", SPACING_SPACE); + + BIND_ENUM_CONSTANT(SPACING_GLYPH); + BIND_ENUM_CONSTANT(SPACING_SPACE); } bool FontData::_set(const StringName &p_name, const Variant &p_value) { @@ -289,6 +299,27 @@ double FontData::get_variation(const String &p_name) const { return TS->font_get_variation(rid, p_name); } +int FontData::get_spacing(int p_type) const { + if (rid == RID()) { + return 0; + } + if (p_type == SPACING_GLYPH) { + return TS->font_get_spacing_glyph(rid); + } else { + return TS->font_get_spacing_space(rid); + } +} + +void FontData::set_spacing(int p_type, int p_value) { + ERR_FAIL_COND(rid == RID()); + if (p_type == SPACING_GLYPH) { + TS->font_set_spacing_glyph(rid, p_value); + } else { + TS->font_set_spacing_space(rid, p_value); + } + emit_changed(); +} + void FontData::set_antialiased(bool p_antialiased) { ERR_FAIL_COND(rid == RID()); TS->font_set_antialiased(rid, p_antialiased); @@ -797,6 +828,8 @@ Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p } void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + ERR_FAIL_COND(data.is_empty()); + uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_size, hash); @@ -827,6 +860,8 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t } void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const { + ERR_FAIL_COND(data.is_empty()); + uint64_t hash = p_text.hash64(); hash = hash_djb2_one_64(p_size, hash); diff --git a/scene/resources/font.h b/scene/resources/font.h index a91c9ec7a5..56b5acde1a 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -42,6 +42,13 @@ class FontData : public Resource { GDCLASS(FontData, Resource); +public: + enum SpacingType { + SPACING_GLYPH, + SPACING_SPACE, + }; + +private: RID rid; int base_size = 16; String path; @@ -78,6 +85,9 @@ public: float get_underline_position(int p_size) const; float get_underline_thickness(int p_size) const; + int get_spacing(int p_type) const; + void set_spacing(int p_type, int p_value); + void set_antialiased(bool p_antialiased); bool get_antialiased() const; @@ -134,7 +144,7 @@ class Font : public Resource { public: enum SpacingType { SPACING_TOP, - SPACING_BOTTOM + SPACING_BOTTOM, }; private: @@ -199,6 +209,7 @@ public: ~Font(); }; +VARIANT_ENUM_CAST(FontData::SpacingType); VARIANT_ENUM_CAST(Font::SpacingType); /*************************************************************************/ diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index ed69c093cf..925867a1f2 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -167,6 +167,7 @@ void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) { } bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 94957df510..444a4bb22a 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -243,6 +243,7 @@ TextServer::Orientation TextParagraph::get_orientation() const { } bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); TS->shaped_text_clear(dropcap_rid); dropcap_margins = p_dropcap_margins; bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); @@ -257,6 +258,7 @@ void TextParagraph::clear_dropcap() { } bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { + ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(Font::SPACING_BOTTOM); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index da68ceb128..755a17f86a 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -227,6 +227,12 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_get_underline_position", "font", "size"), &TextServer::font_get_underline_position); ClassDB::bind_method(D_METHOD("font_get_underline_thickness", "font", "size"), &TextServer::font_get_underline_thickness); + ClassDB::bind_method(D_METHOD("font_get_spacing_space", "font"), &TextServer::font_get_spacing_space); + ClassDB::bind_method(D_METHOD("font_set_spacing_space", "font", "value"), &TextServer::font_set_spacing_space); + + ClassDB::bind_method(D_METHOD("font_get_spacing_glyph", "font"), &TextServer::font_get_spacing_glyph); + ClassDB::bind_method(D_METHOD("font_set_spacing_glyph", "font", "value"), &TextServer::font_set_spacing_glyph); + ClassDB::bind_method(D_METHOD("font_set_antialiased", "font", "antialiased"), &TextServer::font_set_antialiased); ClassDB::bind_method(D_METHOD("font_get_antialiased", "font"), &TextServer::font_get_antialiased); diff --git a/servers/text_server.h b/servers/text_server.h index 23367de4c8..3268741a74 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -241,6 +241,12 @@ public: virtual float font_get_ascent(RID p_font, int p_size) const = 0; virtual float font_get_descent(RID p_font, int p_size) const = 0; + virtual int font_get_spacing_space(RID p_font) const = 0; + virtual void font_set_spacing_space(RID p_font, int p_value) = 0; + + virtual int font_get_spacing_glyph(RID p_font) const = 0; + virtual void font_set_spacing_glyph(RID p_font, int p_value) = 0; + virtual float font_get_underline_position(RID p_font, int p_size) const = 0; virtual float font_get_underline_thickness(RID p_font, int p_size) const = 0; |