Initial commit: AetherBags + KamiToolKit for FC Gitea
Debug Build and Test / Build against Latest Dalamud (push) Has been cancelled
Debug Build and Test / Build against Staging Dalamud (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-08 14:46:31 -05:00
commit 8db4ce6094
375 changed files with 34124 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
using System;
using System.Numerics;
using Dalamud.Game.Addon.Events;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes;
using KamiToolKit.Enums;
using KamiToolKit.Nodes;
namespace KamiToolKit;
public abstract unsafe partial class NodeBase {
private Vector2 clickStartPosition = Vector2.Zero;
private NodeEditMode currentEditMode = 0;
private ViewportEventListener? editEventListener;
private bool isCursorSet;
private bool isMoving;
private bool isResizing;
private NodeEditOverlayNode? overlayNode;
public Action<NodeBase>? OnResizeComplete { get; set; }
public Action<NodeBase>? OnMoveComplete { get; set; }
public Action<NodeBase>? OnEditComplete { get; set; }
public bool EnableMoving {
get;
set {
field = value;
if (value) {
EnableEditMode(NodeEditMode.Move);
}
else {
DisableEditMode(NodeEditMode.Move);
}
}
}
public bool EnableResizing {
get;
set {
field = value;
if (value) {
EnableEditMode(NodeEditMode.Resize);
}
else {
DisableEditMode(NodeEditMode.Resize);
}
}
}
public void EnableEditMode(NodeEditMode mode) {
currentEditMode |= mode;
if (overlayNode is null) {
overlayNode = new NodeEditOverlayNode {
Position = new Vector2(-16.0f, -16.0f),
Size = Size + new Vector2(32.0f, 32.0f),
};
overlayNode.AttachNode(this);
ChildNodes.Add(overlayNode);
}
overlayNode.ShowParts = currentEditMode.HasFlag(NodeEditMode.Resize);
if (editEventListener is null) {
editEventListener = new ViewportEventListener(OnEditEvent);
editEventListener.AddEvent(AtkEventType.MouseMove, overlayNode);
editEventListener.AddEvent(AtkEventType.MouseDown, overlayNode);
}
}
public void DisableEditMode(NodeEditMode mode) {
currentEditMode &= ~mode;
if (currentEditMode.HasFlag(NodeEditMode.Resize) || currentEditMode.HasFlag(NodeEditMode.Move)) return;
if (editEventListener is not null) {
editEventListener.RemoveEvent(AtkEventType.MouseMove);
editEventListener.RemoveEvent(AtkEventType.MouseDown);
editEventListener.Dispose();
editEventListener = null;
}
if (overlayNode is not null) {
ChildNodes.Remove(overlayNode);
overlayNode.DetachNode();
overlayNode.Dispose();
overlayNode = null;
}
}
private void OnEditEvent(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
if (overlayNode is null) return;
if (editEventListener is null) return;
ref var mouseData = ref atkEventData->MouseData;
var mousePosition = new Vector2(mouseData.PosX, mouseData.PosY);
var mouseDelta = mousePosition - clickStartPosition;
switch (eventType) {
// Move Logic
case AtkEventType.MouseMove when isMoving: {
Position += mouseDelta;
clickStartPosition = mousePosition;
atkEvent->SetEventIsHandled(true);
}
break;
// Update hover state when not resizing, as we latch that for the behavior
case AtkEventType.MouseMove when !isResizing: {
overlayNode.UpdateHover(atkEventData);
}
break;
// Resize Logic
case AtkEventType.MouseMove when isResizing: {
Position += overlayNode.GetPositionDelta(mouseDelta);
Size += overlayNode.GetSizeDelta(mouseDelta);
overlayNode.Size = Size + new Vector2(32.0f, 32.0f);
clickStartPosition = mousePosition;
atkEvent->SetEventIsHandled(true);
}
break;
// Begin Resize Event
case AtkEventType.MouseDown when !isResizing && overlayNode.AnyHovered() && currentEditMode.HasFlag(NodeEditMode.Resize): {
editEventListener.AddEvent(AtkEventType.MouseUp, overlayNode);
isResizing = true;
clickStartPosition = mousePosition;
atkEvent->SetEventIsHandled(true);
}
break;
// End Resize Event
case AtkEventType.MouseUp when isResizing: {
OnResizeComplete?.Invoke(this);
OnEditComplete?.Invoke(this);
isResizing = false;
editEventListener.RemoveEvent(AtkEventType.MouseUp);
}
break;
// Begin Move Event
case AtkEventType.MouseDown when !overlayNode.AnyHovered() && overlayNode.CheckCollision(atkEventData) && !isMoving && currentEditMode.HasFlag(NodeEditMode.Move): {
editEventListener.AddEvent(AtkEventType.MouseUp, overlayNode);
isMoving = true;
clickStartPosition = mousePosition;
atkEvent->SetEventIsHandled(true);
}
break;
// End Move Event
case AtkEventType.MouseUp when isMoving: {
OnMoveComplete?.Invoke(this);
OnEditComplete?.Invoke(this);
isMoving = false;
editEventListener.RemoveEvent(AtkEventType.MouseUp);
}
break;
}
if (isCursorSet) {
ResetCursor();
isCursorSet = false;
}
if (currentEditMode.HasFlag(NodeEditMode.Move)) {
if (isMoving) {
SetCursor(AddonCursorType.Grab);
isCursorSet = true;
}
else if (CheckCollision(atkEventData)) {
SetCursor(AddonCursorType.Hand);
isCursorSet = true;
}
}
if (overlayNode.AnyHovered() && currentEditMode.HasFlag(NodeEditMode.Resize)) {
overlayNode.SetCursor();
isCursorSet = true;
}
}
private static void SetCursor(AddonCursorType cursor)
=> DalamudInterface.Instance.AddonEventManager.SetCursor(cursor);
private static void ResetCursor()
=> DalamudInterface.Instance.AddonEventManager.ResetCursor();
}