mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-13 00:38:42 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
298 lines
9.2 KiB
C#
298 lines
9.2 KiB
C#
namespace Sandbox.UI;
|
|
|
|
|
|
public partial class Panel
|
|
{
|
|
/// <summary>
|
|
/// Current mouse position local to this panels top left corner.
|
|
/// </summary>
|
|
[Hide]
|
|
public Vector2 MousePosition
|
|
{
|
|
get
|
|
{
|
|
if ( FindRootPanel() is not RootPanel root )
|
|
return default;
|
|
|
|
var mp = root.MousePos;
|
|
|
|
if ( GlobalMatrix.HasValue )
|
|
{
|
|
mp = GlobalMatrix.Value.Transform( mp );
|
|
}
|
|
|
|
return mp - Box.Rect.Position;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by <see cref="PanelInput.CheckHover(Panel, Vector2, ref Panel)" /> to transform
|
|
/// the current mouse position using the panel's LocalMatrix (by default). This can be overriden for special cases.
|
|
/// </summary>
|
|
/// <param name="pos"></param>
|
|
/// <returns></returns>
|
|
public virtual Vector2 GetTransformPosition( Vector2 pos )
|
|
{
|
|
return LocalMatrix?.Transform( pos ) ?? pos;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether given screen position is within this panel. This will accurately handle border radius as well.
|
|
/// </summary>
|
|
/// <param name="pos">The position to test, in screen coordinates.</param>
|
|
public bool IsInside( Vector2 pos )
|
|
{
|
|
var rect = Box.Rect;
|
|
|
|
if ( pos.x < rect.Left || pos.x > rect.Right ) return false;
|
|
if ( pos.y < rect.Top || pos.y > rect.Bottom ) return false;
|
|
|
|
var s = ComputedStyle;
|
|
if ( s == null ) return false;
|
|
|
|
pos.x -= rect.Left;
|
|
pos.y -= rect.Top;
|
|
|
|
|
|
if ( s.BorderTopLeftRadius.HasValue && s.BorderTopLeftRadius.Value.Unit > 0 )
|
|
{
|
|
var r = s.BorderTopLeftRadius.Value.GetPixels( (rect.Width + rect.Height) * 0.5f );
|
|
r = MathF.Min( MathF.Min( r, rect.Width / 2.0f ), rect.Height / 2.0f );
|
|
var c = new Vector2( r, r );
|
|
if ( pos.x < c.x && pos.y < c.y && Vector2.Distance( pos, c ) > r )
|
|
return false;
|
|
}
|
|
|
|
if ( s.BorderTopRightRadius.HasValue && s.BorderTopRightRadius.Value.Unit > 0 )
|
|
{
|
|
var r = s.BorderTopRightRadius.Value.GetPixels( (rect.Width + rect.Height) * 0.5f );
|
|
r = MathF.Min( MathF.Min( r, rect.Width / 2.0f ), rect.Height / 2.0f );
|
|
var c = new Vector2( rect.Width - r, r );
|
|
if ( pos.x > c.x && pos.y < c.y && Vector2.Distance( pos, c ) > r )
|
|
return false;
|
|
}
|
|
|
|
if ( s.BorderBottomRightRadius.HasValue && s.BorderBottomRightRadius.Value.Unit > 0 )
|
|
{
|
|
var r = s.BorderBottomRightRadius.Value.GetPixels( (rect.Width + rect.Height) * 0.5f );
|
|
r = MathF.Min( MathF.Min( r, rect.Width / 2.0f ), rect.Height / 2.0f );
|
|
var c = new Vector2( rect.Width - r, rect.Height - r );
|
|
if ( pos.x > c.x && pos.y > c.y && Vector2.Distance( pos, c ) > r )
|
|
return false;
|
|
}
|
|
|
|
if ( s.BorderBottomLeftRadius.HasValue && s.BorderBottomLeftRadius.Value.Unit > 0 )
|
|
{
|
|
var r = s.BorderBottomLeftRadius.Value.GetPixels( (rect.Width + rect.Height) * 0.5f );
|
|
r = MathF.Min( MathF.Min( r, rect.Width / 2.0f ), rect.Height / 2.0f );
|
|
var c = new Vector2( r, rect.Height - r );
|
|
if ( pos.x < c.x && pos.y > c.y && Vector2.Distance( pos, c ) > r )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the given rect is inside this panels bounds. (<see cref="Box.Rect"/>)
|
|
/// </summary>
|
|
/// <param name="rect">The rect to test, which should have screen-space coordinates.</param>
|
|
/// <param name="fullyInside"><see langword="true"/> to test if the given rect is completely inside the panel. <see langword="false"/> to test for an intersection.</param>
|
|
public bool IsInside( Rect rect, bool fullyInside )
|
|
{
|
|
return rect.IsInside( Box.Rect, fullyInside );
|
|
}
|
|
|
|
/// <summary>
|
|
/// False by default, can this element accept keyboard focus. If an element accepts
|
|
/// focus it'll be able to receive keyboard input.
|
|
/// </summary>
|
|
[Property]
|
|
public bool AcceptsFocus { get; set; }
|
|
|
|
/// <summary>
|
|
/// Describe what to do with keyboard input. The default is InputMode.UI which means that when
|
|
/// focused, this panel will receive Keys Typed and Button Events.
|
|
/// If you set this to InputMode.Game, this panel will redirect its inputs to the game, which means
|
|
/// for example that if you're focused on this panel and press space, it'll send the jump button to the game.
|
|
/// </summary>
|
|
[Property]
|
|
public PanelInputType ButtonInput { get; set; }
|
|
|
|
/// <summary>
|
|
/// False by default. Anything that is capable of accepting IME input should return true. Which is probably just a TextEntry.
|
|
/// </summary>
|
|
[Hide]
|
|
public virtual bool AcceptsImeInput => false;
|
|
|
|
/// <summary>
|
|
/// Give input focus to this panel.
|
|
/// </summary>
|
|
public bool Focus()
|
|
{
|
|
return InputFocus.Set( this );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove input focus from this panel.
|
|
/// </summary>
|
|
public bool Blur()
|
|
{
|
|
return InputFocus.Clear( this );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when any button, mouse (except for mouse4/5) and keyboard, are pressed or depressed while hovering this panel.
|
|
/// </summary>
|
|
public virtual void OnButtonEvent( ButtonEvent e )
|
|
{
|
|
Parent?.OnButtonEvent( e );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when a printable character has been typed (pressed) while this panel has input focus. (<see cref="Focus"/>)
|
|
/// </summary>
|
|
public virtual void OnKeyTyped( char k )
|
|
{
|
|
Parent?.OnKeyTyped( k );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when any keyboard button has been typed (pressed) while this panel has input focus. (<see cref="Focus"/>)
|
|
/// </summary>
|
|
public virtual void OnButtonTyped( ButtonEvent e )
|
|
{
|
|
Parent?.OnButtonTyped( e );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the user presses CTRL+V while this panel has input focus.
|
|
/// </summary>
|
|
/// <param name="text"></param>
|
|
public virtual void OnPaste( string text )
|
|
{
|
|
Parent?.OnPaste( text );
|
|
}
|
|
|
|
/// <summary>
|
|
/// If we have a value that can be copied to the clipboard, return it here.
|
|
/// </summary>
|
|
public virtual string GetClipboardValue( bool cut )
|
|
{
|
|
if ( AllowChildSelection )
|
|
return CollectSelectedChildrenText( this );
|
|
|
|
if ( Parent != null )
|
|
return Parent.GetClipboardValue( cut );
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the player scrolls their mouse wheel while hovering this panel.
|
|
/// </summary>
|
|
/// <param name="value">The scroll wheel delta. Positive values are scrolling down, negative - up.</param>
|
|
public virtual void OnMouseWheel( Vector2 value )
|
|
{
|
|
if ( TryScroll( value ) )
|
|
return;
|
|
|
|
Parent?.OnMouseWheel( value );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from <see cref="OnMouseWheel"/> to try to scroll.
|
|
/// </summary>
|
|
/// <param name="value">The scroll wheel delta. Positive values are scrolling down, negative - up.</param>
|
|
/// <returns>Return true to NOT propagate the event to the <see cref="Parent"/>.</returns>
|
|
public bool TryScroll( Vector2 value )
|
|
{
|
|
if ( ComputedStyle == null ) return false;
|
|
if ( !HasScrollY && !HasScrollX ) return false;
|
|
|
|
// If we're not scrolling in the same direction that this panel overflows in, ignore
|
|
if ( ComputedStyle.OverflowX != OverflowMode.Scroll && value.x != 0 ) return false;
|
|
if ( ComputedStyle.OverflowY != OverflowMode.Scroll && value.y != 0 ) return false;
|
|
|
|
var velocityAdd = Vector2.Zero;
|
|
|
|
if ( ComputedStyle.OverflowX == OverflowMode.Scroll && HasScrollX ) velocityAdd += new Vector2( value.x * -20, 0 );
|
|
if ( ComputedStyle.OverflowY == OverflowMode.Scroll && HasScrollY ) velocityAdd += new Vector2( 0, value.y * 20 );
|
|
|
|
velocityAdd *= (1 + ScrollVelocity.Length / 100.0f);
|
|
ScrollVelocity += velocityAdd;
|
|
|
|
if ( velocityAdd.Length.AlmostEqual( 0 ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scroll to the bottom, if the panel has scrolling enabled.
|
|
/// </summary>
|
|
/// <returns>Whether we scrolled to the bottom or not.</returns>
|
|
public bool TryScrollToBottom()
|
|
{
|
|
if ( ComputedStyle == null ) return false;
|
|
if ( !HasScrollY ) return false;
|
|
|
|
ScrollOffset = new Vector2( ScrollOffset.x, ScrollSize.y );
|
|
IsScrollAtBottom = true;
|
|
ScrollVelocity = new Vector2( 0, 0 );
|
|
return true;
|
|
}
|
|
|
|
internal static Panel MouseCapture { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Captures the mouse cursor while active. The cursor will be hidden and will be stuck in place.
|
|
/// <para>You will want to use <see cref="Mouse.Delta"/> in
|
|
/// <see cref="Panel.Tick"/> while <see cref="HasMouseCapture"/> to read mouse movements.</para>
|
|
/// <para>You can call this from <see cref="OnButtonEvent"/> for mouse clicks.</para>
|
|
/// </summary>
|
|
/// <param name="b">Whether to enable or disable the capture.</param>
|
|
public void SetMouseCapture( bool b )
|
|
{
|
|
if ( b )
|
|
{
|
|
MouseCapture = this;
|
|
return;
|
|
}
|
|
|
|
if ( MouseCapture == this )
|
|
{
|
|
MouseCapture = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether this panel is capturing the mouse cursor. See <see cref="SetMouseCapture"/>.
|
|
/// </summary>
|
|
[Hide]
|
|
public bool HasMouseCapture => MouseCapture == this;
|
|
|
|
//
|
|
// These are used by the input system as an optimization
|
|
//
|
|
internal Vector2 WorldCursor;
|
|
internal float WorldDistance = float.MaxValue;
|
|
|
|
/// <summary>
|
|
/// Transform a ray in 3D space to a position on the panel. This is used for world panel input.
|
|
/// </summary>
|
|
/// <param name="ray">The ray in 3D world space to test against this panel.</param>
|
|
/// <param name="position">Position on the panel where the intersection happened, local to the panel's top left corner.</param>
|
|
/// <param name="distance">Distance from the ray's origin to the intersection in 3D space.</param>
|
|
/// <returns>Return true if a hit/intersection was detected.</returns>
|
|
public virtual bool RayToLocalPosition( Ray ray, out Vector2 position, out float distance )
|
|
{
|
|
position = default;
|
|
distance = default;
|
|
|
|
return false;
|
|
}
|
|
}
|