diff --git a/SkEditor+ Installer/Release/setup.exe b/SkEditor+ Installer/Release/setup.exe index 35f70d2..6cc3dc7 100644 Binary files a/SkEditor+ Installer/Release/setup.exe and b/SkEditor+ Installer/Release/setup.exe differ diff --git a/SkEditorPlus/Data/CompletionData.cs b/SkEditorPlus/Data/CompletionData.cs index 50ac8f3..8726f78 100644 --- a/SkEditorPlus/Data/CompletionData.cs +++ b/SkEditorPlus/Data/CompletionData.cs @@ -1,55 +1,43 @@ -using AvalonEditB.CodeCompletion; -using AvalonEditB.Document; -using AvalonEditB.Editing; -using SkEditorPlus.Windows.Generators; +using HandyControl.Controls; using System; using System.Collections.Generic; -using System.Windows; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; namespace SkEditorPlus.Data { - public class CompletionData : ICompletionData + public class CompletionData { - public CompletionData(string text, string description = "Brak opisu") - { - Text = text; - Description = description; - } - public System.Windows.Media.ImageSource Image + public static List completionList = new() { - get { return null; } - } - - public string Text { get; private set; } - - public object Content - { - get { return this.Text; } - } - - public object Description - { get; private set; } - - double ICompletionData.Priority => 1; - - public void Complete(TextArea textArea, ISegment completionSegment, - EventArgs insertionRequestEventArgs) - { - var caretOffset = textArea.Caret.Offset; - var line = textArea.Document.GetLineByOffset(caretOffset); - textArea.Document.Replace(line.Offset, caretOffset - line.Offset, ""); - - CommandGenerator commandGenerator = new(GetMainWindow().skEditor); - commandGenerator.ShowDialog(); - } - public static MainWindow GetMainWindow() + new CompletionDataElement("command", "command /{c}:\n\t"), + new CompletionDataElement("commandgen"), + new CompletionDataElement("options", "options:\n\t"), + new CompletionDataElement("variables", "variables:\n\t"), + new CompletionDataElement("trigger", "trigger:\n\t"), + new CompletionDataElement("if", "if {c}:"), + new CompletionDataElement("ifelse", "if {c}:\n\t\nelse:\n\t"), + new CompletionDataElement("else"), + new CompletionDataElement("send", "send \"{c}\""), + new CompletionDataElement("sendallp", "send \"{c}\" to all players"), + }; + + public static ListBoxItem[] GetCompletionData(string word) { - List windowList = new(); - foreach (Window window in System.Windows.Application.Current.Windows) - windowList.Add(window); - return (MainWindow)windowList.Find(window => window.GetType() == typeof(MainWindow)); + List completions = new(); + foreach (var item in completionList) + { + if (item.Name.StartsWith(word)) + { + //if (item.Name.Equals(word)) continue; + completions.Add(new ListBoxItem { Content = item.Name }); + } + } + return completions.ToArray(); } } -} \ No newline at end of file +} diff --git a/SkEditorPlus/Data/CompletionDataElement.cs b/SkEditorPlus/Data/CompletionDataElement.cs new file mode 100644 index 0000000..e33a3bc --- /dev/null +++ b/SkEditorPlus/Data/CompletionDataElement.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkEditorPlus.Data +{ + public class CompletionDataElement + { + public string Name { get; set; } + public string Description { get; set; } + + public string Word { get; set; } + + public CompletionDataElement(string name, string word = "none", string description = "none") + { + Name = name; + + if (!description.Equals("none")) + { + Description = description; + } + + if (!word.Equals("none")) + { + Word = word; + return; + } + Word = word; + } + } +} diff --git a/SkEditorPlus/MainWindow.xaml b/SkEditorPlus/MainWindow.xaml index d7bc0de..966a920 100644 --- a/SkEditorPlus/MainWindow.xaml +++ b/SkEditorPlus/MainWindow.xaml @@ -15,7 +15,7 @@ WindowStartupLocation="CenterScreen" WindowState="Maximized" mc:Ignorable="d" - MaxHeight="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}" Closing="OnClosing"> + MaxHeight="{x:Static SystemParameters.MaximizedPrimaryScreenHeight}" Closing="OnClosing" SizeChanged="OnSizeChanged" StateChanged="Window_StateChanged"> diff --git a/SkEditorPlus/MainWindow.xaml.cs b/SkEditorPlus/MainWindow.xaml.cs index 288018d..d4e33a2 100644 --- a/SkEditorPlus/MainWindow.xaml.cs +++ b/SkEditorPlus/MainWindow.xaml.cs @@ -9,8 +9,11 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Interop; using System.Windows.Media; using Window = HandyControl.Controls.Window; @@ -29,11 +32,23 @@ public Menu GetMenu() return MenuBar; } - + [DllImport("user32.dll")] + static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd); + const uint GW_HWNDNEXT = 2; + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool IsWindowVisible(IntPtr hWnd); + + public event LoadFinishedEvent LoadFinished; - private static readonly string version = "1.4.0"; + private static readonly string version = "1.4.1"; public static string Version { get => version; } @@ -243,5 +258,32 @@ public void HandleNamedPipe_OpenRequest(string filesToOpen) } catch { } } + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + FixCut(); + } + + private void FixCut() + { + if (WindowState == WindowState.Maximized) + { + IntPtr hWnd = new WindowInteropHelper(Application.Current.MainWindow).Handle; + + IntPtr hNext = hWnd; + do + hNext = GetWindow(hNext, GW_HWNDNEXT); + while (!IsWindowVisible(hNext)); + + SetForegroundWindow(hNext); + + Activate(); + } + } + + private void Window_StateChanged(object sender, EventArgs e) + { + FixCut(); + } } } diff --git a/SkEditorPlus/Managers/CompletionManager.cs b/SkEditorPlus/Managers/CompletionManager.cs index 58f2970..ecd0246 100644 --- a/SkEditorPlus/Managers/CompletionManager.cs +++ b/SkEditorPlus/Managers/CompletionManager.cs @@ -1,96 +1,263 @@ using AvalonEditB; -using AvalonEditB.CodeCompletion; +using HandyControl.Controls; using SkEditorPlus.Data; +using SkEditorPlus.Windows; +using SkEditorPlus.Windows.Generators; +using System; using System.Collections.Generic; using System.Linq; using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; using System.Windows.Input; -using System.Windows.Markup; -using Window = System.Windows.Window; +using System.Windows.Media; namespace SkEditorPlus.Managers { public class CompletionManager { - static CompletionWindow completionWindow; - public static TextEditor textEditor; - public static void LoadCompletionManager(TextEditor textEditor) + private readonly SkEditorAPI skEditor; + + private static ListBoxItem[] completionList = System.Array.Empty(); + + private CompletionWindow completionWindow; + + private Popup popup; + + public static CompletionManager instance; + + public CompletionManager(SkEditorAPI skEditor) + { + instance = this; + this.skEditor = skEditor; + } + + public void LoadCompletionManager(TextEditor textEditor) { CompletionManager.textEditor = textEditor; - textEditor.TextArea.TextEntering += OnTextEntering; textEditor.TextArea.TextEntered += OnTextEntered; + textEditor.PreviewKeyDown += OnKeyDown; } - public static MainWindow GetMainWindow() - { - List windowList = new(); - foreach (Window window in System.Windows.Application.Current.Windows) - windowList.Add(window); - return (MainWindow)windowList.Find(window => window.GetType() == typeof(MainWindow)); - } - private static void OnKeyDown(object sender, KeyEventArgs e) + public void OnKeyDown(object sender, KeyEventArgs e) { - if (e.Key == Key.Back) + var caretOffset = textEditor.CaretOffset; + var line = textEditor.Document.GetLineByOffset(caretOffset); + var text = textEditor.Document.GetText(line.Offset, caretOffset - line.Offset); + if (textEditor.SelectedText.Equals(text)) text = ""; + else if (text.Length > 0) text = text.Remove(text.Length - 1); + + if (e.Key == Key.Back || e.Key == Key.Left) { - OnTextEntered(sender, new TextCompositionEventArgs(InputManager.Current.PrimaryKeyboardDevice, new TextComposition(InputManager.Current, textEditor, e.Key.ToString()))); + HidePopup(); + ShowCompletionWindow(text); + } + + else if (e.Key == Key.Right) + { + if (text.Length > 0) + { + try + { + var caretText = textEditor.Document.GetText(line.Offset, caretOffset - line.Offset + 1); + HidePopup(); + ShowCompletionWindow(caretText); + } + catch { } + } + } + + else if (e.Key == Key.Down && completionWindow != null) + { + int selectedIndex = completionWindow.completionList.SelectedIndex; + if (selectedIndex < completionWindow.completionList.Items.Count - 1) + { + completionWindow.completionList.SelectedIndex++; + completionWindow.completionList.ScrollIntoView(completionWindow.completionList.SelectedItem); + } + } + else if (e.Key == Key.Up && completionWindow != null) + { + int selectedIndex = completionWindow.completionList.SelectedIndex; + if (selectedIndex > 0) + { + completionWindow.completionList.SelectedIndex--; + completionWindow.completionList.ScrollIntoView(completionWindow.completionList.SelectedItem); + } + } + else if (e.Key == Key.Enter || e.Key == Key.Tab) + { + if (completionWindow == null) return; + if (!popup.IsOpen) return; + + int selectedIndex = completionWindow.completionList.SelectedIndex; + if (selectedIndex < 0) return; + + ListBoxItem item = (ListBoxItem)completionWindow.completionList.SelectedItem; + + var split = text.Split(' '); + var lastWord = split[^1].TrimStart(); + var index = text.LastIndexOf(lastWord); + var newText = text.Remove(index, lastWord.Length); + + switch (item.Content) + { + case "commandgen": + textEditor.Document.Replace(line.Offset, caretOffset - line.Offset, ""); + CommandGenerator commandGenerator = new(skEditor); + commandGenerator.ShowDialog(); + break; + + default: + CompletionDataElement element = null; + foreach (CompletionDataElement dataElement in CompletionData.completionList) + { + if (dataElement.Name.Equals(item.Content)) + { + element = dataElement; + break; + } + } + + + newText += element.Word; + + string[] lines = newText.Split("\n"); + string firstLine = lines[0]; + + int tabs = GetTabCount(firstLine); + + for (int i = 0; i < lines.Length; i++) + { + if (i == 0) continue; + lines[i] = new string('\t', tabs) + lines[i]; + } + + newText = string.Join("\n", lines); + + + if (element.Word.Contains("{c}")) + { + var caretIndex = newText.IndexOf("{c}"); + newText = newText.Replace("{c}", ""); + + textEditor.Document.Replace(line.Offset, caretOffset - line.Offset, newText); + + caretIndex = Math.Min(caretIndex, newText.Length); + textEditor.CaretOffset = line.Offset + caretIndex; + } + else + { + textEditor.Document.Replace(line.Offset, caretOffset - line.Offset, newText); + } + + break; + } + + completionWindow.completionList.SelectedIndex = -1; + completionWindow.completionList.ItemsSource = null; + completionWindow = null; + popup.IsOpen = false; + popup = null; + e.Handled = true; } } - static void OnTextEntered(object sender, TextCompositionEventArgs e) + private static int GetTabCount(string line) { - completionWindow = new CompletionWindow(textEditor.TextArea) - { - StartOffset = 0 - }; + return line.TakeWhile(c => c == '\t').Count(); + } - completionWindow.CompletionList.ListBox.ItemContainerStyle = (Style)Application.Current.FindResource("CompletionListStyle"); - completionWindow.CompletionList.ListBox.Background = System.Windows.Media.Brushes.Transparent; - completionWindow.Background = System.Windows.Media.Brushes.Transparent; - completionWindow.CompletionList.ListBox.BorderBrush = System.Windows.Media.Brushes.Transparent; - completionWindow.CompletionList.ListBox.BorderThickness = new Thickness(0); + private void OnTextEntered(object sender, TextCompositionEventArgs e) + { + string text = GetText(); + string lastWord = GetLastWord(text); + + if (IsInQuote(text, lastWord)) + { + return; + } - IList data = completionWindow.CompletionList.CompletionData; + HidePopup(); + ShowCompletionWindow(lastWord); + } + private string GetText() + { var caretOffset = textEditor.CaretOffset; var line = textEditor.Document.GetLineByOffset(caretOffset); - var wordBeforeCaret = textEditor.Document.GetText(line.Offset, caretOffset - line.Offset); - - if (wordBeforeCaret.ToLower().Equals("command")) - { - data.Add(new CompletionData("Command", "Otwiera generator komendy")); - } + return textEditor.Document.GetText(line.Offset, caretOffset - line.Offset); + } - else if (data.Count != 0) + private string GetLastWord(string text) + { + if (text.Contains(' ')) { - data.Remove(data.First()); + var split = text.Split(' '); + return split[^1]; } + return text; + } - if (data.Count != 0) - completionWindow.Show(); + private bool IsInQuote(string text, string lastWord) + { + string textBeforeLastWord = text[..^lastWord.Length]; + return textBeforeLastWord.Count(c => c == '"') % 2 == 1; + } - completionWindow.Closed += delegate + private void HidePopup() + { + if (popup != null) { - completionWindow = null; - }; - - completionWindow.CompletionList.ListBox.SelectedIndex = 0; + popup.IsOpen = false; + } } - static void OnTextEntering(object sender, TextCompositionEventArgs e) + private void ShowCompletionWindow(string lastWord) { - if (e.Text.Length > 0 && completionWindow != null) + lastWord = lastWord.TrimStart(); + + var completionList = CompletionData.GetCompletionData(lastWord); + + if (lastWord.Length <= 0 || completionList.Length <= 0) { - if (!char.IsLetterOrDigit(e.Text[0])) + return; + } + + completionWindow = new CompletionWindow + { + completionList = { - completionWindow.CompletionList.RequestInsertion(e); + ItemsSource = completionList } - } + }; + + popup = new Popup + { + PlacementTarget = textEditor, + Placement = PlacementMode.Absolute, + HorizontalOffset = -5, + VerticalOffset = -5, + AllowsTransparency = true, + StaysOpen = false, + Child = completionWindow, + IsOpen = true + }; + + var caret = textEditor.TextArea.Caret.CalculateCaretRectangle(); + var pointOnScreen = textEditor.TextArea.TextView.PointToScreen(caret.Location - textEditor.TextArea.TextView.ScrollOffset); + + popup.HorizontalOffset = pointOnScreen.X + 10; + popup.VerticalOffset = pointOnScreen.Y + 10; + + completionWindow.completionList.SelectedIndex = 0; } + } } \ No newline at end of file diff --git a/SkEditorPlus/Managers/FileManager.cs b/SkEditorPlus/Managers/FileManager.cs index 20c74f4..d9b8218 100644 --- a/SkEditorPlus/Managers/FileManager.cs +++ b/SkEditorPlus/Managers/FileManager.cs @@ -827,7 +827,9 @@ private void CreateFile(string header, string tooltip = null) tabControl.Items.Add(tabItem); ChangeGeometry(); - //CompletionManager.LoadCompletionManager(GetTextEditor()); + + CompletionManager completionManager = new(skEditor); + //completionManager.LoadCompletionManager(GetTextEditor()); } } } diff --git a/SkEditorPlus/Managers/RPCManager.cs b/SkEditorPlus/Managers/RPCManager.cs index b3425de..4337930 100644 --- a/SkEditorPlus/Managers/RPCManager.cs +++ b/SkEditorPlus/Managers/RPCManager.cs @@ -52,6 +52,9 @@ public static void SetFile(string name) { try { + client.UpdateDetails("Pracuje nad nową funkcją :)"); + return; + if (!Properties.Settings.Default.DiscordRPC) return; if (name.EndsWith("*")) name = name[..^1]; string file = (string)Application.Current.FindResource("DiscordRPCFile"); diff --git a/SkEditorPlus/SkEditorPlus.csproj b/SkEditorPlus/SkEditorPlus.csproj index 939d8fc..0538dc3 100644 --- a/SkEditorPlus/SkEditorPlus.csproj +++ b/SkEditorPlus/SkEditorPlus.csproj @@ -99,6 +99,9 @@ Code + + Code + diff --git a/SkEditorPlus/SkEditorPlus.csproj.user b/SkEditorPlus/SkEditorPlus.csproj.user index e28243f..c1f3385 100644 --- a/SkEditorPlus/SkEditorPlus.csproj.user +++ b/SkEditorPlus/SkEditorPlus.csproj.user @@ -86,6 +86,9 @@ Designer + + Designer + Designer diff --git a/SkEditorPlus/Styles/CompletionListStyle.xaml b/SkEditorPlus/Styles/CompletionListStyle.xaml index bcbf70a..79d544f 100644 --- a/SkEditorPlus/Styles/CompletionListStyle.xaml +++ b/SkEditorPlus/Styles/CompletionListStyle.xaml @@ -7,8 +7,8 @@ + TextBlock.Foreground="White" + TextBlock.FontFamily="Cascadia Mono"> diff --git a/SkEditorPlus/Windows/CompletionWindow.xaml b/SkEditorPlus/Windows/CompletionWindow.xaml new file mode 100644 index 0000000..c801244 --- /dev/null +++ b/SkEditorPlus/Windows/CompletionWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/SkEditorPlus/Windows/CompletionWindow.xaml.cs b/SkEditorPlus/Windows/CompletionWindow.xaml.cs new file mode 100644 index 0000000..3ce0fc9 --- /dev/null +++ b/SkEditorPlus/Windows/CompletionWindow.xaml.cs @@ -0,0 +1,28 @@ +using SkEditorPlus.Managers; +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace SkEditorPlus.Windows +{ + public partial class CompletionWindow : UserControl + { + + public CompletionWindow() + { + InitializeComponent(); + } + + private void OnCompletionListKeyDown(object sender, KeyEventArgs e) + { + CompletionManager.instance.OnKeyDown(sender, e); + } + } +} diff --git a/SkEditorPlus/Windows/FormattingWindow.xaml.cs b/SkEditorPlus/Windows/FormattingWindow.xaml.cs index ea9afae..79c41ed 100644 --- a/SkEditorPlus/Windows/FormattingWindow.xaml.cs +++ b/SkEditorPlus/Windows/FormattingWindow.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Windows.Shapes; using AvalonEditB.Document; +using System.Text; namespace SkEditorPlus.Windows { @@ -58,6 +59,50 @@ private void FixDotVariables() code = code.Replace(variableMatch.Value, variable); } textEditor.Document.Text = code; + + + return; + + string valuePattern = @"%.+?%"; + string optionalPattern = @"\[.*?\]"; + string groupPattern = @"\(([^)]+)\)"; + string optionalGroupPattern = @"\[\(([^)]+)\)\]"; + + Regex patternCompiler = new( + "(?" + valuePattern + ")" + + "|(?" + optionalPattern + ")" + + "|(?" + groupPattern + ")" + + "|(?" + optionalGroupPattern + ")", + RegexOptions.Compiled + ); + + string input = textEditor.Text; + + MatchCollection matches = patternCompiler.Matches(input); + + foreach (Match match in matches) + { + if (match.Groups["OPTIONAL"].Success) + { + string value = match.Groups["OPTIONAL"].Value; + value = value.Substring(1, value.Length - 2); + input = input.Replace(match.Value, value); + } + else if (match.Groups["GROUP"].Success) + { + string value = match.Groups["GROUP"].Value; + value = value.Substring(1, value.Length - 2).Split('|')[0]; + input = input.Replace(match.Value, value); + } + else if (match.Groups["OPTIONALGROUP"].Success) + { + string value = match.Groups["OPTIONALGROUP"].Value; + value = value.Substring(2, value.Length - 4).Split('|')[0]; + input = input.Replace(match.Value, value); + } + } + + textEditor.Document.Text = input; } private void Test() diff --git a/SkEditorPlus/Windows/NewOptionsWindow.xaml.cs b/SkEditorPlus/Windows/NewOptionsWindow.xaml.cs index a1fa314..1422afb 100644 --- a/SkEditorPlus/Windows/NewOptionsWindow.xaml.cs +++ b/SkEditorPlus/Windows/NewOptionsWindow.xaml.cs @@ -27,8 +27,8 @@ public partial class NewOptionsWindow : HandyControl.Controls.Window private SettingsBindings settingsBindings; // This is polish easter egg :) - private static string pozdrowieniaSwirek = "pozdrowieniaswiateczne"; - private static bool[] pozdrowieniaSwirekBools = new bool[pozdrowieniaSwirek.Length]; + private static readonly string pozdrowieniaSwirek = "pozdrowieniaswiateczne"; + private static readonly bool[] pozdrowieniaSwirekBools = new bool[pozdrowieniaSwirek.Length]; public NewOptionsWindow(SkEditorAPI skEditor) { @@ -58,6 +58,7 @@ public NewOptionsWindow(SkEditorAPI skEditor) { string fileName = Path.GetFileNameWithoutExtension(file); if (fileName != null) + if (fileName != null) { if (languageComboBox.Items.Cast().All(item => item.Content.ToString() != fileName)) { @@ -97,6 +98,7 @@ private void OnFontButtonClick(object sender, RoutedEventArgs e) foreach (TabItem ti in skEditor.GetMainWindow().tabControl.Items) { + if (!skEditor.IsFileOpen()) continue; TextEditor textEditor = (TextEditor)ti.Content; textEditor.FontFamily = new FontFamily(fontSelector.ResultFontFamily.Source); } @@ -118,6 +120,7 @@ private void TransparencyChanged(object sender, RoutedPropertyChangedEventArgs