Just looked at an inventory label containing "Keep Wall Masonry Repair Kit"
Lua Code:
GetUIGlobalScale() == 0.64
label:GetScale() == 1
label:GetTextWidth() == 229.6875
label:GetStringWidth(label:GetText()) == 147 == 0.64 * 229.6875
GetUIGlobalScale() == 1.10
label:GetScale() == 1
label:GetTextWidth() == 223.6364
label:GetStringWidth(label:GetText()) == 246 == 1.10 * 223.6364
GetUIGlobalScale() == 0.64
label:GetScale() == 1.25
label:GetTextWidth() == 229.6875
label:GetStringWidth(label:GetText()) == 229.6875 == 0.64 * 1.25^2 * 229.6875
GetUIGlobalScale() == 1.10
label:GetScale() == 1.25
label:GetTextWidth() == 223.6364
label:GetStringWidth(label:GetText()) == 384.375 == 1.10 * 1.25^2 * 223.6364
You were right about GetStringWidth being multiplied twice by the control's scale. That's really weird. Anyway, with control scale == 1 it's pretty clear that GetStringWidth returns pixels, while GetTextWidth returns UI units (unaffected by scaling).
In the meantime, I found a way around explicitly setting the width of the Label control -- SetWidth(0) and wrap it in a container with SetResizeToFitDescendents(true) and SetResizeToFitPadding(10, 0). Unfortunatelly that trick doesn't work for Button controls, so those still have to adjust to UI scale changes.