using System; using System.Linq; using FlaxEditor; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace Game; public class ConsoleScript : Script { public Color BackgroundColor; public Texture BackgroundTexture; private ConsoleContentTextBox consoleBox; public FontAsset ConsoleFont; [Limit(5, 720)] public int ConsoleFontSize = 16; [Limit(0.05f, 1.0f)] public float ConsoleHeight = 0.65f; private ConsoleInputTextBox consoleInputBox; internal InputEvent consoleInputEvent; private ConsoleContentTextBox consoleNotifyBox; [Limit(0)] public int ConsoleNotifyLines = 15; [Limit(0f)] public float ConsoleSpeed = 3500f; private UIControl rootControl; private int fontHeight; public override void OnStart() { consoleInputEvent = new InputEvent("Console"); consoleInputEvent.Pressed += OnConsoleInputEvent; FontReference fontReference = new FontReference(ConsoleFont, ConsoleFontSize); Font fontRaw = fontReference.GetFont(); fontHeight = (int)(fontRaw.Height / Platform.DpiScale); // root actor which holds all the elements //var rootContainerControl = new ContainerControl(new Rectangle(0, 0, screenSize.X, screenSize.Y)); ContainerControl rootContainerControl = new ContainerControl(new Rectangle()); rootContainerControl.SetAnchorPreset(AnchorPresets.StretchAll, false); rootControl = Actor.AddChild(); rootControl.Name = "ConsoleRoot"; rootControl.Control = rootContainerControl; VerticalPanel contentContainer = new VerticalPanel { AutoSize = true, Margin = Margin.Zero, Spacing = 0, Bounds = new Rectangle(), BackgroundColor = BackgroundColor }; contentContainer.SetAnchorPreset(AnchorPresets.StretchAll, true); UIControl contentContainerControl = rootControl.AddChild(); contentContainerControl.Name = "ContentContainer"; contentContainerControl.Control = contentContainer; { if (consoleBox == null) { //consoleBox = new ConsoleContentTextBox(null, 0, 0, consoleSize.X, consoleSize.Y - fontHeight); consoleBox = new ConsoleContentTextBox(fontReference, null, 0, 0, 0, 0); consoleBox.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, true); //consoleBox.AnchorMax = new Float2(1.0f, ConsoleHeight); //consoleBox.Height = consoleSize.Y - fontHeight; //consoleBox.HorizontalAlignment = TextAlignment.Near; //consoleBox.VerticalAlignment = TextAlignment.Near; consoleBox.HeightMultiplier = ConsoleHeight; consoleBox.Wrapping = TextWrapping.WrapWords; consoleBox.BackgroundColor = Color.Transparent; consoleBox.BackgroundSelectedColor = Color.Transparent; consoleBox.BackgroundSelectedFlashSpeed = 0; consoleBox.BorderSelectedColor = Color.Transparent; consoleBox.CaretFlashSpeed = 0; } Float2 locationFix = consoleBox.Location; UIControl parentControl = contentContainerControl.AddChild(); parentControl.Name = "ConsoleContent"; parentControl.Control = consoleBox; consoleBox.Location = locationFix; // workaround to UIControl.Control overriding the old position if (consoleNotifyBox == null) { //consoleBox = new ConsoleContentTextBox(null, 0, 0, consoleSize.X, consoleSize.Y - fontHeight); consoleNotifyBox = new ConsoleContentTextBox(fontReference, null, 0, 0, 0, 0); consoleNotifyBox.HeightMultiplier = 0; consoleNotifyBox.Height = ConsoleNotifyLines * fontHeight; consoleNotifyBox.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, true); //consoleBox.AnchorMax = new Float2(1.0f, ConsoleHeight); //consoleBox.HorizontalAlignment = TextAlignment.Near; //consoleBox.VerticalAlignment = TextAlignment.Near; //consoleNotifyBox.HeightMultiplier = ConsoleHeight; consoleNotifyBox.Wrapping = TextWrapping.WrapWords; consoleNotifyBox.BackgroundColor = Color.Transparent; consoleNotifyBox.BackgroundSelectedColor = Color.Transparent; consoleNotifyBox.BackgroundSelectedFlashSpeed = 0; consoleNotifyBox.BorderSelectedColor = Color.Transparent; consoleNotifyBox.CaretFlashSpeed = 0; consoleNotifyBox.SelectionAllowed = false; } Float2 locationFix2 = consoleNotifyBox.Location; UIControl parentControl2 = Actor.AddChild(); parentControl2.Name = "ConsoleNotifyContent"; parentControl2.Control = consoleNotifyBox; consoleNotifyBox.Location = locationFix2; // workaround to UIControl.Control overriding the old position } { if (consoleInputBox == null) { //consoleInputBox = new ConsoleInputTextBox(consoleBox, 0, consoleSize.Y - fontHeight, consoleSize.X, fontHeight); consoleInputBox = new ConsoleInputTextBox(consoleBox, 0, 0, 0, 0); consoleInputBox.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false); //consoleInputBox.Location = new Float2(0, consoleSize.Y - fontHeight); consoleInputBox.Height = fontHeight; consoleInputBox.Font = fontReference; consoleBox.inputBox = consoleInputBox; consoleInputBox.Wrapping = TextWrapping.WrapWords; consoleInputBox.BackgroundColor = Color.Transparent; consoleInputBox.BackgroundSelectedColor = Color.Transparent; consoleInputBox.BackgroundSelectedFlashSpeed = 0; consoleInputBox.BorderSelectedColor = Color.Transparent; consoleInputBox.CaretFlashSpeed = 0; } Float2 locationFix = consoleInputBox.Location; UIControl parentControl = contentContainerControl.AddChild(); parentControl.Name = "ConsoleInput"; parentControl.Control = consoleInputBox; consoleInputBox.Location = locationFix; // workaround to UIControl.Control overriding the old position } /*FlaxEditor.Editor.Options.OptionsChanged += (FlaxEditor.Options.EditorOptions options) => { };*/ /*Console.Print("normal line"); Console.Print( "a very very very long long long line in repeat a very very very long long long line in repeat 1 a very very ver" + "y long long long line in repeat a very very very 2 long long long line in repeat a very very very 3 long long" + " long line in repeat a very very very long long long 4 line in repeat"); Console.Print("another normal line");*/ //for (int i=0; i<10000; i++) // Debug.Log("a very very very long long long line in repeat a very very very long long long line in"); Debug.Logger.LogHandler.SendLog += OnSendLog; Debug.Logger.LogHandler.SendExceptionLog += OnSendExceptionLog; Console.OnOpen += OnConsoleOpen; Console.OnClose += OnConsoleClose; Console.OnPrint += OnPrint; // hide console by default, and close it instantly Console.Close(); OnConsoleClose(); Float2 rootlocation = rootControl.Control.Location; rootlocation.Y = -rootControl.Control.Height; rootControl.Control.Location = rootlocation; //Console.Print("Renderer: " + GPUDevice.Instance.RendererType); } private void OnSendLog(LogType level, string msg, Object obj, string stackTrace) { Console.Print("[DEBUG] " + msg); } private void OnSendExceptionLog(Exception exception, Object obj) { AssertionException assert = exception as AssertionException; if (assert != null) { string[] assertLines = assert.Message.Split('\n'); if (assertLines.Length > 2) Console.Print("Assert Failure: " + assertLines[2]); else Console.Print("Assert Failure: " + assert.Message); } else { Console.Print("[EXCEP] " + exception.ToString()); } } public override void OnDestroy() { base.OnDestroy(); //consoleInputEvent.Triggered -= OnConsoleInputEvent; consoleInputEvent?.Dispose(); consoleBox?.Dispose(); consoleNotifyBox?.Dispose(); Console.OnOpen -= OnConsoleOpen; Console.OnClose -= OnConsoleClose; Console.OnPrint -= OnPrint; Debug.Logger.LogHandler.SendLog -= OnSendLog; Debug.Logger.LogHandler.SendExceptionLog -= OnSendExceptionLog; } private void OnConsoleInputEvent() { string currentInput = Input.InputText; if (Input.InputText.Length > 0) { // TODO: Remove when engine has support for binding to physical keys/scancode instead of virtual ones var consoleKeys = Input.ActionMappings.Where(x => x.Name == "Console" && x.Key != KeyboardKeys.None); bool backslash = consoleKeys.Any(x => x.Key == KeyboardKeys.Backslash); bool backquote = consoleKeys.Any(x => x.Key == KeyboardKeys.BackQuote); // Workaround to only trigger Console key from key bound to left side of 1 (tilde/backquote/backslash key) if ((backslash || backquote) && (Input.InputText.ToLowerInvariant().Contains('ö') || Input.InputText.ToLowerInvariant().Contains('æ') || Input.InputText.ToLowerInvariant().Contains('ø'))) // Scandinavian keyboard layouts return; if (backquote && Input.InputText.ToLowerInvariant().Contains('\'')) // UK keyboard layouts return; if (backslash && (Input.InputText.ToLowerInvariant().Contains('\\') || Input.InputText.ToLowerInvariant() .Contains('|'))) // US/International keyboard layouts { return; } } if (!consoleInputBox.IsFocused) Console.Open(); else Console.Close(); } public void OnConsoleOpen() { Screen.CursorVisible = true; Screen.CursorLock = CursorLockMode.None; consoleInputBox.Focus(); Parent.As().ReceivesEvents = true; } public void OnConsoleClose() { Screen.CursorVisible = false; Screen.CursorLock = CursorLockMode.Locked; consoleInputBox.Defocus(); #if FLAX_EDITOR Editor.Instance.Windows.GameWin.Focus(); #endif Parent.As().ReceivesEvents = false; } public override void OnUpdate() { #if !USE_EDITOR consoleNotifyBox.Width = consoleBox.Width; #endif if (!Console.IsOpen && Input.GetAction("ClearConsole")) Console.Clear(); float targetY; float conHeight = rootControl.Control.Height /*/ Platform.DpiScale*/; if (!Console.IsOpen) targetY = -conHeight; else targetY = 0.0f; Float2 location = rootControl.Control.Location; if (location.Y != targetY) { if (location.Y > targetY) { // closing location.Y -= Time.UnscaledDeltaTime * ConsoleSpeed; if (location.Y < targetY) location.Y = targetY; if (location.Y < targetY * ConsoleHeight) location.Y = targetY; } else if (location.Y < targetY) { // opening if (location.Y < -conHeight * ConsoleHeight) location.Y = -conHeight * ConsoleHeight; location.Y += Time.UnscaledDeltaTime * ConsoleSpeed; if (location.Y > targetY) location.Y = targetY; } rootControl.Control.Location = location; if (Console.IsOpen) { consoleNotifyBox.Visible = false; consoleInputBox.Visible = true; } else if (!Console.IsOpen) { if (location.Y < -conHeight * ConsoleHeight + fontHeight) { consoleNotifyBox.Visible = true; consoleInputBox.Visible = false; } } } } public void OnPrint(string text) { consoleNotifyBox.Height = Math.Min(ConsoleNotifyLines, Console.Lines.Length) * fontHeight; } public void SetInput(string text) { consoleInputBox.Text = text; } }