namespace Sandbox.UI; /// /// Simulates PanelInput for world space panels using a ray and inputs. /// internal class WorldPanelInput : PanelInput { internal Ray Ray { get; set; } internal bool MouseLeftPressed; internal bool MouseRightPressed; internal Vector2 MouseWheel; internal bool UseMouseInput; internal override void Tick( IEnumerable panels, bool mouseIsActive ) { bool hoveredAny = false; var inputData = GetInputData(); List worldPanels = new(); foreach ( var panel in panels.Where( p => p.ChildrenWantMouseInput ) ) { if ( panel.RayToLocalPosition( Ray, out panel.WorldCursor, out panel.WorldDistance ) ) worldPanels.Add( panel ); } // In order of distance, update our mouse on them foreach ( var panel in worldPanels.OrderBy( x => x.WorldDistance ) ) { inputData.MousePos = panel.WorldCursor; if ( UpdateMouse( panel, inputData ) ) { hoveredAny = true; break; } } if ( !hoveredAny ) { SetHovered( null ); } SimulateEvents(); } internal override InputData GetInputData() { if ( UseMouseInput ) { return base.GetInputData(); } return new InputData { Mouse0 = MouseLeftPressed, Mouse2 = MouseRightPressed, MouseWheel = MouseWheel, }; } // // Simulate some events, these are a bit different from how InputRouter // handles it, so it's a bit shit that this is repeated but required for now. // // // We only want to count as a double click if it was clicked within // a small amount of pixels & on the same root panel. // It's not a double click if you click, move the mouse, and then click again. // internal Panel LastClickRoot; internal Vector2 LastClickPos; internal RealTimeSince LastClickTimeSince; // Bit more forgiving then normal panels ( shaky VR hands ) internal const float MaxAltClickDelta = 50.0f; internal Queue DoubleClicks = new(); internal Vector2 MouseMovement; internal Vector2 LastMousePosition; internal override bool UpdateMouse( RootPanel root, InputData data ) { MouseMovement += LastMousePosition - data.MousePos; LastMousePosition = data.MousePos; var leftMousePressed = !MouseStates[0].Pressed && data.Mouse0; if ( leftMousePressed ) { // Are we a double clicker ( 250ms matches engine ) if ( LastClickTimeSince < 0.25f && LastClickRoot == root ) { // let's be a lot more forgiving with the delta then on normal panels // people can have shaky vr hands var AltClickDelta = LastClickPos - data.MousePos; if ( AltClickDelta.Length < MaxAltClickDelta / root.Scale ) { DoubleClicks.Enqueue( "mouseleft" ); } } LastClickRoot = root; LastClickPos = data.MousePos; LastClickTimeSince = 0; } return base.UpdateMouse( root, data ); } internal void SimulateEvents() { var listSize = DoubleClicks.Count; for ( int i = 0; i < listSize; i++ ) if ( DoubleClicks.TryDequeue( out var e ) ) { Hovered?.CreateEvent( new MousePanelEvent( "ondoubleclick", Hovered, e ) ); } if ( MouseMovement != 0 ) { // If we're pressing down on a panel we send all the mouse move events to that var moveRecv = Hovered; if ( Active != null ) moveRecv = Active; moveRecv?.CreateEvent( new MousePanelEvent( "onmousemove", moveRecv, "none" ) ); MouseMovement = 0; } } }