feat(ui): add per-tab notification sound for inactive tabs
This commit is contained in:
@@ -446,6 +446,10 @@ public class Tab
|
|||||||
public bool AllSenderMessages;
|
public bool AllSenderMessages;
|
||||||
public TellTarget TellTarget = TellTarget.Empty();
|
public TellTarget TellTarget = TellTarget.Empty();
|
||||||
|
|
||||||
|
// UI-3: per-tab notification sound for messages arriving in an inactive tab.
|
||||||
|
public bool EnableNotificationSound;
|
||||||
|
public uint NotificationSoundId = 1;
|
||||||
|
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
public uint Unread;
|
public uint Unread;
|
||||||
|
|
||||||
@@ -564,6 +568,8 @@ public class Tab
|
|||||||
IsPinned = IsPinned,
|
IsPinned = IsPinned,
|
||||||
AllSenderMessages = AllSenderMessages,
|
AllSenderMessages = AllSenderMessages,
|
||||||
TellTarget = TellTarget.Clone(),
|
TellTarget = TellTarget.Clone(),
|
||||||
|
EnableNotificationSound = EnableNotificationSound,
|
||||||
|
NotificationSoundId = NotificationSoundId,
|
||||||
IsGreeted = IsGreeted,
|
IsGreeted = IsGreeted,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ using Dalamud.Game.Text.SeStringHandling;
|
|||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
|
using HellionChat._Helpers;
|
||||||
using HellionChat.Code;
|
using HellionChat.Code;
|
||||||
using HellionChat.Resources;
|
using HellionChat.Resources;
|
||||||
using HellionChat.Util;
|
using HellionChat.Util;
|
||||||
@@ -330,6 +332,7 @@ internal class MessageManager : IAsyncDisposable
|
|||||||
Store.UpsertMessage(message);
|
Store.UpsertMessage(message);
|
||||||
|
|
||||||
var currentMatches = Plugin.CurrentTab.Matches(message);
|
var currentMatches = Plugin.CurrentTab.Matches(message);
|
||||||
|
uint? notificationSound = null;
|
||||||
foreach (var tab in Plugin.Config.Tabs)
|
foreach (var tab in Plugin.Config.Tabs)
|
||||||
{
|
{
|
||||||
var unread = !(
|
var unread = !(
|
||||||
@@ -337,7 +340,31 @@ internal class MessageManager : IAsyncDisposable
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (tab.Matches(message))
|
if (tab.Matches(message))
|
||||||
|
{
|
||||||
tab.AddMessage(message, unread);
|
tab.AddMessage(message, unread);
|
||||||
|
|
||||||
|
// UI-3: per-tab notification sound. Fire once for the first
|
||||||
|
// inactive tab that wants it — keeps a message matching several
|
||||||
|
// background tabs from stacking sounds.
|
||||||
|
// TEST-MIRROR: ../_Helpers/TabSoundDecision.cs
|
||||||
|
if (notificationSound is null
|
||||||
|
&& TabSoundDecision.ShouldPlay(
|
||||||
|
Plugin.CurrentTab == tab,
|
||||||
|
tab.EnableNotificationSound,
|
||||||
|
Plugin.Config.PlaySounds))
|
||||||
|
{
|
||||||
|
notificationSound = tab.NotificationSoundId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notificationSound is { } soundId)
|
||||||
|
{
|
||||||
|
// ProcessMessage runs on the PendingMessageThread worker; the native
|
||||||
|
// UIGlobals.PlaySoundEffect must be marshalled onto the framework
|
||||||
|
// thread (reference_dalamud_framework_thread).
|
||||||
|
Plugin.Framework.RunOnFrameworkThread(
|
||||||
|
() => { unsafe { UIGlobals.PlaySoundEffect(soundId); } });
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageProcessed?.Invoke(message);
|
MessageProcessed?.Invoke(message);
|
||||||
|
|||||||
@@ -165,6 +165,27 @@ internal sealed class Tabs : ISettingsTab
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_Tabs_ShowTimestamps, ref tab.DisplayTimestamp);
|
ImGui.Checkbox(Language.Options_Tabs_ShowTimestamps, ref tab.DisplayTimestamp);
|
||||||
|
ImGui.Checkbox(
|
||||||
|
HellionStrings.Tabs_NotificationSound_Enable_Name,
|
||||||
|
ref tab.EnableNotificationSound
|
||||||
|
);
|
||||||
|
ImGuiUtil.HelpMarker(HellionStrings.Tabs_NotificationSound_Description);
|
||||||
|
if (tab.EnableNotificationSound)
|
||||||
|
{
|
||||||
|
using var indent = ImRaii.PushIndent(10.0f);
|
||||||
|
var soundPreview = $"{HellionStrings.Tabs_NotificationSound_Option} {tab.NotificationSoundId}";
|
||||||
|
using var combo = ImRaii.Combo($"##notif-sound-{i}", soundPreview);
|
||||||
|
if (combo.Success)
|
||||||
|
{
|
||||||
|
for (uint s = 1; s <= 16; s++)
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable(
|
||||||
|
$"{HellionStrings.Tabs_NotificationSound_Option} {s}",
|
||||||
|
tab.NotificationSoundId == s))
|
||||||
|
tab.NotificationSoundId = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImGui.Checkbox(Language.Options_Tabs_PopOut, ref tab.PopOut);
|
ImGui.Checkbox(Language.Options_Tabs_PopOut, ref tab.PopOut);
|
||||||
if (tab.PopOut)
|
if (tab.PopOut)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace HellionChat._Helpers;
|
||||||
|
|
||||||
|
// UI-3 pure decision helper: should an incoming message play a per-tab
|
||||||
|
// notification sound? Kept Dalamud-free so the Build Suite can test the
|
||||||
|
// "inactive + enabled + global-allowed" rule in isolation.
|
||||||
|
// TEST-MIRROR: ../../../Hellion Build test/Ui/TabSoundDecisionTests.cs
|
||||||
|
public static class TabSoundDecision
|
||||||
|
{
|
||||||
|
// True only when the message landed in a tab the user is not looking at,
|
||||||
|
// that tab has its own sound switched on, and the global sound master is
|
||||||
|
// not muted.
|
||||||
|
public static bool ShouldPlay(bool isActiveTab, bool tabSoundEnabled, bool globalSoundsEnabled)
|
||||||
|
=> !isActiveTab && tabSoundEnabled && globalSoundsEnabled;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user