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)
|
||||
{
|
||||
style = (int)(flags.HasFlag(ImGuiViewportFlags.NoDecoration) ? WS.WS_POPUP : WS.WS_OVERLAPPEDWINDOW);
|
||||
exStyle =
|
||||
(int)(flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon) ? WS.WS_EX_TOOLWINDOW : (uint)WS.WS_EX_APPWINDOW);
|
||||
style = (flags & ImGuiViewportFlags.NoDecoration) != 0 ? unchecked((int)WS.WS_POPUP) : WS.WS_OVERLAPPEDWINDOW;
|
||||
exStyle = (flags & ImGuiViewportFlags.NoTaskBarIcon) != 0 ? WS.WS_EX_TOOLWINDOW : WS.WS_EX_APPWINDOW;
|
||||
exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP;
|
||||
if (flags.HasFlag(ImGuiViewportFlags.TopMost))
|
||||
if ((flags & ImGuiViewportFlags.TopMost) != 0)
|
||||
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.Memory;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -446,19 +447,19 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler
|
|||
ClientToScreen(this.hWnd, &pos);
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||
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)
|
||||
// (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)
|
||||
// (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;
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
|
||||
ClientToScreen(focusedWindow, &mousePos);
|
||||
io.AddMousePosEvent(mousePos.x, mousePos.y);
|
||||
}
|
||||
// (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured)
|
||||
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)
|
||||
// (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)
|
||||
// (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;
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0)
|
||||
ClientToScreen(focusedWindow, &mousePos);
|
||||
io.AddMousePosEvent(mousePos.x, mousePos.y);
|
||||
}
|
||||
|
||||
// (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);
|
||||
}
|
||||
|
||||
if (data->Hwnd == 0)
|
||||
Util.Fatal($"CreateWindowExW failed: {GetLastError()}", "ImGui Viewport error");
|
||||
|
||||
data->HwndOwned = true;
|
||||
viewport.PlatformRequestResize = false;
|
||||
viewport.PlatformHandle = viewport.PlatformHandleRaw = data->Hwnd;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using CheapLoc;
|
||||
|
|
@ -19,10 +16,13 @@ using Dalamud.Interface.Utility.Internal;
|
|||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing.Persistence;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.Windowing;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -31,11 +31,15 @@ namespace Dalamud.Interface.Windowing;
|
|||
public abstract class Window
|
||||
{
|
||||
private const float FadeInOutTime = 0.072f;
|
||||
private const string AdditionsPopupName = "WindowSystemContextActions";
|
||||
|
||||
private static readonly ModuleLog Log = new("WindowSystem");
|
||||
|
||||
private static bool wasEscPressedLastFrame = false;
|
||||
|
||||
private readonly TitleBarButton additionsButton;
|
||||
private readonly List<TitleBarButton> allButtons = [];
|
||||
|
||||
private bool internalLastIsOpen = false;
|
||||
private bool internalIsOpen = false;
|
||||
private bool internalIsPinned = false;
|
||||
|
|
@ -69,6 +73,20 @@ public abstract class Window
|
|||
this.WindowName = name;
|
||||
this.Flags = flags;
|
||||
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>
|
||||
|
|
@ -448,11 +466,12 @@ public abstract class Window
|
|||
ImGuiP.GetCurrentWindow().InheritNoInputs = this.internalIsClickthrough;
|
||||
}
|
||||
|
||||
// Not supported yet on non-main viewports
|
||||
if (this.internalIsClickthrough && ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
||||
if (ImGui.GetWindowViewport().ID != ImGui.GetMainViewport().ID)
|
||||
{
|
||||
this.internalIsClickthrough = false;
|
||||
this.presetDirty = true;
|
||||
if ((flags & ImGuiWindowFlags.NoInputs) == ImGuiWindowFlags.NoInputs)
|
||||
ImGui.GetWindowViewport().Flags |= ImGuiViewportFlags.NoInputs;
|
||||
else
|
||||
ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
|
||||
}
|
||||
|
||||
// Draw the actual window contents
|
||||
|
|
@ -466,7 +485,6 @@ public abstract class Window
|
|||
}
|
||||
}
|
||||
|
||||
const string additionsPopupName = "WindowSystemContextActions";
|
||||
var flagsApplicableForTitleBarIcons = !flags.HasFlag(ImGuiWindowFlags.NoDecoration) &&
|
||||
!flags.HasFlag(ImGuiWindowFlags.NoTitleBar);
|
||||
var showAdditions = (this.AllowPinning || this.AllowClickthrough) &&
|
||||
|
|
@ -477,7 +495,7 @@ public abstract class Window
|
|||
{
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f);
|
||||
|
||||
if (ImGui.BeginPopup(additionsPopupName, ImGuiWindowFlags.NoMove))
|
||||
if (ImGui.BeginPopup(AdditionsPopupName, ImGuiWindowFlags.NoMove))
|
||||
{
|
||||
if (this.internalIsClickthrough)
|
||||
ImGui.BeginDisabled();
|
||||
|
|
@ -498,11 +516,6 @@ public abstract class Window
|
|||
if (this.internalIsClickthrough)
|
||||
ImGui.EndDisabled();
|
||||
|
||||
var isAvailable = ImGuiHelpers.CheckIsWindowOnMainViewport();
|
||||
|
||||
if (!isAvailable)
|
||||
ImGui.BeginDisabled();
|
||||
|
||||
if (this.AllowClickthrough)
|
||||
{
|
||||
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."));
|
||||
}
|
||||
|
||||
if (!isAvailable)
|
||||
ImGui.EndDisabled();
|
||||
|
||||
var alpha = (this.internalAlpha ?? ImGui.GetStyle().Alpha) * 100f;
|
||||
if (ImGui.SliderFloat(Loc.Localize("WindowSystemContextActionAlpha", "Opacity"), ref alpha, 20f,
|
||||
100f))
|
||||
|
|
@ -534,18 +544,11 @@ public abstract class Window
|
|||
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."));
|
||||
}
|
||||
ImGui.TextColored(
|
||||
ImGuiColors.DalamudGrey,
|
||||
Loc.Localize(
|
||||
"WindowSystemContextActionClickthroughDisclaimer",
|
||||
"Open this menu again by clicking the three dashes to disable clickthrough."));
|
||||
|
||||
if (ImGui.Button(Loc.Localize("WindowSystemContextActionPrintWindow", "Print window")))
|
||||
printWindow = true;
|
||||
|
|
@ -556,34 +559,15 @@ public abstract class Window
|
|||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
unsafe
|
||||
if (flagsApplicableForTitleBarIcons)
|
||||
{
|
||||
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.TitleBarButtons.Append(additionsButton)
|
||||
: this.TitleBarButtons);
|
||||
}
|
||||
this.allButtons.Clear();
|
||||
this.allButtons.EnsureCapacity(this.TitleBarButtons.Count + 1);
|
||||
this.allButtons.AddRange(this.TitleBarButtons);
|
||||
if (showAdditions)
|
||||
this.allButtons.Add(this.additionsButton);
|
||||
this.allButtons.Sort(static (a, b) => b.Priority - a.Priority);
|
||||
this.DrawTitleBarButtons();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var style = ImGui.GetStyle();
|
||||
|
|
@ -776,26 +763,22 @@ public abstract class Window
|
|||
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;
|
||||
bool hovered, held, pressed;
|
||||
|
||||
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;
|
||||
var pad = ImGui.GetStyle().TouchExtraPadding;
|
||||
var rect = new ImRect(pos - pad, max + pad);
|
||||
hovered = rect.Contains(ImGui.GetMousePos());
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// Temporarily enable inputs
|
||||
// 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)
|
||||
ImGui.GetWindowViewport().Flags &= ~ImGuiViewportFlags.NoInputs;
|
||||
|
||||
// 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
|
||||
{
|
||||
|
|
@ -824,7 +807,7 @@ public abstract class Window
|
|||
return pressed;
|
||||
}
|
||||
|
||||
foreach (var button in buttons.OrderBy(x => x.Priority))
|
||||
foreach (var button in this.allButtons)
|
||||
{
|
||||
if (this.internalIsClickthrough && !button.AvailableClickthrough)
|
||||
return;
|
||||
|
|
@ -932,7 +915,7 @@ public abstract class Window
|
|||
/// <summary>
|
||||
/// Gets or sets an action that is called when the button is clicked.
|
||||
/// </summary>
|
||||
public Action<ImGuiMouseButton> Click { get; set; }
|
||||
public Action<ImGuiMouseButton>? Click { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the priority the button shall be shown in.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue