847 changes: 847 additions & 0 deletions MenuExample/MenuExample.cs

Large diffs are not rendered by default.

25 changes: 16 additions & 9 deletions MenuExample.csproj → MenuExample/MenuExample.csproj
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
Expand All @@ -8,13 +8,13 @@
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MenuExample</RootNamespace>
<AssemblyName>MenuExample</AssemblyName>
<AssemblyName>MenuExample.net</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DebugType>embedded</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
Expand All @@ -30,14 +30,12 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="NativeUI">
<HintPath>..\..\NativeUI.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet">
<HintPath>..\..\ScriptHookVDotNet.dll</HintPath>
<Reference Include="CitizenFX.Core.Client, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\CitizenFX.Core.Client.1.0.4207\lib\net45\CitizenFX.Core.Client.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand All @@ -49,6 +47,15 @@
<ItemGroup>
<Compile Include="MenuExample.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NativeUI\NativeUI.csproj">
<Project>{f3e16ed9-dbf7-4e7b-b04b-9b24b11891d3}</Project>
<Name>NativeUI</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand All @@ -57,4 +64,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
4 changes: 4 additions & 0 deletions MenuExample/packages.config
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CitizenFX.Core.Client" version="1.0.4207" targetFramework="net452" />
</packages>
222 changes: 0 additions & 222 deletions MenuPool.cs

This file was deleted.

4 changes: 2 additions & 2 deletions NativeUI.sln
Expand Up @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeUI", "NativeUI.csproj", "{F3E16ED9-DBF7-4E7B-B04B-9B24B11891D3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeUI", "NativeUI\NativeUI.csproj", "{F3E16ED9-DBF7-4E7B-B04B-9B24B11891D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MenuExample", "MenuExample.csproj", "{79D9A09F-1999-4148-9BA0-20AEBA9B3C8B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MenuExample", "MenuExample\MenuExample.csproj", "{79D9A09F-1999-4148-9BA0-20AEBA9B3C8B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
358 changes: 358 additions & 0 deletions NativeUI/Colors.cs

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions NativeUI/Controls.cs
@@ -0,0 +1,87 @@
using CitizenFX.Core;
using static CitizenFX.Core.Native.API;
using System.Linq;

namespace NativeUI
{
/// <summary>
/// Class that provides tools to handle the game controls.
/// </summary>
public static class Controls
{
/// <summary>
/// All of the controls required for using a keyboard.
/// </summary>
private static readonly Control[] NecessaryControlsKeyboard = new Control[]
{
Control.FrontendAccept,
Control.FrontendAxisX,
Control.FrontendAxisY,
Control.FrontendDown,
Control.FrontendUp,
Control.FrontendLeft,
Control.FrontendRight,
Control.FrontendCancel,
Control.FrontendSelect,
Control.CursorScrollDown,
Control.CursorScrollUp,
Control.CursorX,
Control.CursorY,
Control.MoveUpDown,
Control.MoveLeftRight,
Control.Sprint,
Control.Jump,
Control.Enter,
Control.VehicleExit,
Control.VehicleAccelerate,
Control.VehicleBrake,
Control.VehicleMoveLeftRight,
Control.VehicleFlyYawLeft,
Control.FlyLeftRight,
Control.FlyUpDown,
Control.VehicleFlyYawRight,
Control.VehicleHandbrake,
};
/// <summary>
/// All of the controls required for using a keyboard.
/// </summary>
private static readonly Control[] NecessaryControlsGamePad = NecessaryControlsKeyboard.Concat(new Control[]
{
Control.LookUpDown,
Control.LookLeftRight,
Control.Aim,
Control.Attack,
})
.ToArray();

/// <summary>
/// Toggles the availability of the controls.
/// It does not disable the basic movement and frontend controls.
/// </summary>
/// <param name="toggle">If we want to enable or disable the controls.</param>
public static void Toggle(bool toggle)
{
// If we want to enable the controls
if (toggle)
{
// Enable all of them
Game.EnableAllControlsThisFrame(0);
Game.EnableAllControlsThisFrame(1);
Game.EnableAllControlsThisFrame(2);
}
// If we don't need them
else
{
// Disable all of the controls
Game.DisableAllControlsThisFrame(2);

// Now, re-enable the controls that are required for the game
// First, pick the right controls for gamepad or keyboard and mouse
Control[] list = Game.CurrentInputMode == InputMode.GamePad ? NecessaryControlsGamePad : NecessaryControlsKeyboard;
// Then, enable all of the controls for that input mode
foreach (Control control in list)
EnableControlAction(0, (int)control, true);
}
}
}
}
90 changes: 56 additions & 34 deletions Sprite.cs → NativeUI/Elements/Sprite.cs
@@ -1,16 +1,18 @@
using System;
using System.Drawing;

using System.IO;
using System.Reflection;
using GTA;
using GTA.Native;
using CitizenFX.Core.Native;
using CitizenFX.Core;
using CitizenFX.Core.UI;
using System.Drawing;

namespace NativeUI
{
public class Sprite
{
public Point Position;
public Size Size;
public PointF Position;
public SizeF Size;
public Color Color;
public bool Visible;
public float Heading;
Expand All @@ -22,7 +24,7 @@ public string TextureDict
{
_textureDict = value;
//if(_autoload && !Function.Call<bool>(Hash.HAS_STREAMED_TEXTURE_DICT_LOADED, value))
//Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, value, true);
//Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, value, true);
}
}

Expand All @@ -38,10 +40,10 @@ public string TextureDict
/// <param name="size"></param>
/// <param name="heading"></param>
/// <param name="color"></param>
public Sprite(string textureDict, string textureName, Point position, Size size, float heading, Color color) //BASE
public Sprite(string textureDict, string textureName, PointF position, SizeF size, float heading, Color color) //BASE
{
//if (!Function.Call<bool>(Hash.HAS_STREAMED_TEXTURE_DICT_LOADED, textureDict))
//Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, textureDict, true);
//Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, textureDict, true);
TextureDict = textureDict;
TextureName = textureName;

Expand All @@ -59,7 +61,7 @@ public string TextureDict
/// <param name="textureName"></param>
/// <param name="position"></param>
/// <param name="size"></param>
public Sprite(string textureDict, string textureName, Point position, Size size) : this(textureDict, textureName, position, size, 0f, Color.FromArgb(255, 255, 255, 255))
public Sprite(string textureDict, string textureName, PointF position, SizeF size) : this(textureDict, textureName, position, size, 0f, Color.FromArgb(255, 255, 255, 255))
{
}

Expand All @@ -70,52 +72,72 @@ public Sprite(string textureDict, string textureName, Point position, Size size)
public void Draw()
{
if (!Visible) return;
if (!Function.Call<bool>(Hash.HAS_STREAMED_TEXTURE_DICT_LOADED, TextureDict))
Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, TextureDict, true);
if (!API.HasStreamedTextureDictLoaded(TextureDict))
API.RequestStreamedTextureDict(TextureDict, true);

int screenw = Game.ScreenResolution.Width;
int screenh = Game.ScreenResolution.Height;
int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw/screenh;
var width = height*ratio;
float ratio = (float)screenw / screenh;
var width = height * ratio;


float w = (Size.Width / width);
float h = (Size.Height / height);
float x = (Position.X / width) + w * 0.5f;
float y = (Position.Y / height) + h * 0.5f;
Function.Call(Hash.DRAW_SPRITE, TextureDict, TextureName, x, y, w, h, Heading, Color.R, Color.G, Color.B, Color.A);

API.DrawSprite(TextureDict, TextureName, x, y, w, h, Heading, Color.R, Color.G, Color.B, Color.A);
}

public static void Draw(string dict, string name, int xpos, int ypos, int boxWidth, int boxHeight, float rotation, Color color)
{
if (!API.HasStreamedTextureDictLoaded(dict))
API.RequestStreamedTextureDict(dict, true);

int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw / screenh;
var width = height * ratio;


float w = (boxWidth / width);
float h = (boxHeight / height);
float x = (xpos / width) + w * 0.5f;
float y = (ypos / height) + h * 0.5f;

API.DrawSprite(dict, name, x, y, w, h, rotation, color.R, color.G, color.B, color.A);
}

/*
/// <summary>
/// Draw a custom texture from a file on a 1080-pixels height base.
/// </summary>
/// <param name="path">Path to texture file.</param>
/// <param name="position"></param>
/// <param name="size"></param>
public static void DrawTexture(string path, Point position, Size size, float rotation, Color color)
public static void DrawTexture(string path, PointF position, SizeF size, float rotation, Color color)
{
int screenw = Game.ScreenResolution.Width;
int screenh = Game.ScreenResolution.Height;
int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw / screenh;
float width = height * ratio;
float reduceX = UI.WIDTH / width;
float reduceY = UI.HEIGHT / height;
float reduceX = Screen.Width / width;
float reduceY = Screen.Height / height;
Point extra = new Point(0,0);
PointF extra = new PointF(0,0);
if (screenw == 1914 && screenh == 1052) //TODO: Fix this when ScriptHookVDotNet 1.2 comes out.
extra = new Point(15, 0);
extra = new PointF(15, 0);
UI.DrawTexture(path, 1, 1, 60,
new Point(Convert.ToInt32(position.X*reduceX) + extra.X, Convert.ToInt32(position.Y*reduceY) + extra.Y),
DrawTexture(path, 1, 1, 60,
new PointF(Convert.ToInt32(position.X*reduceX) + extra.X, Convert.ToInt32(position.Y*reduceY) + extra.Y),
new PointF(0f, 0f),
new Size(Convert.ToInt32(size.Width * reduceX), Convert.ToInt32(size.Height * reduceY)),
new SizeF(Convert.ToInt32(size.Width * reduceX), Convert.ToInt32(size.Height * reduceY)),
rotation, color);
}
Expand All @@ -125,7 +147,7 @@ public static void DrawTexture(string path, Point position, Size size, float rot
/// <param name="path">Path to texture file.</param>
/// <param name="position"></param>
/// <param name="size"></param>
public static void DrawTexture(string path, Point position, Size size)
public static void DrawTexture(string path, PointF position, SizeF size)
{
int screenw = Game.ScreenResolution.Width;
int screenh = Game.ScreenResolution.Height;
Expand All @@ -138,17 +160,17 @@ public static void DrawTexture(string path, Point position, Size size)
float reduceY = UI.HEIGHT / height;
Point extra = new Point(0, 0);
PointF extra = new PointF(0, 0);
if (screenw == 1914 && screenh == 1052) //TODO: Fix this when ScriptHookVDotNet 1.2 comes out.
extra = new Point(15, 0);
extra = new PointF(15, 0);
UI.DrawTexture(path, 1, 1, 60,
new Point(Convert.ToInt32(position.X * reduceX) + extra.X, Convert.ToInt32(position.Y * reduceY) + extra.Y),
new PointF(0f, 0f),
new Size(Convert.ToInt32(size.Width * reduceX), Convert.ToInt32(size.Height * reduceY)),
new PointF(Convert.ToInt32(position.X * reduceX) + extra.X, Convert.ToInt32(position.Y * reduceY) + extra.Y),
new PointFF(0f, 0f),
new SizeF(Convert.ToInt32(size.Width * reduceX), Convert.ToInt32(size.Height * reduceY)),
0f, Color.White);
}

*/

/// <summary>
/// Save an embedded resource to a temporary file.
Expand Down
54 changes: 54 additions & 0 deletions NativeUI/Elements/UIResRectangle.cs
@@ -0,0 +1,54 @@
using System.Drawing;
using CitizenFX.Core.Native;
using CitizenFX.Core.UI;

namespace NativeUI
{
/// <summary>
/// A rectangle in 1080 pixels height system.
/// </summary>
public class UIResRectangle : CitizenFX.Core.UI.Rectangle
{
public UIResRectangle()
{ }

public UIResRectangle(PointF pos, SizeF size) : base(pos, size)
{ }

public UIResRectangle(PointF pos, SizeF size, Color color) : base(pos, size, color)
{ }

public override void Draw(SizeF offset)
{
if (!Enabled) return;
int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw / screenh;
var width = height * ratio;

float w = Size.Width / width;
float h = Size.Height / height;
float x = ((Position.X + offset.Width) / width) + w * 0.5f;
float y = ((Position.Y + offset.Height) / height) + h * 0.5f;

API.DrawRect(x, y, w, h, Color.R, Color.G, Color.B, Color.A);
}

public static void Draw(float xPos, float yPos, int boxWidth, int boxHeight, Color color)
{
int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw / screenh;
var width = height * ratio;

float w = boxWidth / width;
float h = boxHeight / height;
float x = ((xPos) / width) + w * 0.5f;
float y = ((yPos) / height) + h * 0.5f;

API.DrawRect(x, y, w, h, color.R, color.G, color.B, color.A);
}
}
}
204 changes: 204 additions & 0 deletions NativeUI/Elements/UIResText.cs
@@ -0,0 +1,204 @@
using System;
using System.Drawing;
using CitizenFX.Core.Native;
using CitizenFX.Core.UI;
using Font = CitizenFX.Core.UI.Font;

namespace NativeUI
{
/// <summary>
/// A Text object in the 1080 pixels height base system.
/// </summary>
public class UIResText : Text
{
public UIResText(string caption, PointF position, float scale) : base(caption, position, scale)
{
TextAlignment = Alignment.Left;
}

public UIResText(string caption, PointF position, float scale, Color color)
: base(caption, position, scale, color)
{
TextAlignment = Alignment.Left;
}

public UIResText(string caption, PointF position, float scale, Color color, Font font, Alignment justify)
: base(caption, position, scale, color, font, CitizenFX.Core.UI.Alignment.Left)
{
TextAlignment = justify;
}


public Alignment TextAlignment { get; set; }

/// <summary>
/// Push a long string into the stack.
/// </summary>
/// <param name="str"></param>
public static void AddLongString(string str)
{
var utf8ByteCount = System.Text.Encoding.UTF8.GetByteCount(str);

if (utf8ByteCount == str.Length)
{
AddLongStringForAscii(str);
}
else
{
AddLongStringForUtf8(str);
}
}

private static void AddLongStringForAscii(string input)
{
const int maxByteLengthPerString = 99;

for (int i = 0; i < input.Length; i += maxByteLengthPerString)
{
string substr = (input.Substring(i, Math.Min(maxByteLengthPerString, input.Length - i)));
API.AddTextComponentString(substr);
}
}

internal static void AddLongStringForUtf8(string input)
{
const int maxByteLengthPerString = 99;

if (maxByteLengthPerString < 0)
{
throw new ArgumentOutOfRangeException("maxLengthPerString");
}
if (string.IsNullOrEmpty(input) || maxByteLengthPerString == 0)
{
return;
}

var enc = System.Text.Encoding.UTF8;

var utf8ByteCount = enc.GetByteCount(input);
if (utf8ByteCount < maxByteLengthPerString)
{
API.AddTextComponentString(input);
return;
}

var startIndex = 0;

for (int i = 0; i < input.Length; i++)
{
var length = i - startIndex;
if (enc.GetByteCount(input.Substring(startIndex, length)) > maxByteLengthPerString)
{
string substr = (input.Substring(startIndex, length - 1));
API.AddTextComponentString(substr);

i -= 1;
startIndex = (startIndex + length - 1);
}
}
API.AddTextComponentString(input.Substring(startIndex, input.Length - startIndex));
}

[Obsolete("Use ScreenTools.GetTextWidth instead.", true)]
public static float MeasureStringWidth(string str, Font font, float scale) => ScreenTools.GetTextWidth(str, font, scale);

[Obsolete("Use ScreenTools.GetTextWidth instead.", true)]
public static float MeasureStringWidthNoConvert(string str, Font font, float scale) => ScreenTools.GetTextWidth(str, font, scale);

/// <summary>
/// Width of the text wrap box. Set to zero to disable.
/// </summary>
public float Wrap { get; set; } = 0;
/// <summary>
/// Size of the text wrap box.
/// </summary>
[Obsolete("Use UIResText.Wrap instead.", true)]
public SizeF WordWrap
{
get => new SizeF(Wrap, 0);
set => Wrap = value.Width;
}

public override void Draw(SizeF offset)
{
int screenw = Screen.Resolution.Width;
int screenh = Screen.Resolution.Height;
const float height = 1080f;
float ratio = (float)screenw / screenh;
var width = height * ratio;

float x = (Position.X) / width;
float y = (Position.Y) / height;

API.SetTextFont((int)Font);
API.SetTextScale(1.0f, Scale);
API.SetTextColour(Color.R, Color.G, Color.B, Color.A);
if (Shadow)
API.SetTextDropShadow();
if (Outline)
API.SetTextOutline();
switch (TextAlignment)
{
case Alignment.Center:
API.SetTextCentre(true);
break;
case Alignment.Right:
API.SetTextRightJustify(true);
API.SetTextWrap(0, x);
break;
}

if (Wrap != 0)
{
float xsize = (Position.X + Wrap) / width;
API.SetTextWrap(x, xsize);
}

API.SetTextEntry("jamyfafi");
AddLongString(Caption);

API.DrawText(x, y);
}

//public static void Draw(string caption, int xPos, int yPos, Font font, float scale, UnknownColors color, Alignment alignment, bool Shadow, bool outline, int wordWrap)
//{
// int screenw = Screen.Resolution.Width;
// int screenh = Screen.Resolution.Height;
// const float height = 1080f;
// float ratio = (float)screenw / screenh;
// var width = height * ratio;

// float x = (xPos) / width;
// float y = (yPos) / height;

// Function.Call(Hash.SET_TEXT_FONT, (int)font);
// Function.Call(Hash.SET_TEXT_SCALE, 1.0f, scale);
// Function.Call(Hash.SET_TEXT_COLOUR, color.R, color.G, color.B, color.A);
// if (Shadow)
// Function.Call(Hash.SET_TEXT_DROP_SHADOW);
// if (outline)
// Function.Call(Hash.SET_TEXT_OUTLINE);
// switch (alignment)
// {
// case Alignment.Center:
// Function.Call(Hash.SET_TEXT_CENTRE, true);
// break;
// case Alignment.Right:
// Function.Call(Hash.SET_TEXT_RIGHT_JUSTIFY, true);
// Function.Call(Hash.SET_TEXT_WRAP, 0, x);
// break;
// }

// if (wordWrap != 0)
// {
// float xsize = (xPos + wordWrap) / width;
// Function.Call(Hash.SET_TEXT_WRAP, x, xsize);
// }

// Function.Call(Hash._SET_TEXT_ENTRY, "jamyfafi");
// AddLongString(caption);

// Function.Call(Hash._DRAW_TEXT, x, y);
//}
}
}
112 changes: 112 additions & 0 deletions NativeUI/Hud/Markers/Marker.cs
@@ -0,0 +1,112 @@
using CitizenFX.Core;
using System;
using System.Drawing;
using System.Threading.Tasks;
using static CitizenFX.Core.Native.API;

namespace NativeUI
{
public class Marker
{
internal float _height = 0;
private float distance;

public MarkerType MarkerType { get; set; }
/// <summary>
/// Hardcoded to be not more than 250units away.
/// </summary>
public float Distance { get => distance; set => distance = value > 250f ? 250f : value; }
public Vector3 Position { get; set; }

// this is optional and default to 0
public Vector3 Direction { get; set; } = Vector3.Zero;
// this is optional and default to 0
public Vector3 Rotation { get; set; } = Vector3.Zero;

public Vector3 Scale { get; set; } = new Vector3(1.5f);
public Color Color { get; set; }
public bool BobUpDown { get; set; }
public bool Rotate { get; set; }
public bool FaceCamera { get; set; }
public bool IsInMarker { get; private set; }
public bool IsInRange { get => MenuPool.PlayerPed.IsInRangeOf(Position, Distance); }
public bool CheckZ { get; set; }
/// <summary>
/// It doesn't work on water and under the map!
/// </summary>
public bool PlaceOnGround { get; set; }

/// <summary>
/// Creates a Marker in a world position
/// </summary>
/// <param name="type">The type of marker</param>
/// <param name="position">Position in world coords of the marker</param>
/// <param name="distance">Drawing distance, if you're more distant than this value the marker won't be drawn </param>
/// <param name="color">Color of the marker</param>
/// <param name="bobUpDown">The marker will bounce up and down</param>
/// <param name="rotate">The marker will rotate on its Z axiz</param>
/// <param name="faceCamera">The marker will face camera</param>
public Marker(MarkerType type, Vector3 position, float distance, Color color, bool placeOnGround = false, bool bobUpDown = false, bool rotate = false, bool faceCamera = false)
{
MarkerType = type;
Position = position;
Distance = distance;
Color = color;
BobUpDown = bobUpDown;
Rotate = rotate;
FaceCamera = faceCamera;
PlaceOnGround = placeOnGround;
if (Rotate && FaceCamera)
Rotate = false;
}

/// <summary>
/// Creates a Marker in a world position
/// </summary>
/// <param name="type">The type of marker</param>
/// <param name="position">Position in world coords of the marker</param>
/// <param name="scale">Dimensions of the marker</param>
/// <param name="distance">Drawing distance, if you're more distant than this value the marker won't be drawn </param>
/// <param name="color">Color of the marker</param>
/// <param name="bobUpDown">The marker will bounce up and down</param>
/// <param name="rotate">The marker will rotate on its Z axiz</param>
/// <param name="faceCamera">The marker will face camera</param>
public Marker(MarkerType type, Vector3 position, Vector3 scale, float distance, Color color, bool placeOnGround = false, bool bobUpDown = false, bool rotate = false, bool faceCamera = false)
{
MarkerType = type;
Position = position;
Scale = scale;
Distance = distance;
Color = color;
BobUpDown = bobUpDown;
Rotate = rotate;
PlaceOnGround = placeOnGround;
FaceCamera = faceCamera;
if (Rotate && FaceCamera)
Rotate = false;
}

public void Draw()
{
// [Position.Z != _height] means that we make the check only if we change position
// but if we change position and the Z is still the same then we don't need to check again
// We draw it with _height + 0.1 to ensure marker drawing (like horizontal circles)
if (IsInRange && PlaceOnGround && (Position.Z != _height + 0.1f))
{
if (GetGroundZFor_3dCoord(Position.X, Position.Y, Position.Z, ref _height, false))
Position = new Vector3(Position.X, Position.Y, _height + 0.03f);
}
World.DrawMarker(MarkerType, Position, Direction, Rotation, Scale, Color, BobUpDown, FaceCamera, Rotate);
if (CheckZ)
{
float distanceSquared = Position.DistanceToSquared(MenuPool.PlayerPed.Position);
IsInMarker = (distanceSquared < Math.Pow(Scale.X / 2, 2) || distanceSquared < Math.Pow(Scale.Y / 2, 2)) || distanceSquared < Math.Pow(Scale.Z / 2, 2);
}
else
{
var distanceSquared = Position.DistanceToSquared2D(MenuPool.PlayerPed.Position);
IsInMarker = distanceSquared <= Math.Pow(Scale.X / 2, 2) || distanceSquared <= Math.Pow(Scale.Y / 2, 2);
}
}
}
}
52 changes: 52 additions & 0 deletions NativeUI/Hud/Markers/MarkersHandler.cs
@@ -0,0 +1,52 @@
using CitizenFX.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static CitizenFX.Core.Native.API;

namespace NativeUI
{
public class MarkersHandler : BaseScript
{

private static List<Marker> _markerList = new List<Marker>();

public MarkersHandler()
{
Tick += MainHandler;
}

public async Task MainHandler()
{
if (_markerList.Count == 0) return;

for(int i=0; i<FilterMarkers().Count; i++)
FilterMarkers()[i].Draw();
await Task.FromResult(0);
}

private List<Marker> FilterMarkers() => _markerList.Where(x => MenuPool.PlayerPed.IsInRangeOf(x.Position, x.Distance)).ToList();

/// <summary>
/// Adds a marker to the list
/// </summary>
/// <param name="marker"></param>
public static void AddMarker(Marker marker)
{
if (!_markerList.Contains(marker))
_markerList.Add(marker);
}

/// <summary>
/// Removes the marker from the list
/// </summary>
/// <param name="marker"></param>
public static void RemoveMarker(Marker marker)
{
if (_markerList.Contains(marker))
_markerList.Remove(marker);
}
}
}
104 changes: 104 additions & 0 deletions NativeUI/Hud/Notifications/NotificationChars.cs
@@ -0,0 +1,104 @@
namespace NativeUI
{
public static class NotificationChar
{
public static string Abigail = "CHAR_ABIGAIL";
public static string Amanda = "CHAR_AMANDA";
public static string Ammunation = "CHAR_AMMUNATION";
public static string Andreas = "CHAR_ANDREAS";
public static string Antonia = "CHAR_ANTONIA";
public static string Ashley = "CHAR_ASHLEY";
public static string BankOfLiberty = "CHAR_BANK_BOL";
public static string BankFleeca = "CHAR_BANK_FLEECA";
public static string BankMaze = "CHAR_BANK_MAZE";
public static string Barry = "CHAR_BARRY";
public static string Beverly = "CHAR_BEVERLY";
public static string BikeSite = "CHAR_BIKESITE";
public static string BlankEntry = "CHAR_BLANK_ENTRY";
public static string Blimp = "CHAR_BLIMP";
public static string Blocked = "CHAR_BLOCKED";
public static string BoatSite = "CHAR_BOATSITE";
public static string BrokenDownGirl = "CHAR_BROKEN_DOWN_GIRL";
public static string BugStars = "CHAR_BUGSTARS";
public static string Call911 = "CHAR_CALL911";
public static string LegendaryMotorsport = "CHAR_CARSITE";
public static string SSASuperAutos = "CHAR_CARSITE2";
public static string Castro = "CHAR_CASTRO";
public static string ChatCall = "CHAR_CHAT_CALL";
public static string Chef = "CHAR_CHEF";
public static string Cheng = "CHAR_CHENG";
public static string ChengSenior = "CHAR_CHENGSR";
public static string Chop = "CHAR_CHOP";
public static string Cris = "CHAR_CRIS";
public static string Dave = "CHAR_DAVE";
public static string Default = "CHAR_DEFAULT";
public static string Denise = "CHAR_DENISE";
public static string DetonateBomb = "CHAR_DETONATEBOMB";
public static string DetonatePhone = "CHAR_DETONATEPHONE";
public static string Devin = "CHAR_DEVIN";
public static string SubMarine = "CHAR_DIAL_A_SUB";
public static string Dom = "CHAR_DOM";
public static string DomesticGirl = "CHAR_DOMESTIC_GIRL";
public static string Dreyfuss = "CHAR_DREYFUSS";
public static string DrFriedlander = "CHAR_DR_FRIEDLANDER";
public static string Epsilon = "CHAR_EPSILON";
public static string EstateAgent = "CHAR_ESTATE_AGENT";
public static string Facebook = "CHAR_FACEBOOK";
public static string FilmNoire = "CHAR_FILMNOIR";
public static string Floyd = "CHAR_FLOYD";
public static string Franklin = "CHAR_FRANKLIN";
public static string FranklinTrevor = "CHAR_FRANK_TREV_CONF";
public static string GayMilitary = "CHAR_GAYMILITARY";
public static string Hao = "CHAR_HAO";
public static string HitcherGirl = "CHAR_HITCHER_GIRL";
public static string Hunter = "CHAR_HUNTER";
public static string Jimmy = "CHAR_JIMMY";
public static string JimmyBoston = "CHAR_JIMMY_BOSTON";
public static string Joe = "CHAR_JOE";
public static string Josef = "CHAR_JOSEF";
public static string Josh = "CHAR_JOSH";
public static string LamarDog = "CHAR_LAMAR";
public static string Lester = "CHAR_LESTER";
public static string Skull = "CHAR_LESTER_DEATHWISH";
public static string LesterFranklin = "CHAR_LEST_FRANK_CONF";
public static string LesterMichael = "CHAR_LEST_MIKE_CONF";
public static string LifeInvader = "CHAR_LIFEINVADER";
public static string LsCustoms = "CHAR_LS_CUSTOMS";
public static string LSTI = "CHAR_LS_TOURIST_BOARD";
public static string Manuel = "CHAR_MANUEL";
public static string Marnie = "CHAR_MARNIE";
public static string Martin = "CHAR_MARTIN";
public static string MaryAnn = "CHAR_MARY_ANN";
public static string Maude = "CHAR_MAUDE";
public static string Mechanic = "CHAR_MECHANIC";
public static string Michael = "CHAR_MICHAEL";
public static string MichaelFranklin = "CHAR_MIKE_FRANK_CONF";
public static string MichaelTrevor = "CHAR_MIKE_TREV_CONF";
public static string WarStock = "CHAR_MILSITE";
public static string Minotaur = "CHAR_MINOTAUR";
public static string Molly = "CHAR_MOLLY";
public static string MorsMutual = "CHAR_MP_MORS_MUTUAL";
public static string ArmyContact = "CHAR_MP_ARMY_CONTACT";
public static string Brucie = "CHAR_MP_BRUCIE";
public static string FibContact = "CHAR_MP_FIB_CONTACT";
public static string RockStarLogo = "CHAR_MP_FM_CONTACT";
public static string Gerald = "CHAR_MP_GERALD";
public static string Julio = "CHAR_MP_JULIO";
public static string MechanicChinese = "CHAR_MP_MECHANIC";
public static string MerryWeather = "CHAR_MP_MERRYWEATHER";
public static string Unicorn = "CHAR_MP_STRIPCLUB_PR";
public static string Mom = "CHAR_MRS_THORNHILL";
public static string MrsThornhill = "CHAR_MRS_THORNHILL";
public static string PatriciaTrevor = "CHAR_PATRICIA";
public static string PegasusDelivery = "CHAR_PEGASUS_DELIVERY";
public static string ElitasTravel = "CHAR_PLANESITE";
public static string Sasquatch = "CHAR_SASQUATCH";
public static string Simeon = "CHAR_SIMEON";
public static string SocialClub = "CHAR_SOCIAL_CLUB";
public static string Solomon = "CHAR_SOLOMON";
public static string Taxi = "CHAR_TAXI";
public static string Trevor = "CHAR_TREVOR";
public static string YouTube = "CHAR_YOUTUBE";
public static string Wade = "CHAR_WADE";
}
}
451 changes: 451 additions & 0 deletions NativeUI/Hud/Notifications/Notifications.cs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions NativeUI/Items/IListItem.cs
@@ -0,0 +1,7 @@
namespace NativeUI
{
public interface IListItem
{
string CurrentItem();
}
}
114 changes: 114 additions & 0 deletions NativeUI/Items/UIMenuCheckboxItem.cs
@@ -0,0 +1,114 @@
using System;
using System.Drawing;
using System.Threading.Tasks;

namespace NativeUI
{
public enum UIMenuCheckboxStyle
{
Cross,
Tick
}

public class UIMenuCheckboxItem : UIMenuItem
{
protected internal Sprite _checkedSprite;

/// <summary>
/// Triggered when the checkbox state is changed.
/// </summary>
public event ItemCheckboxEvent CheckboxEvent;

public UIMenuCheckboxStyle Style { get; set; }

/// <summary>
/// Checkbox item with a toggleable checkbox.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="check">Boolean value whether the checkbox is checked.</param>
public UIMenuCheckboxItem(string text, bool check) : this(text, check, "")
{
}

/// <summary>
/// Checkbox item with a toggleable checkbox.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="check">Boolean value whether the checkbox is checked.</param>
/// <param name="description">Description for this item.</param>
public UIMenuCheckboxItem(string text, bool check, string description) : this(text, UIMenuCheckboxStyle.Tick, check, description, Color.Transparent, Color.FromArgb(255, 255, 255, 255))
{
}

/// <summary>
/// Checkbox item with a toggleable checkbox.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="style">CheckBox style (Tick or Cross).</param>
/// <param name="check">Boolean value whether the checkbox is checked.</param>
/// <param name="description">Description for this item.</param>
public UIMenuCheckboxItem(string text, UIMenuCheckboxStyle style, bool check, string description) : this(text, style, check, description, Color.Transparent, Color.FromArgb(255, 255, 255, 255))
{
}

/// <summary>
/// Checkbox item with a toggleable checkbox.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="style">CheckBox style (Tick or Cross).</param>
/// <param name="check">Boolean value whether the checkbox is checked.</param>
/// <param name="description">Description for this item.</param>
/// <param name="mainColor">Main item color.</param>
/// <param name="highlightColor">Highlight item color.</param>
public UIMenuCheckboxItem(string text, UIMenuCheckboxStyle style, bool check, string description, Color mainColor, Color highlightColor) : base(text, description, mainColor, highlightColor)
{
const int y = 0;
Style = style;
_checkedSprite = new Sprite("commonmenu", "shop_box_blank", new PointF(410, y + 95), new SizeF(50, 50));
Checked = check;
}


/// <summary>
/// Change or get whether the checkbox is checked.
/// </summary>
public bool Checked { get; set; }

/// <summary>
/// Change item's position.
/// </summary>
/// <param name="y">New Y value.</param>
public override void Position(int y)
{
base.Position(y);
_checkedSprite.Position = new PointF(380 + Offset.X + Parent.WidthOffset, y + 138 + Offset.Y);
}


/// <summary>
/// Draw item.
/// </summary>
public override async Task Draw()
{
base.Draw();
_checkedSprite.Position = new PointF(380 + Offset.X + Parent.WidthOffset, _checkedSprite.Position.Y);
_checkedSprite.TextureName = Selected ? (Checked ? (Style == UIMenuCheckboxStyle.Tick ? "shop_box_tickb" : "shop_box_crossb") : "shop_box_blankb") : Checked ? (Style == UIMenuCheckboxStyle.Tick ? "shop_box_tick" : "shop_box_cross") : "shop_box_blank";
_checkedSprite.Draw();
}

public void CheckboxEventTrigger()
{
CheckboxEvent?.Invoke(this, Checked);
}

public override void SetRightBadge(BadgeStyle badge)
{
throw new Exception("UIMenuCheckboxItem cannot have a right badge.");
}

public override void SetRightLabel(string text)
{
throw new Exception("UIMenuListItem cannot have a right label.");
}
}
}
48 changes: 26 additions & 22 deletions UIMenuColoredItem.cs → NativeUI/Items/UIMenuColoredItem.cs
@@ -1,22 +1,26 @@
using System.Drawing;
using CitizenFX.Core.UI;
using System;
using System.Drawing;
using System.Threading.Tasks;

namespace NativeUI
{
[Obsolete("UIMenuColoredItem is deprecated: use UIMenuItem(text, description, mainColor, hilightColor) instead", true)]
public class UIMenuColoredItem : UIMenuItem
{
public Color MainColor { get; set; }
public Color HighlightColor { get; set; }

public Color TextColor { get; set; }
public Color HighlightedTextColor { get; set; }
public UIMenuColoredItem(string label, Color color, Color highlightColor) : base(label)

public UIMenuColoredItem(string label, Color color, Color highlightColor) : base(label, "")
{
MainColor = color;
HighlightColor = highlightColor;

TextColor = Color.White;
HighlightedTextColor = Color.Black;
TextColor = Colors.White;
HighlightedTextColor = Colors.Black;

Init();
}
Expand All @@ -26,30 +30,30 @@ public UIMenuColoredItem(string label, string description, Color color, Color hi
MainColor = color;
HighlightColor = highlightColor;

TextColor = Color.White;
HighlightedTextColor = Color.Black;
TextColor = Colors.White;
HighlightedTextColor = Colors.Black;

Init();
}

protected void Init()
{
_selectedSprite = new Sprite("commonmenu", "gradient_nav", new Point(0, 0), new Size(431, 38), 0, HighlightColor);
_rectangle = new UIResRectangle(new Point(0, 0), new Size(431, 38), Color.FromArgb(150, 0, 0, 0));
_text = new UIResText(Text, new Point(8, 0), 0.33f, Color.WhiteSmoke, GTA.Font.ChaletLondon, UIResText.Alignment.Left);
_selectedSprite = new Sprite("commonmenu", "gradient_nav", new PointF(0, 0), new SizeF(431, 38), 0, HighlightColor);
_rectangle = new UIResRectangle(new PointF(0, 0), new SizeF(431, 38), Color.FromArgb(150, 0, 0, 0));
_text = new UIResText(Text, new PointF(8, 0), 0.33f, Colors.WhiteSmoke, CitizenFX.Core.UI.Font.ChaletLondon, Alignment.Left);
Description = Description;

_badgeLeft = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));
_badgeRight = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));
_badgeLeft = new Sprite("commonmenu", "", new PointF(0, 0), new SizeF(40, 40));
_badgeRight = new Sprite("commonmenu", "", new PointF(0, 0), new SizeF(40, 40));

_labelText = new UIResText("", new Point(0, 0), 0.35f) { TextAlignment = UIResText.Alignment.Right };
_labelText = new UIResText("", new PointF(0, 0), 0.35f) { TextAlignment = Alignment.Right };
}
public override void Draw()


public override async Task Draw()
{
_rectangle.Size = new Size(431 + Parent.WidthOffset, 38);
_selectedSprite.Size = new Size(431 + Parent.WidthOffset, 38);
_rectangle.Size = new SizeF(431 + Parent.WidthOffset, 38);
_selectedSprite.Size = new SizeF(431 + Parent.WidthOffset, 38);

if (Hovered && !Selected)
{
Expand All @@ -71,20 +75,20 @@ public override void Draw()

if (LeftBadge != BadgeStyle.None)
{
_text.Position = new Point(35 + Offset.X, _text.Position.Y);
_text.Position = new PointF(35 + Offset.X, _text.Position.Y);
_badgeLeft.TextureDict = BadgeToSpriteLib(LeftBadge);
_badgeLeft.TextureName = BadgeToSpriteName(LeftBadge, Selected);
_badgeLeft.Color = BadgeToColor(LeftBadge, Selected);
_badgeLeft.Draw();
}
else
{
_text.Position = new Point(8 + Offset.X, _text.Position.Y);
_text.Position = new PointF(8 + Offset.X, _text.Position.Y);
}

if (RightBadge != BadgeStyle.None)
{
_badgeRight.Position = new Point(385 + Offset.X + Parent.WidthOffset, _badgeRight.Position.Y);
_badgeRight.Position = new PointF(385 + Offset.X + Parent.WidthOffset, _badgeRight.Position.Y);
_badgeRight.TextureDict = BadgeToSpriteLib(RightBadge);
_badgeRight.TextureName = BadgeToSpriteName(RightBadge, Selected);
_badgeRight.Color = BadgeToColor(RightBadge, Selected);
Expand All @@ -93,7 +97,7 @@ public override void Draw()

if (!string.IsNullOrWhiteSpace(RightLabel))
{
_labelText.Position = new Point(420 + Offset.X + Parent.WidthOffset, _labelText.Position.Y);
_labelText.Position = new PointF(420 + Offset.X + Parent.WidthOffset, _labelText.Position.Y);
_labelText.Caption = RightLabel;
_labelText.Color = _text.Color = Enabled ? Selected ? HighlightedTextColor : TextColor : Color.FromArgb(163, 159, 148);
_labelText.Draw();
Expand Down
110 changes: 110 additions & 0 deletions NativeUI/Items/UIMenuDynamicListItem.cs
@@ -0,0 +1,110 @@
using CitizenFX.Core.UI;
using System;
using System.Drawing;
using System.Threading.Tasks;
using Font = CitizenFX.Core.UI.Font;

namespace NativeUI
{
public class UIMenuDynamicListItem : UIMenuItem, IListItem
{
public enum ChangeDirection
{
Left,
Right
}

public delegate string DynamicListItemChangeCallback(UIMenuDynamicListItem sender, ChangeDirection direction);

protected UIResText _itemText;
protected Sprite _arrowLeft;
protected Sprite _arrowRight;

public string CurrentListItem { get; internal set; }
public DynamicListItemChangeCallback Callback { get; set; }

/// <summary>
/// List item with items generated at runtime
/// </summary>
/// <param name="text">Label text</param>
public UIMenuDynamicListItem(string text, string startingItem, DynamicListItemChangeCallback changeCallback) : this(text, null, startingItem, changeCallback)
{
}

/// <summary>
/// List item with items generated at runtime
/// </summary>
/// <param name="text">Label text</param>
/// <param name="description">Item description</param>
public UIMenuDynamicListItem(string text, string description, string startingItem, DynamicListItemChangeCallback changeCallback) : base(text, description)
{
const int y = 0;
_arrowLeft = new Sprite("commonmenu", "arrowleft", new PointF(110, 105 + y), new SizeF(30, 30));
_arrowRight = new Sprite("commonmenu", "arrowright", new PointF(280, 105 + y), new SizeF(30, 30));
_itemText = new UIResText("", new PointF(290, y + 104), 0.35f, Colors.White, Font.ChaletLondon,
Alignment.Right);

CurrentListItem = startingItem;
Callback = changeCallback;
}


/// <summary>
/// Change item's position.
/// </summary>
/// <param name="y">New Y position.</param>
public override void Position(int y)
{
_arrowLeft.Position = new PointF(300 + Offset.X + Parent.WidthOffset, 147 + y + Offset.Y);
_arrowRight.Position = new PointF(400 + Offset.X + Parent.WidthOffset, 147 + y + Offset.Y);
_itemText.Position = new PointF(300 + Offset.X + Parent.WidthOffset, y + 147 + Offset.Y);
base.Position(y);
}

/// <summary>
/// Draw item.
/// </summary>
public override async Task Draw()
{
base.Draw();

string caption = CurrentListItem;
float offset = ScreenTools.GetTextWidth(caption, _itemText.Font, _itemText.Scale);

_itemText.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);

_itemText.Caption = caption;

_arrowLeft.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowRight.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);

_arrowLeft.Position = new PointF(375 - (int)offset + Offset.X + Parent.WidthOffset, _arrowLeft.Position.Y);
if (Selected)
{
_arrowLeft.Draw();
_arrowRight.Draw();
_itemText.Position = new PointF(403 + Offset.X + Parent.WidthOffset, _itemText.Position.Y);
}
else
{
_itemText.Position = new PointF(418 + Offset.X + Parent.WidthOffset, _itemText.Position.Y);
}
_itemText.Draw();
}

public override void SetRightBadge(BadgeStyle badge)
{
throw new Exception("UIMenuListItem cannot have a right badge.");
}

public override void SetRightLabel(string text)
{
throw new Exception("UIMenuListItem cannot have a right label.");
}

public string CurrentItem()
{
return CurrentListItem;
}
}
}
205 changes: 137 additions & 68 deletions UIMenuItem.cs → NativeUI/Items/UIMenuItem.cs
@@ -1,61 +1,89 @@
using System;
using CitizenFX.Core.UI;
using System;
using System.Drawing;
using System.Threading.Tasks;

namespace NativeUI
{
{
/// <summary>
/// Simple item with a label.
/// </summary>
public class UIMenuItem
{
protected UIResRectangle _rectangle;
internal UIResRectangle _rectangle;
protected UIResText _text;
protected Sprite _selectedSprite;

protected Sprite _badgeLeft;
protected Sprite _badgeRight;

protected UIResText _labelText;
protected SizeF Resolution = ScreenTools.ResolutionMaintainRatio;
public Color MainColor { get; set; }
public Color HighlightColor { get; set; }

public Color TextColor { get; set; }
public Color HighlightedTextColor { get; set; }

// Allows you to attach data to a menu item if you want to identify the menu item without having to put identification info in the visible text or description.
// Taken from MenuAPI (Thanks Tom).
public dynamic ItemData { get; set; }

private readonly Color _defaultColor = Color.FromArgb(20, 255, 255, 255);
private readonly Color _disabledColor = Color.FromArgb(163, 159, 148); // Why allocating memory for same color every time?

/// <summary>
/// Called when user selects the current item.
/// </summary>
public event ItemActivatedEvent Activated;


/// <summary>
/// Basic menu button.
/// </summary>
/// <param name="text">Button label.</param>
public UIMenuItem(string text) : this(text, "")
{
}

/// <summary>
/// Basic menu button.
/// </summary>
/// <param name="text">Button label.</param>
/// <param name="description">Description.</param>
public UIMenuItem(string text, string description)
{
Enabled = true;
/// <summary>
/// Basic menu button.
/// </summary>
/// <param name="text">Button label.</param>
public UIMenuItem(string text) : this(text, "", Color.Transparent, Color.FromArgb(255, 255, 255, 255)){}

_rectangle = new UIResRectangle(new Point(0, 0), new Size(431, 38), Color.FromArgb(150, 0, 0, 0));
_text = new UIResText(text, new Point(8, 0), 0.33f, Color.WhiteSmoke, GTA.Font.ChaletLondon, UIResText.Alignment.Left);
Description = description;
_selectedSprite = new Sprite("commonmenu", "gradient_nav", new Point(0, 0), new Size(431, 38));
/// <summary>
/// Basic menu button with description.
/// </summary>
/// <param name="text">Button label.</param>
/// <param name="description">Description.</param>
public UIMenuItem(string text, string description) : this(text, description, Color.Transparent, Color.FromArgb(255, 255, 255, 255)){}

_badgeLeft = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));
_badgeRight = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));
/// <summary>
/// Basic menu button with description and colors.
/// </summary>
/// <param name="text">Button label.</param>
/// <param name="description">Button label.</param>
/// <param name="description">Button label.</param>
/// <param name="description">Button label.</param>
public UIMenuItem(string text, string description, Color color, Color highlightColor)
{
Enabled = true;

_labelText = new UIResText("", new Point(0, 0), 0.35f) {TextAlignment = UIResText.Alignment.Right};
}
MainColor = color;
HighlightColor = highlightColor;

TextColor = Colors.White;
HighlightedTextColor = Colors.Black;

/// <summary>
/// Whether this item is currently selected.
/// </summary>
public virtual bool Selected { get; set; }
_rectangle = new UIResRectangle(new Point(0, 0), new Size(431, 38), _defaultColor); // Color.FromArgb(150, 0, 0, 0)
_text = new UIResText(text, new Point(8, 0), 0.33f, Colors.WhiteSmoke, CitizenFX.Core.UI.Font.ChaletLondon, Alignment.Left);
Description = description;
_selectedSprite = new Sprite("commonmenu", "gradient_nav", new Point(0, 0), new Size(431, 38));

_badgeLeft = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));
_badgeRight = new Sprite("commonmenu", "", new Point(0, 0), new Size(40, 40));

_labelText = new UIResText("", new Point(0, 0), 0.35f) { TextAlignment = Alignment.Right };
}


/// <summary>
/// Whether this item is currently selected.
/// </summary>
public virtual bool Selected { get; set; }


/// <summary>
Expand All @@ -79,70 +107,79 @@ internal virtual void ItemActivate(UIMenu sender)
{
Activated?.Invoke(sender, this);
}


/// <summary>
/// Set item's position.
/// </summary>
/// <param name="y"></param>
public virtual void Position(int y)
{
_rectangle.Position = new Point(Offset.X, y + 144 + Offset.Y);
_selectedSprite.Position = new Point(0 + Offset.X, y + 144 + Offset.Y);
_text.Position = new Point(8 + Offset.X, y + 147 + Offset.Y);
_rectangle.Position = new PointF(Offset.X, y + 144 + Offset.Y);
_selectedSprite.Position = new PointF(0 + Offset.X, y + 144 + Offset.Y);
_text.Position = new PointF(8 + Offset.X, y + 147 + Offset.Y);

_badgeLeft.Position = new Point(0 + Offset.X, y + 142 + Offset.Y);
_badgeRight.Position = new Point(385 + Offset.X, y + 142 + Offset.Y);
_badgeLeft.Position = new PointF(0 + Offset.X, y + 142 + Offset.Y);
_badgeRight.Position = new PointF(385 + Offset.X, y + 142 + Offset.Y);

_labelText.Position = new Point(420 + Offset.X, y + 148 + Offset.Y);
_labelText.Position = new PointF(420 + Offset.X, y + 148 + Offset.Y);
}


/// <summary>
/// Draw this item.
/// </summary>
public virtual void Draw()
public virtual async Task Draw()
{
_rectangle.Size = new Size(431 + Parent.WidthOffset, 38);
_selectedSprite.Size = new Size(431 + Parent.WidthOffset, 38);
// Removed because of Progress Item height calculations
//_rectangle.Size = new SizeF(431 + Parent.WidthOffset, 38);
//_selectedSprite.Size = new SizeF(431 + Parent.WidthOffset, 38);

if (Hovered && !Selected)
{
_rectangle.Color = Color.FromArgb(20, 255, 255, 255);
_rectangle.Draw();
}
if (Selected)
_selectedSprite.Draw();
if (Selected)
{
_selectedSprite.Color = HighlightColor;
_selectedSprite.Draw();
}
else
{

_text.Color = Enabled ? Selected ? Color.Black : Color.WhiteSmoke : Color.FromArgb(163, 159, 148);
_selectedSprite.Color = MainColor;
_selectedSprite.Draw();
}

if (LeftBadge != BadgeStyle.None)
_text.Color = Enabled ? (Selected ? HighlightedTextColor : TextColor) : _disabledColor; // No alloc anymore there

if (LeftBadge == BadgeStyle.None)
{
_text.Position = new Point(35 + Offset.X, _text.Position.Y);
_text.Position = new PointF(8 + Offset.X, _text.Position.Y);
}
else
{
_text.Position = new PointF(35 + Offset.X, _text.Position.Y);
_badgeLeft.TextureDict = BadgeToSpriteLib(LeftBadge);
_badgeLeft.TextureName = BadgeToSpriteName(LeftBadge, Selected);
_badgeLeft.Color = BadgeToColor(LeftBadge, Selected);
_badgeLeft.Draw();
}
else
{
_text.Position = new Point(8 + Offset.X, _text.Position.Y);
}

if (RightBadge != BadgeStyle.None)
{
_badgeRight.Position = new Point(385 + Offset.X + Parent.WidthOffset, _badgeRight.Position.Y);
_badgeRight.Position = new PointF(385 + Offset.X + Parent.WidthOffset, _badgeRight.Position.Y);
_badgeRight.TextureDict = BadgeToSpriteLib(RightBadge);
_badgeRight.TextureName = BadgeToSpriteName(RightBadge, Selected);
_badgeRight.Color = BadgeToColor(RightBadge, Selected);
_badgeRight.Draw();
}
}

if (!String.IsNullOrWhiteSpace(RightLabel))
if (!String.IsNullOrWhiteSpace(RightLabel))
{
_labelText.Position = new Point(420 + Offset.X + Parent.WidthOffset, _labelText.Position.Y);
_labelText.Caption = RightLabel;
_labelText.Color = _text.Color = Enabled ? Selected ? Color.Black : Color.WhiteSmoke : Color.FromArgb(163, 159, 148);
if (RightBadge == BadgeStyle.None)
_labelText.Position = new PointF(420 + Offset.X + Parent.WidthOffset, _labelText.Position.Y);
else
_labelText.Position = new PointF(390 + Offset.X + Parent.WidthOffset, _labelText.Position.Y);
_labelText.Caption = RightLabel;
_labelText.Color = _text.Color = Enabled ? (Selected ? Colors.Black : Colors.WhiteSmoke) : _disabledColor; // No alloc anymore there
_labelText.Draw();
}
_text.Draw();
Expand All @@ -152,7 +189,7 @@ public virtual void Draw()
/// <summary>
/// This item's offset.
/// </summary>
public Point Offset { get; set; }
public PointF Offset { get; set; }


/// <summary>
Expand Down Expand Up @@ -193,7 +230,7 @@ public virtual void SetRightLabel(string text)
{
RightLabel = text;
}

/// <summary>
/// Returns the current right label.
/// </summary>
Expand Down Expand Up @@ -236,15 +273,31 @@ public enum BadgeStyle
Trevor,
Lock,
Tick,
}

internal static string BadgeToSpriteLib(BadgeStyle badge)
Sale,
ArrowLeft,
ArrowRight,
Audio1,
Audio2,
Audio3,
AudioInactive,
AudioMute
}

internal static string BadgeToSpriteLib(BadgeStyle badge)
{
switch (badge)
{
default:
case BadgeStyle.Sale:
return "mpshopsale";
case BadgeStyle.Audio1:
case BadgeStyle.Audio2:
case BadgeStyle.Audio3:
case BadgeStyle.AudioInactive:
case BadgeStyle.AudioMute:
return "mpleaderboard";
default:
return "commonmenu";
}
}
}

internal static string BadgeToSpriteName(BadgeStyle badge, bool selected)
Expand Down Expand Up @@ -297,7 +350,23 @@ internal static string BadgeToSpriteName(BadgeStyle badge, bool selected)
return "shop_tick_icon";
case BadgeStyle.Trevor:
return selected ? "shop_trevor_icon_b" : "shop_trevor_icon_a";
default:
case BadgeStyle.Sale:
return "saleicon";
case BadgeStyle.ArrowLeft:
return "arrowleft";
case BadgeStyle.ArrowRight:
return "arrowright";
case BadgeStyle.Audio1:
return "leaderboard_audio_1";
case BadgeStyle.Audio2:
return "leaderboard_audio_2";
case BadgeStyle.Audio3:
return "leaderboard_audio_3";
case BadgeStyle.AudioInactive:
return "leaderboard_audio_inactive";
case BadgeStyle.AudioMute:
return "leaderboard_audio_mute";
default:
return "";
}
}
Expand Down
202 changes: 202 additions & 0 deletions NativeUI/Items/UIMenuListItem.cs
@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;
using CitizenFX.Core.UI;

namespace NativeUI
{
public class UIMenuListItem : UIMenuItem, IListItem
{
protected internal UIResText _itemText;
protected internal Sprite _arrowLeft;
protected internal Sprite _arrowRight;

protected internal int _index;
protected internal List<dynamic> _items;


/// <summary>
/// Triggered when the list is changed.
/// </summary>
public event ItemListEvent OnListChanged;

/// <summary>
/// Triggered when a list item is selected.
/// </summary>
public event ItemListEvent OnListSelected;

/// <summary>
/// Returns the current selected index.
/// </summary>
public int Index
{
get { return _index % Items.Count; }
set { _index = 100000000 - (100000000 % Items.Count) + value; }
}

/// <summary>
/// Returns the current selected index.
/// </summary>
public List<object> Items
{
get => _items;
set
{
Index = 0;
_items = value;
}
}

public List<UIMenuPanel> Panels = new List<UIMenuPanel>();


/// <summary>
/// List item, with left/right arrows.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="items">List that contains your items.</param>
/// <param name="index">Index in the list. If unsure user 0.</param>
public UIMenuListItem(string text, List<dynamic> items, int index) : this(text, items, index, "")
{
}

/// <summary>
/// List item, with left/right arrows.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="items">List that contains your items.</param>
/// <param name="index">Index in the list. If unsure user 0.</param>
/// <param name="description">Description for this item.</param>
public UIMenuListItem(string text, List<dynamic> items, int index, string description) : this(text, items, index, description, Color.Transparent, Color.FromArgb(255, 255, 255, 255))
{
}

public UIMenuListItem(string text, List<dynamic> items, int index, string description, Color mainColor, Color higlightColor) : base(text, description, mainColor, higlightColor)
{
const int y = 0;
_items = items;
_arrowLeft = new Sprite("commonmenu", "arrowleft", new PointF(110, 105 + y), new SizeF(30, 30));
_arrowRight = new Sprite("commonmenu", "arrowright", new PointF(280, 105 + y), new SizeF(30, 30));
_itemText = new UIResText("", new PointF(290, y + 104), 0.35f, Colors.White, CitizenFX.Core.UI.Font.ChaletLondon,
Alignment.Left)
{ TextAlignment = Alignment.Right };
Index = index;
}


/// <summary>
/// Change item's position.
/// </summary>
/// <param name="y">New Y position.</param>
public override void Position(int y)
{
_arrowLeft.Position = new PointF(300 + Offset.X + Parent.WidthOffset, 147 + y + Offset.Y);
_arrowRight.Position = new PointF(400 + Offset.X + Parent.WidthOffset, 147 + y + Offset.Y);
_itemText.Position = new PointF(300 + Offset.X + Parent.WidthOffset, y + 147 + Offset.Y);
base.Position(y);
}


/// <summary>
/// Find an item in the list and return it's index.
/// </summary>
/// <param name="item">Item to search for.</param>
/// <returns>Item index.</returns>
[Obsolete("Use UIMenuListItem.Items.FindIndex(p => ReferenceEquals(p, item)) instead.")]
public virtual int ItemToIndex(dynamic item)
{
return _items.FindIndex(p => ReferenceEquals(p, item));
}


/// <summary>
/// Find an item by it's index and return the item.
/// </summary>
/// <param name="index">Item's index.</param>
/// <returns>Item</returns>
[Obsolete("Use UIMenuListItem.Items[Index] instead.")]
public virtual dynamic IndexToItem(int index)
{
return _items[index];
}


/// <summary>
/// Draw item.
/// </summary>
public override async Task Draw()
{
base.Draw();

string caption = _items[Index].ToString();
float offset = ScreenTools.GetTextWidth(caption, _itemText.Font, _itemText.Scale);

_itemText.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);

_itemText.Caption = caption;

_arrowLeft.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowRight.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);

_arrowLeft.Position = new PointF(375 - (int)offset + Offset.X + Parent.WidthOffset, _arrowLeft.Position.Y);
if (Selected)
{
_arrowLeft.Draw();
_arrowRight.Draw();
_itemText.Position = new PointF(403 + Offset.X + Parent.WidthOffset, _itemText.Position.Y);
}
else
{
_itemText.Position = new PointF(418 + Offset.X + Parent.WidthOffset, _itemText.Position.Y);
}
_itemText.Draw();
}

internal virtual void ListChangedTrigger(int newindex)
{
OnListChanged?.Invoke(this, newindex);
}

internal virtual void ListSelectedTrigger(int newindex)
{
OnListSelected?.Invoke(this, newindex);
}

public override void SetRightBadge(BadgeStyle badge)
{
throw new Exception("UIMenuListItem cannot have a right badge.");
}

public override void SetRightLabel(string text)
{
throw new Exception("UIMenuListItem cannot have a right label.");
}

/// <summary>
/// Add a Panel to the UIMenuListItem
/// </summary>
/// <param name="panel"></param>
public virtual void AddPanel(UIMenuPanel panel)
{
Panels.Add(panel);
panel.SetParentItem(this);
}

/// <summary>
/// Removes a panel at a defined Index
/// </summary>
/// <param name="Index"></param>
public virtual void RemovePanelAt(int Index)
{
Panels.RemoveAt(Index);
}


[Obsolete("Use UIMenuListItem.Items[Index].ToString() instead.")]
public string CurrentItem()
{
return _items[Index].ToString();
}
}
}
146 changes: 146 additions & 0 deletions NativeUI/Items/UIMenuProgressItem.cs
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;
using CitizenFX.Core;
using CitizenFX.Core.Native;

namespace NativeUI
{
public class UIMenuProgressItem : UIMenuItem
{
protected UIResRectangle _background;
protected int Items;
protected bool Pressed;
protected bool Counter;
protected float _max = 407.5f;
protected UIMenuGridAudio Audio;
protected UIResRectangle _bar;
protected int _index;
public int Index
{
get { return _index; }
set
{
if (value > Items)
_index = Items;
else if (value < 0)
_index = 0;
else
_index = value;
if (Counter)
SetRightLabel(_index + "/" + Items);
else
SetRightLabel("" + _index);

UpdateBar();
}
}

protected void UpdateBar()
{
_bar.Size = new SizeF(Index *(_max / Items), _bar.Size.Height);
}

public event OnProgressChanged OnProgressChanged;
public event OnProgressSelected OnProgressSelected;

public UIMenuProgressItem(string text, string description, int maxItems, int index, bool counter) : base(text, description)
{
Items = maxItems;
_index = index;
Counter = counter;
_max = 407.5f;
Audio = new UIMenuGridAudio("CONTINUOUS_SLIDER", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0);
_background = new UIResRectangle(new PointF(0, 0), new SizeF(415, 14), Color.FromArgb(255, 0, 0, 0));
_bar = new UIResRectangle(new PointF(0, 0), new SizeF(407.5f, 7.5f));
if (Counter)
SetRightLabel(_index + "/" + Items);
else
SetRightLabel(""+_index);

UpdateBar();
}

public void ProgressChanged(UIMenu Menu, UIMenuProgressItem Item, int index)
{
OnProgressChanged?.Invoke(Menu, Item, index);
}

public void ProgressSelected(UIMenu Menu, UIMenuProgressItem Item, int index)
{
OnProgressSelected?.Invoke(Menu, Item, index);
}

public override void SetRightBadge(BadgeStyle badge)
{
throw new Exception("UIMenuProgressItem cannot have a right badge.");
}

public override void Position(int y)
{
_rectangle.Position = new PointF(Offset.X, y + 144 + Offset.Y);
_selectedSprite.Position = new PointF(0 + Offset.X, y + 144 + Offset.Y);
_text.Position = new PointF(8 + Offset.X, y + 141.5f + Offset.Y);

_labelText.Position = new PointF(420 + Offset.X, y + 141.5f + Offset.Y);

_max = 407.5f + Parent.WidthOffset;
_background.Size = new SizeF(415f + Parent.WidthOffset, 14f);
_background.Position = new PointF(8f + Offset.X, 170f + y + Offset.Y);
_bar.Position = new PointF(11.75f + Offset.X, 172.5f + y + Offset.Y);
}

public void CalculateProgress(float CursorX)
{
var Progress = CursorX - _bar.Position.X;
Index = (int)Math.Round(Items * ((Progress >= 0f && Progress <= _max ) ? Progress : (Progress < 0) ? 0 : _max) / _max);
}

public async void Functions()
{
if (ScreenTools.IsMouseInBounds(new PointF(_bar.Position.X, _bar.Position.Y - 7.5f), new SizeF(_max, _bar.Size.Height + 19)))
{
if (API.IsDisabledControlPressed(0, 24))
{
if (!Pressed)
{
Pressed = true;
Audio.Id = API.GetSoundId();
API.PlaySoundFrontend(Audio.Id, Audio.Slider, Audio.Library, true);
while (API.IsDisabledControlPressed(0, 24) && ScreenTools.IsMouseInBounds(new PointF(_bar.Position.X, _bar.Position.Y - 7.5f), new SizeF(_max, _bar.Size.Height + 19)))
{
await BaseScript.Delay(0);
float CursorX = API.GetDisabledControlNormal(0, 239) * Resolution.Width;
CalculateProgress(CursorX);
Parent.ProgressChange(this, _index);
ProgressChanged(Parent, this, _index);

}
API.StopSound(Audio.Id);
API.ReleaseSoundId(Audio.Id);
Pressed = false;
}
}
}
}

public async override Task Draw()
{
base.Draw();
if (Selected)
{
_background.Color = Colors.Black;
_bar.Color = Colors.White;
}
else
{
_background.Color = Colors.White;
_bar.Color = Colors.Black;
}
Functions();
_background.Draw();
_bar.Draw();
}
}
}
18 changes: 18 additions & 0 deletions NativeUI/Items/UIMenuSeparatorItem.cs
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NativeUI
{
public class UIMenuSeparatorItem : UIMenuItem
{
/// <summary>
/// Use it to create an Empty item to separate menu Items
/// </summary>
public UIMenuSeparatorItem() : base("", "")
{
}
}
}
35 changes: 35 additions & 0 deletions NativeUI/Items/UIMenuSliderHeritageItem.cs
@@ -0,0 +1,35 @@
using System.Drawing;
using System.Threading.Tasks;

namespace NativeUI
{
public class UIMenuSliderHeritageItem : UIMenuSliderItem
{
public UIMenuSliderHeritageItem(string text, string description, bool divider):base(text, description, divider)
{
const int y = 0;
_arrowLeft = new Sprite("mpleaderboard", "leaderboard_female_icon", new PointF(0, 105 + y), new Size(40, 40), 0, Color.FromArgb(255,255,255));
_arrowRight = new Sprite("mpleaderboard", "leaderboard_male_icon", new PointF(0, 105 + y), new Size(40, 40), 0, Color.FromArgb(255, 255, 255));
}

public override void Position(int y)
{
base.Position(y);
_rectangleBackground.Position = new PointF(250f + base.Offset.X + Parent.WidthOffset, y + 158.5f + base.Offset.Y);
_rectangleSlider.Position = new PointF(250f + base.Offset.X + Parent.WidthOffset, y + 158.5f + base.Offset.Y);
_rectangleDivider.Position = new PointF(323.5f + base.Offset.X + Parent.WidthOffset, y + 153 + base.Offset.Y);
_arrowLeft.Position = new PointF(217f + base.Offset.X + Parent.WidthOffset, y + 143.5f + base.Offset.Y);
_arrowRight.Position = new PointF(395f + base.Offset.X + Parent.WidthOffset, y + 143.5f + base.Offset.Y);
}

public async override Task Draw()
{
await base.Draw();
_arrowLeft.Color = Enabled ? Selected ? Color.FromArgb(255, 102, 178) : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowRight.Color = Enabled ? Selected ? Color.FromArgb(51,51,255) : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowLeft.Draw();
_arrowRight.Draw();
await Task.FromResult(0);
}
}
}
164 changes: 164 additions & 0 deletions NativeUI/Items/UIMenuSliderItem.cs
@@ -0,0 +1,164 @@
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;

namespace NativeUI
{
public class UIMenuSliderItem : UIMenuItem
{
protected internal Sprite _arrowLeft;
protected internal Sprite _arrowRight;

protected internal UIResRectangle _rectangleBackground;
protected internal UIResRectangle _rectangleSlider;
protected internal UIResRectangle _rectangleDivider;

protected internal int _value = 0;
protected internal int _max = 100;
protected internal int _multiplier = 5;


/// <summary>
/// Triggered when the slider is changed.
/// </summary>
public event ItemSliderEvent OnSliderChanged;
public bool Divider;

/// <summary>
/// The maximum value of the slider.
/// </summary>
public int Maximum
{
get
{
return _max;
}
set
{
_max = value;
if (_value > value)
{
_value = value;
}
}
}
/// <summary>
/// Curent value of the slider.
/// </summary>
public int Value
{
get
{
return _value;
}
set
{
if (value > _max)
_value = _max;
else if (value < 0)
_value = 0;
else
_value = value;
SliderChanged(_value);
}
}
/// <summary>
/// The multiplier of the left and right navigation movements.
/// </summary>
public int Multiplier
{
get
{
return _multiplier;
}
set
{
_multiplier = value;
}
}


/// <summary>
/// List item, with slider.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="items">List that contains your items.</param>
/// <param name="index">Index in the list. If unsure user 0.</param>
public UIMenuSliderItem(string text) : this(text, "", false)
{
}

/// <summary>
/// List item, with slider.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="items">List that contains your items.</param>
/// <param name="index">Index in the list. If unsure user 0.</param>
/// <param name="description">Description for this item.</param>
public UIMenuSliderItem(string text, string description) : this(text, description, false)
{
}

/// <summary>
/// List item, with slider.
/// </summary>
/// <param name="text">Item label.</param>
/// <param name="items">List that contains your items.</param>
/// <param name="index">Index in the list. If unsure user 0.</param>
/// <param name="description">Description for this item.</param>
/// /// <param name="divider">Put a divider in the center of the slider</param>
public UIMenuSliderItem(string text, string description, bool divider) : base(text, description)
{
const int y = 0;
_arrowLeft = new Sprite("commonmenutu", "arrowleft", new Point(0, 105 + y), new Size(15, 15));
_arrowRight = new Sprite("commonmenutu", "arrowright", new Point(0, 105 + y), new Size(15, 15));
_rectangleBackground = new UIResRectangle(new Point(0, 0), new Size(150, 9), Color.FromArgb(255, 4, 32, 57));
_rectangleSlider = new UIResRectangle(new Point(0, 0), new Size(75, 9), Color.FromArgb(255, 57, 116, 200));
Divider = divider;
if (Divider)
_rectangleDivider = new UIResRectangle(new Point(0, 0), new Size(2, 20), Colors.WhiteSmoke);
else
_rectangleDivider = new UIResRectangle(new Point(0, 0), new Size(2, 20), Color.Transparent);
}

/// <summary>
/// Change item's position.
/// </summary>
/// <param name="y">New Y position.</param>
public override void Position(int y)
{
_rectangleBackground.Position = new PointF(250 + Offset.X, y + 158 + Offset.Y);
_rectangleSlider.Position = new PointF(250 + Offset.X, y + 158 + Offset.Y);
_rectangleDivider.Position = new PointF(323 + Offset.X, y + 153 + Offset.Y);
_arrowLeft.Position = new PointF(235 + Offset.X + Parent.WidthOffset, 155 + y + Offset.Y);
_arrowRight.Position = new PointF(400 + Offset.X + Parent.WidthOffset, 155 + y + Offset.Y);
base.Position(y);
}

/// <summary>
/// Draw item.
/// </summary>
public override async Task Draw()
{
base.Draw();

_arrowLeft.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowRight.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
float offset = 176 + Offset.X + _rectangleBackground.Size.Width - _rectangleSlider.Size.Width;
_rectangleSlider.Position = new PointF((int)(offset + (_value / (float)_max * 73)), _rectangleSlider.Position.Y);
if (Selected)
{
_arrowLeft.Draw();
_arrowRight.Draw();
}
_rectangleBackground.Draw();
_rectangleSlider.Draw();
_rectangleDivider.Draw();
}

internal virtual void SliderChanged(int value)
{
OnSliderChanged?.Invoke(this, value);
}
}
}
171 changes: 171 additions & 0 deletions NativeUI/Items/UIMenuSliderProgressItem.cs
@@ -0,0 +1,171 @@
using CitizenFX.Core;
using CitizenFX.Core.Native;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NativeUI
{
public class UIMenuSliderProgressItem : UIMenuItem
{
protected internal Sprite _arrowLeft;
protected internal Sprite _arrowRight;
protected internal bool Pressed;
protected internal UIMenuGridAudio Audio;
protected internal UIResRectangle _rectangleBackground;
protected internal UIResRectangle _rectangleSlider;
protected internal UIResRectangle _rectangleDivider;

protected internal int _value = 0;
protected internal int _max;
protected internal int _multiplier = 5;
protected internal bool Divider;
protected internal Color SliderColor;
protected internal Color BackgroundSliderColor;

public UIMenuSliderProgressItem(string text, int maxCount, int startIndex, bool divider = false) : this(text, maxCount, startIndex, "", divider)
{
_max = maxCount;
_value = startIndex;
}

public UIMenuSliderProgressItem(string text, int maxCount, int startIndex, string description, bool divider = false) : this(text, maxCount, startIndex, description, Color.FromArgb(255, 57, 119, 200), Color.FromArgb(255, 4, 32, 57), divider)
{
_max = maxCount;
_value = startIndex;
}

public UIMenuSliderProgressItem(string text, int maxCount, int startIndex, string description, Color sliderColor, Color backgroundSliderColor, bool divider = false) : base(text, description)
{
_max = maxCount;
_value = startIndex;
_arrowLeft = new Sprite("commonmenu", "arrowleft", new PointF(0, 105), new SizeF(25, 25));
_arrowRight = new Sprite("commonmenu", "arrowright", new PointF(0, 105), new SizeF(25, 25));
SliderColor = sliderColor;
BackgroundSliderColor = backgroundSliderColor;
_rectangleBackground = new UIResRectangle(new PointF(0, 0), new SizeF(150, 10), BackgroundSliderColor);
_rectangleSlider = new UIResRectangle(new PointF(0, 0), new SizeF(75, 10), SliderColor);
if (divider)
_rectangleDivider = new UIResRectangle(new Point(0, 0), new Size(2, 20), Colors.WhiteSmoke);
else
_rectangleDivider = new UIResRectangle(new Point(0, 0), new Size(2, 20), Color.Transparent);
float offset = _rectangleBackground.Size.Width / _max * _value;
_rectangleSlider.Size = new SizeF(offset, _rectangleSlider.Size.Height);
Audio = new UIMenuGridAudio("CONTINUOUS_SLIDER", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0);
}

public override void Position(int y)
{
base.Position(y);
_rectangleBackground.Position = new PointF(250f + base.Offset.X + Parent.WidthOffset, y + 158.5f + base.Offset.Y);
_rectangleSlider.Position = new PointF(250f + base.Offset.X + Parent.WidthOffset, y + 158.5f + base.Offset.Y);
_rectangleDivider.Position = new PointF(323.5f + base.Offset.X + Parent.WidthOffset, y + 153 + base.Offset.Y);
_arrowLeft.Position = new PointF(225 + base.Offset.X + Parent.WidthOffset, y + 150.5f + base.Offset.Y);
_arrowRight.Position = new PointF(400 + base.Offset.X + Parent.WidthOffset, y + 150.5f + base.Offset.Y);
}

public int Value
{
get
{
float offset = _rectangleBackground.Size.Width / _max * _value;
_rectangleSlider.Size = new SizeF(offset, _rectangleSlider.Size.Height);
return _value;
}
set
{
if (value > _max)
_value = _max;
else if (value < 0)
_value = 0;
else
_value = value;
SliderProgressChanged(Value);
}
}

public int Multiplier
{
get => _multiplier;
set =>_multiplier = value;
}

/// <summary>
/// Triggered when the slider is changed.
/// </summary>
public event ItemSliderProgressEvent OnSliderChanged;

internal virtual void SliderProgressChanged(int value)
{
OnSliderChanged?.Invoke(this, value);
}

public async void Functions()
{
if (ScreenTools.IsMouseInBounds(new PointF(_rectangleBackground.Position.X, _rectangleBackground.Position.Y), new SizeF(150f, _rectangleBackground.Size.Height)))
{
if (API.IsDisabledControlPressed(0, 24))
{
if (!Pressed)
{
Pressed = true;
Audio.Id = API.GetSoundId();
API.PlaySoundFrontend(Audio.Id, Audio.Slider, Audio.Library, true);
}
await BaseScript.Delay(0);
float CursorX = API.GetDisabledControlNormal(0, 239) * Resolution.Width;
var Progress = CursorX - _rectangleSlider.Position.X;
Value = (int)Math.Round(_max * ((Progress >= 0f && Progress <= 150f) ? Progress : (Progress < 0) ? 0 : 150f) / 150f);
SliderProgressChanged(Value);
}
else
{
API.StopSound(Audio.Id);
API.ReleaseSoundId(Audio.Id);
Pressed = false;
}
}
else if (ScreenTools.IsMouseInBounds(_arrowLeft.Position, _arrowLeft.Size))
{
if (API.IsDisabledControlPressed(0, 24))
{
Value -= Multiplier;
SliderProgressChanged(Value);
}
}
else if (ScreenTools.IsMouseInBounds(_arrowRight.Position, _arrowRight.Size))
{
if (API.IsDisabledControlPressed(0, 24))
{
Value += Multiplier;
SliderProgressChanged(Value);
}
}
else
{
API.StopSound(Audio.Id);
API.ReleaseSoundId(Audio.Id);
Pressed = false;
}
}

/// <summary>
/// Draw item.
/// </summary>
public override async Task Draw()
{
base.Draw();
_arrowLeft.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowRight.Color = Enabled ? Selected ? Colors.Black : Colors.WhiteSmoke : Color.FromArgb(163, 159, 148);
_arrowLeft.Draw();
_arrowRight.Draw();
_rectangleBackground.Draw();
_rectangleSlider.Draw();
_rectangleDivider.Draw();
Functions();
}
}
}