using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Utility; using LightlessSync.API.Data.Enum; using LightlessSync.API.Data.Extensions; using LightlessSync.LightlessConfiguration.Models; using LightlessSync.PlayerData.Pairs; using LightlessSync.Services; using LightlessSync.Services.Mediator; using LightlessSync.Utils; using LightlessSync.WebAPI; using System.Numerics; namespace LightlessSync.UI; public class TopTabMenu { private readonly ApiController _apiController; private readonly LightlessMediator _lightlessMediator; private readonly PairManager _pairManager; private readonly PairRequestService _pairRequestService; private readonly DalamudUtilService _dalamudUtilService; private readonly HashSet _pendingPairRequestActions = new(StringComparer.Ordinal); private bool _pairRequestsExpanded; // useless for now private int _lastRequestCount; private readonly UiSharedService _uiSharedService; private readonly NotificationService _lightlessNotificationService; private string _filter = string.Empty; private int _globalControlCountdown = 0; private float _pairRequestsHeight = 150f; private string _pairToAdd = string.Empty; private SelectedTab _selectedTab = SelectedTab.None; public TopTabMenu(LightlessMediator lightlessMediator, ApiController apiController, PairManager pairManager, UiSharedService uiSharedService, PairRequestService pairRequestService, DalamudUtilService dalamudUtilService, NotificationService lightlessNotificationService) { _lightlessMediator = lightlessMediator; _apiController = apiController; _pairManager = pairManager; _pairRequestService = pairRequestService; _dalamudUtilService = dalamudUtilService; _uiSharedService = uiSharedService; _lightlessNotificationService = lightlessNotificationService; } private enum SelectedTab { None, Individual, Syncshell, Lightfinder, UserConfig, Settings } public string Filter { get => _filter; private set { if (!string.Equals(_filter, value, StringComparison.OrdinalIgnoreCase)) { _lightlessMediator.Publish(new RefreshUiMessage()); } _filter = value; } } private SelectedTab TabSelection { get => _selectedTab; set { _selectedTab = value; } } public void Draw() { var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; var spacing = ImGui.GetStyle().ItemSpacing; var buttonX = (availableWidth - (spacing.X * 4)) / 5f; var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; var buttonSize = new Vector2(buttonX, buttonY); var drawList = ImGui.GetWindowDrawList(); var underlineColor = ImGui.GetColorU32(UIColors.Get("LightlessPurpleActive")); // ImGui.GetColorU32(ImGuiCol.Separator); var btncolor = ImRaii.PushColor(ImGuiCol.Button, ImGui.ColorConvertFloat4ToU32(new(0, 0, 0, 0))); ImGuiHelpers.ScaledDummy(spacing.Y / 2f); using (ImRaii.PushFont(UiBuilder.IconFont)) { var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.User.ToIconString(), buttonSize)) { TabSelection = TabSelection == SelectedTab.Individual ? SelectedTab.None : SelectedTab.Individual; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); if (TabSelection == SelectedTab.Individual) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); } UiSharedService.AttachToolTip("Individual Pair Menu"); using (ImRaii.PushFont(UiBuilder.IconFont)) { var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.Users.ToIconString(), buttonSize)) { TabSelection = TabSelection == SelectedTab.Syncshell ? SelectedTab.None : SelectedTab.Syncshell; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); if (TabSelection == SelectedTab.Syncshell) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); } UiSharedService.AttachToolTip("Syncshell Menu"); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.Compass.ToIconString(), buttonSize)) { TabSelection = TabSelection == SelectedTab.Lightfinder ? SelectedTab.None : SelectedTab.Lightfinder; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); if (TabSelection == SelectedTab.Lightfinder) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); } UiSharedService.AttachToolTip("Lightfinder"); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.UserCog.ToIconString(), buttonSize)) { TabSelection = TabSelection == SelectedTab.UserConfig ? SelectedTab.None : SelectedTab.UserConfig; } ImGui.SameLine(); var xAfter = ImGui.GetCursorScreenPos(); if (TabSelection == SelectedTab.UserConfig) drawList.AddLine(x with { Y = x.Y + buttonSize.Y + spacing.Y }, xAfter with { Y = xAfter.Y + buttonSize.Y + spacing.Y, X = xAfter.X - spacing.X }, underlineColor, 2); } UiSharedService.AttachToolTip("Your User Menu"); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { var x = ImGui.GetCursorScreenPos(); if (ImGui.Button(FontAwesomeIcon.Cog.ToIconString(), buttonSize)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(SettingsUi))); } ImGui.SameLine(); } UiSharedService.AttachToolTip("Open Lightless Settings"); ImGui.NewLine(); btncolor.Dispose(); ImGuiHelpers.ScaledDummy(spacing); if (TabSelection == SelectedTab.Individual) { DrawAddPair(availableWidth, spacing.X); DrawGlobalIndividualButtons(availableWidth, spacing.X); } else if (TabSelection == SelectedTab.Syncshell) { DrawSyncshellMenu(availableWidth, spacing.X); DrawGlobalSyncshellButtons(availableWidth, spacing.X); } else if (TabSelection == SelectedTab.Lightfinder) { DrawLightfinderMenu(availableWidth, spacing.X); } else if (TabSelection == SelectedTab.UserConfig) { DrawUserConfig(availableWidth, spacing.X); } if (TabSelection != SelectedTab.None) ImGuiHelpers.ScaledDummy(3f); #if DEBUG if (ImGui.Button("Test Pair Request")) { _lightlessNotificationService.ShowPairRequestNotification( "Debug User", "debug-user-id", onAccept: () => { _lightlessMediator.Publish(new NotificationMessage( "Pair Accepted", "Debug pair request was accepted!", NotificationType.Info, TimeSpan.FromSeconds(3))); }, onDecline: () => { _lightlessMediator.Publish(new NotificationMessage( "Pair Declined", "Debug pair request was declined.", NotificationType.Warning, TimeSpan.FromSeconds(3))); } ); } ImGui.SameLine(); if (ImGui.Button("Test Info")) { _lightlessMediator.Publish(new NotificationMessage( "Information", "This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps. This is a test ifno notification with some longer text to see how it wraps.", NotificationType.Info, TimeSpan.FromSeconds(5))); } ImGui.SameLine(); if (ImGui.Button("Test Warning")) { _lightlessMediator.Publish(new NotificationMessage( "Warning", "This is a test warning notification.", NotificationType.Warning, TimeSpan.FromSeconds(7))); } ImGui.SameLine(); if (ImGui.Button("Test Error")) { _lightlessMediator.Publish(new NotificationMessage( "Error", "This is a test error notification erp police", NotificationType.Error, TimeSpan.FromSeconds(10))); } if (ImGui.Button("Test Download Progress")) { var downloadStatus = new List<(string playerName, float progress, string status)> { ("Mauwmauw Nekochan", 0.85f, "downloading"), ("Raelynn Kitsune", 0.34f, "downloading"), ("Jaina Elraeth", 0.67f, "downloading"), ("Vaelstra Bloodthorn", 0.19f, "downloading"), ("Lydia Hera Moondrop", 0.86f, "downloading"), ("C'liina Star", 1.0f, "completed") }; _lightlessNotificationService.ShowPairDownloadNotification(downloadStatus); } ImGui.SameLine(); if (ImGui.Button("Dismiss Download")) { _lightlessNotificationService.DismissPairDownloadNotification(); } #endif DrawIncomingPairRequests(availableWidth); ImGui.Separator(); DrawFilter(availableWidth, spacing.X); } private void DrawAddPair(float availableXWidth, float spacingX) { var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.UserPlus, "Add"); ImGui.SetNextItemWidth(availableXWidth - buttonSize - spacingX); ImGui.InputTextWithHint("##otheruid", "Other players UID/Alias", ref _pairToAdd, 20); ImGui.SameLine(); var alreadyExisting = _pairManager.DirectPairs.Exists(p => string.Equals(p.UserData.UID, _pairToAdd, StringComparison.Ordinal) || string.Equals(p.UserData.Alias, _pairToAdd, StringComparison.Ordinal)); using (ImRaii.Disabled(alreadyExisting || string.IsNullOrEmpty(_pairToAdd))) { if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserPlus, "Add")) { _ = _apiController.UserAddPair(new(new(_pairToAdd))); _pairToAdd = string.Empty; } } UiSharedService.AttachToolTip("Pair with " + (_pairToAdd.IsNullOrEmpty() ? "other user" : _pairToAdd)); } private void DrawIncomingPairRequests(float availableWidth) { var requests = _pairRequestService.GetActiveRequests(); var count = requests.Count; if (count == 0) { _pairRequestsExpanded = false; _lastRequestCount = 0; return; } if (count > _lastRequestCount) { _pairRequestsExpanded = true; } _lastRequestCount = count; var label = $"Incoming Pair Requests - {count}##IncomingPairRequestsHeader"; using (ImRaii.PushColor(ImGuiCol.Header, UIColors.Get("LightlessPurple"))) using (ImRaii.PushColor(ImGuiCol.HeaderHovered, UIColors.Get("LightlessPurpleActive"))) using (ImRaii.PushColor(ImGuiCol.HeaderActive, UIColors.Get("LightlessPurple"))) { bool open = ImGui.CollapsingHeader(label, ImGuiTreeNodeFlags.DefaultOpen); _pairRequestsExpanded = open; if (ImGui.IsItemHovered()) UiSharedService.AttachToolTip("Expand to view incoming pair requests."); if (open) { var lineHeight = ImGui.GetTextLineHeightWithSpacing(); //var desiredHeight = Math.Clamp(count * lineHeight * 2f, 130f * ImGuiHelpers.GlobalScale, 185f * ImGuiHelpers.GlobalScale); we use resize bar instead ImGui.SetCursorPosX(ImGui.GetCursorPosX() - 2f); using (ImRaii.PushColor(ImGuiCol.ChildBg, UIColors.Get("LightlessPurple"))) using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 6f)) { if (ImGui.BeginChild("##IncomingPairRequestsOuter", new Vector2(availableWidth + 5f, _pairRequestsHeight), true)) { var defaultChildBg = ImGui.GetStyle().Colors[(int)ImGuiCol.WindowBg]; using (ImRaii.PushColor(ImGuiCol.ChildBg, defaultChildBg)) using (ImRaii.PushStyle(ImGuiStyleVar.ChildRounding, 5f)) using (ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(6, 6))) { if (ImGui.BeginChild("##IncomingPairRequestsInner", new Vector2(0, 0), true)) { using (ImRaii.PushColor(ImGuiCol.TableBorderStrong, ImGui.GetStyle().Colors[(int)ImGuiCol.Border])) using (ImRaii.PushColor(ImGuiCol.TableBorderLight, ImGui.GetStyle().Colors[(int)ImGuiCol.Border])) { DrawPairRequestList(requests); } } ImGui.EndChild(); } } ImGui.EndChild(); ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("ButtonDefault")); ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple")); ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive")); ImGui.Button("##resizeHandle", new Vector2(availableWidth, 4f)); ImGui.PopStyleColor(3); if (ImGui.IsItemActive()) { _pairRequestsHeight += ImGui.GetIO().MouseDelta.Y; _pairRequestsHeight = Math.Clamp(_pairRequestsHeight, 100f, 300f); } } } } } private void DrawPairRequestList(IReadOnlyList requests) { float playerColWidth = 207f * ImGuiHelpers.GlobalScale; float receivedColWidth = 73f * ImGuiHelpers.GlobalScale; float actionsColWidth = 50f * ImGuiHelpers.GlobalScale; ImGui.Separator(); ImGui.TextUnformatted("Player"); ImGui.SameLine(playerColWidth + 2f); ImGui.TextUnformatted("Received"); ImGui.SameLine(playerColWidth + receivedColWidth + 12f); ImGui.TextUnformatted("Actions"); ImGui.Separator(); foreach (var request in requests) { ImGui.BeginGroup(); var label = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName; ImGui.TextUnformatted(label.Truncate(26)); if (ImGui.IsItemHovered()) { ImGui.BeginTooltip(); ImGui.TextUnformatted(label); ImGui.EndTooltip(); } ImGui.SameLine(playerColWidth); ImGui.TextUnformatted(GetRelativeTime(request.ReceivedAt)); ImGui.SameLine(playerColWidth + receivedColWidth); DrawPairRequestActions(request); ImGui.EndGroup(); } } private void DrawPairRequestActions(PairRequestService.PairRequestDisplay request) { using var id = ImRaii.PushId(request.HashedCid); var label = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName; var inFlight = _pendingPairRequestActions.Contains(request.HashedCid); using (ImRaii.Disabled(inFlight)) { if (_uiSharedService.IconButton(FontAwesomeIcon.Check)) { _ = AcceptPairRequestAsync(request); } if (ImGui.IsItemHovered()) ImGui.SetTooltip("Accept request"); ImGui.SameLine(); if (_uiSharedService.IconButton(FontAwesomeIcon.Times)) { RejectPairRequest(request.HashedCid, label); } if (ImGui.IsItemHovered()) ImGui.SetTooltip("Decline request"); } } private static string GetRelativeTime(DateTime receivedAt) { var delta = DateTime.UtcNow - receivedAt; if (delta <= TimeSpan.FromSeconds(10)) { return "Just now"; } if (delta.TotalMinutes >= 1) { return $"{Math.Floor(delta.TotalMinutes)}m {delta.Seconds:D2}s ago"; } return $"{delta.Seconds}s ago"; } private async Task AcceptPairRequestAsync(PairRequestService.PairRequestDisplay request) { if (!_pendingPairRequestActions.Add(request.HashedCid)) { return; } try { var myCidHash = (await _dalamudUtilService.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256(); await _apiController.TryPairWithContentId(request.HashedCid, myCidHash).ConfigureAwait(false); _pairRequestService.RemoveRequest(request.HashedCid); var display = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName; _lightlessMediator.Publish(new NotificationMessage( "Pair request accepted", $"Sent a pair request back to {display}.", NotificationType.Info, TimeSpan.FromSeconds(3))); } catch (Exception ex) { _lightlessMediator.Publish(new NotificationMessage( "Failed to accept pair request", ex.Message, NotificationType.Error, TimeSpan.FromSeconds(5))); } finally { _pendingPairRequestActions.Remove(request.HashedCid); } } private void RejectPairRequest(string hashedCid, string playerName) { if (!_pairRequestService.RemoveRequest(hashedCid)) { return; } _lightlessMediator.Publish(new NotificationMessage("Pair request declined", "Declined " + playerName + "'s pending pair request.", NotificationType.Info, TimeSpan.FromSeconds(3))); } private void DrawFilter(float availableWidth, float spacingX) { var buttonSize = _uiSharedService.GetIconTextButtonSize(FontAwesomeIcon.Ban, "Clear"); ImGui.SetNextItemWidth(availableWidth - buttonSize - spacingX); string filter = Filter; if (ImGui.InputTextWithHint("##filter", "Filter for UID/notes", ref filter, 255)) { Filter = filter; } ImGui.SameLine(); using var disabled = ImRaii.Disabled(string.IsNullOrEmpty(Filter)); if (_uiSharedService.IconTextButton(FontAwesomeIcon.Ban, "Clear")) { Filter = string.Empty; } } private void DrawGlobalIndividualButtons(float availableXWidth, float spacingX) { var buttonX = (availableXWidth - (spacingX * 3)) / 4f; var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; var buttonSize = new Vector2(buttonX, buttonY); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize)) { ImGui.OpenPopup("Individual Pause"); } } UiSharedService.AttachToolTip("Globally resume or pause all individual pairs." + UiSharedService.TooltipSeparator + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize)) { ImGui.OpenPopup("Individual Sounds"); } } UiSharedService.AttachToolTip("Globally enable or disable sound sync with all individual pairs." + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize)) { ImGui.OpenPopup("Individual Animations"); } } UiSharedService.AttachToolTip("Globally enable or disable animation sync with all individual pairs." + UiSharedService.TooltipSeparator + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize)) { ImGui.OpenPopup("Individual VFX"); } } UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all individual pairs." + UiSharedService.TooltipSeparator + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); PopupIndividualSetting("Individual Pause", "Unpause all individuals", "Pause all individuals", FontAwesomeIcon.Play, FontAwesomeIcon.Pause, (perm) => { perm.SetPaused(false); return perm; }, (perm) => { perm.SetPaused(true); return perm; }); PopupIndividualSetting("Individual Sounds", "Enable sounds for all individuals", "Disable sounds for all individuals", FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute, (perm) => { perm.SetDisableSounds(false); return perm; }, (perm) => { perm.SetDisableSounds(true); return perm; }); PopupIndividualSetting("Individual Animations", "Enable animations for all individuals", "Disable animations for all individuals", FontAwesomeIcon.Running, FontAwesomeIcon.Stop, (perm) => { perm.SetDisableAnimations(false); return perm; }, (perm) => { perm.SetDisableAnimations(true); return perm; }); PopupIndividualSetting("Individual VFX", "Enable VFX for all individuals", "Disable VFX for all individuals", FontAwesomeIcon.Sun, FontAwesomeIcon.Circle, (perm) => { perm.SetDisableVFX(false); return perm; }, (perm) => { perm.SetDisableVFX(true); return perm; }); } private void DrawGlobalSyncshellButtons(float availableXWidth, float spacingX) { var buttonX = (availableXWidth - (spacingX * 4)) / 5f; var buttonY = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause).Y; var buttonSize = new Vector2(buttonX, buttonY); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Pause.ToIconString(), buttonSize)) { ImGui.OpenPopup("Syncshell Pause"); } } UiSharedService.AttachToolTip("Globally resume or pause all syncshells." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.VolumeUp.ToIconString(), buttonSize)) { ImGui.OpenPopup("Syncshell Sounds"); } } UiSharedService.AttachToolTip("Globally enable or disable sound sync with all syncshells." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Running.ToIconString(), buttonSize)) { ImGui.OpenPopup("Syncshell Animations"); } } UiSharedService.AttachToolTip("Globally enable or disable animation sync with all syncshells." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0); if (ImGui.Button(FontAwesomeIcon.Sun.ToIconString(), buttonSize)) { ImGui.OpenPopup("Syncshell VFX"); } } UiSharedService.AttachToolTip("Globally enable or disable VFX sync with all syncshells." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); PopupSyncshellSetting("Syncshell Pause", "Unpause all syncshells", "Pause all syncshells", FontAwesomeIcon.Play, FontAwesomeIcon.Pause, (perm) => { perm.SetPaused(false); return perm; }, (perm) => { perm.SetPaused(true); return perm; }); PopupSyncshellSetting("Syncshell Sounds", "Enable sounds for all syncshells", "Disable sounds for all syncshells", FontAwesomeIcon.VolumeUp, FontAwesomeIcon.VolumeMute, (perm) => { perm.SetDisableSounds(false); return perm; }, (perm) => { perm.SetDisableSounds(true); return perm; }); PopupSyncshellSetting("Syncshell Animations", "Enable animations for all syncshells", "Disable animations for all syncshells", FontAwesomeIcon.Running, FontAwesomeIcon.Stop, (perm) => { perm.SetDisableAnimations(false); return perm; }, (perm) => { perm.SetDisableAnimations(true); return perm; }); PopupSyncshellSetting("Syncshell VFX", "Enable VFX for all syncshells", "Disable VFX for all syncshells", FontAwesomeIcon.Sun, FontAwesomeIcon.Circle, (perm) => { perm.SetDisableVFX(false); return perm; }, (perm) => { perm.SetDisableVFX(true); return perm; }); ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { using var disabled = ImRaii.Disabled(_globalControlCountdown > 0 || !UiSharedService.CtrlPressed()); if (ImGui.Button(FontAwesomeIcon.Check.ToIconString(), buttonSize)) { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys.OrderBy(g => g.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Group.GID, g => { var perm = g.GroupUserPermissions; perm.SetDisableSounds(g.GroupPermissions.IsPreferDisableSounds()); perm.SetDisableAnimations(g.GroupPermissions.IsPreferDisableAnimations()); perm.SetDisableVFX(g.GroupPermissions.IsPreferDisableVFX()); return perm; }, StringComparer.Ordinal); _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); } } UiSharedService.AttachToolTip("Globally align syncshell permissions to suggested syncshell permissions." + UiSharedService.TooltipSeparator + "Note: This will not affect users with preferred permissions in syncshells." + Environment.NewLine + "Note: If multiple users share one syncshell the permissions to that user will be set to " + Environment.NewLine + "the ones of the last applied syncshell in alphabetical order." + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button" + (_globalControlCountdown > 0 ? UiSharedService.TooltipSeparator + "Available again in " + _globalControlCountdown + " seconds." : string.Empty)); } private void DrawSyncshellMenu(float availableWidth, float spacingX) { var buttonX = (availableWidth - (spacingX)) / 2f; using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct() .Count(g => string.Equals(g.OwnerUID, _apiController.UID, StringComparison.Ordinal)) >= _apiController.ServerInfo.MaxGroupsCreatedByUser)) { if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Create new Syncshell", buttonX)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(CreateSyncshellUI))); } ImGui.SameLine(); } using (ImRaii.Disabled(_pairManager.GroupPairs.Select(k => k.Key).Distinct().Count() >= _apiController.ServerInfo.MaxGroupsJoinedByUser)) { if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Join existing Syncshell", buttonX)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(JoinSyncshellUI))); } } } private void DrawLightfinderMenu(float availableWidth, float spacingX) { var buttonX = (availableWidth - (spacingX)) / 2f; if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCirclePlus, "Lightfinder", buttonX, center: true)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(BroadcastUI))); } ImGui.SameLine(); if (_uiSharedService.IconTextButton(FontAwesomeIcon.Globe, "Syncshell Finder", buttonX, center: true)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(SyncshellFinderUI))); } } private void DrawUserConfig(float availableWidth, float spacingX) { var buttonX = (availableWidth - spacingX) / 2f; if (_uiSharedService.IconTextButton(FontAwesomeIcon.UserCircle, "Edit Lightless Profile", buttonX)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(EditProfileUi))); } UiSharedService.AttachToolTip("Edit your Lightless Profile"); ImGui.SameLine(); if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Chara Data Analysis", buttonX)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi))); } UiSharedService.AttachToolTip("View and analyze your generated character data"); if (_uiSharedService.IconTextButton(FontAwesomeIcon.Running, "Character Data Hub", availableWidth)) { _lightlessMediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi))); } } private async Task GlobalControlCountdown(int countdown) { #if DEBUG return; #endif _globalControlCountdown = countdown; while (_globalControlCountdown > 0) { await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); _globalControlCountdown--; } } private void PopupIndividualSetting(string popupTitle, string enableText, string disableText, FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon, Func actEnable, Func actDisable) { if (ImGui.BeginPopup(popupTitle)) { if (_uiSharedService.IconTextButton(enableIcon, enableText, null, true)) { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys .Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional) .ToDictionary(g => g.UserPair.User.UID, g => { return actEnable(g.UserPair.OwnPermissions); }, StringComparer.Ordinal); _ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false); ImGui.CloseCurrentPopup(); } if (_uiSharedService.IconTextButton(disableIcon, disableText, null, true)) { _ = GlobalControlCountdown(10); var bulkIndividualPairs = _pairManager.PairsWithGroups.Keys .Where(g => g.IndividualPairStatus == IndividualPairStatus.Bidirectional) .ToDictionary(g => g.UserPair.User.UID, g => { return actDisable(g.UserPair.OwnPermissions); }, StringComparer.Ordinal); _ = _apiController.SetBulkPermissions(new(bulkIndividualPairs, new(StringComparer.Ordinal))).ConfigureAwait(false); ImGui.CloseCurrentPopup(); } ImGui.EndPopup(); } } private void PopupSyncshellSetting(string popupTitle, string enableText, string disableText, FontAwesomeIcon enableIcon, FontAwesomeIcon disableIcon, Func actEnable, Func actDisable) { if (ImGui.BeginPopup(popupTitle)) { if (_uiSharedService.IconTextButton(enableIcon, enableText, null, true)) { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys .OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Group.GID, g => { return actEnable(g.GroupUserPermissions); }, StringComparer.Ordinal); _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); ImGui.CloseCurrentPopup(); } if (_uiSharedService.IconTextButton(disableIcon, disableText, null, true)) { _ = GlobalControlCountdown(10); var bulkSyncshells = _pairManager.GroupPairs.Keys .OrderBy(u => u.GroupAliasOrGID, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Group.GID, g => { return actDisable(g.GroupUserPermissions); }, StringComparer.Ordinal); _ = _apiController.SetBulkPermissions(new(new(StringComparer.Ordinal), bulkSyncshells)).ConfigureAwait(false); ImGui.CloseCurrentPopup(); } ImGui.EndPopup(); } } }