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

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-08 14:46:31 -05:00
commit 8db4ce6094
375 changed files with 34124 additions and 0 deletions
@@ -0,0 +1,93 @@
using System;
using System.Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace KamiToolKit.Premade.Color;
public class ColorEditNode : SimpleOverlayNode {
private readonly ColorPreviewNode previewNode;
private readonly TextNode labelNode;
private ColorPickerAddon? colorPicker = new() {
InternalName = "ColorPicker",
Title = "Color Picker",
};
public ColorEditNode() {
DisableCollisionNode = true;
previewNode = new ColorPreviewNode();
previewNode.AttachNode(this);
labelNode = new TextNode {
AlignmentType = AlignmentType.Left,
};
labelNode.AttachNode(this);
previewNode.CollisionNode.ShowClickableCursor = true;
previewNode.CollisionNode.AddEvent(AtkEventType.MouseClick, OnClicked);
labelNode.ShowClickableCursor = true;
labelNode.AddEvent(AtkEventType.MouseClick, OnClicked);
}
protected override void Dispose(bool disposing, bool isNativeDestructor) {
base.Dispose(disposing, isNativeDestructor);
colorPicker?.Dispose();
colorPicker = null;
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
previewNode.Size = new Vector2(Height - 6.0f, Height - 6.0f);
previewNode.Position = new Vector2(3.0f, 3.0f);
labelNode.Size = new Vector2(Width - Height - 12.0f, Height);
labelNode.Position = new Vector2(previewNode.Bounds.Right + 12.0f, 0.0f);
}
private void OnClicked() {
var originalColor = CurrentColor;
colorPicker?.DefaultColor = DefaultColor;
colorPicker?.InitialColor = CurrentColor;
colorPicker?.OnColorPreviewed = color => {
previewNode.Color = color;
CurrentColor = color;
OnColorPreviewed?.Invoke(color);
};
colorPicker?.OnColorCancelled = () => {
CurrentColor = originalColor;
OnColorCancelled?.Invoke();
};
colorPicker?.OnColorConfirmed = color => {
CurrentColor = color;
OnColorConfirmed?.Invoke(color);
};
colorPicker?.Toggle();
}
public Vector4 CurrentColor {
get => previewNode.Color;
set => previewNode.Color = value;
}
public ReadOnlySeString String {
get => labelNode.String;
set => labelNode.String = value;
}
public Vector4? DefaultColor { get; set; }
public Action? OnColorCancelled { get; set; }
public Action<Vector4>? OnColorPreviewed { get; set; }
public Action<Vector4>? OnColorConfirmed { get; set; }
}
@@ -0,0 +1,135 @@
using System;
using System.Numerics;
using Dalamud.Interface;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Nodes;
namespace KamiToolKit.Premade.Color;
public class ColorPickerAddon : NativeAddon {
private ColorPickerWidget? colorPicker;
private HorizontalLineNode? horizontalLine;
private TextButtonNode? confirmButton;
private ColorOptionTextButtonNode? defaultColorPreview;
private TextButtonNode? cancelButton;
private bool isCancelClicked;
private Vector4 initialRgba;
private ColorHelpers.HsvaColor initialHsva;
protected override unsafe void OnSetup(AtkUnitBase* addon) {
SetWindowSize(new Vector2(400.0f, 425.0f));
initialHsva = InitialHsvaColor;
initialRgba = ColorHelpers.HsvToRgb(initialHsva);
colorPicker = new ColorPickerWidget {
Position = ContentStartPosition,
Size = ContentSize,
};
colorPicker.AttachNode(this);
colorPicker.ColorPreviewed += hsva => OnHsvaColorPreviewed?.Invoke(hsva);
colorPicker.RgbaColorPreviewed += rgba => OnColorPreviewed?.Invoke(rgba);
colorPicker.SetColor(initialRgba);
horizontalLine = new HorizontalLineNode {
Position = ContentStartPosition + new Vector2(2.0f, ContentSize.Y - 40.0f),
Size = new Vector2(ContentSize.X - 4.0f, 2.0f),
};
horizontalLine.AttachNode(this);
confirmButton = new TextButtonNode {
Position = ContentStartPosition + new Vector2(0.0f, ContentSize.Y - 24.0f),
Size = new Vector2(100.0f, 24.0f),
String = "Confirm",
OnClick = OnConfirmClicked,
};
confirmButton.AttachNode(this);
if (DefaultHsvaColor is { } defaultColor) {
defaultColorPreview = new ColorOptionTextButtonNode {
Size = new Vector2(100.0f, 24.0f),
Position = ContentStartPosition + new Vector2(ContentSize.X / 2.0f - 50.0f, ContentSize.Y - 24.0f),
String = "Default",
OnClick = OnDefaultClicked,
DefaultHsvaColor = defaultColor,
};
defaultColorPreview.AttachNode(this);
}
cancelButton = new TextButtonNode {
Position = ContentStartPosition + new Vector2(ContentSize.X - 100.0f, ContentSize.Y - 24.0f),
Size = new Vector2(100.0f, 24.0f),
String = "Cancel",
OnClick = OnCancelClicked,
};
cancelButton.AttachNode(this);
}
protected override unsafe void OnHide(AtkUnitBase* addon) {
if (!isCancelClicked) {
OnHsvaColorPreviewed?.Invoke(initialHsva);
OnColorPreviewed?.Invoke(initialRgba);
OnColorCancelled?.Invoke();
}
}
private void OnConfirmClicked() {
if (colorPicker is null) return;
var rgba = ColorHelpers.HsvToRgb(colorPicker.CurrentColor);
OnColorConfirmed?.Invoke(rgba);
OnHsvaColorConfirmed?.Invoke(colorPicker.CurrentColor);
isCancelClicked = true;
Close();
}
private void OnDefaultClicked() {
if (colorPicker is null) return;
if (DefaultHsvaColor is { } defaultColor) {
colorPicker.SetColor(defaultColor);
}
}
private void OnCancelClicked() {
isCancelClicked = true;
OnHsvaColorPreviewed?.Invoke(initialHsva);
OnColorPreviewed?.Invoke(initialRgba);
OnColorCancelled?.Invoke();
Close();
}
public Action<Vector4>? OnColorPreviewed { get; set; }
public Action<ColorHelpers.HsvaColor>? OnHsvaColorPreviewed { get; set; }
public Action<Vector4>? OnColorConfirmed { get; set; }
public Action<ColorHelpers.HsvaColor>? OnHsvaColorConfirmed { get; set; }
public Action? OnColorCancelled { get; set; }
public ColorHelpers.HsvaColor? DefaultHsvaColor { get; set; }
public Vector4? DefaultColor {
get;
set {
field = value;
DefaultHsvaColor = value is null ? null : ColorHelpers.RgbaToHsv(value.Value);
}
}
public Vector4 InitialColor {
get => ColorHelpers.HsvToRgb(InitialHsvaColor);
set => InitialHsvaColor = ColorHelpers.RgbaToHsv(value);
}
public ColorHelpers.HsvaColor InitialHsvaColor { get; set; } =
ColorHelpers.RgbaToHsv(new Vector4(1.0f, 0.0f, 0.0f, 1.0f));
}
@@ -0,0 +1,173 @@
using System;
using System.Numerics;
using Dalamud.Interface;
using KamiToolKit.Classes;
using KamiToolKit.Nodes;
using KamiToolKit.Premade.Nodes;
namespace KamiToolKit.Premade.Color;
public class ColorPickerWidget : SimpleComponentNode {
public readonly ColorRingWithSquareNode ColorPickerNode;
public readonly AlphaBarNode AlphaBarNode;
public readonly ColorPreviewWithInput ColorPreviewWithInput;
public ColorHelpers.HsvaColor CurrentColor { get; private set; }
public Action<ColorHelpers.HsvaColor>? ColorPreviewed;
public Action<Vector4>? RgbaColorPreviewed;
private int batchDepth;
private bool previewDirty;
public ColorPickerWidget() {
ColorPickerNode = new ColorRingWithSquareNode {
OnHueChanged = SetHue,
OnSaturationChanged = SetSaturation,
OnValueChanged = SetValue,
};
ColorPickerNode.AttachNode(this);
AlphaBarNode = new AlphaBarNode {
OnAlphaChanged = SetAlpha,
};
AlphaBarNode.AttachNode(this);
ColorPreviewWithInput = new ColorPreviewWithInput {
OnHsvaColorChanged = newColor => {
using (BeginBatchUpdate()) {
SetHue(newColor.H);
SetSaturation(newColor.S);
SetValue(newColor.V);
SetAlpha(newColor.A);
}
},
};
ColorPreviewWithInput.AttachNode(this);
CurrentColor = ColorHelpers.RgbaToHsv(new Vector4(1.0f, 0.0f, 0.0f, 1.0f));
using (BeginBatchUpdate()) {
SetHue(CurrentColor.H);
SetSaturation(CurrentColor.S);
SetValue(CurrentColor.V);
SetAlpha(CurrentColor.A);
}
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
var mainWidgetWidth = Width * 3.0f / 4.0f;
ColorPickerNode.Size = new Vector2(mainWidgetWidth, mainWidgetWidth);
AlphaBarNode.Size = new Vector2(Width / 16.0f, mainWidgetWidth - 60.0f);
AlphaBarNode.Position = new Vector2(mainWidgetWidth + (Width - mainWidgetWidth) / 3.0f - AlphaBarNode.Width / 2.0f, 30.0f);
ColorPreviewWithInput.Size = new Vector2(150.0f, 32.0f);
ColorPreviewWithInput.Position = new Vector2(Width / 2.0f - 75.0f, ColorPickerNode.Y + ColorPickerNode.Height - 1.0f);
}
private IDisposable BeginBatchUpdate() {
batchDepth++;
return new BatchToken(this);
}
internal void EndBatchUpdate() {
batchDepth--;
if (batchDepth <= 0) {
batchDepth = 0;
if (previewDirty) {
previewDirty = false;
RaisePreview();
}
}
}
private void RaisePreviewMaybe() {
if (batchDepth > 0) {
previewDirty = true;
return;
}
RaisePreview();
}
private void RaisePreview() {
var hsva = CurrentColor;
ColorPreviewed?.Invoke(hsva);
RgbaColorPreviewed?.Invoke(ColorHelpers.HsvToRgb(hsva));
}
public void SetAlpha(float alpha) {
CurrentColor = CurrentColor with { A = alpha };
ColorPreviewWithInput.ColorHsva = CurrentColor;
AlphaBarNode.ColorHsva = CurrentColor;
RaisePreviewMaybe();
}
public void SetHue(float hue) {
CurrentColor = CurrentColor with { H = hue };
ColorPickerNode.RotationDegrees = hue * 360.0f;
ColorPickerNode.SelectorColor = CurrentColor;
ColorPickerNode.SquareColor = CurrentColor with { S = 1.0f, V = 1.0f };
ColorPreviewWithInput.ColorHsva = CurrentColor;
AlphaBarNode.ColorHsva = CurrentColor;
RaisePreviewMaybe();
}
public void SetSaturation(float saturation) {
CurrentColor = CurrentColor with { S = saturation };
ColorPreviewWithInput.ColorHsva = CurrentColor;
ColorPickerNode.SelectorColor = CurrentColor;
ColorPickerNode.SquareColor = CurrentColor;
ColorPickerNode.SquareSaturationValue = CurrentColor;
AlphaBarNode.ColorHsva = CurrentColor;
RaisePreviewMaybe();
}
public void SetValue(float value) {
CurrentColor = CurrentColor with { V = value };
ColorPreviewWithInput.ColorHsva = CurrentColor;
ColorPickerNode.SelectorColor = CurrentColor;
ColorPickerNode.SquareColor = CurrentColor;
ColorPickerNode.SquareSaturationValue = CurrentColor;
AlphaBarNode.ColorHsva = CurrentColor;
RaisePreviewMaybe();
}
public void SetColor(Vector4 color) {
var converted = ColorHelpers.RgbaToHsv(color);
using (BeginBatchUpdate()) {
SetHue(converted.H);
SetSaturation(converted.S);
SetValue(converted.V);
SetAlpha(converted.A);
}
}
public void SetColor(ColorHelpers.HsvaColor color) {
using (BeginBatchUpdate()) {
SetHue(color.H);
SetSaturation(color.S);
SetValue(color.V);
SetAlpha(color.A);
}
}
}
@@ -0,0 +1,55 @@
using System.Drawing;
using System.Numerics;
using Dalamud.Interface;
using KamiToolKit.Classes;
using KamiToolKit.Enums;
using KamiToolKit.Nodes;
namespace KamiToolKit.Premade.Color;
public class ColorPreviewNode : SimpleComponentNode {
public readonly BackgroundImageNode SelectedColorPreviewNode;
public readonly ImGuiImageNode AlphaLayerPreviewNode;
public readonly BackgroundImageNode SelectedColorPreviewBorderNode;
public ColorPreviewNode() {
SelectedColorPreviewBorderNode = new BackgroundImageNode {
Color = KnownColor.White.Vector(),
};
SelectedColorPreviewBorderNode.AttachNode(this);
AlphaLayerPreviewNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("alpha_background.png"),
WrapMode = WrapMode.Tile,
};
AlphaLayerPreviewNode.AttachNode(this);
SelectedColorPreviewNode = new BackgroundImageNode {
Color = new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
};
SelectedColorPreviewNode.AttachNode(this);
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
SelectedColorPreviewBorderNode.Size = new Vector2(Height - 4.0f, Width - 4.0f);
SelectedColorPreviewBorderNode.Position = new Vector2(2.0f, 2.0f);
AlphaLayerPreviewNode.Size = new Vector2(Height - 6.0f, Width - 6.0f);
AlphaLayerPreviewNode.Position = new Vector2(3.0f, 3.0f);
SelectedColorPreviewNode.Size = new Vector2(Height - 6.0f, Width - 6.0f);
SelectedColorPreviewNode.Position = new Vector2(3.0f, 3.0f);
}
public override Vector4 Color {
get => SelectedColorPreviewNode.Color;
set => SelectedColorPreviewNode.Color = value;
}
public override ColorHelpers.HsvaColor ColorHsva {
get => SelectedColorPreviewNode.ColorHsva;
set => SelectedColorPreviewNode.ColorHsva = value;
}
}
@@ -0,0 +1,88 @@
using System;
using System.Globalization;
using System.Numerics;
using Dalamud.Interface;
using KamiToolKit.Nodes;
using Lumina.Text.ReadOnly;
namespace KamiToolKit.Premade.Color;
public class ColorPreviewWithInput : SimpleComponentNode {
public readonly ColorPreviewNode ColorPreviewNode;
public readonly TextInputNode ColorInputNode;
public ColorPreviewWithInput() {
ColorPreviewNode = new ColorPreviewNode();
ColorPreviewNode.AttachNode(this);
ColorInputNode = new TextInputNode {
AutoSelectAll = true,
OnInputComplete = OnTextInputComplete,
};
ColorInputNode.AttachNode(this);
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
ColorPreviewNode.Size = new Vector2(Height, Height);
ColorPreviewNode.Position = Vector2.Zero;
ColorInputNode.Size = new Vector2(Width - Height - 8.0f, Height - 2.0f);
ColorInputNode.Position = new Vector2(Height + 8.0f, 1.0f);
}
public Action<ColorHelpers.HsvaColor>? OnHsvaColorChanged { get; set; }
public Action<Vector4>? OnColorChanged { get; set; }
public ReadOnlySeString String {
get => ColorInputNode.String;
set => ColorInputNode.String = value;
}
public override Vector4 Color {
get => ColorPreviewNode.Color;
set {
ColorPreviewNode.Color = value;
UpdateColorText();
}
}
public override ColorHelpers.HsvaColor ColorHsva {
get => ColorPreviewNode.ColorHsva;
set {
ColorPreviewNode.ColorHsva = value;
UpdateColorText();
}
}
private void OnTextInputComplete(ReadOnlySeString obj) {
var str = obj.ToString();
if (string.IsNullOrEmpty(str) || !str.StartsWith('#')) return;
var hexString = str.TrimStart('#');
// Allow #RRGGBB and #RRGGBBAA only
if (hexString.Length != 6 && hexString.Length != 8) return;
const NumberStyles style = NumberStyles.HexNumber;
var culture = CultureInfo.InvariantCulture;
if (!byte.TryParse(hexString[0..2], style, culture, out var r)) return;
if (!byte.TryParse(hexString[2..4], style, culture, out var g)) return;
if (!byte.TryParse(hexString[4..6], style, culture, out var b)) return;
byte a = 255;
if (hexString.Length == 8 && !byte.TryParse(hexString[6..8], style, culture, out a)) return;
var newColor = new Vector4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
Color = newColor;
OnColorChanged?.Invoke(newColor);
OnHsvaColorChanged?.Invoke(ColorHelpers.RgbaToHsv(newColor));
}
private void UpdateColorText()
=> ColorInputNode.String = $"#{(int)(Color.X * 255):X2}{(int)(Color.Y * 255):X2}{(int)(Color.Z * 255):X2}{(int)(Color.W * 255):X2}";
}
@@ -0,0 +1,201 @@
using System;
using System.Numerics;
using Dalamud.Interface;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes;
using KamiToolKit.Enums;
using KamiToolKit.Nodes;
namespace KamiToolKit.Premade.Color;
public unsafe class ColorRingWithSquareNode : SimpleComponentNode {
public readonly ColorSquareNode ColorSquareNode;
public readonly ImGuiImageNode ColorRingNode;
public readonly ImGuiImageNode ColorRingSelectorNode;
private bool isRingDrag;
private bool isSquareDrag;
private readonly ViewportEventListener eventListener;
public ColorRingWithSquareNode() {
eventListener = new ViewportEventListener(SquareEventHandler);
ColorSquareNode = new ColorSquareNode {
DrawFlags = DrawFlags.UseTransformedCollision,
};
ColorSquareNode.AttachNode(this);
ColorRingNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("color_ring.png"),
FitTexture = true,
ImageNodeFlags = ImageNodeFlags.FlipV,
};
ColorRingNode.AttachNode(this);
ColorRingSelectorNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("color_ring_selector.png"),
FitTexture = true,
MultiplyColor = new Vector3(1.0f, 0.0f, 0.0f),
};
ColorRingSelectorNode.AttachNode(this);
AddEvent(AtkEventType.MouseDown, OnMouseDown);
AddEvent(AtkEventType.MouseUp, OnMouseUp);
AddEvent(AtkEventType.MouseMove, OnMouseMove);
AddEvent(AtkEventType.MouseOut, OnMouseOut);
}
protected override void Dispose(bool disposing, bool isNativeDestructor) {
if (disposing) {
base.Dispose(disposing, isNativeDestructor);
eventListener.Dispose();
}
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
ColorSquareNode.Size = Size / 2.0f - new Vector2(24.0f, 24.0f);
ColorSquareNode.Position = Size / 4.0f + new Vector2(12.0f, 12.0f);
ColorSquareNode.RotationDegrees = 45.0f;
ColorRingNode.Size = Size;
ColorRingSelectorNode.Size = Size;
ColorRingSelectorNode.Origin = Size / 2.0f;
}
private bool IsRingClicked(AtkEventData* data) {
var clickPosition = data->MousePosition;
var scale = ParentAddon is not null ? ParentAddon->Scale : 1.0f;
var center = ColorRingNode.ScreenPosition + ColorRingNode.Size * scale / 2.0f;
var distance = Vector2.Distance(clickPosition, center);
var scaledDistance = distance / (Width * scale / 256.0f);
return scaledDistance is >= 82.0f and <= 99.0f;
}
private float GetRingClickAngle(AtkEventData* data) {
var clickPosition = data->MousePosition;
var scale = ParentAddon is not null ? ParentAddon->Scale : 1.0f;
var center = ColorRingNode.ScreenPosition + ColorRingNode.Size * scale / 2.0f;
var relativePosition = clickPosition - center;
var calculatedAngle = MathF.Atan2(relativePosition.Y, relativePosition.X) * 180.0f / MathF.PI;
return calculatedAngle;
}
private void OnMouseDown(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
if (ColorSquareNode.CheckCollision(atkEventData)) {
UpdateSquareColor(atkEventData->MousePosition);
if (!isSquareDrag) {
isSquareDrag = true;
eventListener.AddEvent(AtkEventType.MouseMove, ColorSquareNode);
eventListener.AddEvent(AtkEventType.MouseUp, ColorSquareNode);
}
}
if (IsRingClicked(atkEventData)) {
isRingDrag = true;
UpdateRingColor(atkEventData);
}
}
private void OnMouseMove(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
if (isRingDrag && !isSquareDrag) {
UpdateRingColor(atkEventData);
}
}
private void OnMouseUp() {
isRingDrag = false;
isSquareDrag = false;
}
private void OnMouseOut() {
isRingDrag = false;
isSquareDrag = false;
}
private void UpdateRingColor(AtkEventData* data) {
var angle = GetRingClickAngle(data);
if (angle < 0) {
angle += 360.0f;
}
OnHueChanged?.Invoke(angle / 360.0f);
}
private void UpdateSquareColor(Vector2 clickPosition) {
// Note: ColorSquareNode.ScreenPosition changes as the node rotates
// However, Position does not change
var scale = ParentAddon is not null ? ParentAddon->Scale : 1.0f;
var center = ScreenPosition + (ColorSquareNode.Position + ColorSquareNode.Origin) * scale;
var relativePosition = clickPosition - center;
var rotatedPoint = RotatePoint(relativePosition / scale, Vector2.Zero, -ColorSquareNode.RotationDegrees) / ColorSquareNode.Scale;
var xClamped = Math.Clamp(rotatedPoint.X, -ColorSquareNode.Width / 2, ColorSquareNode.Width / 2);
var yClamped = Math.Clamp(rotatedPoint.Y, -ColorSquareNode.Height / 2, ColorSquareNode.Height / 2);
ColorSquareNode.ColorDotPosition = new Vector2(xClamped, yClamped) + ColorSquareNode.Origin;
var saturation = ColorSquareNode.ColorDotPosition.X / ColorSquareNode.Width;
var lightness = 1 - ColorSquareNode.ColorDotPosition.Y / ColorSquareNode.Height;
OnSaturationChanged?.Invoke(saturation);
OnValueChanged?.Invoke(lightness);
}
private void SquareEventHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
if (eventType is AtkEventType.MouseMove && isSquareDrag && !isRingDrag) {
UpdateSquareColor(new Vector2(atkEventData->MouseData.PosX, atkEventData->MouseData.PosY));
}
if (eventType is AtkEventType.MouseUp) {
isSquareDrag = false;
eventListener.RemoveEvent(AtkEventType.MouseMove);
eventListener.RemoveEvent(AtkEventType.MouseUp);
}
}
private static Vector2 RotatePoint(Vector2 pointToRotate, Vector2 centerPoint, float angleInDegrees) {
var angleInRadians = angleInDegrees * (MathF.PI / 180);
var cosTheta = MathF.Cos(angleInRadians);
var sinTheta = MathF.Sin(angleInRadians);
return new Vector2 {
X = cosTheta * (pointToRotate.X - centerPoint.X) - sinTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.X,
Y = sinTheta * (pointToRotate.X - centerPoint.X) + cosTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.Y,
};
}
public Action<float>? OnHueChanged { get; init; }
public Action<float>? OnSaturationChanged { get; init; }
public Action<float>? OnValueChanged { get; init; }
public override float RotationDegrees {
get => ColorSquareNode.RotationDegrees;
set {
ColorSquareNode.RotationDegrees = value + 45.0f;
ColorRingSelectorNode.RotationDegrees = value;
}
}
public ColorHelpers.HsvaColor SelectorColor {
get => ColorRingSelectorNode.MultiplyColorHsva;
set => ColorRingSelectorNode.MultiplyColorHsva = value;
}
public ColorHelpers.HsvaColor SquareColor {
get => ColorSquareNode.MultiplyColorHsva;
set => ColorSquareNode.MultiplyColorHsva = value with { S = 1.0f, V = 1.0f };
}
public ColorHelpers.HsvaColor SquareSaturationValue {
get => ColorSquareNode.MultiplyColorHsva;
set => ColorSquareNode.ColorDotPosition = new Vector2(ColorSquareNode.Width * value.S, ColorSquareNode.Height - ColorSquareNode.Height * value.V);
}
}
@@ -0,0 +1,65 @@
using System.Numerics;
using Dalamud.Interface;
using FFXIVClientStructs.FFXIV.Component.GUI;
using KamiToolKit.Classes;
using KamiToolKit.Nodes;
namespace KamiToolKit.Premade.Color;
public class ColorSquareNode : SimpleComponentNode {
public readonly ImGuiImageNode WhiteGradientNode;
public readonly ImGuiImageNode ColorGradientNode;
public readonly ImGuiImageNode BlackGradientNode;
public readonly ImGuiImageNode ColorDotNode;
public ColorSquareNode() {
WhiteGradientNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("HorizontalGradient_WhiteToAlpha.png"),
FitTexture = true,
};
WhiteGradientNode.AttachNode(this);
ColorGradientNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("HorizontalGradient_WhiteToAlpha.png"),
FitTexture = true,
ImageNodeFlags = ImageNodeFlags.FlipH,
};
ColorGradientNode.AttachNode(this);
BlackGradientNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("VerticalGradient_AlphaToBlack.png"),
FitTexture = true,
};
BlackGradientNode.AttachNode(this);
ColorDotNode = new ImGuiImageNode {
TexturePath = DalamudInterface.Instance.GetAssetPath("color_select_dot.png"),
FitTexture = true,
};
ColorDotNode.AttachNode(this);
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
WhiteGradientNode.Size = Size;
ColorGradientNode.Size = Size;
BlackGradientNode.Size = Size;
Origin = Size / 2.0f;
ColorDotNode.Size = new Vector2(16.0f, 16.0f);
ColorDotNode.Origin = ColorDotNode.Size / 2.0f;
ColorDotNode.Position = new Vector2(Width, 0.0f) - ColorDotNode.Origin;
}
public override ColorHelpers.HsvaColor MultiplyColorHsva {
get => ColorGradientNode.MultiplyColorHsva;
set => ColorGradientNode.MultiplyColorHsva = value;
}
public Vector2 ColorDotPosition {
get => ColorDotNode.Position + ColorDotNode.Origin;
set => ColorDotNode.Position = value - ColorDotNode.Origin;
}
}