Initial commit: AetherBags + KamiToolKit for FC Gitea
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public sealed class AlphaImageNode : ImGuiImageNode {
|
||||
public AlphaImageNode() {
|
||||
TexturePath = DalamudInterface.Instance.GetAssetPath("alpha_background.png");
|
||||
WrapMode = WrapMode.Tile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class AlternateCooldownNode : ResNode {
|
||||
|
||||
public readonly ImageNode CooldownImage;
|
||||
|
||||
public AlternateCooldownNode() {
|
||||
CooldownImage = new ImageNode {
|
||||
NodeId = 15,
|
||||
Size = new Vector2(44.0f, 46.0f),
|
||||
Position = new Vector2(0.0f, 2.0f),
|
||||
Origin = new Vector2(22.0f, 23.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
PartId = 80,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconARecast2Texture(CooldownImage);
|
||||
|
||||
CooldownImage.AttachNode(this);
|
||||
|
||||
BuildTimeline();
|
||||
}
|
||||
|
||||
private void BuildTimeline() {
|
||||
CooldownImage.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(11, 92)
|
||||
.AddFrame(11, alpha: 255, scale: new Vector2(1.0f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f), partId: 1)
|
||||
.AddFrame(92, alpha: 255, scale: new Vector2(1.0f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f), partId: 79)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(93, 174)
|
||||
.AddFrame(93, alpha: 255, scale: new Vector2(1.0f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f), partId: 82)
|
||||
.AddFrame(174, alpha: 255, scale: new Vector2(1.0f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f), partId: 160)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(175, 205)
|
||||
.AddFrame(175, alpha: 255, scale: new Vector2(1.0f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f), partId: 80)
|
||||
.AddFrame(191, alpha: 255, scale: new Vector2(1.2f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(200.0f), partId: 80)
|
||||
.AddFrame(205, alpha: 0, scale: new Vector2(1.25f), multiplyColor: new Vector3(100.0f), addColor: new Vector3(200.0f), partId: 80)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class AntsNode : ResNode {
|
||||
|
||||
public readonly ImageNode AntsImageNode;
|
||||
|
||||
public AntsNode() {
|
||||
AntsImageNode = new ImageNode {
|
||||
NodeId = 13,
|
||||
Size = new Vector2(48, 48),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
PartId = 13,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconAFrameTexture(AntsImageNode);
|
||||
|
||||
AntsImageNode.AttachNode(this);
|
||||
|
||||
BuildTimeline();
|
||||
}
|
||||
|
||||
private void BuildTimeline() {
|
||||
AntsImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(2, 9)
|
||||
.AddFrame(2, partId: 6)
|
||||
.AddFrame(9, partId: 13)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A simple image node that makes it easy to display a single color.
|
||||
/// </summary>
|
||||
public unsafe class BackgroundImageNode : SimpleImageNode {
|
||||
public BackgroundImageNode() {
|
||||
FitTexture = true;
|
||||
}
|
||||
|
||||
public new Vector4 Color {
|
||||
get => new(AddColor.X, AddColor.Y, AddColor.Z, ResNode->Color.A / 255.0f);
|
||||
set {
|
||||
ResNode->Color = new Vector4(0.0f, 0.0f, 0.0f, value.W).ToByteColor();
|
||||
AddColor = value.AsVector3Color();
|
||||
}
|
||||
}
|
||||
|
||||
public new ColorHelpers.HsvaColor ColorHsva {
|
||||
get => ColorHelpers.RgbaToHsv(Color);
|
||||
set => Color = ColorHelpers.HsvToRgb(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A node that shows a border loaded from the party list textures
|
||||
/// </summary>
|
||||
public unsafe class BorderNineGridNode : NineGridNode {
|
||||
public BorderNineGridNode() {
|
||||
PartsList.Add(new Part {
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
Size = new Vector2(64.0f, 64.0f),
|
||||
Id = 0,
|
||||
TexturePath = "ui/uld/PartyListTargetBase.tex",
|
||||
});
|
||||
|
||||
TopOffset = 20;
|
||||
LeftOffset = 20;
|
||||
RightOffset = 20;
|
||||
BottomOffset = 20;
|
||||
PartsRenderType = 108;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
// Simple helper class for making basic text label, node will auto-resize to fit label
|
||||
public sealed class CategoryTextNode : TextNode {
|
||||
public CategoryTextNode() {
|
||||
Height = 16.0f;
|
||||
TextFlags = TextFlags.AutoAdjustNodeSize;
|
||||
TextColor = ColorHelper.GetColor(2);
|
||||
TextOutlineColor = ColorHelper.GetColor(7);
|
||||
FontType = FontType.Axis;
|
||||
FontSize = 14;
|
||||
LineSpacing = 24;
|
||||
AlignmentType = AlignmentType.Left;
|
||||
}
|
||||
|
||||
public override float Height {
|
||||
get => base.Height;
|
||||
set => base.Height = value + 8.0f; // Add extra height for padding
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class CheckboxNode : ComponentNode<AtkComponentCheckBox, AtkUldComponentDataCheckBox> {
|
||||
|
||||
public readonly ImageNode BoxBackground;
|
||||
public readonly ImageNode BoxForeground;
|
||||
public readonly TextNode Label;
|
||||
|
||||
public CheckboxNode() {
|
||||
SetInternalComponentType(ComponentType.CheckBox);
|
||||
|
||||
BoxBackground = new SimpleImageNode {
|
||||
TexturePath = "ui/uld/CheckBoxA.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(16.0f, 16.0f),
|
||||
Size = new Vector2(16.0f, 16.0f),
|
||||
Position = new Vector2(0.0f, 2.0f),
|
||||
WrapMode = WrapMode.Stretch,
|
||||
};
|
||||
BoxBackground.AttachNode(this);
|
||||
|
||||
BoxForeground = new SimpleImageNode {
|
||||
TexturePath = "ui/uld/CheckBoxA.tex",
|
||||
TextureCoordinates = new Vector2(16.0f, 0.0f),
|
||||
TextureSize = new Vector2(16.0f, 16.0f),
|
||||
Size = new Vector2(16.0f, 16.0f),
|
||||
Position = new Vector2(0.0f, 2.0f),
|
||||
WrapMode = WrapMode.Stretch,
|
||||
};
|
||||
BoxForeground.AttachNode(this);
|
||||
|
||||
Label = new TextNode {
|
||||
Size = new Vector2(0.0f, 20.0f),
|
||||
Position = new Vector2(20.0f, 0.0f),
|
||||
FontType = FontType.Axis,
|
||||
AlignmentType = AlignmentType.Left,
|
||||
FontSize = 14,
|
||||
LineSpacing = 14,
|
||||
TextColor = ColorHelper.GetColor(8),
|
||||
TextOutlineColor = ColorHelper.GetColor(7),
|
||||
TextFlags = TextFlags.AutoAdjustNodeSize,
|
||||
};
|
||||
Label.AttachNode(this);
|
||||
|
||||
Component->Flags = 606464;
|
||||
|
||||
Data->Nodes[0] = Label.NodeId;
|
||||
Data->Nodes[1] = BoxBackground.NodeId;
|
||||
Data->Nodes[2] = 0;
|
||||
|
||||
LoadTimelines();
|
||||
|
||||
AddEvent(AtkEventType.ButtonClick, ClickHandler);
|
||||
AddEvent(AtkEventType.InputReceived, ClickHandler);
|
||||
|
||||
InitializeComponentEvents();
|
||||
Component->Left = 20;
|
||||
Component->Right = 20;
|
||||
Component->Top = 0;
|
||||
Component->Bottom = 0;
|
||||
|
||||
BoxForeground.IsVisible = Component->IsChecked;
|
||||
BoxForeground.DrawFlags = 0;
|
||||
}
|
||||
|
||||
public Action<bool>? OnClick { get; set; }
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => Label.String;
|
||||
set {
|
||||
Label.String = value;
|
||||
Width = Height + Label.Width + 4.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsChecked {
|
||||
get => Component->IsChecked;
|
||||
set => Component->SetChecked(value);
|
||||
}
|
||||
|
||||
private void ClickHandler() {
|
||||
OnClick?.Invoke(Component->IsChecked);
|
||||
}
|
||||
|
||||
public bool DisableAutoResize {
|
||||
get => Label.TextFlags.HasFlag(TextFlags.AutoAdjustNodeSize);
|
||||
set {
|
||||
if (value) {
|
||||
Label.TextFlags &= ~TextFlags.AutoAdjustNodeSize;
|
||||
Label.TextFlags |= TextFlags.Ellipsis;
|
||||
}
|
||||
else {
|
||||
Label.TextFlags |= TextFlags.AutoAdjustNodeSize;
|
||||
Label.TextFlags &= ~TextFlags.Ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
BoxBackground.Size = new Vector2(Height, Height) - new Vector2(4.0f, 4.0f);
|
||||
BoxForeground.Size = new Vector2(Height, Height) - new Vector2(4.0f, 4.0f);
|
||||
|
||||
Label.Height = Height;
|
||||
Label.X = Height;
|
||||
|
||||
if (DisableAutoResize) {
|
||||
Label.Width = Width - Height;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTimelines() {
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 155)
|
||||
.AddLabelPair(1, 10, 1)
|
||||
.AddLabelPair(11, 20, 2)
|
||||
.AddLabelPair(21, 30, 3)
|
||||
.AddLabelPair(31, 40, 7)
|
||||
.AddLabelPair(41, 50, 6)
|
||||
.AddLabelPair(51, 60, 4)
|
||||
.AddLabelPair(61, 70, 8)
|
||||
.AddLabelPair(71, 80, 9)
|
||||
.AddLabelPair(81, 90, 10)
|
||||
.AddLabelPair(91, 100, 14)
|
||||
.AddLabelPair(101, 110, 13)
|
||||
.AddLabelPair(111, 115, 11)
|
||||
.AddLabelPair(116, 125, 12)
|
||||
.AddLabelPair(126, 135, 5)
|
||||
.AddLabelPair(136, 145, 15)
|
||||
.AddLabelPair(146, 155, 16)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
CollisionNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 155)
|
||||
.AddEmptyFrame(1)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
BoxBackground.AddTimeline(new TimelineBuilder()
|
||||
.AddFrameSetWithFrame(1, 10, 1, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.BeginFrameSet(11, 20)
|
||||
.AddFrame(11, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(13, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(21, 30, 21, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(31, 40, 31, new Vector2(0.0f, 2.0f), 102, multiplyColor: new Vector3(80.0f))
|
||||
.AddFrameSetWithFrame(41, 50, 41, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.BeginFrameSet(51, 60)
|
||||
.AddFrame(51, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.AddFrame(60, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(61, 70, 61, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.BeginFrameSet(71, 80)
|
||||
.AddFrame(71, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(73, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(81, 90, 81, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(91, 100, 91, new Vector2(0.0f, 2.0f), 102, multiplyColor: new Vector3(80.0f))
|
||||
.AddFrameSetWithFrame(101, 110, 101, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.BeginFrameSet(111, 115)
|
||||
.AddFrame(111, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.AddFrame(115, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(116, 125, 116, new Vector2(0.0f, 2.0f), addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(126, 135, 126, new Vector2(0.0f, 2.0f), 255, new Vector3(16.0f), new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(136, 145, 126, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(146, 155, 146, new Vector2(0.0f, 2.0f), 255, multiplyColor: new Vector3(100.0f))
|
||||
.Build());
|
||||
|
||||
BoxForeground.AddTimeline(new TimelineBuilder()
|
||||
.AddFrameSetWithFrame(61, 70, 61, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.BeginFrameSet(71, 80)
|
||||
.AddFrame(71, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(73, alpha: 255, multiplyColor: new Vector3(100.0f), addColor: new Vector3(16.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(81, 90, 81, alpha: 255, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(91, 100, 91, alpha: 102, multiplyColor: new Vector3(80.0f))
|
||||
.AddFrameSetWithFrame(101, 110, 101, alpha: 255, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.BeginFrameSet(111, 115)
|
||||
.AddFrame(111, alpha: 255, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(115, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(116, 125)
|
||||
.AddFrame(116, alpha: 0, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(119, alpha: 255, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(126, 135)
|
||||
.AddFrame(126, alpha: 255, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(129, alpha: 0, addColor: new Vector3(16.0f), multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(136, 145)
|
||||
.AddFrame(136, alpha: 0, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(140, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(146, 255)
|
||||
.AddFrame(146, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrame(150, alpha: 0, multiplyColor: new Vector3(100.0f))
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
Label.AddTimeline(new TimelineBuilder()
|
||||
.AddFrameSetWithFrame(1, 10, 1, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(11, 20, 11, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(21, 30, 21, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(31, 40, 31, alpha: 102, multiplyColor: new Vector3(80.0f))
|
||||
.AddFrameSetWithFrame(41, 50, 41, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(51, 60, 51, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(61, 70, 61, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(71, 80, 71, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(81, 90, 81, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(91, 100, 91, alpha: 102, multiplyColor: new Vector3(80.0f))
|
||||
.AddFrameSetWithFrame(101, 110, 101, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(111, 115, 111, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(116, 135, 116, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(126, 135, 126, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(136, 145, 136, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.AddFrameSetWithFrame(146, 155, 146, alpha: 255, multiplyColor: new Vector3(100.0f))
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class ClippingMaskNode : NodeBase<AtkClippingMaskNode> {
|
||||
public readonly PartsList PartsList;
|
||||
|
||||
public ClippingMaskNode() : base(NodeType.ClippingMask) {
|
||||
PartsList = new PartsList();
|
||||
|
||||
Node->PartsList = PartsList.InternalPartsList;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
if (!isNativeDestructor) {
|
||||
PartsList.Dispose();
|
||||
Node->PartsList = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort PartId {
|
||||
get => Node->PartId;
|
||||
set => Node->PartId = value;
|
||||
}
|
||||
|
||||
public AtkUldPart* AddPart(Part part)
|
||||
=> PartsList.Add(part);
|
||||
|
||||
public void AddPart(params Part[] parts)
|
||||
=> PartsList.Add(parts);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class CollisionNode() : NodeBase<AtkCollisionNode>(NodeType.Collision) {
|
||||
public virtual CollisionType CollisionType {
|
||||
get => Node->CollisionType;
|
||||
set => Node->CollisionType = value;
|
||||
}
|
||||
|
||||
public virtual uint Uses {
|
||||
get => Node->Uses;
|
||||
set => Node->Uses = (ushort)value;
|
||||
}
|
||||
|
||||
public virtual AtkComponentBase* LinkedComponent {
|
||||
get => Node->LinkedComponent;
|
||||
set => Node->LinkedComponent = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class CooldownNode : ResNode {
|
||||
|
||||
public readonly ImageNode CooldownImage;
|
||||
public readonly ImageNode GlossyImageFrame;
|
||||
|
||||
public CooldownNode() {
|
||||
GlossyImageFrame = new ImageNode {
|
||||
NodeId = 18,
|
||||
Size = new Vector2(48.0f, 48.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconAFrameTexture(GlossyImageFrame);
|
||||
|
||||
GlossyImageFrame.AttachNode(this);
|
||||
|
||||
CooldownImage = new ImageNode {
|
||||
NodeId = 17,
|
||||
Size = new Vector2(44.0f, 46.0f),
|
||||
Position = new Vector2(2.0f, 2.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
PartId = 80,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconARecastTexture(CooldownImage);
|
||||
|
||||
CooldownImage.AttachNode(this);
|
||||
|
||||
BuildTimelines();
|
||||
}
|
||||
|
||||
private void BuildTimelines() {
|
||||
GlossyImageFrame.AddTimeline(new TimelineBuilder()
|
||||
.AddFrameSetWithFrame(1, 10, 1, partId: 0)
|
||||
.AddFrameSetWithFrame(11, 20, 11, partId: 1)
|
||||
.AddFrameSetWithFrame(21, 30, 21, partId: 2)
|
||||
.AddFrameSetWithFrame(31, 40, 31, partId: 3)
|
||||
.AddFrameSetWithFrame(41, 50, 41, partId: 18)
|
||||
.AddFrameSetWithFrame(51, 60, 51, partId: 19)
|
||||
.AddFrameSetWithFrame(143, 165, 143, partId: 0)
|
||||
.Build());
|
||||
|
||||
CooldownImage.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(61, 142)
|
||||
.AddFrame(61, alpha: 255, partId: 1)
|
||||
.AddFrame(142, alpha: 255, partId: 79)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(143, 165)
|
||||
.AddFrame(143, alpha: 255, partId: 80)
|
||||
.AddFrame(165, alpha: 0, partId: 79)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
using Lumina.Text.Payloads;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A counter node for displaying numbers
|
||||
/// </summary>
|
||||
public unsafe class CounterNode : NodeBase<AtkCounterNode> {
|
||||
|
||||
public readonly PartsList PartsList;
|
||||
|
||||
public CounterNode() : base(NodeType.Counter) {
|
||||
PartsList = new PartsList();
|
||||
PartsList.Add(new Part());
|
||||
|
||||
Node->PartsList = PartsList.InternalPartsList;
|
||||
|
||||
NumberWidth = 10;
|
||||
CommaWidth = 8;
|
||||
SpaceWidth = 6;
|
||||
TextAlignment = AlignmentType.Right;
|
||||
CounterWidth = 32;
|
||||
Font = CounterFont.MoneyFont;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
if (!isNativeDestructor) {
|
||||
PartsList.Dispose();
|
||||
Node->PartsList = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
protected string TexturePath {
|
||||
get => PartsList[0]->LoadedPath;
|
||||
set => PartsList[0]->LoadTexture(value);
|
||||
}
|
||||
|
||||
protected Vector2 TextureCoordinates {
|
||||
get => new(PartsList[0]->U, PartsList[0]->V);
|
||||
set {
|
||||
PartsList[0]->U = (ushort) value.X;
|
||||
PartsList[0]->V = (ushort) value.X;
|
||||
}
|
||||
}
|
||||
|
||||
protected Vector2 TextureSize {
|
||||
get => new(PartsList[0]->Width, PartsList[0]->Height);
|
||||
set {
|
||||
PartsList[0]->Width = (ushort) value.X;
|
||||
PartsList[0]->Height = (ushort) value.X;
|
||||
}
|
||||
}
|
||||
|
||||
public uint NumberWidth {
|
||||
get => Node->NumberWidth;
|
||||
set => Node->NumberWidth = (byte)value;
|
||||
}
|
||||
|
||||
public uint CommaWidth {
|
||||
get => Node->CommaWidth;
|
||||
set => Node->CommaWidth = (byte)value;
|
||||
}
|
||||
|
||||
public uint SpaceWidth {
|
||||
get => Node->SpaceWidth;
|
||||
set => Node->SpaceWidth = (byte)value;
|
||||
}
|
||||
|
||||
public AlignmentType TextAlignment {
|
||||
get => (AlignmentType) Node->TextAlign;
|
||||
set => Node->TextAlign = (ushort) value;
|
||||
}
|
||||
|
||||
public float CounterWidth {
|
||||
get => Node->CounterWidth;
|
||||
set => Node->CounterWidth = value;
|
||||
}
|
||||
|
||||
public int Number {
|
||||
get => int.Parse(Node->NodeText.ToString());
|
||||
set => Node->SetText(ParseNumber(value));
|
||||
}
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => Node->NodeText.AsSpan();
|
||||
set => Node->SetText(ParseString(value));
|
||||
}
|
||||
|
||||
public CounterFont Font {
|
||||
get;
|
||||
set {
|
||||
field = value;
|
||||
|
||||
var fontPath = string.Empty;
|
||||
var partSize = Vector2.Zero;
|
||||
|
||||
switch (value) {
|
||||
case CounterFont.MoneyFont:
|
||||
fontPath = "ui/uld/Money_Number.tex";
|
||||
partSize = new Vector2(22.0f, 22.0f);
|
||||
break;
|
||||
|
||||
case CounterFont.ChocoboRace:
|
||||
fontPath = "ui/uld/RaceChocoboNum.tex";
|
||||
partSize = new Vector2(30.0f, 60.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fontPath != string.Empty && partSize != Vector2.Zero) {
|
||||
PartsList[0]->Width = (ushort)partSize.X;
|
||||
PartsList[0]->Height = (ushort)partSize.Y;
|
||||
PartsList[0]->LoadTexture(fontPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlySeString ParseString(ReadOnlySeString value) {
|
||||
using var builder = new RentedSeStringBuilder();
|
||||
return builder.Builder.Append(value).GetViewAsSpan();
|
||||
}
|
||||
|
||||
private static ReadOnlySeString ParseNumber(int value) {
|
||||
using var rentedBuilder = new RentedSeStringBuilder();
|
||||
|
||||
// <kilo(lnum1,\,)>
|
||||
var evaluatedString = DalamudInterface.Instance.SeStringEvaluator.EvaluateFromAddon(18, [ value ]);
|
||||
|
||||
foreach (var payload in evaluatedString) {
|
||||
switch (payload.Type) {
|
||||
|
||||
// Fix for French thousands separators.
|
||||
// The game calls FormatAddonText2 that does this.
|
||||
case ReadOnlySePayloadType.Macro when payload.MacroCode is MacroCode.NonBreakingSpace:
|
||||
rentedBuilder.Builder.Append(' ');
|
||||
break;
|
||||
|
||||
default:
|
||||
rentedBuilder.Builder.Append(payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rentedBuilder.Builder.GetViewAsSpan();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class CursorNode : ResNode {
|
||||
|
||||
public readonly SimpleImageNode CursorImageNode;
|
||||
|
||||
public CursorNode() {
|
||||
CursorImageNode = new SimpleImageNode {
|
||||
NodeId = 3,
|
||||
TexturePath = "ui/uld/TextInputA.tex",
|
||||
Size = new Vector2(4.0f, 24.0f),
|
||||
TextureCoordinates = new Vector2(68.0f, 0.0f),
|
||||
TextureSize = new Vector2(4.0f, 24.0f),
|
||||
WrapMode = WrapMode.Tile,
|
||||
};
|
||||
CursorImageNode.AttachNode(this);
|
||||
|
||||
CursorImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 8)
|
||||
.AddEmptyFrame(1)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
Timeline?.PlayAnimation(101);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Client.Enums;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class DragDropNode : ComponentNode<AtkComponentDragDrop, AtkUldComponentDataDragDrop> {
|
||||
|
||||
public readonly ImageNode DragDropBackgroundNode;
|
||||
public readonly IconNode IconNode;
|
||||
|
||||
public DragDropNode() {
|
||||
SetInternalComponentType(ComponentType.DragDrop);
|
||||
|
||||
DragDropBackgroundNode = new SimpleImageNode {
|
||||
NodeId = 3,
|
||||
Size = new Vector2(44.0f, 44.0f),
|
||||
TexturePath = "ui/uld/DragTargetA.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(44.0f, 44.0f),
|
||||
WrapMode = WrapMode.Tile,
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
DragDropBackgroundNode.AttachNode(this);
|
||||
|
||||
IconNode = new IconNode {
|
||||
NodeId = 2,
|
||||
Size = new Vector2(44.0f, 48.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
IconNode.AttachNode(this);
|
||||
|
||||
LoadTimelines();
|
||||
|
||||
Data->Nodes[0] = IconNode.NodeId;
|
||||
|
||||
AcceptedType = DragDropType.Everything;
|
||||
Payload = new DragDropPayload();
|
||||
|
||||
Component->AtkDragDropInterface.DragDropType = DragDropType.Everything;
|
||||
Component->AtkDragDropInterface.DragDropReferenceIndex = 0;
|
||||
|
||||
InitializeComponentEvents();
|
||||
|
||||
AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler);
|
||||
AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler);
|
||||
AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler);
|
||||
AddEvent(AtkEventType.DragDropClick, DragDropClickHandler);
|
||||
AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler);
|
||||
AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler);
|
||||
}
|
||||
|
||||
private bool IsDragDropEndRegistered { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when a DragDrop is beginning
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnBegin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when a DragDrop has finished
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when a compatible DragDrop is dropped onto this node
|
||||
/// </summary>
|
||||
public Action<DragDropNode, DragDropPayload>? OnPayloadAccepted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when the item in this drag drop is being dropped onto the world
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnDiscard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when the item is clicked
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnClicked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when the item is being moused over
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnRollOver { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event that is triggered when the item is no longer being moused over
|
||||
/// </summary>
|
||||
public Action<DragDropNode>? OnRollOut { get; set; }
|
||||
|
||||
public DragDropPayload Payload { get; set; }
|
||||
|
||||
public uint IconId {
|
||||
get => IconNode.IconId;
|
||||
set {
|
||||
IconNode.IconId = value;
|
||||
IconNode.IsVisible = value != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsIconDisabled {
|
||||
get => IconNode.IsIconDisabled;
|
||||
set => IconNode.IsIconDisabled = value;
|
||||
}
|
||||
|
||||
public int Quantity {
|
||||
get => int.Parse(Component->GetQuantityText().ToString());
|
||||
set => Component->SetQuantity(value);
|
||||
}
|
||||
|
||||
public string QuantityString {
|
||||
get => Component->GetQuantityText().ToString();
|
||||
set => Component->SetQuantityText(value);
|
||||
}
|
||||
|
||||
public DragDropType AcceptedType {
|
||||
get => Component->AcceptedType;
|
||||
set => Component->AcceptedType = value;
|
||||
}
|
||||
|
||||
public AtkDragDropInterface.SoundEffectSuppression SoundEffectSuppression {
|
||||
get => Component->AtkDragDropInterface.DragDropSoundEffectSuppression;
|
||||
set => Component->AtkDragDropInterface.DragDropSoundEffectSuppression = value;
|
||||
}
|
||||
|
||||
public bool IsDraggable {
|
||||
get => !Component->Flags.HasFlag(DragDropFlag.Locked);
|
||||
set {
|
||||
if (value) {
|
||||
Component->Flags &= ~DragDropFlag.Locked;
|
||||
}
|
||||
else {
|
||||
Component->Flags |= DragDropFlag.Locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When true, allows left-clicking the item to trigger OnClicked
|
||||
/// </summary>
|
||||
public bool IsClickable {
|
||||
get => Component->Flags.HasFlag(DragDropFlag.Clickable);
|
||||
set {
|
||||
if (value) {
|
||||
Component->Flags |= DragDropFlag.Clickable;
|
||||
}
|
||||
else {
|
||||
Component->Flags &= ~DragDropFlag.Clickable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||
atkEvent->SetEventIsHandled();
|
||||
Payload.ToDragDropInterface(atkEventData->DragDropData.DragDropInterface);
|
||||
OnBegin?.Invoke(this);
|
||||
|
||||
if (!IsDragDropEndRegistered) {
|
||||
AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler);
|
||||
IsDragDropEndRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override ReadOnlySeString TextTooltip {
|
||||
get;
|
||||
set {
|
||||
field = value;
|
||||
switch (value) {
|
||||
case { IsEmpty: false } when !TooltipRegistered:
|
||||
AddEvent(AtkEventType.DragDropRollOver, ShowTooltip);
|
||||
AddEvent(AtkEventType.DragDropRollOut, HideTooltip);
|
||||
|
||||
TooltipRegistered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||
atkEvent->SetEventIsHandled();
|
||||
|
||||
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
|
||||
atkEvent->State.ReturnFlags = 1;
|
||||
|
||||
var payload = DragDropPayload.FromDragDropInterface(atkEventData->DragDropData.DragDropInterface);
|
||||
|
||||
Payload.Clear();
|
||||
IconId = 0;
|
||||
|
||||
OnPayloadAccepted?.Invoke(this, payload);
|
||||
}
|
||||
|
||||
private void DragDropDiscardHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||
atkEvent->SetEventIsHandled();
|
||||
|
||||
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
|
||||
atkEvent->State.ReturnFlags = 1;
|
||||
|
||||
OnDiscard?.Invoke(this);
|
||||
}
|
||||
|
||||
private void DragDropEndHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||
atkEvent->SetEventIsHandled();
|
||||
atkEventData->DragDropData.DragDropInterface->GetPayloadContainer()->Clear();
|
||||
OnEnd?.Invoke(this);
|
||||
|
||||
if (IsDragDropEndRegistered) {
|
||||
RemoveEvent(AtkEventType.DragDropEnd, DragDropEndHandler);
|
||||
IsDragDropEndRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DragDropClickHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||
atkEvent->SetEventIsHandled();
|
||||
|
||||
atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags;
|
||||
atkEvent->State.ReturnFlags = 1;
|
||||
|
||||
OnClicked?.Invoke(this);
|
||||
}
|
||||
|
||||
private void DragDropRollOverHandler()
|
||||
=> OnRollOver?.Invoke(this);
|
||||
|
||||
private void DragDropRollOutHandler()
|
||||
=> OnRollOut?.Invoke(this);
|
||||
|
||||
/// Clear the payload data and set iconId to zero
|
||||
public void Clear() {
|
||||
Payload.Clear();
|
||||
IconId = 0;
|
||||
}
|
||||
|
||||
// Show fancy tooltip for the currently stored data
|
||||
public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) {
|
||||
if (AtkStage.Instance()->DragDropManager.IsDragging) return;
|
||||
|
||||
var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode(ResNode);
|
||||
if (addon is null) return;
|
||||
|
||||
var tooltipArgs = new AtkTooltipManager.AtkTooltipArgs();
|
||||
tooltipArgs.Ctor();
|
||||
tooltipArgs.ActionArgs.Id = Payload.Int2;
|
||||
tooltipArgs.ActionArgs.Kind = (DetailKind)actionKind;
|
||||
|
||||
AtkStage.Instance()->TooltipManager.ShowTooltip(
|
||||
AtkTooltipManager.AtkTooltipType.Action,
|
||||
addon->Id,
|
||||
ResNode,
|
||||
&tooltipArgs);
|
||||
}
|
||||
|
||||
private void LoadTimelines() {
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 59)
|
||||
.AddLabelPair(1, 10, 1)
|
||||
.AddLabelPair(11, 19, 2)
|
||||
.AddLabelPair(20, 29, 3)
|
||||
.AddLabelPair(30, 39, 7)
|
||||
.AddLabelPair(40, 49, 6)
|
||||
.AddLabelPair(50, 59, 4)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Interface.Textures;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Timelines;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class GifImageNode : ResNode {
|
||||
|
||||
public ImageNode ImageNode;
|
||||
|
||||
public GifImageNode() {
|
||||
ImageNode = new ImageNode();
|
||||
ImageNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public required string FilePath {
|
||||
set {
|
||||
Task.Run(() => LoadFrames(value));
|
||||
}
|
||||
}
|
||||
|
||||
public override float Width {
|
||||
get => base.Width;
|
||||
set {
|
||||
ImageNode.Width = value;
|
||||
base.Width = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override float Height {
|
||||
get => base.Height;
|
||||
set {
|
||||
ImageNode.Height = value;
|
||||
base.Height = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GifFrameSize { get; private set; }
|
||||
|
||||
public bool FitNodeToGif { get; set; }
|
||||
|
||||
public Action? OnGifLoaded { get; set; }
|
||||
|
||||
private async void LoadFrames(string filepath) {
|
||||
try {
|
||||
var image = await LoadAsync(filepath);
|
||||
if (image.Length <= 0) return;
|
||||
|
||||
using var memoryStream = new MemoryStream(image);
|
||||
using var processedImage = Image.Load<Rgba32>(memoryStream);
|
||||
if (processedImage.Frames.Count is 0) return;
|
||||
|
||||
uint currentPartId = 0;
|
||||
var frameDelay = processedImage.Frames.RootFrame.Metadata.GetGifMetadata().FrameDelay / 3.33333333f;
|
||||
var frameCount = (int)(processedImage.Frames.Count * frameDelay);
|
||||
GifFrameSize = new Vector2(processedImage.Width, processedImage.Height);
|
||||
|
||||
if (FitNodeToGif) {
|
||||
Size = GifFrameSize;
|
||||
}
|
||||
|
||||
foreach (var frame in processedImage.Frames) {
|
||||
var buffer = new byte[8 * frame.Width * frame.Height];
|
||||
|
||||
frame.CopyPixelDataTo(buffer);
|
||||
|
||||
var texture = await DalamudInterface.Instance.TextureProvider.CreateFromRawAsync(RawImageSpecification.Rgba32(frame.Width, frame.Height), buffer);
|
||||
|
||||
unsafe {
|
||||
var newPart = ImageNode.AddPart(new Part {
|
||||
Size = texture.Size,
|
||||
Id = currentPartId++,
|
||||
});
|
||||
|
||||
newPart->LoadTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
ImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, frameCount)
|
||||
.AddFrame(0, partId: 0)
|
||||
.AddFrame(frameCount, partId: currentPartId)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, frameCount)
|
||||
.AddLabel(1, 200, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(frameCount, 0, AtkTimelineJumpBehavior.LoopForever, 200)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
Timeline?.PlayAnimation( AtkTimelineJumpBehavior.LoopForever, 200);
|
||||
|
||||
await DalamudInterface.Instance.Framework.RunOnFrameworkThread(() => {
|
||||
OnGifLoaded?.Invoke();
|
||||
});
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<byte[]> LoadAsync(string path) {
|
||||
byte[] data = [];
|
||||
|
||||
if (File.Exists(path)) {
|
||||
data = await File.ReadAllBytesAsync(path);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class HoldButtonProgressNode : ResNode {
|
||||
|
||||
public readonly ImageNode ImageNode;
|
||||
|
||||
public HoldButtonProgressNode() {
|
||||
ImageNode = new SimpleImageNode {
|
||||
NodeId = 4,
|
||||
TexturePath = "ui/uld/LongPressButtonA.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 36.0f),
|
||||
TextureSize = new Vector2(100.0f, 36.0f),
|
||||
Size = new Vector2(0.0f, 36.0f),
|
||||
WrapMode = WrapMode.Tile,
|
||||
};
|
||||
ImageNode.AttachNode(this);
|
||||
|
||||
BuildTimelines();
|
||||
}
|
||||
|
||||
private void BuildTimelines() {
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 83)
|
||||
.AddLabel(1, 29, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(60, 30, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(61, 31, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(73, 32, AtkTimelineJumpBehavior.PlayOnce, 31)
|
||||
.AddLabel(74, 33, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(83, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(18, 26)
|
||||
.AddEmptyFrame(18)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(37, 53)
|
||||
.AddEmptyFrame(37)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(54, 71)
|
||||
.AddEmptyFrame(54)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
ImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 60)
|
||||
.AddFrame(1, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(61, 73)
|
||||
.AddFrame(61, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(74, 83)
|
||||
.AddFrame(74, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(76, addColor: new Vector3(150, 150, 100), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(83, addColor: new Vector3(20, 20, 20), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class HorizontalLineNode : SimpleNineGridNode {
|
||||
public HorizontalLineNode() {
|
||||
TexturePath = "ui/uld/WindowA_Line.tex";
|
||||
TextureCoordinates = Vector2.Zero;
|
||||
TextureSize = new Vector2(32.0f, 4.0f);
|
||||
LeftOffset = 12.0f;
|
||||
RightOffset = 12.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class IconExtras : ResNode {
|
||||
|
||||
public readonly AlternateCooldownNode AlternateCooldownNode;
|
||||
public readonly AntsNode AntsNode;
|
||||
public readonly ImageNode ChargeCountImageNode;
|
||||
public readonly ImageNode ClickFlashImageNode;
|
||||
public readonly CooldownNode CooldownNode;
|
||||
public readonly ImageNode HoveredBorderImageNode;
|
||||
public readonly TextNode QuantityTextNode;
|
||||
public readonly TextNode ResourceCostTextNode;
|
||||
|
||||
public readonly ImageNode TimelineImageNode;
|
||||
|
||||
public IconExtras() {
|
||||
TimelineImageNode = new SimpleImageNode {
|
||||
NodeId = 19,
|
||||
Size = new Vector2(40.0f, 40.0f),
|
||||
Position = new Vector2(4.0f, 4.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
ImageNodeFlags = ImageNodeFlags.AutoFit,
|
||||
};
|
||||
TimelineImageNode.AttachNode(this);
|
||||
|
||||
CooldownNode = new CooldownNode {
|
||||
NodeId = 16,
|
||||
Size = new Vector2(48.0f, 48.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
CooldownNode.AttachNode(this);
|
||||
|
||||
AlternateCooldownNode = new AlternateCooldownNode {
|
||||
NodeId = 14,
|
||||
Size = new Vector2(44.0f, 48.0f),
|
||||
Position = new Vector2(2.0f, 0.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
AlternateCooldownNode.AttachNode(this);
|
||||
|
||||
AntsNode = new AntsNode {
|
||||
NodeId = 12,
|
||||
Size = new Vector2(48.0f, 48.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
AntsNode.AttachNode(this);
|
||||
|
||||
HoveredBorderImageNode = new ImageNode {
|
||||
NodeId = 11,
|
||||
Size = new Vector2(72.0f, 72.0f),
|
||||
Position = new Vector2(-12.0f, -12.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
PartId = 16,
|
||||
WrapMode = WrapMode.Tile,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconAFrameTexture(HoveredBorderImageNode);
|
||||
|
||||
HoveredBorderImageNode.AttachNode(this);
|
||||
|
||||
ChargeCountImageNode = new ImageNode {
|
||||
NodeId = 10,
|
||||
Size = new Vector2(20.0f, 20.0f),
|
||||
Position = new Vector2(28.0f, 28.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
};
|
||||
|
||||
foreach (var yIndex in Enumerable.Range(0, 2))
|
||||
foreach (var xIndex in Enumerable.Range(0, 5)) {
|
||||
var coordinate = new Vector2(xIndex * 20.0f, yIndex * 20.0f);
|
||||
ChargeCountImageNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/IconA_ChargeIcon.tex",
|
||||
TextureCoordinates = coordinate,
|
||||
Size = new Vector2(20.0f, 20.0f),
|
||||
Id = (uint)(xIndex + yIndex),
|
||||
});
|
||||
}
|
||||
ChargeCountImageNode.AttachNode(this);
|
||||
|
||||
QuantityTextNode = new TextNode {
|
||||
NodeId = 9,
|
||||
Size = new Vector2(40.0f, 12.0f),
|
||||
Position = new Vector2(4.0f, 34.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
Color = ColorHelper.GetColor(50),
|
||||
TextOutlineColor = ColorHelper.GetColor(51),
|
||||
AlignmentType = AlignmentType.Right,
|
||||
};
|
||||
QuantityTextNode.AttachNode(this);
|
||||
|
||||
// Also cooldown time text for non-globals
|
||||
ResourceCostTextNode = new TextNode {
|
||||
NodeId = 8,
|
||||
Size = new Vector2(48.0f, 12.0f),
|
||||
Position = new Vector2(3.0f, 37.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
Color = ColorHelper.GetColor(50),
|
||||
TextOutlineColor = ColorHelper.GetColor(51),
|
||||
AlignmentType = AlignmentType.Left,
|
||||
};
|
||||
ResourceCostTextNode.AttachNode(this);
|
||||
|
||||
ClickFlashImageNode = new ImageNode {
|
||||
NodeId = 7,
|
||||
Size = new Vector2(64, 64),
|
||||
Position = new Vector2(-8.0f, -8.0f),
|
||||
Origin = new Vector2(32.0f, 32.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Tile,
|
||||
PartId = 17,
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconAFrameTexture(ClickFlashImageNode);
|
||||
|
||||
ClickFlashImageNode.AttachNode(this);
|
||||
|
||||
BuildTimelines();
|
||||
}
|
||||
|
||||
private void BuildTimelines() {
|
||||
TimelineImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 0, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.AddFrame(12, alpha: 63, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 63, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(40, 49)
|
||||
.AddFrame(40, alpha: 63, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, alpha: 63, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.AddFrame(52, alpha: 0, multiplyColor: new Vector3(100.0f), addColor: new Vector3(255.0f))
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
CooldownNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 165)
|
||||
.AddLabel(1, 19, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(11, 20, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(21, 21, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(31, 22, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(41, 101, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(51, 102, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabelPair(61, 142, 24)
|
||||
.AddLabelPair(143, 165, 25)
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(1, 9, 1, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.AddFrame(12, multiplyColor: new Vector3(100.0f), addColor: new Vector3(16.0f))
|
||||
.EndFrameSet()
|
||||
.AddFrameSetWithFrame(20, 29, 20, multiplyColor: new Vector3(100.0f), addColor: new Vector3(16.0f))
|
||||
.AddFrameSetWithFrame(30, 39, 30, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.AddFrameSetWithFrame(40, 49, 40, multiplyColor: new Vector3(100.0f), addColor: new Vector3(16.0f))
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, multiplyColor: new Vector3(100.0f), addColor: new Vector3(16.0f))
|
||||
.AddFrame(52, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
AlternateCooldownNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 205)
|
||||
.AddLabel(1, 17, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(11, 101, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(92, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(93, 102, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(174, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(175, 103, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(205, 0, AtkTimelineJumpBehavior.LoopForever, 103)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
AntsNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 9)
|
||||
.AddLabel(1, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(2, 26, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(9, 0, AtkTimelineJumpBehavior.LoopForever, 26)
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
HoveredBorderImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 0, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.AddFrame(12, alpha: 255, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 255, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(40, 49)
|
||||
.AddFrame(40, alpha: 255, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, alpha: 255, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.AddFrame(52, alpha: 0, multiplyColor: new Vector3(100.0f), addColor: new Vector3(0.0f))
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
|
||||
ClickFlashImageNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 255, scale: new Vector2(0.1f))
|
||||
.AddFrame(29, alpha: 0, scale: new Vector2(1.0f))
|
||||
.EndFrameSet()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A simple image node for use with displaying game icons.
|
||||
/// </summary>
|
||||
/// <remarks>This node is not intended to be used with multiple <see cref="Part" />'s.</remarks>
|
||||
public unsafe class IconImageNode : SimpleImageNode {
|
||||
|
||||
public IconImageNode() {
|
||||
TextureSize = new Vector2(32.0f, 32.0f);
|
||||
}
|
||||
|
||||
public uint IconId {
|
||||
get;
|
||||
set {
|
||||
if (value != field) {
|
||||
field = value;
|
||||
PartsList[0]->LoadIcon(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTextureReady => PartsList[0]->IsTextureReady;
|
||||
public uint? LoadedIconId => Node->IconId;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Enums;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class IconIndicator : ResNode {
|
||||
|
||||
public readonly ImageNode IconNode;
|
||||
|
||||
public IconIndicator(uint innerNodeId) {
|
||||
IconNode = new ImageNode {
|
||||
NodeId = innerNodeId,
|
||||
Size = new Vector2(18, 18),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
WrapMode = WrapMode.Stretch,
|
||||
PartId = (uint)(innerNodeId == 5 ? 25 : 30),
|
||||
};
|
||||
|
||||
IconNodeTextureHelper.LoadIconAFrameTexture(IconNode);
|
||||
|
||||
IconNode.AttachNode(this);
|
||||
|
||||
BuildTimeline();
|
||||
}
|
||||
|
||||
private void BuildTimeline() {
|
||||
IconNode.AddTimeline(new TimelineBuilder()
|
||||
.AddFrameSetWithFrame(11, 20, 11, partId: 14)
|
||||
.AddFrameSetWithFrame(21, 30, 21, partId: 15)
|
||||
.AddFrameSetWithFrame(31, 40, 31, partId: 21)
|
||||
.AddFrameSetWithFrame(41, 50, 41, partId: 22)
|
||||
.AddFrameSetWithFrame(51, 60, 51, partId: 23)
|
||||
.AddFrameSetWithFrame(61, 70, 61, partId: 24)
|
||||
.AddFrameSetWithFrame(71, 79, 71, partId: 29)
|
||||
.AddFrameSetWithFrame(80, 89, 80, partId: 30)
|
||||
.AddFrameSetWithFrame(90, 99, 90, partId: 25)
|
||||
.AddFrameSetWithFrame(100, 109, 100, partId: 26)
|
||||
.AddFrameSetWithFrame(110, 119, 110, partId: 27)
|
||||
.AddFrameSetWithFrame(120, 129, 120, partId: 28)
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public static unsafe class IconNodeTextureHelper {
|
||||
public static void LoadIconAFrameTexture(ImageNode image) {
|
||||
image.AddPart(new Part { Id = 0, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 1, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(48.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 2, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(0.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 3, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(48.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 4, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(0.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 5, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(48.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 6, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(96.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 7, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(144.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 8, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(192.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 9, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(96.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 10, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(144.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 11, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(192.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 12, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(96.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 13, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(144.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 14, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(192.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 15, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(192.0f, 114.0f) });
|
||||
image.AddPart(new Part { Id = 16, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(72.0f, 72.0f), TextureCoordinates = new Vector2(240.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 17, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(64.0f, 64.0f), TextureCoordinates = new Vector2(240.0f, 72.0f) });
|
||||
image.AddPart(new Part { Id = 18, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(312.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 19, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(312.0f, 48.0f) });
|
||||
image.AddPart(new Part { Id = 20, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(48.0f, 48.0f), TextureCoordinates = new Vector2(312.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 21, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(210.0f, 114.0f) });
|
||||
image.AddPart(new Part { Id = 22, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(360.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 23, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(378.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 24, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(360.0f, 114.0f) });
|
||||
image.AddPart(new Part { Id = 25, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(210.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 26, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(408.0f, 0.0f) });
|
||||
image.AddPart(new Part { Id = 27, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(408.0f, 18.0f) });
|
||||
image.AddPart(new Part { Id = 28, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(408.0f, 36.0f) });
|
||||
image.AddPart(new Part { Id = 29, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(396.0f, 96.0f) });
|
||||
image.AddPart(new Part { Id = 30, TexturePath = "ui/uld/IconA_Frame.tex", Size = new Vector2(18.0f, 18.0f), TextureCoordinates = new Vector2(396.0f, 114.0f) });
|
||||
}
|
||||
|
||||
public static void LoadIconARecast2Texture(ImageNode imageNode) {
|
||||
foreach (var yIndex in Enumerable.Range(0, 9))
|
||||
foreach (var xIndex in Enumerable.Range(0, 9)) {
|
||||
var coordinate = new Vector2(xIndex * 44.0f, yIndex * 48.0f);
|
||||
imageNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/IconA_Recast2.tex",
|
||||
TextureCoordinates = coordinate,
|
||||
Size = new Vector2(44.0f, 46.0f),
|
||||
Id = (uint)(xIndex + yIndex),
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var yIndex in Enumerable.Range(9, 9))
|
||||
foreach (var xIndex in Enumerable.Range(9, 9)) {
|
||||
var coordinate = new Vector2(xIndex * 44.0f, (yIndex - 9) * 48.0f);
|
||||
imageNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/IconA_Recast2.tex",
|
||||
TextureCoordinates = coordinate,
|
||||
Size = new Vector2(44.0f, 46.0f),
|
||||
Id = (uint)(xIndex + yIndex),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadIconARecastTexture(ImageNode imageNode) {
|
||||
foreach (var yIndex in Enumerable.Range(0, 9))
|
||||
foreach (var xIndex in Enumerable.Range(0, 9)) {
|
||||
var coordinate = new Vector2(xIndex * 44.0f, yIndex * 48.0f);
|
||||
imageNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/IconA_Recast.tex",
|
||||
TextureCoordinates = coordinate,
|
||||
Size = new Vector2(44.0f, 46.0f),
|
||||
Id = (uint)(xIndex + yIndex),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.IO;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A simple image node that allows you to load an IDalamudTextureWrap texture into a native image node.
|
||||
/// This node creates a single <see cref="Part" />
|
||||
/// </summary>
|
||||
/// <remarks>This node is not intended to be used with multiple <see cref="Part" />'s.</remarks>
|
||||
public class ImGuiImageNode : SimpleImageNode {
|
||||
|
||||
public IDalamudTextureWrap? LoadedTexture;
|
||||
|
||||
public override unsafe string TexturePath {
|
||||
get => base.TexturePath;
|
||||
set {
|
||||
if (Path.IsPathRooted(value)) {
|
||||
LoadTextureFromFile(value);
|
||||
}
|
||||
else if (DalamudInterface.Instance.DataManager.FileExists(value)) {
|
||||
PartsList[0]->LoadTexture(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes ownership of passed in IDalamudTextureWrap, disposes texture when node is disposed.
|
||||
/// </summary>
|
||||
public unsafe void LoadTexture(IDalamudTextureWrap texture) {
|
||||
var previouslyLoadedTexture = LoadedTexture;
|
||||
|
||||
PartsList[0]->LoadTexture(texture);
|
||||
|
||||
// Delay unloading texture until new texture is loaded.
|
||||
previouslyLoadedTexture?.Dispose();
|
||||
LoadedTexture = texture;
|
||||
}
|
||||
|
||||
public void LoadTextureFromFile(string fileSystemPath) {
|
||||
DalamudInterface.Instance.Framework.RunOnTick(async () => {
|
||||
Alpha = 0.0f;
|
||||
|
||||
var newTexture = await DalamudInterface.Instance.TextureProvider.GetFromFile(fileSystemPath).RentAsync();
|
||||
|
||||
LoadTexture(newTexture);
|
||||
TextureSize = newTexture.Size;
|
||||
|
||||
Alpha = 1.0f;
|
||||
MarkDirty();
|
||||
});
|
||||
}
|
||||
|
||||
// Note, disposes loaded IDalamudTextureWrap if either native or managed code frees this node.
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
|
||||
LoadedTexture?.Dispose();
|
||||
LoadedTexture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class ImageNode : NodeBase<AtkImageNode> {
|
||||
|
||||
public readonly PartsList PartsList;
|
||||
|
||||
public ImageNode() : base(NodeType.Image) {
|
||||
PartsList = new PartsList();
|
||||
|
||||
Node->PartsList = PartsList.InternalPartsList;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
if (!isNativeDestructor) {
|
||||
PartsList.Dispose();
|
||||
Node->PartsList = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
public uint PartId {
|
||||
get => Node->PartId;
|
||||
set => Node->PartId = (ushort) value;
|
||||
}
|
||||
|
||||
public WrapMode WrapMode {
|
||||
get => (WrapMode) Node->WrapMode;
|
||||
set => Node->WrapMode = (byte) value;
|
||||
}
|
||||
|
||||
public ImageNodeFlags ImageNodeFlags {
|
||||
get => Node->Flags;
|
||||
set => Node->Flags = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When set to true, will cause the loaded texture to
|
||||
/// fit itself to the size of the node
|
||||
/// </summary>
|
||||
public bool FitTexture {
|
||||
set {
|
||||
if (value) {
|
||||
ImageNodeFlags = ImageNodeFlags.AutoFit;
|
||||
WrapMode = WrapMode.Stretch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AtkUldPart* AddPart(Part part)
|
||||
=> PartsList.Add(part);
|
||||
|
||||
public void AddPart(params Part[] parts)
|
||||
=> PartsList.Add(parts);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public sealed class LabelTextNode : TextNode {
|
||||
public LabelTextNode() {
|
||||
TextColor = ColorHelper.GetColor(8);
|
||||
TextOutlineColor = ColorHelper.GetColor(7);
|
||||
FontType = FontType.Axis;
|
||||
FontSize = 14;
|
||||
LineSpacing = 24;
|
||||
AlignmentType = AlignmentType.Left;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class NineGridNode : NodeBase<AtkNineGridNode> {
|
||||
|
||||
public readonly PartsList PartsList;
|
||||
|
||||
public NineGridNode() : base(NodeType.NineGrid) {
|
||||
PartsList = new PartsList();
|
||||
|
||||
Node->PartsList = PartsList.InternalPartsList;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
if (!isNativeDestructor) {
|
||||
PartsList.Dispose();
|
||||
Node->PartsList = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
public uint PartId {
|
||||
get => Node->PartId;
|
||||
set => Node->PartId = value;
|
||||
}
|
||||
|
||||
public Vector4 Offsets {
|
||||
get => new(Node->TopOffset, Node->BottomOffset, Node->LeftOffset, Node->RightOffset);
|
||||
set {
|
||||
Node->TopOffset = (short)value.X;
|
||||
Node->BottomOffset = (short)value.Y;
|
||||
Node->LeftOffset = (short)value.Z;
|
||||
Node->RightOffset = (short)value.W;
|
||||
}
|
||||
}
|
||||
|
||||
public float TopOffset {
|
||||
get => Node->TopOffset;
|
||||
set => Node->TopOffset = (short)value;
|
||||
}
|
||||
|
||||
public float BottomOffset {
|
||||
get => Node->BottomOffset;
|
||||
set => Node->BottomOffset = (short)value;
|
||||
}
|
||||
|
||||
public float LeftOffset {
|
||||
get => Node->LeftOffset;
|
||||
set => Node->LeftOffset = (short)value;
|
||||
}
|
||||
|
||||
public float RightOffset {
|
||||
get => Node->RightOffset;
|
||||
set => Node->RightOffset = (short)value;
|
||||
}
|
||||
|
||||
public uint BlendMode {
|
||||
get => Node->BlendMode;
|
||||
set => Node->BlendMode = value;
|
||||
}
|
||||
|
||||
public byte PartsRenderType {
|
||||
get => Node->PartsTypeRenderType;
|
||||
set => Node->PartsTypeRenderType = value;
|
||||
}
|
||||
|
||||
public AtkUldPart* AddPart(Part part)
|
||||
=> PartsList.Add(part);
|
||||
|
||||
public void AddPart(params Part[] parts)
|
||||
=> PartsList.Add(parts);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.Addon.Events;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Enums;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
internal unsafe class NodeEditOverlayNode : SimpleComponentNode {
|
||||
|
||||
private readonly NineGridNode backgroundNode;
|
||||
private readonly ResizeNineGridNode bottomEditNode;
|
||||
private readonly ResizeButtonNode leftCornerEditNode;
|
||||
private readonly ResizeNineGridNode leftEditNode;
|
||||
private readonly ResizeButtonNode rightCornerEditNode;
|
||||
private readonly ResizeNineGridNode rightEditNode;
|
||||
private readonly ResizeNineGridNode topEditNode;
|
||||
|
||||
public NodeEditOverlayNode() {
|
||||
backgroundNode = new SimpleNineGridNode {
|
||||
TexturePath = "ui/uld/HUDLayout.tex",
|
||||
TextureSize = new Vector2(44.0f, 32.0f),
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TopOffset = 20,
|
||||
BottomOffset = 8,
|
||||
LeftOffset = 21,
|
||||
RightOffset = 21,
|
||||
Alpha = 0.75f,
|
||||
};
|
||||
backgroundNode.AttachNode(this);
|
||||
|
||||
rightEditNode = new ResizeNineGridNode();
|
||||
rightEditNode.AttachNode(this);
|
||||
|
||||
bottomEditNode = new ResizeNineGridNode();
|
||||
bottomEditNode.AttachNode(this);
|
||||
|
||||
leftEditNode = new ResizeNineGridNode();
|
||||
leftEditNode.AttachNode(this);
|
||||
|
||||
topEditNode = new ResizeNineGridNode();
|
||||
topEditNode.AttachNode(this);
|
||||
|
||||
rightCornerEditNode = new ResizeButtonNode(ResizeDirection.BottomRight);
|
||||
rightCornerEditNode.AttachNode(this);
|
||||
|
||||
leftCornerEditNode = new ResizeButtonNode(ResizeDirection.BottomLeft);
|
||||
leftCornerEditNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public bool ShowParts {
|
||||
get;
|
||||
set {
|
||||
field = value;
|
||||
rightEditNode.IsVisible = value;
|
||||
bottomEditNode.IsVisible = value;
|
||||
leftEditNode.IsVisible = value;
|
||||
topEditNode.IsVisible = value;
|
||||
rightCornerEditNode.IsVisible = value;
|
||||
leftCornerEditNode.IsVisible = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
backgroundNode.Size = Size - new Vector2(24.0f, 24.0f);
|
||||
backgroundNode.Position = new Vector2(12.0f, 12.0f);
|
||||
|
||||
const float lineThickness = 4.0f;
|
||||
|
||||
leftEditNode.Size = new Vector2(Height - 32.0f, lineThickness);
|
||||
leftEditNode.Position = new Vector2(16.0f + leftEditNode.Height / 2.0f, 16.0f);
|
||||
leftEditNode.RotationDegrees = 90.0f;
|
||||
|
||||
rightEditNode.Size = new Vector2(Height - 32.0f, lineThickness);
|
||||
rightEditNode.Position = new Vector2(Width - 16.0f + rightEditNode.Height / 2.0f, 16.0f);
|
||||
rightEditNode.RotationDegrees = 90.0f;
|
||||
|
||||
topEditNode.Size = new Vector2(Width - 32.0f, lineThickness);
|
||||
topEditNode.Position = new Vector2(16.0f, 16.0f - lineThickness / 2.0f);
|
||||
|
||||
bottomEditNode.Size = new Vector2(Width - 32.0f, lineThickness);
|
||||
bottomEditNode.Position = new Vector2(16.0f, Height - 16.0f - lineThickness / 2.0f);
|
||||
|
||||
leftCornerEditNode.Size = new Vector2(24.0f, 24.0f);
|
||||
leftCornerEditNode.Position = new Vector2(16.0f - lineThickness / 4.0f, Height - 16.0f - leftCornerEditNode.Height);
|
||||
|
||||
rightCornerEditNode.Size = new Vector2(24.0f, 24.0f);
|
||||
rightCornerEditNode.Position = new Vector2(Width - 16.0f - rightCornerEditNode.Width + lineThickness / 4.0f, Height - 16.0f - rightCornerEditNode.Height);
|
||||
}
|
||||
|
||||
public Vector2 GetSizeDelta(Vector2 mouseDelta) {
|
||||
if (leftEditNode.IsHovered) return new Vector2(-mouseDelta.X, 0.0f);
|
||||
if (rightEditNode.IsHovered) return new Vector2(mouseDelta.X, 0.0f);
|
||||
if (topEditNode.IsHovered) return new Vector2(0.0f, -mouseDelta.Y);
|
||||
if (bottomEditNode.IsHovered) return new Vector2(0.0f, mouseDelta.Y);
|
||||
if (rightCornerEditNode.IsHovered) return mouseDelta;
|
||||
if (leftCornerEditNode.IsHovered) return new Vector2(-mouseDelta.X, mouseDelta.Y);
|
||||
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
public Vector2 GetPositionDelta(Vector2 mouseDelta) {
|
||||
if (leftEditNode.IsHovered) return new Vector2(mouseDelta.X, 0.0f);
|
||||
if (topEditNode.IsHovered) return new Vector2(0.0f, mouseDelta.Y);
|
||||
if (leftCornerEditNode.IsHovered) return new Vector2(mouseDelta.X, 0.0f);
|
||||
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
public void UpdateHover(AtkEventData* eventData) {
|
||||
rightEditNode.IsHovered = rightEditNode.CheckCollision(eventData);
|
||||
bottomEditNode.IsHovered = bottomEditNode.CheckCollision(eventData);
|
||||
leftEditNode.IsHovered = leftEditNode.CheckCollision(eventData);
|
||||
topEditNode.IsHovered = topEditNode.CheckCollision(eventData);
|
||||
rightCornerEditNode.IsHovered = rightCornerEditNode.CheckCollision(eventData);
|
||||
leftCornerEditNode.IsHovered = leftCornerEditNode.CheckCollision(eventData);
|
||||
|
||||
if (rightCornerEditNode.IsHovered) {
|
||||
bottomEditNode.IsHovered = false;
|
||||
rightEditNode.IsHovered = false;
|
||||
}
|
||||
|
||||
if (leftCornerEditNode.IsHovered) {
|
||||
leftEditNode.IsHovered = false;
|
||||
bottomEditNode.IsHovered = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AnyHovered() {
|
||||
if (rightEditNode.IsHovered) return true;
|
||||
if (bottomEditNode.IsHovered) return true;
|
||||
if (leftEditNode.IsHovered) return true;
|
||||
if (topEditNode.IsHovered) return true;
|
||||
if (rightCornerEditNode.IsHovered) return true;
|
||||
if (leftCornerEditNode.IsHovered) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetCursor() {
|
||||
if (rightEditNode.IsHovered) SetCursor(AddonCursorType.ResizeWE);
|
||||
if (bottomEditNode.IsHovered) SetCursor(AddonCursorType.ResizeNS);
|
||||
if (leftEditNode.IsHovered) SetCursor(AddonCursorType.ResizeWE);
|
||||
if (topEditNode.IsHovered) SetCursor(AddonCursorType.ResizeNS);
|
||||
if (rightCornerEditNode.IsHovered) SetCursor(AddonCursorType.ResizeNWSR);
|
||||
if (leftCornerEditNode.IsHovered) SetCursor(AddonCursorType.ResizeNESW);
|
||||
}
|
||||
|
||||
private static void SetCursor(AddonCursorType cursor)
|
||||
=> DalamudInterface.Instance.AddonEventManager.SetCursor(cursor);
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Timelines;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class NumericInputNode : ComponentNode<AtkComponentNumericInput, AtkUldComponentDataNumericInput> {
|
||||
|
||||
public readonly ButtonBase AddButton;
|
||||
public readonly NineGridNode BackgroundNode;
|
||||
public readonly CursorNode CursorNode;
|
||||
public readonly NineGridNode FocusBorderNode;
|
||||
public readonly ButtonBase SubtractButton;
|
||||
public readonly TextNode ValueTextNode;
|
||||
|
||||
public NumericInputNode() {
|
||||
SetInternalComponentType(ComponentType.NumericInput);
|
||||
|
||||
BackgroundNode = new SimpleNineGridNode {
|
||||
NodeId = 8,
|
||||
Position = new Vector2(0.0f, 3.0f),
|
||||
TexturePath = "ui/uld/NumericStepperB.tex",
|
||||
TextureCoordinates = new Vector2(56.0f, 0.0f),
|
||||
TextureSize = new Vector2(24.0f, 24.0f),
|
||||
Height = 24.0f,
|
||||
Offsets = new Vector4(10.0f),
|
||||
};
|
||||
BackgroundNode.AttachNode(this);
|
||||
|
||||
AddButton = new TextureButtonNode {
|
||||
NodeId = 7,
|
||||
TexturePath = "ui/uld/NumericStepperB.tex",
|
||||
TextureCoordinates = new Vector2(28.0f, 0.0f),
|
||||
TextureSize = new Vector2(28.0f, 28.0f),
|
||||
Size = new Vector2(28.0f, 28.0f),
|
||||
};
|
||||
AddButton.AttachNode(this);
|
||||
|
||||
SubtractButton = new TextureButtonNode {
|
||||
NodeId = 6,
|
||||
TexturePath = "ui/uld/NumericStepperB.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(28.0f, 28.0f),
|
||||
Size = new Vector2(28.0f, 28.0f),
|
||||
};
|
||||
SubtractButton.AttachNode(this);
|
||||
|
||||
ValueTextNode = new TextNode {
|
||||
NodeId = 5,
|
||||
Position = new Vector2(6.0f, 6.0f),
|
||||
FontType = FontType.Axis,
|
||||
TextColor = ColorHelper.GetColor(1),
|
||||
FontSize = 12,
|
||||
AlignmentType = AlignmentType.Top,
|
||||
String = "999",
|
||||
};
|
||||
ValueTextNode.AttachNode(this);
|
||||
|
||||
FocusBorderNode = new SimpleNineGridNode {
|
||||
NodeId = 4,
|
||||
TexturePath = "ui/uld/TextInputA.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(24.0f, 24.0f),
|
||||
Position = new Vector2(-3.0f, -2.0f),
|
||||
Offsets = new Vector4(10.0f),
|
||||
IsVisible = false,
|
||||
};
|
||||
FocusBorderNode.AttachNode(this);
|
||||
|
||||
CursorNode = new CursorNode {
|
||||
NodeId = 2,
|
||||
Size = new Vector2(4.0f, 24.0f),
|
||||
OriginY = 4.0f,
|
||||
};
|
||||
|
||||
CursorNode.AttachNode(this);
|
||||
|
||||
BuildTimelines();
|
||||
|
||||
Data->Nodes[0] = ValueTextNode.NodeId;
|
||||
Data->Nodes[1] = 0;
|
||||
Data->Nodes[2] = CursorNode.NodeId;
|
||||
Data->Nodes[3] = AddButton.NodeId;
|
||||
Data->Nodes[4] = SubtractButton.NodeId;
|
||||
|
||||
Data->Max = int.MaxValue;
|
||||
|
||||
InitializeComponentEvents();
|
||||
|
||||
AddEvent(AtkEventType.ValueUpdate, ValueUpdateHandler);
|
||||
}
|
||||
|
||||
public int Value {
|
||||
get => Component->Value;
|
||||
set => Component->InnerSetValue(value, true, false);
|
||||
}
|
||||
|
||||
public int Min {
|
||||
get => Component->Data.Min;
|
||||
set => Component->Data.Min = value;
|
||||
}
|
||||
|
||||
public int Max {
|
||||
get => Component->Data.Max;
|
||||
set => Component->Data.Max = value;
|
||||
}
|
||||
|
||||
public int Step {
|
||||
get => Component->Data.Add;
|
||||
set => Component->Data.Add = value;
|
||||
}
|
||||
|
||||
public Action<int>? OnValueUpdate { get; set; }
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
ValueTextNode.Size = new Vector2(Width - 58.0f, Height / 2.0f);
|
||||
FocusBorderNode.Size = new Vector2(Width - 40.0f, Height + 4.0f);
|
||||
|
||||
BackgroundNode.Width = Width - 46.0f;
|
||||
AddButton.X = Width - 50.0f;
|
||||
SubtractButton.X = Width - 28.0f;
|
||||
}
|
||||
|
||||
private void ValueUpdateHandler() {
|
||||
OnValueUpdate?.Invoke(Value);
|
||||
}
|
||||
|
||||
private void BuildTimelines() {
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 29)
|
||||
.AddLabel(1, 17, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(9, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(10, 18, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(19, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(20, 7, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(29, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
BackgroundNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 9)
|
||||
.AddFrame(1, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(12, addColor: new Vector3(20, 20, 20), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
ValueTextNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 19)
|
||||
.AddFrame(1, alpha: 255)
|
||||
.AddFrame(1, textColor: new Vector3(255.0f, 255.0f, 255.0f) * 255.0f)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 127)
|
||||
.AddFrame(20, textColor: new Vector3(255.0f, 255.0f, 255.0f) * 255.0f)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
FocusBorderNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 0)
|
||||
.AddFrame(12, alpha: 255)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
CursorNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 15)
|
||||
.AddLabel(1, 101, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(15, 0, AtkTimelineJumpBehavior.LoopForever, 101)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(1, 19)
|
||||
.AddEmptyFrame(1)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A generic basic resource node.
|
||||
/// </summary>
|
||||
public class ResNode() : NodeBase<AtkResNode>(NodeType.Res);
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class ResizeNineGridNode : SimpleComponentNode {
|
||||
|
||||
public readonly NineGridNode BorderNode;
|
||||
|
||||
public ResizeNineGridNode() {
|
||||
BorderNode = new SimpleNineGridNode {
|
||||
TexturePath = "ui/uld/WindowA_line.tex",
|
||||
TextureCoordinates = new Vector2(2.0f, 1.0f),
|
||||
TextureSize = new Vector2(28.0f, 3.0f),
|
||||
LeftOffset = 12,
|
||||
RightOffset = 12,
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
};
|
||||
BorderNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public bool IsHovered {
|
||||
get;
|
||||
set {
|
||||
field = value;
|
||||
if (value) {
|
||||
BorderNode.AddColor = new Vector3(100.0f, 100.0f, 100.0f) / 255.0f;
|
||||
}
|
||||
else {
|
||||
BorderNode.AddColor = Vector3.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
BorderNode.Size = Size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class SimpleClippingMaskNode : ClippingMaskNode {
|
||||
public SimpleClippingMaskNode() {
|
||||
PartsList.Add(new Part());
|
||||
}
|
||||
|
||||
public float U {
|
||||
get => PartsList[0]->U;
|
||||
set => PartsList[0]->U = (ushort)value;
|
||||
}
|
||||
|
||||
public float V {
|
||||
get => PartsList[0]->V;
|
||||
set => PartsList[0]->V = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureCoordinates {
|
||||
get => new(U, V);
|
||||
set {
|
||||
U = value.X;
|
||||
V = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public float TextureHeight {
|
||||
get => PartsList[0]->Height;
|
||||
set => PartsList[0]->Height = (ushort)value;
|
||||
}
|
||||
|
||||
public float TextureWidth {
|
||||
get => PartsList[0]->Width;
|
||||
set => PartsList[0]->Width = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureSize {
|
||||
get => new(TextureWidth, TextureHeight);
|
||||
set {
|
||||
TextureWidth = value.X;
|
||||
TextureHeight = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string TexturePath {
|
||||
get => PartsList[0]->LoadedPath;
|
||||
set => PartsList[0]->LoadTexture(value);
|
||||
}
|
||||
|
||||
public Vector2 ActualTextureSize => PartsList[0]->LoadedTextureSize;
|
||||
|
||||
public void LoadTexture(string path)
|
||||
=> PartsList[0]->LoadTexture(path);
|
||||
|
||||
public void LoadIcon(uint iconId)
|
||||
=> PartsList[0]->LoadIcon(iconId);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class SimpleComponentNode : ComponentNode<AtkComponentBase, AtkUldComponentDataBase> {
|
||||
public override ReadOnlySeString TextTooltip {
|
||||
get => CollisionNode.TextTooltip;
|
||||
set => CollisionNode.TextTooltip = value;
|
||||
}
|
||||
|
||||
public bool DisableCollisionNode {
|
||||
set {
|
||||
if (!value) {
|
||||
throw new Exception("Clearing DisableCollisionNode is not supported.");
|
||||
}
|
||||
|
||||
CollisionNode.NodeFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class SimpleCounterNode : CounterNode {
|
||||
public SimpleCounterNode() {
|
||||
PartsList.Add(new Part {
|
||||
TexturePath = "ui/uld/Money_Number.tex",
|
||||
TextureCoordinates = Vector2.Zero,
|
||||
Size = new Vector2(22.0f, 22.0f),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A simple image node that automatically creates a single <see cref="Part" />, and exposes helpers to modify that
|
||||
/// part.
|
||||
/// </summary>
|
||||
/// <remarks>This node is not intended to be used with multiple <see cref="Part" />'s.</remarks>
|
||||
public unsafe class SimpleImageNode : ImageNode {
|
||||
public SimpleImageNode() {
|
||||
PartsList.Add(new Part());
|
||||
}
|
||||
|
||||
public float U {
|
||||
get => PartsList[0]->U;
|
||||
set => PartsList[0]->U = (ushort)value;
|
||||
}
|
||||
|
||||
public float V {
|
||||
get => PartsList[0]->V;
|
||||
set => PartsList[0]->V = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureCoordinates {
|
||||
get => new(U, V);
|
||||
set {
|
||||
U = value.X;
|
||||
V = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public float TextureHeight {
|
||||
get => PartsList[0]->Height;
|
||||
set => PartsList[0]->Height = (ushort)value;
|
||||
}
|
||||
|
||||
public float TextureWidth {
|
||||
get => PartsList[0]->Width;
|
||||
set => PartsList[0]->Width = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureSize {
|
||||
get => new(TextureWidth, TextureHeight);
|
||||
set {
|
||||
TextureWidth = value.X;
|
||||
TextureHeight = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string TexturePath {
|
||||
get => PartsList[0]->LoadedPath;
|
||||
set => PartsList[0]->LoadTexture(value);
|
||||
}
|
||||
|
||||
public Vector2 ActualTextureSize => PartsList[0]->LoadedTextureSize;
|
||||
|
||||
public void LoadTexture(string path, bool resolveTheme = true)
|
||||
=> PartsList[0]->LoadTexture(path, resolveTheme);
|
||||
|
||||
public void LoadIcon(uint iconId)
|
||||
=> PartsList[0]->LoadIcon(iconId);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class SimpleNineGridNode : NineGridNode {
|
||||
public SimpleNineGridNode() {
|
||||
PartsList.Add(new Part());
|
||||
}
|
||||
|
||||
public float U {
|
||||
get => PartsList[0]->U;
|
||||
set => PartsList[0]->U = (ushort)value;
|
||||
}
|
||||
|
||||
public float V {
|
||||
get => PartsList[0]->V;
|
||||
set => PartsList[0]->V = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureCoordinates {
|
||||
get => new(U, V);
|
||||
set {
|
||||
U = value.X;
|
||||
V = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public float TextureWidth {
|
||||
get => PartsList[0]->Width;
|
||||
set => PartsList[0]->Width = (ushort)value;
|
||||
}
|
||||
|
||||
public float TextureHeight {
|
||||
get => PartsList[0]->Height;
|
||||
set => PartsList[0]->Height = (ushort)value;
|
||||
}
|
||||
|
||||
public Vector2 TextureSize {
|
||||
get => new(TextureWidth, TextureHeight);
|
||||
set {
|
||||
TextureWidth = value.X;
|
||||
TextureHeight = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public string TexturePath {
|
||||
get => PartsList[0]->LoadedPath;
|
||||
set => PartsList[0]->LoadTexture(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class SimpleOverlayNode : SimpleComponentNode {
|
||||
public SimpleOverlayNode()
|
||||
=> DisableCollisionNode = true;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class TextInputSelectionListNode : ResNode {
|
||||
|
||||
public readonly NineGridNode BackgroundNode;
|
||||
public readonly TextInputButtonNode[] Buttons = new TextInputButtonNode[9];
|
||||
public readonly TextNode LabelNode;
|
||||
|
||||
public TextInputSelectionListNode() {
|
||||
BackgroundNode = new SimpleNineGridNode {
|
||||
NodeId = 15,
|
||||
Size = new Vector2(186.0f, 208.0f),
|
||||
TexturePath = "ui/uld/TextInputA.tex",
|
||||
TextureCoordinates = new Vector2(48.0f, 0.0f),
|
||||
TextureSize = new Vector2(20.0f, 20.0f),
|
||||
TopOffset = 8.0f,
|
||||
BottomOffset = 8.0f,
|
||||
LeftOffset = 9.0f,
|
||||
RightOffset = 9.0f,
|
||||
PartsRenderType = 4,
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.Fill | NodeFlags.EmitsEvents,
|
||||
};
|
||||
BackgroundNode.AttachNode(this);
|
||||
|
||||
LabelNode = new TextNode {
|
||||
NodeId = 14,
|
||||
Position = new Vector2(13.0f, 182.0f),
|
||||
Size = new Vector2(160.0f, 21.0f),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
AlignmentType = (AlignmentType)21,
|
||||
FontType = FontType.MiedingerMed,
|
||||
};
|
||||
LabelNode.AttachNode(this);
|
||||
|
||||
foreach (var index in Enumerable.Range(0, 9)) {
|
||||
Buttons[index] = new TextInputButtonNode {
|
||||
NodeId = (uint)(13 - index),
|
||||
Position = new Vector2(13.0f, 164.0f - 20.0f * index),
|
||||
Size = new Vector2(160.0f, 24.0f),
|
||||
};
|
||||
|
||||
Buttons[index].AttachNode(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class TextNineGridNode : ComponentNode<AtkComponentTextNineGrid, AtkUldComponentDataTextNineGrid> {
|
||||
|
||||
public readonly NineGridNode BackgroundNineGrid;
|
||||
public readonly TextNode TextNode;
|
||||
|
||||
public TextNineGridNode() {
|
||||
SetInternalComponentType(ComponentType.TextNineGrid);
|
||||
|
||||
BackgroundNineGrid = new SimpleNineGridNode {
|
||||
TexturePath = "ui/uld/ToolTipS.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(32.0f, 24.0f),
|
||||
TopOffset = 10,
|
||||
BottomOffset = 10,
|
||||
LeftOffset = 15,
|
||||
RightOffset = 15,
|
||||
};
|
||||
BackgroundNineGrid.AttachNode(this);
|
||||
|
||||
TextNode = new TextNode {
|
||||
TextOutlineColor = ColorHelper.GetColor(55),
|
||||
Position = new Vector2(4.0f, 1.0f),
|
||||
FontSize = 23,
|
||||
AlignmentType = AlignmentType.Right,
|
||||
FontType = FontType.TrumpGothic,
|
||||
TextFlags = TextFlags.Edge,
|
||||
};
|
||||
TextNode.AttachNode(this);
|
||||
|
||||
Data->Nodes[0] = TextNode.NodeId;
|
||||
Data->Nodes[1] = 0;
|
||||
|
||||
InitializeComponentEvents();
|
||||
|
||||
// Disable ParentNode else SetText
|
||||
// causes this node to resize itself incorrectly.
|
||||
Component->ParentNode = null;
|
||||
}
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => TextNode.String;
|
||||
set => Component->SetText(value);
|
||||
}
|
||||
|
||||
public int Number {
|
||||
get => int.Parse(TextNode.String);
|
||||
set => TextNode.String = value.ToString();
|
||||
}
|
||||
|
||||
public int FontSize {
|
||||
get => (int)TextNode.FontSize;
|
||||
set => TextNode.FontSize = (uint)value;
|
||||
}
|
||||
|
||||
public FontType FontType {
|
||||
get => TextNode.FontType;
|
||||
set => TextNode.FontType = value;
|
||||
}
|
||||
|
||||
public Vector4 TextOutlineColor {
|
||||
get => TextNode.TextOutlineColor;
|
||||
set => TextNode.TextOutlineColor = value;
|
||||
}
|
||||
|
||||
public Vector4 TextColor {
|
||||
get => TextNode.TextColor;
|
||||
set => TextNode.TextColor = value;
|
||||
}
|
||||
|
||||
public TextFlags TextFlags {
|
||||
get => TextNode.TextFlags;
|
||||
set => TextNode.TextFlags = value;
|
||||
}
|
||||
|
||||
public AlignmentType AlignmentType {
|
||||
get => TextNode.AlignmentType;
|
||||
set => TextNode.AlignmentType = value;
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
BackgroundNineGrid.Size = Size;
|
||||
TextNode.Size = Size - new Vector2(8.0f, 2.0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class TextNode : NodeBase<AtkTextNode> {
|
||||
|
||||
public TextNode() : base(NodeType.Text) {
|
||||
TextColor = ColorHelper.GetColor(8);
|
||||
TextOutlineColor = ColorHelper.GetColor(7);
|
||||
FontSize = 12;
|
||||
FontType = FontType.Axis;
|
||||
LineSpacing = 12;
|
||||
AlignmentType = AlignmentType.Left;
|
||||
}
|
||||
|
||||
public Vector4 TextColor {
|
||||
get => Node->TextColor.ToVector4();
|
||||
set => Node->TextColor = value.ToByteColor();
|
||||
}
|
||||
|
||||
public Vector4 TextOutlineColor {
|
||||
get => Node->EdgeColor.ToVector4();
|
||||
set => Node->EdgeColor = value.ToByteColor();
|
||||
}
|
||||
|
||||
public Vector4 BackgroundColor {
|
||||
get => Node->BackgroundColor.ToVector4();
|
||||
set => Node->BackgroundColor = value.ToByteColor();
|
||||
}
|
||||
|
||||
public uint SelectStart {
|
||||
get => Node->SelectStart;
|
||||
set => Node->SelectStart = value;
|
||||
}
|
||||
|
||||
public uint SelectEnd {
|
||||
get => Node->SelectEnd;
|
||||
set => Node->SelectEnd = value;
|
||||
}
|
||||
|
||||
public AlignmentType AlignmentType {
|
||||
get => Node->AlignmentType;
|
||||
set {
|
||||
Node->SetAlignment(value);
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public FontType FontType {
|
||||
get => Node->FontType;
|
||||
set {
|
||||
Node->SetFont(value);
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public TextFlags TextFlags {
|
||||
get => Node->TextFlags;
|
||||
set {
|
||||
Node->TextFlags = value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTextFlags(params TextFlags[] flags) {
|
||||
foreach (var flag in flags) {
|
||||
TextFlags |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTextFlags(params TextFlags[] flags) {
|
||||
foreach (var flag in flags) {
|
||||
TextFlags &= ~flag;
|
||||
}
|
||||
}
|
||||
|
||||
public uint FontSize {
|
||||
get => Node->FontSize;
|
||||
set {
|
||||
Node->FontSize = (byte)value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public uint LineSpacing {
|
||||
get => Node->LineSpacing;
|
||||
set {
|
||||
Node->LineSpacing = (byte)value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public uint CharSpacing {
|
||||
get => Node->CharSpacing;
|
||||
set {
|
||||
Node->CharSpacing = (byte)value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public uint TextId {
|
||||
get => Node->TextId;
|
||||
set => Node->TextId = value;
|
||||
}
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => new(Node->GetText().AsSpan());
|
||||
set {
|
||||
using var builder = new RentedSeStringBuilder();
|
||||
Node->SetText(builder.Builder.Append(value).GetViewAsSpan());
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 Size {
|
||||
get => base.Size;
|
||||
set {
|
||||
base.Size = value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNumber(int number, bool showCommas = false, bool showPlusSign = false, int digits = 0, bool zeroPad = false)
|
||||
=> Node->SetNumber(number, showCommas, showPlusSign, (byte)digits, zeroPad);
|
||||
|
||||
public Vector2 GetTextDrawSize(ReadOnlySeString text, bool considerScale = true) {
|
||||
using var builder = new RentedSeStringBuilder();
|
||||
|
||||
ushort sizeX = 0;
|
||||
ushort sizeY = 0;
|
||||
|
||||
fixed (byte* ptr = builder.Builder.Append(text).GetViewAsSpan())
|
||||
Node->GetTextDrawSize(&sizeX, &sizeY, ptr, considerScale: considerScale);
|
||||
|
||||
return new Vector2(sizeX, sizeY);
|
||||
}
|
||||
|
||||
public Vector2 GetTextDrawSize(bool considerScale = true) {
|
||||
ushort sizeX = 0;
|
||||
ushort sizeY = 0;
|
||||
|
||||
Node->GetTextDrawSize(&sizeX, &sizeY, considerScale: considerScale);
|
||||
|
||||
return new Vector2(sizeX, sizeY);
|
||||
}
|
||||
|
||||
private void UpdateText() {
|
||||
using var builder = new RentedSeStringBuilder();
|
||||
Node->SetText(builder.Builder.Append(String).GetViewAsSpan());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// WARNING: This is a non-owning texture image node.
|
||||
/// This node is meant to reference a texture that is owned elsewhere.
|
||||
/// </summary>
|
||||
public unsafe class TextureImageNode : SimpleImageNode {
|
||||
public void SetTexture(Texture* texture) {
|
||||
var asset = PartsList[0]->UldAsset;
|
||||
asset->AtkTexture.KernelTexture = texture;
|
||||
asset->AtkTexture.TextureType = TextureType.KernelTexture;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing, bool isNativeDestructor) {
|
||||
if (disposing) {
|
||||
var asset = PartsList[0]->UldAsset;
|
||||
asset->AtkTexture.KernelTexture = null;
|
||||
asset->AtkTexture.TextureType = 0;
|
||||
|
||||
base.Dispose(disposing, isNativeDestructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Timelines;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public unsafe class TreeListCategoryNode : ResNode {
|
||||
|
||||
public readonly NineGridNode BackgroundNode;
|
||||
public readonly SimpleComponentNode ChildContainer;
|
||||
public readonly ImageNode CollapseArrowNode;
|
||||
public readonly CollisionNode CollisionNode;
|
||||
public readonly TextNode LabelNode;
|
||||
|
||||
private readonly List<NodeBase> children = [];
|
||||
|
||||
public IReadOnlyCollection<TreeListHeaderNode> HeaderNodes => children.OfType<TreeListHeaderNode>().ToList();
|
||||
public IReadOnlyCollection<NodeBase> Children => children.AsReadOnly();
|
||||
public IEnumerable<T> GetNodes<T>() where T : NodeBase => children.OfType<T>();
|
||||
|
||||
public Action<bool>? OnToggle;
|
||||
|
||||
public TreeListCategoryNode() {
|
||||
CollisionNode = new CollisionNode {
|
||||
Height = 28.0f,
|
||||
};
|
||||
CollisionNode.AttachNode(this);
|
||||
|
||||
BackgroundNode = new SimpleNineGridNode {
|
||||
TexturePath = "ui/uld/ListItemB.tex",
|
||||
TextureSize = new Vector2(48.0f, 28.0f),
|
||||
TextureCoordinates = new Vector2(0.0f, 24.0f),
|
||||
Height = 28.0f,
|
||||
TopOffset = 10.0f,
|
||||
LeftOffset = 12.0f,
|
||||
RightOffset = 12.0f,
|
||||
BottomOffset = 12.0f,
|
||||
};
|
||||
BackgroundNode.AttachNode(this);
|
||||
|
||||
CollapseArrowNode = new ImageNode {
|
||||
Position = new Vector2(0.0f, 1.0f),
|
||||
Size = new Vector2(24.0f, 24.0f),
|
||||
PartId = 1,
|
||||
};
|
||||
|
||||
CollapseArrowNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/ListItemB.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
Size = new Vector2(24.0f, 24.0f),
|
||||
Id = 0,
|
||||
});
|
||||
|
||||
CollapseArrowNode.AddPart(new Part {
|
||||
TexturePath = "ui/uld/ListItemB.tex",
|
||||
TextureCoordinates = new Vector2(24.0f, 0.0f),
|
||||
Size = new Vector2(24.0f, 24.0f),
|
||||
Id = 1,
|
||||
});
|
||||
CollapseArrowNode.AttachNode(this);
|
||||
|
||||
LabelNode = new TextNode {
|
||||
Position = new Vector2(23.0f, 0.0f),
|
||||
FontType = FontType.Axis,
|
||||
FontSize = 14,
|
||||
Height = 28.0f,
|
||||
AlignmentType = AlignmentType.Left,
|
||||
TextColor = ColorHelper.GetColor(50),
|
||||
TextOutlineColor = ColorHelper.GetColor(7),
|
||||
};
|
||||
LabelNode.AttachNode(this);
|
||||
|
||||
ChildContainer = new SimpleComponentNode {
|
||||
Position = new Vector2(0.0f, 24.0f + VerticalPadding),
|
||||
};
|
||||
ChildContainer.AttachNode(this);
|
||||
|
||||
BuildTimelines();
|
||||
|
||||
CollisionNode.ShowClickableCursor = true;
|
||||
CollisionNode.AddEvent(AtkEventType.MouseOver, () => Timeline?.PlayAnimation(IsCollapsed ? 2 : 9));
|
||||
CollisionNode.AddEvent(AtkEventType.MouseOut, () => Timeline?.PlayAnimation(IsCollapsed ? 1 : 8));
|
||||
CollisionNode.AddEvent(AtkEventType.MouseClick, () => {
|
||||
IsCollapsed = !IsCollapsed;
|
||||
UpdateCollapsed();
|
||||
OnToggle?.Invoke(!IsCollapsed);
|
||||
});
|
||||
}
|
||||
|
||||
public TreeListNode? ParentTreeListNode { get; set; }
|
||||
|
||||
private bool InternalIsCollapsed { get; set; }
|
||||
|
||||
public bool IsCollapsed {
|
||||
get => InternalIsCollapsed;
|
||||
set {
|
||||
InternalIsCollapsed = value;
|
||||
UpdateCollapsed();
|
||||
Timeline?.PlayAnimation(IsCollapsed ? 1 : 8);
|
||||
}
|
||||
}
|
||||
|
||||
public float VerticalPadding { get; set; } = 4.0f;
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => LabelNode.String;
|
||||
set => LabelNode.String = value;
|
||||
}
|
||||
|
||||
private void UpdateCollapsed() {
|
||||
Timeline?.PlayAnimation(IsCollapsed ? 1 : 8);
|
||||
ChildContainer.IsVisible = !IsCollapsed;
|
||||
Height = IsCollapsed ? BackgroundNode.Height : ChildContainer.Height + BackgroundNode.Height;
|
||||
ParentTreeListNode?.RefreshLayout();
|
||||
}
|
||||
|
||||
public void RecalculateLayout() {
|
||||
ChildContainer.Height = 0.0f;
|
||||
|
||||
foreach (var child in children) {
|
||||
if (!child.IsVisible) continue;
|
||||
|
||||
child.Y = ChildContainer.Height;
|
||||
child.Width = ChildContainer.Width;
|
||||
|
||||
ChildContainer.Height += child.Height + VerticalPadding;
|
||||
Height = ChildContainer.Height + BackgroundNode.Height;
|
||||
}
|
||||
|
||||
UpdateCollapsed();
|
||||
}
|
||||
|
||||
public void AddHeader(ReadOnlySeString label) {
|
||||
var newHeaderNode = new TreeListHeaderNode {
|
||||
Size = new Vector2(Width, 24.0f),
|
||||
String = label,
|
||||
};
|
||||
|
||||
AddNode(newHeaderNode);
|
||||
}
|
||||
|
||||
public void AddNode(NodeBase node) {
|
||||
node.Y = ChildContainer.Height;
|
||||
node.Width = ChildContainer.Width;
|
||||
node.NodeId = (uint)children.Count + 2;
|
||||
|
||||
ChildContainer.Height += node.Height + VerticalPadding;
|
||||
Height = ChildContainer.Height + BackgroundNode.Height;
|
||||
|
||||
children.Add(node);
|
||||
node.AttachNode(ChildContainer);
|
||||
UpdateCollapsed();
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
BackgroundNode.Width = Width;
|
||||
CollapseArrowNode.Width = 24.0f;
|
||||
LabelNode.Width = Width - 23.0f;
|
||||
ChildContainer.Width = Width;
|
||||
CollisionNode.Width = Width;
|
||||
|
||||
foreach (var node in children) {
|
||||
node.Width = Width;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateChildrenNodeId() {
|
||||
CollisionNode.NodeId = NodeId * 10000 + 1;
|
||||
BackgroundNode.NodeId = NodeId * 10000 + 2;
|
||||
CollapseArrowNode.NodeId = NodeId * 10000 + 3;
|
||||
LabelNode.NodeId = NodeId * 10000 + 4;
|
||||
ChildContainer.NodeId = NodeId * 10000 + 5;
|
||||
}
|
||||
|
||||
private void BuildTimelines() {
|
||||
AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 119)
|
||||
.AddLabel(1, 1, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(9, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(10, 2, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(19, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(20, 3, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(29, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(30, 7, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(39, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(40, 6, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(49, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(50, 4, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(59, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(60, 8, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(69, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(70, 9, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(79, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(80, 10, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(89, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(90, 14, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(99, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(100, 13, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(109, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.AddLabel(110, 11, AtkTimelineJumpBehavior.Start, 0)
|
||||
.AddLabel(119, 0, AtkTimelineJumpBehavior.PlayOnce, 0)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
CollapseArrowNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 9)
|
||||
.AddFrame(1, alpha: 255)
|
||||
.AddFrame(1, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(1, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 255)
|
||||
.AddFrame(12, alpha: 255)
|
||||
.AddFrame(10, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(12, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(10, partId: 0)
|
||||
.AddFrame(12, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 255)
|
||||
.AddFrame(20, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(20, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(30, 39)
|
||||
.AddFrame(30, alpha: 178)
|
||||
.AddFrame(30, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(50, 50, 50))
|
||||
.AddFrame(30, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(40, 49)
|
||||
.AddFrame(40, alpha: 255)
|
||||
.AddFrame(40, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(40, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, alpha: 255)
|
||||
.AddFrame(52, alpha: 255)
|
||||
.AddFrame(50, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(52, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(50, partId: 0)
|
||||
.AddFrame(52, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(60, 69)
|
||||
.AddFrame(60, alpha: 255)
|
||||
.AddFrame(60, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(60, partId: 1)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(70, 79)
|
||||
.AddFrame(70, alpha: 255)
|
||||
.AddFrame(72, alpha: 255)
|
||||
.AddFrame(70, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(72, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(70, partId: 1)
|
||||
.AddFrame(72, partId: 1)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(80, 89)
|
||||
.AddFrame(80, alpha: 255)
|
||||
.AddFrame(80, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(80, partId: 0)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(90, 99)
|
||||
.AddFrame(90, alpha: 178)
|
||||
.AddFrame(90, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(50, 50, 50))
|
||||
.AddFrame(90, partId: 1)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(100, 109)
|
||||
.AddFrame(100, alpha: 255)
|
||||
.AddFrame(100, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(100, partId: 1)
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(110, 119)
|
||||
.AddFrame(110, alpha: 255)
|
||||
.AddFrame(112, alpha: 255)
|
||||
.AddFrame(110, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(112, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(110, partId: 1)
|
||||
.AddFrame(112, partId: 1)
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
LabelNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 9)
|
||||
.AddFrame(1, alpha: 229)
|
||||
.AddFrame(1, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 229)
|
||||
.AddFrame(10, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 229)
|
||||
.AddFrame(20, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(30, 39)
|
||||
.AddFrame(30, alpha: 153)
|
||||
.AddFrame(30, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(80, 80, 80))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(40, 49)
|
||||
.AddFrame(40, alpha: 229)
|
||||
.AddFrame(40, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, alpha: 229)
|
||||
.AddFrame(50, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(60, 69)
|
||||
.AddFrame(60, alpha: 229)
|
||||
.AddFrame(60, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(70, 79)
|
||||
.AddFrame(70, alpha: 229)
|
||||
.AddFrame(70, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(80, 89)
|
||||
.AddFrame(80, alpha: 229)
|
||||
.AddFrame(80, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(90, 99)
|
||||
.AddFrame(90, alpha: 153)
|
||||
.AddFrame(90, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(80, 80, 80))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(100, 109)
|
||||
.AddFrame(100, alpha: 229)
|
||||
.AddFrame(100, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(110, 119)
|
||||
.AddFrame(110, alpha: 229)
|
||||
.AddFrame(110, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
|
||||
BackgroundNode.AddTimeline(new TimelineBuilder()
|
||||
.BeginFrameSet(1, 9)
|
||||
.AddFrame(1, alpha: 255)
|
||||
.AddFrame(1, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(10, 19)
|
||||
.AddFrame(10, alpha: 255)
|
||||
.AddFrame(12, alpha: 255)
|
||||
.AddFrame(10, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(12, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(20, 29)
|
||||
.AddFrame(20, alpha: 255)
|
||||
.AddFrame(20, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(30, 39)
|
||||
.AddFrame(30, alpha: 178)
|
||||
.AddFrame(30, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(50, 50, 50))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(40, 49)
|
||||
.AddFrame(40, alpha: 255)
|
||||
.AddFrame(40, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(50, 59)
|
||||
.AddFrame(50, alpha: 255)
|
||||
.AddFrame(52, alpha: 255)
|
||||
.AddFrame(50, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(52, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(60, 69)
|
||||
.AddFrame(60, alpha: 255)
|
||||
.AddFrame(60, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(70, 79)
|
||||
.AddFrame(70, alpha: 255)
|
||||
.AddFrame(72, alpha: 255)
|
||||
.AddFrame(70, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(72, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(80, 89)
|
||||
.AddFrame(80, alpha: 255)
|
||||
.AddFrame(80, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(90, 99)
|
||||
.AddFrame(90, alpha: 178)
|
||||
.AddFrame(90, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(50, 50, 50))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(100, 109)
|
||||
.AddFrame(100, alpha: 255)
|
||||
.AddFrame(100, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.BeginFrameSet(110, 119)
|
||||
.AddFrame(110, alpha: 255)
|
||||
.AddFrame(112, alpha: 255)
|
||||
.AddFrame(110, addColor: new Vector3(16, 16, 16), multiplyColor: new Vector3(100, 100, 100))
|
||||
.AddFrame(112, addColor: new Vector3(0, 0, 0), multiplyColor: new Vector3(100, 100, 100))
|
||||
.EndFrameSet()
|
||||
.Build()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using Lumina.Text.ReadOnly;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class TreeListHeaderNode : ResNode {
|
||||
|
||||
public readonly NineGridNode DecorationNode;
|
||||
public readonly TextNode LabelNode;
|
||||
|
||||
public TreeListHeaderNode() {
|
||||
DecorationNode = new SimpleNineGridNode {
|
||||
TexturePath = "ui/uld/journal_Separator.tex",
|
||||
TextureCoordinates = new Vector2(0.0f, 0.0f),
|
||||
TextureSize = new Vector2(424.0f, 24.0f),
|
||||
Size = new Vector2(24.0f, 24.0f),
|
||||
LeftOffset = 25.0f,
|
||||
RightOffset = 20.0f,
|
||||
};
|
||||
DecorationNode.AttachNode(this);
|
||||
|
||||
LabelNode = new TextNode {
|
||||
Position = new Vector2(22.0f, 1.0f),
|
||||
TextColor = ColorHelper.GetColor(7),
|
||||
AlignmentType = AlignmentType.Left,
|
||||
FontSize = 12,
|
||||
FontType = FontType.Axis,
|
||||
};
|
||||
LabelNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public ReadOnlySeString String {
|
||||
get => LabelNode.String;
|
||||
set => LabelNode.String = value;
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged() {
|
||||
base.OnSizeChanged();
|
||||
|
||||
DecorationNode.Size = Size;
|
||||
LabelNode.Size = new Vector2(Width - 22.0f, Height);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public sealed unsafe class VerticalLineNode : HorizontalLineNode {
|
||||
public VerticalLineNode() {
|
||||
RotationDegrees = 90.0f;
|
||||
}
|
||||
|
||||
public override float Height {
|
||||
get => ResNode->GetWidth();
|
||||
set => ResNode->SetWidth((ushort) value);
|
||||
}
|
||||
|
||||
public override float Width {
|
||||
get => ResNode->GetHeight();
|
||||
set => ResNode->SetHeight((ushort) value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Numerics;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace KamiToolKit.Nodes;
|
||||
|
||||
public class WindowBackgroundNode : NineGridNode {
|
||||
public WindowBackgroundNode(bool selectedPath, string path = "ui/uld/WindowA_Bg") {
|
||||
var basePath = $"{path}{(selectedPath ? "Selected" : "Normal")}";
|
||||
|
||||
PartsList.Add(
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 0.0f), Size = new Vector2(16.0f, 64.0f), Id = 0, TexturePath = $"{basePath}_Corner.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 0.0f), Size = new Vector2(32.0f, 64.0f), Id = 1, TexturePath = $"{basePath}_H.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(16.0f, 0.0f), Size = new Vector2(16.0f, 64.0f), Id = 2, TexturePath = $"{basePath}_Corner.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 0.0f), Size = new Vector2(16.0f, 32.0f), Id = 3, TexturePath = $"{basePath}_V.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 0.0f), Size = new Vector2(32.0f, 32.0f), Id = 4, TexturePath = $"{basePath}_HV.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(16.0f, 0.0f), Size = new Vector2(16.0f, 32.0f), Id = 5, TexturePath = $"{basePath}_V.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 64.0f), Size = new Vector2(16.0f, 32.0f), Id = 6, TexturePath = $"{basePath}_Corner.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(0.0f, 64.0f), Size = new Vector2(32.0f, 32.0f), Id = 7, TexturePath = $"{basePath}_H.tex" },
|
||||
new Part { TextureCoordinates = new Vector2(16.0f, 64.0f), Size = new Vector2(16.0f, 32.0f), Id = 8, TexturePath = $"{basePath}_Corner.tex" }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user