mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Support make clickthrough
This commit is contained in:
parent
e5451c37af
commit
544f8b28bf
3 changed files with 79 additions and 91 deletions
|
|
@ -299,11 +299,12 @@ internal sealed partial class Win32InputHandler
|
||||||
|
|
||||||
private static void ViewportFlagsToWin32Styles(ImGuiViewportFlags flags, out int style, out int exStyle)
|
private static void ViewportFlagsToWin32Styles(ImGuiViewportFlags flags, out int style, out int exStyle)
|
||||||
{
|
{
|
||||||
style = (int)(flags.HasFlag(ImGuiViewportFlags.NoDecoration) ? WS.WS_POPUP : WS.WS_OVERLAPPEDWINDOW);
|
style = (flags & ImGuiViewportFlags.NoDecoration) != 0 ? unchecked((int)WS.WS_POPUP) : WS.WS_OVERLAPPEDWINDOW;
|
||||||
exStyle =
|
exStyle = (flags & ImGuiViewportFlags.NoTaskBarIcon) != 0 ? WS.WS_EX_TOOLWINDOW : WS.WS_EX_APPWINDOW;
|
||||||
(int)(flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon) ? WS.WS_EX_TOOLWINDOW : (uint)WS.WS_EX_APPWINDOW);
|
|
||||||
exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP;
|
exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP;
|
||||||
if (flags.HasFlag(ImGuiViewportFlags.TopMost))
|
if ((flags & ImGuiViewportFlags.TopMost) != 0)
|
||||||
exStyle |= WS.WS_EX_TOPMOST;
|
exStyle |= WS.WS_EX_TOPMOST;
|
||||||
|
if ((flags & ImGuiViewportFlags.NoInputs) != 0)
|
||||||
|
exStyle |= WS.WS_EX_TRANSPARENT | WS.WS_EX_LAYERED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
||||||
|
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -446,19 +447,19 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
|
||||||
ClientToScreen(this.hWnd, &pos);
|
ClientToScreen(this.hWnd, &pos);
|
||||||
SetCursorPos(pos.x, pos.y);
|
SetCursorPos(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||||
if (!io.WantSetMousePos && !this.mouseTracked && hasMouseScreenPos)
|
if (!io.WantSetMousePos && !this.mouseTracked && hasMouseScreenPos)
|
||||||
{
|
{
|
||||||
// Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
// Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
|
||||||
// (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
|
// (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.)
|
||||||
// Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
|
// Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
|
||||||
// (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
|
// (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.)
|
||||||
var mousePos = mouseScreenPos;
|
var mousePos = mouseScreenPos;
|
||||||
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
|
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
|
||||||
ClientToScreen(focusedWindow, &mousePos);
|
ClientToScreen(focusedWindow, &mousePos);
|
||||||
io.AddMousePosEvent(mousePos.x, mousePos.y);
|
io.AddMousePosEvent(mousePos.x, mousePos.y);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
|
// (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
|
||||||
|
|
@ -827,6 +828,9 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->Hwnd == 0)
|
||||||
|
Util.Fatal($"CreateWindowExW failed: {GetLastError()}", "ImGui Viewport error");
|
||||||
|
|
||||||
data->HwndOwned = true;
|
data->HwndOwned = true;
|
||||||
viewport.PlatformRequestResize = false;
|
viewport.PlatformRequestResize = false;
|
||||||
viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd;
|
viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
|
|
@ -19,10 +16,13 @@ using Dalamud.Interface.Utility.Internal;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing.Persistence;
|
using Dalamud.Interface.Windowing.Persistence;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Utility;
|
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
namespace Dalamud.Interface.Windowing;
|
namespace Dalamud.Interface.Windowing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -31,11 +31,15 @@ namespace Dalamud.Interface.Windowing;
|
||||||
public abstract class Window
|
public abstract class Window
|
||||||
{
|
{
|
||||||
private const float FadeInOutTime = 0.072f;
|
private const float FadeInOutTime = 0.072f;
|
||||||
|
private const string AdditionsPopupName = "WindowSystemContextActions";
|
||||||
|
|
||||||
private static readonly ModuleLog Log = new("WindowSystem");
|
private static readonly ModuleLog Log = new("WindowSystem");
|
||||||
|
|
||||||
private static bool wasEscPressedLastFrame = false;
|
private static bool wasEscPressedLastFrame = false;
|
||||||
|
|
||||||
|
private readonly TitleBarButton additionsButton;
|
||||||
|
private readonly List<TitleBarButton> allButtons = [];
|
||||||
|
|
||||||
private bool internalLastIsOpen = false;
|
private bool internalLastIsOpen = false;
|
||||||
private bool internalIsOpen = false;
|
private bool internalIsOpen = false;
|
||||||
private bool internalIsPinned = false;
|
private bool internalIsPinned = false;
|
||||||
|
|
@ -69,6 +73,20 @@ public abstract class Window
|
||||||
this.WindowName = name;
|
this.WindowName = name;
|
||||||
this.Flags = flags;
|
this.Flags = flags;
|
||||||
this.ForceMainWindow = forceMainWindow;
|
this.ForceMainWindow = forceMainWindow;
|
||||||
|
|
||||||
|
this.additionsButton = new()
|
||||||
|
{
|
||||||
|
Icon = FontAwesomeIcon.Bars,
|
||||||
|
IconOffset = new Vector2(2.5f, 1),
|
||||||
|
Click = _ =>
|
||||||
|
{
|
||||||
|
this.internalIsClickthrough = false;
|
||||||
|
this.presetDirty = false;
|
||||||
|
ImGui.OpenPopup(AdditionsPopupName);
|
||||||
|
},
|
||||||
|
Priority = int.MinValue,
|
||||||
|
AvailableClickthrough = true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -448,11 +466,12 @@ public abstract class Window
|
||||||
ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough;
|
ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not supported yet on non-main viewports
|
if (ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
||||||
if (this.internalIsClickthrough && ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
|
||||||
{
|
{
|
||||||
this.internalIsClickthrough = false;
|
if ((flags & ImGuiWindowFlags.NoInputs) == ImGuiWindowFlags.NoInputs)
|
||||||
this.presetDirty = true;
|
ImGui.GetWindowViewport().Flags |= ImGuiViewportFlags.NoInputs;
|
||||||
|
else
|
||||||
|
ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the actual window contents
|
// Draw the actual window contents
|
||||||
|
|
@ -466,7 +485,6 @@ public abstract class Window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const string additionsPopupName = "WindowSystemContextActions";
|
|
||||||
var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) &&
|
var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) &&
|
||||||
!flags.HasFlag(ImGuiWindowFlags.NoTitleBar);
|
!flags.HasFlag(ImGuiWindowFlags.NoTitleBar);
|
||||||
var showAdditions = (this.AllowPinning || this.AllowClickthrough) &&
|
var showAdditions = (this.AllowPinning || this.AllowClickthrough) &&
|
||||||
|
|
@ -477,7 +495,7 @@ public abstract class Window
|
||||||
{
|
{
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
||||||
|
|
||||||
if (ImGui.BeginPopup(additionsPopupName, ImGuiWindowFlags.NoMove))
|
if (ImGui.BeginPopup(AdditionsPopupName, ImGuiWindowFlags.NoMove))
|
||||||
{
|
{
|
||||||
if (this.internalIsClickthrough)
|
if (this.internalIsClickthrough)
|
||||||
ImGui.BeginDisabled();
|
ImGui.BeginDisabled();
|
||||||
|
|
@ -498,11 +516,6 @@ public abstract class Window
|
||||||
if (this.internalIsClickthrough)
|
if (this.internalIsClickthrough)
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
var isAvailable = ImGuiHelpers.CheckIsWindowOnMainViewport();
|
|
||||||
|
|
||||||
if (!isAvailable)
|
|
||||||
ImGui.BeginDisabled();
|
|
||||||
|
|
||||||
if (this.AllowClickthrough)
|
if (this.AllowClickthrough)
|
||||||
{
|
{
|
||||||
if (ImGui.Checkbox(
|
if (ImGui.Checkbox(
|
||||||
|
|
@ -516,9 +529,6 @@ public abstract class Window
|
||||||
Loc.Localize("WindowSystemContextActionClickthroughHint", "Clickthrough windows will not receive mouse input, move or resize. They are completely inert."));
|
Loc.Localize("WindowSystemContextActionClickthroughHint", "Clickthrough windows will not receive mouse input, move or resize. They are completely inert."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAvailable)
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
var alpha = (this.internalAlpha ?? ImGui.GetStyle().Alpha) * 100f;
|
var alpha = (this.internalAlpha ?? ImGui.GetStyle().Alpha) * 100f;
|
||||||
if (ImGui.SliderFloat(Loc.Localize("WindowSystemContextActionAlpha", "Opacity"), ref alpha, 20f,
|
if (ImGui.SliderFloat(Loc.Localize("WindowSystemContextActionAlpha", "Opacity"), ref alpha, 20f,
|
||||||
100f))
|
100f))
|
||||||
|
|
@ -534,18 +544,11 @@ public abstract class Window
|
||||||
this.presetDirty = true;
|
this.presetDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAvailable)
|
ImGui.TextColored(
|
||||||
{
|
ImGuiColors.DalamudGrey,
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey,
|
Loc.Localize(
|
||||||
Loc.Localize("WindowSystemContextActionClickthroughDisclaimer",
|
"WindowSystemContextActionClickthroughDisclaimer",
|
||||||
"Open this menu again by clicking the three dashes to disable clickthrough."));
|
"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 (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window")))
|
if (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window")))
|
||||||
printWindow = true;
|
printWindow = true;
|
||||||
|
|
@ -556,34 +559,15 @@ public abstract class Window
|
||||||
ImGui.PopStyleVar();
|
ImGui.PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe
|
if (flagsApplicableForTitleBarIcons)
|
||||||
{
|
{
|
||||||
var window = ImGuiP.GetCurrentWindow();
|
this.allButtons.Clear();
|
||||||
|
this.allButtons.EnsureCapacity(this.TitleBarButtons.Count + 1);
|
||||||
ImRect outRect;
|
this.allButtons.AddRange(this.TitleBarButtons);
|
||||||
ImGuiP.TitleBarRect(&outRect, window);
|
if (showAdditions)
|
||||||
|
this.allButtons.Add(this.additionsButton);
|
||||||
var additionsButton = new TitleBarButton
|
this.allButtons.Sort(static (a, b) => b.Priority - a.Priority);
|
||||||
{
|
this.DrawTitleBarButtons();
|
||||||
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.TitleBarButtons.Append(additionsButton)
|
|
||||||
: this.TitleBarButtons);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasFocused)
|
if (wasFocused)
|
||||||
|
|
@ -740,8 +724,11 @@ public abstract class Window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void DrawTitleBarButtons(ImGuiWindowPtr window, ImGuiWindowFlags flags, ImRect titleBarRect, IEnumerable<TitleBarButton> buttons)
|
private unsafe void DrawTitleBarButtons()
|
||||||
{
|
{
|
||||||
|
var window = ImGuiP.GetCurrentWindow();
|
||||||
|
var flags = window.Flags;
|
||||||
|
var titleBarRect = window.TitleBarRect();
|
||||||
ImGui.PushClipRect(ImGui.GetWindowPos(), ImGui.GetWindowPos() + ImGui.GetWindowSize(), false);
|
ImGui.PushClipRect(ImGui.GetWindowPos(), ImGui.GetWindowPos() + ImGui.GetWindowSize(), false);
|
||||||
|
|
||||||
var style = ImGui.GetStyle();
|
var style = ImGui.GetStyle();
|
||||||
|
|
@ -776,26 +763,22 @@ public abstract class Window
|
||||||
var max = pos + new Vector2(fontSize, fontSize);
|
var max = pos + new Vector2(fontSize, fontSize);
|
||||||
ImRect bb = new(pos, max);
|
ImRect bb = new(pos, max);
|
||||||
var isClipped = !ImGuiP.ItemAdd(bb, id, null, 0);
|
var isClipped = !ImGuiP.ItemAdd(bb, id, null, 0);
|
||||||
bool hovered, held;
|
bool hovered, held, pressed;
|
||||||
var pressed = false;
|
|
||||||
|
|
||||||
if (this.internalIsClickthrough)
|
if (this.internalIsClickthrough)
|
||||||
{
|
{
|
||||||
hovered = false;
|
|
||||||
held = false;
|
|
||||||
|
|
||||||
// ButtonBehavior does not function if the window is clickthrough, so we have to do it ourselves
|
// ButtonBehavior does not function if the window is clickthrough, so we have to do it ourselves
|
||||||
if (ImGui.IsMouseHoveringRect(pos, max))
|
var pad = ImGui.GetStyle().TouchExtraPadding;
|
||||||
{
|
var rect = new ImRect(pos - pad, max + pad);
|
||||||
hovered = true;
|
hovered = rect.Contains(ImGui.GetMousePos());
|
||||||
|
|
||||||
// We can't use ImGui native functions here, because they don't work with clickthrough
|
// Temporarily enable inputs
|
||||||
if ((global::Windows.Win32.PInvoke.GetKeyState((int)VirtualKey.LBUTTON) & 0x8000) != 0)
|
// This will be reset on next frame, and then enabled again if it is still being hovered
|
||||||
{
|
if (hovered && ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
||||||
held = true;
|
ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
|
||||||
pressed = true;
|
|
||||||
}
|
// We can't use ImGui native functions here, because they don't work with clickthrough
|
||||||
}
|
pressed = held = hovered && (GetKeyState(VK.VK_LBUTTON) & 0x8000) != 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -824,7 +807,7 @@ public abstract class Window
|
||||||
return pressed;
|
return pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var button in buttons.OrderBy(x => x.Priority))
|
foreach (var button in this.allButtons)
|
||||||
{
|
{
|
||||||
if (this.internalIsClickthrough && !button.AvailableClickthrough)
|
if (this.internalIsClickthrough && !button.AvailableClickthrough)
|
||||||
return;
|
return;
|
||||||
|
|
@ -932,7 +915,7 @@ public abstract class Window
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets an action that is called when the button is clicked.
|
/// Gets or sets an action that is called when the button is clicked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<ImGuiMouseButton> Click { get; set; }
|
public Action<ImGuiMouseButton>? Click { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the priority the button shall be shown in.
|
/// Gets or sets the priority the button shall be shown in.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue