mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 13:14:17 +01:00
Add configurable "anchor position" for notifications
This commit is contained in:
parent
20ef5fb919
commit
2b49170f6a
7 changed files with 401 additions and 16 deletions
|
|
@ -3,12 +3,14 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.FontIdentifier;
|
using Dalamud.Interface.FontIdentifier;
|
||||||
|
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Interface.Internal.ReShadeHandling;
|
using Dalamud.Interface.Internal.ReShadeHandling;
|
||||||
using Dalamud.Interface.Style;
|
using Dalamud.Interface.Style;
|
||||||
|
|
@ -501,6 +503,11 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SendUpdateNotificationToChat { get; set; } = false;
|
public bool SendUpdateNotificationToChat { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating where notifications are anchored to on the screen.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 NotificationAnchorPosition { get; set; } = new(1f, 1f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load a configuration from the provided path.
|
/// Load a configuration from the provided path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,9 @@ internal sealed partial class ActiveNotification
|
||||||
/// <summary>Draws this notification.</summary>
|
/// <summary>Draws this notification.</summary>
|
||||||
/// <param name="width">The maximum width of the notification window.</param>
|
/// <param name="width">The maximum width of the notification window.</param>
|
||||||
/// <param name="offsetY">The offset from the bottom.</param>
|
/// <param name="offsetY">The offset from the bottom.</param>
|
||||||
|
/// <param name="anchorPosition">Where notifications are anchored to on the screen.</param>
|
||||||
/// <returns>The height of the notification.</returns>
|
/// <returns>The height of the notification.</returns>
|
||||||
public float Draw(float width, float offsetY)
|
public float Draw(float width, float offsetY, Vector2 anchorPosition, NotificationSnapDirection snapDirection)
|
||||||
{
|
{
|
||||||
var opacity =
|
var opacity =
|
||||||
Math.Clamp(
|
Math.Clamp(
|
||||||
|
|
@ -35,7 +36,6 @@ internal sealed partial class ActiveNotification
|
||||||
(NotificationConstants.ScaledWindowPadding * 2);
|
(NotificationConstants.ScaledWindowPadding * 2);
|
||||||
|
|
||||||
var viewport = ImGuiHelpers.MainViewport;
|
var viewport = ImGuiHelpers.MainViewport;
|
||||||
var viewportPos = viewport.WorkPos;
|
|
||||||
var viewportSize = viewport.WorkSize;
|
var viewportSize = viewport.WorkSize;
|
||||||
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, opacity);
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, opacity);
|
||||||
|
|
@ -52,13 +52,78 @@ internal sealed partial class ActiveNotification
|
||||||
NotificationConstants.BackgroundOpacity));
|
NotificationConstants.BackgroundOpacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 topLeft;
|
||||||
|
Vector2 pivot;
|
||||||
|
if (snapDirection is NotificationSnapDirection.Top or NotificationSnapDirection.Bottom)
|
||||||
|
{
|
||||||
|
// Top or bottom
|
||||||
|
var xPos = (viewportSize.X - width) * anchorPosition.X;
|
||||||
|
xPos = Math.Max(NotificationConstants.ScaledViewportEdgeMargin, Math.Min(viewportSize.X - width - NotificationConstants.ScaledViewportEdgeMargin, xPos));
|
||||||
|
|
||||||
|
if (snapDirection == NotificationSnapDirection.Top)
|
||||||
|
{
|
||||||
|
// Top
|
||||||
|
var yPos = NotificationConstants.ScaledViewportEdgeMargin - offsetY;
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Bottom
|
||||||
|
var yPos = viewportSize.Y - offsetY - NotificationConstants.ScaledViewportEdgeMargin;
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Left or Right
|
||||||
|
var yPos = (viewportSize.Y * anchorPosition.Y) - offsetY;
|
||||||
|
yPos = Math.Max(
|
||||||
|
NotificationConstants.ScaledViewportEdgeMargin,
|
||||||
|
Math.Min(viewportSize.Y - offsetY - NotificationConstants.ScaledViewportEdgeMargin, yPos));
|
||||||
|
|
||||||
|
if (snapDirection == NotificationSnapDirection.Left)
|
||||||
|
{
|
||||||
|
// Left
|
||||||
|
var xPos = NotificationConstants.ScaledViewportEdgeMargin;
|
||||||
|
|
||||||
|
if (anchorPosition.Y > 0.5f)
|
||||||
|
{
|
||||||
|
// Bottom
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Top
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Right
|
||||||
|
var xPos = viewportSize.X - width - NotificationConstants.ScaledViewportEdgeMargin;
|
||||||
|
|
||||||
|
if (anchorPosition.Y > 0.5f)
|
||||||
|
{
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
pivot = new(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGuiHelpers.ForceNextWindowMainViewport();
|
ImGuiHelpers.ForceNextWindowMainViewport();
|
||||||
ImGui.SetNextWindowPos(
|
ImGui.SetNextWindowPos(
|
||||||
(viewportPos + viewportSize) -
|
topLeft,
|
||||||
new Vector2(NotificationConstants.ScaledViewportEdgeMargin) -
|
|
||||||
new Vector2(0, offsetY),
|
|
||||||
ImGuiCond.Always,
|
ImGuiCond.Always,
|
||||||
Vector2.One);
|
pivot);
|
||||||
ImGui.SetNextWindowSizeConstraints(
|
ImGui.SetNextWindowSizeConstraints(
|
||||||
new(width, actionWindowHeight),
|
new(width, actionWindowHeight),
|
||||||
new(
|
new(
|
||||||
|
|
@ -142,7 +207,7 @@ internal sealed partial class ActiveNotification
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
ImGui.PopStyleVar(3);
|
ImGui.PopStyleVar(3);
|
||||||
|
|
||||||
return windowSize.Y;
|
return NotificationManager.ShouldScrollDownwards(anchorPosition) ? -windowSize.Y : windowSize.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Calculates the effective expiry, taking ImGui window state into account.</summary>
|
/// <summary>Calculates the effective expiry, taking ImGui window state into account.</summary>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ internal static class NotificationConstants
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float ProgressWaveLoopMaxColorTimeRatio = 0.7f;
|
public const float ProgressWaveLoopMaxColorTimeRatio = 0.7f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ratio of the screen at which the notification window will snap to the top or bottom of the screen.
|
||||||
|
/// </summary>
|
||||||
|
public const float NotificationTopBottomSnapMargin = 0.08f;
|
||||||
|
|
||||||
/// <summary>Default duration of the notification.</summary>
|
/// <summary>Default duration of the notification.</summary>
|
||||||
public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds(7);
|
public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds(7);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Interface.GameFonts;
|
using Dalamud.Interface.GameFonts;
|
||||||
using Dalamud.Interface.ManagedFontAtlas;
|
using Dalamud.Interface.ManagedFontAtlas;
|
||||||
|
|
@ -22,9 +24,14 @@ internal class NotificationManager : INotificationManager, IInternalDisposableSe
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly GameGui gameGui = Service<GameGui>.Get();
|
private readonly GameGui gameGui = Service<GameGui>.Get();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
private readonly List<ActiveNotification> notifications = new();
|
private readonly List<ActiveNotification> notifications = new();
|
||||||
private readonly ConcurrentBag<ActiveNotification> pendingNotifications = new();
|
private readonly ConcurrentBag<ActiveNotification> pendingNotifications = new();
|
||||||
|
|
||||||
|
private NotificationPositionChooser? positionChooser;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private NotificationManager(FontAtlasFactory fontAtlasFactory)
|
private NotificationManager(FontAtlasFactory fontAtlasFactory)
|
||||||
{
|
{
|
||||||
|
|
@ -48,6 +55,48 @@ internal class NotificationManager : INotificationManager, IInternalDisposableSe
|
||||||
/// <summary>Gets the private atlas for use with notification windows.</summary>
|
/// <summary>Gets the private atlas for use with notification windows.</summary>
|
||||||
private IFontAtlas PrivateAtlas { get; }
|
private IFontAtlas PrivateAtlas { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate the width to be used to draw notifications.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The width.</returns>
|
||||||
|
public static float CalculateNotificationWidth()
|
||||||
|
{
|
||||||
|
var viewportSize = ImGuiHelpers.MainViewport.WorkSize;
|
||||||
|
var width = ImGui.CalcTextSize(NotificationConstants.NotificationWidthMeasurementString).X;
|
||||||
|
width += NotificationConstants.ScaledWindowPadding * 3;
|
||||||
|
width += NotificationConstants.ScaledIconSize;
|
||||||
|
return Math.Min(width, viewportSize.X * NotificationConstants.MaxNotificationWindowWidthWrtMainViewportWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if notifications should scroll downwards on the screen, based on the anchor position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="anchorPosition">Where notifications are anchored to.</param>
|
||||||
|
/// <returns>A value indicating wether notifications should scroll downwards.</returns>
|
||||||
|
public static bool ShouldScrollDownwards(Vector2 anchorPosition)
|
||||||
|
{
|
||||||
|
return anchorPosition.Y < 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Choose the snap position for a notification based on the anchor position.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="anchorPosition">Where notifications are anchored to.</param>
|
||||||
|
/// <returns>The snap position.</returns>
|
||||||
|
public static NotificationSnapDirection ChooseSnapDirection(Vector2 anchorPosition)
|
||||||
|
{
|
||||||
|
if (anchorPosition.Y <= NotificationConstants.NotificationTopBottomSnapMargin)
|
||||||
|
return NotificationSnapDirection.Top;
|
||||||
|
|
||||||
|
if (anchorPosition.Y >= 1f - NotificationConstants.NotificationTopBottomSnapMargin)
|
||||||
|
return NotificationSnapDirection.Bottom;
|
||||||
|
|
||||||
|
if (anchorPosition.X <= 0.5f)
|
||||||
|
return NotificationSnapDirection.Left;
|
||||||
|
|
||||||
|
return NotificationSnapDirection.Right;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void DisposeService()
|
public void DisposeService()
|
||||||
{
|
{
|
||||||
|
|
@ -98,25 +147,38 @@ internal class NotificationManager : INotificationManager, IInternalDisposableSe
|
||||||
/// <summary>Draw all currently queued notifications.</summary>
|
/// <summary>Draw all currently queued notifications.</summary>
|
||||||
public void Draw()
|
public void Draw()
|
||||||
{
|
{
|
||||||
var viewportSize = ImGuiHelpers.MainViewport.WorkSize;
|
|
||||||
var height = 0f;
|
var height = 0f;
|
||||||
var uiHidden = this.gameGui.GameUiHidden;
|
var uiHidden = this.gameGui.GameUiHidden;
|
||||||
|
|
||||||
while (this.pendingNotifications.TryTake(out var newNotification))
|
while (this.pendingNotifications.TryTake(out var newNotification))
|
||||||
this.notifications.Add(newNotification);
|
this.notifications.Add(newNotification);
|
||||||
|
|
||||||
var width = ImGui.CalcTextSize(NotificationConstants.NotificationWidthMeasurementString).X;
|
var width = CalculateNotificationWidth();
|
||||||
width += NotificationConstants.ScaledWindowPadding * 3;
|
|
||||||
width += NotificationConstants.ScaledIconSize;
|
|
||||||
width = Math.Min(width, viewportSize.X * NotificationConstants.MaxNotificationWindowWidthWrtMainViewportWidth);
|
|
||||||
|
|
||||||
this.notifications.RemoveAll(static x => x.UpdateOrDisposeInternal());
|
this.notifications.RemoveAll(static x => x.UpdateOrDisposeInternal());
|
||||||
|
|
||||||
|
var scrollsDownwards = ShouldScrollDownwards(this.configuration.NotificationAnchorPosition);
|
||||||
|
var snapDirection = ChooseSnapDirection(this.configuration.NotificationAnchorPosition);
|
||||||
|
|
||||||
foreach (var tn in this.notifications)
|
foreach (var tn in this.notifications)
|
||||||
{
|
{
|
||||||
if (uiHidden && tn.RespectUiHidden)
|
if (uiHidden && tn.RespectUiHidden)
|
||||||
continue;
|
continue;
|
||||||
height += tn.Draw(width, height) + NotificationConstants.ScaledWindowGap;
|
|
||||||
|
height += tn.Draw(width, height, this.configuration.NotificationAnchorPosition, snapDirection);
|
||||||
|
height += scrollsDownwards ? -NotificationConstants.ScaledWindowGap : NotificationConstants.ScaledWindowGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.positionChooser?.Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the position chooser for notifications. Will block the UI until the user makes a selection.
|
||||||
|
/// </summary>
|
||||||
|
public void StartPositionChooser()
|
||||||
|
{
|
||||||
|
this.positionChooser = new NotificationPositionChooser(this.configuration);
|
||||||
|
this.positionChooser.SelectionMade += () => { this.positionChooser = null; };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class responsible for drawing UI that lets users choose the position of notifications.
|
||||||
|
/// </summary>
|
||||||
|
internal class NotificationPositionChooser
|
||||||
|
{
|
||||||
|
private readonly DalamudConfiguration configuration;
|
||||||
|
private readonly Vector2 previousAnchorPosition;
|
||||||
|
|
||||||
|
private Vector2 currentAnchorPosition;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="NotificationPositionChooser"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">The configuration we are reading or writing from.</param>
|
||||||
|
public NotificationPositionChooser(DalamudConfiguration configuration)
|
||||||
|
{
|
||||||
|
this.configuration = configuration;
|
||||||
|
this.previousAnchorPosition = configuration.NotificationAnchorPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an action that is invoked when the user makes a selection.
|
||||||
|
/// </summary>
|
||||||
|
public event Action? SelectionMade;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draw the chooser UI.
|
||||||
|
/// </summary>
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
using var style1 = ImRaii.PushStyle(ImGuiStyleVar.WindowRounding, 0f);
|
||||||
|
using var style2 = ImRaii.PushStyle(ImGuiStyleVar.WindowBorderSize, 0f);
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0, 0, 0, 0));
|
||||||
|
|
||||||
|
ImGui.SetNextWindowFocus();
|
||||||
|
ImGui.SetNextWindowPos(ImGuiHelpers.MainViewport.Pos);
|
||||||
|
ImGui.SetNextWindowSize(ImGuiHelpers.MainViewport.Size);
|
||||||
|
ImGuiHelpers.ForceNextWindowMainViewport();
|
||||||
|
|
||||||
|
ImGui.SetNextWindowBgAlpha(0.6f);
|
||||||
|
|
||||||
|
ImGui.Begin(
|
||||||
|
"###NotificationPositionChooser",
|
||||||
|
ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoMove |
|
||||||
|
ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav);
|
||||||
|
|
||||||
|
var mousePosUnit = ImGui.GetMousePos() / ImGuiHelpers.MainViewport.Size;
|
||||||
|
|
||||||
|
// Store the offset as a Vector2
|
||||||
|
this.currentAnchorPosition = mousePosUnit;
|
||||||
|
|
||||||
|
DrawPreview(this.previousAnchorPosition, 0.3f);
|
||||||
|
DrawPreview(this.currentAnchorPosition, 1f);
|
||||||
|
|
||||||
|
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
|
||||||
|
{
|
||||||
|
this.SelectionMade?.Invoke();
|
||||||
|
}
|
||||||
|
else if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
|
||||||
|
{
|
||||||
|
this.configuration.NotificationAnchorPosition = this.currentAnchorPosition;
|
||||||
|
this.configuration.QueueSave();
|
||||||
|
|
||||||
|
this.SelectionMade?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the middle of the screen, draw some instructions
|
||||||
|
string[] instructions = ["Drag to move the notifications to where you would like them to appear.",
|
||||||
|
"Click to select the position.",
|
||||||
|
"Right-click to close without making changes."];
|
||||||
|
|
||||||
|
var dl = ImGui.GetWindowDrawList();
|
||||||
|
for (var i = 0; i < instructions.Length; i++)
|
||||||
|
{
|
||||||
|
var instruction = instructions[i];
|
||||||
|
var instructionSize = ImGui.CalcTextSize(instruction);
|
||||||
|
var instructionPos = new Vector2(
|
||||||
|
ImGuiHelpers.MainViewport.Size.X / 2 - instructionSize.X / 2,
|
||||||
|
ImGuiHelpers.MainViewport.Size.Y / 2 - instructionSize.Y / 2 + i * instructionSize.Y);
|
||||||
|
dl.AddText(instructionPos, 0xFFFFFFFF, instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawPreview(Vector2 anchorPosition, float borderAlpha)
|
||||||
|
{
|
||||||
|
var dl = ImGui.GetWindowDrawList();
|
||||||
|
var width = NotificationManager.CalculateNotificationWidth();
|
||||||
|
var height = 100f * ImGuiHelpers.GlobalScale;
|
||||||
|
var smallBoxHeight = height * 0.4f;
|
||||||
|
var edgeMargin = NotificationConstants.ScaledViewportEdgeMargin;
|
||||||
|
var spacing = 10f * ImGuiHelpers.GlobalScale;
|
||||||
|
|
||||||
|
var viewportSize = ImGuiHelpers.MainViewport.Size;
|
||||||
|
var borderColor = ImGui.ColorConvertFloat4ToU32(new(1f, 1f, 1f, borderAlpha));
|
||||||
|
var borderThickness = 4.0f * ImGuiHelpers.GlobalScale;
|
||||||
|
var borderRounding = 4.0f * ImGuiHelpers.GlobalScale;
|
||||||
|
var backgroundColor = new Vector4(0, 0, 0, 0.5f); // Semi-transparent black
|
||||||
|
|
||||||
|
// Calculate positions based on the snap position
|
||||||
|
Vector2 topLeft, bottomRight, smallTopLeft, smallBottomRight;
|
||||||
|
|
||||||
|
var snapPos = NotificationManager.ChooseSnapDirection(anchorPosition);
|
||||||
|
if (snapPos is NotificationSnapDirection.Top or NotificationSnapDirection.Bottom)
|
||||||
|
{
|
||||||
|
// Calculate X position - same logic for top and bottom
|
||||||
|
var xPos = (viewportSize.X - width) * anchorPosition.X;
|
||||||
|
xPos = Math.Max(edgeMargin, Math.Min(viewportSize.X - width - edgeMargin, xPos));
|
||||||
|
|
||||||
|
if (snapPos == NotificationSnapDirection.Top)
|
||||||
|
{
|
||||||
|
// For top position: big box at top, small box below it
|
||||||
|
var yPos = edgeMargin;
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos + height + spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos + height + spacing + smallBoxHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For bottom position: big box at bottom, small box above it
|
||||||
|
var yPos = viewportSize.Y - height - edgeMargin;
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos - smallBoxHeight - spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos - spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For left and right positions, boxes are still stacked vertically (one above the other)
|
||||||
|
// Only the horizontal position changes
|
||||||
|
|
||||||
|
// Calculate Y position based on unit offset - used for both left and right positions
|
||||||
|
var yPos = (viewportSize.Y - height) * anchorPosition.Y;
|
||||||
|
yPos = Math.Max(edgeMargin, Math.Min(viewportSize.Y - height - edgeMargin, yPos));
|
||||||
|
|
||||||
|
if (snapPos == NotificationSnapDirection.Left)
|
||||||
|
{
|
||||||
|
// For left position: boxes are at the left edge of the screen
|
||||||
|
var xPos = edgeMargin;
|
||||||
|
|
||||||
|
if (anchorPosition.Y > 0.5f)
|
||||||
|
{
|
||||||
|
// Small box on top
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos - smallBoxHeight - spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos - spacing);
|
||||||
|
|
||||||
|
// Big box below
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Big box on top
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
|
||||||
|
// Small box below
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos + height + spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos + height + spacing + smallBoxHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For right position: boxes are at the right edge of the screen
|
||||||
|
var xPos = viewportSize.X - width - edgeMargin;
|
||||||
|
|
||||||
|
if (anchorPosition.Y > 0.5f)
|
||||||
|
{
|
||||||
|
// Small box on top
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos - smallBoxHeight - spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos - spacing);
|
||||||
|
|
||||||
|
// Big box below
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Big box on top
|
||||||
|
topLeft = new Vector2(xPos, yPos);
|
||||||
|
bottomRight = new Vector2(xPos + width, yPos + height);
|
||||||
|
|
||||||
|
// Small box below
|
||||||
|
smallTopLeft = new Vector2(xPos, yPos + height + spacing);
|
||||||
|
smallBottomRight = new Vector2(xPos + width, yPos + height + spacing + smallBoxHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the big box
|
||||||
|
dl.AddRectFilled(topLeft, bottomRight, ImGui.ColorConvertFloat4ToU32(backgroundColor), borderRounding, ImDrawFlags.RoundCornersAll);
|
||||||
|
dl.AddRect(topLeft, bottomRight, borderColor, borderRounding, ImDrawFlags.RoundCornersAll, borderThickness);
|
||||||
|
|
||||||
|
// Draw the small box
|
||||||
|
dl.AddRectFilled(smallTopLeft, smallBottomRight, ImGui.ColorConvertFloat4ToU32(backgroundColor), borderRounding, ImDrawFlags.RoundCornersAll);
|
||||||
|
dl.AddRect(smallTopLeft, smallBottomRight, borderColor, borderRounding, ImDrawFlags.RoundCornersAll, borderThickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where notifications should snap to on the screen when they are shown.
|
||||||
|
/// </summary>
|
||||||
|
public enum NotificationSnapDirection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Snap to the top of the screen.
|
||||||
|
/// </summary>
|
||||||
|
Top,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snap to the bottom of the screen.
|
||||||
|
/// </summary>
|
||||||
|
Bottom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snap to the left of the screen.
|
||||||
|
/// </summary>
|
||||||
|
Left,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snap to the right of the screen.
|
||||||
|
/// </summary>
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.FontIdentifier;
|
using Dalamud.Interface.FontIdentifier;
|
||||||
using Dalamud.Interface.GameFonts;
|
using Dalamud.Interface.GameFonts;
|
||||||
using Dalamud.Interface.ImGuiFontChooserDialog;
|
using Dalamud.Interface.ImGuiFontChooserDialog;
|
||||||
|
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||||
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
|
using Dalamud.Interface.Internal.Windows.Settings.Widgets;
|
||||||
using Dalamud.Interface.ManagedFontAtlas.Internals;
|
using Dalamud.Interface.ManagedFontAtlas.Internals;
|
||||||
|
|
@ -38,7 +39,7 @@ public class SettingsTabLook : SettingsTab
|
||||||
private IFontSpec defaultFontSpec = null!;
|
private IFontSpec defaultFontSpec = null!;
|
||||||
|
|
||||||
public override SettingsEntry[] Entries { get; } =
|
public override SettingsEntry[] Entries { get; } =
|
||||||
{
|
[
|
||||||
new GapSettingsEntry(5, true),
|
new GapSettingsEntry(5, true),
|
||||||
|
|
||||||
new ButtonSettingsEntry(
|
new ButtonSettingsEntry(
|
||||||
|
|
@ -46,6 +47,11 @@ public class SettingsTabLook : SettingsTab
|
||||||
Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
|
Loc.Localize("DalamudSettingsStyleEditorHint", "Modify the look & feel of Dalamud windows."),
|
||||||
() => Service<DalamudInterface>.Get().OpenStyleEditor()),
|
() => Service<DalamudInterface>.Get().OpenStyleEditor()),
|
||||||
|
|
||||||
|
new ButtonSettingsEntry(
|
||||||
|
Loc.Localize("DalamudSettingsOpenNotificationEditor", "Modify Notification Position"),
|
||||||
|
Loc.Localize("DalamudSettingsNotificationEditorHint", "Choose where Dalamud notifications appear on the screen."),
|
||||||
|
() => Service<NotificationManager>.Get().StartPositionChooser()),
|
||||||
|
|
||||||
new SettingsEntry<bool>(
|
new SettingsEntry<bool>(
|
||||||
Loc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"),
|
Loc.Localize("DalamudSettingsUseDarkMode", "Use Windows immersive/dark mode"),
|
||||||
Loc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."),
|
Loc.Localize("DalamudSettingsUseDarkModeHint", "This will cause the FFXIV window title bar to follow your preferred Windows color settings, and switch to dark mode if enabled."),
|
||||||
|
|
@ -167,8 +173,8 @@ public class SettingsTabLook : SettingsTab
|
||||||
ImGui.TextUnformatted("\uE020\uE021\uE022\uE023\uE024\uE025\uE026\uE027");
|
ImGui.TextUnformatted("\uE020\uE021\uE022\uE023\uE024\uE025\uE026\uE027");
|
||||||
ImGui.PopStyleVar(1);
|
ImGui.PopStyleVar(1);
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
};
|
];
|
||||||
|
|
||||||
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
|
public override string Title => Loc.Localize("DalamudSettingsVisual", "Look & Feel");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue