added changelog model and update notes UI with particle effects
This commit is contained in:
25
LightlessSync/UI/Models/Changelog.cs
Normal file
25
LightlessSync/UI/Models/Changelog.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace LightlessSync.UI.Models
|
||||||
|
{
|
||||||
|
public class ChangelogFile
|
||||||
|
{
|
||||||
|
public string Tagline { get; init; } = string.Empty;
|
||||||
|
public string Subline { get; init; } = string.Empty;
|
||||||
|
public List<ChangelogEntry> Changelog { get; init; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangelogEntry
|
||||||
|
{
|
||||||
|
public string Name { get; init; } = string.Empty;
|
||||||
|
public string Date { get; init; } = string.Empty;
|
||||||
|
public string Tagline { get; init; } = string.Empty;
|
||||||
|
public bool? IsCurrent { get; init; }
|
||||||
|
public string? Message { get; init; }
|
||||||
|
public List<ChangelogVersion>? Versions { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangelogVersion
|
||||||
|
{
|
||||||
|
public string Number { get; init; } = string.Empty;
|
||||||
|
public List<string> Items { get; init; } = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ using System.Text;
|
|||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using LightlessSync.UI.Models;
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
private ChangelogFile _changelog = new();
|
private ChangelogFile _changelog = new();
|
||||||
private bool _scrollToTop;
|
private bool _scrollToTop;
|
||||||
private int _selectedTab;
|
private int _selectedTab;
|
||||||
|
|
||||||
private struct Particle
|
private struct Particle
|
||||||
{
|
{
|
||||||
public Vector2 Position;
|
public Vector2 Position;
|
||||||
@@ -37,17 +38,17 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
public float Depth;
|
public float Depth;
|
||||||
public float Hue;
|
public float Hue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ParticleType
|
private enum ParticleType
|
||||||
{
|
{
|
||||||
TwinklingStar,
|
TwinklingStar,
|
||||||
ShootingStar
|
ShootingStar
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<Particle> _particles = [];
|
private readonly List<Particle> _particles = [];
|
||||||
private float _particleSpawnTimer;
|
private float _particleSpawnTimer;
|
||||||
private readonly Random _random = new();
|
private readonly Random _random = new();
|
||||||
|
|
||||||
private const float HeaderHeight = 150f;
|
private const float HeaderHeight = 150f;
|
||||||
private const float ParticleSpawnInterval = 0.2f;
|
private const float ParticleSpawnInterval = 0.2f;
|
||||||
private const int MaxParticles = 50;
|
private const int MaxParticles = 50;
|
||||||
@@ -69,12 +70,12 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
RespectCloseHotkey = true;
|
RespectCloseHotkey = true;
|
||||||
ShowCloseButton = true;
|
ShowCloseButton = true;
|
||||||
|
|
||||||
Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoTitleBar;
|
Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse |
|
||||||
|
ImGuiWindowFlags.NoTitleBar;
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
MinimumSize = new Vector2(800, 700),
|
MinimumSize = new Vector2(800, 700), MaximumSize = new Vector2(800, 700),
|
||||||
MaximumSize = new Vector2(800, 700),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadEmbeddedResources();
|
LoadEmbeddedResources();
|
||||||
@@ -101,61 +102,63 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
var windowPos = ImGui.GetWindowPos();
|
var windowPos = ImGui.GetWindowPos();
|
||||||
var windowPadding = ImGui.GetStyle().WindowPadding;
|
var windowPadding = ImGui.GetStyle().WindowPadding;
|
||||||
var headerWidth = (800f * ImGuiHelpers.GlobalScale) - (windowPadding.X * 2);
|
var headerWidth = (800f * ImGuiHelpers.GlobalScale) - (windowPadding.X * 2);
|
||||||
|
|
||||||
var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
|
var headerStart = windowPos + new Vector2(windowPadding.X, windowPadding.Y);
|
||||||
var headerEnd = headerStart + new Vector2(headerWidth, HeaderHeight);
|
var headerEnd = headerStart + new Vector2(headerWidth, HeaderHeight);
|
||||||
var headerSize = new Vector2(headerWidth, HeaderHeight);
|
var headerSize = new Vector2(headerWidth, HeaderHeight);
|
||||||
|
|
||||||
var extendedParticleSize = new Vector2(headerWidth, HeaderHeight + ExtendedParticleHeight);
|
var extendedParticleSize = new Vector2(headerWidth, HeaderHeight + ExtendedParticleHeight);
|
||||||
|
|
||||||
DrawGradientBackground(headerStart, headerEnd);
|
DrawGradientBackground(headerStart, headerEnd);
|
||||||
DrawHeaderText(headerStart);
|
DrawHeaderText(headerStart);
|
||||||
DrawHeaderButtons(headerStart, headerWidth);
|
DrawHeaderButtons(headerStart, headerWidth);
|
||||||
DrawBottomGradient(headerStart, headerEnd, headerWidth);
|
DrawBottomGradient(headerStart, headerEnd, headerWidth);
|
||||||
|
|
||||||
ImGui.SetCursorPosY(windowPadding.Y + HeaderHeight + 5);
|
ImGui.SetCursorPosY(windowPadding.Y + HeaderHeight + 5);
|
||||||
ImGui.SetCursorPosX(20);
|
ImGui.SetCursorPosX(20);
|
||||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
{
|
{
|
||||||
ImGui.TextColored(UIColors.Get("LightlessGreen"), FontAwesomeIcon.Star.ToIconString());
|
ImGui.TextColored(UIColors.Get("LightlessGreen"), FontAwesomeIcon.Star.ToIconString());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
ImGui.TextColored(UIColors.Get("LightlessGreen"), "What's New");
|
ImGui.TextColored(UIColors.Get("LightlessGreen"), "What's New");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_changelog.Tagline))
|
if (!string.IsNullOrEmpty(_changelog.Tagline))
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 10);
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + 10);
|
||||||
ImGui.TextColored(new Vector4(0.75f, 0.75f, 0.85f, 1.0f), _changelog.Tagline);
|
ImGui.TextColored(new Vector4(0.75f, 0.75f, 0.85f, 1.0f), _changelog.Tagline);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_changelog.Subline))
|
if (!string.IsNullOrEmpty(_changelog.Subline))
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextColored(new Vector4(0.65f, 0.65f, 0.75f, 1.0f), $" – {_changelog.Subline}");
|
ImGui.TextColored(new Vector4(0.65f, 0.65f, 0.75f, 1.0f), $" – {_changelog.Subline}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(3);
|
ImGuiHelpers.ScaledDummy(3);
|
||||||
|
|
||||||
DrawParticleEffects(headerStart, extendedParticleSize);
|
DrawParticleEffects(headerStart, extendedParticleSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGradientBackground(Vector2 headerStart, Vector2 headerEnd)
|
private void DrawGradientBackground(Vector2 headerStart, Vector2 headerEnd)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
var darkPurple = new Vector4(0.08f, 0.05f, 0.15f, 1.0f);
|
var darkPurple = new Vector4(0.08f, 0.05f, 0.15f, 1.0f);
|
||||||
var deepPurple = new Vector4(0.12f, 0.08f, 0.20f, 1.0f);
|
var deepPurple = new Vector4(0.12f, 0.08f, 0.20f, 1.0f);
|
||||||
|
|
||||||
drawList.AddRectFilledMultiColor(
|
drawList.AddRectFilledMultiColor(
|
||||||
headerStart,
|
headerStart,
|
||||||
headerEnd,
|
headerEnd,
|
||||||
ImGui.GetColorU32(darkPurple),
|
ImGui.GetColorU32(darkPurple),
|
||||||
ImGui.GetColorU32(darkPurple),
|
ImGui.GetColorU32(darkPurple),
|
||||||
ImGui.GetColorU32(deepPurple),
|
ImGui.GetColorU32(deepPurple),
|
||||||
ImGui.GetColorU32(deepPurple)
|
ImGui.GetColorU32(deepPurple)
|
||||||
);
|
);
|
||||||
|
|
||||||
var random = new Random(42);
|
var random = new Random(42);
|
||||||
for (int i = 0; i < 50; i++)
|
for (int i = 0; i < 50; i++)
|
||||||
{
|
{
|
||||||
@@ -167,12 +170,12 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
drawList.AddCircleFilled(starPos, 1f, ImGui.GetColorU32(new Vector4(1f, 1f, 1f, brightness)));
|
drawList.AddCircleFilled(starPos, 1f, ImGui.GetColorU32(new Vector4(1f, 1f, 1f, brightness)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawBottomGradient(Vector2 headerStart, Vector2 headerEnd, float width)
|
private void DrawBottomGradient(Vector2 headerStart, Vector2 headerEnd, float width)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
var gradientHeight = 60f;
|
var gradientHeight = 60f;
|
||||||
|
|
||||||
for (int i = 0; i < gradientHeight; i++)
|
for (int i = 0; i < gradientHeight; i++)
|
||||||
{
|
{
|
||||||
var progress = i / gradientHeight;
|
var progress = i / gradientHeight;
|
||||||
@@ -190,23 +193,23 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawHeaderText(Vector2 headerStart)
|
private void DrawHeaderText(Vector2 headerStart)
|
||||||
{
|
{
|
||||||
var textX = 20f;
|
var textX = 20f;
|
||||||
var textY = 30f;
|
var textY = 30f;
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY));
|
ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY));
|
||||||
|
|
||||||
using (_uiShared.UidFont.Push())
|
using (_uiShared.UidFont.Push())
|
||||||
{
|
{
|
||||||
ImGui.TextColored(new Vector4(0.95f, 0.95f, 0.95f, 1.0f), "Lightless Sync");
|
ImGui.TextColored(new Vector4(0.95f, 0.95f, 0.95f, 1.0f), "Lightless Sync");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY + 45f));
|
ImGui.SetCursorScreenPos(headerStart + new Vector2(textX, textY + 45f));
|
||||||
ImGui.TextColored(UIColors.Get("LightlessBlue"), "Update Notes");
|
ImGui.TextColored(UIColors.Get("LightlessBlue"), "Update Notes");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawHeaderButtons(Vector2 headerStart, float headerWidth)
|
private void DrawHeaderButtons(Vector2 headerStart, float headerWidth)
|
||||||
{
|
{
|
||||||
var buttonSize = _uiShared.GetIconButtonSize(FontAwesomeIcon.Globe);
|
var buttonSize = _uiShared.GetIconButtonSize(FontAwesomeIcon.Globe);
|
||||||
@@ -216,9 +219,9 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
var buttonY = headerStart.Y + topPadding;
|
var buttonY = headerStart.Y + topPadding;
|
||||||
var gitButtonX = headerStart.X + headerWidth - rightPadding - buttonSize.X;
|
var gitButtonX = headerStart.X + headerWidth - rightPadding - buttonSize.X;
|
||||||
var discordButtonX = gitButtonX - buttonSize.X - spacing;
|
var discordButtonX = gitButtonX - buttonSize.X - spacing;
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(new Vector2(discordButtonX, buttonY));
|
ImGui.SetCursorScreenPos(new Vector2(discordButtonX, buttonY));
|
||||||
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0)))
|
using (ImRaii.PushColor(ImGuiCol.Button, new Vector4(0, 0, 0, 0)))
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple") with { W = 0.3f }))
|
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple") with { W = 0.3f }))
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive") with { W = 0.5f }))
|
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive") with { W = 0.5f }))
|
||||||
@@ -227,71 +230,73 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Util.OpenLink("https://discord.gg/dsbjcXMnhA");
|
Util.OpenLink("https://discord.gg/dsbjcXMnhA");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
ImGui.SetTooltip("Join our Discord");
|
ImGui.SetTooltip("Join our Discord");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(new Vector2(gitButtonX, buttonY));
|
ImGui.SetCursorScreenPos(new Vector2(gitButtonX, buttonY));
|
||||||
if (_uiShared.IconButton(FontAwesomeIcon.Code))
|
if (_uiShared.IconButton(FontAwesomeIcon.Code))
|
||||||
{
|
{
|
||||||
Util.OpenLink("https://git.lightless-sync.org/Lightless-Sync");
|
Util.OpenLink("https://git.lightless-sync.org/Lightless-Sync");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
ImGui.SetTooltip("View on Git");
|
ImGui.SetTooltip("View on Git");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawParticleEffects(Vector2 bannerStart, Vector2 bannerSize)
|
private void DrawParticleEffects(Vector2 bannerStart, Vector2 bannerSize)
|
||||||
{
|
{
|
||||||
var deltaTime = ImGui.GetIO().DeltaTime;
|
var deltaTime = ImGui.GetIO().DeltaTime;
|
||||||
_particleSpawnTimer += deltaTime;
|
_particleSpawnTimer += deltaTime;
|
||||||
|
|
||||||
if (_particleSpawnTimer > ParticleSpawnInterval && _particles.Count < MaxParticles)
|
if (_particleSpawnTimer > ParticleSpawnInterval && _particles.Count < MaxParticles)
|
||||||
{
|
{
|
||||||
SpawnParticle(bannerSize);
|
SpawnParticle(bannerSize);
|
||||||
_particleSpawnTimer = 0f;
|
_particleSpawnTimer = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_random.NextDouble() < 0.003)
|
if (_random.NextDouble() < 0.003)
|
||||||
{
|
{
|
||||||
SpawnShootingStar(bannerSize);
|
SpawnShootingStar(bannerSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
|
||||||
for (int i = _particles.Count - 1; i >= 0; i--)
|
for (int i = _particles.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var particle = _particles[i];
|
var particle = _particles[i];
|
||||||
|
|
||||||
var screenPos = bannerStart + particle.Position;
|
var screenPos = bannerStart + particle.Position;
|
||||||
|
|
||||||
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
|
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null)
|
||||||
{
|
{
|
||||||
particle.Trail.Insert(0, particle.Position);
|
particle.Trail.Insert(0, particle.Position);
|
||||||
if (particle.Trail.Count > MaxTrailLength)
|
if (particle.Trail.Count > MaxTrailLength)
|
||||||
particle.Trail.RemoveAt(particle.Trail.Count - 1);
|
particle.Trail.RemoveAt(particle.Trail.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (particle.Type == ParticleType.TwinklingStar)
|
if (particle.Type == ParticleType.TwinklingStar)
|
||||||
{
|
{
|
||||||
particle.Twinkle += 0.005f * particle.Depth;
|
particle.Twinkle += 0.005f * particle.Depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
particle.Position += particle.Velocity * deltaTime;
|
particle.Position += particle.Velocity * deltaTime;
|
||||||
particle.Life -= deltaTime;
|
particle.Life -= deltaTime;
|
||||||
|
|
||||||
var isOutOfBounds = particle.Position.X < -50 || particle.Position.X > bannerSize.X + 50 ||
|
var isOutOfBounds = particle.Position.X < -50 || particle.Position.X > bannerSize.X + 50 ||
|
||||||
particle.Position.Y < -50 || particle.Position.Y > bannerSize.Y + 50;
|
particle.Position.Y < -50 || particle.Position.Y > bannerSize.Y + 50;
|
||||||
|
|
||||||
if (particle.Life <= 0 || (particle.Type != ParticleType.TwinklingStar && isOutOfBounds))
|
if (particle.Life <= 0 || (particle.Type != ParticleType.TwinklingStar && isOutOfBounds))
|
||||||
{
|
{
|
||||||
_particles.RemoveAt(i);
|
_particles.RemoveAt(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (particle.Type == ParticleType.TwinklingStar)
|
if (particle.Type == ParticleType.TwinklingStar)
|
||||||
{
|
{
|
||||||
if (particle.Position.X < 0 || particle.Position.X > bannerSize.X)
|
if (particle.Position.X < 0 || particle.Position.X > bannerSize.X)
|
||||||
@@ -299,11 +304,11 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
if (particle.Position.Y < 0 || particle.Position.Y > bannerSize.Y)
|
if (particle.Position.Y < 0 || particle.Position.Y > bannerSize.Y)
|
||||||
particle.Velocity = particle.Velocity with { Y = -particle.Velocity.Y };
|
particle.Velocity = particle.Velocity with { Y = -particle.Velocity.Y };
|
||||||
}
|
}
|
||||||
|
|
||||||
var fadeIn = Math.Min(1f, (particle.MaxLife - particle.Life) / 20f);
|
var fadeIn = Math.Min(1f, (particle.MaxLife - particle.Life) / 20f);
|
||||||
var fadeOut = Math.Min(1f, particle.Life / 20f);
|
var fadeOut = Math.Min(1f, particle.Life / 20f);
|
||||||
var lifeFade = Math.Min(fadeIn, fadeOut);
|
var lifeFade = Math.Min(fadeIn, fadeOut);
|
||||||
|
|
||||||
var edgeFadeX = Math.Min(
|
var edgeFadeX = Math.Min(
|
||||||
Math.Min(1f, (particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance),
|
Math.Min(1f, (particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance),
|
||||||
Math.Min(1f, (bannerSize.X - particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance)
|
Math.Min(1f, (bannerSize.X - particle.Position.X + EdgeFadeDistance) / EdgeFadeDistance)
|
||||||
@@ -313,22 +318,22 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
Math.Min(1f, (bannerSize.Y - particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance)
|
Math.Min(1f, (bannerSize.Y - particle.Position.Y + EdgeFadeDistance) / EdgeFadeDistance)
|
||||||
);
|
);
|
||||||
var edgeFade = Math.Min(edgeFadeX, edgeFadeY);
|
var edgeFade = Math.Min(edgeFadeX, edgeFadeY);
|
||||||
|
|
||||||
var baseAlpha = lifeFade * edgeFade;
|
var baseAlpha = lifeFade * edgeFade;
|
||||||
var finalAlpha = particle.Type == ParticleType.TwinklingStar
|
var finalAlpha = particle.Type == ParticleType.TwinklingStar
|
||||||
? baseAlpha * (0.6f + 0.4f * MathF.Sin(particle.Twinkle))
|
? baseAlpha * (0.6f + 0.4f * MathF.Sin(particle.Twinkle))
|
||||||
: baseAlpha;
|
: baseAlpha;
|
||||||
|
|
||||||
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null && particle.Trail.Count > 1)
|
if (particle.Type == ParticleType.ShootingStar && particle.Trail != null && particle.Trail.Count > 1)
|
||||||
{
|
{
|
||||||
var cyanColor = new Vector4(0.4f, 0.8f, 1.0f, 1.0f);
|
var cyanColor = new Vector4(0.4f, 0.8f, 1.0f, 1.0f);
|
||||||
|
|
||||||
for (int t = 1; t < particle.Trail.Count; t++)
|
for (int t = 1; t < particle.Trail.Count; t++)
|
||||||
{
|
{
|
||||||
var trailProgress = (float)t / particle.Trail.Count;
|
var trailProgress = (float)t / particle.Trail.Count;
|
||||||
var trailAlpha = Math.Min(1f, (1f - trailProgress) * finalAlpha * 1.8f);
|
var trailAlpha = Math.Min(1f, (1f - trailProgress) * finalAlpha * 1.8f);
|
||||||
var trailWidth = (1f - trailProgress) * 3f + 1f;
|
var trailWidth = (1f - trailProgress) * 3f + 1f;
|
||||||
|
|
||||||
var glowAlpha = trailAlpha * 0.4f;
|
var glowAlpha = trailAlpha * 0.4f;
|
||||||
drawList.AddLine(
|
drawList.AddLine(
|
||||||
bannerStart + particle.Trail[t - 1],
|
bannerStart + particle.Trail[t - 1],
|
||||||
@@ -336,7 +341,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.GetColorU32(cyanColor with { W = glowAlpha }),
|
ImGui.GetColorU32(cyanColor with { W = glowAlpha }),
|
||||||
trailWidth + 4f
|
trailWidth + 4f
|
||||||
);
|
);
|
||||||
|
|
||||||
drawList.AddLine(
|
drawList.AddLine(
|
||||||
bannerStart + particle.Trail[t - 1],
|
bannerStart + particle.Trail[t - 1],
|
||||||
bannerStart + particle.Trail[t],
|
bannerStart + particle.Trail[t],
|
||||||
@@ -349,61 +354,92 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
DrawTwinklingStar(drawList, screenPos, particle.Size, particle.Hue, finalAlpha, particle.Depth);
|
DrawTwinklingStar(drawList, screenPos, particle.Size, particle.Hue, finalAlpha, particle.Depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
_particles[i] = particle;
|
_particles[i] = particle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTwinklingStar(ImDrawListPtr drawList, Vector2 position, float size, float hue, float alpha, float depth)
|
private void DrawTwinklingStar(ImDrawListPtr drawList, Vector2 position, float size, float hue, float alpha,
|
||||||
|
float depth)
|
||||||
{
|
{
|
||||||
var color = HslToRgb(hue, 1.0f, 0.85f);
|
var color = HslToRgb(hue, 1.0f, 0.85f);
|
||||||
color.W = alpha;
|
color.W = alpha;
|
||||||
|
|
||||||
drawList.AddCircleFilled(position, size, ImGui.GetColorU32(color));
|
drawList.AddCircleFilled(position, size, ImGui.GetColorU32(color));
|
||||||
|
|
||||||
var glowColor = color with { W = alpha * 0.3f };
|
var glowColor = color with { W = alpha * 0.3f };
|
||||||
drawList.AddCircleFilled(position, size * (1.2f + depth * 0.3f), ImGui.GetColorU32(glowColor));
|
drawList.AddCircleFilled(position, size * (1.2f + depth * 0.3f), ImGui.GetColorU32(glowColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector4 HslToRgb(float h, float s, float l)
|
private static Vector4 HslToRgb(float h, float s, float l)
|
||||||
{
|
{
|
||||||
h = h / 360f;
|
h = h / 360f;
|
||||||
float c = (1 - MathF.Abs(2 * l - 1)) * s;
|
float c = (1 - MathF.Abs(2 * l - 1)) * s;
|
||||||
float x = c * (1 - MathF.Abs((h * 6) % 2 - 1));
|
float x = c * (1 - MathF.Abs((h * 6) % 2 - 1));
|
||||||
float m = l - c / 2;
|
float m = l - c / 2;
|
||||||
|
|
||||||
float r, g, b;
|
float r, g, b;
|
||||||
if (h < 1f / 6f) { r = c; g = x; b = 0; }
|
if (h < 1f / 6f)
|
||||||
else if (h < 2f / 6f) { r = x; g = c; b = 0; }
|
{
|
||||||
else if (h < 3f / 6f) { r = 0; g = c; b = x; }
|
r = c;
|
||||||
else if (h < 4f / 6f) { r = 0; g = x; b = c; }
|
g = x;
|
||||||
else if (h < 5f / 6f) { r = x; g = 0; b = c; }
|
b = 0;
|
||||||
else { r = c; g = 0; b = x; }
|
}
|
||||||
|
else if (h < 2f / 6f)
|
||||||
|
{
|
||||||
|
r = x;
|
||||||
|
g = c;
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
else if (h < 3f / 6f)
|
||||||
|
{
|
||||||
|
r = 0;
|
||||||
|
g = c;
|
||||||
|
b = x;
|
||||||
|
}
|
||||||
|
else if (h < 4f / 6f)
|
||||||
|
{
|
||||||
|
r = 0;
|
||||||
|
g = x;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
else if (h < 5f / 6f)
|
||||||
|
{
|
||||||
|
r = x;
|
||||||
|
g = 0;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = c;
|
||||||
|
g = 0;
|
||||||
|
b = x;
|
||||||
|
}
|
||||||
|
|
||||||
return new Vector4(r + m, g + m, b + m, 1.0f);
|
return new Vector4(r + m, g + m, b + m, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void SpawnParticle(Vector2 bannerSize)
|
private void SpawnParticle(Vector2 bannerSize)
|
||||||
{
|
{
|
||||||
var position = new Vector2(
|
var position = new Vector2(
|
||||||
(float)_random.NextDouble() * bannerSize.X,
|
(float)_random.NextDouble() * bannerSize.X,
|
||||||
(float)_random.NextDouble() * bannerSize.Y
|
(float)_random.NextDouble() * bannerSize.Y
|
||||||
);
|
);
|
||||||
|
|
||||||
var depthLayers = new[] { 0.5f, 1.0f, 1.5f };
|
var depthLayers = new[] { 0.5f, 1.0f, 1.5f };
|
||||||
var depth = depthLayers[_random.Next(depthLayers.Length)];
|
var depth = depthLayers[_random.Next(depthLayers.Length)];
|
||||||
|
|
||||||
var velocity = new Vector2(
|
var velocity = new Vector2(
|
||||||
((float)_random.NextDouble() - 0.5f) * 0.05f * depth,
|
((float)_random.NextDouble() - 0.5f) * 0.05f * depth,
|
||||||
((float)_random.NextDouble() - 0.5f) * 0.05f * depth
|
((float)_random.NextDouble() - 0.5f) * 0.05f * depth
|
||||||
);
|
);
|
||||||
|
|
||||||
var isBlue = _random.NextDouble() < 0.5;
|
var isBlue = _random.NextDouble() < 0.5;
|
||||||
var hue = isBlue ? 220f + (float)_random.NextDouble() * 30f : 270f + (float)_random.NextDouble() * 40f;
|
var hue = isBlue ? 220f + (float)_random.NextDouble() * 30f : 270f + (float)_random.NextDouble() * 40f;
|
||||||
var size = (0.5f + (float)_random.NextDouble() * 2f) * depth;
|
var size = (0.5f + (float)_random.NextDouble() * 2f) * depth;
|
||||||
var maxLife = 120f + (float)_random.NextDouble() * 60f;
|
var maxLife = 120f + (float)_random.NextDouble() * 60f;
|
||||||
|
|
||||||
_particles.Add(new Particle
|
_particles.Add(new Particle
|
||||||
{
|
{
|
||||||
Position = position,
|
Position = position,
|
||||||
@@ -418,13 +454,13 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
Hue = hue
|
Hue = hue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnShootingStar(Vector2 bannerSize)
|
private void SpawnShootingStar(Vector2 bannerSize)
|
||||||
{
|
{
|
||||||
var maxLife = 80f + (float)_random.NextDouble() * 40f;
|
var maxLife = 80f + (float)_random.NextDouble() * 40f;
|
||||||
var startX = bannerSize.X * (0.3f + (float)_random.NextDouble() * 0.6f);
|
var startX = bannerSize.X * (0.3f + (float)_random.NextDouble() * 0.6f);
|
||||||
var startY = -10f;
|
var startY = -10f;
|
||||||
|
|
||||||
_particles.Add(new Particle
|
_particles.Add(new Particle
|
||||||
{
|
{
|
||||||
Position = new Vector2(startX, startY),
|
Position = new Vector2(startX, startY),
|
||||||
@@ -442,16 +478,16 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
Hue = 270f
|
Hue = 270f
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void DrawCloseButton()
|
private void DrawCloseButton()
|
||||||
{
|
{
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
var closeWidth = 200f * ImGuiHelpers.GlobalScale;
|
var closeWidth = 200f * ImGuiHelpers.GlobalScale;
|
||||||
var closeHeight = 35f * ImGuiHelpers.GlobalScale;
|
var closeHeight = 35f * ImGuiHelpers.GlobalScale;
|
||||||
ImGui.SetCursorPosX((ImGui.GetWindowSize().X - closeWidth) / 2);
|
ImGui.SetCursorPosX((ImGui.GetWindowSize().X - closeWidth) / 2);
|
||||||
|
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 8f))
|
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 8f))
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, UIColors.Get("LightlessPurple")))
|
using (ImRaii.PushColor(ImGuiCol.Button, UIColors.Get("LightlessPurple")))
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurpleActive")))
|
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurpleActive")))
|
||||||
@@ -463,10 +499,12 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawChangelog()
|
private void DrawChangelog()
|
||||||
{
|
{
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 6f))
|
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 6f))
|
||||||
using (var child = ImRaii.Child("###ll_changelog", new Vector2(0, ImGui.GetContentRegionAvail().Y - 60), false, ImGuiWindowFlags.AlwaysVerticalScrollbar))
|
using (var child = ImRaii.Child("###ll_changelog", new Vector2(0, ImGui.GetContentRegionAvail().Y - 60), false,
|
||||||
|
ImGuiWindowFlags.AlwaysVerticalScrollbar))
|
||||||
{
|
{
|
||||||
if (!child)
|
if (!child)
|
||||||
return;
|
return;
|
||||||
@@ -476,7 +514,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
_scrollToTop = false;
|
_scrollToTop = false;
|
||||||
ImGui.SetScrollHereY(0);
|
ImGui.SetScrollHereY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PushTextWrapPos();
|
ImGui.PushTextWrapPos();
|
||||||
|
|
||||||
foreach (var entry in _changelog.Changelog)
|
foreach (var entry in _changelog.Changelog)
|
||||||
@@ -494,10 +532,10 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
: new Vector4(0.95f, 0.95f, 1.0f, 1.0f);
|
: new Vector4(0.95f, 0.95f, 1.0f, 1.0f);
|
||||||
|
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
var flags = entry.IsCurrent == true
|
var flags = entry.IsCurrent == true
|
||||||
? ImGuiTreeNodeFlags.DefaultOpen
|
? ImGuiTreeNodeFlags.DefaultOpen
|
||||||
: ImGuiTreeNodeFlags.None;
|
: ImGuiTreeNodeFlags.None;
|
||||||
|
|
||||||
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 4f))
|
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 4f))
|
||||||
using (ImRaii.PushColor(ImGuiCol.Header, UIColors.Get("ButtonDefault")))
|
using (ImRaii.PushColor(ImGuiCol.Header, UIColors.Get("ButtonDefault")))
|
||||||
using (ImRaii.PushColor(ImGuiCol.HeaderHovered, UIColors.Get("LightlessPurple")))
|
using (ImRaii.PushColor(ImGuiCol.HeaderHovered, UIColors.Get("LightlessPurple")))
|
||||||
@@ -539,7 +577,7 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(8);
|
ImGuiHelpers.ScaledDummy(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawFeatureSection(string title, Vector4 accentColor)
|
private static void DrawFeatureSection(string title, Vector4 accentColor)
|
||||||
{
|
{
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
@@ -548,24 +586,24 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
var backgroundMin = startPos + new Vector2(-8, -4);
|
var backgroundMin = startPos + new Vector2(-8, -4);
|
||||||
var backgroundMax = startPos + new Vector2(availableWidth + 8, 28);
|
var backgroundMax = startPos + new Vector2(availableWidth + 8, 28);
|
||||||
|
|
||||||
var bgColor = new Vector4(0.12f, 0.12f, 0.15f, 0.7f);
|
var bgColor = new Vector4(0.12f, 0.12f, 0.15f, 0.7f);
|
||||||
drawList.AddRectFilled(backgroundMin, backgroundMax, ImGui.GetColorU32(bgColor), 6f);
|
drawList.AddRectFilled(backgroundMin, backgroundMax, ImGui.GetColorU32(bgColor), 6f);
|
||||||
|
|
||||||
drawList.AddRectFilled(
|
drawList.AddRectFilled(
|
||||||
backgroundMin,
|
backgroundMin,
|
||||||
backgroundMin + new Vector2(4, backgroundMax.Y - backgroundMin.Y),
|
backgroundMin + new Vector2(4, backgroundMax.Y - backgroundMin.Y),
|
||||||
ImGui.GetColorU32(accentColor),
|
ImGui.GetColorU32(accentColor),
|
||||||
3f
|
3f
|
||||||
);
|
);
|
||||||
|
|
||||||
var glowColor = accentColor with { W = 0.15f };
|
var glowColor = accentColor with { W = 0.15f };
|
||||||
drawList.AddRect(
|
drawList.AddRect(
|
||||||
backgroundMin,
|
backgroundMin,
|
||||||
backgroundMax,
|
backgroundMax,
|
||||||
ImGui.GetColorU32(glowColor),
|
ImGui.GetColorU32(glowColor),
|
||||||
6f,
|
6f,
|
||||||
ImDrawFlags.None,
|
ImDrawFlags.None,
|
||||||
1.5f
|
1.5f
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -597,26 +635,4 @@ public class UpdateNotesUi : WindowMediatorSubscriberBase
|
|||||||
// Ignore - window will gracefully render with defaults
|
// Ignore - window will gracefully render with defaults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private sealed record ChangelogFile
|
|
||||||
{
|
|
||||||
public string Tagline { get; init; } = string.Empty;
|
|
||||||
public string Subline { get; init; } = string.Empty;
|
|
||||||
public List<ChangelogEntry> Changelog { get; init; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed record ChangelogEntry
|
|
||||||
{
|
|
||||||
public string Name { get; init; } = string.Empty;
|
|
||||||
public string Date { get; init; } = string.Empty;
|
|
||||||
public string Tagline { get; init; } = string.Empty;
|
|
||||||
public bool? IsCurrent { get; init; }
|
|
||||||
public string? Message { get; init; }
|
|
||||||
public List<ChangelogVersion>? Versions { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed record ChangelogVersion
|
|
||||||
{
|
|
||||||
public string Number { get; init; } = string.Empty;
|
|
||||||
public List<string> Items { get; init; } = new();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user