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 List<GroupFullInfoDto> _currentSyncshells = [];
private GroupJoinDto? _joinDto; private GroupJoinDto? _joinDto;
private GroupJoinInfoDto? _joinInfo; private GroupJoinInfoDto? _joinInfo;
private bool _joinModalOpen = true;
private readonly List<GroupJoinDto> _nearbySyncshells = []; private readonly List<GroupJoinDto> _nearbySyncshells = [];
private DefaultPermissionsDto _ownPermissions = null!; private DefaultPermissionsDto _ownPermissions = null!;
private readonly HashSet<string> _recentlyJoined = new(StringComparer.Ordinal); private readonly HashSet<string> _recentlyJoined = new(StringComparer.Ordinal);
@@ -66,6 +67,11 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private LightfinderTab _selectedTab = LightfinderTab.NearbySyncshells; private LightfinderTab _selectedTab = LightfinderTab.NearbySyncshells;
private string _userUid = string.Empty; 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 } private enum LightfinderTab { NearbySyncshells, NearbyPlayers, BroadcastSettings, Help }
#if DEBUG #if DEBUG
@@ -106,14 +112,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
_lightlessProfileManager = lightlessProfileManager; _lightlessProfileManager = lightlessProfileManager;
_actorObjectService = actorObjectService; _actorObjectService = actorObjectService;
_animatedHeader.Height = 100f; _animatedHeader.Height = 85f;
_animatedHeader.EnableBottomGradient = true; _animatedHeader.EnableBottomGradient = true;
_animatedHeader.GradientHeight = 120f; _animatedHeader.GradientHeight = 90f;
_animatedHeader.EnableParticles = _configService.Current.EnableParticleEffects; _animatedHeader.EnableParticles = _configService.Current.EnableParticleEffects;
IsOpen = false; IsOpen = false;
WindowBuilder.For(this) WindowBuilder.For(this)
.SetSizeConstraints(new Vector2(620, 85), new Vector2(700, 600)) .SetSizeConstraints(new Vector2(620, 85), new Vector2(700, 2000))
.Apply(); .Apply();
Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshNearbySyncshellsAsync().ConfigureAwait(false)); Mediator.Subscribe<SyncshellBroadcastsUpdatedMessage>(this, async _ => await RefreshNearbySyncshellsAsync().ConfigureAwait(false));
@@ -367,6 +373,52 @@ public class LightFinderUI : WindowMediatorSubscriberBase
#endregion #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 #region Nearby Syncshells Tab
private void DrawNearbySyncshellsTab() private void DrawNearbySyncshellsTab()
@@ -400,10 +452,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
var cardData = BuildSyncshellCardData(); var cardData = BuildSyncshellCardData();
if (cardData.Count == 0) if (cardData.Count == 0)
{ {
UpdateItemAnimations([]);
ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells found."); ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby syncshells found.");
return; return;
} }
// Update animations for syncshell items
UpdateItemAnimations(cardData.Select(c => $"shell_{c.Shell.Group.GID}"));
if (_compactView) if (_compactView)
DrawSyncshellGrid(cardData); DrawSyncshellGrid(cardData);
else else
@@ -499,7 +555,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawSyncshellListItem(GroupJoinDto shell, string broadcasterName, bool isOwnBroadcast) 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); ImGui.PushID(shell.Group.GID);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 74f * ImGuiHelpers.GlobalScale; float rowHeight = 74f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"ShellRow##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true); 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) 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); ImGui.PushID(shell.Group.GID);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 36f * ImGuiHelpers.GlobalScale; float rowHeight = 36f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"ShellCompact##{shell.Group.GID}", new Vector2(-1, rowHeight), border: true); 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); _joinDto = new GroupJoinDto(shell.Group, shell.Password, shell.GroupUserPreferredPermissions);
_joinInfo = info; _joinInfo = info;
_joinModalOpen = true;
_ownPermissions = _apiController.DefaultPermissions.DeepClone()!; _ownPermissions = _apiController.DefaultPermissions.DeepClone()!;
} }
} }
@@ -685,65 +754,188 @@ public class LightFinderUI : WindowMediatorSubscriberBase
{ {
if (_joinDto == null || _joinInfo == null) return; if (_joinDto == null || _joinInfo == null) return;
ImGui.Separator(); var scale = ImGuiHelpers.GlobalScale;
ImGui.TextUnformatted($"Join Syncshell: {_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
ImGuiHelpers.ScaledDummy(2f); // if not already open
ImGui.TextUnformatted("Suggested Permissions:"); if (!ImGui.IsPopupOpen("JoinSyncshellModal"))
ImGui.OpenPopup("JoinSyncshellModal");
DrawPermissionRow("Sounds", _joinInfo.GroupPermissions.IsPreferDisableSounds(), _ownPermissions.DisableGroupSounds, v => _ownPermissions.DisableGroupSounds = v); Vector2 windowPos = ImGui.GetWindowPos();
DrawPermissionRow("Animations", _joinInfo.GroupPermissions.IsPreferDisableAnimations(), _ownPermissions.DisableGroupAnimations, v => _ownPermissions.DisableGroupAnimations = v); Vector2 windowSize = ImGui.GetWindowSize();
DrawPermissionRow("VFX", _joinInfo.GroupPermissions.IsPreferDisableVFX(), _ownPermissions.DisableGroupVFX, v => _ownPermissions.DisableGroupVFX = v); float modalWidth = Math.Min(420f * scale, windowSize.X - 40f * scale);
float modalHeight = 295f * scale;
ImGui.NewLine(); ImGui.SetNextWindowPos(new Vector2(
windowPos.X + (windowSize.X - modalWidth) * 0.5f,
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, $"Finalize and join {_joinDto.Group.AliasOrGID}")) 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);
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; float contentWidth = ImGui.GetContentRegionAvail().X;
finalPermissions.SetDisableSounds(_ownPermissions.DisableGroupSounds);
finalPermissions.SetDisableAnimations(_ownPermissions.DisableGroupAnimations);
finalPermissions.SetDisableVFX(_ownPermissions.DisableGroupVFX);
_ = _apiController.GroupJoinFinalize(new GroupJoinDto(_joinDto.Group, _joinDto.Password, finalPermissions)); // Header
_recentlyJoined.Add(_joinDto.Group.GID); _uiSharedService.MediumText("Join Syncshell", UIColors.Get("LightlessPurple"));
ImGuiHelpers.ScaledDummy(2f);
ImGui.TextColored(ImGuiColors.DalamudGrey, $"{_joinDto.Group.AliasOrGID} by {_joinInfo.OwnerAliasOrUID}");
_joinDto = null; ImGuiHelpers.ScaledDummy(8f);
_joinInfo = null; UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault").WithAlpha(0.4f));
} ImGuiHelpers.ScaledDummy(8f);
ImGui.SameLine(); // Permissions section
if (ImGui.Button("Cancel")) ImGui.TextColored(ImGuiColors.DalamudWhite, "Permissions");
{ ImGuiHelpers.ScaledDummy(6f);
_joinDto = null;
_joinInfo = null; 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(); var scale = ImGuiHelpers.GlobalScale;
ImGui.TextUnformatted($"- {label}"); float rowHeight = 28f * scale;
bool isDifferent = current != suggested;
ImGui.SameLine(120 * ImGuiHelpers.GlobalScale);
ImGui.Text("Current:"); using (ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 4f * scale))
ImGui.SameLine(); using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 4f * scale))
_uiSharedService.BooleanToColoredIcon(!current); using (ImRaii.PushColor(ImGuiCol.ChildBg, new Vector4(0.18f, 0.15f, 0.22f, 0.6f)))
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)
{ {
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply")) ImGui.BeginChild($"PermRow_{label}", new Vector2(contentWidth, rowHeight), false, ImGuiWindowFlags.NoScrollbar);
apply(suggested);
} float innerPadding = 8f * scale;
ImGui.SetCursorPos(new Vector2(innerPadding, (rowHeight - ImGui.GetTextLineHeight()) * 0.5f));
ImGui.NewLine(); // 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 #endregion
#region Nearby Players Tab #region Nearby Players Tab
@@ -785,10 +977,14 @@ public class LightFinderUI : WindowMediatorSubscriberBase
var playerData = BuildNearbyPlayerData(activeBroadcasts); var playerData = BuildNearbyPlayerData(activeBroadcasts);
if (playerData.Count == 0) if (playerData.Count == 0)
{ {
UpdateItemAnimations([]);
ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby Lightfinder players found."); ImGui.TextColored(ImGuiColors.DalamudGrey, "No nearby Lightfinder players found.");
return; return;
} }
// Update animations for player items
UpdateItemAnimations(playerData.Select(p => $"player_{p.HashedCid}"));
ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 8f); ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 8f);
ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f); ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1f);
@@ -848,7 +1044,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawNearbyPlayerRow(NearbyPlayerData data) private void DrawNearbyPlayerRow(NearbyPlayerData data)
{ {
var itemId = $"player_{data.HashedCid}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(data.HashedCid); ImGui.PushID(data.HashedCid);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 74f * ImGuiHelpers.GlobalScale; float rowHeight = 74f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"PlayerRow##{data.HashedCid}", new Vector2(-1, rowHeight), border: true); ImGui.BeginChild($"PlayerRow##{data.HashedCid}", new Vector2(-1, rowHeight), border: true);
@@ -903,7 +1105,7 @@ public class LightFinderUI : WindowMediatorSubscriberBase
ImGui.SetCursorPosX(startX); ImGui.SetCursorPosX(startX);
_uiSharedService.IconText(FontAwesomeIcon.Wifi, UIColors.Get("LightlessBlue")); _uiSharedService.IconText(FontAwesomeIcon.Wifi, UIColors.Get("LightlessBlue"));
ImGui.SameLine(0f, 4f * ImGuiHelpers.GlobalScale); 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)); ImGui.Dummy(new Vector2(0, 4 * ImGuiHelpers.GlobalScale));
@@ -916,7 +1118,13 @@ public class LightFinderUI : WindowMediatorSubscriberBase
private void DrawNearbyPlayerCompactRow(NearbyPlayerData data) private void DrawNearbyPlayerCompactRow(NearbyPlayerData data)
{ {
var itemId = $"player_{data.HashedCid}";
var alpha = GetItemAlpha(itemId);
if (alpha <= 0.01f)
return;
ImGui.PushID(data.HashedCid); ImGui.PushID(data.HashedCid);
using var alphaStyle = ImRaii.PushStyle(ImGuiStyleVar.Alpha, alpha);
float rowHeight = 36f * ImGuiHelpers.GlobalScale; float rowHeight = 36f * ImGuiHelpers.GlobalScale;
ImGui.BeginChild($"PlayerCompact##{data.HashedCid}", new Vector2(-1, rowHeight), border: true); ImGui.BeginChild($"PlayerCompact##{data.HashedCid}", new Vector2(-1, rowHeight), border: true);