basically function but needs a lot of refinement

This commit is contained in:
2024-04-23 03:24:23 -05:00
parent 3ac7bf33c4
commit d3beca8014
26 changed files with 985 additions and 345 deletions

31
CardEditImageBox.cs Normal file
View File

@@ -0,0 +1,31 @@
using Godot;
using System;
public partial class CardEditImageBox : TextureRect
{
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public override void _GuiInput(InputEvent @event)
{
if (@event is InputEventMouseButton iemb)
{
if (iemb.Pressed && iemb.ButtonIndex == MouseButton.Left)
{
// bool inControl = _HasPoint(iemb.Position);
// GD.Print(inControl);
// if (iemb.Position.X >= 0 && iemb.Position.X <= Size.X
// && iemb.Position.Y >= 0 && iemb.Position.Y <= Size.Y)
// if (inControl)
GetNode<FileDialog>("%CardImagePicker").Show();
}
}
}
}

16
CardImagePicker.cs Normal file
View File

@@ -0,0 +1,16 @@
using Godot;
using System;
public partial class CardImagePicker : FileDialog
{
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
// FileSelected += GetNode<card_edit_popup>("/game/CardEditPopup").FileSelected;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

View File

@@ -105,42 +105,19 @@ public static class CommandTypeHelper
// return (CommandType)ct; // return (CommandType)ct;
// return null; // return null;
var c = commandType.ToLower(); var c = commandType.ToLower();
if (c == "movecard") return c switch
{ {
return CommandType.MoveCard; "movecard" => CommandType.MoveCard,
} "moverow" => CommandType.MoveRow,
else if (c == "moverow") "deletecard" or "deletecards" => CommandType.DeleteCard,
{ "deleterow" => CommandType.DeleteRow,
return CommandType.MoveRow; "createcard" or "addcard" => CommandType.CreateCard,
} "createrow" or "addrow" => CommandType.CreateRow,
else if (c == "deletecard") "renamecard" => CommandType.RenameCard,
{ "renamerow" => CommandType.RenameRow,
return CommandType.DeleteCard; "recolorrow" or "changerowcolor" => CommandType.RecolorRow,
} "changecardimage" or "changecardpicture" => CommandType.ChangeCardImage,
else if (c == "createcard") _ => null,
{ };
return CommandType.CreateCard;
}
else if (c == "createrow")
{
return CommandType.CreateRow;
}
else if (c == "renamecard")
{
return CommandType.RenameCard;
}
else if (c == "renamerow")
{
return CommandType.RenameRow;
}
else if (c == "recolorrow")
{
return CommandType.RecolorRow;
}
else if (c == "changecardimage")
{
return CommandType.ChangeCardImage;
}
return null;
} }
} }

View File

@@ -1,18 +1,20 @@
using Godot; using Godot;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
public partial class CommandHandler : Node public partial class CommandHandler : Node
{ {
private Settings Settings { get; set; } private Settings Settings { get; set; }
private game Game { get; set; } private game Game { get; set; }
private readonly List<Task> TaskQueue = new(); private readonly ConcurrentQueue<Action> ActionQueue = new();
private System.Net.Http.HttpClient _Client = null; private System.Net.Http.HttpClient _Client = null;
private System.Net.Http.HttpClient Client private System.Net.Http.HttpClient Client
{ get { get
@@ -21,34 +23,42 @@ public partial class CommandHandler : Node
return _Client; return _Client;
} }
} }
private defer_manager _Deferer = null;
private defer_manager Deferer
{ get
{
_Deferer ??= GetNode<defer_manager>("/root/DeferManager");
return _Deferer;
}
}
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
Settings = GetNode<Settings>("/root/Settings"); Settings = GetNode<Settings>("/root/Settings");
Game = GetNode<game>("/root/Game"); Game = GetNode<game>("/root/Game");
GetNode<TwitchChatWatcher>("/root/TwitchChatWatcher").IncomingCommand
+= IncomingCommand;
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
//delete/cleanup one finished task per frame while (ActionQueue.TryDequeue(out Action t))
//currently just logs errors
var t = TaskQueue.FirstOrDefault(t => t.IsCompleted, null);
if (t is not null)
{ {
if (t.Exception is not null) t?.Invoke();
GD.PrintErr(t.Exception);
TaskQueue.Remove(t);
} }
} }
private void IncomingCommand(Command command) private void IncomingCommand(Command command)
{ {
GD.Print("Received command");
if (!Settings.IsUserAuthorized(command.User, command.IsStreamer, if (!Settings.IsUserAuthorized(command.User, command.IsStreamer,
command.IsModerator)) command.IsModerator))
return; return;
GD.Print($"User {command.User} is authorized");
var baseArgs = command.GetArgs(); var baseArgs = command.GetArgs();
var type = CommandTypeHelper.ParseCommand(baseArgs.Pop()); var type = CommandTypeHelper.ParseCommand(baseArgs.Pop());
GD.Print($"Command type: {type}");
var args = baseArgs.DuplicateAtState(); var args = baseArgs.DuplicateAtState();
switch (type) switch (type)
{ {
@@ -65,7 +75,9 @@ public partial class CommandHandler : Node
DeleteRow(args); DeleteRow(args);
break; break;
case CommandType.CreateCard: case CommandType.CreateCard:
TaskQueue.Add(Task.Run(() => CreateCard(args))); Task.Run(async () => await CreateCard(args))
.ContinueWith(t => GD.PrintErr(t.Exception.StackTrace),
TaskContinuationOptions.OnlyOnFaulted);
break; break;
case CommandType.CreateRow: case CommandType.CreateRow:
CreateRow(args); CreateRow(args);
@@ -80,10 +92,12 @@ public partial class CommandHandler : Node
RecolorRow(args); RecolorRow(args);
break; break;
case CommandType.ChangeCardImage: case CommandType.ChangeCardImage:
TaskQueue.Add(Task.Run(() => ChangeCardImage(args))); Task.Run(async () => await ChangeCardImage(args))
.ContinueWith(t => GD.PushError(t.Exception.InnerException.StackTrace),
TaskContinuationOptions.OnlyOnFaulted);
break; break;
default: default:
throw new Exception(); throw new Exception("invalid command type");
} }
} }
@@ -120,10 +134,14 @@ public partial class CommandHandler : Node
} }
private async Task CreateCard(CommandArguments args) private async Task CreateCard(CommandArguments args)
{ {
var title = args.Pop();
var url = args.Pop(); var url = args.Pop();
Image img = await ImageFromUrl(url); var title = args.Remaining();
Game.CreateCard(title, img); ImageWithMetadata img;
if (!string.IsNullOrWhiteSpace(url) && url != "_")
img = await ImageFromUrl(url);
else
img = null;
await Deferer.DeferAsync(() => Game.CreateCard(title, img));
} }
private void CreateRow(CommandArguments args) private void CreateRow(CommandArguments args)
{ {
@@ -152,7 +170,8 @@ public partial class CommandHandler : Node
var rowId = args.Pop(); var rowId = args.Pop();
var colorStr = args.Pop(); var colorStr = args.Pop();
var color = Color.FromString(colorStr, new Color(0, 0, 0, 0)); var color = Color.FromString(colorStr, new Color(0, 0, 0, 0));
if (color == new Color(0, 0, 0, 0)) GD.Print($"Recoloring row to {color}");
if (color.IsEqualApprox(new Color(0, 0, 0, 0)))
throw new Exception($"invalid color {colorStr}"); throw new Exception($"invalid color {colorStr}");
Game.RecolorRow(rowId, color); Game.RecolorRow(rowId, color);
} }
@@ -160,18 +179,38 @@ public partial class CommandHandler : Node
{ {
var cardId = args.Pop(); var cardId = args.Pop();
var img = await ImageFromUrl(args.Pop()); var img = await ImageFromUrl(args.Pop());
Game.ChangeCardImage(cardId, img); await Deferer.DeferAsync(() => Game.ChangeCardImage(cardId, img));
// Game.ChangeCardImage(cardId, img);
} }
private async Task<Image> ImageFromUrl(string url) private async Task<ImageWithMetadata> ImageFromUrl(string url)
{ {
StretchMode mode = StretchMode.Unspecified;
if (url.Contains('|'))
{
var spl = url.Split('|', 2);
url = spl[0];
mode = spl[1].ToLower() switch
{
"unspecified" => StretchMode.Unspecified,
"fit" => StretchMode.Fit,
"stretch" => StretchMode.Stretch,
"crop" => StretchMode.Crop,
_ => throw new Exception($"Unrecognized {nameof(StretchMode)}"),
};
}
GD.Print($"Stretch mode: {mode}");
var uri = new Uri(url); var uri = new Uri(url);
GD.Print("Starting image download");
var resp = await Client.GetAsync(uri); var resp = await Client.GetAsync(uri);
if (!resp.IsSuccessStatusCode) if (!resp.IsSuccessStatusCode)
return null; throw new Exception("Failed to download image");
GD.Print("Downloaded image successfully");
Image img = new(); Image img = new();
var arr = await resp.Content.ReadAsByteArrayAsync(); var arr = await resp.Content.ReadAsByteArrayAsync();
var ext = Path.GetExtension(uri.AbsolutePath).TrimStart('.').ToLower();
GD.Print($"Image extension: {ext}");
//TODO detect images by header rather than extension //TODO detect images by header rather than extension
switch (Path.GetExtension(uri.AbsolutePath).ToLower()) switch (ext)
{ {
case "png": case "png":
img.LoadPngFromBuffer(arr); img.LoadPngFromBuffer(arr);
@@ -189,7 +228,8 @@ public partial class CommandHandler : Node
default: default:
throw new Exception("unrecognized filetype"); throw new Exception("unrecognized filetype");
} }
return img; GD.Print($"Loaded picture {img}");
return new ImageWithMetadata(img, mode);
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {

View File

@@ -1,44 +0,0 @@
// using System.Collections.Generic;
// public static class CommandParser
// {
// public static List<string> SplitString(string s)
// {
// List<string> l = new();
// bool quoted = false;
// bool escaped = false;
// bool trimming = true;
// string current = "";
// foreach (char c in s)
// {
// switch (c)
// {
// case '\\':
// if (escaped)
// current += '\\';
// escaped = !escaped;
// break;
// case '"':
// if (escaped)
// {
// current += '"';
// escaped = false;
// }
// else
// {
// if (quoted)
// {
// quoted = false;
// }
// else
// {
// }
// }
// break;
// case
// }
// }
// }
// }

54
ConfigStretchContainer.cs Normal file
View File

@@ -0,0 +1,54 @@
using Godot;
using System;
public partial class ConfigStretchContainer : VBoxContainer
{
private Settings _Settings;
private Settings Settings
{ get
{
_Settings ??= GetNode<Settings>("/root/Settings");
return _Settings;
}
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public void LoadButtonState()
{
switch (Settings.StretchMode)
{
case StretchMode.Fit:
GetNode<BaseButton>("%ConfigStretchFitButton").ButtonPressed = true;
break;
case StretchMode.Crop:
GetNode<BaseButton>("%ConfigStretchCropButton").ButtonPressed = true;
break;
case StretchMode.Stretch:
GetNode<BaseButton>("%ConfigStretchStretchButton").ButtonPressed = true;
break;
default:
throw new Exception($"Unrecognized {nameof(StretchMode)} {(int)Settings.StretchMode}");
}
}
public StretchMode GetStretchMode()
{
if (GetNode<BaseButton>("%ConfigStretchFitButton").ButtonPressed)
return StretchMode.Fit;
else if (GetNode<BaseButton>("%ConfigStretchCropButton").ButtonPressed)
return StretchMode.Crop;
else if (GetNode<BaseButton>("%ConfigStretchStretchButton").ButtonPressed)
return StretchMode.Stretch;
throw new Exception($"No {nameof(StretchMode)} buttons pressed");
}
public void OkClicked()
{
Settings.StretchMode = GetStretchMode();
}
}

39
ImageWithMetadata.cs Normal file
View File

@@ -0,0 +1,39 @@
using System.Data.Common;
using Godot;
public record class ImageWithMetadata
{
public Image Image;
public StretchMode StretchMode;
public ImageWithMetadata(Image image)
{
Image = image;
StretchMode = StretchMode.Unspecified;
}
public ImageWithMetadata(Image image, StretchMode stretchMode)
: this(image)
{
StretchMode = stretchMode;
}
// public static implicit operator Image(ImageWithMetadata iwm)
// => iwm.Image;
// public static implicit operator ImageWithMetadata(Image image)
// => new(image);
}
public enum StretchMode
{
Unspecified,
/// <summary>
/// Fit to either the width or height of the card
/// </summary>
Fit,
/// <summary>
/// Stretch the image to fill the card
/// </summary>
Stretch,
/// <summary>
/// Crop image to fit card (maintain center)
/// </summary>
Crop,
}

View File

@@ -3,8 +3,16 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
public partial class PictureDropHandler : Control public partial class PictureDropHandler : Node
{ {
private Settings _Settings;
private Settings Settings
{ get
{
_Settings ??= GetNode<Settings>("/root/Settings");
return _Settings;
}
}
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
@@ -28,6 +36,7 @@ public partial class PictureDropHandler : Control
continue; continue;
var c = card.MakeCard(GetTree()); var c = card.MakeCard(GetTree());
c.SetImage(tex); c.SetImage(tex);
c.SetStretchMode(Settings.StretchMode);
g.AddUnassignedCard(c); g.AddUnassignedCard(c);
} }
} }

View File

@@ -1,13 +1,20 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Godot; using Godot;
public partial class Settings : Node public partial class Settings : Node
{ {
[Export]
public bool AllowStreamer { get; set; } public bool AllowStreamer { get; set; }
[Export]
public bool AllowModerators { get; set; } public bool AllowModerators { get; set; }
[Export]
public string Command { get; set; }
public List<string> UserWhitelist { get; } = new(); public List<string> UserWhitelist { get; } = new();
public List<string> UserBlacklist { get; } = new(); public List<string> UserBlacklist { get; } = new();
public Vector2 CardSize { get; private set; } public Vector2 CardSize { get; private set; }
[Export]
public StretchMode StretchMode { get; set; } = StretchMode.Fit;
[Signal] [Signal]
public delegate void ChangeCardSizeEventHandler(Vector2 size); public delegate void ChangeCardSizeEventHandler(Vector2 size);
@@ -19,6 +26,7 @@ public partial class Settings : Node
} }
public bool IsUserAuthorized(string user, bool isStreamer = false, bool isModerator = false) public bool IsUserAuthorized(string user, bool isStreamer = false, bool isModerator = false)
{ {
user = user.ToLower();
if (UserBlacklist.Contains(user)) if (UserBlacklist.Contains(user))
return false; return false;
if (UserWhitelist.Contains(user)) if (UserWhitelist.Contains(user))
@@ -32,8 +40,8 @@ public partial class Settings : Node
public void SetUserLists(IEnumerable<string> white, IEnumerable<string> black) public void SetUserLists(IEnumerable<string> white, IEnumerable<string> black)
{ {
UserWhitelist.Clear(); UserWhitelist.Clear();
UserWhitelist.AddRange(white); UserWhitelist.AddRange(white.Select(s => s.ToLower()));
UserBlacklist.Clear(); UserBlacklist.Clear();
UserBlacklist.AddRange(black); UserBlacklist.AddRange(black.Select(s => s.ToLower()));
} }
} }

View File

@@ -15,22 +15,35 @@ public partial class TwitchChatWatcher : Node
private readonly CancellationTokenSource TokenSource = new(); private readonly CancellationTokenSource TokenSource = new();
public CancellationToken Token => TokenSource.Token; public CancellationToken Token => TokenSource.Token;
private CommandHandler CommandHandler { get; set; } private CommandHandler CommandHandler { get; set; }
public WebSocketState State => Socket.State;
[Export]
public bool PrintAllIncoming { get; set; }
private Settings Settings;
[Signal] [Signal]
public delegate void IncomingCommandEventHandler(Command command); public delegate void IncomingCommandEventHandler(Command command);
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
CommandHandler = GetNode<CommandHandler>("/root/CommandHandler"); Settings = GetNode<Settings>("/root/Settings")
?? throw new Exception($"{nameof(Settings)} node not found");
CommandHandler = GetNode<CommandHandler>("/root/CommandHandler")
?? throw new Exception($"{nameof(Command)} not found");
} }
private readonly ConcurrentQueue<string> PrintQueue = new();
private readonly ConcurrentQueue<string> ErrorQueue = new();
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
if (PrintQueue.TryDequeue(out string s))
GD.Print(s);
if (ErrorQueue.TryDequeue(out string e))
GD.PrintErr(e);
} }
public async Task ConnectAsync() public async Task ConnectAsync()
{ {
GD.Print("Connecting");
if (Socket.State == WebSocketState.Open) if (Socket.State == WebSocketState.Open)
return; return;
await Socket.ConnectAsync(new Uri("wss://irc-ws.chat.twitch.tv:443"), Token); await Socket.ConnectAsync(new Uri("wss://irc-ws.chat.twitch.tv:443"), Token);
@@ -40,15 +53,22 @@ public partial class TwitchChatWatcher : Node
} }
public async Task Authenticate(string user = null, string pass = null) public async Task Authenticate(string user = null, string pass = null)
{ {
GD.Print("Authenticating");
user ??= $"justinfan{Random.Shared.NextInt64(10000):D4}"; user ??= $"justinfan{Random.Shared.NextInt64(10000):D4}";
pass ??= "pass"; pass ??= "pass";
await SendMessageAsync(TwitchChatMessageType.PASS, parameters: new string[] { pass }); await SendMessageAsync(TwitchChatMessageType.PASS, parameters: new string[] { pass });
await SendMessageAsync(TwitchChatMessageType.NICK, parameters: new string[] { user }); await SendMessageAsync(TwitchChatMessageType.NICK, parameters: new string[] { user });
} }
public async Task RequestTags()
{
await SendMessageAsync("CAP REQ :twitch.tv/tags");
}
public async Task JoinChannel(string channel) public async Task JoinChannel(string channel)
{ {
GD.Print("Joining channel");
channel = channel.TrimStart('#'); channel = channel.TrimStart('#');
await SendMessageAsync(TwitchChatMessageType.JOIN, parameters: new string[] {"#" + channel}); await SendMessageAsync(TwitchChatMessageType.JOIN,
parameters: new string[] {"#" + channel});
} }
public async Task SendMessageAsync(string message) public async Task SendMessageAsync(string message)
{ {
@@ -58,7 +78,7 @@ public partial class TwitchChatWatcher : Node
public async Task SendMessageAsync(TwitchChatMessageType command, IEnumerable<string> parameters = null, public async Task SendMessageAsync(TwitchChatMessageType command, IEnumerable<string> parameters = null,
IDictionary<string, string> tags = null, string prefix = null) IDictionary<string, string> tags = null, string prefix = null)
{ {
string EscapeTagValue(string s) static string EscapeTagValue(string s)
{ {
if (s is null) if (s is null)
return ""; return "";
@@ -89,41 +109,103 @@ public partial class TwitchChatWatcher : Node
} }
await SendMessageAsync(message); await SendMessageAsync(message);
} }
private static ulong PacketCount;
private async Task GetPacketsTask() private async Task GetPacketsTask()
{ {
var buffer = ArraySegment<byte>.Empty; try
{
var arr = new byte[16 * 1024];
var stringData = ""; var stringData = "";
while (!Token.IsCancellationRequested) while (!Token.IsCancellationRequested)
{ {
var res = await Socket.ReceiveAsync(buffer, Token); var res = await Socket.ReceiveAsync(arr, Token);
if (Token.IsCancellationRequested) if (Token.IsCancellationRequested)
return; return;
stringData += Encoding.UTF8.GetString(buffer); if (Socket.State != WebSocketState.Open)
{
ErrorQueue.Enqueue("Socket closed");
return;
}
if (res.Count == 0)
{
ErrorQueue.Enqueue("Empty packet received");
continue;
}
PacketCount++;
PrintQueue.Enqueue($"Packet count: {PacketCount}");
stringData += Encoding.UTF8.GetString(arr, 0, res.Count);
//PrintQueue.Enqueue(stringData);
var lines = stringData.Split("\r\n", StringSplitOptions.TrimEntries); var lines = stringData.Split("\r\n", StringSplitOptions.TrimEntries);
if (!lines.Any())
continue;
stringData = lines.Last(); stringData = lines.Last();
PrintQueue.Enqueue($"Line count: {lines.SkipLast(1).Count()}");
foreach (var line in lines.SkipLast(1)) foreach (var line in lines.SkipLast(1))
MessageStrings.Enqueue(line); MessageStrings.Enqueue(line);
} }
} }
catch (Exception e)
{
ErrorQueue.Enqueue(e.ToString());
}
finally
{
if (!Token.IsCancellationRequested)
ErrorQueue.Enqueue($"{nameof(GetPacketsTask)} exited without cancellation");
else
PrintQueue.Enqueue($"{nameof(GetPacketsTask)} cancelled and exited");
}
}
private readonly ConcurrentQueue<string> MessageStrings = new(); private readonly ConcurrentQueue<string> MessageStrings = new();
private void HandleMessages() private async Task HandleMessages()
{
try
{
while (!Token.IsCancellationRequested)
{ {
while (MessageStrings.TryDequeue(out string message)) while (MessageStrings.TryDequeue(out string message))
{ {
if (string.IsNullOrWhiteSpace(message))
continue;
PrintQueue.Enqueue(message);
// if (PrintAllIncoming)
// PrintQueue.Enqueue(message);
var tcm = TwitchChatMessage.Parse(message); var tcm = TwitchChatMessage.Parse(message);
if (tcm.MessageType == TwitchChatMessageType.PING) if (tcm.MessageType == TwitchChatMessageType.PING)
_ = SendPong(tcm); _ = Task.Run(() => SendPong(tcm), Token);
else if (tcm is Privmsg p) else if (tcm is Privmsg p)
{ {
EmitSignal(SignalName.IncomingCommand, new Command(p.DisplayName, var com = Settings.Command;
false, p.Moderator, p.ChatMessage)); if (!p.ChatMessage.StartsWith(com))
continue;
var chat = p.ChatMessage;
chat = chat[com.Length..].TrimStart();
CallDeferred("emit_signal", SignalName.IncomingCommand,
new Command(p.DisplayName,
false, p.Moderator, chat));
} }
} }
await Task.Delay(50);
}
}
catch (Exception e)
{
ErrorQueue.Enqueue(e.ToString() + System.Environment.NewLine
+ e.StackTrace);
}
finally
{
if (!Token.IsCancellationRequested)
ErrorQueue.Enqueue($"{nameof(HandleMessages)} exited without cancellation");
else
ErrorQueue.Enqueue($"{nameof(HandleMessages)} cancelled and exited");
}
} }
private async Task SendPong(TwitchChatMessage ping) private async Task SendPong(TwitchChatMessage ping)
{ {
var pong = TwitchChatMessage.MakePong(ping); var pong = TwitchChatMessage.MakePong(ping);
await SendMessageAsync(TwitchChatMessageType.PONG, ping.Parameters, await SendMessageAsync(TwitchChatMessageType.PONG, ping.Parameters,
ping.MessageTags, ping.Prefix); ping.MessageTags, ping.Prefix);
PrintQueue.Enqueue("Sent Pong");
} }
} }

98
card.cs
View File

@@ -1,7 +1,6 @@
using Godot; using Godot;
using System;
public partial class card : Control public partial class card : Panel
{ {
private Settings Settings => GetNode<Settings>("/root/Settings"); private Settings Settings => GetNode<Settings>("/root/Settings");
[Export] [Export]
@@ -18,7 +17,7 @@ public partial class card : Control
} }
private void PropogateCardName() private void PropogateCardName()
{ {
GetNode<Label>("CardNameLabel").Text = _CardName; GetNode<Label>("%CardNameLabel").Text = _CardName;
} }
[Export] [Export]
private string _CardId; private string _CardId;
@@ -51,6 +50,55 @@ public partial class card : Control
_Texture = null; _Texture = null;
} }
} }
private StretchMode? _StretchMode = null;
public void SetStretchMode(StretchMode stretchMode)
{
_StretchMode = stretchMode;
if (IsNodeReady())
PropogateStretchMode();
}
public StretchMode GetStretchMode()
{
var n = GetNode<TextureRect>("%CardImage");
return (n.StretchMode, n.ExpandMode) switch
{
(TextureRect.StretchModeEnum.Scale,
TextureRect.ExpandModeEnum.IgnoreSize)
=> StretchMode.Stretch,
(TextureRect.StretchModeEnum.KeepAspectCovered,
TextureRect.ExpandModeEnum.IgnoreSize)
=> StretchMode.Crop,
(TextureRect.StretchModeEnum.KeepAspectCentered,
TextureRect.ExpandModeEnum.IgnoreSize)
=> StretchMode.Fit,
_ => StretchMode.Unspecified,
};
}
private void PropogateStretchMode()
{
var ci = GetNode<TextureRect>("%CardImage");
switch (_StretchMode)
{
case null:
break;
case StretchMode.Stretch:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.Scale;
break;
case StretchMode.Crop:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.KeepAspectCovered;
break;
case StretchMode.Unspecified:
case StretchMode.Fit:
default:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered;
break;
}
_StretchMode = null;
}
public Texture2D GetTexture() public Texture2D GetTexture()
=> GetNode<TextureRect>("%CardImage").Texture; => GetNode<TextureRect>("%CardImage").Texture;
public Vector2 Center public Vector2 Center
@@ -61,20 +109,21 @@ public partial class card : Control
PropogateCardName(); PropogateCardName();
PropogateCardId(); PropogateCardId();
PropogateTexture(); PropogateTexture();
PropogateStretchMode();
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
public override Variant _GetDragData(Vector2 atPosition) public override Variant _GetDragData(Vector2 atPosition)
{ {
GD.Print($"starting to drag {CardId}"); GD.Print($"starting to drag {CardId}");
var prev = card_preview.MakePreview(this); var prev = card_preview.MakePreview(this);
var prev_root = new Control(); var prev_root = new Control();
//prev.Position = atPosition;
prev.Position = prev.Size / -2;
prev_root.AddChild(prev); prev_root.AddChild(prev);
prev.Position = prev.Size / -2;
SetDragPreview(prev_root); SetDragPreview(prev_root);
return this; return this;
} }
@@ -90,34 +139,17 @@ public partial class card : Control
var node = GetNode<TextureRect>("%CardImage"); var node = GetNode<TextureRect>("%CardImage");
node.Texture = texture; node.Texture = texture;
} }
// private void HandleInput(InputEvent @event) public override void _GuiInput(InputEvent @event)
// { {
// if (@event is InputEventMouseButton iemb) if (@event.IsActionPressed("LocationMenu"))
// { {
// if (iemb.ButtonIndex == MouseButton.Left) CardMenu();
// { }
// if (iemb.Pressed) }
// { private void CardMenu()
// InputSingleton.Instance.ClickedOn = this; {
// StartDrag(); this.GetParentOfType<game>().EditCardInMenu(this);
// } }
// else
// {
// InputSingleton.Instance.ClickedOn = null;
// StopDrag();
// }
// }
// }
// }
// private void StartDrag()
// {
// }
// private void StopDrag()
// {
// }
public static card MakeCard(SceneTree tree) public static card MakeCard(SceneTree tree)
{ {
var scene = GD.Load<PackedScene>("res://card.tscn"); var scene = GD.Load<PackedScene>("res://card.tscn");

View File

@@ -13,18 +13,9 @@ outline_color = Color(0, 0, 0, 1)
outline_size = 2 outline_size = 2
outline_color = Color(0, 0, 0, 1) outline_color = Color(0, 0, 0, 1)
[node name="Card" type="Control" groups=["CardGroup"]] [node name="Card" type="Panel" groups=["CardGroup"]]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
mouse_filter = 1
script = ExtResource("1_dysu7")
[node name="CardBackground" type="Panel" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 1 clip_contents = true
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
@@ -32,8 +23,9 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
mouse_filter = 1 mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_ndhi0") theme_override_styles/panel = SubResource("StyleBoxFlat_ndhi0")
script = ExtResource("1_dysu7")
[node name="CardImage" type="TextureRect" parent="CardBackground"] [node name="CardImage" type="TextureRect" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
@@ -41,22 +33,23 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
mouse_filter = 2
expand_mode = 1 expand_mode = 1
stretch_mode = 5 stretch_mode = 5
[node name="CardNameLabel" type="Label" parent="."] [node name="CardNameLabel" type="Label" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
z_index = 1 layout_mode = 1
layout_mode = 0 anchors_preset = 10
offset_right = 40.0 anchor_right = 1.0
offset_bottom = 23.0 offset_bottom = 23.0
mouse_filter = 1 grow_horizontal = 2
text = "Name" text = "Name"
label_settings = SubResource("LabelSettings_ui6ue") label_settings = SubResource("LabelSettings_ui6ue")
autowrap_mode = 3
[node name="CardIdLabel" type="Label" parent="."] [node name="CardIdLabel" type="Label" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
z_index = 1
layout_mode = 1 layout_mode = 1
anchors_preset = 2 anchors_preset = 2
anchor_top = 1.0 anchor_top = 1.0
@@ -64,6 +57,5 @@ anchor_bottom = 1.0
offset_top = -23.0 offset_top = -23.0
offset_right = 40.0 offset_right = 40.0
grow_vertical = 0 grow_vertical = 0
mouse_filter = 1
text = "ID" text = "ID"
label_settings = SubResource("LabelSettings_gncft") label_settings = SubResource("LabelSettings_gncft")

130
card_edit_popup.cs Normal file
View File

@@ -0,0 +1,130 @@
using Godot;
using System;
public partial class card_edit_popup : ConfirmationDialog
{
private LineEdit _TitleEdit;
public LineEdit TitleEdit
{ get
{
_TitleEdit ??= GetNode<LineEdit>("%TitleEdit");
return _TitleEdit;
}
}
private TextureRect _ImageBox;
public TextureRect ImageBox
{ get
{
_ImageBox ??= GetNode<TextureRect>("%CardEditImageBox");
return _ImageBox;
}
}
card EditingCard;
[Export]
private string Text
{
get => TitleEdit.Text;
set => TitleEdit.Text = value;
}
[Export]
private Texture2D Texture
{
get => ImageBox.Texture;
set => ImageBox.Texture = value;
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GetNode<BaseButton>("%StretchModeFitButton").ButtonGroup.Pressed
+= StetchModeChanged;
TitleEdit.TextSubmitted +=
(s) => GetOkButton().EmitSignal(Button.SignalName.Pressed);
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public void ActivateForCard(card c)
{
EditingCard = c;
Text = c.CardName;
Texture = c.GetTexture();
SetStretchModeButton(c.GetStretchMode());
Show();
}
public void OnVisibilityChange()
{
if (Visible)
{
TitleEdit.GrabFocus();
TitleEdit.SelectAll();
}
}
public void SetStretchModeButton(StretchMode stretchMode)
{
switch (stretchMode)
{
case StretchMode.Unspecified:
case StretchMode.Fit:
default:
GetNode<BaseButton>("%StretchModeFitButton").ButtonPressed = true;
break;
case StretchMode.Stretch:
GetNode<BaseButton>("%StretchModeStretchButton").ButtonPressed = true;
break;
case StretchMode.Crop:
GetNode<BaseButton>("%StretchModeCropButton").ButtonPressed = true;
break;
}
}
public StretchMode GetStretchMode()
{
if (GetNode<BaseButton>("%StretchModeFitButton").ButtonPressed)
return StretchMode.Fit;
else if (GetNode<BaseButton>("%StretchModeStretchButton").ButtonPressed)
return StretchMode.Stretch;
else if (GetNode<BaseButton>("%StretchModeCropButton").ButtonPressed)
return StretchMode.Crop;
return StretchMode.Unspecified;
}
private void StetchModeChanged(BaseButton button)
{
var ci = ImageBox;
switch (GetStretchMode())
{
case StretchMode.Stretch:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.Scale;
break;
case StretchMode.Crop:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.KeepAspectCovered;
break;
case StretchMode.Unspecified:
case StretchMode.Fit:
default:
ci.ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
ci.StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered;
break;
}
}
public void OkClicked()
{
EditingCard.CardName = Text;
EditingCard.SetTexture(Texture);
EditingCard.SetStretchMode(GetStretchMode());
// Hide();
}
public void CancelClicked()
{
// Hide();
}
public void FileSelected(string path)
{
Image image = new();
image.Load(path);
var texture = ImageTexture.CreateFromImage(image);
Texture = texture;
}
}

97
card_edit_popup.tscn Normal file
View File

@@ -0,0 +1,97 @@
[gd_scene load_steps=6 format=3 uid="uid://dxvues6b3g2tn"]
[ext_resource type="Script" path="res://card_edit_popup.cs" id="1_xxurr"]
[ext_resource type="Script" path="res://CardEditImageBox.cs" id="2_flth7"]
[ext_resource type="PackedScene" uid="uid://bhlqt64wrhx83" path="res://card_image_picker.tscn" id="3_4k21m"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nx8uj"]
[sub_resource type="ButtonGroup" id="ButtonGroup_t74v5"]
[node name="CardEditPopup" type="ConfirmationDialog"]
title = "Edit Card"
position = Vector2i(0, 36)
size = Vector2i(350, 320)
visible = true
unresizable = true
always_on_top = true
script = ExtResource("1_xxurr")
[node name="CardEditContainer" type="VBoxContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = 342.0
offset_bottom = 271.0
grow_horizontal = 2
grow_vertical = 2
[node name="TitleContainer" type="HBoxContainer" parent="CardEditContainer"]
layout_mode = 2
[node name="TitleLabel" type="Label" parent="CardEditContainer/TitleContainer"]
layout_mode = 2
text = "Text"
[node name="TitleEdit" type="LineEdit" parent="CardEditContainer/TitleContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="CardDetailsContainer" type="HSplitContainer" parent="CardEditContainer"]
layout_mode = 2
split_offset = 6
dragger_visibility = 1
[node name="CardImageSelectContainer" type="PanelContainer" parent="CardEditContainer/CardDetailsContainer"]
clip_contents = true
layout_mode = 2
size_flags_horizontal = 0
size_flags_vertical = 0
mouse_filter = 1
theme_override_styles/panel = SubResource("StyleBoxFlat_nx8uj")
[node name="CardEditImageBox" type="TextureRect" parent="CardEditContainer/CardDetailsContainer/CardImageSelectContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(200, 200)
layout_mode = 2
expand_mode = 1
stretch_mode = 5
script = ExtResource("2_flth7")
[node name="ImageStretchButtonsContainer" type="VBoxContainer" parent="CardEditContainer/CardDetailsContainer"]
layout_mode = 2
alignment = 1
[node name="StretchModeLabel" type="Label" parent="CardEditContainer/CardDetailsContainer/ImageStretchButtonsContainer"]
layout_mode = 2
text = " Stretch Mode"
[node name="StretchModeFitButton" type="CheckBox" parent="CardEditContainer/CardDetailsContainer/ImageStretchButtonsContainer"]
unique_name_in_owner = true
layout_mode = 2
button_pressed = true
button_group = SubResource("ButtonGroup_t74v5")
text = "Fit"
[node name="StretchModeStretchButton" type="CheckBox" parent="CardEditContainer/CardDetailsContainer/ImageStretchButtonsContainer"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_t74v5")
text = "Stretch"
[node name="StretchModeCropButton" type="CheckBox" parent="CardEditContainer/CardDetailsContainer/ImageStretchButtonsContainer"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_t74v5")
text = "Crop"
[node name="CardImagePicker" parent="." instance=ExtResource("3_4k21m")]
unique_name_in_owner = true
[connection signal="canceled" from="." to="." method="CancelClicked"]
[connection signal="confirmed" from="." to="." method="OkClicked"]
[connection signal="visibility_changed" from="." to="." method="OnVisibilityChange"]
[connection signal="file_selected" from="CardImagePicker" to="." method="FileSelected"]

15
card_image_picker.tscn Normal file
View File

@@ -0,0 +1,15 @@
[gd_scene load_steps=2 format=3 uid="uid://bhlqt64wrhx83"]
[ext_resource type="Script" path="res://CardImagePicker.cs" id="1_j0c8l"]
[node name="CardImagePicker" type="FileDialog"]
title = "Choose Picture"
size = Vector2i(312, 154)
ok_button_text = "Open"
dialog_hide_on_ok = true
mode_overrides_title = false
file_mode = 0
access = 2
filters = PackedStringArray("*.png, *.jpg, *.jpeg, *.webp, *.svg; Supported Images")
use_native_dialog = true
script = ExtResource("1_j0c8l")

View File

@@ -1,7 +1,7 @@
using Godot; using Godot;
using System; using System;
public partial class card_preview : Control public partial class card_preview : PanelContainer
{ {
[Export] [Export]
private string _CardName; private string _CardName;
@@ -39,7 +39,7 @@ public partial class card_preview : Control
public void SetTexture(Texture2D texture) public void SetTexture(Texture2D texture)
{ {
_Texture = texture; _Texture = texture;
if (IsNodeReady()) // if (IsNodeReady())
PropogateTexture(); PropogateTexture();
} }
private void PropogateTexture() private void PropogateTexture()
@@ -55,6 +55,7 @@ public partial class card_preview : Control
{ {
PropogateCardId(); PropogateCardId();
PropogateCardName(); PropogateCardName();
GD.Print(Size);
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
@@ -75,13 +76,19 @@ public partial class card_preview : Control
} }
public static card_preview MakePreview(card c) public static card_preview MakePreview(card c)
{ {
const float TRANSPARENCY = 0.25f; const float TRANSPARENCY = 0.75f;
var scene = GD.Load<PackedScene>("res://card_preview.tscn"); var scene = GD.Load<PackedScene>("res://card_preview.tscn");
var prev = scene.Instantiate() as card_preview; var prev = scene.Instantiate() as card_preview;
prev.CardName = c.CardName; prev.CardName = c.CardName;
prev.CardId = c.CardId; prev.CardId = c.CardId;
prev.SetTexture(c.GetTexture()); prev.SetTexture(c.GetTexture());
prev.Size = c.Size; // prev.Size = c.Size;
var tr = prev.GetNode<TextureRect>("%CardPreviewImage");
tr.CustomMinimumSize = c.Size;
var ci = c.GetNode<TextureRect>("%CardImage");
tr.ExpandMode = ci.ExpandMode;
tr.StretchMode = ci.StretchMode;
prev.GetNode<Control>("%CardPreviewImage").CustomMinimumSize = c.Size;
prev.Scale = c.Scale; prev.Scale = c.Scale;
prev.Modulate = new(1, 1, 1, TRANSPARENCY); prev.Modulate = new(1, 1, 1, TRANSPARENCY);
return prev; return prev;

View File

@@ -13,51 +13,36 @@ outline_color = Color(0, 0, 0, 1)
outline_size = 2 outline_size = 2
outline_color = Color(0, 0, 0, 1) outline_color = Color(0, 0, 0, 1)
[node name="CardPreview" type="Control" groups=["CardGroup"]] [node name="CardPreview" type="PanelContainer"]
custom_minimum_size = Vector2(200, 200)
layout_mode = 3
anchors_preset = 0
offset_right = 200.0
offset_bottom = 200.0
script = ExtResource("1_o00ln")
[node name="CardPreviewBackground" type="Panel" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 1 clip_contents = true
anchors_preset = 15 offset_left = 20.0
anchor_right = 1.0 offset_top = 20.0
anchor_bottom = 1.0 offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_ndhi0") theme_override_styles/panel = SubResource("StyleBoxFlat_ndhi0")
script = ExtResource("1_o00ln")
[node name="CardPreviewImage" type="TextureRect" parent="CardPreviewBackground"] [node name="CardPreviewImage" type="TextureRect" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 1 layout_mode = 2
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
expand_mode = 1 expand_mode = 1
stretch_mode = 5 stretch_mode = 5
[node name="CardPreviewNameLabel" type="Label" parent="."] [node name="CardPreviewNameLabel" type="Label" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 0 layout_mode = 2
offset_right = 40.0 size_flags_vertical = 1
offset_bottom = 23.0
text = "Name" text = "Name"
label_settings = SubResource("LabelSettings_ui6ue") label_settings = SubResource("LabelSettings_ui6ue")
autowrap_mode = 3
[node name="CardPreviewIdLabel" type="Label" parent="."] [node name="CardPreviewIdLabel" type="Label" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 1 layout_mode = 2
anchors_preset = 2 size_flags_vertical = 8
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -23.0
offset_right = 40.0
grow_vertical = 0
text = "ID" text = "ID"
label_settings = SubResource("LabelSettings_gncft") label_settings = SubResource("LabelSettings_gncft")
vertical_alignment = 2

81
defer_manager.cs Normal file
View File

@@ -0,0 +1,81 @@
using Godot;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Provides defer-like behavior that's easier to work with in C#
/// </summary>
public partial class defer_manager : Node
{
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
}
private readonly ConcurrentQueue<Action> ActionQueue = new();
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
while (ActionQueue.TryDequeue(out Action a))
{
a?.Invoke();
}
}
public void DeferForget(Action action)
{
ActionQueue.Enqueue(action);
}
public async Task DeferAsync(Action action)
{
using SemaphoreSlim slim = new(0);
ActionQueue.Enqueue(() =>
{
action?.Invoke();
slim.Release();
}
);
await slim.WaitAsync();
}
public async Task<T> DeferAsync<T>(Func<T> func)
{
if (func is null)
throw new Exception("Cannot defer null function");
using SemaphoreSlim slim = new(0);
T[] box = { default };
ActionQueue.Enqueue(() =>
{
box[0] = func.Invoke();
slim.Release();
}
);
await slim.WaitAsync();
return box[0];
}
public void Defer(Action action)
{
using SemaphoreSlim slim = new(0);
ActionQueue.Enqueue(() =>
{
action?.Invoke();
slim.Release();
}
);
slim.WaitAsync();
}
public T Defer<T>(Func<T> func)
{
if (func is null)
throw new Exception("Cannot defer null function");
using SemaphoreSlim slim = new(0);
T[] box = { default };
ActionQueue.Enqueue(() =>
{
box[0] = func.Invoke();
slim.Release();
}
);
slim.WaitAsync();
return box[0];
}
}

6
defer_manager.tscn Normal file
View File

@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://c34yvmvtgju"]
[ext_resource type="Script" path="res://defer_manager.cs" id="1_s88sy"]
[node name="DeferManager" type="Node"]
script = ExtResource("1_s88sy")

111
game.cs
View File

@@ -36,10 +36,9 @@ public partial class game : Control
public override void _Ready() public override void _Ready()
{ {
var rows = this.GetAllDescendents<row>().ToArray(); var rows = this.GetAllDescendents<row>().ToArray();
for (int i = 0; i < 50; i++) for (int i = 0; i < 20; i++)
{ {
var c = card.MakeCard(GetTree()); var c = card.MakeCard(GetTree());
GD.Print(c.CardId);
c.CardName = $"Card {c.CardId}"; c.CardName = $"Card {c.CardId}";
if (GD.RandRange(0, 1) == 1) if (GD.RandRange(0, 1) == 1)
{ {
@@ -56,31 +55,26 @@ public partial class game : Control
PropogateCardSize(); PropogateCardSize();
} }
// public override void _UnhandledInput(InputEvent @event) public override void _UnhandledInput(InputEvent @event)
// { {
// if (@event is InputEventMouseButton iemb) if (@event.IsActionPressed("OpenMenu"))
// { {
// if (iemb.ButtonIndex == MouseButton.Right) GetNode<settings_popup>("%SettingsPopup").Visible = true;
// { GetViewport().SetInputAsHandled();
}
// } }
// }
// else if (@event is InputEventKey iek)
// {
// }
// }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
public void SetContainerMinima() public void SetContainerMinima()
{ {
var node = GetNode<VBoxContainer>("%RowContainer"); // var node = GetNode<VBoxContainer>("%RowContainer");
node.CustomMinimumSize = new Vector2(0, _CardSize.Y * node.GetChildCount()); // node.CustomMinimumSize = new Vector2(0, _CardSize.Y * node.GetChildCount());
} }
public row GetRowById(string id) public row GetRowById(string id)
=> GetTree().GetNodesInGroup("RowGroup").OfType<row>().FirstOrDefault(r => r.RowId == id); => GetTree().GetNodesInGroup("RowGroup").OfType<row>()
.FirstOrDefault(r => r.RowId == id);
public void AddRow(row row, string after = null) public void AddRow(row row, string after = null)
{ {
if (after is not null) if (after is not null)
@@ -118,49 +112,10 @@ public partial class game : Control
} }
return c; return c;
} }
/// <summary> public void EditCardInMenu(card c)
/// {
/// </summary> GetNode<card_edit_popup>("%CardEditPopup").ActivateForCard(c);
/// <param name="id"></param> }
/// <param name="target_row">null to unassign card</param>
/// <returns>false if something goes wrong</returns>
// public bool MoveCard(string id, row target_row, float horizontal)
// {
// foreach (row r in Rows)
// {
// if (r.TryRemoveCard(id) is card c)
// {
// //add to row
// if (target_row is not null)
// {
// target_row.AddCard(c);
// return true;
// }
// //unassign
// else
// {
// AddUnassignedCard(c);
// return true;
// }
// }
// }
// if (RemoveUnassignedCard(id) is card cc)
// {
// //add to row
// if (target_row is not null)
// {
// target_row.AddCard(cc);
// return true;
// }
// //unassign
// else
// {
// AddUnassignedCard(cc);
// return true;
// }
// }
// return false;
// }
public card ClaimCard(string id) public card ClaimCard(string id)
{ {
foreach (row r in Rows) foreach (row r in Rows)
@@ -221,15 +176,25 @@ public partial class game : Control
var r = FindRow(rowId); var r = FindRow(rowId);
if (r is null) if (r is null)
throw new Exception($"row {r.RowId} not found"); throw new Exception($"row {r.RowId} not found");
} if (!deleteCards)
public void CreateCard(string title = null, Image image = null)
{ {
var scn = GD.Load<PackedScene>("res://card.tscn"); var unassigned = GetNode("%UnassignedCardContainer");
var c = scn.Instantiate() as card; foreach (var c in r.Cards.ToArray())
c.Reparent(unassigned);
}
//GetNode("%RowContainer").RemoveChild(r);
r.QueueFree();
}
public void CreateCard(string title = null, ImageWithMetadata image = null)
{
var c = card.MakeCard(GetTree());
if (!string.IsNullOrWhiteSpace(title)) if (!string.IsNullOrWhiteSpace(title))
c.CardName = title; c.CardName = title;
if (image is not null) if (image is not null)
c.SetTexture(ImageTexture.CreateFromImage(image)); {
c.SetTexture(ImageTexture.CreateFromImage(image.Image));
c.SetStretchMode(image.StretchMode);
}
AddUnassignedCard(c); AddUnassignedCard(c);
} }
public void CreateRow(Color? color = null, string title = null) public void CreateRow(Color? color = null, string title = null)
@@ -240,25 +205,27 @@ public partial class game : Control
r.RowText = title; r.RowText = title;
if (color is Color color1) if (color is Color color1)
r.RowColor = color1; r.RowColor = color1;
AddRow(r);
} }
public void RenameCard(string cardId, string newName) public void RenameCard(string cardId, string newName)
{ {
FindCard(cardId).CardName = newName;
} }
public void RenameRow(string rowId, string newTitle) public void RenameRow(string rowId, string newTitle)
{ {
GetRowById(rowId).RowText = newTitle;
} }
public void RecolorRow(string rowId, Color color) public void RecolorRow(string rowId, Color color)
{ {
GetRowById(rowId).RowColor = color;
} }
public void ChangeCardImage(string cardId, Image image) public void ChangeCardImage(string cardId, ImageWithMetadata image)
{ {
var c = FindCard(cardId); var c = FindCard(cardId);
if (c is null) if (c is null)
throw new Exception($"card {c.CardId} not found"); throw new Exception($"card {c.CardId} not found");
c.SetTexture(ImageTexture.CreateFromImage(image)); c.SetTexture(ImageTexture.CreateFromImage(image.Image));
c.SetStretchMode(image.StretchMode);
} }
#endregion //Commands #endregion //Commands
} }

View File

@@ -2,10 +2,10 @@
[ext_resource type="PackedScene" uid="uid://b7pebyti48f7b" path="res://row.tscn" id="1_numg7"] [ext_resource type="PackedScene" uid="uid://b7pebyti48f7b" path="res://row.tscn" id="1_numg7"]
[ext_resource type="Script" path="res://game.cs" id="1_vl33u"] [ext_resource type="Script" path="res://game.cs" id="1_vl33u"]
[ext_resource type="Script" path="res://UnassignedCardPanel.cs" id="3_dbs2t"]
[ext_resource type="Script" path="res://PictureDropHandler.cs" id="3_owd27"] [ext_resource type="Script" path="res://PictureDropHandler.cs" id="3_owd27"]
[ext_resource type="Script" path="res://TwitchChatWatcher.cs" id="5_qurdj"]
[ext_resource type="Script" path="res://CommandHandler.cs" id="5_yfhlo"]
[ext_resource type="PackedScene" uid="uid://jm7tss267q8y" path="res://settings_popup.tscn" id="6_e1cou"] [ext_resource type="PackedScene" uid="uid://jm7tss267q8y" path="res://settings_popup.tscn" id="6_e1cou"]
[ext_resource type="PackedScene" uid="uid://dxvues6b3g2tn" path="res://card_edit_popup.tscn" id="6_eqvov"]
[node name="Game" type="Control"] [node name="Game" type="Control"]
layout_mode = 3 layout_mode = 3
@@ -18,7 +18,7 @@ size_flags_vertical = 3
script = ExtResource("1_vl33u") script = ExtResource("1_vl33u")
_CardSize = Vector2(150, 150) _CardSize = Vector2(150, 150)
[node name="GameContainer" type="VBoxContainer" parent="."] [node name="GameScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@@ -26,46 +26,57 @@ anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="RowContainer" type="VBoxContainer" parent="GameContainer"] [node name="GameContainer" type="VBoxContainer" parent="GameScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="RowContainer" type="VBoxContainer" parent="GameScrollContainer/GameContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="Row_A" parent="GameContainer/RowContainer" instance=ExtResource("1_numg7")] [node name="Row_A" parent="GameScrollContainer/GameContainer/RowContainer" instance=ExtResource("1_numg7")]
layout_mode = 2 layout_mode = 2
_RowColor = Color(0.964706, 0.482353, 0.494118, 1) _RowColor = Color(0.964706, 0.482353, 0.494118, 1)
_RowText = "A" _RowText = "A"
RowId = "1" RowId = "1"
[node name="Row_B" parent="GameContainer/RowContainer" instance=ExtResource("1_numg7")] [node name="Row_B" parent="GameScrollContainer/GameContainer/RowContainer" instance=ExtResource("1_numg7")]
layout_mode = 2 layout_mode = 2
_RowColor = Color(0.996078, 0.878431, 0.541176, 1) _RowColor = Color(0.996078, 0.878431, 0.541176, 1)
_RowText = "B" _RowText = "B"
RowId = "2" RowId = "2"
[node name="Row_C" parent="GameContainer/RowContainer" instance=ExtResource("1_numg7")] [node name="Row_C" parent="GameScrollContainer/GameContainer/RowContainer" instance=ExtResource("1_numg7")]
layout_mode = 2 layout_mode = 2
_RowColor = Color(0.988235, 1, 0.494118, 1) _RowColor = Color(0.988235, 1, 0.494118, 1)
_RowText = "C" _RowText = "C"
RowId = "3" RowId = "3"
[node name="Row_D" parent="GameContainer/RowContainer" instance=ExtResource("1_numg7")] [node name="Row_D" parent="GameScrollContainer/GameContainer/RowContainer" instance=ExtResource("1_numg7")]
layout_mode = 2 layout_mode = 2
_RowColor = Color(0.639216, 0.937255, 0.34902, 1) _RowColor = Color(0.639216, 0.937255, 0.34902, 1)
_RowText = "D" _RowText = "D"
RowId = "4" RowId = "4"
[node name="Row_F" parent="GameContainer/RowContainer" instance=ExtResource("1_numg7")] [node name="Row_F" parent="GameScrollContainer/GameContainer/RowContainer" instance=ExtResource("1_numg7")]
layout_mode = 2 layout_mode = 2
_RowColor = Color(0.298039, 0.87451, 0.952941, 1) _RowColor = Color(0.298039, 0.87451, 0.952941, 1)
_RowText = "F" _RowText = "F"
RowId = "5" RowId = "5"
[node name="PictureDropHandler" type="Control" parent="."] [node name="UnassignedCardPanel" type="PanelContainer" parent="GameScrollContainer/GameContainer"]
layout_mode = 2
size_flags_vertical = 3
script = ExtResource("3_dbs2t")
[node name="UnassignedCardContainer" type="HFlowContainer" parent="GameScrollContainer/GameContainer/UnassignedCardPanel"]
unique_name_in_owner = true
layout_mode = 2
[node name="PictureDropHandler" type="Node" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("3_owd27") script = ExtResource("3_owd27")
[node name="CardContextMenu" type="PopupMenu" parent="."] [node name="CardContextMenu" type="PopupMenu" parent="."]
@@ -80,12 +91,10 @@ item_count = 1
item_0/text = "" item_0/text = ""
item_0/id = 0 item_0/id = 0
[node name="TwitchChatWatcher" type="Node" parent="."]
script = ExtResource("5_qurdj")
[node name="CommandHandler" type="Node" parent="."]
script = ExtResource("5_yfhlo")
[node name="SettingsPopup" parent="." instance=ExtResource("6_e1cou")] [node name="SettingsPopup" parent="." instance=ExtResource("6_e1cou")]
unique_name_in_owner = true
visible = false
[connection signal="IncomingCommand" from="TwitchChatWatcher" to="CommandHandler" method="IncomingCommand"] [node name="CardEditPopup" parent="." instance=ExtResource("6_eqvov")]
unique_name_in_owner = true
visible = false

View File

@@ -13,10 +13,12 @@ config_version=5
config/name="TierMakerControl" config/name="TierMakerControl"
run/main_scene="res://game.tscn" run/main_scene="res://game.tscn"
config/features=PackedStringArray("4.2", "C#", "Forward Plus") config/features=PackedStringArray("4.2", "C#", "Forward Plus")
run/flush_stdout_on_print=true
config/icon="res://icon.svg" config/icon="res://icon.svg"
[autoload] [autoload]
DeferManager="*res://defer_manager.tscn"
Settings="*res://Settings.cs" Settings="*res://Settings.cs"
CommandHandler="*res://CommandHandler.cs" CommandHandler="*res://CommandHandler.cs"
TwitchChatWatcher="*res://TwitchChatWatcher.cs" TwitchChatWatcher="*res://TwitchChatWatcher.cs"
@@ -32,3 +34,14 @@ OpenMenu={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194332,"key_label":0,"unicode":0,"echo":false,"script":null)
] ]
} }
ContextMenu={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(78, 14),"global_position":Vector2(82, 55),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
LocationMenu={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"button_mask":1,"position":Vector2(78, 20),"global_position":Vector2(82, 61),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(193, 8),"global_position":Vector2(197, 49),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}

9
row.cs
View File

@@ -3,6 +3,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
public partial class row : Control public partial class row : Control
{ {
@@ -89,6 +90,14 @@ public partial class row : Control
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
public override void _Input(InputEvent @event)
{
}
private void RowMenu()
{
}
public void DropOn(Vector2 atPosition, Variant data) public void DropOn(Vector2 atPosition, Variant data)
{ {
GD.Print($"Dropping at {atPosition}"); GD.Print($"Dropping at {atPosition}");

View File

@@ -51,7 +51,6 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_22f0d")
[node name="RowTitleBoxLabel" type="Label" parent="RowGrid/RowBaseContainer/RowTitleBoxBackground"] [node name="RowTitleBoxLabel" type="Label" parent="RowGrid/RowBaseContainer/RowTitleBoxBackground"]
unique_name_in_owner = true unique_name_in_owner = true
z_index = 1
layout_mode = 1 layout_mode = 1
anchors_preset = 8 anchors_preset = 8
anchor_left = 0.5 anchor_left = 0.5
@@ -73,16 +72,12 @@ justification_flags = 161
[node name="RowTitleBoxIdLabel" type="Label" parent="RowGrid/RowBaseContainer/RowTitleBoxBackground"] [node name="RowTitleBoxIdLabel" type="Label" parent="RowGrid/RowBaseContainer/RowTitleBoxBackground"]
unique_name_in_owner = true unique_name_in_owner = true
z_index = 1
layout_mode = 1 layout_mode = 1
anchors_preset = 12 anchors_preset = 2
anchor_top = 1.0 anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
offset_left = -24.0
offset_top = -23.0 offset_top = -23.0
offset_right = 24.0 offset_right = 48.0
grow_horizontal = 2
grow_vertical = 0 grow_vertical = 0
text = "RowId" text = "RowId"
label_settings = SubResource("LabelSettings_7hvr3") label_settings = SubResource("LabelSettings_7hvr3")

View File

@@ -1,35 +1,75 @@
using Godot; using Godot;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
public partial class settings_popup : Popup public partial class settings_popup : PopupPanel
{ {
[Signal]
public delegate void BeforeOkEventHandler();
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
Transient = true;
Exclusive = true;
} }
private readonly ConcurrentQueue<Action> ActionQueue = new();
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) public override void _Process(double delta)
{ {
// //perform 1 action every frame
// if (ActionQueue.TryDequeue(out Action a))
// {
// GD.Print("Found action");
// a?.Invoke();
// }
} }
public void _on_cancel_button_pressed() public void _on_cancel_button_pressed()
{ {
GD.Print("Cancel pressed");
Hide(); Hide();
} }
public void _on_ok_button_pressed() public void _on_ok_button_pressed()
{ {
GD.Print("OK pressed");
var tcw = GetNode<TwitchChatWatcher>("/root/TwitchChatWatcher");
string chName = GetNode<LineEdit>("%ChannelNameEdit").Text;
Task.Run(tcw.ConnectAsync).ContinueWith(t => tcw.Authenticate(null, null))
.ContinueWith(t => tcw.RequestTags())
.ContinueWith(t => tcw.JoinChannel(chName))
.ContinueWith(FinishConnection);
ProcessMode = ProcessModeEnum.Disabled;
}
private void FinishConnection(Task t)
{
GD.Print(t.Status);
if (t.IsCompletedSuccessfully)
{
CallDeferred(nameof(SuccessfulConnection));
}
else
{
GD.PrintErr(t.Exception);
ActionQueue.Enqueue(() => ProcessMode = ProcessModeEnum.Inherit);
}
}
private void SuccessfulConnection()
{
GD.Print("Running completion task");
var tcw = GetNode<TwitchChatWatcher>("/root/TwitchChatWatcher");
if (tcw.State != System.Net.WebSockets.WebSocketState.Open)
throw new Exception("Websocket closed");
var settings = GetNode<Settings>("/root/Settings"); var settings = GetNode<Settings>("/root/Settings");
settings.AllowModerators = GetNode<CheckBox>("%CheckBoxModerator").ButtonPressed; settings.AllowModerators = GetNode<CheckBox>("%CheckBoxModerator").ButtonPressed;
settings.Command = GetNode<LineEdit>("%CommandEdit").Text;
settings.SetUserLists(GetNode<TextEdit>("%WhiteListEdit").Text.Split('\n', settings.SetUserLists(GetNode<TextEdit>("%WhiteListEdit").Text.Split('\n',
StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries), StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries),
GetNode<TextEdit>("%BlackListEdit").Text.Split('\n', GetNode<TextEdit>("%BlackListEdit").Text.Split('\n',
StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)); StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries));
var tcw = GetNode<TwitchChatWatcher>("/root/TwitchChatWatcher"); ProcessMode = ProcessModeEnum.Inherit;
tcw.ConnectAsync().RunSynchronously();
tcw.Authenticate().RunSynchronously();
tcw.JoinChannel(GetNode<LineEdit>("%ChannelNameEdit").Text).RunSynchronously();
Hide(); Hide();
} }
} }

View File

@@ -1,91 +1,141 @@
[gd_scene load_steps=2 format=3 uid="uid://jm7tss267q8y"] [gd_scene load_steps=5 format=3 uid="uid://jm7tss267q8y"]
[ext_resource type="Script" path="res://settings_popup.cs" id="1_blkox"] [ext_resource type="Script" path="res://settings_popup.cs" id="1_blkox"]
[ext_resource type="Script" path="res://ConfigStretchContainer.cs" id="2_fhn7i"]
[node name="SettingsPopup" type="Popup"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jcc71"]
[sub_resource type="ButtonGroup" id="ButtonGroup_4itga"]
[node name="SettingsPopup" type="PopupPanel"]
title = "Settings" title = "Settings"
size = Vector2i(720, 480) size = Vector2i(728, 523)
visible = true visible = true
theme_override_styles/panel = SubResource("StyleBoxFlat_jcc71")
script = ExtResource("1_blkox") script = ExtResource("1_blkox")
[node name="SettingsPopupContainer" type="TabContainer" parent="."] [node name="SettingsDivider" type="VSplitContainer" parent="."]
offset_right = 728.0
offset_bottom = 523.0
[node name="SettingsPopupContainer" type="TabContainer" parent="SettingsDivider"]
custom_minimum_size = Vector2(720, 480) custom_minimum_size = Vector2(720, 480)
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="ChatContainer" type="VBoxContainer" parent="SettingsPopupContainer"]
layout_mode = 2 layout_mode = 2
[node name="ChannelNameContainer" type="HBoxContainer" parent="SettingsPopupContainer/ChatContainer"] [node name="ChatContainer" type="VBoxContainer" parent="SettingsDivider/SettingsPopupContainer"]
layout_mode = 2 layout_mode = 2
[node name="ChannelNameLabel" type="Label" parent="SettingsPopupContainer/ChatContainer/ChannelNameContainer"] [node name="TopBoxContainer" type="GridContainer" parent="SettingsDivider/SettingsPopupContainer/ChatContainer"]
layout_mode = 2
columns = 2
[node name="ChannelNameLabel" type="Label" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/TopBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "Channel Name" text = "Channel Name"
[node name="ChannelNameEdit" type="LineEdit" parent="SettingsPopupContainer/ChatContainer/ChannelNameContainer"] [node name="ChannelNameEdit" type="LineEdit" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/TopBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(500, 0) custom_minimum_size = Vector2(500, 0)
layout_mode = 2 layout_mode = 2
[node name="CheckBoxModerator" type="CheckBox" parent="SettingsPopupContainer/ChatContainer"] [node name="CommandLabel" type="Label" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/TopBoxContainer"]
layout_mode = 2
text = "Command"
[node name="CommandEdit" type="LineEdit" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/TopBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(500, 0)
layout_mode = 2
[node name="CheckBoxModerator" type="CheckBox" parent="SettingsDivider/SettingsPopupContainer/ChatContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Allow moderators" text = "Allow moderators"
[node name="UserListContainer" type="HBoxContainer" parent="SettingsPopupContainer/ChatContainer"] [node name="UserListContainer" type="HBoxContainer" parent="SettingsDivider/SettingsPopupContainer/ChatContainer"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="WhiteListContainer" type="VBoxContainer" parent="SettingsPopupContainer/ChatContainer/UserListContainer"] [node name="WhiteListContainer" type="VBoxContainer" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="WhiteListLabel" type="Label" parent="SettingsPopupContainer/ChatContainer/UserListContainer/WhiteListContainer"] [node name="WhiteListLabel" type="Label" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer/WhiteListContainer"]
layout_mode = 2 layout_mode = 2
text = "Whitelist" text = "Whitelist"
[node name="WhiteListEdit" type="TextEdit" parent="SettingsPopupContainer/ChatContainer/UserListContainer/WhiteListContainer"] [node name="WhiteListEdit" type="TextEdit" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer/WhiteListContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
[node name="BlackListContainer" type="VBoxContainer" parent="SettingsPopupContainer/ChatContainer/UserListContainer"] [node name="BlackListContainer" type="VBoxContainer" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="BlackListLabel" type="Label" parent="SettingsPopupContainer/ChatContainer/UserListContainer/BlackListContainer"] [node name="BlackListLabel" type="Label" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer/BlackListContainer"]
layout_mode = 2 layout_mode = 2
text = "Blacklist text = "Blacklist
" "
[node name="BlackListEdit" type="TextEdit" parent="SettingsPopupContainer/ChatContainer/UserListContainer/BlackListContainer"] [node name="BlackListEdit" type="TextEdit" parent="SettingsDivider/SettingsPopupContainer/ChatContainer/UserListContainer/BlackListContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
[node name="HBoxContainer" type="HBoxContainer" parent="SettingsPopupContainer/ChatContainer"] [node name="ConfigContainer" type="VBoxContainer" parent="SettingsDivider/SettingsPopupContainer"]
visible = false
layout_mode = 2
[node name="ConfigStretchContainer" type="VBoxContainer" parent="SettingsDivider/SettingsPopupContainer/ConfigContainer"]
layout_mode = 2
size_flags_vertical = 3
alignment = 1
script = ExtResource("2_fhn7i")
[node name="ConfigStretchLabel" type="Label" parent="SettingsDivider/SettingsPopupContainer/ConfigContainer/ConfigStretchContainer"]
layout_mode = 2
text = " Default Stretch Mode"
[node name="ConfigStretchFitButton" type="CheckBox" parent="SettingsDivider/SettingsPopupContainer/ConfigContainer/ConfigStretchContainer"]
unique_name_in_owner = true
layout_mode = 2
button_pressed = true
button_group = SubResource("ButtonGroup_4itga")
text = "Fit"
[node name="ConfigStretchStretchButton" type="CheckBox" parent="SettingsDivider/SettingsPopupContainer/ConfigContainer/ConfigStretchContainer"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_4itga")
text = "Stretch"
[node name="ConfigStretchCropButton" type="CheckBox" parent="SettingsDivider/SettingsPopupContainer/ConfigContainer/ConfigStretchContainer"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_4itga")
text = "Crop"
[node name="ButtonContainer" type="HBoxContainer" parent="SettingsDivider"]
layout_mode = 2 layout_mode = 2
alignment = 2 alignment = 2
[node name="CancelButton" type="Button" parent="SettingsPopupContainer/ChatContainer/HBoxContainer"] [node name="CancelButton" type="Button" parent="SettingsDivider/ButtonContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0) custom_minimum_size = Vector2(100, 0)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 8 size_flags_horizontal = 8
text = "Cancel" text = "Cancel"
[node name="OkButton" type="Button" parent="SettingsPopupContainer/ChatContainer/HBoxContainer"] [node name="OkButton" type="Button" parent="SettingsDivider/ButtonContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0) custom_minimum_size = Vector2(100, 0)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 8 size_flags_horizontal = 8
text = "OK" text = "OK"
[connection signal="pressed" from="SettingsPopupContainer/ChatContainer/HBoxContainer/CancelButton" to="." method="_on_cancel_button_pressed"] [connection signal="BeforeOk" from="." to="SettingsDivider/SettingsPopupContainer/ConfigContainer/ConfigStretchContainer" method="OkClicked"]
[connection signal="pressed" from="SettingsPopupContainer/ChatContainer/HBoxContainer/OkButton" to="." method="_on_ok_button_pressed"] [connection signal="pressed" from="SettingsDivider/ButtonContainer/CancelButton" to="." method="_on_cancel_button_pressed"]
[connection signal="pressed" from="SettingsDivider/ButtonContainer/OkButton" to="." method="_on_ok_button_pressed"]