From abf1e7fb746ba8fa0c87f3da0f96cce8f04857fe Mon Sep 17 00:00:00 2001 From: JacobTech Date: Fri, 22 Dec 2023 11:14:28 -0500 Subject: [PATCH] Framework Update A few changes, mostly to update the .NET framework version. Started work on the settings menu by adding experiments. --- Luski/Classes/ExperimentInfo.cs | 9 + Luski/Classes/ExperimentJson.cs | 13 ++ Luski/Classes/ExperimentSelectorInfo.cs | 14 ++ Luski/{Clesses => Classes}/ServerInfo.cs | 2 +- Luski/{Clesses => Classes}/ServerList.cs | 2 +- Luski/{Clesses => Classes}/Settings.cs | 6 +- Luski/{Clesses => Classes}/UpdaterSettings.cs | 2 +- Luski/GUI/MainScreen/UI/AddServerIcon.cs | 9 +- .../MainScreen/UI/LuskiControls/DropDown.cs | 167 ++++++++++++++ .../UI/LuskiControls/DropDownOption.cs | 19 ++ .../MainScreen/UI/LuskiControls/TextBox.cs | 203 ++++++++++++++++++ .../MainScreen/UI/PublicServers/Category.cs | 2 +- .../MainScreen/UI/PublicServers/Channel.cs | 26 ++- .../UI/PublicServers/ChatMessage.cs | 7 +- .../MainScreen/UI/PublicServers/PublicChat.cs | 92 ++++++-- Luski/GUI/MainScreen/UI/ServerIcon.cs | 3 +- Luski/GUI/MainScreen/UI/SettingsMenu.cs | 171 +++++++++++++++ .../MainScreen/UI/SettingsPanel/Category.cs | 52 +++++ .../UI/SettingsPanel/CategoryButton.cs | 96 +++++++++ .../UI/SettingsPanel/ExperimentDropButton.cs | 55 +++++ .../UI/SettingsPanel/ExperimentGUI.cs | 163 ++++++++++++++ Luski/GUI/MainScreenWindow.cs | 45 +++- Luski/GUI/StartPage/UI/CreateAccount.cs | 2 +- Luski/Globals.cs | 119 +++++++++- Luski/Luski.csproj | 4 +- Luski/LuskiExperiments.cs | 78 +++++++ Luski/Program.cs | 40 +--- .../Textures/RoundedRectangleBottom.png | Bin 0 -> 1073 bytes .../Textures/RoundedRectangleTop.png | Bin 0 -> 1063 bytes Luski/Resources/Textures/Textbox.png | Bin 0 -> 1522 bytes Luski/Resources/Textures/close.png | Bin 0 -> 492 bytes Luski/Resources/Textures/person.png | Bin 0 -> 415 bytes Luski/Resources/Textures/settings.png | Bin 0 -> 487 bytes 33 files changed, 1303 insertions(+), 98 deletions(-) create mode 100644 Luski/Classes/ExperimentInfo.cs create mode 100644 Luski/Classes/ExperimentJson.cs create mode 100644 Luski/Classes/ExperimentSelectorInfo.cs rename Luski/{Clesses => Classes}/ServerInfo.cs (94%) rename Luski/{Clesses => Classes}/ServerList.cs (96%) rename Luski/{Clesses => Classes}/Settings.cs (81%) rename Luski/{Clesses => Classes}/UpdaterSettings.cs (97%) create mode 100644 Luski/GUI/MainScreen/UI/LuskiControls/DropDown.cs create mode 100644 Luski/GUI/MainScreen/UI/LuskiControls/DropDownOption.cs create mode 100644 Luski/GUI/MainScreen/UI/LuskiControls/TextBox.cs create mode 100644 Luski/GUI/MainScreen/UI/SettingsMenu.cs create mode 100644 Luski/GUI/MainScreen/UI/SettingsPanel/Category.cs create mode 100644 Luski/GUI/MainScreen/UI/SettingsPanel/CategoryButton.cs create mode 100644 Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentDropButton.cs create mode 100644 Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentGUI.cs create mode 100644 Luski/LuskiExperiments.cs create mode 100644 Luski/Resources/Textures/RoundedRectangleBottom.png create mode 100644 Luski/Resources/Textures/RoundedRectangleTop.png create mode 100644 Luski/Resources/Textures/Textbox.png create mode 100644 Luski/Resources/Textures/close.png create mode 100644 Luski/Resources/Textures/person.png create mode 100644 Luski/Resources/Textures/settings.png diff --git a/Luski/Classes/ExperimentInfo.cs b/Luski/Classes/ExperimentInfo.cs new file mode 100644 index 0000000..7522679 --- /dev/null +++ b/Luski/Classes/ExperimentInfo.cs @@ -0,0 +1,9 @@ +namespace Luski.Classes; + +public class ExperimentInfo +{ + public string DisplayName { get; set; } = default!; + public string Name { get; set; } = default!; + public List Options { get; set; } = default!; + public int? Selected { get; set; } = null; +} \ No newline at end of file diff --git a/Luski/Classes/ExperimentJson.cs b/Luski/Classes/ExperimentJson.cs new file mode 100644 index 0000000..64f3af8 --- /dev/null +++ b/Luski/Classes/ExperimentJson.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Luski.Classes; + +public class ExperimentJson +{ + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; set; } = default!; + [JsonInclude] + [JsonPropertyName("selected")] + public int Selected { get; set; } = default!; +} \ No newline at end of file diff --git a/Luski/Classes/ExperimentSelectorInfo.cs b/Luski/Classes/ExperimentSelectorInfo.cs new file mode 100644 index 0000000..40581ed --- /dev/null +++ b/Luski/Classes/ExperimentSelectorInfo.cs @@ -0,0 +1,14 @@ +namespace Luski.Classes; + +public class ExperimentSelectorInfo +{ + public event Func? EventToggled; + public string Name { get; set; } = default!; + public string Description { get; set; } = default!; + public bool RequiresRestart { get; set; } = false; + + internal void SendTog(bool b) + { + if (EventToggled is not null) EventToggled.Invoke(b); + } +} \ No newline at end of file diff --git a/Luski/Clesses/ServerInfo.cs b/Luski/Classes/ServerInfo.cs similarity index 94% rename from Luski/Clesses/ServerInfo.cs rename to Luski/Classes/ServerInfo.cs index 479e808..498e128 100644 --- a/Luski/Clesses/ServerInfo.cs +++ b/Luski/Classes/ServerInfo.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Luski.Clesses; +namespace Luski.Classes; public class ServerInfo { diff --git a/Luski/Clesses/ServerList.cs b/Luski/Classes/ServerList.cs similarity index 96% rename from Luski/Clesses/ServerList.cs rename to Luski/Classes/ServerList.cs index f2c74fb..373eac4 100644 --- a/Luski/Clesses/ServerList.cs +++ b/Luski/Classes/ServerList.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Luski.Clesses; +namespace Luski.Classes; public class ServerList { diff --git a/Luski/Clesses/Settings.cs b/Luski/Classes/Settings.cs similarity index 81% rename from Luski/Clesses/Settings.cs rename to Luski/Classes/Settings.cs index f9fef7f..988fd8c 100644 --- a/Luski/Clesses/Settings.cs +++ b/Luski/Classes/Settings.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Luski.Clesses; +namespace Luski.Classes; public class Settings { @@ -16,6 +16,10 @@ public class Settings [JsonInclude] [JsonPropertyName("loadperchannel")] public int LoadPerChannel { get; set; } = 50; + [JsonInclude] + [JsonPropertyName("experiments")] + public List Experiments { get; set; } = Array.Empty().ToList(); + } [JsonSerializable(typeof(Settings))] diff --git a/Luski/Clesses/UpdaterSettings.cs b/Luski/Classes/UpdaterSettings.cs similarity index 97% rename from Luski/Clesses/UpdaterSettings.cs rename to Luski/Classes/UpdaterSettings.cs index 8df4964..1488b80 100644 --- a/Luski/Clesses/UpdaterSettings.cs +++ b/Luski/Classes/UpdaterSettings.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Luski.net.Enums; -namespace Luski.Clesses; +namespace Luski.Classes; public class UpdaterSettings { diff --git a/Luski/GUI/MainScreen/UI/AddServerIcon.cs b/Luski/GUI/MainScreen/UI/AddServerIcon.cs index 2e1d384..9ea6330 100644 --- a/Luski/GUI/MainScreen/UI/AddServerIcon.cs +++ b/Luski/GUI/MainScreen/UI/AddServerIcon.cs @@ -1,10 +1,4 @@ -using System.Reflection; -using GraphicsManager; -using GraphicsManager.Interfaces; using GraphicsManager.Objects; -using GraphicsManager.Objects.Core; -using Luski.net; -using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; namespace Luski.GUI.MainScreen.UI; @@ -14,8 +8,7 @@ public class AddServerIcon : UserControl public Rectangle Button; public AddServerIcon() { - Button = new(Globals.ms.TextureManager.AddTexture(Tools.GetResourceStream(Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.add.png"))) + Button = new(Globals.ms.TextureManager.GetTextureResource("add.png")) { Location = new((int)(18 * Globals.Settings.Scale), (int)(8 * Globals.Settings.Scale), 0), Size = new((int)(32 * Globals.Settings.Scale)), diff --git a/Luski/GUI/MainScreen/UI/LuskiControls/DropDown.cs b/Luski/GUI/MainScreen/UI/LuskiControls/DropDown.cs new file mode 100644 index 0000000..7197561 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/LuskiControls/DropDown.cs @@ -0,0 +1,167 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; + +namespace Luski.GUI.MainScreen.UI.LuskiControls; + +public class DropDown : UserControl where TSelection : DropDownOption +{ + public readonly FlowLayout DropDownContainer; + + private IParent ip; + + public required IParent DropDownParentOverride + { + get + { + return ip; + } + set + { + ip = value; + DropDownContainer.TransferOwners(Globals.ms, value); + } + } + private bool IsOpen; + public event Func? OpenStatusChanged; + + public DropDown(TSelection defaultOption) + { + ip = this; + DropDownContainer = new() + { + Visible = false + }; + Clicked += OnClicked; + WindowLoaded += OnWindowLoaded; + AddOption(defaultOption); + SelectedOption = defaultOption; + } + + public DropDown(Texture t, TSelection defaultOption) + :base(t) + { + ip = this; + TextureDisplay = TextureDisplay.HorizontalCenter; + DropDownContainer = new() + { + Visible = false, + Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right + }; + Clicked += OnClicked; + WindowLoaded += OnWindowLoaded; + DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + defaultOption.Size.Y); + DropDownContainer.Controls.Add(defaultOption); + defaultOption.Clicked += OptionOnClicked; + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + SelectedOption = defaultOption; + } + + public void SetSelected(TSelection obj) + { + SelectedOption = obj; + Controls.Clear(); + obj.LoadDisplay.Invoke(); + } + + public DropDown(Texture t, IRenderObject spacer, TSelection defaultOption) + :base(t) + { + ip = this; + TextureDisplay = TextureDisplay.HorizontalCenter; + DropDownContainer = new() + { + Visible = false, + Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right + }; + Clicked += OnClicked; + WindowLoaded += OnWindowLoaded; + DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + spacer.Size.Y); + DropDownContainer.Controls.Add(spacer); + DropDownContainer.Size = new(base.Size.X, DropDownContainer.Size.Y + defaultOption.Size.Y); + DropDownContainer.Controls.Add(defaultOption); + defaultOption.Clicked += OptionOnClicked; + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + SelectedOption = defaultOption; + } + + public TSelection SelectedOption { get; private set; } + + private Task OnWindowLoaded(IRenderObject arg) + { + SelectedOption.LoadDisplay.Invoke(); + Window!.MouseDown += WindowOnMouseDown; + return Task.CompletedTask; + } + + private void WindowOnMouseDown(MouseButtonEventArgs obj) + { + if (IsOpen && !MouseInside) + { + IsOpen = false; + DropDownContainer.Visible = IsOpen; + if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen); + TryDraw(); + } + } + + private Task OnClicked(IRenderObject arg) + { + BlockDraw = true; + if (IsOpen) + { + IsOpen = false; + DropDownContainer.Visible = IsOpen; + if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen); + BlockDraw = false; + TryDraw(); + return Task.CompletedTask; + } + + DropDownContainer.Location = this.GetParentLocation(DropDownParentOverride)+ new Vector3i(0, Size.Y, 0); + DropDownParentOverride.Controls.Add(DropDownContainer); + DropDownContainer.Size = new(Size.X, DropDownContainer.Size.Y); + DropDownContainer.ForceDistanceUpdate(DropDownParentOverride); + IsOpen = true; + DropDownContainer.Visible = IsOpen; + DropDownParentOverride.Controls.MoveControlToEnd(DropDownContainer); + if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen); + BlockDraw = false; + TryDraw(); + return Task.CompletedTask; + } + + public void AddOption(TSelection Option) + { + DropDownContainer.Size = new(Size.X, DropDownContainer.Size.Y + Option.Size.Y); + DropDownContainer.Controls.Add(Option); + Option.Clicked += OptionOnClicked; + } + + public event Func? OptionSelected; + + private Task OptionOnClicked(IRenderObject arg) + { + TSelection but =(arg as TSelection)!; + if (but == SelectedOption) + { + IsOpen = false; + DropDownContainer.Visible = IsOpen; + if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen); + TryDraw(); + return Task.CompletedTask; + } + SelectedOption = but; + Controls.Clear(); + SelectedOption.LoadDisplay.Invoke(); + IsOpen = false; + DropDownContainer.Visible = IsOpen; + if (OpenStatusChanged is not null) OpenStatusChanged.Invoke(IsOpen); + if (OptionSelected is not null) OptionSelected.Invoke(SelectedOption); + TryDraw(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/LuskiControls/DropDownOption.cs b/Luski/GUI/MainScreen/UI/LuskiControls/DropDownOption.cs new file mode 100644 index 0000000..5767ab2 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/LuskiControls/DropDownOption.cs @@ -0,0 +1,19 @@ +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI.LuskiControls; + +public class DropDownOption : UserControl +{ + public DropDownOption() + { + } + + + public DropDownOption(Texture t) : base(t) + { + } + + public required Action LoadDisplay; +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/LuskiControls/TextBox.cs b/Luski/GUI/MainScreen/UI/LuskiControls/TextBox.cs new file mode 100644 index 0000000..d868fb6 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/LuskiControls/TextBox.cs @@ -0,0 +1,203 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; +using OpenTK.Windowing.Common.Input; +using OpenTK.Windowing.GraphicsLibraryFramework; + +namespace Luski.GUI.MainScreen.UI.LuskiControls; + +public class TextBox : UserControl +{ + private Label _watermark, _label; + private bool use; + + public TextBox() + :base(Globals.ms.TextureManager.GetTextureResource("Textbox.png")) + { + TextureDisplay = TextureDisplay.HorizontalCenter; + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + _label = new Label(Globals.DefaultFont) + { + HoverMouse = MouseCursor.IBeam, + IgnoreHover = true + }; + _watermark = new(_label.Font) + { + Color = new(128, 128, 128, 255), + HoverMouse = MouseCursor.IBeam, + IgnoreHover = true + }; + Controls.Add(_label); + Controls.Add(_watermark); + } + + public event Func? KeyPress; + + public override void UnFocus() + { + use = false; + if (Window is not null && Window.focused == this) + Window.focused = null; + } + public override void Focus() + { + if (Window is not null) + { + if (Window.focused is not null) + { + Window.focused.UnFocus(); + } + + Window.focused = this; + use = true; + } + } + + public override void LoadToParent(IParent parent, IWindow window) + { + if (Loaded) return; + window.MouseDown += Window_MouseDown; + window.KeyDown += Window_KeyDown; + window.TextInput += WindowOnTextInput; + if (!window.Context.IsCurrent) window.Context.MakeCurrent(); + base.LoadToParent(parent, window); + } + + private void WindowOnTextInput(TextInputEventArgs obj) + { + if (!use) return; + Text += obj.AsString; + } + + + public char? PasswordChar { get => _label.PasswordChar; set => _label.PasswordChar = value; } + public TextLocation TextLocation { get; set; } = TextLocation.PostiveTureCenterLeft; + + public override bool Visible + { + get => base.Visible; + set + { + if (value == base.Visible) return; + if (value) + { + if (!string.IsNullOrEmpty(_label.Text)) + { + _label.Visible = true; + _watermark.Visible = false; + } + else + { + _label.Visible = false; + _watermark.Visible = true; + } + } + else + { + _label.Visible = value; + _watermark.Visible = value; + } + + base.Visible = value; + } + } + + public FontInteraction Font { get => _label.Font; } + public FontInteraction WatermarkFont { get => _watermark.Font; } + + public Color4 TextColor { get => _label.Color; set => _label.Color = value; } + public Color4 WatermarkColor { get => _watermark.Color; set => _watermark.Color = value; } + + public string Text + { + get => _label.Text; + set + { + int old = _label.TrueHeight; + _label.Text = value; + if (!string.IsNullOrEmpty(value)) + { + bool f = false; + if (!_label.Visible) + { + f = true; + _label.Visible = true; + _label.Location = TextLocation switch + { + TextLocation.PostiveTureCenterLeft => new(5, ((Size.Y - _label.PostiveTrueHeight) / 2) - _label.Size.Y + _label.TrueHeight, Location.Z), + _ => _label.Location + }; + /* + _label.Location = TextLocation switch + { + TextLocation.TrueCenterLeft => new(Location.X + 5, Location.Y + ((Size.Y - _label.TrueHeight) / 2) - (_label.Size.Y - _label.TrueHeight), Location.Z), + TextLocation.PostiveTureCenterLeft => new(Location.X + 5, Location.Y + ((Size.Y - _label.PostiveTrueHeight) / 2) - _label.Size.Y + _label.TrueHeight, Location.Z), + TextLocation.PxLeft => new(Location.X + 5, Location.Y + ((Size.Y - _label.Size.Y) / 2), Location.Z), + _ => new(Location.X + 5, Location.Y + 5, Location.Z) + };*/ + _watermark.Location = _label.Location; + } + if (_watermark.Visible) _watermark.Visible = false; + if (!f && TextLocation == TextLocation.TrueCenterLeft && old != _label.TrueHeight) + { + //_label.Location = new(Location.X + 5, Location.Y + ((Size.Y - _label.TrueHeight) / 2) - (_label.Size.Y - _label.TrueHeight), Location.Z); + _watermark.Location = _label.Location; + } + } + else + { + if (_label.Visible) _label.Visible = false; + if (!_watermark.Visible) + { + _watermark.Visible = true; + /* + _watermark.Location = TextLocation switch + { + TextLocation.TrueCenterLeft => new(Location.X + 5, Location.Y + ((Size.Y - _watermark.TrueHeight) / 2) - (_watermark.Size.Y - _watermark.TrueHeight), Location.Z), + TextLocation.PostiveTureCenterLeft => new(Location.X + 5, Location.Y + ((Size.Y - _label.PostiveTrueHeight) / 2) - _label.Size.Y + _label.TrueHeight, Location.Z), + TextLocation.PxLeft => new(Location.X + 5, Location.Y + ((Size.Y - _watermark.Size.Y) / 2), Location.Z), + _ => new(Location.X + 5, Location.Y + 5, Location.Z) + };*/ + _label.Location = _label.Location; + } + } + } + } + public string WatermarkText + { + get + { + return _watermark.Text; + } + set + { + _watermark.Text = value; + } + } + + private void Window_KeyDown(KeyboardKeyEventArgs obj) + { + if (!use) return; + if (obj.Key == Keys.CapsLock || obj.Key == Keys.Menu || obj.Key == Keys.LeftSuper || obj.Key == Keys.RightSuper || obj.Key == Keys.End || obj.Key == Keys.Home || obj.Key == Keys.PageDown || obj.Key == Keys.PageUp || obj.Key == Keys.Insert || obj.Key == Keys.Up || obj.Key == Keys.Down || obj.Key == Keys.Left || obj.Key == Keys.Right) return; + if (obj.Key == Keys.Backspace || obj.Key == Keys.Delete) + { + if (!(Text.Length > 0)) return; + Text = Text.Remove(Text.Length - 1, 1); + } + if (obj.Key == Keys.V && obj.Control && Window is not null) Text += Window.ClipboardString; + if (KeyPress is not null) _ = KeyPress.Invoke(obj); + } + + private void Window_MouseDown(MouseButtonEventArgs e) + { + if (MouseInside && e.Button == MouseButton.Button1) + { + use = true; + Focus(); + } + else use = false; + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/PublicServers/Category.cs b/Luski/GUI/MainScreen/UI/PublicServers/Category.cs index 0b4f193..69749d2 100644 --- a/Luski/GUI/MainScreen/UI/PublicServers/Category.cs +++ b/Luski/GUI/MainScreen/UI/PublicServers/Category.cs @@ -108,7 +108,7 @@ public class Category : UserControl, IChannelAdder Extended = !Extended; - Window!.ForceUpdate(new(Window.Size)); + // Window!.ForceUpdate(new(Window.Size)); } private bool e; diff --git a/Luski/GUI/MainScreen/UI/PublicServers/Channel.cs b/Luski/GUI/MainScreen/UI/PublicServers/Channel.cs index 6026285..4d8e027 100644 --- a/Luski/GUI/MainScreen/UI/PublicServers/Channel.cs +++ b/Luski/GUI/MainScreen/UI/PublicServers/Channel.cs @@ -11,20 +11,15 @@ namespace Luski.GUI.MainScreen.UI.PublicServers; public class Channel : UserControl { - //private Rectangle SelectedRct, SelectedRctL, SelectedRctR; private ChannelSelector CS; - //public readonly static Texture[] SelectedTextures = new Texture[] {null!, null!, null!}; - - public static Texture? seltec = null; - public SocketChannel CurrentChannel { get; set; } private Channel(Stream UserIcon, ChannelSelector cs, SocketChannel chan) - :base(seltec) + : base(Globals.ms.TextureManager.GetTextureResource("RoundedRectangle.png")) { CS = cs; CurrentChannel = chan; - Size = new((int)(307 * Globals.Settings.Scale), (int)(40* Globals.Settings.Scale)); + base.Size = new((int)(307 * Globals.Settings.Scale), (int)(40* Globals.Settings.Scale)); TextureDisplay = TextureDisplay.HorizontalCenter; Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; @@ -52,10 +47,10 @@ public class Channel : UserControl Controls.Add(ChannelName); Clicked += AllOnClicked; ChannelName.Location = new((int)(40 * Globals.Settings.Scale), - (Size.Y / 2) - ((int)ChannelName.Font.PixelHeight) + (ChannelName.PostiveTrueHeight / 2) + (base.Size.Y / 2) - ((int)ChannelName.Font.PixelHeight) + (ChannelName.PostiveTrueHeight / 2) , 0); ContextMenu = new((int)(150 * Globals.Settings.Scale)); - HoverMouse = MouseCursor.Hand; + base.HoverMouse = MouseCursor.Hand; } public bool Selected { get; private set; } @@ -78,18 +73,27 @@ public class Channel : UserControl await CS.Selected.ToggleSelected(); } BackgroundColor = bc; + Task? mm = null; if (Selected) { CS.Selected = this; IReadOnlyList m; - m = await CurrentChannel.GetMessages(CancellationToken.None, 200); + m = await CurrentChannel.GetMessages(CancellationToken.None, Globals.Settings.LoadPerChannel); //m = Array.Empty(); Globals.ms.pc.ClearChat(); await Globals.ms.pc.LoadChannel(CurrentChannel); - _ = Globals.ms.pc.AddMessages(m); + mm = Globals.ms.pc.AddMessages(m); + if (m.Count > 0)Globals.ms.pc.MessageFlow.ScrollToBottom(); } + + if (mm is not null) + { + Console.WriteLine("Waiting"); + Task.WaitAll(mm); + Console.WriteLine("Done"); + } BlockDraw = false; TryDraw(); } diff --git a/Luski/GUI/MainScreen/UI/PublicServers/ChatMessage.cs b/Luski/GUI/MainScreen/UI/PublicServers/ChatMessage.cs index 1171e71..2a76852 100644 --- a/Luski/GUI/MainScreen/UI/PublicServers/ChatMessage.cs +++ b/Luski/GUI/MainScreen/UI/PublicServers/ChatMessage.cs @@ -117,6 +117,7 @@ public class ChatMessage : UserControl public async Task AddMessage(SocketMessage msg) { + BlockDraw = true; Label newLabel; if (!string.IsNullOrWhiteSpace(msg.Context)) { @@ -171,6 +172,8 @@ public class ChatMessage : UserControl } if (LastObject is Label ll) Size = new(Size.X, (int)(ll.Location.Y + ll.Size.Y + VerticalPadding)); else Size = new(Size.X ,(int)(LastObject.Location.Y + LastObject.Size.Y + VerticalPadding)); + BlockDraw = false; + TryDraw(); } private Task NewLabelOnClicked(IRenderObject arg) @@ -206,7 +209,7 @@ public class ChatMessage : UserControl Controls.Remove(l.First()); Labels.Remove(l.First()); } - Window!.DrawFrame(); + Window!.TryDraw(); return Task.CompletedTask; } @@ -223,7 +226,7 @@ public class ChatMessage : UserControl Controls.Add(m); Labels.Add(m); - Window!.DrawFrame(); + Window!.TryDraw(); return Task.CompletedTask; } } \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/PublicServers/PublicChat.cs b/Luski/GUI/MainScreen/UI/PublicServers/PublicChat.cs index c0508b0..a4ff983 100644 --- a/Luski/GUI/MainScreen/UI/PublicServers/PublicChat.cs +++ b/Luski/GUI/MainScreen/UI/PublicServers/PublicChat.cs @@ -1,5 +1,8 @@ +using System.Reflection; +using GraphicsManager; using GraphicsManager.Enums; using GraphicsManager.Objects; +using Luski.GUI.MainScreen.UI.LuskiControls; using Luski.net.Structures.Public; using OpenTK.Mathematics; using OpenTK.Windowing.Common.Input; @@ -11,12 +14,14 @@ public class PublicChat : UserControl public FlowLayout MessageFlow; private Label title, desc; - //private Textbox tb; + private TextBox tb; private SocketChannel? Channel; + UserControl titlecon; + private Rectangle? UserCon; public PublicChat() { - UserControl titlecon; + base.Size = new((int)(980 * Globals.Settings.Scale), (int)(866 * Globals.Settings.Scale)); BackgroundColor = new(50, 50, 50, 255); Anchor = ObjectAnchor.All; @@ -29,7 +34,27 @@ public class PublicChat : UserControl HScrollPixels = Globals.Settings.PerScrollPixels, }); - Controls.Add(titlecon = new UserControl(){Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right, Size = new((int)(980 * Globals.Settings.Scale), (int)(52 * Globals.Settings.Scale)), BackgroundColor = BackgroundColor}); + Controls.Add(titlecon = new UserControl() + { + Anchor = ObjectAnchor.Left | ObjectAnchor.Top | ObjectAnchor.Right, + Size = new((int)(980 * Globals.Settings.Scale), (int)(48 * Globals.Settings.Scale)), + BackgroundColor = BackgroundColor + }); + if (LuskiExperiments.ServerExperiments.MemberList.IsEnabled()) + { + UserCon = + new(Globals.ms.TextureManager.GetTextureResource("person.png")) + { + Size = new((int)(24 * Globals.Settings.Scale)), + Location = new((int)(944 * Globals.Settings.Scale),(int)(12 * Globals.Settings.Scale),0), + Anchor = ObjectAnchor.Right | ObjectAnchor.Top, + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context], + BackgroundColor = Color4.Red + }; + titlecon.Controls.Add(UserCon); + } + LuskiExperiments.ServerExperiments.MemberList.EventToggled += MemberListOnEventToggled; + titlecon.ForceDistanceUpdate(this); titlecon.Controls.Add(title = new Label(Globals.DefaultFont) { @@ -42,20 +67,47 @@ public class PublicChat : UserControl Color = new(161,161,161,255), Location = new(title.Location.X + title.Size.X + 5, title.Location.Y, 0) }); - /* - Controls.Add(tb = new Textbox(Globals.DefaultFont, Globals.DefaultFont) + + Controls.Add(tb = new() { - InsideColor = new(28, 28, 28, 255), - BorderColor = Color4.DarkCyan, - Location = new((int)(10 * Globals.Settings.Scale), (int)(824 * Globals.Settings.Scale)), + // InsideColor = new(28, 28, 28, 255), + //BorderColor = Color4.DarkCyan, + Location = new((int)(10 * Globals.Settings.Scale), (int)(824 * Globals.Settings.Scale), 0), Size = new((int)(960 * Globals.Settings.Scale), (int)(34 * Globals.Settings.Scale)), Anchor = ObjectAnchor.Bottom | ObjectAnchor.Left | ObjectAnchor.Right, - HoverMouse = MouseCursor.IBeam - });*/ + HoverMouse = MouseCursor.IBeam, + //Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context], + BackgroundColor = Color4.Red + }); + tb.ForceDistanceUpdate(this); //tb.KeyPress += TbOnKeyPress; //Globals.Luski.MainServer.MessageReceived += LuskiOnMessageReceived; } - + + private Task MemberListOnEventToggled(bool arg) + { + if (arg) + { + UserCon = + new(Globals.ms.TextureManager.GetTextureResource("person.png")) + { + Size = new((int)(24 * Globals.Settings.Scale)), + Location = new(base.Size.X - (int)(36 * Globals.Settings.Scale),(int)(12 * Globals.Settings.Scale),0), + Anchor = ObjectAnchor.Right | ObjectAnchor.Top, + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context], + BackgroundColor = Color4.Red + }; + titlecon.Controls.Add(UserCon); + } + else + { + titlecon.Controls.Remove(UserCon!); + UserCon = null; + } + + return Task.CompletedTask; + } + private SocketMessage? lastm; private long? lastUser; private ChatMessage? LastChatMessage; @@ -70,7 +122,7 @@ public class PublicChat : UserControl { Window.Title = $"{channel.Name} | {channel.Server.Name} - Luski"; } - //this.tb.WatermarkText = $"Message {channel.Name}"; + tb.WatermarkText = $"Message {channel.Name}"; } public void ClearChat() @@ -83,6 +135,7 @@ public class PublicChat : UserControl public async Task AddMessages(IEnumerable messages, bool reverse = true) { + MessageFlow.BlockDraw = true; if (reverse) { foreach (SocketMessage message in messages.Reverse()) @@ -97,6 +150,7 @@ public class PublicChat : UserControl await AddMessage(message); } } + MessageFlow.BlockDraw = false; } public async Task AddMessage(SocketMessage Message) @@ -104,8 +158,8 @@ public class PublicChat : UserControl bool hasbeentenmin = false; if (lastm is not null) hasbeentenmin = - new DateTime(2022, 1, 1, 0, 0, 0, 0).AddMilliseconds(lastm.ID >> 22).ToLocalTime().AddMinutes(10) < - new DateTime(2022, 1, 1, 0, 0, 0, 0).AddMilliseconds(Message.ID >> 22).ToLocalTime(); + Channel!.Epoch.AddMilliseconds(lastm.ID >> 22).ToLocalTime().AddMinutes(10) < + Channel!.Epoch.AddMilliseconds(Message.ID >> 22).ToLocalTime(); lastm = Message; if (lastUser is null || lastUser != Message.AuthorID || hasbeentenmin) { @@ -119,7 +173,7 @@ public class PublicChat : UserControl Globals.ms.Invoke(() => { MessageFlow.Controls.Add(LastChatMessage); - Window.DrawFrame(); + Window.TryDraw(); }); } } @@ -130,7 +184,13 @@ public class PublicChat : UserControl await LastChatMessage!.AddMessage(Message); } else - Globals.ms.Invoke(() => { LastChatMessage!.AddMessage(Message); Window!.DrawFrame(); }); + { + Globals.ms.Invoke(() => + { + LastChatMessage!.AddMessage(Message); + Window!.TryDraw(); + }); + } } lastUser = Message.AuthorID; diff --git a/Luski/GUI/MainScreen/UI/ServerIcon.cs b/Luski/GUI/MainScreen/UI/ServerIcon.cs index d9a9308..ba77566 100644 --- a/Luski/GUI/MainScreen/UI/ServerIcon.cs +++ b/Luski/GUI/MainScreen/UI/ServerIcon.cs @@ -43,8 +43,7 @@ public class ServerIcon : UserControl where TServer : Server public ServerIcon(TServer Server) { - Rectangle r = new(Globals.ms.TextureManager.AddTexture(Tools.GetResourceStream(Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.rc.png"))) + Rectangle r = new(Globals.ms.TextureManager.GetTextureResource("rc.png")) { Location = new((int)(18 * Globals.Settings.Scale), (int)(8 * Globals.Settings.Scale), 0), Size = new((int)(32 * Globals.Settings.Scale)), diff --git a/Luski/GUI/MainScreen/UI/SettingsMenu.cs b/Luski/GUI/MainScreen/UI/SettingsMenu.cs new file mode 100644 index 0000000..e63793a --- /dev/null +++ b/Luski/GUI/MainScreen/UI/SettingsMenu.cs @@ -0,0 +1,171 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using Luski.Classes; +using Luski.GUI.MainScreen.UI.SettingsPanel; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI; + +public class SettingsMenu : UserControl +{ + private string BehindName; + public FlowLayout page; + public CategoryButton? Selected; + private FlowLayout fl; + private Category? AppSettings; + private FontInteraction f; + public SettingsMenu() + { + LuskiExperiments.Settings.Theme.EventToggled += ThemeOnEventToggled; + BehindName = Globals.ms.Title; + Globals.ms.Title = "Settings - Luski"; + BackgroundColor = new(34, 34, 34, 255); + base.Size = Globals.ms.Size; + Anchor = ObjectAnchor.All; + if (CategoryButton.seltec is null) + { + CategoryButton.seltec = Globals.ms.TextureManager.GetTextureResource("RoundedRectangle.png"); + } + fl = new() + { + BackgroundColor = new(20, 20, 20, 255), + Size = new((int)(307 * Globals.Settings.Scale), base.Size.Y), + Anchor = ObjectAnchor.Top | ObjectAnchor.Left | ObjectAnchor.Bottom + }; + f = Globals.DefaultFont.Clone(); + f.FontSize = FontSize.Bold; + f.PixelHeight = (uint)(f.PixelHeight * 1.4f); + + + if (LuskiExperiments.Settings.Theme.IsEnabled()) + { + AppSettings = new("APP SETTINGS"); + CategoryButton cb2 = new("Appearance", this) + { + OnPageLoad = () => + { + page!.Controls.Add(new Label(f) + { + Text = " \nAppearance\n " + }); + } + }; + AppSettings.AddButton(cb2); + fl.Controls.Insert(0, AppSettings); + fl.ScrollToBottom(); + } + + + Category As = new("ADVANCED SETTINGS"); + CategoryButton cb = new("Experiments", this) + { + OnPageLoad = () => + { + page!.Controls.Add(new Label(f) + { + Text = " \nExperiments\n " + }); + ExperimentGUI? g = null; + foreach (ExperimentInfo exp in Globals.Experiments) + { + g = new(exp); + page.Controls.Add(g); + } + + if (g is not null) + { + g.line.WindowLoaded += _ => + { + page.ParentResize(new(Globals.ms.Size)); + return Task.CompletedTask; + }; + } + } + }; + CategoryButton us = new("Updater Config", this) + { + OnPageLoad = () => + { + page!.Controls.Add(new Label(f) + { + Text = " \nUpdater Config\n " + }); + } + }; + As.AddButton(cb); + As.AddButton(us); + fl.Controls.Add(As); + page = new() + { + BackgroundColor = this.BackgroundColor, + Location = new(fl.Size.X + (int)(40 * Globals.Settings.Scale), 0, 0), + Size = new(Globals.ms.Size.X - fl.Size.X - ((int)(80 * Globals.Settings.Scale)), Globals.ms.Size.Y), + AllowHoverFromBehind = true, + Anchor = ObjectAnchor.All, + HScrollPixels = Globals.Settings.PerScrollPixels + }; + Controls.Add(page); + _ = cb.ToggleSelected(); + fl.ForceDistanceUpdate(this); + + Rectangle closebtn = new(Globals.ms.TextureManager.GetTextureResource("close.png")) + { + Location = new(Globals.ms.Size.X - (int)(40 * Globals.Settings.Scale), (int)(8 * Globals.Settings.Scale),0), + Size = new((int)(32 * Globals.Settings.Scale)), + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context], + BackgroundColor = Color4.Gray, + Anchor = ObjectAnchor.Top | ObjectAnchor.Right + }; + closebtn.MouseEnter += _ => + { + closebtn.BackgroundColor = Color4.White; + return Task.CompletedTask; + }; + closebtn.MouseLeave += _ => + { + closebtn.BackgroundColor = Color4.Gray; + return Task.CompletedTask; + }; + closebtn.Clicked += ClosebtnOnClicked; + closebtn.ForceDistanceUpdate(this); + Controls.Add(closebtn); + Controls.Add(fl); + } + + private Task ThemeOnEventToggled(bool arg) + { + if (arg) + { + AppSettings = new("APP SETTINGS"); + CategoryButton cb = new("Appearance", this) + { + OnPageLoad = () => + { + page.Controls.Add(new Label(f) + { + Text = " \nAppearance\n " + }); + } + }; + AppSettings.AddButton(cb); + fl.Controls.Insert(0, AppSettings); + fl.ScrollToBottom(); + } + else + { + fl.Controls.Remove(AppSettings!); + } + + return Task.CompletedTask; + } + + private Task ClosebtnOnClicked(IRenderObject arg) + { + Globals.ms.Controls.Remove(this); + Globals.ms.Title = BehindName; + Globals.ms.DrawFrame(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/SettingsPanel/Category.cs b/Luski/GUI/MainScreen/UI/SettingsPanel/Category.cs new file mode 100644 index 0000000..a2d9c05 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/SettingsPanel/Category.cs @@ -0,0 +1,52 @@ +using GraphicsManager.Enums; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI.SettingsPanel; + +public class Category : UserControl +{ + private Label Top; + private static FontInteraction? fi; + private Rectangle line; + public string Name + { + get => Top.Text; + } + + public Category(string Name) + { + if (fi is null) + { + fi = Globals.DefaultFont.Clone(); + fi.FontSize = FontSize.Bold; + } + Top = new(fi) + { + Location = new((int)(5 * Globals.Settings.Scale), (int)(5 * Globals.Settings.Scale), 0), + Text = Name + }; + BackgroundColor = new(255, 255, 255, 0); + base.Size = new((int)(307 * Globals.Settings.Scale), (int)(20 * Globals.Settings.Scale) + Top.Size.Y); + Top.Location = new((base.Size.X - Top.Size.X) / 2, Top.Location.Y, 0); + line = new() + { + Size = new(base.Size.X, (int)Globals.Settings.Scale), + BackgroundColor = Color4.Gray, + Location = new(0, base.Size.Y - (int)Globals.Settings.Scale, 0) + }; + line.ForceDistanceUpdate(this); + Controls.Add(line); + Controls.Add(Top); + } + + public void AddButton(CategoryButton cb) + { + Controls.Add(cb); + int f = (int)(5 * Globals.Settings.Scale); + cb.Location = new (line.Location.X + f, line.Location.Y - f, 0); + line.Location = new(line.Location.X, line.Location.Y + cb.Size.Y + f, 0); + Size = new(Size.X, Size.Y + cb.Size.Y + f); + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/SettingsPanel/CategoryButton.cs b/Luski/GUI/MainScreen/UI/SettingsPanel/CategoryButton.cs new file mode 100644 index 0000000..d209123 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/SettingsPanel/CategoryButton.cs @@ -0,0 +1,96 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI.SettingsPanel; + +public class CategoryButton : UserControl +{ + public static Texture? seltec = null; + private SettingsMenu SM; + private Label l; + public required Action OnPageLoad; + + public CategoryButton(string Text, SettingsMenu SM) + :base(seltec) + { + this.SM = SM; + base.Size = new((int)(297 * Globals.Settings.Scale), (int)(40* Globals.Settings.Scale)); + TextureDisplay = TextureDisplay.HorizontalCenter; + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + l = new Label(Globals.DefaultFont) + { + Text = Text, + Color = Color4.Gray, + IgnoreHover = true + }; + l.Location = new((int)(5 * Globals.Settings.Scale), + (base.Size.Y / 2) - ((int)l.Font.PixelHeight) + (l.PostiveTrueHeight / 2) + , 0); + Controls.Add(l); + BackgroundColor = new(0, 0, 0, 0); + Clicked += OnClicked; + MouseEnter += o => + { + if (!Selected) + { + BackgroundColor = new(141, 151, 165, 30); + } + return Task.CompletedTask; + }; + MouseLeave += o => + { + if (!Selected) + { + BackgroundColor = new(0,0,0,0); + } + return Task.CompletedTask; + }; + } + + private async Task OnClicked(IRenderObject arg) + { + if (!Selected) await ToggleSelected(); + } + + public bool Selected { get; private set; } + + public async Task ToggleSelected() + { + try + { + Color4 bc = new(141,151,165,51), f= Color4.White; + if (Selected) + { + bc = new (0,0,0,0); + f = Color4.Gray; + } + + BlockDraw = true; + Selected = !Selected; + + if (SM.Selected is not null && SM.Selected != this) + { + await SM.Selected.ToggleSelected(); + } + BackgroundColor = bc; + l.Color = f; + if (Selected) + { + SM.Selected = this; + Globals.ms.Title = $"Settings | {l.Text} - Luski"; + SM.page.Controls.Clear(); + OnPageLoad.Invoke(); + } + + BlockDraw = false; + TryDraw(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentDropButton.cs b/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentDropButton.cs new file mode 100644 index 0000000..1b6105f --- /dev/null +++ b/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentDropButton.cs @@ -0,0 +1,55 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using Luski.Classes; +using Luski.GUI.MainScreen.UI.LuskiControls; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI.SettingsPanel; + +public class ExperimentDropButton : DropDownOption +{ + private Label l, d; + public ExperimentSelectorInfo ESI; + + public ExperimentDropButton(ExperimentSelectorInfo esi) + :base(CategoryButton.seltec!) + { + ESI = esi; + base.Size = new((int)(297 * Globals.Settings.Scale), (int)(40* Globals.Settings.Scale)); + TextureDisplay = TextureDisplay.HorizontalCenter; + Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + l = new Label(Globals.DefaultFont) + { + Text = esi.Name, + Color = Color4.DarkGray, + IgnoreHover = true + }; + l.Location = new((int)(10 * Globals.Settings.Scale), + (base.Size.Y / 2) - ((int)l.Font.PixelHeight) + (l.PostiveTrueHeight / 2) + , 0); + d = new(Globals.MessageFont) + { + Text = esi.Description, + Color = Color4.Gray, + IgnoreHover = true + }; + d.Location = new(l.Location.X + l.Size.X + l.Location.X, + l.Location.Y + (int)l.Font.PixelHeight - (int)d.Font.PixelHeight + , 0); + Controls.Add(d); + Controls.Add(l); + BackgroundColor = new(0, 0, 0, 0); + MouseEnter += o => + { + BackgroundColor = new(141, 151, 165, 30); + return Task.CompletedTask; + }; + MouseLeave += o => + { + BackgroundColor = new(0,0,0,0); + return Task.CompletedTask; + }; + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentGUI.cs b/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentGUI.cs new file mode 100644 index 0000000..ef77081 --- /dev/null +++ b/Luski/GUI/MainScreen/UI/SettingsPanel/ExperimentGUI.cs @@ -0,0 +1,163 @@ +using GraphicsManager.Enums; +using GraphicsManager.Objects; +using GraphicsManager.Objects.Core; +using Luski.Classes; +using Luski.GUI.MainScreen.UI.LuskiControls; +using OpenTK.Mathematics; + +namespace Luski.GUI.MainScreen.UI.SettingsPanel; + +public class ExperimentGUI : UserControl +{ + private Label Top; + public Rectangle line; + public DropDown dd; + private static Texture? TopOpen, BottomOpen; + private ExperimentSelectorInfo? currentEnabled; + + private static ExperimentSelectorInfo DisabledESI = new() + { + Name = "Disabled" + }; + + public string Name + { + get => Top.Text; + } + + public ExperimentGUI(ExperimentInfo ei) + { + if (TopOpen is null) + { + TopOpen = Globals.ms.TextureManager.GetTextureResource("RoundedRectangleTop.png"); + } + if (BottomOpen is null) + { + BottomOpen = Globals.ms.TextureManager.GetTextureResource("RoundedRectangleBottom.png"); + } + Top = new(Globals.DefaultFont) + { + Location = new((int)(5 * Globals.Settings.Scale), (int)(5 * Globals.Settings.Scale), 0), + Text = ei.DisplayName + }; + Label n = new(Globals.MessageFont) + { + Text = ei.Name, + Location = new(Top.Location.X, Top.Location.Y + Top.Size.Y + Top.Location.Y, 0), + Color = Color4.Gray + }; + Controls.Add(n); + BackgroundColor = new(255, 255, 255, 0); + ExperimentDropButton Disabled = new(DisabledESI) + { + LoadDisplay = () => + { + Label ll = new Label(Globals.DefaultFont) + { + Text = DisabledESI.Name, + Color = Color4.DarkGray, + IgnoreHover = true + }; + ll.Location = new((int)(10 * Globals.Settings.Scale), + (dd!.Size.Y / 2) - ((int)ll.Font.PixelHeight) + (ll.PostiveTrueHeight / 2) + , 0); + dd.Controls.Add(ll); + }, + Size = new((int)(297 * Globals.Settings.Scale), (int)(40* Globals.Settings.Scale)) + }; + dd = new(CategoryButton.seltec!, new Rectangle() + { + Size = new((int)Globals.Settings.Scale), + BackgroundColor = Color4.Gray + }, Disabled) + { + Size = new((int)(40 * Globals.Settings.Scale)), + Location = new(Top.Location.X, n.Location.Y + n.Size.Y + Top.Location.Y, 0), + BackgroundColor = new(40, 40, 40, 255), + Anchor = ObjectAnchor.Right | ObjectAnchor.Left, + DropDownParentOverride = this + }; + dd.DropDownContainer.Textures.Add(BottomOpen); + dd.DropDownContainer.TextureDisplay = TextureDisplay.HorizontalCenter; + dd.DropDownContainer.Shader = Rectangle.DefaultAlphaShader[Globals.ms.Context]; + dd.DropDownContainer.BackgroundColor = dd.BackgroundColor; + dd.OpenStatusChanged += b => + { + BlockDraw = true; + Size = new(base.Size.X, base.Size.Y + ( b ? dd.DropDownContainer.Size.Y : -1 * dd.DropDownContainer.Size.Y)); + line!.Location = new(line.Location.X, + line.Location.Y + (b ? dd.DropDownContainer.Size.Y : -1 * dd.DropDownContainer.Size.Y), 0); + if (b) + { + dd.Textures[0] = TopOpen; + } + else + { + dd.Textures[0] = CategoryButton.seltec!; + } + + BlockDraw = false; + return Task.CompletedTask; + }; + int i = -1; + foreach (ExperimentSelectorInfo o in ei.Options) + { + i++; + ExperimentDropButton oo = new ExperimentDropButton(o) + { + Tag = i, + LoadDisplay = () => + { + Label ll = new Label(Globals.DefaultFont) + { + Text = o.Name, + Color = Color4.DarkGray, + IgnoreHover = true + }; + ll.Location = new((int)(10 * Globals.Settings.Scale), + (dd.Size.Y / 2) - ((int)ll.Font.PixelHeight) + (ll.PostiveTrueHeight / 2) + , 0); + dd.Controls.Add(ll); + } + }; + dd.AddOption(oo); + if (o.IsEnabled()) + { + Console.WriteLine($"Default for {ei.DisplayName}: {o.Name}"); + dd.SetSelected(oo); + currentEnabled = o; + } + } + dd.OptionSelected += DdOnOptionSelected; + Controls.Add(dd); + base.Size = new(Globals.ms.Size.X - (int)(307 * Globals.Settings.Scale) - (int)(80 * Globals.Settings.Scale), (int)(15 * Globals.Settings.Scale) + dd.Size.Y + dd.Location.Y ); + dd.Size = new(base.Size.X - Top.Location.X - Top.Location.X, dd.Size.Y); + dd.ForceDistanceUpdate(this); + + line = new() + { + Size = new(base.Size.X - 2, (int)Globals.Settings.Scale), + BackgroundColor = Color4.Gray, + Location = new(1, base.Size.Y - (int)Globals.Settings.Scale, 0), + Anchor = ObjectAnchor.Left | ObjectAnchor.Right | ObjectAnchor.Bottom + }; + line.ForceDistanceUpdate(this); + Controls.Add(line); + Controls.Add(Top); + } + + private Task DdOnOptionSelected(ExperimentDropButton arg) + { + if (arg.ESI == DisabledESI) + { + currentEnabled!.Toggle(); + currentEnabled = null; + } + else + { + currentEnabled = arg.ESI; + arg.ESI.Toggle(); + } + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Luski/GUI/MainScreenWindow.cs b/Luski/GUI/MainScreenWindow.cs index 2c6994d..33d8914 100644 --- a/Luski/GUI/MainScreenWindow.cs +++ b/Luski/GUI/MainScreenWindow.cs @@ -98,8 +98,7 @@ public class MainScreenWindow : Window Globals.MessageFont.ExtraLinePixels = (uint)(5 * Globals.Settings.Scale); Globals.MessageFont.FontSize = FontSize.Regular; Globals.SmallTimeFont = Globals.DefaultFont.Clone(); - Globals.LuskiTexture = TextureManager.AddTexture(Tools.GetResourceStream(Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.Luski.png")); + Globals.LuskiTexture = TextureManager.GetTextureResource("Luski.png"); CenterWindow(0); if ((Globals.Luski.MainServer is not null && !Globals.Luski.MainServer.IsLogedIn) || !Globals.Luski.LoadedServers.Any(s => s.IsLogedIn)) @@ -188,9 +187,6 @@ public class MainScreenWindow : Window #region Channel Selector Init SocketChannel current_channel = await Server.User.GetSelectedChannel(CancellationToken.None); - Channel.seltec = Globals.ms.TextureManager.AddTexture( - Tools.GetResourceStream(Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.RoundedRectangle.png")); List parents = new(); SocketCategory? cur = await current_channel.GetParent(); while (cur is not null) @@ -203,7 +199,7 @@ public class MainScreenWindow : Window ChannelSelector cs = new(parents[0]) { BackgroundColor = new(34, 34, 34, 255), - Size = new((int)(307 * Globals.Settings.Scale), SerBox.Size.Y - 106), + Size = new((int)(307 * Globals.Settings.Scale), SerBox.Size.Y - (int)(54 * Globals.Settings.Scale)), Anchor = ObjectAnchor.Top | ObjectAnchor.Left | ObjectAnchor.Bottom }; parents.RemoveAt(0); @@ -238,13 +234,12 @@ public class MainScreenWindow : Window Role[] ra = await Server.User.GetRoles(); Color c = ra[0].Color; Color4 c4 = new(c.R, c.G, c.B, c.A); - Rectangle u = new Rectangle(TextureManager.AddTexture(Tools.GetResourceStream( - Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.Status.png"))) + Rectangle u = new Rectangle(TextureManager.GetTextureResource("Status.png")) { Anchor = ObjectAnchor.Bottom | ObjectAnchor.Left, Size = new((int)(46 * Globals.Settings.Scale)), - Location = new((int)(4 * Globals.Settings.Scale), (int)(812 * Globals.Settings.Scale), 0) + Location = new((int)(4 * Globals.Settings.Scale), SerBox.Size.Y, 0) }; + u.Location = new(u.Location.X, cs.Size.Y + u.Location.X, 0); u.Shader = Rectangle.DefaultAlphaTextureShader[Context]; u.Textures.Add(await Server.User.GetIcon()); SerBox.Controls.Add(u); @@ -261,6 +256,28 @@ public class MainScreenWindow : Window (u.Location.Y + (u.Size.Y / 2) - (ul.PostiveTrueHeight / 2) - ul.Size.Y + ul.TrueHeight), 0); SerBox.Controls.Add(ul); + Rectangle setting = new(TextureManager.GetTextureResource("settings.png")) + { + Location = new(cs.Size.X - (int)(40 * Globals.Settings.Scale), cs.Size.Y + (int)(Globals.Settings.Scale * 11),0), + Size = new((int)(32 * Globals.Settings.Scale)), + Shader = Rectangle.DefaultAlphaShader[Context], + BackgroundColor = Color4.Gray, + Anchor = ObjectAnchor.Bottom | ObjectAnchor.Left + }; + setting.MouseEnter += o => + { + setting.BackgroundColor = Color4.White; + return Task.CompletedTask; + }; + setting.MouseLeave += o => + { + setting.BackgroundColor = Color4.Gray; + return Task.CompletedTask; + }; + setting.Clicked += SettingOnClicked; + setting.ForceDistanceUpdate(SerBox); + SerBox.Controls.Add(setting); + #endregion } catch (Exception e) @@ -271,6 +288,14 @@ public class MainScreenWindow : Window BlockDraw = false; } + private Task SettingOnClicked(IRenderObject arg) + { + SettingsMenu sm = new(); + Controls.Add(sm); + Globals.ms.DrawFrame(); + return Task.CompletedTask; + } + public async Task LoadMainServer(MainServer Server) { if (SerBox is null) diff --git a/Luski/GUI/StartPage/UI/CreateAccount.cs b/Luski/GUI/StartPage/UI/CreateAccount.cs index af9c067..97d1f71 100644 --- a/Luski/GUI/StartPage/UI/CreateAccount.cs +++ b/Luski/GUI/StartPage/UI/CreateAccount.cs @@ -75,7 +75,7 @@ public class CreateAccount : UserControl rec.Textures[1].Unit = TextureUnit.Texture1; } - Window!.DrawFrame(); + Window!.TryDraw(); return Task.CompletedTask; } diff --git a/Luski/Globals.cs b/Luski/Globals.cs index f1fd9db..55ef74c 100644 --- a/Luski/Globals.cs +++ b/Luski/Globals.cs @@ -4,7 +4,7 @@ using System.Text.Json.Serialization.Metadata; using GraphicsManager; using GraphicsManager.Interfaces; using GraphicsManager.Objects.Core; -using Luski.Clesses; +using Luski.Classes; using Luski.GUI; using Luski.net; using Luski.net.Interfaces; @@ -19,6 +19,109 @@ public static class Globals { #pragma warning disable CS8618 public static Dictionary> AllowedBehindObjects = new(); + + private static List AddedExperiments = new(); + private static List EnabledExperiments = new(); + public static List MissingExperiments = new(); + private static Dictionary ExperimentSelectorInfos = new(); + public static IReadOnlyList Experiments => AddedExperiments.AsReadOnly(); + + private static int LastExpCount = 0; + + public static void RegisterExperiment(ExperimentInfo exp) + { + IEnumerable found = MissingExperiments.Where(e => e.Name == exp.Name); + ExperimentJson[] experimentInfos = found as ExperimentJson[] ?? found.ToArray(); + if (experimentInfos.Length > 0) + { + MissingExperiments.Remove(experimentInfos[0]); + EnabledExperiments.Add(exp); + } + AddedExperiments.Add(exp); + foreach (ExperimentSelectorInfo esi in exp.Options) + { + ExperimentSelectorInfos.Add(esi, exp); + } + } + + public static void Toggle(this ExperimentSelectorInfo exp) + { + if (!ExperimentSelectorInfos.TryGetValue(exp, out ExperimentInfo EI)) return; + if (Settings.Experiments.Any(e => e.Name == EI.Name)) + { + ExperimentJson ej = Settings.Experiments.Where(e => e.Name == EI.Name).First(); + if (EI.Selected == EI.Options.IndexOf(exp)) + { + Settings.Experiments.Remove(ej); + EnabledExperiments.Remove(EI); + EI.Selected = null; + LastExpCount--; + exp.SendTog(false); + } + else + { + EI.Options[EI.Selected!.Value].SendTog(false); + ej.Selected = EI.Options.IndexOf(exp); + EI.Selected = ej.Selected; + exp.SendTog(true); + } + } + else + { + LastExpCount++; + ExperimentJson ej = new() + { + Name = EI.Name, + Selected = EI.Options.IndexOf(exp) + }; + Settings.Experiments.Add(ej); + EI.Selected = ej.Selected; + if (!EnabledExperiments.Contains(EI)) EnabledExperiments.Add(EI); + exp.SendTog(true); + } + Settings.SaveSettings(Path.Combine(Globals.LuskiPath, "Settings.json"), SettingsContext.Default.Settings); + } + + public static bool IsEnabled(this ExperimentSelectorInfo exp) + { + if (!ExperimentSelectorInfos.TryGetValue(exp, out ExperimentInfo EI)) return false; + if (LastExpCount != Settings.Experiments.Count) + { + //rescan + foreach (ExperimentJson experiment in Settings.Experiments) + { + IEnumerable found = AddedExperiments.Where(e => e.Name == experiment.Name); + ExperimentInfo[] experimentInfos = found as ExperimentInfo[] ?? found.ToArray(); + if (experimentInfos.Count() > 0) + { + if (!EnabledExperiments.Contains(EI)) EnabledExperiments.Add(experimentInfos[0]); + experimentInfos[0].Selected = experiment.Selected; + } + else + { + MissingExperiments.Add(experiment); + } + } + + LastExpCount = Settings.Experiments.Count; + } + + //detect + return EnabledExperiments.Contains(EI) && EI.Selected.HasValue && EI.Options.Count > EI.Selected && + EI.Options[EI.Selected.Value] == exp; + } + + private static Dictionary> TextureResources = new(); + + public static Texture GetTextureResource(this TextureManager tm, string File) + { + if (!TextureResources.ContainsKey(tm)) TextureResources.Add(tm, new()); + if (TextureResources[tm].TryGetValue(File, out Texture? t)) return t; + t = tm.AddTexture(Tools.GetResourceStream(Assembly.GetExecutingAssembly(), + $"Luski.Resources.Textures.{File}")); + TextureResources[tm].Add(File,t); + return t; + } public static void AddBehindObject(this Window w, IRenderObject obj) { @@ -57,7 +160,6 @@ public static class Globals public static bool Download { get; set; } = false; public static API Luski { get; } = new(); public static MainScreenWindow ms; - private static Texture? gac; public static Color4 ToColor4(this Color col) { @@ -66,13 +168,7 @@ public static class Globals public static Texture GetAlphaCircle(this TextureManager tm) { - if (gac is null) - { - gac = tm.AddTexture(Tools.GetResourceStream(Assembly.GetExecutingAssembly(), - "Luski.Resources.Textures.Status.png")); - } - - return gac; + return tm.GetTextureResource("Status.png"); } public static Dictionary UserTextureMap = new(); @@ -144,6 +240,11 @@ public static class Globals return @out; } + public static void SaveSettings(this TResult json, string path, JsonTypeInfo TypeInfo) where TResult : new() + { + File.WriteAllText(path, JsonSerializer.Serialize(json, TypeInfo)); + } + public static string JT { get diff --git a/Luski/Luski.csproj b/Luski/Luski.csproj index a0f927e..1dc1be8 100644 --- a/Luski/Luski.csproj +++ b/Luski/Luski.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 enable enable 1.1.0.1 @@ -19,7 +19,7 @@ - + diff --git a/Luski/LuskiExperiments.cs b/Luski/LuskiExperiments.cs new file mode 100644 index 0000000..f9b94a6 --- /dev/null +++ b/Luski/LuskiExperiments.cs @@ -0,0 +1,78 @@ +using Luski.Classes; + +namespace Luski; + +public static class LuskiExperiments +{ + public static List LuskiExperimentsList = new() + { + Parents.MainServer, + Parents.ThemeEdit, + new() + { + DisplayName = "Server Member List", + Name = "2023_12_member_list", + Options = new() + { + ServerExperiments.MemberList + } + } + }; + + public static class Parents + { + public static ExperimentInfo MainServer = new() + { + DisplayName = "Main Servers", + Name = "2023_12_main_servers", + Options = new() + { + MainServers.EnableLuskiMainServers, + MainServers.EnableMainServers, + } + }; + public static ExperimentInfo ThemeEdit = new() + { + DisplayName = "Theme Editor", + Name = "2023_12_theme_edit", + Options = new() + { + Settings.Theme, + } + }; + } + + public static class Settings + { + public static readonly ExperimentSelectorInfo Theme = new() + { + Name = "Appearance Tab", + Description = "Adds a tab to edit the theme options.", + }; + } + + public static class MainServers + { + public static readonly ExperimentSelectorInfo EnableMainServers = new() + { + Name = "Enable Main Servers", + Description = "Will allow the Luski client to connect to all main servers.", + RequiresRestart = true, + }; + public static readonly ExperimentSelectorInfo EnableLuskiMainServers = new() + { + Name = "Enable Luski Main Servers", + Description = "Will force the Luski client to connect to Luski main servers.", + RequiresRestart = true, + }; + } + + public static class ServerExperiments + { + public static readonly ExperimentSelectorInfo MemberList = new() + { + Name = "Member List", + Description = "Adds a list on the side of a chat that shows members.",RequiresRestart = false + }; + } +} \ No newline at end of file diff --git a/Luski/Program.cs b/Luski/Program.cs index 186f587..07dc05e 100644 --- a/Luski/Program.cs +++ b/Luski/Program.cs @@ -2,42 +2,21 @@ using System.Reflection; using GraphicsManager; using Luski; -using Luski.Clesses; +using Luski.Classes; using Luski.GUI; -using OpenTK.Graphics.OpenGL; -using OpenTK.Mathematics; using OpenTK.Windowing.Common.Input; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using Image = OpenTK.Windowing.Common.Input.Image; -using Rectangle = GraphicsManager.Objects.Rectangle; try { - /*Window w = new(); - Rectangle r, rr; - w.Size = new(1000); - r = new() - { - Location = new(250,250,0), - Size = new(500), - BackgroundColor = Color4.Green - }; - GL.Enable(EnableCap.DepthTest); - // GL.DepthFunc(DepthFunction.Notequal); - rr = new() - { - Location = new(500,500,-1), - Size = new(500), - BackgroundColor = Color4.Blue - }; - w.Controls.Add(rr); - w.Controls.Add(r); - w.StartRender();*/ - - - Globals.Settings = Globals.GetSettings(Path.Combine(Globals.LuskiPath, "Settings.json"), SettingsContext.Default.Settings); + foreach (ExperimentInfo le in LuskiExperiments.LuskiExperimentsList) + { + Globals.RegisterExperiment(le); + } + ServerList serverlist = Globals.GetSettings(Path.Combine(Globals.LuskiPath, "Servers.json"), ServerListContext.Default.ServerList); List> bbb = new(); if (serverlist.Server.Length > 0) @@ -50,15 +29,12 @@ try bbb.Add(Globals.Luski.TryGetPublicServer(out _,server.Domain, server.Version, server.Secure)); break; case true: - Globals.Luski.GetMainServer(server.Domain, server.Version); + if (LuskiExperiments.MainServers.EnableMainServers.IsEnabled()) Globals.Luski.GetMainServer(server.Domain, server.Version); break; } } } - - //var ttttt = GraphicsManager.Tools.GetFamilyList("fc-list ':' family"); - //var ffff = GraphicsManager.Tools.GetFontList($"fc-list ':'family='{ttttt[new Random().Next(0, ttttt.Length)]}' file"); - + Globals.UpdaterSettings = Globals.GetSettings(Path.Combine(Globals.LuskiPath, "UpdaterSettings.json"), UpdaterSettingsContext.Default.UpdaterSettings); //Globals.Luski.LoadedServers.First().CreateAccount("JacobTech", "JacobGuin12173*", "JacobTech", "/home/jacob/Pictures/Logo.png", CancellationToken.None); Task.WaitAll(bbb.ToArray()); diff --git a/Luski/Resources/Textures/RoundedRectangleBottom.png b/Luski/Resources/Textures/RoundedRectangleBottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8287e0841ab911d8230ca943f3bf8f9a5d743347 GIT binary patch literal 1073 zcmeAS@N?(olHy`uVBq!ia0y~yU~B-g8#vg2B-4g{+(3$@*vT`50|;t3QaTtIm|u9h zIEGZrd3$%SpLC`~+r#T?TrMey1WG;?SaIllAj?U2$LaI5ZnZft*tlY)W57jc<|U^X zm&{r@q4kbR-X2%6BTjW3jMayWV|!KmQ}T2ueiq&O z%wWJc-@bm`&!0bMN%p#J-M!oTZ6(8u33u<_mFjhiH*b=amG!NbV^DkYYR>(Yt3Iv{ zU;pay@&2tlc39k9$xspGd-=+xOF_JB&Fi%r4kiTrt*gIP%=qERufKIbu~omySp!0^ zzAbzG=FJ%`h6gi&P6-POdzJP7`q#4$Zf)Dj#ZYk!sPyigKh=AgR%`-t*X7m!Ucal< z`o*@~Y#?~l%=lr;wr$&9eOp`qZ`G>(`F4d>RZ;8X_pdY0zvok6!Z7dDxpRJd>;L~d z_xb-%&KJu*e=Zhe_;I-Q$A^W}rcd8r`DQocid9nm`>Qz`>>ZY0{`qdB{6E!{<(F6f z{rlHlo}*#;*8ThcAD&!))%Kt8j_a?lK700TF)!(&`z5Vvuv14wzIXO4989vxw zH1YlV`>(b1-nwVgnF?Gv7?!bey07 literal 0 HcmV?d00001 diff --git a/Luski/Resources/Textures/RoundedRectangleTop.png b/Luski/Resources/Textures/RoundedRectangleTop.png new file mode 100644 index 0000000000000000000000000000000000000000..bc72e937437fe0c46cbdc6cb46691df50993c304 GIT binary patch literal 1063 zcmeAS@N?(olHy`uVBq!ia0y~yU~B-g8#vg2B-4g{+(3$@*vT`50|;t3QaTtInD2SI zIEGZrd3$%YUre^d@rUI%PKq#lHb=eY3gF;dCf?A*(z|extm6k!)7xLf1R5Bc-4=XU zaEnVXX`zR(0gG|;n>?XgzWh`8c>g~)oV;dh!roWE&ziUUtY6jkmEqUlI$d4etKH)I zt8#B=&3g8%Fe&MgVjiQw<4>P9ojZ4K)$3iek58UF*~-Rd&Fj~%*>}3HuTx|=Ki_`+ z(W6JBVq(@@y?RwjUcP?9LWUpp;o;$1_wSE)cXux>DG8ZxSG()`|6Sn>vzDzqm=Mr> zaKWu@d%vG$+_8V%>#XeT)!Vjhd-dkco8|xiEoVx2$o}K=9dj0YnTD@lzgGSJwzgIN z0sqy~*uB-?*O{4{*T48B&voG2x3axeUtfi%GbgZ=m6g5v`uh6)z3+B2{`m0F`RuuK zc2{B;dJY`#m(R}2dpG-fjWNUFhs`xkA`R}BUVnXOV_tT>v$?tXz7Y?@;?HZh{#n23 z*YDHK`S7W1(jc=2cZv+wh4tIhW8*s-E+|Nj%pj0RlSuU|K{w2VyF*47rQZ&3JKw?8Z_ zEGs85@!`gF#)KncVq#fmX76XH1RA$-S*BbyAA{SPOet%I2AAC1VecP3O0qCC{CHzC zgMriZ>C>-D|GKH);8FfhNrJ(DHUmS85JN)%6N7*k1A`MMgM$Vm1BWUD!$KAYg(;(w f)D46c^$c&OY-ZbbX|)?L=QDV^`njxgN@xNAQ|gap literal 0 HcmV?d00001 diff --git a/Luski/Resources/Textures/Textbox.png b/Luski/Resources/Textures/Textbox.png new file mode 100644 index 0000000000000000000000000000000000000000..67cf0865c0c740262132f093111dd7970e292efc GIT binary patch literal 1522 zcmeHH|5MU;7=Pn8EES^Y&{o#jN>4p)SK-PU&hbV20@9hLp*Bq|HHBa(YVujFOjF4& z(M_{Wu4CrcW`^bz$}e=oEani+G=q%FDKg4HMfRcn8+(3uKks`zch5b~>t3&?D1u6H zTfJd50KhFgjCce91O)#fXD9fzYwdA?lLI?6JklACeCLyw0dTAjCw?87Q!_gurL8-+ zQD&g=u01?|X~#rALH6Lw`Q8TvsA&6*IL=cPIW_#yVB5U+n+Olix0hsnC>Mg0cJmY` zsGNNNMCDf(LLD423RR(grhQju@UtfzeL)`M_?62At|8s=0<+oN!M7H?;(5jhHxbGN z0<>luxTc)tiu@Hm#TpYUJ>73atrn?g++OWYGawOFRaKWeJ3FhYtFIu@o_(XEqal~< zrgSJYI*G^IgU90&lajXXy)ks_=FAbqqu=~vo1*T{fjmb3rVtG3~1#7D+dpJ zTkqb*(rWjar&&^0+1y;_p~HvW(gI*yBEbz059>VHnPAcK{c@oX_~uB1YUp{;*^#n} ziV8muC+J;Gc7WB}Yh{18q5lfb@b_4o-^Nvt@AIB>!L|k1Y-8a$LcYWM`Io&pALrso zB1FVRiommf>7$;O5sZQS6^W^-a%rv|V<(B;H#j(0wB$f#*fTHSWFM~Uc<`d@#>=9? zq{W_*C5}eO{eXT2(JUEmfMSX7y}iA)y@hByzte@CT8t z^r=#Cjq^J2Jc8N13WU@-fGu=8aMcF^!mb1G`2hgdZUdlbEr5vsmp{?+SD0jc(}F9A zY#g5*2#XPZVz?sNV=Ia7nuh|uQP_*?K}c~U%-m5dsU+jf88;hzuf2VJdv5onb=k}= zDJ^x&^8~)koE-O{tjUt{_!gW(q3EqeLdE`pfs_{YQtsB#fq{Wm9pYy%if-8^o$cE- zu>Q++W~SI~>VQo?bI*&YV&iv?Pv3ItCNj}^0igL<*F+grybkjs3SclX!v3M5vZFw@ z8y*B!YM;`QAWv|L7T&U9BbgHIQZ-)?4KPs%pjN%jwqnFRsQdnkV+=-P{tnPQqR}MQ zth~F)_tL!32W4bs$(xQ#64TOdKU9Ja--8Da8s4cIHs8Bxa(8E2jL_tu{rd_`F{b>rGyp$ literal 0 HcmV?d00001 diff --git a/Luski/Resources/Textures/close.png b/Luski/Resources/Textures/close.png new file mode 100644 index 0000000000000000000000000000000000000000..f65f3de7f746b74c9e933c0418abbafe38a0c351 GIT binary patch literal 492 zcmV~ z<&>2yC$KxSKVv5k=iHC`qCMP~?UJA<3YX`(-&xzXxp|)bzATIX{1HK#rfwJp4{D#+ zb$xt?s4Ppr-zI3=)^%NX0F2{!WD9{7e(cZZ)BE&&e@zgAv)dpDl`Vu-Re3+fSpPQy zvXLWYU-cIv0H$>{Z*}1$C&~s(3c2It-$7&G-yi*lmWLb75fL$t4nSjSgLILoZ zcT^#zu~fxDkR-{ehXh&@RjOq4O!AeiPXvI(dTdE}Ch)Ko82!h1uS5?5>D><2lCWan z^0mp{zA6X~14VS3Kmax{9B2p-Q14gt(1OdvC$B{~KwBVKm!~fP@BoZ(g{7IOg;E+~;GHBHyLqDo;SDJT=@N|DM?W|Uq+MagI_;|FUq6`w iQf8qt{$gMMSMdTbI)DmL*br#|0000Z$4(;8>Rx_18<~)H}g*f zEX!g(pQGJwllgYLg=v~d)0AFNtyU355o)#CS8OmCK$0Xho6SN=p(ll=k8j!+bv3`D>B0{^n%T115MLzuKB)CFR&~Ns;cJda5x-b+cv!*>k?5E zu~;lfn_ey#1VKRB^mIDW3o?=h*dqmJ>YlI`45S_xJw;niD)F_-X#W;0oyUVo>qDoIn5o002ov JPDHLkV1g2$s+<4- literal 0 HcmV?d00001 diff --git a/Luski/Resources/Textures/settings.png b/Luski/Resources/Textures/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..00b702712a241677ae0e6b8c3acef9f93ec73905 GIT binary patch literal 487 zcmVtPtCeaFRqiv*J6`Fw`DuEYI)U%VY>ecy-E=@hQlYgV=*c2Md#@RLF4>&N4f zVDKdd7fT384<`?X11aBbw~y2bz|EVcnf$N>p*AvZ%Fi+Qs3E{71l)#{A;K9jneg@CP9iDfr~PaVFi8Vg8m{%b zy3I*FctU{1eY|GW!+j*tA^|$8lLV%i1jsofk()BlcF~fK0Fgl*BJY#E z_CcU5H2(gO`S7?@>@m4&=YQK&#gWD;u+M|m!b-l}%xsziwdpvHtGKvMS%ST76G8lH d?oRNH{Q>)DEkeVYMN$9&002ovPDHLkV1nV9-o^j` literal 0 HcmV?d00001