using Sandbox.UI.Construct; namespace Sandbox.UI; public partial class Popup : BasePopup { /// /// Which panel triggered this popup. Set by or the constructor. /// public Panel PopupSource { get; set; } /// /// Currently selected option in the popup. Used internally for keyboard navigation. /// public Panel SelectedChild { get; set; } /// /// Positioning mode for this popup. /// public PositionMode Position { get; set; } /// /// Offset away from based on . /// public float PopupSourceOffset { get; set; } /// /// If true, will close this popup when the is hidden. /// public bool CloseWhenParentIsHidden { get; set; } = false; /// /// Dictates where a is positioned. /// public enum PositionMode { /// /// To the left of the source panel, centered. /// Left, /// /// To the left of the source panel, aligned to the bottom. /// LeftBottom, /// /// Above the source panel, aligned to the left. /// AboveLeft, /// /// Below the source panel, aliging on the left. Do not stretch to size of . /// BelowLeft, /// /// Below the source panel, centered horizontally. /// BelowCenter, /// /// Below the source panel, stretch to the width of the . /// BelowStretch, /// /// Above, centered /// AboveCenter, /// /// Position where the mouse cursor is currently /// UnderMouse } public Popup() { } /// public Popup( Panel sourcePanel, PositionMode position, float offset ) { SetPositioning( sourcePanel, position, offset ); } /// /// Sets , and . /// Applies relevant CSS classes. /// /// Which panel triggered this popup. /// Desired positioning mode. /// Offset away from the . public void SetPositioning( Panel sourcePanel, PositionMode position, float offset ) { Parent = sourcePanel.FindPopupPanel(); PopupSource = sourcePanel; Position = position; PopupSourceOffset = offset; AddClass( "popup-panel" ); PositionMe( true ); switch ( Position ) { case PositionMode.Left: AddClass( "left" ); break; case PositionMode.LeftBottom: AddClass( "left-bottom" ); break; case PositionMode.AboveLeft: AddClass( "above-left" ); break; case PositionMode.AboveCenter: AddClass( "above-center" ); break; case PositionMode.BelowLeft: AddClass( "below-left" ); break; case PositionMode.BelowCenter: AddClass( "below-center" ); break; case PositionMode.BelowStretch: AddClass( "below-stretch" ); break; } } /// /// Header panel that holds and . /// protected Panel Header; /// /// Label that dispalys . /// protected Label TitleLabel; /// /// Panel that dispalys . /// protected IconPanel IconPanel; void CreateHeader() { if ( Header.IsValid() ) return; Header = Add.Panel( "header" ); IconPanel = Header.Add.Icon( null ); TitleLabel = Header.Add.Label( null, "title" ); } /// /// If set, will add an unselectable header with given text and . /// public string Title { get => TitleLabel?.Text; set { CreateHeader(); TitleLabel.Text = value; } } /// /// If set, will add an unselectable header with given icon and . /// public string Icon { get => IconPanel?.Text; set { CreateHeader(); IconPanel.Text = value; } } /// /// Closes all panels, marks this one as a success and closes it. /// public void Success() { AddClass( "success" ); Popup.CloseAll(); } /// /// Closes all panels, marks this one as a failure and closes it. /// public void Failure() { AddClass( "failure" ); Popup.CloseAll(); } /// /// Add an option to this popup with given text and click action. /// public Panel AddOption( string text, Action action = null ) { return AddChild( new Button( text, () => { CloseAll(); action?.Invoke(); } ) ); } /// /// Add an option to this popup with given text, icon and click action. /// public Panel AddOption( string text, string icon, Action action = null ) { return AddChild( new Button( text, icon, () => { CloseAll(); action?.Invoke(); } ) ); } /// /// Move selection in given direction. /// /// Positive numbers move selection downwards, negative - upwards. public void MoveSelection( int dir ) { var currentIndex = GetChildIndex( SelectedChild ); if ( currentIndex >= 0 ) currentIndex += dir; else if ( currentIndex < 0 ) currentIndex = dir == 1 ? 0 : -1; SelectedChild?.SetClass( "active", false ); SelectedChild = GetChild( currentIndex, true ); SelectedChild?.SetClass( "active", true ); } public override void Tick() { base.Tick(); if ( CloseWhenParentIsHidden && !PopupSource.IsValid() ) { Delete(); return; } PositionMe( false ); } public override void OnLayout( ref Rect layoutRect ) { var padding = 10; var h = Screen.Height - padding; var w = Screen.Width - padding; if ( layoutRect.Bottom > h ) { layoutRect.Top -= layoutRect.Bottom - h; layoutRect.Bottom -= layoutRect.Bottom - h; } if ( layoutRect.Right > w ) { layoutRect.Left -= layoutRect.Right - w; layoutRect.Right -= layoutRect.Right - w; } } void PositionMe( bool isInitial ) { var rect = PopupSource.Box.Rect * PopupSource.ScaleFromScreen; var w = Screen.Width * PopupSource.ScaleFromScreen; var h = Screen.Height * PopupSource.ScaleFromScreen; Style.MaxHeight = Screen.Height - 50; switch ( Position ) { case PositionMode.Left: { Style.Left = null; Style.Right = ((w - rect.Left) + PopupSourceOffset); Style.Top = rect.Top + rect.Height * 0.5f; break; } case PositionMode.LeftBottom: { Style.Left = null; Style.Right = ((w - rect.Left) + PopupSourceOffset); Style.Top = null; Style.Bottom = (h - rect.Bottom); break; } case PositionMode.AboveLeft: { Style.Left = rect.Left; Style.Bottom = (Parent.Box.Rect * Parent.ScaleFromScreen).Height - rect.Top + PopupSourceOffset; break; } case PositionMode.AboveCenter: { Style.Left = rect.Left + rect.Width * 0.5f; Style.Bottom = (Parent.Box.Rect * Parent.ScaleFromScreen).Height - rect.Top + PopupSourceOffset; break; } case PositionMode.BelowLeft: { Style.Left = rect.Left; Style.Top = rect.Bottom + PopupSourceOffset; break; } case PositionMode.BelowCenter: { Style.Left = rect.Center.x; // centering is done via styles Style.Top = rect.Bottom + PopupSourceOffset; break; } case PositionMode.BelowStretch: { Style.Left = rect.Left; Style.Width = rect.Width; Style.Top = rect.Bottom + PopupSourceOffset; break; } case PositionMode.UnderMouse: { if ( isInitial ) { Style.Left = Mouse.Position.x * PopupSource.ScaleFromScreen; Style.Top = (Mouse.Position.y + PopupSourceOffset) * PopupSource.ScaleFromScreen; } break; } } Style.Dirty(); } }