Add MCDO (#80)
* update api * mcd online editor impl * most of chara data hub impl * some state of things * some refactoring * random bullshit go * more nearby impl * add uid to peformance msg * cleanup/homogenization * some split, update nuget packages * migrate to latest packages where possible, remove lz4net, do some split, idk * some polish and cleanup * more cleanup, beautification, etc. * fixes and cleanups --------- Co-authored-by: Stanley Dimant <root.darkarchon@outlook.com>
This commit is contained in:
194
MareSynchronos/UI/CharaDataHubUi.Functions.cs
Normal file
194
MareSynchronos/UI/CharaDataHubUi.Functions.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.Services.CharaData.Models;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
internal sealed partial class CharaDataHubUi
|
||||
{
|
||||
private static string GetAccessTypeString(AccessTypeDto dto) => dto switch
|
||||
{
|
||||
AccessTypeDto.AllPairs => "All Pairs",
|
||||
AccessTypeDto.ClosePairs => "Close Pairs",
|
||||
AccessTypeDto.Individuals => "Specified",
|
||||
AccessTypeDto.Public => "Everyone"
|
||||
};
|
||||
|
||||
private static string GetShareTypeString(ShareTypeDto dto) => dto switch
|
||||
{
|
||||
ShareTypeDto.Private => "Private",
|
||||
ShareTypeDto.Shared => "Shared"
|
||||
};
|
||||
|
||||
private static string GetWorldDataTooltipText(PoseEntryExtended poseEntry)
|
||||
{
|
||||
if (!poseEntry.HasWorldData) return "This Pose has no world data attached.";
|
||||
return poseEntry.WorldDataDescriptor;
|
||||
}
|
||||
|
||||
|
||||
private void GposeMetaInfoAction(Action<CharaDataMetaInfoExtendedDto?> gposeActionDraw, string actionDescription, CharaDataMetaInfoExtendedDto? dto, bool hasValidGposeTarget, bool isSpawning)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(actionDescription);
|
||||
bool isDisabled = false;
|
||||
|
||||
void AddErrorStart(StringBuilder sb)
|
||||
{
|
||||
sb.Append(UiSharedService.TooltipSeparator);
|
||||
sb.AppendLine("Cannot execute:");
|
||||
}
|
||||
|
||||
if (dto == null)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- No metainfo present");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (!dto?.CanBeDownloaded ?? false)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Character is not downloadable");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (!_uiSharedService.IsInGpose)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires to be in GPose");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (!hasValidGposeTarget && !isSpawning)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires a valid GPose target");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (isSpawning && !_charaDataManager.BrioAvailable)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires Brio to be installed.");
|
||||
isDisabled = true;
|
||||
}
|
||||
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
using var dis = ImRaii.Disabled(isDisabled);
|
||||
gposeActionDraw.Invoke(dto);
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
UiSharedService.AttachToolTip(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void GposePoseAction(Action poseActionDraw, string poseDescription, bool hasValidGposeTarget)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(poseDescription);
|
||||
bool isDisabled = false;
|
||||
|
||||
void AddErrorStart(StringBuilder sb)
|
||||
{
|
||||
sb.Append(UiSharedService.TooltipSeparator);
|
||||
sb.AppendLine("Cannot execute:");
|
||||
}
|
||||
|
||||
if (!_uiSharedService.IsInGpose)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires to be in GPose");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (!hasValidGposeTarget)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires a valid GPose target");
|
||||
isDisabled = true;
|
||||
}
|
||||
if (!_charaDataManager.BrioAvailable)
|
||||
{
|
||||
if (!isDisabled) AddErrorStart(sb);
|
||||
sb.AppendLine("- Requires Brio to be installed.");
|
||||
isDisabled = true;
|
||||
}
|
||||
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
using var dis = ImRaii.Disabled(isDisabled);
|
||||
poseActionDraw.Invoke();
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
UiSharedService.AttachToolTip(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void SetWindowSizeConstraints(bool? inGposeTab = null)
|
||||
{
|
||||
SizeConstraints = new()
|
||||
{
|
||||
MinimumSize = new((inGposeTab ?? false) ? 400 : 1000, 500),
|
||||
MaximumSize = new((inGposeTab ?? false) ? 400 : 1000, 2000)
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateFilteredFavorites()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (_charaDataManager.DownloadMetaInfoTask != null)
|
||||
{
|
||||
await _charaDataManager.DownloadMetaInfoTask.ConfigureAwait(false);
|
||||
}
|
||||
Dictionary<string, (CharaDataFavorite, CharaDataMetaInfoExtendedDto?, bool)> newFiltered = [];
|
||||
foreach (var favorite in _configService.Current.FavoriteCodes)
|
||||
{
|
||||
var uid = favorite.Key.Split(":")[0];
|
||||
var note = _serverConfigurationManager.GetNoteForUid(uid) ?? string.Empty;
|
||||
bool hasMetaInfo = _charaDataManager.TryGetMetaInfo(favorite.Key, out var metaInfo);
|
||||
bool addFavorite =
|
||||
(string.IsNullOrEmpty(_filterCodeNote)
|
||||
|| (note.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)
|
||||
|| uid.Contains(_filterCodeNote, StringComparison.OrdinalIgnoreCase)))
|
||||
&& (string.IsNullOrEmpty(_filterDescription)
|
||||
|| (favorite.Value.CustomDescription.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase)
|
||||
|| (metaInfo != null && metaInfo!.Description.Contains(_filterDescription, StringComparison.OrdinalIgnoreCase))))
|
||||
&& (!_filterPoseOnly
|
||||
|| (metaInfo != null && metaInfo!.HasPoses))
|
||||
&& (!_filterWorldOnly
|
||||
|| (metaInfo != null && metaInfo!.HasWorldData));
|
||||
if (addFavorite)
|
||||
{
|
||||
newFiltered[favorite.Key] = (favorite.Value, metaInfo, hasMetaInfo);
|
||||
}
|
||||
}
|
||||
|
||||
_filteredFavorites = newFiltered;
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateFilteredItems()
|
||||
{
|
||||
if (_charaDataManager.GetSharedWithYouTask == null)
|
||||
{
|
||||
_filteredDict = _charaDataManager.SharedWithYouData
|
||||
.SelectMany(k => k.Value)
|
||||
.Where(k =>
|
||||
(!_sharedWithYouDownloadableFilter || k.CanBeDownloaded)
|
||||
&& (string.IsNullOrEmpty(_sharedWithYouDescriptionFilter) || k.Description.Contains(_sharedWithYouDescriptionFilter, StringComparison.OrdinalIgnoreCase)))
|
||||
.GroupBy(k => k.Uploader)
|
||||
.ToDictionary(k =>
|
||||
{
|
||||
var note = _serverConfigurationManager.GetNoteForUid(k.Key.UID);
|
||||
if (note == null) return k.Key.AliasOrUID;
|
||||
return $"{note} ({k.Key.AliasOrUID})";
|
||||
}, k => k.ToList(), StringComparer.OrdinalIgnoreCase)
|
||||
.Where(k => (string.IsNullOrEmpty(_sharedWithYouOwnerFilter) || k.Key.Contains(_sharedWithYouOwnerFilter, StringComparison.OrdinalIgnoreCase)))
|
||||
.OrderBy(k => k.Key, StringComparer.OrdinalIgnoreCase).ToDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
738
MareSynchronos/UI/CharaDataHubUi.McdOnline.cs
Normal file
738
MareSynchronos/UI/CharaDataHubUi.McdOnline.cs
Normal file
@@ -0,0 +1,738 @@
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
using MareSynchronos.Services.CharaData.Models;
|
||||
using System.Numerics;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
internal sealed partial class CharaDataHubUi
|
||||
{
|
||||
private void DrawEditCharaData(CharaDataFullExtendedDto? dataDto)
|
||||
{
|
||||
using var imguiid = ImRaii.PushId(dataDto?.Id ?? "NoData");
|
||||
|
||||
if (dataDto == null)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
UiSharedService.DrawGroupedCenteredColorText("Select an entry above to edit its data.", ImGuiColors.DalamudYellow);
|
||||
return;
|
||||
}
|
||||
|
||||
var updateDto = _charaDataManager.GetUpdateDto(dataDto.Id);
|
||||
|
||||
if (updateDto == null)
|
||||
{
|
||||
UiSharedService.DrawGroupedCenteredColorText("Something went awfully wrong and there's no update DTO. Try updating Character Data via the button above.", ImGuiColors.DalamudYellow);
|
||||
return;
|
||||
}
|
||||
|
||||
bool canUpdate = updateDto.HasChanges;
|
||||
if (canUpdate || _charaDataManager.CharaUpdateTask != null)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
var indent = ImRaii.PushIndent(10f);
|
||||
if (canUpdate || (!_charaDataManager.UploadTask?.IsCompleted ?? false))
|
||||
{
|
||||
UiSharedService.DrawGrouped(() =>
|
||||
{
|
||||
if (canUpdate)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
UiSharedService.ColorTextWrapped("Warning: You have unsaved changes!", ImGuiColors.DalamudRed);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(_charaDataManager.CharaUpdateTask != null && !_charaDataManager.CharaUpdateTask.IsCompleted))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleUp, "Save to Server"))
|
||||
{
|
||||
_charaDataManager.UploadCharaData(dataDto.Id);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Undo, "Undo all changes"))
|
||||
{
|
||||
updateDto.UndoChanges();
|
||||
}
|
||||
}
|
||||
if (_charaDataManager.CharaUpdateTask != null && !_charaDataManager.CharaUpdateTask.IsCompleted)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Updating data on server, please wait.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_charaDataManager.UploadTask?.IsCompleted ?? false)
|
||||
{
|
||||
DisableDisabled(() =>
|
||||
{
|
||||
if (_charaDataManager.UploadProgress != null)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped(_charaDataManager.UploadProgress.Value ?? string.Empty, ImGuiColors.DalamudYellow);
|
||||
}
|
||||
if ((!_charaDataManager.UploadTask?.IsCompleted ?? false) && _uiSharedService.IconTextButton(FontAwesomeIcon.Ban, "Cancel Upload"))
|
||||
{
|
||||
_charaDataManager.CancelUpload();
|
||||
}
|
||||
else if (_charaDataManager.UploadTask?.IsCompleted ?? false)
|
||||
{
|
||||
var color = UiSharedService.GetBoolColor(_charaDataManager.UploadTask.Result.Success);
|
||||
UiSharedService.ColorTextWrapped(_charaDataManager.UploadTask.Result.Output, color);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
indent.Dispose();
|
||||
|
||||
if (canUpdate || _charaDataManager.CharaUpdateTask != null)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
using var child = ImRaii.Child("editChild", new(0, 0), false, ImGuiWindowFlags.AlwaysAutoResize);
|
||||
|
||||
DrawEditCharaDataGeneral(dataDto, updateDto);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
DrawEditCharaDataAccessAndSharing(updateDto);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
DrawEditCharaDataAppearance(dataDto, updateDto);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
DrawEditCharaDataPoses(updateDto);
|
||||
}
|
||||
|
||||
private void DrawEditCharaDataAccessAndSharing(CharaDataExtendedUpdateDto updateDto)
|
||||
{
|
||||
_uiSharedService.BigText("Access and Sharing");
|
||||
|
||||
ImGui.SetNextItemWidth(200);
|
||||
var dtoAccessType = updateDto.AccessType;
|
||||
if (ImGui.BeginCombo("Access Restrictions", GetAccessTypeString(dtoAccessType)))
|
||||
{
|
||||
foreach (var accessType in Enum.GetValues(typeof(AccessTypeDto)).Cast<AccessTypeDto>())
|
||||
{
|
||||
if (ImGui.Selectable(GetAccessTypeString(accessType), accessType == dtoAccessType))
|
||||
{
|
||||
updateDto.AccessType = accessType;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("You can control who has access to your character data based on the access restrictions." + UiSharedService.TooltipSeparator
|
||||
+ "Specified: Only people you directly specify in 'Specific Individuals' can access this character data" + Environment.NewLine
|
||||
+ "Close Pairs: Only people you have directly paired can access this character data" + Environment.NewLine
|
||||
+ "All Pairs: All people you have paired can access this character data" + Environment.NewLine
|
||||
+ "Everyone: Everyone can access this character data" + UiSharedService.TooltipSeparator
|
||||
+ "Note: To access your character data the person in question requires to have the code. Exceptions for 'Shared' data, see 'Sharing' below." + Environment.NewLine
|
||||
+ "Note: For 'Close' and 'All Pairs' the pause state plays a role. Paused people will not be able to access your character data." + Environment.NewLine
|
||||
+ "Note: Directly specified individuals in the 'Specific Individuals' list will be able to access your character data regardless of pause or pair state.");
|
||||
|
||||
DrawSpecificIndividuals(updateDto);
|
||||
|
||||
ImGui.SetNextItemWidth(200);
|
||||
var dtoShareType = updateDto.ShareType;
|
||||
using (ImRaii.Disabled(dtoAccessType == AccessTypeDto.Public))
|
||||
{
|
||||
if (ImGui.BeginCombo("Sharing", GetShareTypeString(dtoShareType)))
|
||||
{
|
||||
foreach (var shareType in Enum.GetValues(typeof(ShareTypeDto)).Cast<ShareTypeDto>())
|
||||
{
|
||||
if (ImGui.Selectable(GetShareTypeString(shareType), shareType == dtoShareType))
|
||||
{
|
||||
updateDto.ShareType = shareType;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
}
|
||||
_uiSharedService.DrawHelpText("This regulates how you want to distribute this character data." + UiSharedService.TooltipSeparator
|
||||
+ "Private: People require to have the code to download this character data" + Environment.NewLine
|
||||
+ "Shared: People that are allowed through 'Access Restrictions' will have this character data entry displayed in 'Shared with You'" + UiSharedService.TooltipSeparator
|
||||
+ "Note: Shared is incompatible with Access Restriction 'Everyone'");
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10f);
|
||||
}
|
||||
|
||||
private void DrawEditCharaDataAppearance(CharaDataFullExtendedDto dataDto, CharaDataExtendedUpdateDto updateDto)
|
||||
{
|
||||
_uiSharedService.BigText("Appearance");
|
||||
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Set Appearance to Current Appearance"))
|
||||
{
|
||||
_charaDataManager.SetAppearanceData(dataDto.Id);
|
||||
}
|
||||
_uiSharedService.DrawHelpText("This will overwrite the appearance data currently stored in this Character Data entry with your current appearance.");
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(dataDto.HasMissingFiles || !updateDto.IsAppearanceEqual || _charaDataManager.DataApplicationTask != null))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.CheckCircle, "Preview Saved Apperance on Self"))
|
||||
{
|
||||
_charaDataManager.ApplyDataToSelf(dataDto);
|
||||
}
|
||||
}
|
||||
_uiSharedService.DrawHelpText("This will download and apply the saved character data to yourself. Once loaded it will automatically revert itself within 15 seconds." + UiSharedService.TooltipSeparator
|
||||
+ "Note: Weapons will not be displayed correctly unless using the same job as the saved data.");
|
||||
|
||||
ImGui.TextUnformatted("Contains Glamourer Data");
|
||||
ImGui.SameLine();
|
||||
bool hasGlamourerdata = !string.IsNullOrEmpty(updateDto.GlamourerData);
|
||||
ImGui.SameLine(200);
|
||||
_uiSharedService.BooleanToColoredIcon(hasGlamourerdata, false);
|
||||
|
||||
ImGui.TextUnformatted("Contains Files");
|
||||
var hasFiles = (updateDto.FileGamePaths ?? []).Any() || (dataDto.OriginalFiles.Any());
|
||||
ImGui.SameLine(200);
|
||||
_uiSharedService.BooleanToColoredIcon(hasFiles, false);
|
||||
if (hasFiles && updateDto.IsAppearanceEqual)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(20, 1);
|
||||
ImGui.SameLine();
|
||||
var pos = ImGui.GetCursorPosX();
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(pos);
|
||||
ImGui.TextUnformatted($"{dataDto.FileGamePaths.DistinctBy(k => k.HashOrFileSwap).Count()} unique file hashes (original upload: {dataDto.OriginalFiles.DistinctBy(k => k.HashOrFileSwap).Count()} file hashes)");
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(pos);
|
||||
ImGui.TextUnformatted($"{dataDto.FileGamePaths.Count} associated game paths");
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(pos);
|
||||
ImGui.TextUnformatted($"{dataDto.FileSwaps!.Count} file swaps");
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(pos);
|
||||
if (!dataDto.HasMissingFiles)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("All files to download this character data are present on the server", ImGuiColors.HealerGreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.ColorTextWrapped($"{dataDto.MissingFiles.DistinctBy(k => k.HashOrFileSwap).Count()} files to download this character data are missing on the server", ImGuiColors.DalamudRed);
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(pos);
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleUp, "Attempt to upload missing files and restore Character Data"))
|
||||
{
|
||||
_charaDataManager.UploadMissingFiles(dataDto.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hasFiles && !updateDto.IsAppearanceEqual)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(20, 1);
|
||||
ImGui.SameLine();
|
||||
UiSharedService.ColorTextWrapped("New data was set. It may contain files that require to be uploaded (will happen on Saving to server)", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
|
||||
ImGui.TextUnformatted("Contains Manipulation Data");
|
||||
bool hasManipData = !string.IsNullOrEmpty(updateDto.ManipulationData);
|
||||
ImGui.SameLine(200);
|
||||
_uiSharedService.BooleanToColoredIcon(hasManipData, false);
|
||||
|
||||
ImGui.TextUnformatted("Contains Customize+ Data");
|
||||
ImGui.SameLine();
|
||||
bool hasCustomizeData = !string.IsNullOrEmpty(updateDto.CustomizeData);
|
||||
ImGui.SameLine(200);
|
||||
_uiSharedService.BooleanToColoredIcon(hasCustomizeData, false);
|
||||
}
|
||||
|
||||
private void DrawEditCharaDataGeneral(CharaDataFullExtendedDto dataDto, CharaDataExtendedUpdateDto updateDto)
|
||||
{
|
||||
_uiSharedService.BigText("General");
|
||||
string code = dataDto.FullId;
|
||||
using (ImRaii.Disabled())
|
||||
{
|
||||
ImGui.SetNextItemWidth(200);
|
||||
ImGui.InputText("##CharaDataCode", ref code, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Chara Data Code");
|
||||
ImGui.SameLine();
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Copy))
|
||||
{
|
||||
ImGui.SetClipboardText(code);
|
||||
}
|
||||
UiSharedService.AttachToolTip("Copy Code to Clipboard");
|
||||
|
||||
string creationTime = dataDto.CreatedDate.ToLocalTime().ToString();
|
||||
string updateTime = dataDto.UpdatedDate.ToLocalTime().ToString();
|
||||
string downloadCount = dataDto.DownloadCount.ToString();
|
||||
using (ImRaii.Disabled())
|
||||
{
|
||||
ImGui.SetNextItemWidth(200);
|
||||
ImGui.InputText("##CreationDate", ref creationTime, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Creation Date");
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(20);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled())
|
||||
{
|
||||
ImGui.SetNextItemWidth(200);
|
||||
ImGui.InputText("##LastUpdate", ref updateTime, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Last Update Date");
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(23);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled())
|
||||
{
|
||||
ImGui.SetNextItemWidth(50);
|
||||
ImGui.InputText("##DlCount", ref downloadCount, 255, ImGuiInputTextFlags.ReadOnly);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Download Count");
|
||||
|
||||
string description = updateDto.Description;
|
||||
ImGui.SetNextItemWidth(735);
|
||||
if (ImGui.InputText("##Description", ref description, 200))
|
||||
{
|
||||
updateDto.Description = description;
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Description");
|
||||
_uiSharedService.DrawHelpText("Description for this Character Data." + UiSharedService.TooltipSeparator
|
||||
+ "Note: the description will be visible to anyone who can access this character data. See 'Access Restrictions' and 'Sharing' below.");
|
||||
|
||||
var expiryDate = updateDto.ExpiryDate;
|
||||
bool isExpiring = expiryDate != DateTime.MaxValue;
|
||||
if (ImGui.Checkbox("Expires", ref isExpiring))
|
||||
{
|
||||
updateDto.SetExpiry(isExpiring);
|
||||
}
|
||||
_uiSharedService.DrawHelpText("If expiration is enabled, the uploaded character data will be automatically deleted from the server at the specified date.");
|
||||
using (ImRaii.Disabled(!isExpiring))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(100);
|
||||
if (ImGui.BeginCombo("Year", expiryDate.Year.ToString()))
|
||||
{
|
||||
for (int year = DateTime.UtcNow.Year; year < DateTime.UtcNow.Year + 4; year++)
|
||||
{
|
||||
if (ImGui.Selectable(year.ToString(), year == expiryDate.Year))
|
||||
{
|
||||
updateDto.SetExpiry(year, expiryDate.Month, expiryDate.Day);
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
||||
int daysInMonth = DateTime.DaysInMonth(expiryDate.Year, expiryDate.Month);
|
||||
ImGui.SetNextItemWidth(100);
|
||||
if (ImGui.BeginCombo("Month", expiryDate.Month.ToString()))
|
||||
{
|
||||
for (int month = 1; month <= 12; month++)
|
||||
{
|
||||
if (ImGui.Selectable(month.ToString(), month == expiryDate.Month))
|
||||
{
|
||||
updateDto.SetExpiry(expiryDate.Year, month, expiryDate.Day);
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
|
||||
ImGui.SetNextItemWidth(100);
|
||||
if (ImGui.BeginCombo("Day", expiryDate.Day.ToString()))
|
||||
{
|
||||
for (int day = 1; day <= daysInMonth; day++)
|
||||
{
|
||||
if (ImGui.Selectable(day.ToString(), day == expiryDate.Day))
|
||||
{
|
||||
updateDto.SetExpiry(expiryDate.Year, expiryDate.Month, day);
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
}
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character Data"))
|
||||
{
|
||||
_ = _charaDataManager.DeleteCharaData(dataDto);
|
||||
_selectedDtoId = string.Empty;
|
||||
}
|
||||
}
|
||||
if (!UiSharedService.CtrlPressed())
|
||||
{
|
||||
UiSharedService.AttachToolTip("Hold CTRL and click to delete the current data. This operation is irreversible.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawEditCharaDataPoses(CharaDataExtendedUpdateDto updateDto)
|
||||
{
|
||||
_uiSharedService.BigText("Poses");
|
||||
var poseCount = updateDto.PoseList.Count();
|
||||
using (ImRaii.Disabled(poseCount >= maxPoses))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add new Pose"))
|
||||
{
|
||||
updateDto.AddPose();
|
||||
}
|
||||
}
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, poseCount == maxPoses))
|
||||
ImGui.TextUnformatted($"{poseCount}/{maxPoses} poses attached");
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
using var indent = ImRaii.PushIndent(10f);
|
||||
int poseNumber = 1;
|
||||
|
||||
if (!_uiSharedService.IsInGpose && _charaDataManager.BrioAvailable)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
UiSharedService.DrawGroupedCenteredColorText("To attach pose and world data you need to be in GPose.", ImGuiColors.DalamudYellow);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
else if (!_charaDataManager.BrioAvailable)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
UiSharedService.DrawGroupedCenteredColorText("To attach pose and world data Brio requires to be installed.", ImGuiColors.DalamudRed);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
foreach (var pose in updateDto.PoseList)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
using var id = ImRaii.PushId("pose" + poseNumber);
|
||||
ImGui.TextUnformatted(poseNumber.ToString());
|
||||
|
||||
if (pose.Id == null)
|
||||
{
|
||||
ImGui.SameLine(50);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.Plus, ImGuiColors.DalamudYellow);
|
||||
UiSharedService.AttachToolTip("This pose has not been added to the server yet. Save changes to upload this Pose data.");
|
||||
}
|
||||
|
||||
bool poseHasChanges = updateDto.PoseHasChanges(pose);
|
||||
if (poseHasChanges)
|
||||
{
|
||||
ImGui.SameLine(50);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.ExclamationTriangle, ImGuiColors.DalamudYellow);
|
||||
UiSharedService.AttachToolTip("This pose has changes that have not been saved to the server yet.");
|
||||
}
|
||||
|
||||
ImGui.SameLine(75);
|
||||
if (pose.Description == null && pose.WorldData == null && pose.PoseData == null)
|
||||
{
|
||||
UiSharedService.ColorText("Pose scheduled for deletion", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
var desc = pose.Description;
|
||||
if (ImGui.InputTextWithHint("##description", "Description", ref desc, 100))
|
||||
{
|
||||
pose.Description = desc;
|
||||
updateDto.UpdatePoseList();
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete"))
|
||||
{
|
||||
updateDto.RemovePose(pose);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(10, 1);
|
||||
ImGui.SameLine();
|
||||
bool hasPoseData = !string.IsNullOrEmpty(pose.PoseData);
|
||||
_uiSharedService.IconText(FontAwesomeIcon.Running, UiSharedService.GetBoolColor(hasPoseData));
|
||||
UiSharedService.AttachToolTip(hasPoseData
|
||||
? "This Pose entry has pose data attached"
|
||||
: "This Pose entry has no pose data attached");
|
||||
ImGui.SameLine();
|
||||
|
||||
using (ImRaii.Disabled(!_uiSharedService.IsInGpose || !(_charaDataManager.AttachingPoseTask?.IsCompleted ?? true) || !_charaDataManager.BrioAvailable))
|
||||
{
|
||||
using var poseid = ImRaii.PushId("poseSet" + poseNumber);
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Plus))
|
||||
{
|
||||
_charaDataManager.AttachPoseData(pose, updateDto);
|
||||
}
|
||||
UiSharedService.AttachToolTip("Apply current pose data to pose");
|
||||
}
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(!hasPoseData))
|
||||
{
|
||||
using var poseid = ImRaii.PushId("poseDelete" + poseNumber);
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Trash))
|
||||
{
|
||||
pose.PoseData = string.Empty;
|
||||
updateDto.UpdatePoseList();
|
||||
}
|
||||
UiSharedService.AttachToolTip("Delete current pose data from pose");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGuiHelpers.ScaledDummy(10, 1);
|
||||
ImGui.SameLine();
|
||||
var worldData = pose.WorldData;
|
||||
bool hasWorldData = (worldData ?? default) != default;
|
||||
_uiSharedService.IconText(FontAwesomeIcon.Globe, UiSharedService.GetBoolColor(hasWorldData));
|
||||
var tooltipText = !hasWorldData ? "This Pose has no world data attached." : "This Pose has world data attached.";
|
||||
if (hasWorldData)
|
||||
{
|
||||
tooltipText += UiSharedService.TooltipSeparator + "Click to show location on map";
|
||||
}
|
||||
UiSharedService.AttachToolTip(tooltipText);
|
||||
if (hasWorldData && ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
_dalamudUtilService.SetMarkerAndOpenMap(position: new Vector3(worldData.Value.PositionX, worldData.Value.PositionY, worldData.Value.PositionZ),
|
||||
_dalamudUtilService.MapData.Value[worldData.Value.LocationInfo.MapId].Map);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(!_uiSharedService.IsInGpose || !(_charaDataManager.AttachingPoseTask?.IsCompleted ?? true) || !_charaDataManager.BrioAvailable))
|
||||
{
|
||||
using var worldId = ImRaii.PushId("worldSet" + poseNumber);
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Plus))
|
||||
{
|
||||
_charaDataManager.AttachWorldData(pose, updateDto);
|
||||
}
|
||||
UiSharedService.AttachToolTip("Apply current world position data to pose");
|
||||
}
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(!hasWorldData))
|
||||
{
|
||||
using var worldId = ImRaii.PushId("worldDelete" + poseNumber);
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Trash))
|
||||
{
|
||||
pose.WorldData = default(WorldData);
|
||||
updateDto.UpdatePoseList();
|
||||
}
|
||||
UiSharedService.AttachToolTip("Delete current world position data from pose");
|
||||
}
|
||||
}
|
||||
|
||||
if (poseHasChanges)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Undo, "Undo"))
|
||||
{
|
||||
updateDto.RevertDeletion(pose);
|
||||
}
|
||||
}
|
||||
|
||||
poseNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMcdOnline()
|
||||
{
|
||||
_uiSharedService.BigText("Mare Character Data Online");
|
||||
|
||||
DrawHelpFoldout("In this tab you can create, view and edit your own Mare Character Data that is stored on the server." + Environment.NewLine + Environment.NewLine
|
||||
+ "Mare Character Data Online functions similar to the previous MCDF standard for exporting your character, except that you do not have to send a file to the other person but solely a code." + Environment.NewLine + Environment.NewLine
|
||||
+ "There would be a bit too much to explain here on what you can do here in its entirety, however, all elements in this tab have help texts attached what they are used for. Please review them carefully." + Environment.NewLine + Environment.NewLine
|
||||
+ "Be mindful that when you share your Character Data with other people there is a chance that, with the help of unsanctioned 3rd party plugins, your appearance could be stolen irreversibly, just like when using MCDF.");
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
using (ImRaii.Disabled(_charaDataManager.GetAllDataTask != null
|
||||
|| (_charaDataManager.DataGetTimeoutTask != null && !_charaDataManager.DataGetTimeoutTask.IsCompleted)))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Download your Character Data from Server"))
|
||||
{
|
||||
_ = _charaDataManager.GetAllData(_disposalCts.Token);
|
||||
}
|
||||
}
|
||||
if (_charaDataManager.DataGetTimeoutTask != null && !_charaDataManager.DataGetTimeoutTask.IsCompleted)
|
||||
{
|
||||
UiSharedService.AttachToolTip("You can only refresh all character data from server every minute. Please wait.");
|
||||
}
|
||||
|
||||
using (var table = ImRaii.Table("Own Character Data", 12, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollY,
|
||||
new Vector2(ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X, 100)))
|
||||
{
|
||||
if (table)
|
||||
{
|
||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("Code");
|
||||
ImGui.TableSetupColumn("Description", ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupColumn("Created");
|
||||
ImGui.TableSetupColumn("Updated");
|
||||
ImGui.TableSetupColumn("Download Count", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("Downloadable", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("Files", ImGuiTableColumnFlags.WidthFixed, 32);
|
||||
ImGui.TableSetupColumn("Glamourer", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("Customize+", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupColumn("Expires", ImGuiTableColumnFlags.WidthFixed, 18);
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var entry in _charaDataManager.OwnCharaData.Values)
|
||||
{
|
||||
var uDto = _charaDataManager.GetUpdateDto(entry.Id);
|
||||
ImGui.TableNextColumn();
|
||||
if (string.Equals(entry.Id, _selectedDtoId, StringComparison.Ordinal))
|
||||
_uiSharedService.IconText(FontAwesomeIcon.CaretRight);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
DrawAddOrRemoveFavorite(entry);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var idText = entry.FullId;
|
||||
if (uDto?.HasChanges ?? false)
|
||||
{
|
||||
UiSharedService.ColorText(idText, ImGuiColors.DalamudYellow);
|
||||
UiSharedService.AttachToolTip("This entry has unsaved changes");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(idText);
|
||||
}
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(entry.Description);
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
UiSharedService.AttachToolTip(entry.Description);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(entry.CreatedDate.ToLocalTime().ToString());
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(entry.UpdatedDate.ToLocalTime().ToString());
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(entry.DownloadCount.ToString());
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
bool isDownloadable = !entry.HasMissingFiles
|
||||
&& !string.IsNullOrEmpty(entry.GlamourerData);
|
||||
_uiSharedService.BooleanToColoredIcon(isDownloadable, false);
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
UiSharedService.AttachToolTip(isDownloadable ? "Can be downloaded by others" : "Cannot be downloaded: Has missing files or data, please review this entry manually");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var count = entry.FileGamePaths.Concat(entry.FileSwaps).Count();
|
||||
ImGui.TextUnformatted(count.ToString());
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
UiSharedService.AttachToolTip(count == 0 ? "No File data attached" : "Has File data attached");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
bool hasGlamourerData = !string.IsNullOrEmpty(entry.GlamourerData);
|
||||
_uiSharedService.BooleanToColoredIcon(hasGlamourerData, false);
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
UiSharedService.AttachToolTip(string.IsNullOrEmpty(entry.GlamourerData) ? "No Glamourer data attached" : "Has Glamourer data attached");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
bool hasCustomizeData = !string.IsNullOrEmpty(entry.CustomizeData);
|
||||
_uiSharedService.BooleanToColoredIcon(hasCustomizeData, false);
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
UiSharedService.AttachToolTip(string.IsNullOrEmpty(entry.CustomizeData) ? "No Customize+ data attached" : "Has Customize+ data attached");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
FontAwesomeIcon eIcon = FontAwesomeIcon.None;
|
||||
if (!Equals(DateTime.MaxValue, entry.ExpiryDate))
|
||||
eIcon = FontAwesomeIcon.Clock;
|
||||
_uiSharedService.IconText(eIcon, ImGuiColors.DalamudYellow);
|
||||
if (ImGui.IsItemClicked()) _selectedDtoId = entry.Id;
|
||||
if (eIcon != FontAwesomeIcon.None)
|
||||
{
|
||||
UiSharedService.AttachToolTip($"This entry will expire on {entry.ExpiryDate.ToLocalTime()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (ImRaii.Disabled(!_charaDataManager.Initialized || _charaDataManager.DataCreationTask != null || _charaDataManager.OwnCharaData.Count == _charaDataManager.MaxCreatableCharaData))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "New Character Data Entry"))
|
||||
{
|
||||
_charaDataManager.CreateCharaDataEntry(_closalCts.Token);
|
||||
}
|
||||
}
|
||||
if (_charaDataManager.DataCreationTask != null)
|
||||
{
|
||||
UiSharedService.AttachToolTip("You can only create new character data every few seconds. Please wait.");
|
||||
}
|
||||
if (!_charaDataManager.Initialized)
|
||||
{
|
||||
UiSharedService.AttachToolTip("Please use the button \"Get Own Chara Data\" once before you can add new data entries.");
|
||||
}
|
||||
|
||||
if (_charaDataManager.Initialized)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
UiSharedService.TextWrapped($"Chara Data Entries on Server: {_charaDataManager.OwnCharaData.Count}/{_charaDataManager.MaxCreatableCharaData}");
|
||||
if (_charaDataManager.OwnCharaData.Count == _charaDataManager.MaxCreatableCharaData)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
UiSharedService.ColorTextWrapped("You have reached the maximum Character Data entries and cannot create more.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
}
|
||||
|
||||
if (_charaDataManager.DataCreationTask != null && !_charaDataManager.DataCreationTask.IsCompleted)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Creating new character data entry on server...", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
else if (_charaDataManager.DataCreationTask != null && _charaDataManager.DataCreationTask.IsCompleted)
|
||||
{
|
||||
var color = _charaDataManager.DataCreationTask.Result.Success ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed;
|
||||
UiSharedService.ColorTextWrapped(_charaDataManager.DataCreationTask.Result.Output, color);
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
ImGui.Separator();
|
||||
|
||||
_ = _charaDataManager.OwnCharaData.TryGetValue(_selectedDtoId, out var dto);
|
||||
DrawEditCharaData(dto);
|
||||
}
|
||||
|
||||
private void DrawSpecificIndividuals(CharaDataExtendedUpdateDto updateDto)
|
||||
{
|
||||
UiSharedService.DrawTree("Access for Specific Individuals", () =>
|
||||
{
|
||||
ImGui.SetNextItemWidth(200);
|
||||
ImGui.InputText("##AliasToAdd", ref _specificIndividualAdd, 20);
|
||||
ImGui.SameLine();
|
||||
using (ImRaii.Disabled(string.IsNullOrEmpty(_specificIndividualAdd)
|
||||
|| updateDto.UserList.Any(f => string.Equals(f.UID, _specificIndividualAdd, StringComparison.Ordinal) || string.Equals(f.Alias, _specificIndividualAdd, StringComparison.Ordinal))))
|
||||
{
|
||||
if (_uiSharedService.IconButton(FontAwesomeIcon.Plus))
|
||||
{
|
||||
updateDto.AddToList(_specificIndividualAdd);
|
||||
_specificIndividualAdd = string.Empty;
|
||||
}
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("UID/Vanity ID to Add");
|
||||
_uiSharedService.DrawHelpText("Users added to this list will be able to access this character data regardless of your pause or pair state with them." + UiSharedService.TooltipSeparator
|
||||
+ "Note: Mistyped entries will be automatically removed on updating data to server.");
|
||||
|
||||
using (var lb = ImRaii.ListBox("Allowed Individuals", new(200, 200)))
|
||||
{
|
||||
foreach (var user in updateDto.UserList)
|
||||
{
|
||||
var userString = string.IsNullOrEmpty(user.Alias) ? user.UID : $"{user.Alias} ({user.UID})";
|
||||
if (ImGui.Selectable(userString, string.Equals(user.UID, _selectedSpecificIndividual, StringComparison.Ordinal)))
|
||||
{
|
||||
_selectedSpecificIndividual = user.UID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (ImRaii.Disabled(string.IsNullOrEmpty(_selectedSpecificIndividual)))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove selected User"))
|
||||
{
|
||||
updateDto.RemoveFromList(_selectedSpecificIndividual);
|
||||
_selectedSpecificIndividual = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
});
|
||||
}
|
||||
}
|
||||
199
MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs
Normal file
199
MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using System.Numerics;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
internal partial class CharaDataHubUi
|
||||
{
|
||||
private void DrawNearbyPoses()
|
||||
{
|
||||
_uiSharedService.BigText("Poses Nearby");
|
||||
|
||||
DrawHelpFoldout("This tab will show you all Shared World Poses nearby you." + Environment.NewLine + Environment.NewLine
|
||||
+ "Shared World Poses are poses in character data that have world data attached to them and are set to shared. "
|
||||
+ "This means that all data that is in 'Shared with You' that has a pose with world data attached to it will be shown here if you are nearby." + Environment.NewLine
|
||||
+ "By default all poses that are shared will be shown. Poses taken in housing areas will by default only be shown on the correct server and location." + Environment.NewLine + Environment.NewLine
|
||||
+ "Shared World Poses will appear in the world as floating wisps, as well as in the list below. You can mouse over a Shared World Pose in the list for it to get highlighted in the world." + Environment.NewLine + Environment.NewLine
|
||||
+ "You can apply Shared World Poses to yourself or spawn the associated character to pose with them." + Environment.NewLine + Environment.NewLine
|
||||
+ "You can adjust the filter and change further settings in the 'Settings & Filter' foldout.");
|
||||
|
||||
UiSharedService.DrawTree("Settings & Filters", () =>
|
||||
{
|
||||
string filterByUser = _charaDataNearbyManager.UserNoteFilter;
|
||||
if (ImGui.InputTextWithHint("##filterbyuser", "Filter by User", ref filterByUser, 50))
|
||||
{
|
||||
_charaDataNearbyManager.UserNoteFilter = filterByUser;
|
||||
}
|
||||
bool onlyCurrent = _configService.Current.NearbyOwnServerOnly;
|
||||
if (ImGui.Checkbox("Only show Poses on current server", ref onlyCurrent))
|
||||
{
|
||||
_configService.Current.NearbyOwnServerOnly = onlyCurrent;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("Toggling this off will show you the location of all shared Poses with World Data from all Servers");
|
||||
bool showOwn = _configService.Current.NearbyShowOwnData;
|
||||
if (ImGui.Checkbox("Also show your own data", ref showOwn))
|
||||
{
|
||||
_configService.Current.NearbyShowOwnData = showOwn;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("Toggling this on will also show you the location of your own Poses");
|
||||
bool ignoreHousing = _configService.Current.NearbyIgnoreHousingLimitations;
|
||||
if (ImGui.Checkbox("Ignore Housing Limitations", ref ignoreHousing))
|
||||
{
|
||||
_configService.Current.NearbyIgnoreHousingLimitations = ignoreHousing;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("This will display all poses in their location regardless of housing limitations. (Ignoring Ward, Plot, Room)" + UiSharedService.TooltipSeparator
|
||||
+ "Note: Poses that utilize housing props, furniture, etc. will not be displayed correctly if not spawned in the right location.");
|
||||
bool showWisps = _configService.Current.NearbyDrawWisps;
|
||||
if (ImGui.Checkbox("Show Pose Wisps in the overworld", ref showWisps))
|
||||
{
|
||||
_configService.Current.NearbyDrawWisps = showWisps;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("When enabled, Mare will draw floating wisps where other's poses are in the world.");
|
||||
int poseDetectionDistance = _configService.Current.NearbyDistanceFilter;
|
||||
ImGui.SetNextItemWidth(100);
|
||||
if (ImGui.SliderInt("Detection Distance", ref poseDetectionDistance, 5, 1000))
|
||||
{
|
||||
_configService.Current.NearbyDistanceFilter = poseDetectionDistance;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiSharedService.DrawHelpText("This setting allows you to change the maximum distance in which poses will be shown. Set it to the maximum if you want to see all poses on the current map.");
|
||||
});
|
||||
|
||||
if (!_uiSharedService.IsInGpose)
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
UiSharedService.DrawGroupedCenteredColorText("Spawning and applying pose data is only available in GPose.", ImGuiColors.DalamudYellow);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
DrawUpdateSharedDataButton();
|
||||
|
||||
UiSharedService.DistanceSeparator();
|
||||
|
||||
using var child = ImRaii.Child("nearbyPosesChild", new(0, 0), false, ImGuiWindowFlags.AlwaysAutoResize);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(3f);
|
||||
|
||||
using var indent = ImRaii.PushIndent(5f);
|
||||
if (_charaDataNearbyManager.NearbyData.Count == 0)
|
||||
{
|
||||
UiSharedService.DrawGroupedCenteredColorText("No Shared World Poses found nearby.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
|
||||
bool wasAnythingHovered = false;
|
||||
int i = 0;
|
||||
foreach (var pose in _charaDataNearbyManager.NearbyData.OrderBy(v => v.Value.Distance))
|
||||
{
|
||||
using var poseId = ImRaii.PushId("nearbyPose" + (i++));
|
||||
var pos = ImGui.GetCursorPos();
|
||||
var circleDiameter = 60f;
|
||||
var circleOriginX = ImGui.GetWindowContentRegionMax().X - circleDiameter - pos.X;
|
||||
float circleOffsetY = 0;
|
||||
|
||||
UiSharedService.DrawGrouped(() =>
|
||||
{
|
||||
string? userNote = _serverConfigurationManager.GetNoteForUid(pose.Key.MetaInfo.Uploader.UID);
|
||||
var noteText = pose.Key.MetaInfo.IsOwnData ? "YOU" : (userNote == null ? pose.Key.MetaInfo.Uploader.AliasOrUID : $"{userNote} ({pose.Key.MetaInfo.Uploader.AliasOrUID})");
|
||||
ImGui.TextUnformatted("Pose by");
|
||||
ImGui.SameLine();
|
||||
UiSharedService.ColorText(noteText, ImGuiColors.ParsedGreen);
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
UiSharedService.ColorText("Character Data Description", ImGuiColors.DalamudGrey);
|
||||
ImGui.SameLine();
|
||||
_uiSharedService.IconText(FontAwesomeIcon.ExternalLinkAlt, ImGuiColors.DalamudGrey);
|
||||
}
|
||||
UiSharedService.AttachToolTip(pose.Key.MetaInfo.Description);
|
||||
UiSharedService.ColorText("Description", ImGuiColors.DalamudGrey);
|
||||
ImGui.SameLine();
|
||||
UiSharedService.TextWrapped(pose.Key.Description ?? "No Pose Description was set", circleOriginX);
|
||||
var posAfterGroup = ImGui.GetCursorPos();
|
||||
var groupHeightCenter = (posAfterGroup.Y - pos.Y) / 2;
|
||||
circleOffsetY = (groupHeightCenter - circleDiameter / 2);
|
||||
if (circleOffsetY < 0) circleOffsetY = 0;
|
||||
ImGui.SetCursorPos(new Vector2(circleOriginX, pos.Y));
|
||||
ImGui.Dummy(new Vector2(circleDiameter, circleDiameter));
|
||||
UiSharedService.AttachToolTip("Click to open corresponding map and set map marker" + UiSharedService.TooltipSeparator
|
||||
+ pose.Key.WorldDataDescriptor);
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
||||
{
|
||||
_dalamudUtilService.SetMarkerAndOpenMap(pose.Key.Position, pose.Key.Map);
|
||||
}
|
||||
ImGui.SetCursorPos(posAfterGroup);
|
||||
if (_uiSharedService.IsInGpose)
|
||||
{
|
||||
GposePoseAction(() =>
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowRight, "Apply Pose"))
|
||||
{
|
||||
_charaDataManager.ApplyFullPoseDataToGposeTarget(pose.Key);
|
||||
}
|
||||
}, $"Apply pose and position to {CharaName(_gposeTarget)}", _hasValidGposeTarget);
|
||||
ImGui.SameLine();
|
||||
GposeMetaInfoAction((_) =>
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Spawn and Pose"))
|
||||
{
|
||||
_charaDataManager.SpawnAndApplyWorldTransform(pose.Key.MetaInfo, pose.Key);
|
||||
}
|
||||
}, "Spawn actor and apply pose and position", pose.Key.MetaInfo, _hasValidGposeTarget, true);
|
||||
}
|
||||
});
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
wasAnythingHovered = true;
|
||||
_nearbyHovered = pose.Key;
|
||||
}
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var circleRadius = circleDiameter / 2f;
|
||||
var windowPos = ImGui.GetWindowPos();
|
||||
var scrollX = ImGui.GetScrollX();
|
||||
var scrollY = ImGui.GetScrollY();
|
||||
var circleCenter = new Vector2(windowPos.X + circleOriginX + circleRadius - scrollX, windowPos.Y + pos.Y + circleRadius + circleOffsetY - scrollY);
|
||||
var rads = pose.Value.Direction * (Math.PI / 180);
|
||||
|
||||
float halfConeAngleRadians = 15f * (float)Math.PI / 180f;
|
||||
Vector2 baseDir1 = new Vector2((float)Math.Sin(rads - halfConeAngleRadians), -(float)Math.Cos(rads - halfConeAngleRadians));
|
||||
Vector2 baseDir2 = new Vector2((float)Math.Sin(rads + halfConeAngleRadians), -(float)Math.Cos(rads + halfConeAngleRadians));
|
||||
|
||||
Vector2 coneBase1 = circleCenter + baseDir1 * circleRadius;
|
||||
Vector2 coneBase2 = circleCenter + baseDir2 * circleRadius;
|
||||
|
||||
// Draw the cone as a filled triangle
|
||||
drawList.AddTriangleFilled(circleCenter, coneBase1, coneBase2, UiSharedService.Color(ImGuiColors.ParsedGreen));
|
||||
drawList.AddCircle(circleCenter, circleDiameter / 2, UiSharedService.Color(ImGuiColors.DalamudWhite), 360, 2);
|
||||
var distance = pose.Value.Distance.ToString("0.0") + "y";
|
||||
var textSize = ImGui.CalcTextSize(distance);
|
||||
drawList.AddText(new Vector2(circleCenter.X - textSize.X / 2, circleCenter.Y + textSize.Y / 3f), UiSharedService.Color(ImGuiColors.DalamudWhite), distance);
|
||||
|
||||
ImGuiHelpers.ScaledDummy(3);
|
||||
}
|
||||
|
||||
if (!wasAnythingHovered) _nearbyHovered = null;
|
||||
_charaDataNearbyManager.SetHoveredVfx(_nearbyHovered);
|
||||
}
|
||||
|
||||
private void DrawUpdateSharedDataButton()
|
||||
{
|
||||
using (ImRaii.Disabled(_charaDataManager.GetAllDataTask != null
|
||||
|| (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted)))
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Update Data Shared With You"))
|
||||
{
|
||||
_ = _charaDataManager.GetAllSharedData(_disposalCts.Token).ContinueWith(u => UpdateFilteredItems());
|
||||
}
|
||||
}
|
||||
if (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted)
|
||||
{
|
||||
UiSharedService.AttachToolTip("You can only refresh all character data from server every minute. Please wait.");
|
||||
}
|
||||
}
|
||||
}
|
||||
1043
MareSynchronos/UI/CharaDataHubUi.cs
Normal file
1043
MareSynchronos/UI/CharaDataHubUi.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,100 +0,0 @@
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.PlayerData.Export;
|
||||
using MareSynchronos.Services;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.UI;
|
||||
|
||||
public class GposeUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
private readonly MareConfigService _configService;
|
||||
private readonly UiSharedService _uiSharedService;
|
||||
private readonly DalamudUtilService _dalamudUtil;
|
||||
private readonly FileDialogManager _fileDialogManager;
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private Task<long>? _expectedLength;
|
||||
private Task? _applicationTask;
|
||||
|
||||
public GposeUi(ILogger<GposeUi> logger, MareCharaFileManager mareCharaFileManager,
|
||||
DalamudUtilService dalamudUtil, FileDialogManager fileDialogManager, MareConfigService configService,
|
||||
MareMediator mediator, PerformanceCollectorService performanceCollectorService, UiSharedService uiSharedService)
|
||||
: base(logger, mediator, "Loporrit Gpose Import UI###LoporritSyncGposeUI", performanceCollectorService)
|
||||
{
|
||||
_mareCharaFileManager = mareCharaFileManager;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileDialogManager = fileDialogManager;
|
||||
_configService = configService;
|
||||
_uiSharedService = uiSharedService;
|
||||
Mediator.Subscribe<GposeStartMessage>(this, (_) => StartGpose());
|
||||
Mediator.Subscribe<GposeEndMessage>(this, (_) => EndGpose());
|
||||
IsOpen = _dalamudUtil.IsInGpose;
|
||||
this.SizeConstraints = new()
|
||||
{
|
||||
MinimumSize = new(200, 200),
|
||||
MaximumSize = new(400, 400)
|
||||
};
|
||||
}
|
||||
|
||||
protected override void DrawInternal()
|
||||
{
|
||||
if (!_dalamudUtil.IsInGpose) IsOpen = false;
|
||||
|
||||
if (!_mareCharaFileManager.CurrentlyWorking)
|
||||
{
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF"))
|
||||
{
|
||||
_fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) =>
|
||||
{
|
||||
if (!success) return;
|
||||
if (paths.FirstOrDefault() is not string path) return;
|
||||
|
||||
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
||||
_configService.Save();
|
||||
|
||||
_expectedLength = Task.Run(() => _mareCharaFileManager.LoadMareCharaFile(path));
|
||||
}, 1, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
||||
}
|
||||
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
||||
if (_mareCharaFileManager.LoadedCharaFile != null && _expectedLength != null)
|
||||
{
|
||||
UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath);
|
||||
UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description);
|
||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF"))
|
||||
{
|
||||
_applicationTask = _dalamudUtil.RunOnFrameworkThread(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject, _expectedLength!.GetAwaiter().GetResult()).ConfigureAwait(false));
|
||||
}
|
||||
UiSharedService.AttachToolTip("Applies it to the currently selected GPose actor");
|
||||
UiSharedService.ColorTextWrapped("Warning: redrawing or changing the character will revert all applied mods.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
if (_applicationTask?.IsFaulted ?? false)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Failure to read MCDF file. MCDF file is possibly corrupt. Re-export the MCDF file and try again.",
|
||||
ImGuiColors.DalamudRed);
|
||||
UiSharedService.ColorTextWrapped("Note: if this is your MCDF, try redrawing yourself, wait and re-export the file. " +
|
||||
"If you received it from someone else have them do the same.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Loading Character...", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
UiSharedService.TextWrapped("Hint: You can disable the automatic loading of this window in the Loporrit settings and open it manually with /sync gpose");
|
||||
}
|
||||
|
||||
private void EndGpose()
|
||||
{
|
||||
IsOpen = false;
|
||||
_applicationTask = null;
|
||||
_expectedLength = null;
|
||||
_mareCharaFileManager.ClearMareCharaFile();
|
||||
}
|
||||
|
||||
private void StartGpose()
|
||||
{
|
||||
IsOpen = _configService.Current.OpenGposeImportOnGposeStart;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Interop.Ipc;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.PlayerData.Export;
|
||||
using MareSynchronos.PlayerData.Handlers;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services;
|
||||
@@ -47,7 +46,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly GuiHookService _guiHookService;
|
||||
@@ -58,16 +56,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly UiSharedService _uiShared;
|
||||
private bool _deleteAccountPopupModalShown = false;
|
||||
private bool _deleteFilesPopupModalShown = false;
|
||||
private string _exportDescription = string.Empty;
|
||||
private string _lastTab = string.Empty;
|
||||
private bool? _notesSuccessfullyApplied = null;
|
||||
private bool _overwriteExistingLabels = false;
|
||||
private bool _readClearCache = false;
|
||||
private bool _readExport = false;
|
||||
private CancellationTokenSource? _validationCts;
|
||||
private Task<List<FileCacheEntity>>? _validationTask;
|
||||
private bool _wasOpen = false;
|
||||
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||
private Task<List<FileCacheEntity>>? _validationTask;
|
||||
private CancellationTokenSource? _validationCts;
|
||||
private (int, int, FileCacheEntity) _currentProgress;
|
||||
private Task? _exportTask;
|
||||
|
||||
@@ -77,7 +73,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
|
||||
public SettingsUi(ILogger<SettingsUi> logger,
|
||||
UiSharedService uiShared, MareConfigService configService,
|
||||
MareCharaFileManager mareCharaFileManager, PairManager pairManager, ChatService chatService, GuiHookService guiHookService,
|
||||
PairManager pairManager, ChatService chatService, GuiHookService guiHookService,
|
||||
ServerConfigurationManager serverConfigurationManager,
|
||||
PlayerPerformanceConfigService playerPerformanceConfigService, PlayerPerformanceService playerPerformanceService,
|
||||
MareMediator mediator, PerformanceCollectorService performanceCollector,
|
||||
@@ -89,7 +85,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
DalamudUtilService dalamudUtilService) : base(logger, mediator, "Loporrit Settings", performanceCollector)
|
||||
{
|
||||
_configService = configService;
|
||||
_mareCharaFileManager = mareCharaFileManager;
|
||||
_pairManager = pairManager;
|
||||
_chatService = chatService;
|
||||
_guiHookService = guiHookService;
|
||||
@@ -742,62 +737,18 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
|
||||
_uiShared.BigText("Export MCDF");
|
||||
|
||||
UiSharedService.TextWrapped("This feature allows you to pack your character appearance into a MCDF file and manually send it to other people. MCDF files can imported during GPose.");
|
||||
ImGuiHelpers.ScaledDummy(10);
|
||||
|
||||
ImGui.Checkbox("##readExport", ref _readExport);
|
||||
ImGui.SameLine();
|
||||
UiSharedService.TextWrapped("I understand that by exporting my character data and sending it to other people I am giving away my current character appearance irrevocably. People I am sharing my data with have the ability to share it with other people without limitations.");
|
||||
|
||||
if (_readExport)
|
||||
UiSharedService.ColorTextWrapped("Exporting MCDF has moved.", ImGuiColors.DalamudYellow);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
UiSharedService.TextWrapped("It is now found in the Main UI under \"Character Data Hub\"");
|
||||
if (_uiShared.IconTextButton(FontAwesomeIcon.Running, "Open Character Data Hub"))
|
||||
{
|
||||
ImGui.Indent();
|
||||
|
||||
if (!_mareCharaFileManager.CurrentlyWorking)
|
||||
{
|
||||
ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255);
|
||||
if (_uiShared.IconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF"))
|
||||
{
|
||||
string defaultFileName = string.IsNullOrEmpty(_exportDescription)
|
||||
? "export.mcdf"
|
||||
: string.Join('_', $"{_exportDescription}.mcdf".Split(Path.GetInvalidFileNameChars()));
|
||||
_uiShared.FileDialogManager.SaveFileDialog("Export Character to file", ".mcdf", defaultFileName, ".mcdf", (success, path) =>
|
||||
{
|
||||
if (!success) return;
|
||||
|
||||
_configService.Current.ExportFolder = Path.GetDirectoryName(path) ?? string.Empty;
|
||||
_configService.Save();
|
||||
|
||||
_exportTask = Task.Run(() =>
|
||||
{
|
||||
var desc = _exportDescription;
|
||||
_exportDescription = string.Empty;
|
||||
_mareCharaFileManager.SaveMareCharaFile(LastCreatedCharacterData, desc, path);
|
||||
});
|
||||
}, Directory.Exists(_configService.Current.ExportFolder) ? _configService.Current.ExportFolder : null);
|
||||
}
|
||||
UiSharedService.ColorTextWrapped("Note: For best results make sure you have everything you want to be shared as well as the correct character appearance" +
|
||||
" equipped and redraw your character before exporting.", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Export in progress", ImGuiColors.DalamudYellow);
|
||||
}
|
||||
|
||||
if (_exportTask?.IsFaulted ?? false)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Export failed, check /xllog for more details.", ImGuiColors.DalamudRed);
|
||||
}
|
||||
|
||||
ImGui.Unindent();
|
||||
Mediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi)));
|
||||
}
|
||||
bool openInGpose = _configService.Current.OpenGposeImportOnGposeStart;
|
||||
if (ImGui.Checkbox("Open MCDF import window when GPose loads", ref openInGpose))
|
||||
{
|
||||
_configService.Current.OpenGposeImportOnGposeStart = openInGpose;
|
||||
_configService.Save();
|
||||
}
|
||||
_uiShared.DrawHelpText("This will automatically open the import menu when loading into Gpose. If unchecked you can open the menu manually with /sync gpose");
|
||||
|
||||
UiSharedService.TextWrapped("Note: this entry will be removed in the near future. Please use the Main UI to open the Character Data Hub.");
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
ImGui.Separator();
|
||||
|
||||
_uiShared.BigText("Storage");
|
||||
@@ -1958,7 +1909,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Export & Storage"))
|
||||
if (ImGui.BeginTabItem("Storage"))
|
||||
{
|
||||
DrawFileStorageSettings();
|
||||
ImGui.EndTabItem();
|
||||
|
||||
@@ -78,6 +78,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
private bool _moodlesExists = false;
|
||||
private bool _penumbraExists = false;
|
||||
private bool _petNamesExists = false;
|
||||
private bool _brioExists = false;
|
||||
|
||||
private int _serverSelectionIndex = -1;
|
||||
|
||||
@@ -111,6 +112,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
_honorificExists = _ipcManager.Honorific.APIAvailable;
|
||||
_petNamesExists = _ipcManager.PetNames.APIAvailable;
|
||||
_moodlesExists = _ipcManager.Moodles.APIAvailable;
|
||||
_brioExists = _ipcManager.Brio.APIAvailable;
|
||||
});
|
||||
|
||||
UidFont = _pluginInterface.UiBuilder.FontAtlas.NewDelegateFontHandle(e =>
|
||||
@@ -133,7 +135,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
public bool HasValidPenumbraModPath => !(_ipcManager.Penumbra.ModDirectory ?? string.Empty).IsNullOrEmpty() && Directory.Exists(_ipcManager.Penumbra.ModDirectory);
|
||||
|
||||
public IFontHandle IconFont { get; init; }
|
||||
public bool IsInGpose => _dalamudUtil.IsInCutscene;
|
||||
public bool IsInGpose => _dalamudUtil.IsInGpose;
|
||||
|
||||
public string PlayerName => _dalamudUtil.GetPlayerName();
|
||||
|
||||
@@ -207,14 +209,46 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
ImGui.TextUnformatted(text);
|
||||
}
|
||||
|
||||
public static void ColorTextWrapped(string text, Vector4 color)
|
||||
public static void ColorTextWrapped(string text, Vector4 color, float wrapPos = 0)
|
||||
{
|
||||
using var raiicolor = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||
TextWrapped(text);
|
||||
TextWrapped(text, wrapPos);
|
||||
}
|
||||
|
||||
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
|
||||
|
||||
public static void DrawGrouped(Action imguiDrawAction, float rounding = 5f, float? expectedWidth = null)
|
||||
{
|
||||
var cursorPos = ImGui.GetCursorPos();
|
||||
using (ImRaii.Group())
|
||||
{
|
||||
if (expectedWidth != null)
|
||||
{
|
||||
ImGui.Dummy(new(expectedWidth.Value, 0));
|
||||
ImGui.SetCursorPos(cursorPos);
|
||||
}
|
||||
|
||||
imguiDrawAction.Invoke();
|
||||
}
|
||||
|
||||
ImGui.GetWindowDrawList().AddRect(
|
||||
ImGui.GetItemRectMin() - ImGui.GetStyle().ItemInnerSpacing,
|
||||
ImGui.GetItemRectMax() + ImGui.GetStyle().ItemInnerSpacing,
|
||||
Color(ImGuiColors.DalamudGrey2), rounding);
|
||||
}
|
||||
|
||||
public static void DrawGroupedCenteredColorText(string text, Vector4 color, float? maxWidth = null)
|
||||
{
|
||||
var availWidth = ImGui.GetContentRegionAvail().X;
|
||||
var textWidth = ImGui.CalcTextSize(text, availWidth).X;
|
||||
if (maxWidth != null && textWidth > maxWidth * ImGuiHelpers.GlobalScale) textWidth = maxWidth.Value * ImGuiHelpers.GlobalScale;
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (availWidth / 2f) - (textWidth / 2f));
|
||||
DrawGrouped(() =>
|
||||
{
|
||||
ColorTextWrapped(text, color, ImGui.GetCursorPosX() + textWidth);
|
||||
}, expectedWidth: maxWidth == null ? null : maxWidth * ImGuiHelpers.GlobalScale);
|
||||
}
|
||||
|
||||
public static void DrawOutlinedFont(string text, Vector4 fontColor, Vector4 outlineColor, int thickness)
|
||||
{
|
||||
var original = ImGui.GetCursorPos();
|
||||
@@ -271,6 +305,15 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
drawList.AddText(textPos, fontColor, text);
|
||||
}
|
||||
|
||||
public static void DrawTree(string leafName, Action drawOnOpened, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.None)
|
||||
{
|
||||
using var tree = ImRaii.TreeNode(leafName, flags);
|
||||
if (tree)
|
||||
{
|
||||
drawOnOpened();
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
|
||||
public float GetIconTextButtonSize(FontAwesomeIcon icon, string text)
|
||||
@@ -428,9 +471,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
|
||||
public static bool ShiftPressed() => (GetKeyState(0xA1) & 0x8000) != 0 || (GetKeyState(0xA0) & 0x8000) != 0;
|
||||
|
||||
public static void TextWrapped(string text)
|
||||
public static void TextWrapped(string text, float wrapPos = 0)
|
||||
{
|
||||
ImGui.PushTextWrapPos(0);
|
||||
ImGui.PushTextWrapPos(wrapPos);
|
||||
ImGui.TextUnformatted(text);
|
||||
ImGui.PopTextWrapPos();
|
||||
}
|
||||
@@ -739,15 +782,10 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
IconText(_penumbraExists ? check : cross, GetBoolColor(_penumbraExists));
|
||||
ImGui.SameLine();
|
||||
AttachToolTip($"Penumbra is " + (_penumbraExists ? "available and up to date." : "unavailable or not up to date."));
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Glamourer");
|
||||
ImGui.SameLine();
|
||||
IconText(_glamourerExists ? check : cross, GetBoolColor(_glamourerExists));
|
||||
ImGui.SameLine();
|
||||
ColorText("Glamourer", GetBoolColor(_glamourerExists));
|
||||
AttachToolTip($"Glamourer is " + (_glamourerExists ? "available and up to date." : "unavailable or not up to date."));
|
||||
ImGui.Spacing();
|
||||
|
||||
if (intro)
|
||||
{
|
||||
@@ -803,6 +841,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
AttachToolTip($"Moodles is " + (_moodlesExists ? "available and up to date." : "unavailable or not up to date."));
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted("Brio");
|
||||
ImGui.SameLine();
|
||||
IconText(_brioExists ? check : cross, GetBoolColor(_brioExists));
|
||||
ImGui.SameLine();
|
||||
AttachToolTip($"Brio is " + (_moodlesExists ? "available and up to date." : "unavailable or not up to date."));
|
||||
ImGui.Spacing();
|
||||
|
||||
if (!_penumbraExists || !_glamourerExists)
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Loporrit.");
|
||||
@@ -923,6 +969,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
Strings.ToS = new Strings.ToSStrings();
|
||||
}
|
||||
|
||||
internal static void DistanceSeparator()
|
||||
{
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
ImGui.Separator();
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
[LibraryImport("user32")]
|
||||
internal static partial short GetKeyState(int nVirtKey);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user