1
0

MainViewport.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System.Numerics;
  2. using Content.Client.Viewport;
  3. using Content.Shared.CCVar;
  4. using Robust.Client.UserInterface;
  5. using Robust.Client.UserInterface.Controls;
  6. using Robust.Shared.Configuration;
  7. namespace Content.Client.UserInterface.Controls
  8. {
  9. /// <summary>
  10. /// Wrapper for <see cref="ScalingViewport"/> that listens to configuration variables.
  11. /// Also does NN-snapping within tolerances.
  12. /// </summary>
  13. public sealed class MainViewport : UIWidget
  14. {
  15. [Dependency] private readonly IConfigurationManager _cfg = default!;
  16. [Dependency] private readonly ViewportManager _vpManager = default!;
  17. public ScalingViewport Viewport { get; }
  18. public MainViewport()
  19. {
  20. IoCManager.InjectDependencies(this);
  21. Viewport = new ScalingViewport
  22. {
  23. AlwaysRender = true,
  24. RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt,
  25. MouseFilter = MouseFilterMode.Stop
  26. };
  27. AddChild(Viewport);
  28. }
  29. protected override void EnteredTree()
  30. {
  31. base.EnteredTree();
  32. _vpManager.AddViewport(this);
  33. }
  34. protected override void ExitedTree()
  35. {
  36. base.ExitedTree();
  37. _vpManager.RemoveViewport(this);
  38. }
  39. public void UpdateCfg()
  40. {
  41. var stretch = _cfg.GetCVar(CCVars.ViewportStretch);
  42. var renderScaleUp = _cfg.GetCVar(CCVars.ViewportScaleRender);
  43. var fixedFactor = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor);
  44. var verticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit);
  45. if (stretch)
  46. {
  47. var snapFactor = CalcSnappingFactor();
  48. if (snapFactor == null)
  49. {
  50. // Did not find a snap, enable stretching.
  51. Viewport.FixedStretchSize = null;
  52. Viewport.StretchMode = ScalingViewportStretchMode.Bilinear;
  53. Viewport.IgnoreDimension = verticalFit ? ScalingViewportIgnoreDimension.Horizontal : ScalingViewportIgnoreDimension.None;
  54. if (renderScaleUp)
  55. {
  56. Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.CeilInt;
  57. }
  58. else
  59. {
  60. Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
  61. Viewport.FixedRenderScale = 1;
  62. }
  63. return;
  64. }
  65. // Found snap, set fixed factor and run non-stretching code.
  66. fixedFactor = snapFactor.Value;
  67. }
  68. Viewport.FixedStretchSize = Viewport.ViewportSize * fixedFactor;
  69. Viewport.StretchMode = ScalingViewportStretchMode.Nearest;
  70. if (renderScaleUp)
  71. {
  72. Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
  73. Viewport.FixedRenderScale = fixedFactor;
  74. }
  75. else
  76. {
  77. // Snapping but forced to render scale at scale 1 so...
  78. // At least we can NN.
  79. Viewport.RenderScaleMode = ScalingViewportRenderScaleMode.Fixed;
  80. Viewport.FixedRenderScale = 1;
  81. }
  82. }
  83. private int? CalcSnappingFactor()
  84. {
  85. // Margin tolerance is tolerance of "the window is too big"
  86. // where we add a margin to the viewport to make it fit.
  87. var cfgToleranceMargin = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin);
  88. // Clip tolerance is tolerance of "the window is too small"
  89. // where we are clipping the viewport to make it fit.
  90. var cfgToleranceClip = _cfg.GetCVar(CCVars.ViewportSnapToleranceClip);
  91. var cfgVerticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit);
  92. // Calculate if the viewport, when rendered at an integer scale,
  93. // is close enough to the control size to enable "snapping" to NN,
  94. // potentially cutting a tiny bit off/leaving a margin.
  95. //
  96. // Idea here is that if you maximize the window at 1080p or 1440p
  97. // we are close enough to an integer scale (2x and 3x resp) that we should "snap" to it.
  98. // Just do it iteratively.
  99. // I'm sure there's a smarter approach that needs one try with math but I'm dumb.
  100. for (var i = 1; i <= 10; i++)
  101. {
  102. var toleranceMargin = i * cfgToleranceMargin;
  103. var toleranceClip = i * cfgToleranceClip;
  104. var scaled = (Vector2) Viewport.ViewportSize * i;
  105. var (dx, dy) = PixelSize - scaled;
  106. // The rule for which snap fits is that at LEAST one axis needs to be in the tolerance size wise.
  107. // One axis MAY be larger but not smaller than tolerance.
  108. // Obviously if it's too small it's bad, and if it's too big on both axis we should stretch up.
  109. // Additionally, if the viewport's supposed to be vertically fit, then the horizontal scale should just be ignored where appropriate.
  110. if ((Fits(dx) || cfgVerticalFit) && Fits(dy) || !cfgVerticalFit && Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy))
  111. {
  112. // Found snap that fits.
  113. return i;
  114. }
  115. bool Larger(float a)
  116. {
  117. return a > toleranceMargin;
  118. }
  119. bool Fits(float a)
  120. {
  121. return a <= toleranceMargin && a >= -toleranceClip;
  122. }
  123. }
  124. return null;
  125. }
  126. protected override void Resized()
  127. {
  128. base.Resized();
  129. UpdateCfg();
  130. }
  131. }
  132. }