summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/array.cpp4
-rw-r--r--core/array.h1
-rw-r--r--core/variant_call.cpp2
-rw-r--r--doc/base/classes.xml2
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java172
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java335
-rw-r--r--platform/windows/os_windows.cpp1
-rw-r--r--scene/2d/parallax_layer.cpp19
-rw-r--r--scene/gui/file_dialog.cpp19
-rw-r--r--scene/gui/file_dialog.h2
-rw-r--r--scene/main/http_request.cpp108
-rw-r--r--scene/main/http_request.h13
-rw-r--r--scene/main/viewport.cpp26
-rw-r--r--scene/resources/curve.cpp6
-rw-r--r--servers/physics/body_pair_sw.cpp8
-rw-r--r--tools/editor/asset_library_editor_plugin.cpp19
-rw-r--r--tools/editor/code_editor.cpp40
-rw-r--r--tools/editor/code_editor.h3
-rw-r--r--tools/editor/editor_plugin.cpp6
-rw-r--r--tools/editor/editor_plugin.h3
-rw-r--r--version.py2
21 files changed, 522 insertions, 269 deletions
diff --git a/core/array.cpp b/core/array.cpp
index bb8e527304..23792f90fc 100644
--- a/core/array.cpp
+++ b/core/array.cpp
@@ -200,6 +200,10 @@ int Array::count(const Variant& p_value) const {
return amount;
}
+bool Array::has(const Variant& p_value) const {
+ return _p->array.find(p_value, 0) != -1;
+}
+
void Array::remove(int p_pos) {
_p->array.remove(p_pos);
diff --git a/core/array.h b/core/array.h
index 096660653e..dfc902525c 100644
--- a/core/array.h
+++ b/core/array.h
@@ -75,6 +75,7 @@ public:
int rfind(const Variant& p_value, int p_from=-1) const;
int find_last(const Variant& p_value) const;
int count(const Variant& p_value) const;
+ bool has(const Variant& p_value) const;
void erase(const Variant& p_value);
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 7da4cef115..7956c14c2c 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -474,6 +474,7 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var
VCALL_LOCALMEM2R(Array,rfind);
VCALL_LOCALMEM1R(Array,find_last);
VCALL_LOCALMEM1R(Array,count);
+ VCALL_LOCALMEM1R(Array,has);
VCALL_LOCALMEM1(Array,erase);
VCALL_LOCALMEM0(Array,sort);
VCALL_LOCALMEM2(Array,sort_custom);
@@ -1516,6 +1517,7 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
ADDFUNC2(ARRAY,INT,Array,rfind,NIL,"what",INT,"from",varray(-1));
ADDFUNC1(ARRAY,INT,Array,find_last,NIL,"value",varray());
ADDFUNC1(ARRAY,INT,Array,count,NIL,"value",varray());
+ ADDFUNC1(ARRAY,BOOL,Array,has,NIL,"value",varray());
ADDFUNC0(ARRAY,NIL,Array,pop_back,varray());
ADDFUNC0(ARRAY,NIL,Array,pop_front,varray());
ADDFUNC0(ARRAY,NIL,Array,sort,varray());
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 4c2a0fe7ba..d70b11b5ae 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -11281,7 +11281,7 @@
<argument index="1" name="title" type="String">
</argument>
<description>
- Add a control to the bottom panel (together with Output, Debug, Animation, etc). If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel].
+ Add a control to the bottom panel (together with Output, Debug, Animation, etc). Returns a reference to the button added. It's up to you to hide/show the button when needed. If your plugin is being removed, also make sure to remove your control by calling [method remove_control_from_bottom_panel].
</description>
</method>
<method name="add_control_to_container">
diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
index fde752acc9..b7de31ada3 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
@@ -28,99 +28,94 @@
/*************************************************************************/
package org.godotengine.godot;
-import org.godotengine.godot.Dictionary;
import android.app.Activity;
import android.util.Log;
+import org.godotengine.godot.payments.PaymentsManager;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
public class GodotPaymentV3 extends Godot.SingletonBase {
private Godot activity;
-
private Integer purchaseCallbackId = 0;
-
private String accessToken;
-
private String purchaseValidationUrlPrefix;
-
private String transactionId;
+ private PaymentsManager mPaymentManager;
+ private Dictionary mSkuDetails = new Dictionary();
- public void purchase( String _sku, String _transactionId) {
- final String sku = _sku;
- final String transactionId = _transactionId;
- activity.getPaymentsManager().setBaseSingleton(this);
+ public void purchase(final String sku, final String transactionId) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
- activity.getPaymentsManager().requestPurchase(sku, transactionId);
+ mPaymentManager.requestPurchase(sku, transactionId);
}
});
}
-
-/* public string requestPurchasedTicket(){
- activity.getPaymentsManager()
- }
-*/
- static public Godot.SingletonBase initialize(Activity p_activity) {
+ static public Godot.SingletonBase initialize(Activity p_activity) {
- return new GodotPaymentV3(p_activity);
- }
+ return new GodotPaymentV3(p_activity);
+ }
-
public GodotPaymentV3(Activity p_activity) {
- registerClass("GodotPayments", new String[] {"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume"});
- activity=(Godot) p_activity;
+ registerClass("GodotPayments", new String[]{"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails"});
+ activity = (Godot) p_activity;
+ mPaymentManager = activity.getPaymentsManager();
+ mPaymentManager.setBaseSingleton(this);
}
- public void consumeUnconsumedPurchases(){
- activity.getPaymentsManager().setBaseSingleton(this);
+ public void consumeUnconsumedPurchases() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
- activity.getPaymentsManager().consumeUnconsumedPurchases();
+ mPaymentManager.consumeUnconsumedPurchases();
}
});
}
private String signature;
- public String getSignature(){
- return this.signature;
+
+ public String getSignature() {
+ return this.signature;
}
-
-
- public void callbackSuccess(String ticket, String signature, String sku){
-// Log.d(this.getClass().getName(), "PRE-Send callback to purchase success");
+
+ public void callbackSuccess(String ticket, String signature, String sku) {
GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku});
-// Log.d(this.getClass().getName(), "POST-Send callback to purchase success");
-}
+ }
+
+ public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) {
+ Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku);
+ GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku});
+ }
- public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku){
-// Log.d(this.getClass().getName(), "PRE-Send callback to consume success");
- Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > "+ticket+","+signature+","+sku);
- GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku});
-// Log.d(this.getClass().getName(), "POST-Send callback to consume success");
+ public void callbackSuccessNoUnconsumedPurchases() {
+ GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[]{});
}
- public void callbackSuccessNoUnconsumedPurchases(){
- GodotLib.calldeferred(purchaseCallbackId, "no_validation_required", new Object[]{});
+ public void callbackFailConsume() {
+ GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[]{});
}
-
- public void callbackFail(){
+
+ public void callbackFail() {
GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{});
-// GodotLib.callobject(purchaseCallbackId, "purchase_fail", new Object[]{});
}
-
- public void callbackCancel(){
+
+ public void callbackCancel() {
GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[]{});
-// GodotLib.callobject(purchaseCallbackId, "purchase_cancel", new Object[]{});
}
-
- public void callbackAlreadyOwned(String sku){
+
+ public void callbackAlreadyOwned(String sku) {
GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[]{sku});
}
-
+
public int getPurchaseCallbackId() {
return purchaseCallbackId;
}
@@ -129,11 +124,11 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
this.purchaseCallbackId = purchaseCallbackId;
}
- public String getPurchaseValidationUrlPrefix(){
- return this.purchaseValidationUrlPrefix ;
+ public String getPurchaseValidationUrlPrefix() {
+ return this.purchaseValidationUrlPrefix;
}
- public void setPurchaseValidationUrlPrefix(String url){
+ public void setPurchaseValidationUrlPrefix(String url) {
this.purchaseValidationUrlPrefix = url;
}
@@ -144,39 +139,80 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
-
- public void setTransactionId(String transactionId){
+
+ public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
-
- public String getTransactionId(){
+
+ public String getTransactionId() {
return this.transactionId;
}
-
+
// request purchased items are not consumed
- public void requestPurchased(){
- activity.getPaymentsManager().setBaseSingleton(this);
+ public void requestPurchased() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
- activity.getPaymentsManager().requestPurchased();
+ mPaymentManager.requestPurchased();
}
});
}
-
+
// callback for requestPurchased()
- public void callbackPurchased(String receipt, String signature, String sku){
+ public void callbackPurchased(String receipt, String signature, String sku) {
GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[]{receipt, signature, sku});
}
-
+
// consume item automatically after purchase. default is true.
- public void setAutoConsume(boolean autoConsume){
- activity.getPaymentsManager().setAutoConsume(autoConsume);
+ public void setAutoConsume(boolean autoConsume) {
+ mPaymentManager.setAutoConsume(autoConsume);
}
-
+
// consume a specific item
- public void consume(String sku){
- activity.getPaymentsManager().consume(sku);
+ public void consume(String sku) {
+ mPaymentManager.consume(sku);
+ }
+
+ // query in app item detail info
+ public void querySkuDetails(String[] list) {
+ List<String> nKeys = Arrays.asList(list);
+ List<String> cKeys = Arrays.asList(mSkuDetails.get_keys());
+ ArrayList<String> fKeys = new ArrayList<String>();
+ for (String key : nKeys) {
+ if (!cKeys.contains(key)) {
+ fKeys.add(key);
+ }
+ }
+ if (fKeys.size() > 0) {
+ mPaymentManager.querySkuDetails(fKeys.toArray(new String[0]));
+ } else {
+ completeSkuDetail();
+ }
+ }
+
+ public void addSkuDetail(String itemJson) {
+ JSONObject o = null;
+ try {
+ o = new JSONObject(itemJson);
+ Dictionary item = new Dictionary();
+ item.put("type", o.optString("type"));
+ item.put("product_id", o.optString("productId"));
+ item.put("title", o.optString("title"));
+ item.put("description", o.optString("description"));
+ item.put("price", o.optString("price"));
+ item.put("price_currency_code", o.optString("price_currency_code"));
+ item.put("price_amount", 0.000001d * o.optLong("price_amount_micros"));
+ mSkuDetails.put(item.get("product_id").toString(), item);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void completeSkuDetail() {
+ GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[]{mSkuDetails});
+ }
+
+ public void errorSkuDetail(String errorMessage) {
+ GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[]{errorMessage});
}
}
-
diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
index eb33b37ecc..753c0a6f93 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
@@ -28,283 +28,370 @@
/*************************************************************************/
package org.godotengine.godot.payments;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
-import android.os.Bundle;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
+import com.android.vending.billing.IInAppBillingService;
-import org.godotengine.godot.Dictionary;
import org.godotengine.godot.Godot;
import org.godotengine.godot.GodotPaymentV3;
-import com.android.vending.billing.IInAppBillingService;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
public class PaymentsManager {
public static final int BILLING_RESPONSE_RESULT_OK = 0;
public static final int REQUEST_CODE_FOR_PURCHASE = 0x1001;
private static boolean auto_consume = true;
-
+
private Activity activity;
IInAppBillingService mService;
- public void setActivity(Activity activity){
+ public void setActivity(Activity activity) {
this.activity = activity;
}
- public static PaymentsManager createManager(Activity activity){
+ public static PaymentsManager createManager(Activity activity) {
PaymentsManager manager = new PaymentsManager(activity);
return manager;
}
-
- private PaymentsManager(Activity activity){
+
+ private PaymentsManager(Activity activity) {
this.activity = activity;
}
-
- public PaymentsManager initService(){
+
+ public PaymentsManager initService() {
Intent intent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
intent.setPackage("com.android.vending");
activity.bindService(
- intent,
- mServiceConn,
+ intent,
+ mServiceConn,
Context.BIND_AUTO_CREATE);
return this;
}
- public void destroy(){
+ public void destroy() {
if (mService != null) {
- activity.unbindService(mServiceConn);
- }
+ activity.unbindService(mServiceConn);
+ }
}
-
+
ServiceConnection mServiceConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
- }
+ }
};
-
- public void requestPurchase(final String sku, String transactionId){
+
+ public void requestPurchase(final String sku, String transactionId) {
new PurchaseTask(mService, Godot.getInstance()) {
-
+
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
-
+
}
-
+
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
}
-
+
@Override
protected void alreadyOwned() {
godotPaymentV3.callbackAlreadyOwned(sku);
}
-
+
}.purchase(sku, transactionId);
}
- public void consumeUnconsumedPurchases(){
+ public void consumeUnconsumedPurchases() {
new ReleaseAllConsumablesTask(mService, activity) {
-
+
@Override
protected void success(String sku, String receipt, String signature, String token) {
godotPaymentV3.callbackSuccessProductMassConsumed(receipt, signature, sku);
}
-
+
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
-
+ Log.d("godot", "consumeUnconsumedPurchases :" + message);
+ godotPaymentV3.callbackFailConsume();
+
}
@Override
protected void notRequired() {
+ Log.d("godot", "callbackSuccessNoUnconsumedPurchases :");
godotPaymentV3.callbackSuccessNoUnconsumedPurchases();
-
+
}
}.consumeItAll();
}
-
- public void requestPurchased(){
- try{
+
+ public void requestPurchased() {
+ try {
PaymentsCache pc = new PaymentsCache(Godot.getInstance());
-// Log.d("godot", "requestPurchased for " + activity.getPackageName());
- Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp",null);
+ String continueToken = null;
-/*
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
- Log.d("godot", String.format("%s %s (%s)", key, value.toString(), value.getClass().getName()));
- }
-*/
-
- if (bundle.getInt("RESPONSE_CODE") == 0){
+ do {
+ Bundle bundle = mService.getPurchases(3, activity.getPackageName(), "inapp", continueToken);
- final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
- final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
-
+ if (bundle.getInt("RESPONSE_CODE") == 0) {
- if (myPurchases == null || myPurchases.size() == 0){
-// Log.d("godot", "No purchases!");
- godotPaymentV3.callbackPurchased("", "", "");
- return;
- }
-
-// Log.d("godot", "# products are purchased:" + myPurchases.size());
- for (int i=0;i<myPurchases.size();i++)
- {
-
- try{
- String receipt = myPurchases.get(i);
- JSONObject inappPurchaseData = new JSONObject(receipt);
- String sku = inappPurchaseData.getString("productId");
- String token = inappPurchaseData.getString("purchaseToken");
- String signature = mySignatures.get(i);
-// Log.d("godot", "purchased item:" + token + "\n" + receipt);
-
- pc.setConsumableValue("ticket_signautre", sku, signature);
- pc.setConsumableValue("ticket", sku, receipt);
- pc.setConsumableFlag("block", sku, true);
- pc.setConsumableValue("token", sku, token);
-
- godotPaymentV3.callbackPurchased(receipt, signature, sku);
- } catch (JSONException e) {
+ final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
+ final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST");
+
+ if (myPurchases == null || myPurchases.size() == 0) {
+ godotPaymentV3.callbackPurchased("", "", "");
+ return;
}
- }
- }
- }catch(Exception e){
+ for (int i = 0; i < myPurchases.size(); i++) {
+
+ try {
+ String receipt = myPurchases.get(i);
+ JSONObject inappPurchaseData = new JSONObject(receipt);
+ String sku = inappPurchaseData.getString("productId");
+ String token = inappPurchaseData.getString("purchaseToken");
+ String signature = mySignatures.get(i);
+
+ pc.setConsumableValue("ticket_signautre", sku, signature);
+ pc.setConsumableValue("ticket", sku, receipt);
+ pc.setConsumableFlag("block", sku, true);
+ pc.setConsumableValue("token", sku, token);
+
+ godotPaymentV3.callbackPurchased(receipt, signature, sku);
+ } catch (JSONException e) {
+ }
+ }
+ }
+ continueToken = bundle.getString("INAPP_CONTINUATION_TOKEN");
+ Log.d("godot", "continue token = " + continueToken);
+ } while (!TextUtils.isEmpty(continueToken));
+ } catch (Exception e) {
Log.d("godot", "Error requesting purchased products:" + e.getClass().getName() + ":" + e.getMessage());
}
}
-
+
public void processPurchaseResponse(int resultCode, Intent data) {
- new HandlePurchaseTask(activity){
+ new HandlePurchaseTask(activity) {
@Override
protected void success(final String sku, final String signature, final String ticket) {
godotPaymentV3.callbackSuccess(ticket, signature, sku);
- if (auto_consume){
+ if (auto_consume) {
new ConsumeTask(mService, activity) {
-
+
@Override
protected void success(String ticket) {
-// godotPaymentV3.callbackSuccess("");
}
-
+
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
-
+
}
}.consume(sku);
}
-
-// godotPaymentV3.callbackSuccess(new PaymentsCache(activity).getConsumableValue("ticket", sku),signature);
-// godotPaymentV3.callbackSuccess(ticket);
- //validatePurchase(purchaseToken, sku);
}
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
-
}
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
-
}
}.handlePurchaseRequest(resultCode, data);
}
-
- public void validatePurchase(String purchaseToken, final String sku){
-
- new ValidateTask(activity, godotPaymentV3){
+
+ public void validatePurchase(String purchaseToken, final String sku) {
+
+ new ValidateTask(activity, godotPaymentV3) {
@Override
protected void success() {
-
+
new ConsumeTask(mService, activity) {
-
+
@Override
protected void success(String ticket) {
godotPaymentV3.callbackSuccess(ticket, null, sku);
-
}
-
+
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
-
}
}.consume(sku);
-
+
}
@Override
protected void error(String message) {
godotPaymentV3.callbackFail();
-
}
@Override
protected void canceled() {
godotPaymentV3.callbackCancel();
-
}
}.validatePurchase(sku);
}
-
- public void setAutoConsume(boolean autoConsume){
+
+ public void setAutoConsume(boolean autoConsume) {
auto_consume = autoConsume;
}
-
- public void consume(final String sku){
+
+ public void consume(final String sku) {
new ConsumeTask(mService, activity) {
-
+
@Override
protected void success(String ticket) {
godotPaymentV3.callbackSuccessProductMassConsumed(ticket, "", sku);
-
}
-
+
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
-
+ godotPaymentV3.callbackFailConsume();
}
}.consume(sku);
}
-
+
+ // Workaround to bug where sometimes response codes come as Long instead of Integer
+ int getResponseCodeFromBundle(Bundle b) {
+ Object o = b.get("RESPONSE_CODE");
+ if (o == null) {
+ //logDebug("Bundle with null response code, assuming OK (known issue)");
+ return BILLING_RESPONSE_RESULT_OK;
+ } else if (o instanceof Integer) return ((Integer) o).intValue();
+ else if (o instanceof Long) return (int) ((Long) o).longValue();
+ else {
+ //logError("Unexpected type for bundle response code.");
+ //logError(o.getClass().getName());
+ throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName());
+ }
+ }
+
+ /**
+ * Returns a human-readable description for the given response code.
+ *
+ * @param code The response code
+ * @return A human-readable string explaining the result code.
+ * It also includes the result code numerically.
+ */
+ public static String getResponseDesc(int code) {
+ String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" +
+ "3:Billing Unavailable/4:Item unavailable/" +
+ "5:Developer Error/6:Error/7:Item Already Owned/" +
+ "8:Item not owned").split("/");
+ String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" +
+ "-1002:Bad response received/" +
+ "-1003:Purchase signature verification failed/" +
+ "-1004:Send intent failed/" +
+ "-1005:User cancelled/" +
+ "-1006:Unknown purchase response/" +
+ "-1007:Missing token/" +
+ "-1008:Unknown error/" +
+ "-1009:Subscriptions not available/" +
+ "-1010:Invalid consumption attempt").split("/");
+
+ if (code <= -1000) {
+ int index = -1000 - code;
+ if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index];
+ else return String.valueOf(code) + ":Unknown IAB Helper Error";
+ } else if (code < 0 || code >= iab_msgs.length)
+ return String.valueOf(code) + ":Unknown";
+ else
+ return iab_msgs[code];
+ }
+
+ public void querySkuDetails(final String[] list) {
+ (new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(list));
+ if (skuList.size() == 0) {
+ return;
+ }
+ // Split the sku list in blocks of no more than 20 elements.
+ ArrayList<ArrayList<String>> packs = new ArrayList<ArrayList<String>>();
+ ArrayList<String> tempList;
+ int n = skuList.size() / 20;
+ int mod = skuList.size() % 20;
+ for (int i = 0; i < n; i++) {
+ tempList = new ArrayList<String>();
+ for (String s : skuList.subList(i * 20, i * 20 + 20)) {
+ tempList.add(s);
+ }
+ packs.add(tempList);
+ }
+ if (mod != 0) {
+ tempList = new ArrayList<String>();
+ for (String s : skuList.subList(n * 20, n * 20 + mod)) {
+ tempList.add(s);
+ }
+ packs.add(tempList);
+
+ for (ArrayList<String> skuPartList : packs) {
+ Bundle querySkus = new Bundle();
+ querySkus.putStringArrayList("ITEM_ID_LIST", skuPartList);
+ Bundle skuDetails = null;
+ try {
+ skuDetails = mService.getSkuDetails(3, activity.getPackageName(), "inapp", querySkus);
+ if (!skuDetails.containsKey("DETAILS_LIST")) {
+ int response = getResponseCodeFromBundle(skuDetails);
+ if (response != BILLING_RESPONSE_RESULT_OK) {
+ godotPaymentV3.errorSkuDetail(getResponseDesc(response));
+ } else {
+ godotPaymentV3.errorSkuDetail("No error but no detail list.");
+ }
+ return;
+ }
+
+ ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
+
+ for (String thisResponse : responseList) {
+ Log.d("godot", "response = "+thisResponse);
+ godotPaymentV3.addSkuDetail(thisResponse);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ godotPaymentV3.errorSkuDetail("RemoteException error!");
+ }
+ }
+ godotPaymentV3.completeSkuDetail();
+ }
+ }
+ })).start();
+ }
+
private GodotPaymentV3 godotPaymentV3;
-
+
public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) {
this.godotPaymentV3 = godotPaymentV3;
}
}
-
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 6aee0d2399..a3858fe641 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1476,6 +1476,7 @@ Point2 OS_Windows::get_window_position() const{
}
void OS_Windows::set_window_position(const Point2& p_position){
+ if (video_mode.fullscreen) return;
RECT r;
GetWindowRect(hWnd,&r);
MoveWindow(hWnd,p_position.x,p_position.y,r.right-r.left,r.bottom-r.top,TRUE);
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index e9378b1d02..05136de5d6 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -123,26 +123,15 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_sca
Point2 new_ofs = ((orig_offset+p_offset)*motion_scale)*p_scale+motion_offset;
if (mirroring.x) {
-
- while( new_ofs.x>=0) {
- new_ofs.x -= mirroring.x*p_scale;
- }
- while(new_ofs.x < -mirroring.x*p_scale) {
- new_ofs.x += mirroring.x*p_scale;
- }
+ double den = mirroring.x*p_scale;
+ new_ofs.x -= den*ceil(new_ofs.x/den);
}
if (mirroring.y) {
-
- while( new_ofs.y>=0) {
- new_ofs.y -= mirroring.y*p_scale;
- }
- while(new_ofs.y < -mirroring.y*p_scale) {
- new_ofs.y += mirroring.y*p_scale;
- }
+ double den = mirroring.y*p_scale;
+ new_ofs.y -= den*ceil(new_ofs.y/den);
}
-
set_pos(new_ofs);
set_scale(Vector2(1,1)*p_scale);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index d335399caa..6b43425edc 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -475,7 +475,7 @@ void FileDialog::update_filters() {
String flt=filters[i].get_slice(";",0).strip_edges();
String desc=filters[i].get_slice(";",1).strip_edges();
if (desc.length())
- filter->add_item(desc+" ( "+flt+" )");
+ filter->add_item(String(XL_MESSAGE(desc))+" ( "+flt+" )");
else
filter->add_item("( "+flt+" )");
}
@@ -498,6 +498,16 @@ void FileDialog::add_filter(const String& p_filter) {
}
+void FileDialog::set_filters(const Vector<String>& p_filters){
+ filters=p_filters;
+ update_filters();
+ invalidate();
+}
+
+Vector<String> FileDialog::get_filters() const{
+ return filters;
+}
+
String FileDialog::get_current_dir() const {
return dir->get_text();
@@ -686,6 +696,8 @@ void FileDialog::_bind_methods() {
ObjectTypeDB::bind_method(_MD("clear_filters"),&FileDialog::clear_filters);
ObjectTypeDB::bind_method(_MD("add_filter","filter"),&FileDialog::add_filter);
+ ObjectTypeDB::bind_method(_MD("set_filters","filters"),&FileDialog::set_filters);
+ ObjectTypeDB::bind_method(_MD("get_filters"),&FileDialog::get_filters);
ObjectTypeDB::bind_method(_MD("get_current_dir"),&FileDialog::get_current_dir);
ObjectTypeDB::bind_method(_MD("get_current_file"),&FileDialog::get_current_file);
ObjectTypeDB::bind_method(_MD("get_current_path"),&FileDialog::get_current_path);
@@ -722,6 +734,11 @@ void FileDialog::_bind_methods() {
BIND_CONSTANT( ACCESS_USERDATA );
BIND_CONSTANT( ACCESS_FILESYSTEM );
+ ADD_PROPERTY( PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"),_SCS("set_mode"),_SCS("get_mode") );
+ ADD_PROPERTY( PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"),_SCS("set_access"),_SCS("get_access") );
+ ADD_PROPERTY( PropertyInfo(Variant::STRING_ARRAY, "filters"),_SCS("set_filters"),_SCS("get_filters") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL, "show_hidden_files"),_SCS("set_show_hidden_files"),_SCS("is_showing_hidden_files") );
+
}
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 1fcf8387ce..150b24cb3f 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -131,6 +131,8 @@ public:
void clear_filters();
void add_filter(const String& p_filter);
+ void set_filters(const Vector<String>& p_filters);
+ Vector<String> get_filters() const;
void set_enable_multiple_selection(bool p_enable);
Vector<String> get_selected_files() const;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 040d509286..9b79af32bf 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -105,6 +105,9 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h
}
Error err = _parse_url(p_url);
+ if (err)
+ return err;
+
validate_ssl=p_ssl_validate_domain;
bool has_user_agent=false;
@@ -127,19 +130,52 @@ Error HTTPRequest::request(const String& p_url, const Vector<String>& p_custom_h
headers.push_back("Accept: */*");
}
+ requesting=true;
+
+ if (use_threads) {
- err = _request();
+ thread_done=false;
+ thread_request_quit=false;
+ client->set_blocking_mode(true);
+ thread=Thread::create(_thread_func,this);
+ } else {
+ client->set_blocking_mode(false);
+ err = _request();
+ if (err!=OK) {
+ call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
+ return ERR_CANT_CONNECT;
+ }
- if (err==OK) {
set_process(true);
- requesting=true;
+
}
- return err;
+ return OK;
}
+void HTTPRequest::_thread_func(void *p_userdata) {
+
+ HTTPRequest *hr = (HTTPRequest*)p_userdata;
+
+ Error err = hr->_request();
+
+ if (err!=OK) {
+ hr->call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
+ } else {
+ while(!hr->thread_request_quit) {
+
+ bool exit = hr->_update_connection();
+ if (exit)
+ break;
+ OS::get_singleton()->delay_usec(1);
+ }
+ }
+
+ hr->thread_done=true;
+}
+
void HTTPRequest::cancel_request() {
if (!requesting)
@@ -147,6 +183,11 @@ void HTTPRequest::cancel_request() {
if (!use_threads) {
set_process(false);
+ } else {
+ thread_request_quit=true;
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ thread=NULL;
}
if (file) {
@@ -167,7 +208,7 @@ void HTTPRequest::cancel_request() {
bool HTTPRequest::_handle_response(bool *ret_value) {
if (!client->has_response()) {
- call_deferred("emit_signal","request_completed",RESULT_NO_RESPONSE,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_NO_RESPONSE,0,StringArray(),ByteArray());
*ret_value=true;
return true;
}
@@ -187,7 +228,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
//redirect
if (max_redirects>=0 && redirections>=max_redirects) {
- call_deferred("emit_signal","request_completed",RESULT_REDIRECT_LIMIT_REACHED,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_REDIRECT_LIMIT_REACHED,response_code,response_headers,ByteArray());
*ret_value=true;
return true;
}
@@ -239,7 +280,8 @@ bool HTTPRequest::_update_connection() {
switch( client->get_status() ) {
case HTTPClient::STATUS_DISCONNECTED: {
- return true; //end it, since it's doing something
+ call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
+ return true; //end it, since it's doing something
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
@@ -247,7 +289,7 @@ bool HTTPRequest::_update_connection() {
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
- call_deferred("emit_signal","request_completed",RESULT_CANT_RESOLVE,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_CANT_RESOLVE,0,StringArray(),ByteArray());
return true;
} break;
@@ -258,7 +300,7 @@ bool HTTPRequest::_update_connection() {
} break; //connecting to ip
case HTTPClient::STATUS_CANT_CONNECT: {
- call_deferred("emit_signal","request_completed",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_CANT_CONNECT,0,StringArray(),ByteArray());
return true;
} break;
@@ -276,17 +318,17 @@ bool HTTPRequest::_update_connection() {
return ret_value;
- call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,ByteArray());
return true;
}
if (got_response && body_len<0) {
//chunked transfer is done
- call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,body);
+ call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,body);
return true;
}
- call_deferred("emit_signal","request_completed",RESULT_CHUNKED_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_CHUNKED_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
return true;
//request migh have been done
} else {
@@ -294,7 +336,7 @@ bool HTTPRequest::_update_connection() {
Error err = client->request(HTTPClient::METHOD_GET,request_string,headers);
if (err!=OK) {
- call_deferred("emit_signal","request_completed",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray());
return true;
}
@@ -320,7 +362,7 @@ bool HTTPRequest::_update_connection() {
if (!client->is_response_chunked() && client->get_response_body_length()==0) {
- call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,ByteArray());
return true;
}
@@ -331,7 +373,7 @@ bool HTTPRequest::_update_connection() {
body_len=client->get_response_body_length();
if (body_size_limit>=0 && body_len>body_size_limit) {
- call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray());
return true;
}
}
@@ -340,7 +382,8 @@ bool HTTPRequest::_update_connection() {
file=FileAccess::open(download_to_file,FileAccess::WRITE);
if (!file) {
- call_deferred("emit_signal","request_completed",RESULT_DOWNLOAD_FILE_CANT_OPEN,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_DOWNLOAD_FILE_CANT_OPEN,response_code,response_headers,ByteArray());
+ return true;
}
}
}
@@ -356,7 +399,7 @@ bool HTTPRequest::_update_connection() {
ByteArray::Read r=chunk.read();
file->store_buffer(r.ptr(),chunk.size());
if (file->get_error()!=OK) {
- call_deferred("emit_signal","request_completed",RESULT_DOWNLOAD_FILE_WRITE_ERROR,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_DOWNLOAD_FILE_WRITE_ERROR,response_code,response_headers,ByteArray());
return true;
}
} else {
@@ -364,18 +407,18 @@ bool HTTPRequest::_update_connection() {
}
if (body_size_limit>=0 && downloaded>body_size_limit) {
- call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_BODY_SIZE_LIMIT_EXCEEDED,response_code,response_headers,ByteArray());
return true;
}
if (body_len>=0) {
if (downloaded==body_len) {
- call_deferred("emit_signal","request_completed",RESULT_SUCCESS,response_code,response_headers,body);
+ call_deferred("_request_done",RESULT_SUCCESS,response_code,response_headers,body);
return true;
}
/*if (body.size()>=body_len) {
- call_deferred("emit_signal","request_completed",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
+ call_deferred("_request_done",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
return true;
}*/
}
@@ -384,11 +427,11 @@ bool HTTPRequest::_update_connection() {
} break; // request resulted in body: { } break which must be read
case HTTPClient::STATUS_CONNECTION_ERROR: {
- call_deferred("emit_signal","request_completed",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_CONNECTION_ERROR,0,StringArray(),ByteArray());
return true;
} break;
case HTTPClient::STATUS_SSL_HANDSHAKE_ERROR: {
- call_deferred("emit_signal","request_completed",RESULT_SSL_HANDSHAKE_ERROR,0,StringArray(),ByteArray());
+ call_deferred("_request_done",RESULT_SSL_HANDSHAKE_ERROR,0,StringArray(),ByteArray());
return true;
} break;
@@ -397,17 +440,35 @@ bool HTTPRequest::_update_connection() {
ERR_FAIL_V(false);
}
+
+void HTTPRequest::_request_done(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data) {
+
+
+ cancel_request();
+ emit_signal("request_completed",p_status,p_code,headers,p_data);
+}
+
+
void HTTPRequest::_notification(int p_what) {
if (p_what==NOTIFICATION_PROCESS) {
+ if (use_threads)
+ return;
bool done = _update_connection();
if (done) {
set_process(false);
+ //cancel_request(); called from _request done now
+ }
+ }
+
+ if (p_what==NOTIFICATION_EXIT_TREE) {
+ if (requesting) {
cancel_request();
}
}
+
}
void HTTPRequest::set_use_threads(bool p_use) {
@@ -491,6 +552,7 @@ void HTTPRequest::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_body_size"),&HTTPRequest::get_body_size);
ObjectTypeDB::bind_method(_MD("_redirect_request"),&HTTPRequest::_redirect_request);
+ ObjectTypeDB::bind_method(_MD("_request_done"),&HTTPRequest::_request_done);
ADD_PROPERTY(PropertyInfo(Variant::BOOL,"use_threads"),_SCS("set_use_threads"),_SCS("is_using_threads"));
ADD_PROPERTY(PropertyInfo(Variant::INT,"body_size_limit",PROPERTY_HINT_RANGE,"-1,2000000000"),_SCS("set_body_size_limit"),_SCS("get_body_size_limit"));
@@ -517,6 +579,7 @@ void HTTPRequest::_bind_methods() {
HTTPRequest::HTTPRequest()
{
+ thread=NULL;
port=80;
redirections=0;
@@ -530,6 +593,7 @@ HTTPRequest::HTTPRequest()
requesting=false;
client.instance();
use_threads=false;
+ thread_done=false;
body_size_limit=-1;
file=NULL;
status=HTTPClient::STATUS_DISCONNECTED;
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 7c3ccb2eb9..8dd433982b 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -32,6 +32,7 @@
#include "node.h"
#include "io/http_client.h"
#include "os/file_access.h"
+#include "os/thread.h"
class HTTPRequest : public Node {
@@ -69,7 +70,7 @@ private:
bool request_sent;
Ref<HTTPClient> client;
ByteArray body;
- bool use_threads;
+ volatile bool use_threads;
bool got_response;
int response_code;
@@ -80,7 +81,7 @@ private:
FileAccess *file;
int body_len;
- int downloaded;
+ volatile int downloaded;
int body_size_limit;
int redirections;
@@ -93,11 +94,19 @@ private:
void _redirect_request(const String& p_new_url);
+
bool _handle_response(bool *ret_value);
Error _parse_url(const String& p_url);
Error _request();
+ volatile bool thread_done;
+ volatile bool thread_request_quit;
+
+ Thread *thread;
+
+ void _request_done(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data);
+ static void _thread_func(void *p_userdata);
protected:
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index bdb2754e5e..f182f2c96c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1782,7 +1782,6 @@ void Viewport::_gui_input_event(InputEvent p_event) {
if (top->data.modal_exclusive || top->data.modal_frame==OS::get_singleton()->get_frames_drawn()) {
//cancel event, sorry, modal exclusive EATS UP ALL
//alternative, you can't pop out a window the same frame it was made modal (fixes many issues)
- //get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID);
get_tree()->set_input_as_handled();
return; // no one gets the event if exclusive NO ONE
}
@@ -2034,8 +2033,22 @@ void Viewport::_gui_input_event(InputEvent p_event) {
}
+ bool is_tooltip_shown = false;
- if (can_tooltip) {
+ if (gui.tooltip_popup) {
+ if (can_tooltip) {
+ String tooltip = over->get_tooltip(gui.tooltip->get_global_transform().xform_inv(mpos));
+
+ if (tooltip.length() == 0)
+ _gui_cancel_tooltip();
+ else if (tooltip == gui.tooltip_label->get_text())
+ is_tooltip_shown = true;
+ }
+ else
+ _gui_cancel_tooltip();
+ }
+
+ if (can_tooltip && !is_tooltip_shown) {
gui.tooltip=over;
gui.tooltip_pos=mpos;//(parent_xform * get_transform()).affine_inverse().xform(pos);
@@ -2062,7 +2075,6 @@ void Viewport::_gui_input_event(InputEvent p_event) {
- //get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID);
get_tree()->set_input_as_handled();
@@ -2102,7 +2114,7 @@ void Viewport::_gui_input_event(InputEvent p_event) {
if (gui.key_event_accepted) {
- get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID);
+ get_tree()->set_input_as_handled();
break;
}
}
@@ -2162,7 +2174,7 @@ void Viewport::_gui_input_event(InputEvent p_event) {
if (next) {
next->grab_focus();
- get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID);
+ get_tree()->set_input_as_handled();
}
}
@@ -2355,8 +2367,7 @@ void Viewport::_gui_control_grab_focus(Control* p_control) {
if (gui.key_focus && gui.key_focus==p_control)
return;
- _gui_remove_focus();
- get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_gui_remove_focus");
+ get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"_viewports","_gui_remove_focus");
gui.key_focus=p_control;
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
p_control->update();
@@ -2664,6 +2675,7 @@ void Viewport::_bind_methods() {
ObjectTypeDB::bind_method(_MD("is_input_disabled"), &Viewport::is_input_disabled);
ObjectTypeDB::bind_method(_MD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip);
+ ObjectTypeDB::bind_method(_MD("_gui_remove_focus"), &Viewport::_gui_remove_focus);
ADD_PROPERTY( PropertyInfo(Variant::RECT2,"rect"), _SCS("set_rect"), _SCS("get_rect") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"own_world"), _SCS("set_use_own_world"), _SCS("is_using_own_world") );
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 88ff09e961..29460790ff 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -644,7 +644,7 @@ Vector2 Curve2D::interpolate_baked(float p_offset,bool p_cubic) const{
if (p_offset>=baked_max_ofs)
return r[bpc-1];
- int idx = Math::floor(p_offset/bake_interval);
+ int idx = Math::floor((double)p_offset/(double)bake_interval);
float frac = Math::fmod(p_offset,bake_interval);
if (idx>=bpc-1) {
@@ -1117,7 +1117,7 @@ Vector3 Curve3D::interpolate_baked(float p_offset,bool p_cubic) const{
if (p_offset>=baked_max_ofs)
return r[bpc-1];
- int idx = Math::floor(p_offset/bake_interval);
+ int idx = Math::floor((double)p_offset/(double)bake_interval);
float frac = Math::fmod(p_offset,bake_interval);
if (idx>=bpc-1) {
@@ -1161,7 +1161,7 @@ float Curve3D::interpolate_baked_tilt(float p_offset) const{
if (p_offset>=baked_max_ofs)
return r[bpc-1];
- int idx = Math::floor(p_offset/bake_interval);
+ int idx = Math::floor((double)p_offset/(double)bake_interval);
float frac = Math::fmod(p_offset,bake_interval);
if (idx>=bpc-1) {
diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp
index 3202e52abb..43e52f26e8 100644
--- a/servers/physics/body_pair_sw.cpp
+++ b/servers/physics/body_pair_sw.cpp
@@ -323,13 +323,13 @@ bool BodyPairSW::setup(float p_step) {
#endif
if (A->can_report_contacts()) {
- Vector3 crB = A->get_angular_velocity().cross( c.rA ) + A->get_linear_velocity();
- A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crB);
+ Vector3 crA = A->get_angular_velocity().cross( c.rA ) + A->get_linear_velocity();
+ A->add_contact(global_A,-c.normal,depth,shape_A,global_B,shape_B,B->get_instance_id(),B->get_self(),crA);
}
if (B->can_report_contacts()) {
- Vector3 crA = A->get_angular_velocity().cross( c.rB ) + A->get_linear_velocity();
- B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crA);
+ Vector3 crB = B->get_angular_velocity().cross( c.rB ) + B->get_linear_velocity();
+ B->add_contact(global_B,c.normal,depth,shape_B,global_A,shape_A,A->get_instance_id(),A->get_self(),crB);
}
if (A->is_shape_set_as_trigger(shape_A) || B->is_shape_set_as_trigger(shape_B) || (A->get_mode()<=PhysicsServer::BODY_MODE_KINEMATIC && B->get_mode()<=PhysicsServer::BODY_MODE_KINEMATIC)) {
diff --git a/tools/editor/asset_library_editor_plugin.cpp b/tools/editor/asset_library_editor_plugin.cpp
index 4f685badfb..3fd5d2b5d1 100644
--- a/tools/editor/asset_library_editor_plugin.cpp
+++ b/tools/editor/asset_library_editor_plugin.cpp
@@ -134,13 +134,13 @@ EditorAssetLibraryItem::EditorAssetLibraryItem() {
category = memnew( LinkButton );
category->set_text("Editor Tools");
category->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
- title->connect("pressed",this,"_category_clicked");
+ category->connect("pressed",this,"_category_clicked");
vb->add_child(category);
author = memnew( LinkButton );
author->set_text("Johny Tolengo");
author->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
- title->connect("pressed",this,"_author_clicked");
+ author->connect("pressed",this,"_author_clicked");
vb->add_child(author);
HBoxContainer *rating_hb = memnew( HBoxContainer );
@@ -325,6 +325,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
String error_text;
+ print_line("COMPLETED: "+itos(p_status)+" code: "+itos(p_code)+" data size: "+itos(p_data.size()));
switch(p_status) {
@@ -383,7 +384,9 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
print_line("max: "+itos(download->get_body_size())+" bytes: "+itos(download->get_downloaded_bytes()));
install->set_disabled(false);
- status->set_text("Success!");
+ progress->set_val(download->get_downloaded_bytes());
+
+ status->set_text("Success! ("+String::humanize_size(download->get_downloaded_bytes())+")");
set_process(false);
}
@@ -411,6 +414,10 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
progress->set_val(download->get_downloaded_bytes());
int cstatus = download->get_http_client_status();
+
+ if (cstatus==HTTPClient::STATUS_BODY)
+ status->set_text("Fetching: "+String::humanize_size(download->get_downloaded_bytes()));
+
if (cstatus!=prev_status) {
switch(cstatus) {
@@ -423,9 +430,6 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
case HTTPClient::STATUS_REQUESTING: {
status->set_text("Requesting..");
} break;
- case HTTPClient::STATUS_BODY: {
- status->set_text("Downloading..");
- } break;
default: {}
}
prev_status=cstatus;
@@ -457,6 +461,7 @@ void EditorAssetLibraryItemDownload::_install() {
void EditorAssetLibraryItemDownload::_make_request() {
download->cancel_request();
download->set_download_file(EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_asset_"+itos(asset_id))+".zip");
+
Error err = download->request(host);
if(err!=OK) {
status->set_text("Error making request");
@@ -1007,6 +1012,7 @@ void EditorAssetLibrary::_api_request(const String& p_request, RequestType p_req
if (requesting!=REQUESTING_NONE) {
request->cancel_request();
}
+
requesting=p_request_type;
error_hb->hide();
@@ -1465,6 +1471,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
request = memnew( HTTPRequest );
add_child(request);
+ request->set_use_threads(EDITOR_DEF("asset_library/use_threads",true));
request->connect("request_completed",this,"_http_request_completed");
last_queue_id=0;
diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp
index 6c8d25aa01..ed7a46d70d 100644
--- a/tools/editor/code_editor.cpp
+++ b/tools/editor/code_editor.cpp
@@ -1032,8 +1032,8 @@ void CodeTextEditor::_reset_zoom() {
void CodeTextEditor::_line_col_changed() {
- String text = String()+TTR("Line:")+" "+itos(text_editor->cursor_get_line()+1)+", "+TTR("Col:")+" "+itos(text_editor->cursor_get_column());
- line_col->set_text(text);
+ line_nb->set_text(itos(text_editor->cursor_get_line() + 1));
+ col_nb->set_text(itos(text_editor->cursor_get_column()));
}
void CodeTextEditor::_text_changed() {
@@ -1191,6 +1191,7 @@ CodeTextEditor::CodeTextEditor() {
HBoxContainer *status_bar = memnew( HBoxContainer );
status_mc->add_child(status_bar);
status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
+ status_bar->add_child( memnew( Label ) ); //to keep the height if the other labels are not visible
idle = memnew( Timer );
add_child(idle);
@@ -1212,14 +1213,33 @@ CodeTextEditor::CodeTextEditor() {
status_bar->add_spacer();
- line_col = memnew( Label );
- status_bar->add_child(line_col);
- line_col->set_valign(Label::VALIGN_CENTER);
- line_col->set_autowrap(true);
- line_col->set_v_size_flags(SIZE_FILL);
- line_col->set_custom_minimum_size(Size2(100,1)*EDSCALE);
- status_bar->add_child( memnew( Label ) ); //to keep the height if the other labels are not visible
-
+ Label *line_txt = memnew( Label );
+ status_bar->add_child(line_txt);
+ line_txt->set_align(Label::ALIGN_RIGHT);
+ line_txt->set_valign(Label::VALIGN_CENTER);
+ line_txt->set_v_size_flags(SIZE_FILL);
+ line_txt->set_text(TTR("Line:"));
+
+ line_nb = memnew( Label );
+ status_bar->add_child(line_nb);
+ line_nb->set_valign(Label::VALIGN_CENTER);
+ line_nb->set_v_size_flags(SIZE_FILL);
+ line_nb->set_autowrap(true); // workaround to prevent resizing the label on each change
+ line_nb->set_custom_minimum_size(Size2(40,1)*EDSCALE);
+
+ Label *col_txt = memnew( Label );
+ status_bar->add_child(col_txt);
+ col_txt->set_align(Label::ALIGN_RIGHT);
+ col_txt->set_valign(Label::VALIGN_CENTER);
+ col_txt->set_v_size_flags(SIZE_FILL);
+ col_txt->set_text(TTR("Col:"));
+
+ col_nb = memnew( Label );
+ status_bar->add_child(col_nb);
+ col_nb->set_valign(Label::VALIGN_CENTER);
+ col_nb->set_v_size_flags(SIZE_FILL);
+ col_nb->set_autowrap(true); // workaround to prevent resizing the label on each change
+ col_nb->set_custom_minimum_size(Size2(40,1)*EDSCALE);
text_editor->connect("input_event", this,"_text_editor_input_event");
text_editor->connect("cursor_changed", this,"_line_col_changed");
diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h
index 7983c46f7a..88d882bd33 100644
--- a/tools/editor/code_editor.h
+++ b/tools/editor/code_editor.h
@@ -196,7 +196,8 @@ class CodeTextEditor : public VBoxContainer {
TextEdit *text_editor;
FindReplaceBar *find_replace_bar;
- Label *line_col;
+ Label *line_nb;
+ Label *col_nb;
Label *info;
Timer *idle;
Timer *code_complete_timer;
diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp
index 01e6b613c0..0d162cbe56 100644
--- a/tools/editor/editor_plugin.cpp
+++ b/tools/editor/editor_plugin.cpp
@@ -44,9 +44,9 @@ void EditorPlugin::remove_custom_type(const String& p_type){
}
-void EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
+ToolButton * EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
- EditorNode::get_singleton()->add_bottom_panel_item(p_title,p_control);
+ return EditorNode::get_singleton()->add_bottom_panel_item(p_title,p_control);
}
void EditorPlugin::add_control_to_dock(DockSlot p_slot,Control *p_control) {
@@ -284,7 +284,7 @@ Control *EditorPlugin::get_base_control() {
void EditorPlugin::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_control_to_container","container","control:Control"),&EditorPlugin::add_control_to_container);
- ObjectTypeDB::bind_method(_MD("add_control_to_bottom_panel","control:Control","title"),&EditorPlugin::add_control_to_bottom_panel);
+ ObjectTypeDB::bind_method(_MD("add_control_to_bottom_panel:ToolButton","control:Control","title"),&EditorPlugin::add_control_to_bottom_panel);
ObjectTypeDB::bind_method(_MD("add_control_to_dock","slot","control:Control"),&EditorPlugin::add_control_to_dock);
ObjectTypeDB::bind_method(_MD("remove_control_from_docks","control:Control"),&EditorPlugin::remove_control_from_docks);
ObjectTypeDB::bind_method(_MD("remove_control_from_bottom_panel","control:Control"),&EditorPlugin::remove_control_from_bottom_panel);
diff --git a/tools/editor/editor_plugin.h b/tools/editor/editor_plugin.h
index b93b6624d0..b960a7d5af 100644
--- a/tools/editor/editor_plugin.h
+++ b/tools/editor/editor_plugin.h
@@ -29,6 +29,7 @@
#ifndef EDITOR_PLUGIN_H
#define EDITOR_PLUGIN_H
+#include "scene/gui/tool_button.h"
#include "scene/main/node.h"
#include "scene/resources/texture.h"
#include "undo_redo.h"
@@ -92,7 +93,7 @@ public:
//TODO: send a resoucre for editing to the editor node?
void add_control_to_container(CustomControlContainer p_location, Control *p_control);
- void add_control_to_bottom_panel(Control *p_control, const String &p_title);
+ ToolButton *add_control_to_bottom_panel(Control *p_control, const String &p_title);
void add_control_to_dock(DockSlot p_slot,Control *p_control);
void remove_control_from_docks(Control *p_control);
void remove_control_from_bottom_panel(Control *p_control);
diff --git a/version.py b/version.py
index dd80ca1b19..19ff05f715 100644
--- a/version.py
+++ b/version.py
@@ -2,4 +2,4 @@ short_name="godot"
name="Godot Engine"
major=2
minor=1
-status="beta"
+status="rc1"