using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; using LightlessSync.UI; using LightlessSync.API.Data.Extensions; using LightlessSync.API.Dto.Group; using LightlessSync.UI.Handlers; using LightlessSync.UI.Style; using LightlessSync.UI.Models; using LightlessSync.WebAPI; using System.Collections.Immutable; using System.Numerics; namespace LightlessSync.UI.Components; public class DrawGroupedGroupFolder : IDrawFolder { private readonly string _tag; private readonly IEnumerable _groups; private readonly TagHandler _tagHandler; private readonly UiSharedService _uiSharedService; private readonly ApiController _apiController; private readonly SelectSyncshellForTagUi _selectSyncshellForTagUi; private readonly RenameSyncshellTagUi _renameSyncshellTagUi; private readonly HashSet _onlinePairBuffer = new(StringComparer.Ordinal); private IImmutableList? _drawPairsCache; private int? _totalPairsCache; private bool _wasHovered = false; private float _menuWidth; private bool _rowClickArmed; public IImmutableList DrawPairs => _drawPairsCache ??= _groups.SelectMany(g => g.GroupDrawFolder.DrawPairs).ToImmutableList(); public int OnlinePairs => CountOnlinePairs(DrawPairs); public int TotalPairs => _totalPairsCache ??= _groups.Sum(g => g.GroupDrawFolder.TotalPairs); public DrawGroupedGroupFolder(IEnumerable groups, TagHandler tagHandler, ApiController apiController, UiSharedService uiSharedService, SelectSyncshellForTagUi selectSyncshellForTagUi, RenameSyncshellTagUi renameSyncshellTagUi, string tag) { _groups = groups; _tagHandler = tagHandler; _uiSharedService = uiSharedService; _selectSyncshellForTagUi = selectSyncshellForTagUi; _renameSyncshellTagUi = renameSyncshellTagUi; _tag = tag; _apiController = apiController; } public void Draw() { string _id = "__folder_syncshells"; if (_tag != "") { _id = $"__folder_{_tag}"; } using var id = ImRaii.PushId(_id); var drawPairs = DrawPairs; var onlinePairs = CountOnlinePairs(drawPairs); var totalPairs = TotalPairs; var hasPairs = drawPairs.Count > 0; var color = ImRaii.PushColor(ImGuiCol.ChildBg, ImGui.GetColorU32(ImGuiCol.FrameBgHovered), _wasHovered); var allowRowClick = string.IsNullOrEmpty(_tag); var suppressRowToggle = false; using (ImRaii.Child("folder__" + _id, new System.Numerics.Vector2(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX(), ImGui.GetFrameHeight()))) { ImGui.Dummy(new Vector2(0f, ImGui.GetFrameHeight())); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(0f, 0f))) ImGui.SameLine(); var icon = _tagHandler.IsTagOpen(_id) ? FontAwesomeIcon.CaretDown : FontAwesomeIcon.CaretRight; ImGui.AlignTextToFramePadding(); _uiSharedService.IconText(icon); if (ImGui.IsItemClicked()) { _tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id)); suppressRowToggle = true; } ImGui.SameLine(); ImGui.AlignTextToFramePadding(); if (_tag != "") { _uiSharedService.IconText(FontAwesomeIcon.FolderPlus); } else { _uiSharedService.IconText(FontAwesomeIcon.UsersRectangle); } using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemSpacing.X / 2f })) { ImGui.SameLine(); ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted("[" + onlinePairs.ToString() + "]"); } UiSharedService.AttachToolTip(onlinePairs + " online in all of your joined syncshells" + Environment.NewLine + totalPairs + " pairs combined in all of your joined syncshells"); ImGui.SameLine(); ImGui.AlignTextToFramePadding(); if (_tag != "") { ImGui.TextUnformatted(_tag); ImGui.SameLine(); DrawPauseButton(hasPairs); ImGui.SameLine(); DrawMenu(ref suppressRowToggle); } else { ImGui.TextUnformatted("All Syncshells"); ImGui.SameLine(); DrawPauseButton(hasPairs); } } color.Dispose(); var rowHovered = ImGui.IsItemHovered(); _wasHovered = rowHovered; if (allowRowClick) { if (rowHovered && ImGui.IsMouseClicked(ImGuiMouseButton.Left) && !suppressRowToggle) { _rowClickArmed = true; } if (_rowClickArmed && rowHovered && ImGui.IsMouseReleased(ImGuiMouseButton.Left)) { _tagHandler.SetTagOpen(_id, !_tagHandler.IsTagOpen(_id)); _rowClickArmed = false; } if (!ImGui.IsMouseDown(ImGuiMouseButton.Left)) { _rowClickArmed = false; } } else { _rowClickArmed = false; } if (_wasHovered) { Selune.RegisterHighlight(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), spanFullWidth: true); } ImGui.Separator(); if (_tagHandler.IsTagOpen(_id)) { using var indent = ImRaii.PushIndent(20f); foreach (var entry in _groups) { entry.GroupDrawFolder.Draw(); } } } protected void DrawPauseButton(bool hasPairs) { if (hasPairs) { var isPaused = _groups.Select(g => g.GroupFullInfo).All(g => g.GroupUserPermissions.IsPaused()); FontAwesomeIcon pauseIcon = isPaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseIcon); var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); if (_tag != "") { var spacingX = ImGui.GetStyle().ItemSpacing.X; var menuButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV); ImGui.SameLine(windowEndX - pauseButtonSize.X - menuButtonSize.X - spacingX); } else { ImGui.SameLine(windowEndX - pauseButtonSize.X); } if (_uiSharedService.IconButton(pauseIcon)) { ChangePauseStateGroups(); } } } private int CountOnlinePairs(IImmutableList drawPairs) { if (drawPairs.Count == 0) { return 0; } _onlinePairBuffer.Clear(); foreach (var pair in drawPairs) { if (!pair.Pair.IsOnline) { continue; } _onlinePairBuffer.Add(pair.Pair.UserData.UID); } return _onlinePairBuffer.Count; } protected void ChangePauseStateGroups() { foreach(var group in _groups) { var perm = group.GroupFullInfo.GroupUserPermissions; perm.SetPaused(!perm.IsPaused()); _ = _apiController.GroupChangeIndividualPermissionState(new GroupPairUserPermissionDto(group.GroupFullInfo.Group, new(_apiController.UID), perm)); } } protected void DrawMenu(ref bool suppressRowToggle) { var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.EllipsisV); var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); ImGui.SameLine(windowEndX - barButtonSize.X); if (_uiSharedService.IconButton(FontAwesomeIcon.EllipsisV)) { suppressRowToggle = true; ImGui.OpenPopup("User Flyout Menu"); } if (ImGui.BeginPopup("User Flyout Menu")) { using (ImRaii.PushId($"buttons-syncshell-{_tag}")) GroupMenu(_menuWidth); _menuWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X; ImGui.EndPopup(); } } protected void GroupMenu(float menuWidth) { ImGui.TextUnformatted("Syncshell Group Menu"); if (_uiSharedService.IconTextButton(FontAwesomeIcon.Users, "Select Syncshells", menuWidth, isInPopup: true)) { _selectSyncshellForTagUi.Open(_tag); } if (_uiSharedService.IconTextButton(FontAwesomeIcon.Edit, "Rename Syncshell Group", menuWidth, isInPopup: true)) { _renameSyncshellTagUi.Open(_tag); } if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell Group", menuWidth, isInPopup: true) && UiSharedService.CtrlPressed()) { _tagHandler.RemoveSyncshellTag(_tag); } UiSharedService.AttachToolTip("Hold CTRL to remove this Group permanently."); } }