#if FIVEM
using CitizenFX.Core;
using CitizenFX.Core.Native;
using CitizenFX.Core.UI;
#elif RAGEMP
using RAGE.Game;
#elif RPH
using Rage;
using Rage.Native;
using Control = Rage.GameControl;
#elif SHVDN3
using GTA;
using GTA.Native;
using GTA.UI;
#endif
using LemonUI.Elements;
using LemonUI.Extensions;
using System;
using System.Drawing;
namespace LemonUI.Menus
{
///
/// Represents a grid where you can select X and Y values.
///
public class NativeGridPanel : NativePanel
{
#region Fields
private PointF position = PointF.Empty;
private float width = 0;
private readonly ScaledText labelTop = new ScaledText(PointF.Empty, "Y+", 0.33f)
{
Alignment = Alignment.Center
};
private readonly ScaledText labelBottom = new ScaledText(PointF.Empty, "Y-", 0.33f)
{
Alignment = Alignment.Center
};
private readonly ScaledText labelLeft = new ScaledText(PointF.Empty, "X-", 0.33f)
{
Alignment = Alignment.Right
};
private readonly ScaledText labelRight = new ScaledText(PointF.Empty, "X+", 0.33f);
private readonly ScaledTexture grid = new ScaledTexture("pause_menu_pages_char_mom_dad", "nose_grid")
{
Color = Color.FromArgb(205, 105, 105, 102)
};
private readonly ScaledTexture dot = new ScaledTexture("commonmenu", "common_medal")
{
Color = Color.FromArgb(255, 255, 255, 255)
};
private PointF innerPosition = PointF.Empty;
private SizeF innerSize = SizeF.Empty;
private GridStyle style = GridStyle.Full;
private float x;
private float y;
#endregion
#region Properties
///
public override bool Clickable => true;
///
/// The X value between 0 and 1.
///
public float X
{
get
{
switch (style)
{
case GridStyle.Full:
case GridStyle.Row:
return x;
default:
return 0.5f;
}
}
set
{
if (value > 1 || value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
if (style == GridStyle.Column)
{
return;
}
PointF before = new PointF(X, Y);
x = value;
UpdateDot(before);
}
}
///
/// The X value between 0 and 1.
///
public float Y
{
get
{
switch (style)
{
case GridStyle.Full:
case GridStyle.Column:
return y;
default:
return 0.5f;
}
}
set
{
if (value > 1 || value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
if (style == GridStyle.Row)
{
return;
}
PointF before = new PointF(X, Y);
y = value;
UpdateDot(before);
}
}
///
/// The text label shown on the top.
///
public string LabelTop
{
get => labelTop.Text;
set => labelTop.Text = value;
}
///
/// The text label shown on the bottom.
///
public string LabelBottom
{
get => labelBottom.Text;
set => labelBottom.Text = value;
}
///
/// The text label shown on the left.
///
public string LabelLeft
{
get => labelLeft.Text;
set => labelLeft.Text = value;
}
///
/// The text label shown on the right.
///
public string LabelRight
{
get => labelRight.Text;
set => labelRight.Text = value;
}
///
/// The style of this grid.
///
public GridStyle Style
{
get => style;
set
{
if (!Enum.IsDefined(typeof(GridStyle), value))
{
throw new ArgumentOutOfRangeException(nameof(value), "The Grid style is not valid! Expected Full, Row or Column.");
}
style = value;
Recalculate();
}
}
#endregion
#region Events
///
/// Event triggered when X and/or Y values are changed.
///
public event GridValueChangedEventHandler ValuesChanged;
#endregion
#region Constructor
///
/// Creates a new .
///
public NativeGridPanel() : base()
{
}
#endregion
#region Functions
private void Recalculate() => Recalculate(position, width);
private void UpdateDot(PointF before, bool trigger = true)
{
float posX = innerSize.Width * (style == GridStyle.Full || style == GridStyle.Row ? x : 0.5f);
float posY = innerSize.Height * (style == GridStyle.Full || style == GridStyle.Column ? y : 0.5f);
dot.Size = new SizeF(45, 45);
dot.Position = new PointF(innerPosition.X + posX - (dot.Size.Width * 0.5f),
innerPosition.Y + posY - (dot.Size.Height * 0.5f));
if (trigger)
{
ValuesChanged?.Invoke(this, new GridValueChangedArgs(before, new PointF(X, Y)));
}
}
///
public override void Recalculate(PointF position, float width)
{
this.position = position;
this.width = width;
const float height = 270;
const int offsetX = 20;
const int offsetY = 20;
base.Recalculate(position, width);
Background.Size = new SizeF(width, height);
switch (style)
{
case GridStyle.Full:
grid.Position = new PointF(position.X + (width * 0.5f) - 95, position.Y + (height * 0.5f) - 94);
grid.Size = new SizeF(192, 192);
break;
case GridStyle.Row:
grid.Position = new PointF(position.X + (width * 0.5f) - 95, position.Y + (height * 0.5f) - 15);
grid.Size = new SizeF(192, 36);
break;
case GridStyle.Column:
grid.Position = new PointF(position.X + (width * 0.5f) - 17, position.Y + (height * 0.5f) - 94);
grid.Size = new SizeF(36, 192);
break;
}
labelTop.Position = new PointF(position.X + (width * 0.5f), position.Y + 10);
labelBottom.Position = new PointF(position.X + (width * 0.5f), position.Y + height - 34);
labelLeft.Position = new PointF(position.X + (width * 0.5f) - 102, position.Y + (height * 0.5f) - (labelLeft.LineHeight * 0.5f));
labelRight.Position = new PointF(position.X + (width * 0.5f) + 102, position.Y + (height * 0.5f) - (labelLeft.LineHeight * 0.5f));
innerPosition = new PointF(grid.Position.X + offsetX, grid.Position.Y + offsetY);
innerSize = new SizeF(grid.Size.Width - (offsetX * 2), grid.Size.Height - (offsetY * 2));
UpdateDot(PointF.Empty, false);
}
///
public override void Process()
{
float previousX = X;
float previousY = Y;
Background.Draw();
switch (style)
{
case GridStyle.Full:
labelTop.Draw();
labelBottom.Draw();
labelLeft.Draw();
labelRight.Draw();
grid.Draw();
break;
case GridStyle.Row:
labelLeft.Draw();
labelRight.Draw();
grid.DrawSpecific(new PointF(0, 0.4f), new PointF(1, 0.6f));
break;
case GridStyle.Column:
labelTop.Draw();
labelBottom.Draw();
grid.DrawSpecific(new PointF(0.4f, 0), new PointF(0.6f, 1));
break;
}
dot.Draw();
#if FIVEM
bool usingKeyboard = API.IsInputDisabled(2);
#elif RAGEMP
bool usingKeyboard = Invoker.Invoke(0xA571D46727E2B718, 2);
#elif RPH
bool usingKeyboard = NativeFunction.CallByHash(0xA571D46727E2B718, 2);
#elif SHVDN3
bool usingKeyboard = Function.Call(Hash._IS_USING_KEYBOARD, 2);
#endif
if (usingKeyboard)
{
if (Screen.IsCursorInArea(grid.Position, grid.Size) && Controls.IsPressed(Control.CursorAccept))
{
PointF cursor = Screen.CursorPositionRelative;
PointF pos = innerPosition.ToRelative();
PointF start = new PointF(cursor.X - pos.X, cursor.Y - pos.Y);
SizeF size = innerSize.ToRelative();
x = start.X / size.Width;
y = start.Y / size.Height;
}
else
{
return;
}
}
else
{
Controls.DisableThisFrame(Control.LookUpDown);
Controls.DisableThisFrame(Control.LookLeftRight);
Controls.EnableThisFrame(Control.ScriptRightAxisX);
Controls.EnableThisFrame(Control.ScriptRightAxisY);
#if FIVEM
float rX = Game.GetControlNormal(0, Control.ScriptRightAxisX);
float rY = Game.GetControlNormal(0, Control.ScriptRightAxisY);
float frameTime = Game.LastFrameTime;
#elif RAGEMP
float rX = Invoker.Invoke(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisX);
float rY = Invoker.Invoke(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisY);
float frameTime = Invoker.Invoke(Natives.GetFrameTime);
#elif RPH
float rX = NativeFunction.CallByHash(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisX);
float rY = NativeFunction.CallByHash(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisY);
float frameTime = Game.FrameTime;
#elif SHVDN3
float rX = Game.GetControlValueNormalized(Control.ScriptRightAxisX);
float rY = Game.GetControlValueNormalized(Control.ScriptRightAxisY);
float frameTime = Game.LastFrameTime;
#endif
x += rX * frameTime;
y += rY * frameTime;
}
// Make sure that the values are not under zero or over one
if (x < 0)
{
x = 0;
}
else if (x > 1)
{
x = 1;
}
if (y < 0)
{
y = 0;
}
else if (y > 1)
{
y = 1;
}
if (previousX != x || previousY != y)
{
UpdateDot(new PointF(previousX, previousX));
}
}
#endregion
}
}