Compare commits
6 Commits
1.42.0.71-
...
1.42.0.72-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db9925693 | ||
|
|
0ba324bfe5 | ||
| 03105e0755 | |||
| b99f68a891 | |||
| 7c7a98f770 | |||
| ab369d008e |
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace LightlessSync.LightlessConfiguration.Configurations;
|
namespace LightlessSync.LightlessConfiguration.Configurations;
|
||||||
|
|
||||||
@@ -13,4 +14,11 @@ public sealed class ChatConfig : ILightlessConfiguration
|
|||||||
public bool IsWindowPinned { get; set; } = false;
|
public bool IsWindowPinned { get; set; } = false;
|
||||||
public bool AutoOpenChatOnPluginLoad { get; set; } = false;
|
public bool AutoOpenChatOnPluginLoad { get; set; } = false;
|
||||||
public float ChatFontScale { get; set; } = 1.0f;
|
public float ChatFontScale { get; set; } = 1.0f;
|
||||||
|
public bool HideInCombat { get; set; } = false;
|
||||||
|
public bool HideInDuty { get; set; } = false;
|
||||||
|
public bool ShowWhenUiHidden { get; set; } = true;
|
||||||
|
public bool ShowInCutscenes { get; set; } = true;
|
||||||
|
public bool ShowInGpose { get; set; } = true;
|
||||||
|
public List<string> ChannelOrder { get; set; } = new();
|
||||||
|
public Dictionary<string, bool> PreferNotesForChannels { get; set; } = new(StringComparer.Ordinal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors></Authors>
|
<Authors></Authors>
|
||||||
<Company></Company>
|
<Company></Company>
|
||||||
<Version>1.42.0.71</Version>
|
<Version>1.42.0.72</Version>
|
||||||
<Description></Description>
|
<Description></Description>
|
||||||
<Copyright></Copyright>
|
<Copyright></Copyright>
|
||||||
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Light-Public-Syncshells/LightlessClient</PackageProjectUrl>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
@@ -105,6 +106,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
services.AddSingleton(new Dalamud.Localization("LightlessSync.Localization.", string.Empty, useEmbedded: true));
|
services.AddSingleton(new Dalamud.Localization("LightlessSync.Localization.", string.Empty, useEmbedded: true));
|
||||||
services.AddSingleton(gameGui);
|
services.AddSingleton(gameGui);
|
||||||
services.AddSingleton(addonLifecycle);
|
services.AddSingleton(addonLifecycle);
|
||||||
|
services.AddSingleton<IUiBuilder>(pluginInterface.UiBuilder);
|
||||||
|
|
||||||
// Core singletons
|
// Core singletons
|
||||||
services.AddSingleton<LightlessMediator>();
|
services.AddSingleton<LightlessMediator>();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
private readonly ActorObjectService _actorObjectService;
|
private readonly ActorObjectService _actorObjectService;
|
||||||
private readonly PairUiService _pairUiService;
|
private readonly PairUiService _pairUiService;
|
||||||
|
private readonly ChatConfigService _chatConfigService;
|
||||||
|
|
||||||
private readonly Lock _sync = new();
|
private readonly Lock _sync = new();
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_actorObjectService = actorObjectService;
|
_actorObjectService = actorObjectService;
|
||||||
_pairUiService = pairUiService;
|
_pairUiService = pairUiService;
|
||||||
|
_chatConfigService = chatConfigService;
|
||||||
|
|
||||||
_isLoggedIn = _dalamudUtilService.IsLoggedIn;
|
_isLoggedIn = _dalamudUtilService.IsLoggedIn;
|
||||||
_isConnected = _apiController.IsConnected;
|
_isConnected = _apiController.IsConnected;
|
||||||
@@ -136,6 +138,42 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void MoveChannel(string draggedKey, string targetKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(draggedKey) || string.IsNullOrWhiteSpace(targetKey))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updated = false;
|
||||||
|
using (_sync.EnterScope())
|
||||||
|
{
|
||||||
|
if (!_channels.ContainsKey(draggedKey) || !_channels.ContainsKey(targetKey))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromIndex = _channelOrder.IndexOf(draggedKey);
|
||||||
|
var toIndex = _channelOrder.IndexOf(targetKey);
|
||||||
|
if (fromIndex < 0 || toIndex < 0 || fromIndex == toIndex)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_channelOrder.RemoveAt(fromIndex);
|
||||||
|
var insertIndex = Math.Clamp(toIndex, 0, _channelOrder.Count);
|
||||||
|
_channelOrder.Insert(insertIndex, draggedKey);
|
||||||
|
_chatConfigService.Current.ChannelOrder = new List<string>(_channelOrder);
|
||||||
|
_chatConfigService.Save();
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
{
|
||||||
|
PublishChannelListChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Task SetChatEnabledAsync(bool enabled)
|
public Task SetChatEnabledAsync(bool enabled)
|
||||||
=> enabled ? EnableChatAsync() : DisableChatAsync();
|
=> enabled ? EnableChatAsync() : DisableChatAsync();
|
||||||
|
|
||||||
@@ -512,7 +550,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
if (!_isLoggedIn || !_apiController.IsConnected)
|
if (!_isLoggedIn || !_apiController.IsConnected)
|
||||||
{
|
{
|
||||||
await LeaveCurrentZoneAsync(force, 0).ConfigureAwait(false);
|
await LeaveCurrentZoneAsync(force, 0, 0).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,6 +558,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
var location = await _dalamudUtilService.GetMapDataAsync().ConfigureAwait(false);
|
var location = await _dalamudUtilService.GetMapDataAsync().ConfigureAwait(false);
|
||||||
var territoryId = (ushort)location.TerritoryId;
|
var territoryId = (ushort)location.TerritoryId;
|
||||||
|
var worldId = (ushort)location.ServerId;
|
||||||
|
|
||||||
string? zoneKey;
|
string? zoneKey;
|
||||||
ZoneChannelDefinition? definition = null;
|
ZoneChannelDefinition? definition = null;
|
||||||
@@ -536,14 +575,14 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
|
|
||||||
if (definition is null)
|
if (definition is null)
|
||||||
{
|
{
|
||||||
await LeaveCurrentZoneAsync(force, territoryId).ConfigureAwait(false);
|
await LeaveCurrentZoneAsync(force, territoryId, worldId).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var descriptor = await BuildZoneDescriptorAsync(definition.Value).ConfigureAwait(false);
|
var descriptor = await BuildZoneDescriptorAsync(definition.Value).ConfigureAwait(false);
|
||||||
if (descriptor is null)
|
if (descriptor is null)
|
||||||
{
|
{
|
||||||
await LeaveCurrentZoneAsync(force, territoryId).ConfigureAwait(false);
|
await LeaveCurrentZoneAsync(force, territoryId, worldId).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +625,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LeaveCurrentZoneAsync(bool force, ushort territoryId)
|
private async Task LeaveCurrentZoneAsync(bool force, ushort territoryId, ushort worldId)
|
||||||
{
|
{
|
||||||
ChatChannelDescriptor? descriptor = null;
|
ChatChannelDescriptor? descriptor = null;
|
||||||
|
|
||||||
@@ -602,7 +641,27 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
state.StatusText = !_chatEnabled
|
state.StatusText = !_chatEnabled
|
||||||
? "Chat services disabled"
|
? "Chat services disabled"
|
||||||
: (_isConnected ? ZoneUnavailableMessage : "Disconnected from chat server");
|
: (_isConnected ? ZoneUnavailableMessage : "Disconnected from chat server");
|
||||||
state.DisplayName = "Zone Chat";
|
if (territoryId != 0
|
||||||
|
&& _dalamudUtilService.TerritoryData.Value.TryGetValue(territoryId, out var territoryName)
|
||||||
|
&& !string.IsNullOrWhiteSpace(territoryName))
|
||||||
|
{
|
||||||
|
state.DisplayName = territoryName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.DisplayName = "Zone Chat";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worldId != 0)
|
||||||
|
{
|
||||||
|
state.Descriptor = new ChatChannelDescriptor
|
||||||
|
{
|
||||||
|
Type = ChatChannelType.Zone,
|
||||||
|
WorldId = worldId,
|
||||||
|
ZoneId = territoryId,
|
||||||
|
CustomKey = string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(_activeChannelKey, ZoneChannelKey, StringComparison.Ordinal))
|
if (string.Equals(_activeChannelKey, ZoneChannelKey, StringComparison.Ordinal))
|
||||||
@@ -1092,17 +1151,50 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
|
|||||||
{
|
{
|
||||||
_channelOrder.Clear();
|
_channelOrder.Clear();
|
||||||
|
|
||||||
if (_channels.ContainsKey(ZoneChannelKey))
|
var configuredOrder = _chatConfigService.Current.ChannelOrder;
|
||||||
|
if (configuredOrder.Count > 0)
|
||||||
{
|
{
|
||||||
_channelOrder.Add(ZoneChannelKey);
|
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
foreach (var key in configuredOrder)
|
||||||
|
{
|
||||||
|
if (_channels.ContainsKey(key) && seen.Add(key))
|
||||||
|
{
|
||||||
|
_channelOrder.Add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var remaining = _channels.Values
|
||||||
|
.Where(state => !seen.Contains(state.Key))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (remaining.Count > 0)
|
||||||
|
{
|
||||||
|
var zoneKeys = remaining
|
||||||
|
.Where(state => state.Type == ChatChannelType.Zone)
|
||||||
|
.Select(state => state.Key);
|
||||||
|
var groupKeys = remaining
|
||||||
|
.Where(state => state.Type == ChatChannelType.Group)
|
||||||
|
.OrderBy(state => state.DisplayName, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(state => state.Key);
|
||||||
|
|
||||||
|
_channelOrder.AddRange(zoneKeys);
|
||||||
|
_channelOrder.AddRange(groupKeys);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_channels.ContainsKey(ZoneChannelKey))
|
||||||
|
{
|
||||||
|
_channelOrder.Add(ZoneChannelKey);
|
||||||
|
}
|
||||||
|
|
||||||
var groups = _channels.Values
|
var groups = _channels.Values
|
||||||
.Where(state => state.Type == ChatChannelType.Group)
|
.Where(state => state.Type == ChatChannelType.Group)
|
||||||
.OrderBy(state => state.DisplayName, StringComparer.OrdinalIgnoreCase)
|
.OrderBy(state => state.DisplayName, StringComparer.OrdinalIgnoreCase)
|
||||||
.Select(state => state.Key);
|
.Select(state => state.Key);
|
||||||
|
|
||||||
_channelOrder.AddRange(groups);
|
_channelOrder.AddRange(groups);
|
||||||
|
}
|
||||||
|
|
||||||
if (_activeChannelKey is null && _channelOrder.Count > 0)
|
if (_activeChannelKey is null && _channelOrder.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
public bool IsInCombat { get; private set; } = false;
|
public bool IsInCombat { get; private set; } = false;
|
||||||
public bool IsPerforming { get; private set; } = false;
|
public bool IsPerforming { get; private set; } = false;
|
||||||
public bool IsInInstance { get; private set; } = false;
|
public bool IsInInstance { get; private set; } = false;
|
||||||
|
public bool IsInDuty => _condition[ConditionFlag.BoundByDuty];
|
||||||
public bool HasModifiedGameFiles => _gameData.HasModifiedGameDataFiles;
|
public bool HasModifiedGameFiles => _gameData.HasModifiedGameDataFiles;
|
||||||
public uint ClassJobId => _classJobId!.Value;
|
public uint ClassJobId => _classJobId!.Value;
|
||||||
public Lazy<Dictionary<uint, string>> JobData { get; private set; }
|
public Lazy<Dictionary<uint, string>> JobData { get; private set; }
|
||||||
@@ -248,6 +249,32 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
public bool IsLodEnabled { get; private set; }
|
public bool IsLodEnabled { get; private set; }
|
||||||
public LightlessMediator Mediator { get; }
|
public LightlessMediator Mediator { get; }
|
||||||
|
|
||||||
|
public bool IsInFieldOperation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!IsInDuty)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var territoryId = _clientState.TerritoryType;
|
||||||
|
if (territoryId == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TerritoryData.Value.TryGetValue(territoryId, out var name) || string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name.Contains("Eureka", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| name.Contains("Bozja", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| name.Contains("Zadnor", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IGameObject? CreateGameObject(IntPtr reference)
|
public IGameObject? CreateGameObject(IntPtr reference)
|
||||||
{
|
{
|
||||||
EnsureIsOnFramework();
|
EnsureIsOnFramework();
|
||||||
|
|||||||
@@ -641,8 +641,8 @@ public unsafe class LightFinderPlateHandler : IHostedService, IMediatorSubscribe
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_activeBroadcastingCids = newSet;
|
_activeBroadcastingCids = newSet;
|
||||||
if (_logger.IsEnabled(LogLevel.Information))
|
if (_logger.IsEnabled(LogLevel.Trace))
|
||||||
_logger.LogInformation("Active broadcast CIDs: {Cids}", string.Join(',', _activeBroadcastingCids));
|
_logger.LogTrace("Active broadcast IDs: {Cids}", string.Join(',', _activeBroadcastingCids));
|
||||||
FlagRefresh();
|
FlagRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
private const float MinTextureFilterPaneWidth = 305f;
|
private const float MinTextureFilterPaneWidth = 305f;
|
||||||
private const float MaxTextureFilterPaneWidth = 405f;
|
private const float MaxTextureFilterPaneWidth = 405f;
|
||||||
private const float MinTextureDetailPaneWidth = 580f;
|
private const float MinTextureDetailPaneWidth = 480f;
|
||||||
private const float MaxTextureDetailPaneWidth = 720f;
|
private const float MaxTextureDetailPaneWidth = 720f;
|
||||||
|
private const float TextureFilterSplitterWidth = 8f;
|
||||||
|
private const float TextureDetailSplitterWidth = 12f;
|
||||||
|
private const float TextureDetailSplitterCollapsedWidth = 18f;
|
||||||
private const float SelectedFilePanelLogicalHeight = 90f;
|
private const float SelectedFilePanelLogicalHeight = 90f;
|
||||||
private static readonly Vector4 SelectedTextureRowTextColor = new(0f, 0f, 0f, 1f);
|
private static readonly Vector4 SelectedTextureRowTextColor = new(0f, 0f, 0f, 1f);
|
||||||
|
|
||||||
@@ -80,6 +83,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
private bool _modalOpen = false;
|
private bool _modalOpen = false;
|
||||||
private bool _showModal = false;
|
private bool _showModal = false;
|
||||||
private bool _textureRowsDirty = true;
|
private bool _textureRowsDirty = true;
|
||||||
|
private bool _textureDetailCollapsed = false;
|
||||||
private bool _conversionFailed;
|
private bool _conversionFailed;
|
||||||
private bool _showAlreadyAddedTransients = false;
|
private bool _showAlreadyAddedTransients = false;
|
||||||
private bool _acknowledgeReview = false;
|
private bool _acknowledgeReview = false;
|
||||||
@@ -111,7 +115,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
_hasUpdate = true;
|
_hasUpdate = true;
|
||||||
});
|
});
|
||||||
WindowBuilder.For(this)
|
WindowBuilder.For(this)
|
||||||
.SetSizeConstraints(new Vector2(1650, 1000), new Vector2(3840, 2160))
|
.SetSizeConstraints(new Vector2(1240, 680), new Vector2(3840, 2160))
|
||||||
.Apply();
|
.Apply();
|
||||||
|
|
||||||
_conversionProgress.ProgressChanged += ConversionProgress_ProgressChanged;
|
_conversionProgress.ProgressChanged += ConversionProgress_ProgressChanged;
|
||||||
@@ -1205,35 +1209,52 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
var availableSize = ImGui.GetContentRegionAvail();
|
var availableSize = ImGui.GetContentRegionAvail();
|
||||||
var windowPos = ImGui.GetWindowPos();
|
var windowPos = ImGui.GetWindowPos();
|
||||||
var spacingX = ImGui.GetStyle().ItemSpacing.X;
|
var spacingX = ImGui.GetStyle().ItemSpacing.X;
|
||||||
var splitterWidth = 6f * scale;
|
var filterSplitterWidth = TextureFilterSplitterWidth * scale;
|
||||||
|
var detailSplitterWidth = (_textureDetailCollapsed ? TextureDetailSplitterCollapsedWidth : TextureDetailSplitterWidth) * scale;
|
||||||
|
var totalSplitterWidth = filterSplitterWidth + detailSplitterWidth;
|
||||||
|
var totalSpacing = 2 * spacingX;
|
||||||
const float minFilterWidth = MinTextureFilterPaneWidth;
|
const float minFilterWidth = MinTextureFilterPaneWidth;
|
||||||
const float minDetailWidth = MinTextureDetailPaneWidth;
|
const float minDetailWidth = MinTextureDetailPaneWidth;
|
||||||
const float minCenterWidth = 340f;
|
const float minCenterWidth = 340f;
|
||||||
|
|
||||||
var dynamicFilterMax = Math.Max(minFilterWidth, availableSize.X - minDetailWidth - minCenterWidth - 2 * (splitterWidth + spacingX));
|
var detailMinForLayout = _textureDetailCollapsed ? 0f : minDetailWidth;
|
||||||
|
var dynamicFilterMax = Math.Max(minFilterWidth, availableSize.X - detailMinForLayout - minCenterWidth - totalSplitterWidth - totalSpacing);
|
||||||
var filterMaxBound = Math.Min(MaxTextureFilterPaneWidth, dynamicFilterMax);
|
var filterMaxBound = Math.Min(MaxTextureFilterPaneWidth, dynamicFilterMax);
|
||||||
var filterWidth = Math.Clamp(_textureFilterPaneWidth, minFilterWidth, filterMaxBound);
|
var filterWidth = Math.Clamp(_textureFilterPaneWidth, minFilterWidth, filterMaxBound);
|
||||||
|
|
||||||
var dynamicDetailMax = Math.Max(minDetailWidth, availableSize.X - filterWidth - minCenterWidth - 2 * (splitterWidth + spacingX));
|
var dynamicDetailMax = Math.Max(detailMinForLayout, availableSize.X - filterWidth - minCenterWidth - totalSplitterWidth - totalSpacing);
|
||||||
var detailMaxBound = Math.Min(MaxTextureDetailPaneWidth, dynamicDetailMax);
|
var detailMaxBound = _textureDetailCollapsed ? 0f : Math.Min(MaxTextureDetailPaneWidth, dynamicDetailMax);
|
||||||
var detailWidth = Math.Clamp(_textureDetailPaneWidth, minDetailWidth, detailMaxBound);
|
var detailWidth = _textureDetailCollapsed ? 0f : Math.Clamp(_textureDetailPaneWidth, minDetailWidth, detailMaxBound);
|
||||||
|
|
||||||
var centerWidth = availableSize.X - filterWidth - detailWidth - 2 * (splitterWidth + spacingX);
|
var centerWidth = availableSize.X - filterWidth - detailWidth - totalSplitterWidth - totalSpacing;
|
||||||
|
|
||||||
if (centerWidth < minCenterWidth)
|
if (centerWidth < minCenterWidth)
|
||||||
{
|
{
|
||||||
var deficit = minCenterWidth - centerWidth;
|
var deficit = minCenterWidth - centerWidth;
|
||||||
detailWidth = Math.Clamp(detailWidth - deficit, minDetailWidth,
|
if (!_textureDetailCollapsed)
|
||||||
Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - filterWidth - minCenterWidth - 2 * (splitterWidth + spacingX))));
|
{
|
||||||
centerWidth = availableSize.X - filterWidth - detailWidth - 2 * (splitterWidth + spacingX);
|
detailWidth = Math.Clamp(detailWidth - deficit, minDetailWidth,
|
||||||
if (centerWidth < minCenterWidth)
|
Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - filterWidth - minCenterWidth - totalSplitterWidth - totalSpacing)));
|
||||||
|
centerWidth = availableSize.X - filterWidth - detailWidth - totalSplitterWidth - totalSpacing;
|
||||||
|
if (centerWidth < minCenterWidth)
|
||||||
|
{
|
||||||
|
deficit = minCenterWidth - centerWidth;
|
||||||
|
filterWidth = Math.Clamp(filterWidth - deficit, minFilterWidth,
|
||||||
|
Math.Min(MaxTextureFilterPaneWidth, Math.Max(minFilterWidth, availableSize.X - detailWidth - minCenterWidth - totalSplitterWidth - totalSpacing)));
|
||||||
|
detailWidth = Math.Clamp(detailWidth, minDetailWidth,
|
||||||
|
Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - filterWidth - minCenterWidth - totalSplitterWidth - totalSpacing)));
|
||||||
|
centerWidth = availableSize.X - filterWidth - detailWidth - totalSplitterWidth - totalSpacing;
|
||||||
|
if (centerWidth < minCenterWidth)
|
||||||
|
{
|
||||||
|
centerWidth = minCenterWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
deficit = minCenterWidth - centerWidth;
|
|
||||||
filterWidth = Math.Clamp(filterWidth - deficit, minFilterWidth,
|
filterWidth = Math.Clamp(filterWidth - deficit, minFilterWidth,
|
||||||
Math.Min(MaxTextureFilterPaneWidth, Math.Max(minFilterWidth, availableSize.X - detailWidth - minCenterWidth - 2 * (splitterWidth + spacingX))));
|
Math.Min(MaxTextureFilterPaneWidth, Math.Max(minFilterWidth, availableSize.X - minCenterWidth - totalSplitterWidth - totalSpacing)));
|
||||||
detailWidth = Math.Clamp(detailWidth, minDetailWidth,
|
centerWidth = availableSize.X - filterWidth - detailWidth - totalSplitterWidth - totalSpacing;
|
||||||
Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - filterWidth - minCenterWidth - 2 * (splitterWidth + spacingX))));
|
|
||||||
centerWidth = availableSize.X - filterWidth - detailWidth - 2 * (splitterWidth + spacingX);
|
|
||||||
if (centerWidth < minCenterWidth)
|
if (centerWidth < minCenterWidth)
|
||||||
{
|
{
|
||||||
centerWidth = minCenterWidth;
|
centerWidth = minCenterWidth;
|
||||||
@@ -1242,7 +1263,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_textureFilterPaneWidth = filterWidth;
|
_textureFilterPaneWidth = filterWidth;
|
||||||
_textureDetailPaneWidth = detailWidth;
|
if (!_textureDetailCollapsed)
|
||||||
|
{
|
||||||
|
_textureDetailPaneWidth = detailWidth;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
using (var filters = ImRaii.Child("textureFilters", new Vector2(filterWidth, 0), true))
|
using (var filters = ImRaii.Child("textureFilters", new Vector2(filterWidth, 0), true))
|
||||||
@@ -1264,8 +1288,8 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
var filterMax = ImGui.GetItemRectMax();
|
var filterMax = ImGui.GetItemRectMax();
|
||||||
var filterHeight = filterMax.Y - filterMin.Y;
|
var filterHeight = filterMax.Y - filterMin.Y;
|
||||||
var filterTopLocal = filterMin - windowPos;
|
var filterTopLocal = filterMin - windowPos;
|
||||||
var maxFilterResize = Math.Min(MaxTextureFilterPaneWidth, Math.Max(minFilterWidth, availableSize.X - minCenterWidth - minDetailWidth - 2 * (splitterWidth + spacingX)));
|
var maxFilterResize = Math.Min(MaxTextureFilterPaneWidth, Math.Max(minFilterWidth, availableSize.X - minCenterWidth - detailMinForLayout - totalSplitterWidth - totalSpacing));
|
||||||
DrawVerticalResizeHandle("##textureFilterSplitter", filterTopLocal.Y, filterHeight, ref _textureFilterPaneWidth, minFilterWidth, maxFilterResize);
|
DrawVerticalResizeHandle("##textureFilterSplitter", filterTopLocal.Y, filterHeight, ref _textureFilterPaneWidth, minFilterWidth, maxFilterResize, out _);
|
||||||
|
|
||||||
TextureRow? selectedRow;
|
TextureRow? selectedRow;
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
@@ -1279,15 +1303,36 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
var tableMax = ImGui.GetItemRectMax();
|
var tableMax = ImGui.GetItemRectMax();
|
||||||
var tableHeight = tableMax.Y - tableMin.Y;
|
var tableHeight = tableMax.Y - tableMin.Y;
|
||||||
var tableTopLocal = tableMin - windowPos;
|
var tableTopLocal = tableMin - windowPos;
|
||||||
var maxDetailResize = Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - _textureFilterPaneWidth - minCenterWidth - 2 * (splitterWidth + spacingX)));
|
var maxDetailResize = Math.Min(MaxTextureDetailPaneWidth, Math.Max(minDetailWidth, availableSize.X - _textureFilterPaneWidth - minCenterWidth - totalSplitterWidth - totalSpacing));
|
||||||
DrawVerticalResizeHandle("##textureDetailSplitter", tableTopLocal.Y, tableHeight, ref _textureDetailPaneWidth, minDetailWidth, maxDetailResize, invert: true);
|
var detailToggle = DrawVerticalResizeHandle(
|
||||||
|
"##textureDetailSplitter",
|
||||||
ImGui.BeginGroup();
|
tableTopLocal.Y,
|
||||||
using (var detailChild = ImRaii.Child("textureDetailPane", new Vector2(detailWidth, 0), true))
|
tableHeight,
|
||||||
|
ref _textureDetailPaneWidth,
|
||||||
|
minDetailWidth,
|
||||||
|
maxDetailResize,
|
||||||
|
out var detailDragging,
|
||||||
|
invert: true,
|
||||||
|
showToggle: true,
|
||||||
|
isCollapsed: _textureDetailCollapsed);
|
||||||
|
if (detailToggle)
|
||||||
{
|
{
|
||||||
DrawTextureDetail(selectedRow);
|
_textureDetailCollapsed = !_textureDetailCollapsed;
|
||||||
|
}
|
||||||
|
if (_textureDetailCollapsed && detailDragging)
|
||||||
|
{
|
||||||
|
_textureDetailCollapsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_textureDetailCollapsed)
|
||||||
|
{
|
||||||
|
ImGui.BeginGroup();
|
||||||
|
using (var detailChild = ImRaii.Child("textureDetailPane", new Vector2(detailWidth, 0), true))
|
||||||
|
{
|
||||||
|
DrawTextureDetail(selectedRow);
|
||||||
|
}
|
||||||
|
ImGui.EndGroup();
|
||||||
}
|
}
|
||||||
ImGui.EndGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawTextureFilters(
|
private void DrawTextureFilters(
|
||||||
@@ -1935,26 +1980,118 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawVerticalResizeHandle(string id, float topY, float height, ref float leftWidth, float minWidth, float maxWidth, bool invert = false)
|
private bool DrawVerticalResizeHandle(
|
||||||
|
string id,
|
||||||
|
float topY,
|
||||||
|
float height,
|
||||||
|
ref float leftWidth,
|
||||||
|
float minWidth,
|
||||||
|
float maxWidth,
|
||||||
|
out bool isDragging,
|
||||||
|
bool invert = false,
|
||||||
|
bool showToggle = false,
|
||||||
|
bool isCollapsed = false)
|
||||||
{
|
{
|
||||||
var scale = ImGuiHelpers.GlobalScale;
|
var scale = ImGuiHelpers.GlobalScale;
|
||||||
var splitterWidth = 8f * scale;
|
var splitterWidth = (showToggle
|
||||||
|
? (isCollapsed ? TextureDetailSplitterCollapsedWidth : TextureDetailSplitterWidth)
|
||||||
|
: TextureFilterSplitterWidth) * scale;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
ImGui.SetCursorPos(new Vector2(cursor.X, topY));
|
var contentMin = ImGui.GetWindowContentRegionMin();
|
||||||
ImGui.PushStyleColor(ImGuiCol.Button, UIColors.Get("ButtonDefault"));
|
var contentMax = ImGui.GetWindowContentRegionMax();
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, UIColors.Get("LightlessPurple"));
|
var clampedTop = MathF.Max(topY, contentMin.Y);
|
||||||
ImGui.PushStyleColor(ImGuiCol.ButtonActive, UIColors.Get("LightlessPurpleActive"));
|
var clampedBottom = MathF.Min(topY + height, contentMax.Y);
|
||||||
ImGui.Button(id, new Vector2(splitterWidth, height));
|
var clampedHeight = MathF.Max(0f, clampedBottom - clampedTop);
|
||||||
ImGui.PopStyleColor(3);
|
var splitterRounding = ImGui.GetStyle().FrameRounding;
|
||||||
|
ImGui.SetCursorPos(new Vector2(cursor.X, clampedTop));
|
||||||
|
if (clampedHeight <= 0f)
|
||||||
|
{
|
||||||
|
isDragging = false;
|
||||||
|
ImGui.SetCursorPos(new Vector2(cursor.X + splitterWidth + ImGui.GetStyle().ItemSpacing.X, cursor.Y));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.IsItemActive())
|
ImGui.InvisibleButton(id, new Vector2(splitterWidth, clampedHeight));
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var rectMin = ImGui.GetItemRectMin();
|
||||||
|
var rectMax = ImGui.GetItemRectMax();
|
||||||
|
var windowPos = ImGui.GetWindowPos();
|
||||||
|
var clipMin = windowPos + contentMin;
|
||||||
|
var clipMax = windowPos + contentMax;
|
||||||
|
drawList.PushClipRect(clipMin, clipMax, true);
|
||||||
|
var clipInset = 1f * scale;
|
||||||
|
var drawMin = new Vector2(
|
||||||
|
MathF.Max(rectMin.X, clipMin.X),
|
||||||
|
MathF.Max(rectMin.Y, clipMin.Y));
|
||||||
|
var drawMax = new Vector2(
|
||||||
|
MathF.Min(rectMax.X, clipMax.X - clipInset),
|
||||||
|
MathF.Min(rectMax.Y, clipMax.Y));
|
||||||
|
var hovered = ImGui.IsItemHovered();
|
||||||
|
isDragging = ImGui.IsItemActive();
|
||||||
|
var baseColor = UIColors.Get("ButtonDefault");
|
||||||
|
var hoverColor = UIColors.Get("LightlessPurple");
|
||||||
|
var activeColor = UIColors.Get("LightlessPurpleActive");
|
||||||
|
var handleColor = isDragging ? activeColor : hovered ? hoverColor : baseColor;
|
||||||
|
drawList.AddRectFilled(drawMin, drawMax, UiSharedService.Color(handleColor), splitterRounding);
|
||||||
|
drawList.AddRect(drawMin, drawMax, UiSharedService.Color(new Vector4(1f, 1f, 1f, 0.12f)), splitterRounding);
|
||||||
|
|
||||||
|
bool toggleHovered = false;
|
||||||
|
bool toggleClicked = false;
|
||||||
|
if (showToggle)
|
||||||
|
{
|
||||||
|
var icon = isCollapsed ? FontAwesomeIcon.ChevronRight : FontAwesomeIcon.ChevronLeft;
|
||||||
|
Vector2 iconSize;
|
||||||
|
using (_uiSharedService.IconFont.Push())
|
||||||
|
{
|
||||||
|
iconSize = ImGui.CalcTextSize(icon.ToIconString());
|
||||||
|
}
|
||||||
|
|
||||||
|
var toggleHeight = MathF.Min(clampedHeight, 64f * scale);
|
||||||
|
var toggleMin = new Vector2(
|
||||||
|
drawMin.X,
|
||||||
|
drawMin.Y + (drawMax.Y - drawMin.Y - toggleHeight) / 2f);
|
||||||
|
var toggleMax = new Vector2(
|
||||||
|
drawMax.X,
|
||||||
|
toggleMin.Y + toggleHeight);
|
||||||
|
var toggleColorBase = UIColors.Get("LightlessPurple");
|
||||||
|
toggleHovered = ImGui.IsMouseHoveringRect(toggleMin, toggleMax);
|
||||||
|
var toggleBg = toggleHovered
|
||||||
|
? new Vector4(toggleColorBase.X, toggleColorBase.Y, toggleColorBase.Z, 0.65f)
|
||||||
|
: new Vector4(toggleColorBase.X, toggleColorBase.Y, toggleColorBase.Z, 0.35f);
|
||||||
|
if (toggleHovered)
|
||||||
|
{
|
||||||
|
UiSharedService.AttachToolTip(isCollapsed ? "Show texture details." : "Hide texture details.");
|
||||||
|
}
|
||||||
|
|
||||||
|
drawList.AddRectFilled(toggleMin, toggleMax, UiSharedService.Color(toggleBg), splitterRounding);
|
||||||
|
drawList.AddRect(toggleMin, toggleMax, UiSharedService.Color(toggleColorBase), splitterRounding);
|
||||||
|
|
||||||
|
var iconPos = new Vector2(
|
||||||
|
drawMin.X + (drawMax.X - drawMin.X - iconSize.X) / 2f,
|
||||||
|
drawMin.Y + (drawMax.Y - drawMin.Y - iconSize.Y) / 2f);
|
||||||
|
using (_uiSharedService.IconFont.Push())
|
||||||
|
{
|
||||||
|
drawList.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toggleHovered && ImGui.IsMouseReleased(ImGuiMouseButton.Left) && !ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
||||||
|
{
|
||||||
|
toggleClicked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDragging && !toggleHovered)
|
||||||
{
|
{
|
||||||
var delta = ImGui.GetIO().MouseDelta.X / scale;
|
var delta = ImGui.GetIO().MouseDelta.X / scale;
|
||||||
leftWidth += invert ? -delta : delta;
|
leftWidth += invert ? -delta : delta;
|
||||||
leftWidth = Math.Clamp(leftWidth, minWidth, maxWidth);
|
leftWidth = Math.Clamp(leftWidth, minWidth, maxWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawList.PopClipRect();
|
||||||
|
|
||||||
ImGui.SetCursorPos(new Vector2(cursor.X + splitterWidth + ImGui.GetStyle().ItemSpacing.X, cursor.Y));
|
ImGui.SetCursorPos(new Vector2(cursor.X + splitterWidth + ImGui.GetStyle().ItemSpacing.X, cursor.Y));
|
||||||
|
return toggleClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (IDalamudTextureWrap? Texture, bool IsLoading, string? Error) GetTexturePreview(TextureRow row)
|
private (IDalamudTextureWrap? Texture, bool IsLoading, string? Error) GetTexturePreview(TextureRow row)
|
||||||
@@ -2094,7 +2231,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui.TextDisabled("-");
|
_uiSharedService.IconText(FontAwesomeIcon.Check, ImGuiColors.DalamudWhite);
|
||||||
UiSharedService.AttachToolTip("Already stored in a compressed format; additional compression is disabled.");
|
UiSharedService.AttachToolTip("Already stored in a compressed format; additional compression is disabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2175,6 +2312,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
_textureSelections[key] = target;
|
_textureSelections[key] = target;
|
||||||
currentSelection = target;
|
currentSelection = target;
|
||||||
}
|
}
|
||||||
|
if (TextureMetadataHelper.TryGetRecommendationInfo(target, out var targetInfo))
|
||||||
|
{
|
||||||
|
UiSharedService.AttachToolTip($"{targetInfo.Title}{UiSharedService.TooltipSeparator}{targetInfo.Description}");
|
||||||
|
}
|
||||||
if (targetSelected)
|
if (targetSelected)
|
||||||
{
|
{
|
||||||
ImGui.SetItemDefaultFocus();
|
ImGui.SetItemDefaultFocus();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using LightlessSync.LightlessConfiguration.Models;
|
|||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
using LightlessSync.Services.Chat;
|
using LightlessSync.Services.Chat;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
@@ -23,8 +24,10 @@ namespace LightlessSync.UI;
|
|||||||
public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private const string ChatDisabledStatus = "Chat services disabled";
|
private const string ChatDisabledStatus = "Chat services disabled";
|
||||||
|
private const string ZoneUnavailableStatus = "Zone chat is only available in major cities.";
|
||||||
private const string SettingsPopupId = "zone_chat_settings_popup";
|
private const string SettingsPopupId = "zone_chat_settings_popup";
|
||||||
private const string ReportPopupId = "Report Message##zone_chat_report_popup";
|
private const string ReportPopupId = "Report Message##zone_chat_report_popup";
|
||||||
|
private const string ChannelDragPayloadId = "zone_chat_channel_drag";
|
||||||
private const float DefaultWindowOpacity = .97f;
|
private const float DefaultWindowOpacity = .97f;
|
||||||
private const float MinWindowOpacity = 0.05f;
|
private const float MinWindowOpacity = 0.05f;
|
||||||
private const float MaxWindowOpacity = 1f;
|
private const float MaxWindowOpacity = 1f;
|
||||||
@@ -32,6 +35,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
private const float MaxChatFontScale = 1.5f;
|
private const float MaxChatFontScale = 1.5f;
|
||||||
private const int ReportReasonMaxLength = 500;
|
private const int ReportReasonMaxLength = 500;
|
||||||
private const int ReportContextMaxLength = 1000;
|
private const int ReportContextMaxLength = 1000;
|
||||||
|
private const int MaxChannelNoteTabLength = 25;
|
||||||
|
|
||||||
private readonly UiSharedService _uiSharedService;
|
private readonly UiSharedService _uiSharedService;
|
||||||
private readonly ZoneChatService _zoneChatService;
|
private readonly ZoneChatService _zoneChatService;
|
||||||
@@ -39,6 +43,9 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
private readonly LightlessProfileManager _profileManager;
|
private readonly LightlessProfileManager _profileManager;
|
||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly ChatConfigService _chatConfigService;
|
private readonly ChatConfigService _chatConfigService;
|
||||||
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
|
private readonly IUiBuilder _uiBuilder;
|
||||||
private readonly Dictionary<string, string> _draftMessages = new(StringComparer.Ordinal);
|
private readonly Dictionary<string, string> _draftMessages = new(StringComparer.Ordinal);
|
||||||
private readonly ImGuiWindowFlags _unpinnedWindowFlags;
|
private readonly ImGuiWindowFlags _unpinnedWindowFlags;
|
||||||
private float _currentWindowOpacity = DefaultWindowOpacity;
|
private float _currentWindowOpacity = DefaultWindowOpacity;
|
||||||
@@ -61,6 +68,10 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
private bool _reportSubmitting;
|
private bool _reportSubmitting;
|
||||||
private string? _reportError;
|
private string? _reportError;
|
||||||
private ChatReportResult? _reportSubmissionResult;
|
private ChatReportResult? _reportSubmissionResult;
|
||||||
|
private string? _dragChannelKey;
|
||||||
|
private string? _dragHoverKey;
|
||||||
|
private bool _HideStateActive;
|
||||||
|
private bool _HideStateWasOpen;
|
||||||
|
|
||||||
public ZoneChatUi(
|
public ZoneChatUi(
|
||||||
ILogger<ZoneChatUi> logger,
|
ILogger<ZoneChatUi> logger,
|
||||||
@@ -70,6 +81,9 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
PairUiService pairUiService,
|
PairUiService pairUiService,
|
||||||
LightlessProfileManager profileManager,
|
LightlessProfileManager profileManager,
|
||||||
ChatConfigService chatConfigService,
|
ChatConfigService chatConfigService,
|
||||||
|
ServerConfigurationManager serverConfigurationManager,
|
||||||
|
DalamudUtilService dalamudUtilService,
|
||||||
|
IUiBuilder uiBuilder,
|
||||||
ApiController apiController,
|
ApiController apiController,
|
||||||
PerformanceCollectorService performanceCollectorService)
|
PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Lightless Chat", performanceCollectorService)
|
: base(logger, mediator, "Lightless Chat", performanceCollectorService)
|
||||||
@@ -79,6 +93,9 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
_pairUiService = pairUiService;
|
_pairUiService = pairUiService;
|
||||||
_profileManager = profileManager;
|
_profileManager = profileManager;
|
||||||
_chatConfigService = chatConfigService;
|
_chatConfigService = chatConfigService;
|
||||||
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
|
_dalamudUtilService = dalamudUtilService;
|
||||||
|
_uiBuilder = uiBuilder;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_isWindowPinned = _chatConfigService.Current.IsWindowPinned;
|
_isWindowPinned = _chatConfigService.Current.IsWindowPinned;
|
||||||
_showRulesOverlay = _chatConfigService.Current.ShowRulesOverlayOnOpen;
|
_showRulesOverlay = _chatConfigService.Current.ShowRulesOverlayOnOpen;
|
||||||
@@ -88,6 +105,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
_unpinnedWindowFlags = Flags;
|
_unpinnedWindowFlags = Flags;
|
||||||
RefreshWindowFlags();
|
RefreshWindowFlags();
|
||||||
|
ApplyUiVisibilitySettings();
|
||||||
Size = new Vector2(450, 420) * ImGuiHelpers.GlobalScale;
|
Size = new Vector2(450, 420) * ImGuiHelpers.GlobalScale;
|
||||||
SizeCondition = ImGuiCond.FirstUseEver;
|
SizeCondition = ImGuiCond.FirstUseEver;
|
||||||
WindowBuilder.For(this)
|
WindowBuilder.For(this)
|
||||||
@@ -98,6 +116,8 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
Mediator.Subscribe<ChatChannelMessageAdded>(this, OnChatChannelMessageAdded);
|
Mediator.Subscribe<ChatChannelMessageAdded>(this, OnChatChannelMessageAdded);
|
||||||
Mediator.Subscribe<ChatChannelsUpdated>(this, _ => _scrollToBottom = true);
|
Mediator.Subscribe<ChatChannelsUpdated>(this, _ => _scrollToBottom = true);
|
||||||
|
Mediator.Subscribe<PriorityFrameworkUpdateMessage>(this, _ => UpdateHideState());
|
||||||
|
Mediator.Subscribe<CutsceneFrameworkUpdateMessage>(this, _ => UpdateHideState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PreDraw()
|
public override void PreDraw()
|
||||||
@@ -108,6 +128,55 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SetNextWindowBgAlpha(_currentWindowOpacity);
|
ImGui.SetNextWindowBgAlpha(_currentWindowOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateHideState()
|
||||||
|
{
|
||||||
|
ApplyUiVisibilitySettings();
|
||||||
|
var shouldHide = ShouldHide();
|
||||||
|
if (shouldHide)
|
||||||
|
{
|
||||||
|
_HideStateWasOpen |= IsOpen;
|
||||||
|
if (IsOpen)
|
||||||
|
{
|
||||||
|
IsOpen = false;
|
||||||
|
}
|
||||||
|
_HideStateActive = true;
|
||||||
|
}
|
||||||
|
else if (_HideStateActive)
|
||||||
|
{
|
||||||
|
if (_HideStateWasOpen)
|
||||||
|
{
|
||||||
|
IsOpen = true;
|
||||||
|
}
|
||||||
|
_HideStateActive = false;
|
||||||
|
_HideStateWasOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyUiVisibilitySettings()
|
||||||
|
{
|
||||||
|
var config = _chatConfigService.Current;
|
||||||
|
_uiBuilder.DisableAutomaticUiHide = config.ShowWhenUiHidden;
|
||||||
|
_uiBuilder.DisableCutsceneUiHide = config.ShowInCutscenes;
|
||||||
|
_uiBuilder.DisableGposeUiHide = config.ShowInGpose;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldHide()
|
||||||
|
{
|
||||||
|
var config = _chatConfigService.Current;
|
||||||
|
|
||||||
|
if (config.HideInCombat && _dalamudUtilService.IsInCombat)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.HideInDuty && _dalamudUtilService.IsInDuty && !_dalamudUtilService.IsInFieldOperation)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void DrawInternal()
|
protected override void DrawInternal()
|
||||||
{
|
{
|
||||||
var childBgColor = ImGui.GetStyle().Colors[(int)ImGuiCol.ChildBg];
|
var childBgColor = ImGui.GetStyle().Colors[(int)ImGuiCol.ChildBg];
|
||||||
@@ -155,7 +224,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DrawHeader(ChatChannelSnapshot channel)
|
private void DrawHeader(ChatChannelSnapshot channel)
|
||||||
{
|
{
|
||||||
var prefix = channel.Type == ChatChannelType.Zone ? "Zone" : "Syncshell";
|
var prefix = channel.Type == ChatChannelType.Zone ? "Zone" : "Syncshell";
|
||||||
Vector4 color;
|
Vector4 color;
|
||||||
@@ -178,11 +247,18 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
if (channel.Type == ChatChannelType.Zone && channel.Descriptor.WorldId != 0)
|
if (channel.Type == ChatChannelType.Zone && channel.Descriptor.WorldId != 0)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted($"World #{channel.Descriptor.WorldId}");
|
var worldId = channel.Descriptor.WorldId;
|
||||||
|
var worldName = _dalamudUtilService.WorldData.Value.TryGetValue(worldId, out var name) ? name : $"World #{worldId}";
|
||||||
|
ImGui.TextUnformatted(worldName);
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip($"World ID: {worldId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var showInlineDisabled = string.Equals(channel.StatusText, ChatDisabledStatus, StringComparison.OrdinalIgnoreCase);
|
var showInlineStatus = string.Equals(channel.StatusText, ChatDisabledStatus, StringComparison.OrdinalIgnoreCase)
|
||||||
if (showInlineDisabled)
|
|| string.Equals(channel.StatusText, ZoneUnavailableStatus, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (showInlineStatus)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
|
||||||
@@ -324,6 +400,15 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
_refocusChatInputKey = null;
|
_refocusChatInputKey = null;
|
||||||
}
|
}
|
||||||
ImGui.InputText(inputId, ref draft, MaxMessageLength);
|
ImGui.InputText(inputId, ref draft, MaxMessageLength);
|
||||||
|
if (ImGui.IsItemActive() || ImGui.IsItemFocused())
|
||||||
|
{
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var itemMin = ImGui.GetItemRectMin();
|
||||||
|
var itemMax = ImGui.GetItemRectMax();
|
||||||
|
var highlight = UIColors.Get("LightlessPurple").WithAlpha(0.35f);
|
||||||
|
var highlightU32 = ImGui.ColorConvertFloat4ToU32(highlight);
|
||||||
|
drawList.AddRect(itemMin, itemMax, highlightU32, style.FrameRounding, ImDrawFlags.None, Math.Max(1f, ImGuiHelpers.GlobalScale));
|
||||||
|
}
|
||||||
var enterPressed = ImGui.IsItemFocused()
|
var enterPressed = ImGui.IsItemFocused()
|
||||||
&& (ImGui.IsKeyPressed(ImGuiKey.Enter) || ImGui.IsKeyPressed(ImGuiKey.KeypadEnter));
|
&& (ImGui.IsKeyPressed(ImGuiKey.Enter) || ImGui.IsKeyPressed(ImGuiKey.KeypadEnter));
|
||||||
_draftMessages[channel.Key] = draft;
|
_draftMessages[channel.Key] = draft;
|
||||||
@@ -480,7 +565,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
_uiSharedService.MediumText("Syncshell Chat Rules", UIColors.Get("LightlessYellow"));
|
_uiSharedService.MediumText("Syncshell Chat Rules", UIColors.Get("LightlessYellow"));
|
||||||
_uiSharedService.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("Syncshell chats are self-moderated (their own set rules) by it's owner and appointed moderators. If they fail to enforce chat rules within their syncshell, the owner (and its moderators) may face punishment."));
|
_uiSharedService.DrawNoteLine("! ", UIColors.Get("LightlessYellow"), new SeStringUtils.RichTextEntry("Syncshell chats are self-moderated (their own set rules) by it's owner and appointed moderators."));
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(5));
|
ImGui.Dummy(new Vector2(5));
|
||||||
|
|
||||||
@@ -1187,6 +1272,71 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SetTooltip("Toggles the timestamp prefix on messages.");
|
ImGui.SetTooltip("Toggles the timestamp prefix on messages.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextUnformatted("Chat Visibility");
|
||||||
|
|
||||||
|
var autoHideCombat = chatConfig.HideInCombat;
|
||||||
|
if (ImGui.Checkbox("Hide in combat", ref autoHideCombat))
|
||||||
|
{
|
||||||
|
chatConfig.HideInCombat = autoHideCombat;
|
||||||
|
_chatConfigService.Save();
|
||||||
|
UpdateHideState();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Temporarily hides the chat window while in combat.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var autoHideDuty = chatConfig.HideInDuty;
|
||||||
|
if (ImGui.Checkbox("Hide in duty (Not in field operations)", ref autoHideDuty))
|
||||||
|
{
|
||||||
|
chatConfig.HideInDuty = autoHideDuty;
|
||||||
|
_chatConfigService.Save();
|
||||||
|
UpdateHideState();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Hides the chat window inside duties.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var showWhenUiHidden = chatConfig.ShowWhenUiHidden;
|
||||||
|
if (ImGui.Checkbox("Show when game UI is hidden", ref showWhenUiHidden))
|
||||||
|
{
|
||||||
|
chatConfig.ShowWhenUiHidden = showWhenUiHidden;
|
||||||
|
_chatConfigService.Save();
|
||||||
|
UpdateHideState();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Allow the chat window to remain visible when the game UI is hidden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var showInCutscenes = chatConfig.ShowInCutscenes;
|
||||||
|
if (ImGui.Checkbox("Show in cutscenes", ref showInCutscenes))
|
||||||
|
{
|
||||||
|
chatConfig.ShowInCutscenes = showInCutscenes;
|
||||||
|
_chatConfigService.Save();
|
||||||
|
UpdateHideState();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Allow the chat window to remain visible during cutscenes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var showInGpose = chatConfig.ShowInGpose;
|
||||||
|
if (ImGui.Checkbox("Show in group pose", ref showInGpose))
|
||||||
|
{
|
||||||
|
chatConfig.ShowInGpose = showInGpose;
|
||||||
|
_chatConfigService.Save();
|
||||||
|
UpdateHideState();
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
ImGui.SetTooltip("Allow the chat window to remain visible in /gpose.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
var fontScale = Math.Clamp(chatConfig.ChatFontScale, MinChatFontScale, MaxChatFontScale);
|
var fontScale = Math.Clamp(chatConfig.ChatFontScale, MinChatFontScale, MaxChatFontScale);
|
||||||
var fontScaleChanged = ImGui.SliderFloat("Message font scale", ref fontScale, MinChatFontScale, MaxChatFontScale, "%.2fx");
|
var fontScaleChanged = ImGui.SliderFloat("Message font scale", ref fontScale, MinChatFontScale, MaxChatFontScale, "%.2fx");
|
||||||
var resetFontScale = ImGui.IsItemClicked(ImGuiMouseButton.Right);
|
var resetFontScale = ImGui.IsItemClicked(ImGuiMouseButton.Right);
|
||||||
@@ -1244,7 +1394,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawChannelButtons(IReadOnlyList<ChatChannelSnapshot> channels)
|
private unsafe void DrawChannelButtons(IReadOnlyList<ChatChannelSnapshot> channels)
|
||||||
{
|
{
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
var baseFramePadding = style.FramePadding;
|
var baseFramePadding = style.FramePadding;
|
||||||
@@ -1305,6 +1455,8 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (child)
|
if (child)
|
||||||
{
|
{
|
||||||
|
var dragActive = _dragChannelKey is not null && ImGui.IsMouseDragging(ImGuiMouseButton.Left);
|
||||||
|
var hoveredTargetThisFrame = false;
|
||||||
var first = true;
|
var first = true;
|
||||||
foreach (var channel in channels)
|
foreach (var channel in channels)
|
||||||
{
|
{
|
||||||
@@ -1315,6 +1467,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
var showBadge = !isSelected && channel.UnreadCount > 0;
|
var showBadge = !isSelected && channel.UnreadCount > 0;
|
||||||
var isZoneChannel = channel.Type == ChatChannelType.Zone;
|
var isZoneChannel = channel.Type == ChatChannelType.Zone;
|
||||||
(string Text, Vector2 TextSize, float Width, float Height)? badgeMetrics = null;
|
(string Text, Vector2 TextSize, float Width, float Height)? badgeMetrics = null;
|
||||||
|
var channelLabel = GetChannelTabLabel(channel);
|
||||||
|
|
||||||
var normal = isSelected ? UIColors.Get("LightlessPurpleDefault") : UIColors.Get("ButtonDefault");
|
var normal = isSelected ? UIColors.Get("LightlessPurpleDefault") : UIColors.Get("ButtonDefault");
|
||||||
var hovered = isSelected
|
var hovered = isSelected
|
||||||
@@ -1343,7 +1496,7 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
badgeMetrics = (badgeText, badgeTextSize, badgeWidth, badgeHeight);
|
badgeMetrics = (badgeText, badgeTextSize, badgeWidth, badgeHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
var clicked = ImGui.Button($"{channel.DisplayName}##chat_channel_{channel.Key}");
|
var clicked = ImGui.Button($"{channelLabel}##chat_channel_{channel.Key}");
|
||||||
|
|
||||||
if (showBadge)
|
if (showBadge)
|
||||||
{
|
{
|
||||||
@@ -1359,10 +1512,77 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
_scrollToBottom = true;
|
_scrollToBottom = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ShouldShowChannelTabContextMenu(channel)
|
||||||
|
&& ImGui.BeginPopupContextItem($"chat_channel_ctx##{channel.Key}"))
|
||||||
|
{
|
||||||
|
DrawChannelTabContextMenu(channel);
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginDragDropSource(ImGuiDragDropFlags.None))
|
||||||
|
{
|
||||||
|
if (!string.Equals(_dragChannelKey, channel.Key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_dragHoverKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dragChannelKey = channel.Key;
|
||||||
|
ImGui.SetDragDropPayload(ChannelDragPayloadId, null, 0);
|
||||||
|
ImGui.TextUnformatted(channelLabel);
|
||||||
|
ImGui.EndDragDropSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isDragTarget = false;
|
||||||
|
|
||||||
|
if (ImGui.BeginDragDropTarget())
|
||||||
|
{
|
||||||
|
var acceptFlags = ImGuiDragDropFlags.AcceptBeforeDelivery | ImGuiDragDropFlags.AcceptNoDrawDefaultRect;
|
||||||
|
var payload = ImGui.AcceptDragDropPayload(ChannelDragPayloadId, acceptFlags);
|
||||||
|
if (!payload.IsNull && _dragChannelKey is { } draggedKey
|
||||||
|
&& !string.Equals(draggedKey, channel.Key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
isDragTarget = true;
|
||||||
|
if (!string.Equals(_dragHoverKey, channel.Key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_dragHoverKey = channel.Key;
|
||||||
|
_zoneChatService.MoveChannel(draggedKey, channel.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndDragDropTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isHoveredDuringDrag = dragActive
|
||||||
|
&& ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem | ImGuiHoveredFlags.AllowWhenOverlapped);
|
||||||
|
|
||||||
|
if (!isDragTarget && isHoveredDuringDrag
|
||||||
|
&& !string.Equals(_dragChannelKey, channel.Key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
isDragTarget = true;
|
||||||
|
if (!string.Equals(_dragHoverKey, channel.Key, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_dragHoverKey = channel.Key;
|
||||||
|
_zoneChatService.MoveChannel(_dragChannelKey!, channel.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
var itemMin = ImGui.GetItemRectMin();
|
var itemMin = ImGui.GetItemRectMin();
|
||||||
var itemMax = ImGui.GetItemRectMax();
|
var itemMax = ImGui.GetItemRectMax();
|
||||||
|
|
||||||
|
if (isHoveredDuringDrag)
|
||||||
|
{
|
||||||
|
var highlight = UIColors.Get("LightlessPurple").WithAlpha(0.35f);
|
||||||
|
var highlightU32 = ImGui.ColorConvertFloat4ToU32(highlight);
|
||||||
|
drawList.AddRectFilled(itemMin, itemMax, highlightU32, style.FrameRounding);
|
||||||
|
drawList.AddRect(itemMin, itemMax, highlightU32, style.FrameRounding, ImDrawFlags.None, Math.Max(1f, ImGuiHelpers.GlobalScale));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDragTarget)
|
||||||
|
{
|
||||||
|
hoveredTargetThisFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isZoneChannel)
|
if (isZoneChannel)
|
||||||
{
|
{
|
||||||
var borderColor = UIColors.Get("LightlessOrange");
|
var borderColor = UIColors.Get("LightlessOrange");
|
||||||
@@ -1390,6 +1610,11 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dragActive && !hoveredTargetThisFrame)
|
||||||
|
{
|
||||||
|
_dragHoverKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (_pendingChannelScroll.HasValue)
|
if (_pendingChannelScroll.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SetScrollX(_pendingChannelScroll.Value);
|
ImGui.SetScrollX(_pendingChannelScroll.Value);
|
||||||
@@ -1430,9 +1655,123 @@ public sealed class ZoneChatUi : WindowMediatorSubscriberBase
|
|||||||
_channelScroll = currentScroll;
|
_channelScroll = currentScroll;
|
||||||
_channelScrollMax = maxScroll;
|
_channelScrollMax = maxScroll;
|
||||||
|
|
||||||
|
if (_dragChannelKey is not null && !ImGui.IsMouseDown(ImGuiMouseButton.Left))
|
||||||
|
{
|
||||||
|
_dragChannelKey = null;
|
||||||
|
_dragHoverKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - style.ItemSpacing.Y * 0.3f);
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - style.ItemSpacing.Y * 0.3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetChannelTabLabel(ChatChannelSnapshot channel)
|
||||||
|
{
|
||||||
|
if (channel.Type != ChatChannelType.Group)
|
||||||
|
{
|
||||||
|
return channel.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_chatConfigService.Current.PreferNotesForChannels.TryGetValue(channel.Key, out var preferNote) || !preferNote)
|
||||||
|
{
|
||||||
|
return channel.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var note = GetChannelNote(channel);
|
||||||
|
if (string.IsNullOrWhiteSpace(note))
|
||||||
|
{
|
||||||
|
return channel.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TruncateChannelNoteForTab(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TruncateChannelNoteForTab(string note)
|
||||||
|
{
|
||||||
|
if (note.Length <= MaxChannelNoteTabLength)
|
||||||
|
{
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ellipsis = "...";
|
||||||
|
var maxPrefix = Math.Max(0, MaxChannelNoteTabLength - ellipsis.Length);
|
||||||
|
return note[..maxPrefix] + ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldShowChannelTabContextMenu(ChatChannelSnapshot channel)
|
||||||
|
{
|
||||||
|
if (channel.Type != ChatChannelType.Group)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_chatConfigService.Current.PreferNotesForChannels.TryGetValue(channel.Key, out var preferNote) && preferNote)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var note = GetChannelNote(channel);
|
||||||
|
return !string.IsNullOrWhiteSpace(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawChannelTabContextMenu(ChatChannelSnapshot channel)
|
||||||
|
{
|
||||||
|
var preferNote = _chatConfigService.Current.PreferNotesForChannels.TryGetValue(channel.Key, out var value) && value;
|
||||||
|
var note = GetChannelNote(channel);
|
||||||
|
var hasNote = !string.IsNullOrWhiteSpace(note);
|
||||||
|
if (preferNote || hasNote)
|
||||||
|
{
|
||||||
|
var label = preferNote ? "Prefer Name Instead" : "Prefer Note Instead";
|
||||||
|
if (ImGui.MenuItem(label))
|
||||||
|
{
|
||||||
|
SetPreferNoteForChannel(channel.Key, !preferNote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferNote)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextDisabled("Name:");
|
||||||
|
ImGui.TextWrapped(channel.DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNote)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextDisabled("Note:");
|
||||||
|
ImGui.TextWrapped(note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetChannelNote(ChatChannelSnapshot channel)
|
||||||
|
{
|
||||||
|
if (channel.Type != ChatChannelType.Group)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gid = channel.Descriptor.CustomKey;
|
||||||
|
if (string.IsNullOrWhiteSpace(gid))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _serverConfigurationManager.GetNoteForGid(gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPreferNoteForChannel(string channelKey, bool preferNote)
|
||||||
|
{
|
||||||
|
if (preferNote)
|
||||||
|
{
|
||||||
|
_chatConfigService.Current.PreferNotesForChannels[channelKey] = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_chatConfigService.Current.PreferNotesForChannels.Remove(channelKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
_chatConfigService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawSystemEntry(ChatMessageEntry entry)
|
private void DrawSystemEntry(ChatMessageEntry entry)
|
||||||
{
|
{
|
||||||
var system = entry.SystemMessage;
|
var system = entry.SystemMessage;
|
||||||
|
|||||||
@@ -584,7 +584,10 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
|
|||||||
OnGroupSendInfo((dto) => _ = Client_GroupSendInfo(dto));
|
OnGroupSendInfo((dto) => _ = Client_GroupSendInfo(dto));
|
||||||
OnGroupUpdateProfile((dto) => _ = Client_GroupSendProfile(dto));
|
OnGroupUpdateProfile((dto) => _ = Client_GroupSendProfile(dto));
|
||||||
OnGroupChangeUserPairPermissions((dto) => _ = Client_GroupChangeUserPairPermissions(dto));
|
OnGroupChangeUserPairPermissions((dto) => _ = Client_GroupChangeUserPairPermissions(dto));
|
||||||
_lightlessHub.On(nameof(Client_ChatReceive), (Func<ChatMessageDto, Task>)Client_ChatReceive);
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
_lightlessHub.On(nameof(Client_ChatReceive), (Func<ChatMessageDto, Task>)Client_ChatReceive);
|
||||||
|
}
|
||||||
|
|
||||||
OnGposeLobbyJoin((dto) => _ = Client_GposeLobbyJoin(dto));
|
OnGposeLobbyJoin((dto) => _ = Client_GposeLobbyJoin(dto));
|
||||||
OnGposeLobbyLeave((dto) => _ = Client_GposeLobbyLeave(dto));
|
OnGposeLobbyLeave((dto) => _ = Client_GposeLobbyLeave(dto));
|
||||||
|
|||||||
Submodule Penumbra.Api updated: 1750c41b53...52a3216a52
Reference in New Issue
Block a user