StrategyFieldInput.cs

A UI panel that captures mouse input over a strategy-view field. Handles left click press/drag/release as normalized panel coordinates, right or middle drag for camera panning, and mouse wheel for zoom, forwarding all of it to MatchController and FootballWorldPanel.

using Sandbox;
using Sandbox.UI;
using Socer;
using System;

public sealed class StrategyFieldInput : Panel
{
    public MatchController Game {get;set;}
    public FootballWorldPanel World {get;set;}
    public Action<Vector2> Press {get;set;}
    public Action<Vector2> Drag {get;set;}
    public Action<Vector2> Release {get;set;}
    bool leftHeld,cameraHeld;
    Vector2 panLast;
    Vector2 Position()=>new(MousePosition.x/Math.Max(1,Box.Rect.Width),MousePosition.y/Math.Max(1,Box.Rect.Height));
    protected override void OnMouseDown(MousePanelEvent e)
    {
        base.OnMouseDown(e);
        if(HasMouseCapture)SetMouseCapture(false);
        if(e.MouseButton==MouseButtons.Right || e.MouseButton==MouseButtons.Middle){cameraHeld=true;panLast=MousePosition;return;}
        if(e.MouseButton==MouseButtons.Left){leftHeld=true;Press?.Invoke(Position());}
    }
    protected override void OnMouseUp(MousePanelEvent e)
    {
        base.OnMouseUp(e);
        if(e.MouseButton==MouseButtons.Left){if(leftHeld)Release?.Invoke(Position());leftHeld=false;}
        if(e.MouseButton==MouseButtons.Right || e.MouseButton==MouseButtons.Middle){MovePan();cameraHeld=false;}
        if(!leftHeld && !cameraHeld)SetMouseCapture(false);
    }
    public override void OnMouseWheel(Vector2 value){Game?.ZoomStrategyCamera(-value.y);}
    public override void Tick()
    {
        base.Tick();
        if(Game?.StrategyLabOpen!=true){leftHeld=false;cameraHeld=false;if(HasMouseCapture)SetMouseCapture(false);return;}
        if(World==null)return;
        if(cameraHeld)MovePan();
        else if(leftHeld)Drag?.Invoke(Position());
    }
    void MovePan(){if(!cameraHeld)return;var pixel=MousePosition;PanPixels(pixel-panLast);panLast=pixel;}
    public void PanPixels(Vector2 delta)
    {
        if(Game==null || World==null)return;
        if(delta.Length<.01f)return;
        // Invert the actual broadcast projection, preserving its current angle.
        var center=World.Project(Game.Broadcast.Center);
        var moved=center+new Vector2(delta.x/Math.Max(1,Box.Rect.Width),delta.y/Math.Max(1,Box.Rect.Height));
        Game.PanStrategyCamera(World.CameraPanDelta(center,moved));
    }
}