Quellcode durchsuchen

Fixed Relative Positioning Drift

I was continuing to see controls slowly drift out of position if relative positioning was turned on and you resized the window a few times. This was due to rounding that happens when the control's position changed. The fix effectively uses the first calculation in all future repositioning which prevents drift.
Peter Robinson vor 3 Jahren
Ursprung
Commit
2855390ddd

+ 47 - 15
engine/source/gui/guiControl.cc

@@ -86,6 +86,10 @@ GuiControl::GuiControl()
    mRenderInsetLT.set(0, 0);
    mRenderInsetRB.set(0, 0);
    mMinExtent.set(0, 0);
+   mStoredRelativePosH.set(0, 0);
+   mStoredRelativePosV.set(0, 0);
+   mUseRelPosH = false;
+   mUseRelPosV = false;
 
    mProfile = NULL;
 
@@ -197,7 +201,7 @@ void GuiControl::initPersistFields()
    addField("HorizSizing",       TypeEnum,			Offset(mHorizSizing, GuiControl), 1, &gHorizSizingTable);
    addField("VertSizing",        TypeEnum,			Offset(mVertSizing, GuiControl), 1, &gVertSizingTable);
 
-   addField("Position",          TypePoint2I,		Offset(mBounds.point, GuiControl));
+   addProtectedField("Position",          TypePoint2I,		Offset(mBounds.point, GuiControl), &setPositionFn, &defaultProtectedGetFn, "The location of the control in relation to its parent's content area.");
    addProtectedField("Extent",            TypePoint2I,		Offset(mBounds.extent, GuiControl), &setExtentFn, &defaultProtectedGetFn, "The size of the control writen as width and height.");
    addProtectedField("MinExtent",         TypePoint2I,		Offset(mMinExtent, GuiControl), &setMinExtentFn, &defaultProtectedGetFn, &writeMinExtentFn, "The extent will not shrink below this size.");
    addField("canSave",           TypeBool,			Offset(mCanSave, GuiControl));
@@ -486,14 +490,15 @@ void GuiControl::parentResized(const Point2I &oldParentExtent, const Point2I &ne
         newExtent.x += deltaX;
     else if (mHorizSizing == horizResizeLeft)
       newPosition.x += deltaX;
-   else if (mHorizSizing == horizResizeRelative && oldParentExtent.x != 0)
-   {
-      S32 newLeft = (newPosition.x * newParentExtent.x) / oldParentExtent.x;
-      S32 newRight = ((newPosition.x + newExtent.x) * newParentExtent.x) / oldParentExtent.x;
+    else if (mHorizSizing == horizResizeRelative && oldParentExtent.x != 0)
+    {
+        Point2F percent = relPosBatteryH(newPosition.x, newExtent.x, oldParentExtent.x);
+        S32 newLeft = mRound(percent.x * newParentExtent.x);
+        S32 newRight = mRound(percent.y * newParentExtent.x);
 
-      newPosition.x = newLeft;
-      newExtent.x = newRight - newLeft;
-   }
+        newPosition.x = newLeft;
+        newExtent.x = newRight - newLeft;
+    }
 
     if (mVertSizing == vertResizeCenter)
        newPosition.y = (parentInnerExt.y - mBounds.extent.y) >> 1;
@@ -501,14 +506,15 @@ void GuiControl::parentResized(const Point2I &oldParentExtent, const Point2I &ne
         newExtent.y += deltaY;
     else if (mVertSizing == vertResizeTop)
       newPosition.y += deltaY;
-   else if(mVertSizing == vertResizeRelative && oldParentExtent.y != 0)
-   {
-      S32 newTop = (newPosition.y * newParentExtent.y) / oldParentExtent.y;
-      S32 newBottom = ((newPosition.y + newExtent.y) * newParentExtent.y) / oldParentExtent.y;
+    else if(mVertSizing == vertResizeRelative && oldParentExtent.y != 0)
+    {
+        Point2F percent = relPosBatteryV(newPosition.y, newExtent.y, oldParentExtent.y);
+        S32 newTop = mRound(percent.x * newParentExtent.y);
+        S32 newBottom = mRound(percent.y * newParentExtent.y);
 
-      newPosition.y = newTop;
-      newExtent.y = newBottom - newTop;
-   }
+        newPosition.y = newTop;
+        newExtent.y = newBottom - newTop;
+    }
 
    newExtent = extentBattery(newExtent);
 
@@ -549,6 +555,32 @@ Point2I GuiControl::extentBattery(Point2I &newExtent)
 	return result;
 }
 
+Point2F GuiControl::relPosBatteryH(S32 pos, S32 ext, S32 parentExt)
+{
+    if (!mUseRelPosH)
+    {
+        relPosBattery(mStoredRelativePosH, pos, ext, parentExt);
+        mUseRelPosH = true;
+    }
+    return mStoredRelativePosH;
+}
+
+Point2F GuiControl::relPosBatteryV(S32 pos, S32 ext, S32 parentExt)
+{
+    if (!mUseRelPosV)
+    {
+        relPosBattery(mStoredRelativePosV, pos, ext, parentExt);
+        mUseRelPosV = true;
+    }
+    return mStoredRelativePosV;
+}
+
+void GuiControl::relPosBattery(Point2F& battery, S32 pos, S32 ext, S32 parentExt)
+{
+    battery.x = static_cast<F32>(pos) / parentExt;
+    battery.y = static_cast<F32>(pos + ext) / parentExt;
+}
+
 //----------------------------------------------------------------
 
 void GuiControl::onRender(Point2I offset, const RectI &updateRect)

+ 14 - 2
engine/source/gui/guiControl.h

@@ -197,6 +197,10 @@ protected:
     S32 mVertSizing;       ///< Set from vertSizingOptions.
 
 	Point2I mStoredExtent; //Used in conjunction with the min extent.
+    Point2F mStoredRelativePosH; //Used to prevent rounding drift when using relative positioning.
+    Point2F mStoredRelativePosV; //Used to prevent rounding drift when using relative positioning.
+    bool mUseRelPosH;
+    bool mUseRelPosV;
 
     StringTableEntry	mConsoleVariable;
     StringTableEntry	mConsoleCommand;
@@ -329,10 +333,12 @@ public:
 	static bool writeTextWrapFn(void* obj, const char* data) { return static_cast<GuiControl*>(obj)->getTextWrap(); }
     static bool writeTextExtendFn(void* obj, const char* data) { return static_cast<GuiControl*>(obj)->getTextExtend(); }
 
-	static bool setExtentFn(void* obj, const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); Vector2 v = Vector2(data); ctrl->setExtent(Point2I(v.x, v.y)); ctrl->resetStoredExtent(); return false; }
-	static bool setMinExtentFn(void* obj, const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); Vector2 v = Vector2(data); ctrl->mMinExtent.set(v.x, v.y); ctrl->resetStoredExtent(); return false; }
+    static bool setExtentFn(void* obj, const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); Vector2 v = Vector2(data); ctrl->setExtent(Point2I(v.x, v.y)); ctrl->resetStoredExtent(); ctrl->resetStoredRelPos(); return false; }
+	static bool setMinExtentFn(void* obj, const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); Vector2 v = Vector2(data); ctrl->mMinExtent.set(v.x, v.y); ctrl->resetStoredExtent(); ctrl->resetStoredRelPos(); return false; }
 	static bool writeMinExtentFn(void* obj,  const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); return ctrl->mMinExtent.x != 0 || ctrl->mMinExtent.y != 0; }
 
+    static bool setPositionFn(void* obj, const char* data) { GuiControl* ctrl = static_cast<GuiControl*>(obj); Vector2 v = Vector2(data); ctrl->setPosition(Point2I(v.x, v.y)); ctrl->resetStoredRelPos(); return false; }
+
     /// @}
 
     /// @name Flags
@@ -767,6 +773,12 @@ public:
 	//Expells all stored extent
 	inline void resetStoredExtent() { mStoredExtent.set(0,0); }
 
+    //Stores the position when using relative positioning
+    Point2F relPosBatteryH(S32 pos, S32 ext, S32 parentExt);
+    Point2F relPosBatteryV(S32 pos, S32 ext, S32 parentExt);
+    void relPosBattery(Point2F& battery, S32 pos, S32 ext, S32 parentExt);
+    void resetStoredRelPos() { mUseRelPosH = false; mUseRelPosV = false; }
+
 protected:
 	virtual void interpolateTick(F32 delta) {};
 	virtual void processTick() {};

+ 2 - 0
engine/source/gui/guiControl_ScriptBinding.h

@@ -258,6 +258,7 @@ ConsoleMethodWithDocs( GuiControl, setPosition, ConsoleVoid, 4, 4, (int x, int y
    //see if we can turn the x/y into ints directly, 
    Point2I lPos(dAtoi(argv[2]), dAtoi(argv[3]));
    object->mBounds.set(lPos,object->mBounds.extent);
+   object->resetStoredRelPos();
 }
 
 /*! Get the width and height of the control.
@@ -279,6 +280,7 @@ ConsoleMethodWithDocs( GuiControl, setExtent, ConsoleVoid, 4, 4, (int width, int
    Point2I kExt(dAtoi(argv[2]), dAtoi(argv[3]));
    object->setExtent(kExt);
    object->resetStoredExtent();
+   object->resetStoredRelPos();
 }
 
 /*! Get the minimum allowed size of the control.