mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Interfacing window system
This commit is contained in:
parent
02e0f1d36c
commit
2dd9ecd8e8
7 changed files with 1040 additions and 947 deletions
210
Dalamud/Interface/Windowing/IWindow.cs
Normal file
210
Dalamud/Interface/Windowing/IWindow.cs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
// <copyright file="IWindow.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a ImGui window for use with the built-in <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
public interface IWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the namespace of the window.
|
||||
/// </summary>
|
||||
string? Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the window.
|
||||
/// If you have multiple windows with the same name, you will need to
|
||||
/// append an unique ID to it by specifying it after "###" behind the window title.
|
||||
/// </summary>
|
||||
string WindowName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the window is focused.
|
||||
/// </summary>
|
||||
bool IsFocused { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window is to be closed with a hotkey, like Escape, and keep game addons open in turn if it is closed.
|
||||
/// </summary>
|
||||
bool RespectCloseHotkey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window should not generate sound effects when opening and closing.
|
||||
/// </summary>
|
||||
bool DisableWindowSounds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value representing the sound effect id to be played when the window is opened.
|
||||
/// </summary>
|
||||
uint OnOpenSfxId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value representing the sound effect id to be played when the window is closed.
|
||||
/// </summary>
|
||||
uint OnCloseSfxId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window should not fade in and out, regardless of the users'
|
||||
/// preference.
|
||||
/// </summary>
|
||||
bool DisableFadeInFadeOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of this window.
|
||||
/// </summary>
|
||||
Vector2? Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the condition that defines when the position of this window is set.
|
||||
/// </summary>
|
||||
ImGuiCond PositionCondition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the window. The size provided will be scaled by the global scale.
|
||||
/// </summary>
|
||||
Vector2? Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the condition that defines when the size of this window is set.
|
||||
/// </summary>
|
||||
ImGuiCond SizeCondition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size constraints of the window. The size constraints provided will be scaled by the global scale.
|
||||
/// </summary>
|
||||
WindowSizeConstraints? SizeConstraints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window is collapsed.
|
||||
/// </summary>
|
||||
bool? Collapsed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the condition that defines when the collapsed state of this window is set.
|
||||
/// </summary>
|
||||
ImGuiCond CollapsedCondition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the window flags.
|
||||
/// </summary>
|
||||
ImGuiWindowFlags Flags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this ImGui window will be forced to stay inside the main game window.
|
||||
/// </summary>
|
||||
bool ForceMainWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets this window's background alpha value.
|
||||
/// </summary>
|
||||
float? BgAlpha { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this ImGui window should display a close button in the title bar.
|
||||
/// </summary>
|
||||
bool ShowCloseButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window should offer to be pinned via the window's titlebar context menu.
|
||||
/// </summary>
|
||||
bool AllowPinning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window should offer to be made click-through via the window's titlebar context menu.
|
||||
/// </summary>
|
||||
bool AllowClickthrough { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of available title bar buttons.
|
||||
///
|
||||
/// If <see cref="AllowPinning"/> or <see cref="AllowClickthrough"/> are set to true, and this features is not
|
||||
/// disabled globally by the user, an internal title bar button to manage these is added when drawing, but it will
|
||||
/// not appear in this collection. If you wish to remove this button, set both of these values to false.
|
||||
/// </summary>
|
||||
List<TitleBarButton> TitleBarButtons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window will stay open.
|
||||
/// </summary>
|
||||
bool IsOpen { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this window will request focus from the window system next frame
|
||||
/// </summary>
|
||||
public bool RequestFocus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Toggle window is open state.
|
||||
/// </summary>
|
||||
void Toggle();
|
||||
|
||||
/// <summary>
|
||||
/// Bring this window to the front.
|
||||
/// </summary>
|
||||
void BringToFront();
|
||||
|
||||
/// <summary>
|
||||
/// Code to always be executed before the open-state of the window is checked.
|
||||
/// </summary>
|
||||
void PreOpenCheck();
|
||||
|
||||
/// <summary>
|
||||
/// Additional conditions for the window to be drawn, regardless of its open-state.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// True if the window should be drawn, false otherwise.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Not being drawn due to failing this condition will not change focus or trigger OnClose.
|
||||
/// This is checked before PreDraw, but after Update.
|
||||
/// </remarks>
|
||||
bool DrawConditions();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed before conditionals are applied and the window is drawn.
|
||||
/// </summary>
|
||||
void PreDraw();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed after the window is drawn.
|
||||
/// </summary>
|
||||
void PostDraw();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed every time the window renders.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In this method, implement your drawing code.
|
||||
/// You do NOT need to ImGui.Begin your window.
|
||||
/// </remarks>
|
||||
void Draw();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed when the window is opened.
|
||||
/// </summary>
|
||||
void OnOpen();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed when the window is closed.
|
||||
/// </summary>
|
||||
void OnClose();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed when the window is safe to be disposed or removed from the window system.
|
||||
/// Doing so in <see cref="WindowHost.OnClose"/> may result in animations not playing correctly.
|
||||
/// </summary>
|
||||
void OnSafeToRemove();
|
||||
|
||||
/// <summary>
|
||||
/// Code to be executed every frame, even when the window is collapsed.
|
||||
/// </summary>
|
||||
void Update();
|
||||
}
|
||||
52
Dalamud/Interface/Windowing/IWindowSystem.cs
Normal file
52
Dalamud/Interface/Windowing/IWindowSystem.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// <copyright file="IWindowSystem.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
public interface IWindowSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a read-only list of all <see cref="IWindow"/>s in this <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
IReadOnlyList<IWindow> Windows { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any window in this <see cref="WindowSystem"/> has focus and is
|
||||
/// not marked to be excluded from consideration.
|
||||
/// </summary>
|
||||
bool HasAnyFocus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name/ID-space of this <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
string? Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Add a window to this <see cref="WindowSystem"/>.
|
||||
/// The window system doesn't own your window, it just renders it
|
||||
/// You need to store a reference to it to use it later.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to add.</param>
|
||||
void AddWindow(IWindow window);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a window from this <see cref="WindowSystem"/>.
|
||||
/// Will not dispose your window, if it is disposable.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to remove.</param>
|
||||
void RemoveWindow(IWindow window);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all windows from this <see cref="WindowSystem"/>.
|
||||
/// Will not dispose your windows, if they are disposable.
|
||||
/// </summary>
|
||||
void RemoveAllWindows();
|
||||
|
||||
/// <summary>
|
||||
/// Draw all registered windows using ImGui.
|
||||
/// </summary>
|
||||
void Draw();
|
||||
}
|
||||
45
Dalamud/Interface/Windowing/TitleBarButton.cs
Normal file
45
Dalamud/Interface/Windowing/TitleBarButton.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// Structure describing a title bar button.
|
||||
/// </summary>
|
||||
public class TitleBarButton
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the icon of the button.
|
||||
/// </summary>
|
||||
public FontAwesomeIcon Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a vector by which the position of the icon within the button shall be offset.
|
||||
/// Automatically scaled by the global font scale for you.
|
||||
/// </summary>
|
||||
public Vector2 IconOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that is called when a tooltip shall be drawn.
|
||||
/// May be null if no tooltip shall be drawn.
|
||||
/// </summary>
|
||||
public Action? ShowTooltip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an action that is called when the button is clicked.
|
||||
/// </summary>
|
||||
public Action<ImGuiMouseButton> Click { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the priority the button shall be shown in.
|
||||
/// Lower = closer to ImGui default buttons.
|
||||
/// </summary>
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the button shall be clickable
|
||||
/// when the respective window is set to clickthrough.
|
||||
/// </summary>
|
||||
public bool AvailableClickthrough { get; set; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
647
Dalamud/Interface/Windowing/WindowHost.cs
Normal file
647
Dalamud/Interface/Windowing/WindowHost.cs
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using CheapLoc;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Internal;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing.Persistence;
|
||||
using Dalamud.Logging.Internal;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// Base class you can use to implement an ImGui window for use with the built-in <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
public class WindowHost
|
||||
{
|
||||
private const float FadeInOutTime = 0.072f;
|
||||
|
||||
private static readonly ModuleLog Log = new("WindowSystem");
|
||||
|
||||
private static bool wasEscPressedLastFrame = false;
|
||||
|
||||
private bool internalLastIsOpen = false;
|
||||
private bool internalIsOpen = false;
|
||||
private bool internalIsPinned = false;
|
||||
private bool internalIsClickthrough = false;
|
||||
private bool didPushInternalAlpha = false;
|
||||
private float? internalAlpha = null;
|
||||
|
||||
private bool hasInitializedFromPreset = false;
|
||||
private PresetModel.PresetWindow? presetWindow;
|
||||
private bool presetDirty = false;
|
||||
|
||||
private bool pushedFadeInAlpha = false;
|
||||
private float fadeInTimer = 0f;
|
||||
private float fadeOutTimer = 0f;
|
||||
private IDrawListTextureWrap? fadeOutTexture = null;
|
||||
private Vector2 fadeOutSize = Vector2.Zero;
|
||||
private Vector2 fadeOutOrigin = Vector2.Zero;
|
||||
|
||||
internal WindowHost(IWindow window)
|
||||
{
|
||||
this.Window = window;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags to control window behavior.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum WindowDrawFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Enable window opening/closing sound effects.
|
||||
/// </summary>
|
||||
UseSoundEffects = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Hook into the game's focus management.
|
||||
/// </summary>
|
||||
UseFocusManagement = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Enable the built-in "additional options" menu on the title bar.
|
||||
/// </summary>
|
||||
UseAdditionalOptions = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Do not draw non-critical animations.
|
||||
/// </summary>
|
||||
IsReducedMotion = 1 << 3,
|
||||
}
|
||||
|
||||
private bool CanShowCloseButton => this.Window.ShowCloseButton && !this.internalIsClickthrough;
|
||||
|
||||
public IWindow Window { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Draw the window via ImGui.
|
||||
/// </summary>
|
||||
/// <param name="internalDrawFlags">Flags controlling window behavior.</param>
|
||||
/// <param name="persistence">Handler for window persistence data.</param>
|
||||
internal void DrawInternal(WindowDrawFlags internalDrawFlags, WindowSystemPersistence? persistence)
|
||||
{
|
||||
this.Window.PreOpenCheck();
|
||||
var doFades = !internalDrawFlags.HasFlag(WindowDrawFlags.IsReducedMotion) && !this.Window.DisableFadeInFadeOut;
|
||||
|
||||
if (!this.Window.IsOpen)
|
||||
{
|
||||
if (this.internalIsOpen != this.internalLastIsOpen)
|
||||
{
|
||||
this.internalLastIsOpen = this.internalIsOpen;
|
||||
this.Window.OnClose();
|
||||
|
||||
this.Window.IsFocused = false;
|
||||
|
||||
if (internalDrawFlags.HasFlag(WindowDrawFlags.UseSoundEffects) && !this.Window.DisableWindowSounds)
|
||||
UIGlobals.PlaySoundEffect(this.Window.OnCloseSfxId);
|
||||
}
|
||||
|
||||
if (this.fadeOutTexture != null)
|
||||
{
|
||||
this.fadeOutTimer -= ImGui.GetIO().DeltaTime;
|
||||
if (this.fadeOutTimer <= 0f)
|
||||
{
|
||||
this.fadeOutTexture.Dispose();
|
||||
this.fadeOutTexture = null;
|
||||
this.Window.OnSafeToRemove();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DrawFakeFadeOutWindow();
|
||||
}
|
||||
}
|
||||
|
||||
this.fadeInTimer = doFades ? 0f : FadeInOutTime;
|
||||
return;
|
||||
}
|
||||
|
||||
this.fadeInTimer += ImGui.GetIO().DeltaTime;
|
||||
if (this.fadeInTimer > FadeInOutTime)
|
||||
this.fadeInTimer = FadeInOutTime;
|
||||
|
||||
this.Window.Update();
|
||||
if (!this.Window.DrawConditions())
|
||||
return;
|
||||
|
||||
var hasNamespace = !string.IsNullOrEmpty(this.Window.Namespace);
|
||||
|
||||
if (hasNamespace)
|
||||
ImGui.PushID(this.Window.Namespace);
|
||||
|
||||
this.PreHandlePreset(persistence);
|
||||
|
||||
if (this.internalLastIsOpen != this.internalIsOpen && this.internalIsOpen)
|
||||
{
|
||||
this.internalLastIsOpen = this.internalIsOpen;
|
||||
this.Window.OnOpen();
|
||||
|
||||
if (internalDrawFlags.HasFlag(WindowDrawFlags.UseSoundEffects) && !this.Window.DisableWindowSounds)
|
||||
UIGlobals.PlaySoundEffect(this.Window.OnOpenSfxId);
|
||||
}
|
||||
|
||||
//TODO: We may have to allow for windows to configure if they should fade
|
||||
if (this.internalAlpha.HasValue)
|
||||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, this.internalAlpha.Value);
|
||||
this.didPushInternalAlpha = true;
|
||||
}
|
||||
this.Window.PreDraw();
|
||||
this.ApplyConditionals();
|
||||
|
||||
if (this.Window.ForceMainWindow)
|
||||
ImGuiHelpers.ForceNextWindowMainViewport();
|
||||
|
||||
var wasFocused = this.Window.IsFocused;
|
||||
if (wasFocused)
|
||||
{
|
||||
var style = ImGui.GetStyle();
|
||||
var focusedHeaderColor = style.Colors[(int)ImGuiCol.TitleBgActive];
|
||||
ImGui.PushStyleColor(ImGuiCol.TitleBgCollapsed, focusedHeaderColor);
|
||||
}
|
||||
|
||||
if (this.Window.RequestFocus)
|
||||
{
|
||||
ImGui.SetNextWindowFocus();
|
||||
this.Window.RequestFocus = false;
|
||||
}
|
||||
|
||||
var flags = this.Window.Flags;
|
||||
|
||||
if (this.internalIsPinned || this.internalIsClickthrough)
|
||||
flags |= ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize;
|
||||
|
||||
if (this.internalIsClickthrough)
|
||||
flags |= ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoNav | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoMouseInputs;
|
||||
|
||||
if (this.CanShowCloseButton ? ImGui.Begin(this.Window.WindowName, ref this.internalIsOpen, flags) : ImGui.Begin(this.Window.WindowName, flags))
|
||||
{
|
||||
var context = ImGui.GetCurrentContext();
|
||||
if (!context.IsNull)
|
||||
{
|
||||
ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough;
|
||||
}
|
||||
|
||||
// Not supported yet on non-main viewports
|
||||
if ((this.internalIsPinned || this.internalIsClickthrough || this.internalAlpha.HasValue) &&
|
||||
ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
||||
{
|
||||
this.internalAlpha = null;
|
||||
this.internalIsPinned = false;
|
||||
this.internalIsClickthrough = false;
|
||||
this.presetDirty = true;
|
||||
}
|
||||
|
||||
// Draw the actual window contents
|
||||
try
|
||||
{
|
||||
this.Window.Draw();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error during Draw(): {WindowName}", this.Window.WindowName);
|
||||
}
|
||||
}
|
||||
|
||||
const string additionsPopupName = "WindowSystemContextActions";
|
||||
var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) &&
|
||||
!flags.HasFlag(ImGuiWindowFlags.NoTitleBar);
|
||||
var showAdditions = (this.Window.AllowPinning || this.Window.AllowClickthrough) &&
|
||||
internalDrawFlags.HasFlag(WindowDrawFlags.UseAdditionalOptions) &&
|
||||
flagsApplicableForTitleBarIcons;
|
||||
var printWindow = false;
|
||||
if (showAdditions)
|
||||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
||||
|
||||
if (ImGui.BeginPopup(additionsPopupName, ImGuiWindowFlags.NoMove))
|
||||
{
|
||||
var isAvailable = ImGuiHelpers.CheckIsWindowOnMainViewport();
|
||||
|
||||
if (!isAvailable)
|
||||
ImGui.BeginDisabled();
|
||||
|
||||
if (this.internalIsClickthrough)
|
||||
ImGui.BeginDisabled();
|
||||
|
||||
if (this.Window.AllowPinning)
|
||||
{
|
||||
var showAsPinned = this.internalIsPinned || this.internalIsClickthrough;
|
||||
if (ImGui.Checkbox(Loc.Localize("WindowSystemContextActionPin", "Pin Window"), ref showAsPinned))
|
||||
{
|
||||
this.internalIsPinned = showAsPinned;
|
||||
this.presetDirty = true;
|
||||
}
|
||||
|
||||
ImGuiComponents.HelpMarker(
|
||||
Loc.Localize("WindowSystemContextActionPinHint", "Pinned windows will not move or resize when you click and drag them, nor will they close when escape is pressed."));
|
||||
}
|
||||
|
||||
if (this.internalIsClickthrough)
|
||||
ImGui.EndDisabled();
|
||||
|
||||
if (this.Window.AllowClickthrough)
|
||||
{
|
||||
if (ImGui.Checkbox(
|
||||
Loc.Localize("WindowSystemContextActionClickthrough", "Make clickthrough"),
|
||||
ref this.internalIsClickthrough))
|
||||
{
|
||||
this.presetDirty = true;
|
||||
}
|
||||
|
||||
ImGuiComponents.HelpMarker(
|
||||
Loc.Localize("WindowSystemContextActionClickthroughHint", "Clickthrough windows will not receive mouse input, move or resize. They are completely inert."));
|
||||
}
|
||||
|
||||
var alpha = (this.internalAlpha ?? ImGui.GetStyle().Alpha) * 100f;
|
||||
if (ImGui.SliderFloat(Loc.Localize("WindowSystemContextActionAlpha", "Opacity"), ref alpha, 20f,
|
||||
100f))
|
||||
{
|
||||
this.internalAlpha = alpha / 100f;
|
||||
this.presetDirty = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button(Loc.Localize("WindowSystemContextActionReset", "Reset")))
|
||||
{
|
||||
this.internalAlpha = null;
|
||||
this.presetDirty = true;
|
||||
}
|
||||
|
||||
if (isAvailable)
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudGrey,
|
||||
Loc.Localize("WindowSystemContextActionClickthroughDisclaimer",
|
||||
"Open this menu again by clicking the three dashes to disable clickthrough."));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudGrey,
|
||||
Loc.Localize("WindowSystemContextActionViewportDisclaimer",
|
||||
"These features are only available if this window is inside the game window."));
|
||||
}
|
||||
|
||||
if (!isAvailable)
|
||||
ImGui.EndDisabled();
|
||||
|
||||
if (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window")))
|
||||
printWindow = true;
|
||||
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
var window = ImGuiP.GetCurrentWindow();
|
||||
|
||||
ImRect outRect;
|
||||
ImGuiP.TitleBarRect(&outRect, window);
|
||||
|
||||
var additionsButton = new TitleBarButton
|
||||
{
|
||||
Icon = FontAwesomeIcon.Bars,
|
||||
IconOffset = new Vector2(2.5f, 1),
|
||||
Click = _ =>
|
||||
{
|
||||
this.internalIsClickthrough = false;
|
||||
this.presetDirty = false;
|
||||
ImGui.OpenPopup(additionsPopupName);
|
||||
},
|
||||
Priority = int.MinValue,
|
||||
AvailableClickthrough = true,
|
||||
};
|
||||
|
||||
if (flagsApplicableForTitleBarIcons)
|
||||
{
|
||||
this.DrawTitleBarButtons(window, flags, outRect,
|
||||
showAdditions
|
||||
? this.Window.TitleBarButtons.Append(additionsButton)
|
||||
: this.Window.TitleBarButtons);
|
||||
}
|
||||
}
|
||||
|
||||
if (wasFocused)
|
||||
{
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
this.Window.IsFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows);
|
||||
|
||||
if (internalDrawFlags.HasFlag(WindowDrawFlags.UseFocusManagement) && !this.internalIsPinned)
|
||||
{
|
||||
var escapeDown = Service<KeyState>.Get()[VirtualKey.ESCAPE];
|
||||
if (escapeDown && this.Window.IsFocused && !wasEscPressedLastFrame && this.Window.RespectCloseHotkey)
|
||||
{
|
||||
this.Window.IsOpen = false;
|
||||
wasEscPressedLastFrame = true;
|
||||
}
|
||||
else if (!escapeDown && wasEscPressedLastFrame)
|
||||
{
|
||||
wasEscPressedLastFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.fadeOutSize = ImGui.GetWindowSize();
|
||||
this.fadeOutOrigin = ImGui.GetWindowPos();
|
||||
var isCollapsed = ImGui.IsWindowCollapsed();
|
||||
var isDocked = ImGui.IsWindowDocked();
|
||||
|
||||
ImGui.End();
|
||||
|
||||
if (this.pushedFadeInAlpha)
|
||||
{
|
||||
ImGui.PopStyleVar();
|
||||
this.pushedFadeInAlpha = false;
|
||||
}
|
||||
|
||||
// TODO: No fade-out if the window is collapsed. We could do this if we knew the "FullSize" of the window
|
||||
// from the internal ImGuiWindow, but I don't want to mess with that here for now. We can do this a lot
|
||||
// easier with the new bindings.
|
||||
// TODO: No fade-out if docking is enabled and the window is docked, since this makes them "unsnap".
|
||||
// Ideally we should get rid of this "fake window" thing and just insert a new drawlist at the correct spot.
|
||||
if (!this.internalIsOpen && this.fadeOutTexture == null && doFades && !isCollapsed && !isDocked)
|
||||
{
|
||||
this.fadeOutTexture = Service<TextureManager>.Get().CreateDrawListTexture(
|
||||
"WindowFadeOutTexture");
|
||||
this.fadeOutTexture.ResizeAndDrawWindow(this.Window.WindowName, Vector2.One);
|
||||
this.fadeOutTimer = FadeInOutTime;
|
||||
}
|
||||
|
||||
if (printWindow)
|
||||
{
|
||||
var tex = Service<TextureManager>.Get().CreateDrawListTexture(
|
||||
Loc.Localize("WindowSystemContextActionPrintWindow", "Print window"));
|
||||
tex.ResizeAndDrawWindow(this.Window.WindowName, Vector2.One);
|
||||
_ = Service<DevTextureSaveMenu>.Get().ShowTextureSaveMenuAsync(
|
||||
this.Window.WindowName,
|
||||
this.Window.WindowName,
|
||||
Task.FromResult<IDalamudTextureWrap>(tex));
|
||||
}
|
||||
|
||||
if (this.didPushInternalAlpha)
|
||||
{
|
||||
ImGui.PopStyleVar();
|
||||
this.didPushInternalAlpha = false;
|
||||
}
|
||||
this.Window.PostDraw();
|
||||
|
||||
this.PostHandlePreset(persistence);
|
||||
|
||||
if (hasNamespace)
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
private unsafe void ApplyConditionals()
|
||||
{
|
||||
if (this.Window.Position.HasValue)
|
||||
{
|
||||
var pos = this.Window.Position.Value;
|
||||
|
||||
if (this.Window.ForceMainWindow)
|
||||
pos += ImGuiHelpers.MainViewport.Pos;
|
||||
|
||||
ImGui.SetNextWindowPos(pos, this.Window.PositionCondition);
|
||||
}
|
||||
|
||||
if (this.Window.Size.HasValue)
|
||||
{
|
||||
ImGui.SetNextWindowSize(this.Window.Size.Value * ImGuiHelpers.GlobalScale, this.Window.SizeCondition);
|
||||
}
|
||||
|
||||
if (this.Window.Collapsed.HasValue)
|
||||
{
|
||||
ImGui.SetNextWindowCollapsed(this.Window.Collapsed.Value, this.Window.CollapsedCondition);
|
||||
}
|
||||
|
||||
if (this.Window.SizeConstraints.HasValue)
|
||||
{
|
||||
var (min, max) = this.GetValidatedConstraints(this.Window.SizeConstraints.Value);
|
||||
ImGui.SetNextWindowSizeConstraints(
|
||||
min * ImGuiHelpers.GlobalScale,
|
||||
max * ImGuiHelpers.GlobalScale
|
||||
);
|
||||
}
|
||||
|
||||
var maxBgAlpha = this.internalAlpha ?? this.Window.BgAlpha;
|
||||
var fadeInAlpha = this.fadeInTimer / FadeInOutTime;
|
||||
if (fadeInAlpha < 1f)
|
||||
{
|
||||
maxBgAlpha = maxBgAlpha.HasValue ?
|
||||
Math.Clamp(maxBgAlpha.Value * fadeInAlpha, 0f, 1f) :
|
||||
(*ImGui.GetStyleColorVec4(ImGuiCol.WindowBg)).W * fadeInAlpha;
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * fadeInAlpha);
|
||||
this.pushedFadeInAlpha = true;
|
||||
}
|
||||
|
||||
if (maxBgAlpha.HasValue)
|
||||
{
|
||||
ImGui.SetNextWindowBgAlpha(maxBgAlpha.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private (Vector2 min, Vector2 max) GetValidatedConstraints(WindowSizeConstraints constraints)
|
||||
{
|
||||
var min = constraints.MinimumSize;
|
||||
var max = constraints.MaximumSize;
|
||||
|
||||
// If max < min, treat as "no constraint" (float.MaxValue)
|
||||
if (max.X < min.X || max.Y < min.Y)
|
||||
max = new Vector2(float.MaxValue);
|
||||
|
||||
return (min, max);
|
||||
}
|
||||
|
||||
|
||||
private void PreHandlePreset(WindowSystemPersistence? persistence)
|
||||
{
|
||||
if (persistence == null || this.hasInitializedFromPreset)
|
||||
return;
|
||||
|
||||
var id = ImGui.GetID(this.Window.WindowName);
|
||||
this.presetWindow = persistence.GetWindow(id);
|
||||
|
||||
this.hasInitializedFromPreset = true;
|
||||
|
||||
// Fresh preset - don't apply anything
|
||||
if (this.presetWindow == null)
|
||||
{
|
||||
this.presetWindow = new PresetModel.PresetWindow();
|
||||
this.presetDirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.internalIsPinned = this.presetWindow.IsPinned;
|
||||
this.internalIsClickthrough = this.presetWindow.IsClickThrough;
|
||||
this.internalAlpha = this.presetWindow.Alpha;
|
||||
}
|
||||
|
||||
private void PostHandlePreset(WindowSystemPersistence? persistence)
|
||||
{
|
||||
if (persistence == null)
|
||||
return;
|
||||
|
||||
Debug.Assert(this.presetWindow != null, "this.presetWindow != null");
|
||||
|
||||
if (this.presetDirty)
|
||||
{
|
||||
this.presetWindow.IsPinned = this.internalIsPinned;
|
||||
this.presetWindow.IsClickThrough = this.internalIsClickthrough;
|
||||
this.presetWindow.Alpha = this.internalAlpha;
|
||||
|
||||
var id = ImGui.GetID(this.Window.WindowName);
|
||||
persistence.SaveWindow(id, this.presetWindow!);
|
||||
this.presetDirty = false;
|
||||
|
||||
Log.Verbose("Saved preset for {WindowName}", this.Window.WindowName);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void DrawTitleBarButtons(ImGuiWindowPtr window, ImGuiWindowFlags flags, ImRect titleBarRect, IEnumerable<TitleBarButton> buttons)
|
||||
{
|
||||
ImGui.PushClipRect(ImGui.GetWindowPos(), ImGui.GetWindowPos() + ImGui.GetWindowSize(), false);
|
||||
|
||||
var style = ImGui.GetStyle();
|
||||
var fontSize = ImGui.GetFontSize();
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
|
||||
var padR = 0f;
|
||||
var buttonSize = ImGui.GetFontSize();
|
||||
|
||||
var numNativeButtons = 0;
|
||||
if (this.CanShowCloseButton)
|
||||
numNativeButtons++;
|
||||
|
||||
if (!flags.HasFlag(ImGuiWindowFlags.NoCollapse) && style.WindowMenuButtonPosition == ImGuiDir.Right)
|
||||
numNativeButtons++;
|
||||
|
||||
// If there are no native buttons, pad from the right to make some space
|
||||
if (numNativeButtons == 0)
|
||||
padR += style.FramePadding.X;
|
||||
|
||||
// Pad to the left, to get out of the way of the native buttons
|
||||
padR += numNativeButtons * (buttonSize + style.ItemInnerSpacing.X);
|
||||
|
||||
Vector2 GetCenter(ImRect rect) => new((rect.Min.X + rect.Max.X) * 0.5f, (rect.Min.Y + rect.Max.Y) * 0.5f);
|
||||
|
||||
var numButtons = 0;
|
||||
bool DrawButton(TitleBarButton button, Vector2 pos)
|
||||
{
|
||||
var id = ImGui.GetID($"###CustomTbButton{numButtons}");
|
||||
numButtons++;
|
||||
|
||||
var max = pos + new Vector2(fontSize, fontSize);
|
||||
ImRect bb = new(pos, max);
|
||||
var isClipped = !ImGuiP.ItemAdd(bb, id, null, 0);
|
||||
bool hovered, held;
|
||||
var pressed = false;
|
||||
|
||||
if (this.internalIsClickthrough)
|
||||
{
|
||||
hovered = false;
|
||||
held = false;
|
||||
|
||||
// ButtonBehavior does not function if the window is clickthrough, so we have to do it ourselves
|
||||
if (ImGui.IsMouseHoveringRect(pos, max))
|
||||
{
|
||||
hovered = true;
|
||||
|
||||
// We can't use ImGui native functions here, because they don't work with clickthrough
|
||||
if ((global::Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0)
|
||||
{
|
||||
held = true;
|
||||
pressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pressed = ImGuiP.ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags.None);
|
||||
}
|
||||
|
||||
if (isClipped)
|
||||
return pressed;
|
||||
|
||||
// Render
|
||||
var bgCol = ImGui.GetColorU32((held && hovered) ? ImGuiCol.ButtonActive : hovered ? ImGuiCol.ButtonHovered : ImGuiCol.Button);
|
||||
var textCol = ImGui.GetColorU32(ImGuiCol.Text);
|
||||
if (hovered || held)
|
||||
drawList.AddCircleFilled(GetCenter(bb) + new Vector2(0.0f, -0.5f), (fontSize * 0.5f) + 1.0f, bgCol);
|
||||
|
||||
var offset = button.IconOffset * ImGuiHelpers.GlobalScale;
|
||||
drawList.AddText(InterfaceManager.IconFont, (float)(fontSize * 0.8), new Vector2(bb.Min.X + offset.X, bb.Min.Y + offset.Y), textCol, button.Icon.ToIconString());
|
||||
|
||||
if (hovered)
|
||||
button.ShowTooltip?.Invoke();
|
||||
|
||||
// Switch to moving the window after mouse is moved beyond the initial drag threshold
|
||||
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left) && !this.internalIsClickthrough)
|
||||
ImGuiP.StartMouseMovingWindow(window);
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
foreach (var button in buttons.OrderBy(x => x.Priority))
|
||||
{
|
||||
if (this.internalIsClickthrough && !button.AvailableClickthrough)
|
||||
return;
|
||||
|
||||
Vector2 position = new(titleBarRect.Max.X - padR - buttonSize, titleBarRect.Min.Y + style.FramePadding.Y);
|
||||
padR += buttonSize + style.ItemInnerSpacing.X;
|
||||
|
||||
if (DrawButton(button, position))
|
||||
button.Click?.Invoke(ImGuiMouseButton.Left);
|
||||
}
|
||||
|
||||
ImGui.PopClipRect();
|
||||
}
|
||||
|
||||
private void DrawFakeFadeOutWindow()
|
||||
{
|
||||
// Draw a fake window to fade out, so that the fade out texture stays in the right place in the
|
||||
// focus order
|
||||
ImGui.SetNextWindowPos(this.fadeOutOrigin);
|
||||
ImGui.SetNextWindowSize(this.fadeOutSize);
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, Vector2.Zero);
|
||||
style.Push(ImGuiStyleVar.WindowBorderSize, 0);
|
||||
style.Push(ImGuiStyleVar.FrameBorderSize, 0);
|
||||
|
||||
const ImGuiWindowFlags flags = ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoNav |
|
||||
ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoMouseInputs |
|
||||
ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoBackground;
|
||||
if (ImGui.Begin(this.Window.WindowName, flags))
|
||||
{
|
||||
var dl = ImGui.GetWindowDrawList();
|
||||
dl.AddImage(
|
||||
this.fadeOutTexture!.Handle,
|
||||
this.fadeOutOrigin,
|
||||
this.fadeOutOrigin + this.fadeOutSize,
|
||||
Vector2.Zero,
|
||||
Vector2.One,
|
||||
ImGui.ColorConvertFloat4ToU32(new(1f, 1f, 1f, Math.Clamp(this.fadeOutTimer / FadeInOutTime, 0f, 1f))));
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
9
Dalamud/Interface/Windowing/WindowSizeConstraints.cs
Normal file
9
Dalamud/Interface/Windowing/WindowSizeConstraints.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
public struct WindowSizeConstraints
|
||||
{
|
||||
public Vector2 MinimumSize { get; set; }
|
||||
public Vector2 MaximumSize { get; set; }
|
||||
}
|
||||
|
|
@ -9,13 +9,13 @@ using Serilog;
|
|||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
/// <summary>
|
||||
/// Class running a WindowSystem using <see cref="Window"/> implementations to simplify ImGui windowing.
|
||||
/// Class running a WindowSystem using <see cref="IWindow"/> implementations to simplify ImGui windowing.
|
||||
/// </summary>
|
||||
public class WindowSystem
|
||||
public class WindowSystem : IWindowSystem
|
||||
{
|
||||
private static DateTimeOffset lastAnyFocus;
|
||||
|
||||
private readonly List<Window> windows = new();
|
||||
private readonly List<WindowHost> windows = new();
|
||||
|
||||
private string lastFocusedWindowName = string.Empty;
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ public class WindowSystem
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any <see cref="WindowSystem"/> contains any <see cref="Window"/>
|
||||
/// Gets a value indicating whether any <see cref="WindowSystem"/> contains any <see cref="IWindow"/>
|
||||
/// that has focus and is not marked to be excluded from consideration.
|
||||
/// </summary>
|
||||
public static bool HasAnyWindowSystemFocus { get; internal set; } = false;
|
||||
|
|
@ -44,20 +44,13 @@ public class WindowSystem
|
|||
/// </summary>
|
||||
public static TimeSpan TimeSinceLastAnyFocus => DateTimeOffset.Now - lastAnyFocus;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of all <see cref="Window"/>s in this <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Window> Windows => this.windows;
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyList<IWindow> Windows => this.windows.Select(c => c.Window).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether any window in this <see cref="WindowSystem"/> has focus and is
|
||||
/// not marked to be excluded from consideration.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public bool HasAnyFocus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name/ID-space of this <see cref="WindowSystem"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public string? Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -66,42 +59,28 @@ public class WindowSystem
|
|||
/// </summary>
|
||||
internal static bool ShouldInhibitAtkCloseEvents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Add a window to this <see cref="WindowSystem"/>.
|
||||
/// The window system doesn't own your window, it just renders it
|
||||
/// You need to store a reference to it to use it later.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to add.</param>
|
||||
public void AddWindow(Window window)
|
||||
/// <inheritdoc/>
|
||||
public void AddWindow(IWindow window)
|
||||
{
|
||||
if (this.windows.Any(x => x.WindowName == window.WindowName))
|
||||
if (this.windows.Any(x => x.Window.WindowName == window.WindowName))
|
||||
throw new ArgumentException("A window with this name/ID already exists.");
|
||||
|
||||
this.windows.Add(window);
|
||||
this.windows.Add(new WindowHost(window));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a window from this <see cref="WindowSystem"/>.
|
||||
/// Will not dispose your window, if it is disposable.
|
||||
/// </summary>
|
||||
/// <param name="window">The window to remove.</param>
|
||||
public void RemoveWindow(Window window)
|
||||
/// <inheritdoc/>
|
||||
public void RemoveWindow(IWindow window)
|
||||
{
|
||||
if (!this.windows.Contains(window))
|
||||
if (this.windows.All(c => c.Window != window))
|
||||
throw new ArgumentException("This window is not registered on this WindowSystem.");
|
||||
|
||||
this.windows.Remove(window);
|
||||
this.windows.RemoveAll(c => c.Window == window);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all windows from this <see cref="WindowSystem"/>.
|
||||
/// Will not dispose your windows, if they are disposable.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public void RemoveAllWindows() => this.windows.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Draw all registered windows using ImGui.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public void Draw()
|
||||
{
|
||||
var hasNamespace = !string.IsNullOrEmpty(this.Namespace);
|
||||
|
|
@ -113,19 +92,19 @@ public class WindowSystem
|
|||
var config = Service<DalamudConfiguration>.GetNullable();
|
||||
var persistence = Service<WindowSystemPersistence>.GetNullable();
|
||||
|
||||
var flags = Window.WindowDrawFlags.None;
|
||||
var flags = WindowHost.WindowDrawFlags.None;
|
||||
|
||||
if (config?.EnablePluginUISoundEffects ?? false)
|
||||
flags |= Window.WindowDrawFlags.UseSoundEffects;
|
||||
flags |= WindowHost.WindowDrawFlags.UseSoundEffects;
|
||||
|
||||
if (config?.EnablePluginUiAdditionalOptions ?? false)
|
||||
flags |= Window.WindowDrawFlags.UseAdditionalOptions;
|
||||
flags |= WindowHost.WindowDrawFlags.UseAdditionalOptions;
|
||||
|
||||
if (config?.IsFocusManagementEnabled ?? false)
|
||||
flags |= Window.WindowDrawFlags.UseFocusManagement;
|
||||
flags |= WindowHost.WindowDrawFlags.UseFocusManagement;
|
||||
|
||||
if (config?.ReduceMotions ?? false)
|
||||
flags |= Window.WindowDrawFlags.IsReducedMotion;
|
||||
flags |= WindowHost.WindowDrawFlags.IsReducedMotion;
|
||||
|
||||
// Shallow clone the list of windows so that we can edit it without modifying it while the loop is iterating
|
||||
foreach (var window in this.windows.ToArray())
|
||||
|
|
@ -136,15 +115,15 @@ public class WindowSystem
|
|||
window.DrawInternal(flags, persistence);
|
||||
}
|
||||
|
||||
var focusedWindow = this.windows.FirstOrDefault(window => window.IsFocused);
|
||||
var focusedWindow = this.windows.FirstOrDefault(window => window.Window.IsFocused);
|
||||
this.HasAnyFocus = focusedWindow != default;
|
||||
|
||||
if (this.HasAnyFocus)
|
||||
{
|
||||
if (this.lastFocusedWindowName != focusedWindow.WindowName)
|
||||
if (this.lastFocusedWindowName != focusedWindow.Window.WindowName)
|
||||
{
|
||||
Log.Verbose($"WindowSystem \"{this.Namespace}\" Window \"{focusedWindow.WindowName}\" has focus now");
|
||||
this.lastFocusedWindowName = focusedWindow.WindowName;
|
||||
Log.Verbose($"WindowSystem \"{this.Namespace}\" Window \"{focusedWindow.Window.WindowName}\" has focus now");
|
||||
this.lastFocusedWindowName = focusedWindow.Window.WindowName;
|
||||
}
|
||||
|
||||
HasAnyWindowSystemFocus = true;
|
||||
|
|
@ -161,10 +140,10 @@ public class WindowSystem
|
|||
}
|
||||
}
|
||||
|
||||
ShouldInhibitAtkCloseEvents |= this.windows.Any(w => w.IsFocused &&
|
||||
w.RespectCloseHotkey &&
|
||||
!w.IsPinned &&
|
||||
!w.IsClickthrough);
|
||||
ShouldInhibitAtkCloseEvents |= this.windows.Any(w => w.Window.IsFocused &&
|
||||
w.Window.RespectCloseHotkey &&
|
||||
!w.Window.IsPinned &&
|
||||
!w.Window.IsClickthrough);
|
||||
|
||||
if (hasNamespace)
|
||||
ImGui.PopID();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue