summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/project_settings.cpp15
-rw-r--r--core/project_settings.h2
-rw-r--r--core/undo_redo.cpp19
-rw-r--r--core/vector.h56
-rw-r--r--doc/classes/@GDScript.xml18
-rw-r--r--doc/classes/AStar.xml43
-rw-r--r--doc/classes/Array.xml8
-rw-r--r--doc/classes/ArrayMesh.xml6
-rw-r--r--doc/classes/ButtonGroup.xml9
-rw-r--r--doc/classes/CanvasItem.xml4
-rw-r--r--doc/classes/Color.xml43
-rw-r--r--doc/classes/EditorImportPlugin.xml2
-rw-r--r--doc/classes/EditorScenePostImport.xml2
-rw-r--r--doc/classes/HTTPClient.xml14
-rw-r--r--doc/classes/MeshDataTool.xml6
-rw-r--r--doc/classes/ProjectSettings.xml16
-rw-r--r--doc/classes/ScriptCreateDialog.xml6
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp5
-rw-r--r--editor/editor_file_dialog.cpp7
-rw-r--r--editor/editor_file_dialog.h1
-rw-r--r--editor/editor_properties.cpp1
-rw-r--r--editor/inspector_dock.cpp12
-rw-r--r--editor/scene_tree_editor.cpp2
-rw-r--r--main/input_default.cpp2
-rw-r--r--modules/csg/csg_shape.cpp99
-rw-r--r--modules/csg/csg_shape.h14
-rw-r--r--modules/csg/doc_classes/CSGCombiner.xml2
-rw-r--r--modules/csg/doc_classes/CSGMesh.xml2
-rw-r--r--modules/csg/doc_classes/CSGShape.xml48
-rw-r--r--platform/android/export/export.cpp7
-rw-r--r--platform/javascript/os_javascript.cpp33
-rw-r--r--platform/javascript/os_javascript.h4
-rw-r--r--platform/osx/os_osx.mm6
-rw-r--r--platform/windows/os_windows.cpp27
-rw-r--r--platform/x11/detect.py11
-rw-r--r--platform/x11/os_x11.cpp435
-rw-r--r--platform/x11/os_x11.h27
-rw-r--r--scene/2d/physics_body_2d.cpp18
-rw-r--r--scene/3d/remote_transform.cpp36
-rw-r--r--scene/gui/base_button.cpp11
-rw-r--r--scene/gui/base_button.h1
-rw-r--r--scene/gui/file_dialog.cpp3
-rw-r--r--scene/gui/tree.cpp17
-rw-r--r--scene/gui/tree.h2
-rw-r--r--scene/main/viewport.cpp7
-rw-r--r--scene/main/viewport.h1
-rw-r--r--servers/audio/audio_stream.cpp7
-rw-r--r--servers/audio_server.cpp1
48 files changed, 759 insertions, 359 deletions
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 407bb78375..031ac06063 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -307,7 +307,7 @@ void ProjectSettings::_convert_to_last_version() {
* If a project file is found, load it or fail.
* If nothing was found, error out.
*/
-Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
+Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
// If looking for files in a network client, use it directly
@@ -450,6 +450,18 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
return OK;
}
+Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
+ Error err = _setup(p_path, p_main_pack, p_upwards);
+ if (err == OK) {
+ String custom_settings = GLOBAL_DEF("application/config/project_settings_override", "");
+ if (custom_settings != "") {
+ _load_settings_text(custom_settings);
+ }
+ }
+
+ return err;
+}
+
bool ProjectSettings::has_setting(String p_var) const {
_THREAD_SAFE_METHOD_
@@ -995,6 +1007,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("application/run/disable_stderr", false);
GLOBAL_DEF("application/config/use_custom_user_dir", false);
GLOBAL_DEF("application/config/custom_user_dir_name", "");
+ GLOBAL_DEF("application/config/project_settings_override", "");
action = Dictionary();
action["deadzone"] = Variant(0.5f);
diff --git a/core/project_settings.h b/core/project_settings.h
index 611355f2ef..f150e4499b 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -112,6 +112,8 @@ protected:
void _add_property_info_bind(const Dictionary &p_info);
+ Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false);
+
protected:
static void _bind_methods();
diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp
index 3d41c374ea..f2f521c196 100644
--- a/core/undo_redo.cpp
+++ b/core/undo_redo.cpp
@@ -268,7 +268,24 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
case Operation::TYPE_METHOD: {
- obj->call(op.name, VARIANT_ARGS_FROM_ARRAY(op.args));
+ Vector<const Variant *> argptrs;
+ argptrs.resize(VARIANT_ARG_MAX);
+ int argc = 0;
+
+ for (int i = 0; i < VARIANT_ARG_MAX; i++) {
+ if (op.args[i].get_type() == Variant::NIL) {
+ break;
+ }
+ argptrs.write[i] = &op.args[i];
+ argc++;
+ }
+ argptrs.resize(argc);
+
+ Variant::CallError ce;
+ obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ ERR_PRINTS("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
+ }
#ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj);
if (res)
diff --git a/core/vector.h b/core/vector.h
index 38a1082407..2fb6f6c1c4 100644
--- a/core/vector.h
+++ b/core/vector.h
@@ -44,17 +44,11 @@
template <class T>
class VectorWriteProxy {
- friend class Vector<T>;
- CowData<T> *_parent;
-
- _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) :
- _parent(parent){};
-
public:
_FORCE_INLINE_ T &operator[](int p_index) {
- CRASH_BAD_INDEX(p_index, _parent->size());
+ CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size());
- return _parent->ptrw()[p_index];
+ return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index];
}
};
@@ -62,39 +56,41 @@ template <class T>
class Vector {
friend class VectorWriteProxy<T>;
- CowData<T> *_cowdata;
-
public:
VectorWriteProxy<T> write;
+private:
+ CowData<T> _cowdata;
+
+public:
bool push_back(const T &p_elem);
- void remove(int p_index) { _cowdata->remove(p_index); }
+ void remove(int p_index) { _cowdata.remove(p_index); }
void erase(const T &p_val) {
int idx = find(p_val);
if (idx >= 0) remove(idx);
};
void invert();
- _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); }
- _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); }
+ _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
+ _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
_FORCE_INLINE_ void clear() { resize(0); }
- _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); }
+ _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); }
- _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); }
- _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); }
- _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); }
- _FORCE_INLINE_ int size() const { return _cowdata->size(); }
- Error resize(int p_size) { return _cowdata->resize(p_size); }
- _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); }
- Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); }
+ _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ const T get(int p_index) const { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
+ _FORCE_INLINE_ int size() const { return _cowdata.size(); }
+ Error resize(int p_size) { return _cowdata.resize(p_size); }
+ _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
+ Error insert(int p_pos, const T &p_val) { return _cowdata.insert(p_pos, p_val); }
void append_array(const Vector<T> &p_other);
template <class C>
void sort_custom() {
- int len = _cowdata->size();
+ int len = _cowdata.size();
if (len == 0)
return;
@@ -110,7 +106,7 @@ public:
void ordered_insert(const T &p_val) {
int i;
- for (i = 0; i < _cowdata->size(); i++) {
+ for (i = 0; i < _cowdata.size(); i++) {
if (p_val < operator[](i)) {
break;
@@ -135,20 +131,14 @@ public:
return ret;
}
- _FORCE_INLINE_ Vector() :
- _cowdata(new CowData<T>()),
- write(VectorWriteProxy<T>(_cowdata)) {}
- _FORCE_INLINE_ Vector(const Vector &p_from) :
- _cowdata(new CowData<T>()),
- write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); }
+ _FORCE_INLINE_ Vector() {}
+ _FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
inline Vector &operator=(const Vector &p_from) {
- _cowdata->_ref(p_from._cowdata);
+ _cowdata._ref(p_from._cowdata);
return *this;
}
- _FORCE_INLINE_ ~Vector() {
- delete _cowdata;
- }
+ _FORCE_INLINE_ ~Vector() {}
};
template <class T>
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index 349daae6d9..2bdfc56c44 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -127,7 +127,7 @@
<description>
Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant.
[codeblock]
- a = atan(0,-1) # a is 3.141593
+ a = atan(0, -1) # a is 3.141593
[/codeblock]
</description>
</method>
@@ -175,7 +175,7 @@
# a is 'A'
a = char(65)
# a is 'a'
- a = char(65+32)
+ a = char(65 + 32)
[/codeblock]
</description>
</method>
@@ -230,7 +230,7 @@
Returns the cosine of angle [code]s[/code] in radians.
[codeblock]
# prints 1 and -1
- print(cos(PI*2))
+ print(cos(PI * 2))
print(cos(PI))
[/codeblock]
</description>
@@ -584,7 +584,7 @@
<description>
Returns the maximum of two values.
[codeblock]
- max(1,2) # returns 2
+ max(1, 2) # returns 2
max(-3.99, -4) # returns -3.99
[/codeblock]
</description>
@@ -599,7 +599,7 @@
<description>
Returns the minimum of two values.
[codeblock]
- min(1,2) # returns 1
+ min(1, 2) # returns 1
min(-3.99, -4) # returns -4
[/codeblock]
</description>
@@ -657,7 +657,7 @@
<description>
Returns the result of [code]x[/code] raised to the power of [code]y[/code].
[codeblock]
- pow(2,5) # returns 32
+ pow(2, 5) # returns 32
[/codeblock]
</description>
</method>
@@ -681,8 +681,8 @@
<description>
Converts one or more arguments to strings in the best way possible and prints them to the console.
[codeblock]
- a = [1,2,3]
- print("a","b",a) # prints ab[1, 2, 3]
+ a = [1, 2, 3]
+ print("a", "b", a) # prints ab[1, 2, 3]
[/codeblock]
</description>
</method>
@@ -1009,7 +1009,7 @@
<description>
Returns the tangent of angle [code]s[/code] in radians.
[codeblock]
- tan( deg2rad(45) ) # returns 1
+ tan(deg2rad(45)) # returns 1
[/codeblock]
</description>
</method>
diff --git a/doc/classes/AStar.xml b/doc/classes/AStar.xml
index 482566ee9f..e1b79a2a3e 100644
--- a/doc/classes/AStar.xml
+++ b/doc/classes/AStar.xml
@@ -47,8 +47,7 @@
Adds a new point at the given position with the given identifier. The algorithm prefers points with lower [code]weight_scale[/code] to form a path. The [code]id[/code] must be 0 or larger, and the [code]weight_scale[/code] must be 1 or larger.
[codeblock]
var as = AStar.new()
-
- as.add_point(1, Vector3(1,0,0), 4) # Adds the point (1,0,0) with weight_scale=4 and id=1
+ as.add_point(1, Vector3(1, 0, 0), 4) # Adds the point (1, 0, 0) with weight_scale 4 and id 1
[/codeblock]
If there already exists a point for the given id, its position and weight scale are updated to the given values.
</description>
@@ -81,15 +80,12 @@
<argument index="2" name="bidirectional" type="bool" default="true">
</argument>
<description>
- Creates a segment between the given points.
+ Creates a segment between the given points. If [code]bidirectional[/code] is [code]false[/code], only movement from [code]id[/code] to [code]to_id[/code] is allowed, not the reverse direction.
[codeblock]
var as = AStar.new()
-
- as.add_point(1, Vector3(1,1,0))
- as.add_point(2, Vector3(0,5,0))
-
- as.connect_points(1, 2, false) # If bidirectional=false it's only possible to go from point 1 to point 2
- # and not from point 2 to point 1.
+ as.add_point(1, Vector3(1, 1, 0))
+ as.add_point(2, Vector3(0, 5, 0))
+ as.connect_points(1, 2, false)
[/codeblock]
</description>
</method>
@@ -129,15 +125,12 @@
Returns the closest position to [code]to_position[/code] that resides inside a segment between two connected points.
[codeblock]
var as = AStar.new()
-
- as.add_point(1, Vector3(0,0,0))
- as.add_point(2, Vector3(0,5,0))
-
+ as.add_point(1, Vector3(0, 0, 0))
+ as.add_point(2, Vector3(0, 5, 0))
as.connect_points(1, 2)
-
- var res = as.get_closest_position_in_segment(Vector3(3,3,0)) # returns (0, 3, 0)
+ var res = as.get_closest_position_in_segment(Vector3(3, 3, 0)) # returns (0, 3, 0)
[/codeblock]
- The result is in the segment that goes from [code]y=0[/code] to [code]y=5[/code]. It's the closest position in the segment to the given point.
+ The result is in the segment that goes from [code]y = 0[/code] to [code]y = 5[/code]. It's the closest position in the segment to the given point.
</description>
</method>
<method name="get_id_path">
@@ -151,11 +144,10 @@
Returns an array with the ids of the points that form the path found by AStar between the given points. The array is ordered from the starting point to the ending point of the path.
[codeblock]
var as = AStar.new()
-
- as.add_point(1, Vector3(0,0,0))
- as.add_point(2, Vector3(0,1,0), 1) # default weight is 1
- as.add_point(3, Vector3(1,1,0))
- as.add_point(4, Vector3(2,0,0))
+ as.add_point(1, Vector3(0, 0, 0))
+ as.add_point(2, Vector3(0, 1, 0), 1) # default weight is 1
+ as.add_point(3, Vector3(1, 1, 0))
+ as.add_point(4, Vector3(2, 0, 0))
as.connect_points(1, 2, false)
as.connect_points(2, 3, false)
@@ -177,11 +169,10 @@
Returns an array with the ids of the points that form the connect with the given point.
[codeblock]
var as = AStar.new()
-
- as.add_point(1, Vector3(0,0,0))
- as.add_point(2, Vector3(0,1,0))
- as.add_point(3, Vector3(1,1,0))
- as.add_point(4, Vector3(2,0,0))
+ as.add_point(1, Vector3(0, 0, 0))
+ as.add_point(2, Vector3(0, 1, 0))
+ as.add_point(3, Vector3(1, 1, 0))
+ as.add_point(4, Vector3(2, 0, 0))
as.connect_points(1, 2, true)
as.connect_points(1, 3, true)
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index b013b3c4ae..6bd0ef3421 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -196,10 +196,10 @@
<description>
Return true if the array contains given value.
[codeblock]
- [ "inside", 7 ].has("inside") == true
- [ "inside", 7 ].has("outside") == false
- [ "inside", 7 ].has(7) == true
- [ "inside", 7 ].has("7") == false
+ ["inside", 7].has("inside") == true
+ ["inside", 7].has("outside") == false
+ ["inside", 7].has(7) == true
+ ["inside", 7].has("7") == false
[/codeblock]
</description>
</method>
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 7806cf4ce4..bcb9b4f6da 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -6,9 +6,9 @@
The [code]ArrayMesh[/code] is used to construct a [Mesh] by specifying the attributes as arrays. The most basic example is the creation of a single triangle
[codeblock]
var vertices = PoolVector3Array()
- vertices.push_back(Vector3(0,1,0))
- vertices.push_back(Vector3(1,0,0))
- vertices.push_back(Vector3(0,0,1))
+ vertices.push_back(Vector3(0, 1, 0))
+ vertices.push_back(Vector3(1, 0, 0))
+ vertices.push_back(Vector3(0, 0, 1))
# Initialize the ArrayMesh.
var arr_mesh = ArrayMesh.new()
var arrays = []
diff --git a/doc/classes/ButtonGroup.xml b/doc/classes/ButtonGroup.xml
index 0f5ea8e6bf..6273c8f83f 100644
--- a/doc/classes/ButtonGroup.xml
+++ b/doc/classes/ButtonGroup.xml
@@ -12,11 +12,18 @@
<demos>
</demos>
<methods>
+ <method name="get_buttons">
+ <return type="Array">
+ </return>
+ <description>
+ Returns an [Array] of [Button]s who have this as their [code]ButtonGroup[/code] (see [member BaseButton.group]).
+ </description>
+ </method>
<method name="get_pressed_button">
<return type="BaseButton">
</return>
<description>
- Return the pressed button.
+ Returns the current pressed button.
</description>
</method>
</methods>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 9e62cde019..898fce78c4 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -497,7 +497,7 @@
<return type="void">
</return>
<description>
- Show the CanvasItem currently hidden.
+ Show the CanvasItem currently hidden. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead.
</description>
</method>
<method name="update">
@@ -531,7 +531,7 @@
If [code]true[/code] the parent [code]CanvasItem[/code]'s [member material] property is used as this one's material. Default value: [code]false[/code].
</member>
<member name="visible" type="bool" setter="set_visible" getter="is_visible">
- If [code]true[/code] this [code]CanvasItem[/code] is drawn. Default value: [code]true[/code].
+ If [code]true[/code] this [code]CanvasItem[/code] is drawn. Default value: [code]true[/code]. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead.
</member>
</members>
<signals>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index 82a10fbaa4..68f5734491 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -19,11 +19,6 @@
</argument>
<description>
Constructs a color from an HTML hexadecimal color string in ARGB or RGB format. See also [method @GDScript.ColorN].
- The following string formats are supported:
- [code]"#ff00ff00"[/code] - ARGB format with '#'
- [code]"ff00ff00"[/code] - ARGB format
- [code]"#ff00ff"[/code] - RGB format with '#'
- [code]"ff00ff"[/code] - RGB format
[codeblock]
# Each of the following creates the same color RGBA(178, 217, 10, 255)
var c1 = Color("#ffb2d90a") # ARGB format with '#'
@@ -57,7 +52,7 @@
<description>
Constructs a color from an RGB profile using values between 0 and 1. Alpha will always be 1.
[codeblock]
- var c = Color(0.2, 1.0, .7) # Equivalent to RGBA(51, 255, 178, 255)
+ var c = Color(0.2, 1.0, 0.7) # Equivalent to RGBA(51, 255, 178, 255)
[/codeblock]
</description>
</method>
@@ -75,7 +70,7 @@
<description>
Constructs a color from an RGBA profile using values between 0 and 1.
[codeblock]
- var c = Color(0.2, 1.0, .7, .8) # Equivalent to RGBA(51, 255, 178, 204)
+ var c = Color(0.2, 1.0, 0.7, 0.8) # Equivalent to RGBA(51, 255, 178, 204)
[/codeblock]
</description>
</method>
@@ -88,8 +83,8 @@
Returns a new color resulting from blending this color over another. If the color is opaque, the result is also opaque. The second color may have a range of alpha values.
[codeblock]
var bg = Color(0.0, 1.0, 0.0, 0.5) # Green with alpha of 50%
- var fg = Color(1.0, 0.0, 0.0, .5) # Red with alpha of 50%
- var blendedColor = bg.blend(fg) # Brown with alpha of 75%
+ var fg = Color(1.0, 0.0, 0.0, 0.5) # Red with alpha of 50%
+ var blended_color = bg.blend(fg) # Brown with alpha of 75%
[/codeblock]
</description>
</method>
@@ -99,8 +94,8 @@
<description>
Returns the most contrasting color.
[codeblock]
- var c = Color(.3, .4, .9)
- var contrastedColor = c.contrasted() # Equivalent to RGBA(204, 229, 102, 255)
+ var c = Color(0.3, 0.4, 0.9)
+ var contrasted_color = c.contrasted() # Equivalent to RGBA(204, 229, 102, 255)
[/codeblock]
</description>
</method>
@@ -110,7 +105,7 @@
<argument index="0" name="amount" type="float">
</argument>
<description>
- Returns a new color resulting from making this color darker by the specified percentage (0-1).
+ Returns a new color resulting from making this color darker by the specified percentage (ratio from 0 to 1).
[codeblock]
var green = Color(0.0, 1.0, 0.0)
var darkgreen = green.darkened(0.2) # 20% darker than regular green
@@ -140,7 +135,7 @@
</return>
<description>
Returns the color's grayscale representation.
- The gray is calculated by [code](r + g + b) / 3[/code].
+ The gray value is calculated as [code](r + g + b) / 3[/code].
[codeblock]
var c = Color(0.2, 0.45, 0.82)
var gray = c.gray() # a value of 0.466667
@@ -153,8 +148,8 @@
<description>
Returns the inverted color [code](1 - r, 1 - g, 1 - b, 1 - a)[/code].
[codeblock]
- var c = Color(.3, .4, .9)
- var invertedColor = c.inverted() # a color of an RGBA(178, 153, 26, 255)
+ var c = Color(0.3, 0.4, 0.9)
+ var inverted_color = c.inverted() # a color of an RGBA(178, 153, 26, 255)
[/codeblock]
</description>
</method>
@@ -164,7 +159,7 @@
<argument index="0" name="amount" type="float">
</argument>
<description>
- Returns a new color resulting from making this color lighter by the specified percentage (0-1).
+ Returns a new color resulting from making this color lighter by the specified percentage (ratio from 0 to 1).
[codeblock]
var green = Color(0.0, 1.0, 0.0)
var lightgreen = green.lightened(0.2) # 20% lighter than regular green
@@ -179,7 +174,7 @@
<argument index="1" name="t" type="float">
</argument>
<description>
- Returns the linear interpolation with another color. The value t is between 0 and 1.
+ Returns the linear interpolation with another color. The interpolation factor [code]t[/code] is between 0 and 1.
[codeblock]
var c1 = Color(1.0, 0.0, 0.0)
var c2 = Color(0.0, 1.0, 0.0)
@@ -193,7 +188,7 @@
<description>
Returns the color's 32-bit integer in ABGR format (each byte represents a component of the ABGR profile). ABGR is the reversed version of the default format.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_abgr32()) # Prints 4281565439
[/codeblock]
</description>
@@ -204,7 +199,7 @@
<description>
Returns the color's 64-bit integer in ABGR format (each word represents a component of the ABGR profile). ABGR is the reversed version of the default format.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_abgr64()) # Prints -225178692812801
[/codeblock]
</description>
@@ -215,7 +210,7 @@
<description>
Returns the color's 32-bit integer in ARGB format (each byte represents a component of the ARGB profile). ARGB is more compatible with DirectX.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_argb32()) # Prints 4294934323
[/codeblock]
</description>
@@ -226,7 +221,7 @@
<description>
Returns the color's 64-bit integer in ARGB format (each word represents a component of the ARGB profile). ARGB is more compatible with DirectX.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_argb64()) # Prints -2147470541
[/codeblock]
</description>
@@ -240,7 +235,7 @@
Returns the color's HTML hexadecimal color string in ARGB format (ex: [code]ff34f822[/code]).
Setting [code]with_alpha[/code] to [code]false[/code] excludes alpha from the hexadecimal string.
[codeblock]
- var c = Color(1, 1, 1, .5)
+ var c = Color(1, 1, 1, 0.5)
var s1 = c.to_html() # Results "7fffffff"
var s2 = c.to_html(false) # Results 'ffffff'
[/codeblock]
@@ -252,7 +247,7 @@
<description>
Returns the color's 32-bit integer in RGBA format (each byte represents a component of the RGBA profile). RGBA is Godot's default format.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_rgba32()) # Prints 4286526463
[/codeblock]
</description>
@@ -263,7 +258,7 @@
<description>
Returns the color's 64-bit integer in RGBA format (each word represents a component of the RGBA profile). RGBA is Godot's default format.
[codeblock]
- var c = Color(1, .5, .2)
+ var c = Color(1, 0.5, 0.2)
print(c.to_rgba64()) # Prints -140736629309441
[/codeblock]
</description>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 24201c39b9..d1e90470e1 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -35,7 +35,7 @@
func get_import_options(i):
return [{"name": "my_option", "default_value": false}]
- func import(source_file, save_path, options, r_platform_variants, r_gen_files):
+ func import(source_file, save_path, options, platform_variants, gen_files):
var file = File.new()
if file.open(source_file, File.READ) != OK:
return FAILED
diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml
index 0c5de68d98..e69f3b0119 100644
--- a/doc/classes/EditorScenePostImport.xml
+++ b/doc/classes/EditorScenePostImport.xml
@@ -20,7 +20,7 @@
func iterate(node):
if node != null:
- node.name = "modified_"+node.name
+ node.name = "modified_" + node.name
for child in node.get_children():
iterate(child)
[/codeblock]
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 2f21505757..3172a5d6c0 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -108,14 +108,14 @@
Generates a GET/POST application/x-www-form-urlencoded style query string from a provided dictionary, e.g.:
[codeblock]
var fields = {"username": "user", "password": "pass"}
- String queryString = httpClient.query_string_from_dict(fields)
- returns:= "username=user&amp;password=pass"
+ String query_string = http_client.query_string_from_dict(fields)
+ # returns: "username=user&amp;password=pass"
[/codeblock]
Furthermore, if a key has a null value, only the key itself is added, without equal sign and value. If the value is an array, for each value in it a pair with the same key is added.
[codeblock]
var fields = {"single": 123, "not_valued": null, "multiple": [22, 33, 44]}
- String queryString = httpClient.query_string_from_dict(fields)
- returns:= "single=123&amp;not_valued&amp;multiple=22&amp;multiple=33&amp;multiple=44"
+ String query_string = http_client.query_string_from_dict(fields)
+ # returns: "single=123&amp;not_valued&amp;multiple=22&amp;multiple=33&amp;multiple=44"
[/codeblock]
</description>
</method>
@@ -143,9 +143,9 @@
To create a POST request with query strings to push to the server, do:
[codeblock]
var fields = {"username" : "user", "password" : "pass"}
- var queryString = httpClient.query_string_from_dict(fields)
- var headers = ["Content-Type: application/x-www-form-urlencoded", "Content-Length: " + str(queryString.length())]
- var result = httpClient.request(httpClient.METHOD_POST, "index.php", headers, queryString)
+ var query_string = http_client.query_string_from_dict(fields)
+ var headers = ["Content-Type: application/x-www-form-urlencoded", "Content-Length: " + str(query_string.length())]
+ var result = http_client.request(http_client.METHOD_POST, "index.php", headers, query_string)
[/codeblock]
</description>
</method>
diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml
index c90dba1561..7bd98323c7 100644
--- a/doc/classes/MeshDataTool.xml
+++ b/doc/classes/MeshDataTool.xml
@@ -11,9 +11,9 @@
var mdt = MeshDataTool.new()
mdt.create_from_surface(mesh, 0)
for i in range(mdt.get_vertex_count()):
- var vertex = mdt.get_vertex(i)
- ...
- mdt.set_vertex(i, vertex)
+ var vertex = mdt.get_vertex(i)
+ ...
+ mdt.set_vertex(i, vertex)
mesh.surface_remove(0)
mdt.commit_to_surface(mesh)
[/codeblock]
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 7a9918237f..1d2057eb4e 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -172,8 +172,8 @@
Path to an image used for boot splash.
</member>
<member name="application/config/custom_user_dir_name" type="String" setter="" getter="">
- This directory is used for storing persistent data (user:// filesystem). If a custom name is set, then system paths will be used to store this on Desktop (AppData on Windows, user ~/.config on Unixes, etc), else the Godot config folder is used. This name needs to be unique, and it's recommended to set it to something before publishing.
- the "use_custom_user_dir" setting must be enabled for this to take effect.
+ This user directory is used for storing persistent data ([code]user://[/code] filesystem). By default (no custom name defined), [code]user://[/code] resolves to a project-specific folder in Godot's own configuration folder (see [method OS.get_user_data_dir]). If a custom directory name is defined, this name will be used instead and appended to the system-specific user data directory (same parent folder as the Godot configuration folder documented in [method OS.get_user_data_dir]).
+ The [member use_custom_user_dir] setting must be enabled for this to take effect.
</member>
<member name="application/config/icon" type="String" setter="" getter="">
Icon used for the project, set when project loads. Exporters will use this icon when possible to.
@@ -181,8 +181,11 @@
<member name="application/config/name" type="String" setter="" getter="">
Name of the project. It is used from both project manager and by the exporters. Overriding this as name.locale allows setting it in multiple languages.
</member>
+ <member name="application/config/project_settings_override" type="String" setter="" getter="">
+ Specifies a file to override project settings. For example: [code]user://custom_settings.cfg[/code].
+ </member>
<member name="application/config/use_custom_user_dir" type="bool" setter="" getter="">
- Allow the project to save to its own custom user dir (in AppData on windows or ~/.config on unixes). This setting only works for desktop exporters. A name must be set in the "custom_user_dir_name" setting for this to take effect.
+ Allow the project to save to its own custom user dir (see [member custom_user_dir_name]). This setting only works for desktop platforms. A name must be set in the [member custom_user_dir_name] setting for this to take effect.
</member>
<member name="application/run/disable_stderr" type="bool" setter="" getter="">
Disable printing to stderr on exported build.
@@ -210,6 +213,9 @@
</member>
<member name="audio/driver" type="String" setter="" getter="">
</member>
+ <member name="audio/enable_audio_input" type="bool" setter="" getter="">
+ This option should be enabled if project works with microphone.
+ </member>
<member name="audio/mix_rate" type="int" setter="" getter="">
Mix rate used for audio. In general, it's better to not touch this and leave it to the host operating system.
</member>
@@ -577,10 +583,10 @@
Log all output to a file.
</member>
<member name="logging/file_logging/log_path" type="String" setter="" getter="">
- Path to logs withint he project. Using an user:// based path is recommended.
+ Path to logs withint he project. Using an [code]user://[/code] based path is recommended.
</member>
<member name="logging/file_logging/max_log_files" type="int" setter="" getter="">
- Amount of log files (used for rotation)/
+ Amount of log files (used for rotation).
</member>
<member name="memory/limits/message_queue/max_size_kb" type="int" setter="" getter="">
Godot uses a message queue to defer some function calls. If you run out of space on it (you will see an error), you can increase the size here.
diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml
index 67ce9a8e87..ac0384e1d4 100644
--- a/doc/classes/ScriptCreateDialog.xml
+++ b/doc/classes/ScriptCreateDialog.xml
@@ -7,9 +7,9 @@
The ScriptCreateDialog creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling a [method popup]() method.
[codeblock]
func _ready():
- dialog.config("Node", "res://new_node.gd") # for in-engine types
- dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # for script types
- dialog.popup_centered()
+ dialog.config("Node", "res://new_node.gd") # for in-engine types
+ dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # for script types
+ dialog.popup_centered()
[/codeblock]
</description>
<tutorials>
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 726cee10a4..3d093b965a 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -159,7 +159,10 @@ Error AudioDriverCoreAudio::init() {
result = AudioUnitInitialize(audio_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
- return capture_init();
+ if (GLOBAL_GET("audio/enable_audio_input")) {
+ return capture_init();
+ }
+ return OK;
}
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 81d9442656..2360e5f410 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -1699,6 +1699,12 @@ EditorFileDialog::~EditorFileDialog() {
memdelete(dir_access);
}
+void EditorLineEditFileChooser::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED)
+ button->set_icon(get_icon("Folder", "EditorIcons"));
+}
+
void EditorLineEditFileChooser::_bind_methods() {
ClassDB::bind_method(D_METHOD("_browse"), &EditorLineEditFileChooser::_browse);
@@ -1725,7 +1731,6 @@ EditorLineEditFileChooser::EditorLineEditFileChooser() {
add_child(line_edit);
line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
button = memnew(Button);
- button->set_text(" .. ");
add_child(button);
button->connect("pressed", this, "_browse");
dialog = memnew(EditorFileDialog);
diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h
index 56cefb9a47..574fefcab4 100644
--- a/editor/editor_file_dialog.h
+++ b/editor/editor_file_dialog.h
@@ -254,6 +254,7 @@ class EditorLineEditFileChooser : public HBoxContainer {
void _browse();
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index ff7ab051b5..b992b90758 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -2421,6 +2421,7 @@ void EditorPropertyResource::update_property() {
sub_inspector->set_keying(is_keying());
sub_inspector->set_read_only(is_read_only());
sub_inspector->set_use_folding(is_using_folding());
+ sub_inspector->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());
sub_inspector_vbox = memnew(VBoxContainer);
add_child(sub_inspector_vbox);
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 750fca2852..d201087ecf 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -334,6 +334,7 @@ void InspectorDock::_notification(int p_what) {
set_theme(editor->get_gui_base()->get_theme());
resource_new_button->set_icon(get_icon("New", "EditorIcons"));
resource_load_button->set_icon(get_icon("Load", "EditorIcons"));
+ resource_save_button->set_icon(get_icon("Save", "EditorIcons"));
backward_button->set_icon(get_icon("Back", "EditorIcons"));
forward_button->set_icon(get_icon("Forward", "EditorIcons"));
history_menu->set_icon(get_icon("History", "EditorIcons"));
@@ -416,6 +417,7 @@ void InspectorDock::update(Object *p_object) {
object_menu->set_disabled(false);
search->set_editable(true);
+ resource_save_button->set_disabled(!is_resource);
PopupMenu *p = object_menu->get_popup();
@@ -515,6 +517,16 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
resource_load_button->connect("pressed", this, "_open_resource_selector");
resource_load_button->set_focus_mode(Control::FOCUS_NONE);
+ resource_save_button = memnew(MenuButton);
+ resource_save_button->set_tooltip(TTR("Save the currently edited resource."));
+ resource_save_button->set_icon(get_icon("Save", "EditorIcons"));
+ general_options_hb->add_child(resource_save_button);
+ resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE);
+ resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS);
+ resource_save_button->get_popup()->connect("id_pressed", this, "_menu_option");
+ resource_save_button->set_focus_mode(Control::FOCUS_NONE);
+ resource_save_button->set_disabled(true);
+
general_options_hb->add_spacer();
backward_button = memnew(ToolButton);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 95f0c4870e..7205f34343 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -1148,7 +1148,7 @@ SceneTreeDialog::SceneTreeDialog() {
set_title(TTR("Select a Node"));
- tree = memnew(SceneTreeEditor(false, false));
+ tree = memnew(SceneTreeEditor(false, false, true));
add_child(tree);
//set_child_rect(tree);
diff --git a/main/input_default.cpp b/main/input_default.cpp
index b1084900d6..67301a38f3 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -280,7 +280,7 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && !mb->is_doubleclick()) {
+ if (mb.is_valid()) {
if (mb->is_pressed()) {
mouse_button_mask |= (1 << (mb->get_button_index() - 1));
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index f4b061f494..80fa2454ec 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -48,18 +48,76 @@ void CSGShape::set_use_collision(bool p_enable) {
PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
+ set_collision_layer(collision_layer);
+ set_collision_mask(collision_mask);
_make_dirty(); //force update
} else {
PhysicsServer::get_singleton()->free(root_collision_instance);
root_collision_instance = RID();
root_collision_shape.unref();
}
+ _change_notify();
}
bool CSGShape::is_using_collision() const {
return use_collision;
}
+void CSGShape::set_collision_layer(uint32_t p_layer) {
+ collision_layer = p_layer;
+ if (root_collision_instance.is_valid()) {
+ PhysicsServer::get_singleton()->body_set_collision_layer(root_collision_instance, p_layer);
+ }
+}
+
+uint32_t CSGShape::get_collision_layer() const {
+
+ return collision_layer;
+}
+
+void CSGShape::set_collision_mask(uint32_t p_mask) {
+
+ collision_mask = p_mask;
+ if (root_collision_instance.is_valid()) {
+ PhysicsServer::get_singleton()->body_set_collision_mask(root_collision_instance, p_mask);
+ }
+}
+
+uint32_t CSGShape::get_collision_mask() const {
+
+ return collision_mask;
+}
+
+void CSGShape::set_collision_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_mask(mask);
+}
+
+bool CSGShape::get_collision_mask_bit(int p_bit) const {
+
+ return get_collision_mask() & (1 << p_bit);
+}
+
+void CSGShape::set_collision_layer_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_layer();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_layer(mask);
+}
+
+bool CSGShape::get_collision_layer_bit(int p_bit) const {
+
+ return get_collision_layer() & (1 << p_bit);
+}
+
bool CSGShape::is_root_shape() const {
return !parent;
@@ -459,6 +517,8 @@ void CSGShape::_notification(int p_what) {
PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
PhysicsServer::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
+ set_collision_layer(collision_layer);
+ set_collision_mask(collision_mask);
}
_make_dirty();
@@ -477,7 +537,7 @@ void CSGShape::_notification(int p_what) {
parent->_make_dirty();
parent = NULL;
- if (use_collision && is_root_shape()) {
+ if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
PhysicsServer::get_singleton()->free(root_collision_instance);
root_collision_instance = RID();
root_collision_shape.unref();
@@ -506,9 +566,12 @@ bool CSGShape::is_calculating_tangents() const {
}
void CSGShape::_validate_property(PropertyInfo &property) const {
- if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
+ bool is_collision_prefixed = property.name.begins_with("collision_");
+ if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
//hide collision if not root
property.usage = PROPERTY_USAGE_NOEDITOR;
+ } else if (is_collision_prefixed && !bool(get("use_collision"))) {
+ property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
}
@@ -520,34 +583,52 @@ void CSGShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation);
ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation);
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
+ ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+
ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision);
ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision);
- ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
- ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+ ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CSGShape::set_collision_layer);
+ ClassDB::bind_method(D_METHOD("get_collision_layer"), &CSGShape::get_collision_layer);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape::get_collision_mask_bit);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape::set_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape::get_collision_layer_bit);
ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape::set_calculate_tangents);
ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape::is_calculating_tangents);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
BIND_ENUM_CONSTANT(OPERATION_SUBTRACTION);
}
CSGShape::CSGShape() {
+ operation = OPERATION_UNION;
+ parent = NULL;
brush = NULL;
- set_notify_local_transform(true);
dirty = false;
- parent = NULL;
- use_collision = false;
- operation = OPERATION_UNION;
snap = 0.001;
+ use_collision = false;
+ collision_layer = 1;
+ collision_mask = 1;
calculate_tangents = true;
+ set_notify_local_transform(true);
}
CSGShape::~CSGShape() {
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 7326f3d36a..28d94bc2f2 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -61,6 +61,8 @@ private:
float snap;
bool use_collision;
+ uint32_t collision_layer;
+ uint32_t collision_mask;
Ref<ConcavePolygonShape> root_collision_shape;
RID root_collision_instance;
@@ -126,6 +128,18 @@ public:
void set_use_collision(bool p_enable);
bool is_using_collision() const;
+ void set_collision_layer(uint32_t p_layer);
+ uint32_t get_collision_layer() const;
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_layer_bit(int p_bit, bool p_value);
+ bool get_collision_layer_bit(int p_bit) const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
void set_snap(float p_snap);
float get_snap() const;
diff --git a/modules/csg/doc_classes/CSGCombiner.xml b/modules/csg/doc_classes/CSGCombiner.xml
index 1cfaa74b7d..69c5df5840 100644
--- a/modules/csg/doc_classes/CSGCombiner.xml
+++ b/modules/csg/doc_classes/CSGCombiner.xml
@@ -4,7 +4,7 @@
A CSG node that allows you to combine other CSG modifiers.
</brief_description>
<description>
- For complex arrangements of shapes it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as their input to create the final shape.
+ For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner node, and then do an operation that takes the two end results as its input to create the final shape.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGMesh.xml b/modules/csg/doc_classes/CSGMesh.xml
index 419214b7e6..58e2bc1c4b 100644
--- a/modules/csg/doc_classes/CSGMesh.xml
+++ b/modules/csg/doc_classes/CSGMesh.xml
@@ -4,7 +4,7 @@
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
- This CSG node allows you to use any mesh resource as a CSG shape provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
+ This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGShape.xml b/modules/csg/doc_classes/CSGShape.xml
index ac3c2342fc..56087cbb82 100644
--- a/modules/csg/doc_classes/CSGShape.xml
+++ b/modules/csg/doc_classes/CSGShape.xml
@@ -11,6 +11,24 @@
<demos>
</demos>
<methods>
+ <method name="get_collision_layer_bit" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="bit" type="int">
+ </argument>
+ <description>
+ Returns an individual bit on the collision mask.
+ </description>
+ </method>
+ <method name="get_collision_mask_bit" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="bit" type="int">
+ </argument>
+ <description>
+ Returns an individual bit on the collision mask.
+ </description>
+ </method>
<method name="is_root_shape" qualifiers="const">
<return type="bool">
</return>
@@ -18,11 +36,41 @@
Returns true if this is a root shape and is thus the object that is rendered.
</description>
</method>
+ <method name="set_collision_layer_bit">
+ <return type="void">
+ </return>
+ <argument index="0" name="bit" type="int">
+ </argument>
+ <argument index="1" name="value" type="bool">
+ </argument>
+ <description>
+ Sets individual bits on the layer mask. Use this if you only need to change one layer's value.
+ </description>
+ </method>
+ <method name="set_collision_mask_bit">
+ <return type="void">
+ </return>
+ <argument index="0" name="bit" type="int">
+ </argument>
+ <argument index="1" name="value" type="bool">
+ </argument>
+ <description>
+ Sets individual bits on the collision mask. Use this if you only need to change one layer's value.
+ </description>
+ </method>
</methods>
<members>
<member name="calculate_tangents" type="bool" setter="set_calculate_tangents" getter="is_calculating_tangents">
Calculate tangents for the CSG shape which allows the use of normal maps. This is only applied on the root shape, this setting is ignored on any child.
</member>
+ <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer">
+ The physics layers this area is in.
+ Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
+ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A.
+ </member>
+ <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask">
+ The physics layers this CSG shape scans for collisions.
+ </member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape.Operation">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index a3b5b6dd58..7eda32b754 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -551,12 +551,15 @@ class EditorExportAndroid : public EditorExportPlatform {
}
static Vector<String> get_abis() {
- // mips and armv6 are dead (especially for games), so not including them
Vector<String> abis;
+ // We can still build armv7 in theory, but it doesn't make much
+ // sense for games, so disabling for now.
+ //abis.push_back("armeabi");
abis.push_back("armeabi-v7a");
abis.push_back("arm64-v8a");
abis.push_back("x86");
- abis.push_back("x86_64");
+ // Don't expose x86_64 for now, we don't support it in detect.py
+ //abis.push_back("x86_64");
return abis;
}
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 9250ca4903..1a40f6a979 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -295,6 +295,30 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM
default: return false;
}
+ if (ev->is_pressed()) {
+
+ uint64_t diff = p_event->timestamp - os->last_click_ms;
+
+ if (ev->get_button_index() == os->last_click_button_index) {
+
+ if (diff < 400 && Point2(os->last_click_pos).distance_to(ev->get_position()) < 5) {
+
+ os->last_click_ms = 0;
+ os->last_click_pos = Point2(-100, -100);
+ os->last_click_button_index = -1;
+ ev->set_doubleclick(true);
+ }
+
+ } else {
+ os->last_click_button_index = ev->get_button_index();
+ }
+
+ if (!ev->is_doubleclick()) {
+ os->last_click_ms += diff;
+ os->last_click_pos = ev->get_position();
+ }
+ }
+
int mask = os->input->get_mouse_button_mask();
int button_flag = 1 << (ev->get_button_index() - 1);
if (ev->is_pressed()) {
@@ -452,7 +476,6 @@ EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEve
InputDefault *input = get_singleton()->input;
Ref<InputEventMouseButton> ev;
ev.instance();
- ev->set_button_mask(input->get_mouse_button_mask());
ev->set_position(input->get_mouse_position());
ev->set_global_position(ev->get_position());
@@ -475,10 +498,14 @@ EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEve
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
+ int button_flag = 1 << (ev->get_button_index() - 1);
+
ev->set_pressed(true);
+ ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
input->parse_input_event(ev);
ev->set_pressed(false);
+ ev->set_button_mask(input->get_mouse_button_mask() & ~button_flag);
input->parse_input_event(ev);
return true;
@@ -1067,6 +1094,10 @@ OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) {
}
set_cmdline(p_argv[0], arguments);
+ last_click_button_index = -1;
+ last_click_ms = 0;
+ last_click_pos = Point2(-100, -100);
+
window_maximized = false;
entering_fullscreen = false;
just_exited_fullscreen = false;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 79dac5940f..45c1a9d32d 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -52,6 +52,10 @@ class OS_JavaScript : public OS_Unix {
CursorShape cursor_shape;
Point2 touches[32];
+ Point2i last_click_pos;
+ uint64_t last_click_ms;
+ int last_click_button_index;
+
MainLoop *main_loop;
AudioDriverJavaScript audio_driver_javascript;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index f8dec3ec7a..dd82e982ed 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1075,6 +1075,8 @@ static int remapKey(unsigned int key) {
inline void sendScrollEvent(int button, double factor, int modifierFlags) {
+ unsigned int mask = 1 << (button - 1);
+
Ref<InputEventMouseButton> sc;
sc.instance();
@@ -1085,9 +1087,13 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) {
Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
sc->set_position(mouse_pos);
sc->set_global_position(mouse_pos);
+ button_mask |= mask;
sc->set_button_mask(button_mask);
OS_OSX::singleton->push_input(sc);
+
sc->set_pressed(false);
+ button_mask &= ~mask;
+ sc->set_button_mask(button_mask);
OS_OSX::singleton->push_input(sc);
}
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 55d2bb2153..e1cbbcc66b 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -496,15 +496,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
mm->set_shift((wParam & MK_SHIFT) != 0);
mm->set_alt(alt_mem);
- int bmask = 0;
- bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0;
- bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0;
- bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0;
- bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0;
- bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0;
- mm->set_button_mask(bmask);
-
- last_button_state = mm->get_button_mask();
+ mm->set_button_mask(last_button_state);
mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
@@ -673,15 +665,12 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
mb->set_shift((wParam & MK_SHIFT) != 0);
mb->set_alt(alt_mem);
//mb->get_alt()=(wParam&MK_MENU)!=0;
- int bmask = 0;
- bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0;
- bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0;
- bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0;
- bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0;
- bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0;
- mb->set_button_mask(bmask);
-
- last_button_state = mb->get_button_mask();
+ if (mb->is_pressed())
+ last_button_state |= (1 << (mb->get_button_index() - 1));
+ else
+ last_button_state &= ~(1 << (mb->get_button_index() - 1));
+ mb->set_button_mask(last_button_state);
+
mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
@@ -721,6 +710,8 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) {
//send release for mouse wheel
Ref<InputEventMouseButton> mbd = mb->duplicate();
+ last_button_state &= ~(1 << (mbd->get_button_index() - 1));
+ mbd->set_button_mask(last_button_state);
mbd->set_pressed(false);
input->parse_input_event(mbd);
}
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 524c8448bc..9b6fb2f478 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -48,6 +48,11 @@ def can_build():
print("xrender not found.. x11 disabled.")
return False
+ x11_error = os.system("pkg-config xi --modversion > /dev/null ")
+ if (x11_error):
+ print("xi not found.. Aborting.")
+ return False
+
return True
def get_opts():
@@ -170,13 +175,9 @@ def configure(env):
env.ParseConfig('pkg-config xinerama --cflags --libs')
env.ParseConfig('pkg-config xrandr --cflags --libs')
env.ParseConfig('pkg-config xrender --cflags --libs')
+ env.ParseConfig('pkg-config xi --cflags --libs')
if (env['touch']):
- x11_error = os.system("pkg-config xi --modversion > /dev/null ")
- if (x11_error):
- print("xi not found.. cannot build with touch. Aborting.")
- sys.exit(255)
- env.ParseConfig('pkg-config xi --cflags --libs')
env.Append(CPPFLAGS=['-DTOUCH_ENABLED'])
# FIXME: Check for existence of the libs before parsing their flags with pkg-config
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 0c02e47b5e..34ec8709c4 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -77,6 +77,13 @@
#include <X11/XKBlib.h>
+// 2.2 is the first release with multitouch
+#define XINPUT_CLIENT_VERSION_MAJOR 2
+#define XINPUT_CLIENT_VERSION_MINOR 2
+
+static const double abs_resolution_mult = 10000.0;
+static const double abs_resolution_range_mult = 10.0;
+
void OS_X11::initialize_core() {
crash_handler.initialize();
@@ -96,6 +103,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
xmbstring = NULL;
x11_window = 0;
last_click_ms = 0;
+ last_click_button_index = -1;
+ last_click_pos = Point2(-100, -100);
args = OS::get_singleton()->get_cmdline_args();
current_videomode = p_desired;
main_loop = NULL;
@@ -168,48 +177,12 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
}
}
-#ifdef TOUCH_ENABLED
- if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) {
- print_verbose("XInput extension not available, touch support disabled.");
- } else {
- // 2.2 is the first release with multitouch
- int xi_major = 2;
- int xi_minor = 2;
- if (XIQueryVersion(x11_display, &xi_major, &xi_minor) != Success) {
- print_verbose(vformat("XInput 2.2 not available (server supports %d.%d), touch support disabled.", xi_major, xi_minor));
- touch.opcode = 0;
- } else {
- int dev_count;
- XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
-
- for (int i = 0; i < dev_count; i++) {
- XIDeviceInfo *dev = &info[i];
- if (!dev->enabled)
- continue;
- if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave))
- continue;
-
- bool direct_touch = false;
- for (int j = 0; j < dev->num_classes; j++) {
- if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
- direct_touch = true;
- break;
- }
- }
- if (direct_touch) {
- touch.devices.push_back(dev->deviceid);
- print_verbose("XInput: Using touch device: " + String(dev->name));
- }
- }
-
- XIFreeDeviceInfo(info);
-
- if (!touch.devices.size()) {
- print_verbose("XInput: No touch devices found.");
- }
- }
+ if (!refresh_device_info()) {
+ OS::get_singleton()->alert("Your system does not support XInput 2.\n"
+ "Please upgrade your distribution.",
+ "Unable to initialize XInput");
+ return ERR_UNAVAILABLE;
}
-#endif
xim = XOpenIM(x11_display, NULL, NULL, NULL);
@@ -413,34 +386,42 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
-#ifdef TOUCH_ENABLED
- if (touch.devices.size()) {
-
- // Must be alive after this block
- static unsigned char mask_data[XIMaskLen(XI_LASTEVENT)] = {};
+ static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
+ static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
- touch.event_mask.deviceid = XIAllDevices;
- touch.event_mask.mask_len = sizeof(mask_data);
- touch.event_mask.mask = mask_data;
+ xi.all_event_mask.deviceid = XIAllDevices;
+ xi.all_event_mask.mask_len = sizeof(all_mask_data);
+ xi.all_event_mask.mask = all_mask_data;
- XISetMask(touch.event_mask.mask, XI_TouchBegin);
- XISetMask(touch.event_mask.mask, XI_TouchUpdate);
- XISetMask(touch.event_mask.mask, XI_TouchEnd);
- XISetMask(touch.event_mask.mask, XI_TouchOwnership);
+ xi.all_master_event_mask.deviceid = XIAllMasterDevices;
+ xi.all_master_event_mask.mask_len = sizeof(all_master_mask_data);
+ xi.all_master_event_mask.mask = all_master_mask_data;
- XISelectEvents(x11_display, x11_window, &touch.event_mask, 1);
+ XISetMask(xi.all_event_mask.mask, XI_HierarchyChanged);
+ XISetMask(xi.all_master_event_mask.mask, XI_DeviceChanged);
+ XISetMask(xi.all_master_event_mask.mask, XI_RawMotion);
- // Disabled by now since grabbing also blocks mouse events
- // (they are received as extended events instead of standard events)
- /*XIClearMask(touch.event_mask.mask, XI_TouchOwnership);
-
- // Grab touch devices to avoid OS gesture interference
- for (int i = 0; i < touch.devices.size(); ++i) {
- XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask);
- }*/
+#ifdef TOUCH_ENABLED
+ if (xi.touch_devices.size()) {
+ XISetMask(xi.all_event_mask.mask, XI_TouchBegin);
+ XISetMask(xi.all_event_mask.mask, XI_TouchUpdate);
+ XISetMask(xi.all_event_mask.mask, XI_TouchEnd);
+ XISetMask(xi.all_event_mask.mask, XI_TouchOwnership);
}
#endif
+ XISelectEvents(x11_display, x11_window, &xi.all_event_mask, 1);
+ XISelectEvents(x11_display, DefaultRootWindow(x11_display), &xi.all_master_event_mask, 1);
+
+ // Disabled by now since grabbing also blocks mouse events
+ // (they are received as extended events instead of standard events)
+ /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
+
+ // Grab touch devices to avoid OS gesture interference
+ for (int i = 0; i < xi.touch_devices.size(); ++i) {
+ XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
+ }*/
+
/* set the titlebar name */
XStoreName(x11_display, x11_window, "Godot");
@@ -590,6 +571,101 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
return OK;
}
+bool OS_X11::refresh_device_info() {
+ int event_base, error_base;
+
+ print_verbose("XInput: Refreshing devices.");
+
+ if (!XQueryExtension(x11_display, "XInputExtension", &xi.opcode, &event_base, &error_base)) {
+ print_verbose("XInput extension not available. Please upgrade your distribution.");
+ return false;
+ }
+
+ int xi_major_query = XINPUT_CLIENT_VERSION_MAJOR;
+ int xi_minor_query = XINPUT_CLIENT_VERSION_MINOR;
+
+ if (XIQueryVersion(x11_display, &xi_major_query, &xi_minor_query) != Success) {
+ print_verbose(vformat("XInput 2 not available (server supports %d.%d).", xi_major_query, xi_minor_query));
+ xi.opcode = 0;
+ return false;
+ }
+
+ if (xi_major_query < XINPUT_CLIENT_VERSION_MAJOR || (xi_major_query == XINPUT_CLIENT_VERSION_MAJOR && xi_minor_query < XINPUT_CLIENT_VERSION_MINOR)) {
+ print_verbose(vformat("XInput %d.%d not available (server supports %d.%d). Touch input unavailable.",
+ XINPUT_CLIENT_VERSION_MAJOR, XINPUT_CLIENT_VERSION_MINOR, xi_major_query, xi_minor_query));
+ }
+
+ xi.absolute_devices.clear();
+ xi.touch_devices.clear();
+
+ int dev_count;
+ XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count);
+
+ for (int i = 0; i < dev_count; i++) {
+ XIDeviceInfo *dev = &info[i];
+ if (!dev->enabled)
+ continue;
+ if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave))
+ continue;
+
+ bool direct_touch = false;
+ bool absolute_mode = false;
+ int resolution_x = 0;
+ int resolution_y = 0;
+ int range_min_x = 0;
+ int range_min_y = 0;
+ int range_max_x = 0;
+ int range_max_y = 0;
+ for (int j = 0; j < dev->num_classes; j++) {
+#ifdef TOUCH_ENABLED
+ if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
+ direct_touch = true;
+ }
+#endif
+ if (dev->classes[j]->type == XIValuatorClass) {
+ XIValuatorClassInfo *class_info = (XIValuatorClassInfo *)dev->classes[j];
+
+ if (class_info->number == 0 && class_info->mode == XIModeAbsolute) {
+ resolution_x = class_info->resolution;
+ range_min_x = class_info->min;
+ range_max_x = class_info->max;
+ absolute_mode = true;
+ } else if (class_info->number == 1 && class_info->mode == XIModeAbsolute) {
+ resolution_y = class_info->resolution;
+ range_min_y = class_info->min;
+ range_max_y = class_info->max;
+ absolute_mode = true;
+ }
+ }
+ }
+ if (direct_touch) {
+ xi.touch_devices.push_back(dev->deviceid);
+ print_verbose("XInput: Using touch device: " + String(dev->name));
+ }
+ if (absolute_mode) {
+ // If no resolution was reported, use the min/max ranges.
+ if (resolution_x <= 0) {
+ resolution_x = (range_max_x - range_min_x) * abs_resolution_range_mult;
+ }
+ if (resolution_y <= 0) {
+ resolution_y = (range_max_y - range_min_y) * abs_resolution_range_mult;
+ }
+
+ xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y);
+ print_verbose("XInput: Absolute pointing device: " + String(dev->name));
+ }
+ }
+
+ XIFreeDeviceInfo(info);
+#ifdef TOUCH_ENABLED
+ if (!xi.touch_devices.size()) {
+ print_verbose("XInput: No touch devices found.");
+ }
+#endif
+
+ return true;
+}
+
void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
@@ -662,10 +738,10 @@ void OS_X11::finalize() {
#ifdef JOYDEV_ENABLED
memdelete(joypad);
#endif
-#ifdef TOUCH_ENABLED
- touch.devices.clear();
- touch.state.clear();
-#endif
+
+ xi.touch_devices.clear();
+ xi.state.clear();
+
memdelete(input);
visual_server->finish();
@@ -725,21 +801,8 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
- while (true) {
- //flush pending motion events
-
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
- if (event.type == MotionNotify) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
- }
- }
+ //flush pending motion events
+ flush_mouse_motion();
if (XGrabPointer(
x11_display, x11_window, True,
@@ -780,6 +843,32 @@ void OS_X11::warp_mouse_position(const Point2 &p_to) {
}
}
+void OS_X11::flush_mouse_motion() {
+ while (true) {
+ if (XPending(x11_display) > 0) {
+ XEvent event;
+ XPeekEvent(x11_display, &event);
+
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+
+ if (event_data->evtype == XI_RawMotion) {
+ XNextEvent(x11_display, &event);
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ xi.relative_motion.x = 0;
+ xi.relative_motion.y = 0;
+}
+
OS::MouseMode OS_X11::get_mouse_mode() const {
return mouse_mode;
}
@@ -1433,37 +1522,17 @@ void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWith
state->set_metakey((p_x11_state & Mod4Mask));
}
-unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_state) {
-
- unsigned int state = 0;
-
- if (p_x11_state & Button1Mask) {
-
- state |= 1 << 0;
- }
-
- if (p_x11_state & Button3Mask) {
-
- state |= 1 << 1;
- }
-
- if (p_x11_state & Button2Mask) {
+unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
- state |= 1 << 2;
- }
+ unsigned int mask = 1 << (p_x11_button - 1);
- if (p_x11_state & Button4Mask) {
-
- state |= 1 << 3;
- }
-
- if (p_x11_state & Button5Mask) {
-
- state |= 1 << 4;
+ if (p_x11_type == ButtonPress) {
+ last_button_state |= mask;
+ } else {
+ last_button_state &= ~mask;
}
- last_button_state = state;
- return state;
+ return last_button_state;
}
void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
@@ -1796,17 +1865,61 @@ void OS_X11::process_xevents() {
continue;
}
-#ifdef TOUCH_ENABLED
if (XGetEventData(x11_display, &event.xcookie)) {
- if (event.xcookie.type == GenericEvent && event.xcookie.extension == touch.opcode) {
+ if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
int index = event_data->detail;
Vector2 pos = Vector2(event_data->event_x, event_data->event_y);
switch (event_data->evtype) {
+ case XI_HierarchyChanged:
+ case XI_DeviceChanged: {
+ refresh_device_info();
+ } break;
+ case XI_RawMotion: {
+ XIRawEvent *raw_event = (XIRawEvent *)event_data;
+ int device_id = raw_event->deviceid;
+
+ // Determine the axis used (called valuators in XInput for some forsaken reason)
+ // Mask is a bitmask indicating which axes are involved.
+ // We are interested in the values of axes 0 and 1.
+ if (raw_event->valuators.mask_len <= 0 || !XIMaskIsSet(raw_event->valuators.mask, 0) || !XIMaskIsSet(raw_event->valuators.mask, 1)) {
+ break;
+ }
+
+ double rel_x = raw_event->raw_values[0];
+ double rel_y = raw_event->raw_values[1];
+
+ // https://bugs.freedesktop.org/show_bug.cgi?id=71609
+ // http://lists.libsdl.org/pipermail/commits-libsdl.org/2015-June/000282.html
+ if (raw_event->time == xi.last_relative_time && rel_x == xi.relative_motion.x && rel_y == xi.relative_motion.y) {
+ break; // Flush duplicate to avoid overly fast motion
+ }
+
+ xi.old_raw_pos.x = xi.raw_pos.x;
+ xi.old_raw_pos.y = xi.raw_pos.y;
+ xi.raw_pos.x = rel_x;
+ xi.raw_pos.y = rel_y;
+
+ Map<int, Vector2>::Element *abs_info = xi.absolute_devices.find(device_id);
+
+ if (abs_info) {
+ // Absolute mode device
+ Vector2 mult = abs_info->value();
+ xi.relative_motion.x += (xi.raw_pos.x - xi.old_raw_pos.x) * mult.x;
+ xi.relative_motion.y += (xi.raw_pos.y - xi.old_raw_pos.y) * mult.y;
+ } else {
+ // Relative mode device
+ xi.relative_motion.x = xi.raw_pos.x;
+ xi.relative_motion.y = xi.raw_pos.y;
+ }
+
+ xi.last_relative_time = raw_event->time;
+ } break;
+#ifdef TOUCH_ENABLED
case XI_TouchBegin: // Fall-through
// Disabled hand-in-hand with the grabbing
//XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
@@ -1822,26 +1935,26 @@ void OS_X11::process_xevents() {
st->set_pressed(is_begin);
if (is_begin) {
- if (touch.state.has(index)) // Defensive
+ if (xi.state.has(index)) // Defensive
break;
- touch.state[index] = pos;
- if (touch.state.size() == 1) {
+ xi.state[index] = pos;
+ if (xi.state.size() == 1) {
// X11 may send a motion event when a touch gesture begins, that would result
// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
- touch.mouse_pos_to_filter = pos;
+ xi.mouse_pos_to_filter = pos;
}
input->parse_input_event(st);
} else {
- if (!touch.state.has(index)) // Defensive
+ if (!xi.state.has(index)) // Defensive
break;
- touch.state.erase(index);
+ xi.state.erase(index);
input->parse_input_event(st);
}
} break;
case XI_TouchUpdate: {
- Map<int, Vector2>::Element *curr_pos_elem = touch.state.find(index);
+ Map<int, Vector2>::Element *curr_pos_elem = xi.state.find(index);
if (!curr_pos_elem) { // Defensive
break;
}
@@ -1858,11 +1971,11 @@ void OS_X11::process_xevents() {
curr_pos_elem->value() = pos;
}
} break;
+#endif
}
}
}
XFreeEventData(x11_display, &event.xcookie);
-#endif
switch (event.type) {
case Expose:
@@ -1908,8 +2021,8 @@ void OS_X11::process_xevents() {
}
#ifdef TOUCH_ENABLED
// Grab touch devices to avoid OS gesture interference
- /*for (int i = 0; i < touch.devices.size(); ++i) {
- XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask);
+ /*for (int i = 0; i < xi.touch_devices.size(); ++i) {
+ XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
if (xic) {
@@ -1930,12 +2043,12 @@ void OS_X11::process_xevents() {
}
#ifdef TOUCH_ENABLED
// Ungrab touch devices so input works as usual while we are unfocused
- /*for (int i = 0; i < touch.devices.size(); ++i) {
- XIUngrabDevice(x11_display, touch.devices[i], CurrentTime);
+ /*for (int i = 0; i < xi.touch_devices.size(); ++i) {
+ XIUngrabDevice(x11_display, xi.touch_devices[i], CurrentTime);
}*/
// Release every pointer to avoid sticky points
- for (Map<int, Vector2>::Element *E = touch.state.front(); E; E = E->next()) {
+ for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
Ref<InputEventScreenTouch> st;
st.instance();
@@ -1943,7 +2056,7 @@ void OS_X11::process_xevents() {
st->set_position(E->get());
input->parse_input_event(st);
}
- touch.state.clear();
+ xi.state.clear();
#endif
if (xic) {
XUnsetICFocus(xic);
@@ -1967,28 +2080,36 @@ void OS_X11::process_xevents() {
mb.instance();
get_key_modifier_state(event.xbutton.state, mb);
- mb->set_button_mask(get_mouse_button_state(event.xbutton.state));
- mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
- mb->set_global_position(mb->get_position());
mb->set_button_index(event.xbutton.button);
if (mb->get_button_index() == 2)
mb->set_button_index(3);
else if (mb->get_button_index() == 3)
mb->set_button_index(2);
+ mb->set_button_mask(get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
+ mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
+ mb->set_global_position(mb->get_position());
mb->set_pressed((event.type == ButtonPress));
- if (event.type == ButtonPress && event.xbutton.button == 1) {
+ if (event.type == ButtonPress) {
uint64_t diff = get_ticks_usec() / 1000 - last_click_ms;
- if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) {
+ if (mb->get_button_index() == last_click_button_index) {
- last_click_ms = 0;
- last_click_pos = Point2(-100, -100);
- mb->set_doubleclick(true);
+ if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) {
- } else {
+ last_click_ms = 0;
+ last_click_pos = Point2(-100, -100);
+ last_click_button_index = -1;
+ mb->set_doubleclick(true);
+ }
+
+ } else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) {
+ last_click_button_index = mb->get_button_index();
+ }
+
+ if (!mb->is_doubleclick()) {
last_click_ms += diff;
last_click_pos = Point2(event.xbutton.x, event.xbutton.y);
}
@@ -2028,34 +2149,27 @@ void OS_X11::process_xevents() {
// Motion is also simple.
// A little hack is in order
// to be able to send relative motion events.
- Point2i pos(event.xmotion.x, event.xmotion.y);
+ Point2 pos(event.xmotion.x, event.xmotion.y);
-#ifdef TOUCH_ENABLED
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false;
// Adding some tolerance to match better Point2i to Vector2
- if (touch.state.size() && Vector2(pos).distance_squared_to(touch.mouse_pos_to_filter) < 2) {
+ if (xi.state.size() && Vector2(pos).distance_squared_to(xi.mouse_pos_to_filter) < 2) {
filter = true;
}
// Invalidate to avoid filtering a possible legitimate similar event coming later
- touch.mouse_pos_to_filter = Vector2(1e10, 1e10);
+ xi.mouse_pos_to_filter = Vector2(1e10, 1e10);
if (filter) {
break;
}
-#endif
if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- if (pos == Point2i(current_videomode.width / 2, current_videomode.height / 2)) {
- //this sucks, it's a hack, etc and is a little inaccurate, etc.
- //but nothing I can do, X11 sucks.
-
- center = pos;
+ if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) {
break;
}
Point2i new_center = pos;
- pos = last_mouse_pos + (pos - center);
+ pos = last_mouse_pos + xi.relative_motion;
center = new_center;
do_mouse_warp = window_has_focus; // warp the cursor if we're focused in
}
@@ -2066,7 +2180,24 @@ void OS_X11::process_xevents() {
last_mouse_pos_valid = true;
}
- Point2i rel = pos - last_mouse_pos;
+ // Hackish but relative mouse motion is already handled in the RawMotion event.
+ // RawMotion does not provide the absolute mouse position (whereas MotionNotify does).
+ // Therefore, RawMotion cannot be the authority on absolute mouse position.
+ // RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion.
+ // Therefore, MotionNotify cannot be the authority on relative mouse motion.
+ // This means we need to take a combined approach...
+ Point2 rel;
+
+ // Only use raw input if in capture mode. Otherwise use the classic behavior.
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ rel = xi.relative_motion;
+ } else {
+ rel = pos - last_mouse_pos;
+ }
+
+ // Reset to prevent lingering motion
+ xi.relative_motion.x = 0;
+ xi.relative_motion.y = 0;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
pos = Point2i(current_videomode.width / 2, current_videomode.height / 2);
@@ -2075,12 +2206,16 @@ void OS_X11::process_xevents() {
Ref<InputEventMouseMotion> mm;
mm.instance();
+ // Make the absolute position integral so it doesn't look _too_ weird :)
+ Point2i posi(pos);
+
get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask(get_mouse_button_state(event.xmotion.state));
- mm->set_position(pos);
- mm->set_global_position(pos);
- input->set_mouse_position(pos);
+ mm->set_button_mask(get_mouse_button_state());
+ mm->set_position(posi);
+ mm->set_global_position(posi);
+ input->set_mouse_position(posi);
mm->set_speed(input->get_last_mouse_speed());
+
mm->set_relative(rel);
last_mouse_pos = pos;
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index bb8411e213..4e73c5beec 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -48,11 +48,9 @@
#include <X11/Xcursor/Xcursor.h>
#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
-#ifdef TOUCH_ENABLED
-#include <X11/extensions/XInput2.h>
-#endif
// Hints for X11 fullscreen
typedef struct {
@@ -121,23 +119,32 @@ class OS_X11 : public OS_Unix {
bool im_active;
Vector2 im_position;
- Point2i last_mouse_pos;
+ Point2 last_mouse_pos;
bool last_mouse_pos_valid;
Point2i last_click_pos;
uint64_t last_click_ms;
+ int last_click_button_index;
uint32_t last_button_state;
-#ifdef TOUCH_ENABLED
+
struct {
int opcode;
- Vector<int> devices;
- XIEventMask event_mask;
+ Vector<int> touch_devices;
+ Map<int, Vector2> absolute_devices;
+ XIEventMask all_event_mask;
+ XIEventMask all_master_event_mask;
Map<int, Vector2> state;
Vector2 mouse_pos_to_filter;
- } touch;
-#endif
+ Vector2 relative_motion;
+ Vector2 raw_pos;
+ Vector2 old_raw_pos;
+ ::Time last_relative_time;
+ } xi;
+
+ bool refresh_device_info();
- unsigned int get_mouse_button_state(unsigned int p_x11_state);
+ unsigned int get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
+ void flush_mouse_motion();
MouseMode mouse_mode;
Point2i center;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 3dde228bfa..c440512c84 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1279,7 +1279,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
colliders.push_back(collision);
motion = collision.remainder;
- bool is_on_slope = false;
if (p_floor_direction == Vector2()) {
//all is a wall
on_wall = true;
@@ -1291,16 +1290,14 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
floor_velocity = collision.collider_vel;
if (p_stop_on_slope) {
- if (Vector2() == lv_n + p_floor_direction) {
+ if (Vector2() == lv_n + p_floor_direction && collision.travel.length() < 1) {
Transform2D gt = get_global_transform();
- gt.elements[2] -= collision.travel;
+ gt.elements[2] -= collision.travel.project(p_floor_direction.tangent());
set_global_transform(gt);
return Vector2();
}
}
- is_on_slope = true;
-
} else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling
on_ceiling = true;
} else {
@@ -1308,14 +1305,9 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
}
}
- if (p_stop_on_slope && is_on_slope) {
- motion = motion.slide(p_floor_direction);
- lv = lv.slide(p_floor_direction);
- } else {
- Vector2 n = collision.normal;
- motion = motion.slide(n);
- lv = lv.slide(n);
- }
+ Vector2 n = collision.normal;
+ motion = motion.slide(n);
+ lv = lv.slide(n);
}
if (p_stop_on_slope)
diff --git a/scene/3d/remote_transform.cpp b/scene/3d/remote_transform.cpp
index c12e49fb47..490fcf24fd 100644
--- a/scene/3d/remote_transform.cpp
+++ b/scene/3d/remote_transform.cpp
@@ -63,38 +63,40 @@ void RemoteTransform::_update_remote() {
if (update_remote_position && update_remote_rotation && update_remote_scale) {
n->set_global_transform(get_global_transform());
} else {
- Transform n_trans = n->get_global_transform();
Transform our_trans = get_global_transform();
- if (!update_remote_position)
- our_trans.set_origin(n_trans.get_origin());
+ if (update_remote_rotation)
+ n->set_rotation(our_trans.basis.get_rotation());
- n->set_global_transform(our_trans);
+ if (update_remote_scale)
+ n->set_scale(our_trans.basis.get_scale());
- if (!update_remote_rotation)
- n->set_rotation(n_trans.basis.get_rotation());
+ if (update_remote_position) {
+ Transform n_trans = n->get_global_transform();
- if (!update_remote_scale)
- n->set_scale(n_trans.basis.get_scale());
+ n_trans.set_origin(our_trans.get_origin());
+ n->set_global_transform(n_trans);
+ }
}
} else {
if (update_remote_position && update_remote_rotation && update_remote_scale) {
- n->set_global_transform(get_global_transform());
+ n->set_transform(get_transform());
} else {
- Transform n_trans = n->get_transform();
Transform our_trans = get_transform();
- if (!update_remote_position)
- our_trans.set_origin(n_trans.get_origin());
+ if (update_remote_rotation)
+ n->set_rotation(our_trans.basis.get_rotation());
- n->set_transform(our_trans);
+ if (update_remote_scale)
+ n->set_scale(our_trans.basis.get_scale());
- if (!update_remote_rotation)
- n->set_rotation(n_trans.basis.get_rotation());
+ if (update_remote_position) {
+ Transform n_trans = n->get_transform();
- if (!update_remote_scale)
- n->set_scale(n_trans.basis.get_scale());
+ n_trans.set_origin(our_trans.get_origin());
+ n->set_transform(n_trans);
+ }
}
}
}
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 1ac19774f7..dc85c8e641 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -595,6 +595,16 @@ void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) {
}
}
+Array ButtonGroup::_get_buttons() {
+
+ Array btns;
+ for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
+ btns.push_back(E->get());
+ }
+
+ return btns;
+}
+
BaseButton *ButtonGroup::get_pressed_button() {
for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) {
@@ -608,6 +618,7 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
+ ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index a131e719ad..272c07f68a 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -143,6 +143,7 @@ protected:
public:
BaseButton *get_pressed_button();
void get_buttons(List<BaseButton *> *r_buttons);
+ Array _get_buttons();
ButtonGroup();
};
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 1e9f4df4a3..a869f7cfcd 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "file_dialog.h"
+
#include "core/os/keyboard.h"
#include "core/print_string.h"
#include "scene/gui/label.h"
@@ -596,7 +597,7 @@ void FileDialog::set_current_file(const String &p_file) {
int lp = p_file.find_last(".");
if (lp != -1) {
file->select(0, lp);
- if (file->is_inside_tree())
+ if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file))
file->grab_focus();
}
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index f441364c44..479b035f86 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2701,6 +2701,7 @@ bool Tree::edit_selected() {
Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
Point2i textedpos = get_global_position() + rect.position - ofs;
+ cache.text_editor_position = textedpos;
text_editor->set_position(textedpos);
text_editor->set_size(rect.size);
text_editor->clear();
@@ -2956,6 +2957,21 @@ void Tree::_notification(int p_what) {
if (p_what == NOTIFICATION_THEME_CHANGED) {
update_cache();
}
+
+ if (p_what == NOTIFICATION_RESIZED || p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+
+ if (popup_edited_item != NULL) {
+ Rect2 rect = popup_edited_item->get_meta("__focus_rect");
+ Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
+ Point2i textedpos = get_global_position() + rect.position - ofs;
+
+ if (cache.text_editor_position != textedpos) {
+ cache.text_editor_position = textedpos;
+ text_editor->set_position(textedpos);
+ value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
+ }
+ }
+ }
}
Size2 Tree::get_minimum_size() const {
@@ -3884,6 +3900,7 @@ Tree::Tree() {
value_editor->set_as_toplevel(true);
text_editor->set_as_toplevel(true);
+ set_notify_transform(true);
updating_value_editor = false;
pressed_button = -1;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 886ce66e2c..8622184bf8 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -457,6 +457,8 @@ private:
TreeItem *hover_item;
int hover_cell;
+ Point2i text_editor_position;
+
} cache;
int _get_title_button_height() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 5b1c2d8020..b220df6ce1 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1727,6 +1727,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.mouse_focus = _gui_find_control(pos);
gui.mouse_focus_mask = 1 << (mb->get_button_index() - 1);
+ gui.last_mouse_focus = gui.mouse_focus;
if (!gui.mouse_focus) {
return;
@@ -2114,14 +2115,14 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
set_input_as_handled();
return;
}
- } else if (gui.mouse_focus) {
+ } else if (touch_event->get_index() == 0 && gui.last_mouse_focus) {
- if (gui.mouse_focus->can_process()) {
+ if (gui.last_mouse_focus->can_process()) {
touch_event = touch_event->xformed_by(Transform2D()); //make a copy
touch_event->set_position(gui.focus_inv_xform.xform(pos));
- _gui_call_input(gui.mouse_focus, touch_event);
+ _gui_call_input(gui.last_mouse_focus, touch_event);
}
set_input_as_handled();
return;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 278350b1c9..6b6ca3b7db 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -271,6 +271,7 @@ private:
bool key_event_accepted;
Control *mouse_focus;
+ Control *last_mouse_focus;
Control *mouse_click_grabber;
int mouse_focus_mask;
Control *key_focus;
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 02a0bed964..21073a1cd1 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -30,6 +30,7 @@
#include "audio_stream.h"
#include "core/os/os.h"
+#include "core/project_settings.h"
//////////////////////////////
@@ -184,6 +185,12 @@ float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() {
}
void AudioStreamPlaybackMicrophone::start(float p_from_pos) {
+
+ if (!GLOBAL_GET("audio/enable_audio_input")) {
+ WARN_PRINTS("Need to enable Project settings > Audio > Enable Audio Input option to use capturing.");
+ return;
+ }
+
input_ofs = 0;
AudioDriver::get_singleton()->capture_start();
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 0cead5b2ba..0073f9e149 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -172,6 +172,7 @@ int AudioDriverManager::get_driver_count() {
}
void AudioDriverManager::initialize(int p_driver) {
+ GLOBAL_DEF_RST("audio/enable_audio_input", false);
int failed_driver = -1;
// Check if there is a selected driver