join syncshell draw modal

This commit is contained in:
choco
2025-12-27 23:09:29 +01:00
parent a99c1c01b0
commit 24fca31606

View File

@@ -58,6 +58,7 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private List<GroupFullInfoDto> _currentSyncshells = [];
private GroupJoinDto? _joinDto;
private GroupJoinInfoDto? _joinInfo;
private bool _joinModalOpen = true;
private readonly List<GroupJoinDto> _nearbySyncshells = [];
private DefaultPermissionsDto _ownPermissions = null!;
private readonly HashSet<string> _recentlyJoined = new(StringComparer.Ordinal);
@@ -66,6 +67,11 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private LightfinderTab _selectedTab = LightfinderTab.NearbySyncshells;
private string _userUid = string.Empty;
private const float AnimationSpeed = 6f;
private readonly Dictionary<string, float> _itemAlpha = new(StringComparer.Ordinal);
private readonly HashSet<string> _currentVisibleItems = new(StringComparer.Ordinal);
private readonly HashSet<string> _previousVisibleItems = new(StringComparer.Ordinal);
private enum LightfinderTab { NearbySyncshells, NearbyPlayers, BroadcastSettings, Help }
#if DEBUG
@@ -106,14 +112,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
_lightlessProfileManager = lightlessProfileManager;
_actorObjectService = actorObjectService;
_animatedHeader.Height = 100f;
_animatedHeader.Height = 85f;
_animatedHeader.EnableBottomGradient = true;
_animatedHeader.GradientHeight = 120f;
_animatedHeader.GradientHeight = 90f;
_animatedHeader.EnableParticles = _configService.Current.EnableParticleEffects;
IsOpen = false;
WindowBuilder.For(this)
.SetSizeConstraints(new Vector2(620, 85), new Vector2(700, 600))
.SetSizeConstraints(new Vector2(620, 85), new Vector2(700, 2000))
.Apply();
Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshNearbySyncshellsAsync().ConfigureAwait(false));
@@ -367,6 +373,52 @@ public class LightFinderUI : WindowMediatorSubscriberBase
#endregion
#region Animation Helpers
private void UpdateItemAnimations(IEnumerable<string> visibleItemIds)
{
var deltaTime = ImGui.GetIO().DeltaTime;
_previousVisibleItems.Clear();
foreach (var id in _currentVisibleItems)
_previousVisibleItems.Add(id);
_currentVisibleItems.Clear();
foreach (var id in visibleItemIds)
_currentVisibleItems.Add(id);
// Fade in new items
foreach (var id in _currentVisibleItems)
{
if (!_itemAlpha.ContainsKey(id))
_itemAlpha[id] = 0f;
_itemAlpha[id] = Math.Min(1f, _itemAlpha[id] + deltaTime * AnimationSpeed);
}
// Fade out removed items
var toRemove = new List<string>();
foreach (var (id, alpha) in _itemAlpha)
{
if (!_currentVisibleItems.Contains(id))
{
_itemAlpha[id] = Math.Max(0f, alpha - deltaTime * AnimationSpeed);
if (_itemAlpha[id] <= 0.01f)
toRemove.Add(id);
}
}
foreach (var id in toRemove)
_itemAlpha.Remove(id);
}
private float GetItemAlpha(string itemId)
{
return _itemAlpha.TryGetValue(itemId, out var alpha) ? alpha : 1f;
}
#endregion
#region Nearby Syncshells Tab
private void DrawNearbySyncshellsTab()
@@ -400,10 +452,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
var cardData = BuildSyncshellCardData();
if (cardData.Count == 0)
{
UpdateItemAnimations([]);
ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells found.");
return;
}
// Update animations for syncshell items
UpdateItemAnimations(cardData.Select(c => $"shell_{c.Shell.Group.GID}"));
if (_compactView)
DrawSyncshellGrid(cardData);
else
@@ -499,7 +555,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawSyncshellListItem(GroupJoinDto shell, string broadcasterName, bool isOwnBroadcast)
{
var itemId = $"shell_{shell.Group.GID}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(shell.Group.GID);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 74f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"ShellRow##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true);
@@ -572,7 +634,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawSyncshellCompactItem(GroupJoinDto shell, bool isOwnBroadcast)
{
var itemId = $"shell_{shell.Group.GID}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(shell.Group.GID);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 36f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"ShellCompact##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true);
@@ -655,6 +723,7 @@ public class LightFinderUI : WindowMediatorSubscriberBase
{
_joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions);
_joinInfo = info;
_joinModalOpen = true;
_ownPermissions = _apiController.DefaultPermissions.DeepClone()!;
}
}
@@ -685,65 +754,188 @@ public class LightFinderUI : WindowMediatorSubscriberBase
{
if (_joinDto == null || _joinInfo == null) return;
ImGui.Separator();
ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
ImGuiHelpers.ScaledDummy(2f);
ImGui.TextUnformatted("Suggested Permissions:");
var scale = ImGuiHelpers.GlobalScale;
DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v);
DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v);
DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v);
// if not already open
if (!ImGui.IsPopupOpen("JoinSyncshellModal"))
ImGui.OpenPopup("JoinSyncshellModal");
ImGui.NewLine();
Vector2 windowPos = ImGui.GetWindowPos();
Vector2 windowSize = ImGui.GetWindowSize();
float modalWidth = Math.Min(420f * scale, windowSize.X - 40f * scale);
float modalHeight = 295f * scale;
ImGui.SetNextWindowPos(new Vector2(
windowPos.X + (windowSize.X - modalWidth) * 0.5f,
windowPos.Y + (windowSize.Y - modalHeight) * 0.5f
), ImGuiCond.Always);
ImGui.SetNextWindowSize(new Vector2(modalWidth, modalHeight));
ImGui.PushStyleVar(ImGuiStyleVar.WindowRounding, 6f * ImGuiHelpers.GlobalScale);
ImGui.PushStyleColor(ImGuiCol.Border, Vector4.Zero);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}"))
using ImRaii.Color modalBorder = ImRaii.PushColor(ImGuiCol.Border, UIColors.Get("LightlessPurple").WithAlpha(0.5f));
using ImRaii.Style rounding = ImRaii.PushStyle(ImGuiStyleVar.WindowRounding, 8f * scale);
using ImRaii.Style borderSize = ImRaii.PushStyle(ImGuiStyleVar.WindowBorderSize, 2f * scale);
using ImRaii.Style padding = ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(16f * scale, 16f * scale));
ImGuiWindowFlags flags = ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar;
if (ImGui.BeginPopupModal("JoinSyncshellModal", ref _joinModalOpen, flags))
{
var finalPermissions = GroupUserPreferredPermissions.NoneSet;
finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds);
finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations);
finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);
float contentWidth = ImGui.GetContentRegionAvail().X;
_ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions));
_recentlyJoined.Add(_joinDto.Group.GID);
// Header
_uiSharedService.MediumText("Join Syncshell", UIColors.Get("LightlessPurple"));
ImGuiHelpers.ScaledDummy(2f);
ImGui.TextColored(ImGuiColors.DalamudGrey, $"{_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
_joinDto = null;
_joinInfo = null;
}
ImGuiHelpers.ScaledDummy(8f);
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault").WithAlpha(0.4f));
ImGuiHelpers.ScaledDummy(8f);
ImGui.SameLine();
if (ImGui.Button("Cancel"))
{
_joinDto = null;
_joinInfo = null;
// Permissions section
ImGui.TextColored(ImGuiColors.DalamudWhite, "Permissions");
ImGuiHelpers.ScaledDummy(6f);
DrawPermissionToggleRow("Sounds", FontAwesomeIcon.VolumeUp,
_joinInfo.GroupPermissions.IsPreferDisableSounds(),
_ownPermissions.DisableGroupSounds,
v => _ownPermissions.DisableGroupSounds = v,
contentWidth);
DrawPermissionToggleRow("Animations", FontAwesomeIcon.Running,
_joinInfo.GroupPermissions.IsPreferDisableAnimations(),
_ownPermissions.DisableGroupAnimations,
v => _ownPermissions.DisableGroupAnimations = v,
contentWidth);
DrawPermissionToggleRow("VFX", FontAwesomeIcon.Magic,
_joinInfo.GroupPermissions.IsPreferDisableVFX(),
_ownPermissions.DisableGroupVFX,
v => _ownPermissions.DisableGroupVFX = v,
contentWidth);
ImGuiHelpers.ScaledDummy(12f);
// Buttons
float buttonHeight = 32f * scale;
float buttonSpacing = 8f * scale;
float joinButtonWidth = (contentWidth - buttonSpacing) * 0.65f;
float cancelButtonWidth = (contentWidth - buttonSpacing) * 0.35f;
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 6f * scale))
{
// Join button
using (ImRaii.PushColor(ImGuiCol.Button, UIColors.Get("LightlessGreen")))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen").WithAlpha(0.85f)))
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreen").WithAlpha(0.75f)))
{
if (ImGui.Button($"Join Syncshell##{_joinDto.Group.GID}", new Vector2(joinButtonWidth, buttonHeight)))
{
var finalPermissions = GroupUserPreferredPermissions.NoneSet;
finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds);
finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations);
finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);
_ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions));
_recentlyJoined.Add(_joinDto.Group.GID);
_joinDto = null;
_joinInfo = null;
ImGui.CloseCurrentPopup();
}
}
ImGui.SameLine(0f, buttonSpacing);
// Cancel button
using (ImRaii.PushColor(ImGuiCol.Button, new Vector4(0.3f, 0.3f, 0.3f, 1f)))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, new Vector4(0.4f, 0.4f, 0.4f, 1f)))
using (ImRaii.PushColor(ImGuiCol.ButtonActive, new Vector4(0.25f, 0.25f, 0.25f, 1f)))
{
if (ImGui.Button("Cancel", new Vector2(cancelButtonWidth, buttonHeight)))
{
_joinDto = null;
_joinInfo = null;
ImGui.CloseCurrentPopup();
}
}
}
// Handle modal close via the bool ref
if (!_joinModalOpen)
{
_joinDto = null;
_joinInfo = null;
}
ImGui.EndPopup();
}
}
private void DrawPermissionRow(string label, bool suggested, bool current, Action<bool> apply)
private void DrawPermissionToggleRow(string label, FontAwesomeIcon icon, bool suggested, bool current, Action<bool> apply, float contentWidth)
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted($"- {label}");
var scale = ImGuiHelpers.GlobalScale;
float rowHeight = 28f * scale;
bool isDifferent = current != suggested;
ImGui.SameLine(120 * ImGuiHelpers.GlobalScale);
ImGui.Text("Current:");
ImGui.SameLine();
_uiSharedService.BooleanToColoredIcon(!current);
ImGui.SameLine(240 * ImGuiHelpers.GlobalScale);
ImGui.Text("Suggested:");
ImGui.SameLine();
_uiSharedService.BooleanToColoredIcon(!suggested);
ImGui.SameLine(380 * ImGuiHelpers.GlobalScale);
using var id = ImRaii.PushId(label);
if (current != suggested)
using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 4f * scale))
using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 4f * scale))
using (ImRaii.PushColor(ImGuiCol.ChildBg, new Vector4(0.18f, 0.15f, 0.22f, 0.6f)))
{
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply"))
apply(suggested);
}
ImGui.BeginChild($"PermRow_{label}", new Vector2(contentWidth, rowHeight), false, ImGuiWindowFlags.NoScrollbar);
ImGui.NewLine();
float innerPadding = 8f * scale;
ImGui.SetCursorPos(new Vector2(innerPadding, (rowHeight - ImGui.GetTextLineHeight()) * 0.5f));
// Icon and label
var enabledColor = UIColors.Get("LightlessGreen");
var disabledColor = UIColors.Get("DimRed");
var currentColor = !current ? enabledColor : disabledColor;
_uiSharedService.IconText(icon, currentColor);
ImGui.SameLine(0f, 6f * scale);
ImGui.TextUnformatted(label);
// Current status
ImGui.SameLine();
float statusX = contentWidth * 0.38f;
ImGui.SetCursorPosX(statusX);
ImGui.TextColored(ImGuiColors.DalamudGrey, "Current:");
ImGui.SameLine(0f, 4f * scale);
_uiSharedService.BooleanToColoredIcon(!current, false);
// Suggested status
ImGui.SameLine();
float suggestedX = contentWidth * 0.60f;
ImGui.SetCursorPosX(suggestedX);
ImGui.TextColored(ImGuiColors.DalamudGrey, "Suggested:");
ImGui.SameLine(0f, 4f * scale);
_uiSharedService.BooleanToColoredIcon(!suggested, false);
// Apply checkmark button if different
if (isDifferent)
{
ImGui.SameLine();
float applyX = contentWidth - 26f * scale;
ImGui.SetCursorPosX(applyX);
ImGui.SetCursorPosY((rowHeight - ImGui.GetFrameHeight()) * 0.5f);
using (ImRaii.PushColor(ImGuiCol.Button, UIColors.Get("LightlessGreen").WithAlpha(0.6f)))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessGreen")))
using (ImRaii.PushColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessGreenDefault")))
{
if (_uiSharedService.IconButton(FontAwesomeIcon.Check))
apply(suggested);
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Apply suggested");
}
ImGui.EndChild();
}
ImGui.Dummy(new Vector2(0, 2f * scale));
}
#endregion
#region Nearby Players Tab
@@ -785,10 +977,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
var playerData = BuildNearbyPlayerData(activeBroadcasts);
if (playerData.Count == 0)
{
UpdateItemAnimations([]);
ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby Lightfinder players found.");
return;
}
// Update animations for player items
UpdateItemAnimations(playerData.Select(p => $"player_{p.HashedCid}"));
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 8f);
ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f);
@@ -848,7 +1044,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawNearbyPlayerRow(NearbyPlayerData data)
{
var itemId = $"player_{data.HashedCid}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(data.HashedCid);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 74f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"PlayerRow##{data.HashedCid}", new Vector2(-1, rowHeight), border: true);
@@ -903,7 +1105,7 @@ public class LightFinderUI : WindowMediatorSubscriberBase
ImGui.SetCursorPosX(startX);
_uiSharedService.IconText(FontAwesomeIcon.Wifi, UIColors.Get("LightlessBlue"));
ImGui.SameLine(0f, 4f * ImGuiHelpers.GlobalScale);
ImGui.TextColored(ImGuiColors.DalamudGrey, "Lightfinder User");
ImGui.TextColored(ImGuiColors.DalamudGrey, "Lightfinder sir ma'am or whatever");
}
ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
@@ -916,7 +1118,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawNearbyPlayerCompactRow(NearbyPlayerData data)
{
var itemId = $"player_{data.HashedCid}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(data.HashedCid);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 36f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"PlayerCompact##{data.HashedCid}", new Vector2(-1, rowHeight), border: true);