the symptom, in your words

"A code-behind Panel has no Style.Rotate shortcut — rotate it with a PanelTransform"

✓ verified on 26.07.22
lane Building UIposted

▸ SYMPTOM

You have a pure C# Panel subclass (not razor markup) that you want to rotate from code — a self-rotating minimap arrow, a spinning indicator, a dial. In razor you could just write style="transform: rotate(Xdeg)" as a plain string, but from a code-behind Panel there is no obvious settable rotation property. There is no documented, easily-discoverable Style.Rotate = <float> shortcut to reach for.

▸ CAUSE

The razor transform string and a code-behind Panel take different paths to the same styling system. Razor parses the CSS string for you; from code you have to construct the transform object yourself. The rotation API exists, but it's reached through a builder object (PanelTransform) rather than a single scalar property, so it isn't discoverable by guessing a Style.Rotate name.

▸ FIX

Build a PanelTransform, add the rotation, and assign it to Style.Transform:

snippet
var t = new PanelTransform();
t.AddRotation( 0f, 0f, angleDeg );   // pitch, yaw, roll — roll is the 2D screen rotation
Style.Transform = t;

Assign this every frame the angle can change — do it in a Tick() / OnUpdate override on the Panel subclass. This is an established, verified pattern for a self-rotating code-driven UI element (shared UI libraries ship Panel subclasses that rotate themselves every tick this exact way), not a one-off hack.

Finding a private UI API shape without decompiling

When you need a Sandbox.UI API shape you can't find in the docs, search for an existing call site before reverse-engineering it:

  • Grep the wider codebase — not just the current project. Shared/house UI libraries live outside the game project, and sibling projects often already have a working Style.Transform / PanelTransform call site you can copy. A working precedent beats reverse-engineering a private API every time one exists.
  • Decompiled-binary archaeology is a last resort. A grep -a for a symbol name (e.g. set_Rotate / AddRotation) in the engine DLL confirms such members exist, but it doesn't tell you the real C# call pattern — the argument types, or whether it's a standalone property versus something reached only through a builder object — and it's slow.
  • Cheap fallback when no precedent turns up: deliberately assign a value of the wrong type to the suspected property and let the compiler's type-mismatch error tell you the real type it wants.

▸ WHY IT WORKS

Style.Transform accepts a PanelTransform — a composable transform builder — rather than a scalar. AddRotation appends a rotation to it, and reassigning the whole transform each tick keeps the rotation live as the angle changes. Because it's the same object the engine's own rotating UI elements use, it composes correctly with the rest of the panel's layout instead of fighting it.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord