Przeglądaj źródła

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 3 lat temu
rodzic
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.