From 2aa4c20c76e85f7fe855863bc817feb94eb094bc Mon Sep 17 00:00:00 2001 From: Mohamadreza Nakhleh Date: Thu, 27 Nov 2025 11:24:29 +0330 Subject: [PATCH 1/2] fix: use KeyPress event for text change detection. it allows a better way for plugins to handle stuff --- edit-sharp/Views/EditorView.cs | 121 +++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/edit-sharp/Views/EditorView.cs b/edit-sharp/Views/EditorView.cs index d7d9ce9..9e39bed 100644 --- a/edit-sharp/Views/EditorView.cs +++ b/edit-sharp/Views/EditorView.cs @@ -19,6 +19,8 @@ public class EditorView : ITextEditor, IEditorHost private string _encoding = "Unknown"; private string _lineCount = "Unknown"; private string? _charCount = "Unknown"; + private bool _programmaticChange = false; // Flag to track programmatic changes + private string _lastText = string.Empty; // Track last text state public EditorView(Window parentWindow) { @@ -35,16 +37,8 @@ public EditorView(Window parentWindow) WordWrap = _wordWrap }; - TextView.TextChanged += () => - { - var content = TextView.Text.ToString(); - _lineCount = TextView.Lines.ToString(); - _charCount = content?.Length.ToString(); - _hasUnsavedChanges = true; - TextChanged?.Invoke(); - UpdateStatus($"Editing {Path.GetFileName(FilePath)} - Unsaved changes"); - foreach (var plugin in _plugins) plugin.OnTextChanged(TextView.Text.ToString()); - }; + // Use KeyPress event instead of TextChanged for keyboard input detection + TextView.KeyPress += OnKeyPress; _parentWindow.Add(TextView); @@ -62,13 +56,77 @@ public EditorView(Window parentWindow) _parentWindow.Add(_statusBar); } + private void OnKeyPress(View.KeyEventEventArgs args) + { + // Check if this is an actual text modification key + var key = args.KeyEvent.Key; + + // Filter out navigation and non-text keys + if (IsNavigationKey(key) || IsModifierOnlyKey(key)) + { + return; + } + + // Schedule the text change notification for after the key is processed + Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(10), (_) => + { + var currentText = TextView.Text.ToString(); + if (currentText != _lastText && !_programmaticChange) + { + _lastText = currentText; + OnUserTextChanged(); + } + return false; // Don't repeat + }); + } + + private bool IsNavigationKey(Key key) + { + var keyWithoutMods = key & ~(Key.CtrlMask | Key.ShiftMask | Key.AltMask); + + return keyWithoutMods switch + { + Key.CursorUp or Key.CursorDown or Key.CursorLeft or Key.CursorRight or + Key.Home or Key.End or Key.PageUp or Key.PageDown or + Key.F1 or Key.F2 or Key.F3 or Key.F4 or Key.F5 or + Key.F6 or Key.F7 or Key.F8 or Key.F9 or Key.F10 or + Key.F11 or Key.F12 or Key.Esc or Key.Tab => true, + _ => false + }; + } + + private bool IsModifierOnlyKey(Key key) + { + // Check if only modifier keys are pressed (Ctrl, Alt, Shift alone) + return key == Key.CtrlMask || key == Key.AltMask || key == Key.ShiftMask; + } + + private void OnUserTextChanged() + { + var content = TextView.Text.ToString(); + _lineCount = TextView.Lines.ToString(); + _charCount = content?.Length.ToString(); + _hasUnsavedChanges = true; + TextChanged?.Invoke(); + UpdateStatus($"Editing {Path.GetFileName(FilePath)} - Unsaved changes"); + + // Notify plugins + foreach (var plugin in _plugins) + { + plugin.OnTextChanged(content); + } + } + public string? Text { get => TextView.Text.ToString(); set { + _programmaticChange = true; TextView.Text = value; + _lastText = value ?? string.Empty; _hasUnsavedChanges = false; + _programmaticChange = false; } } @@ -132,17 +190,41 @@ public void OpenFile(string? path = null) ShowError($"Error opening file: {ex.Message}"); } } + private static string DetectLineEnding(string content) { - var crlf = content.Split("\r\n").Length - 1; - var lf = content.Split("\n").Length - 1; - var cr = content.Split("\r").Length - 1; + if (string.IsNullOrEmpty(content)) + return "Unknown"; + + var crlfCount = 0; + var lfCount = 0; + var crCount = 0; - if (crlf > 0 && crlf >= lf && crlf >= cr) + for (int i = 0; i < content.Length; i++) + { + if (content[i] == '\r') + { + if (i + 1 < content.Length && content[i + 1] == '\n') + { + crlfCount++; + i++; // Skip the \n + } + else + { + crCount++; + } + } + else if (content[i] == '\n') + { + lfCount++; + } + } + + if (crlfCount > 0 && crlfCount >= lfCount && crlfCount >= crCount) return "CRLF"; - if (lf > 0 && lf >= cr) + if (lfCount > 0 && lfCount >= crCount) return "LF"; - return cr > 0 ? "CR" : "Unknown"; + return crCount > 0 ? "CR" : "Unknown"; } public void SaveFile() @@ -301,7 +383,8 @@ public void UpdateStatus(string message) public event Action TextChanged; public event Action FileOpened; public event Action FileSaved; - public string CurrentFilePath { get; } + + public string CurrentFilePath => FilePath ?? string.Empty; public string? GetText() { @@ -315,6 +398,6 @@ public void SetStatusMessage(string message) public void AddMenuItem(string menuPath, Action action) { - + } -} +} \ No newline at end of file From 78952c7ec676fb67845c3d83162496475c631e93 Mon Sep 17 00:00:00 2001 From: Mohamadreza Nakhleh Date: Thu, 27 Nov 2025 11:51:32 +0330 Subject: [PATCH 2/2] fix: 1-improved textview status bar information and some fixes 2-removed plugin messgesboxes --- .../Plugins/BasicPlugins/SimplePlugin.cs | 10 +- edit-sharp/Views/EditorView.cs | 119 ++++++++++++++---- 2 files changed, 99 insertions(+), 30 deletions(-) diff --git a/edit-sharp/Plugins/BasicPlugins/SimplePlugin.cs b/edit-sharp/Plugins/BasicPlugins/SimplePlugin.cs index a9a872a..ea88e9c 100644 --- a/edit-sharp/Plugins/BasicPlugins/SimplePlugin.cs +++ b/edit-sharp/Plugins/BasicPlugins/SimplePlugin.cs @@ -15,21 +15,17 @@ public class DialogPlugin : IEditorPlugin public void Initialize(IEditorHost host) { - _host = host; - var result = _host.CreateDialog("MyCustomDialog", 50, 50); - Application.Run(result); + } public void OnFileOpened(string filePath) { - var result = _host.CreateDialog("MyCustomDialog", 50, 50); - Application.Run(result); + } public void OnTextChanged(string? newText) { - var result = _host.CreateDialog("MyCustomDialog", 50, 50); - Application.Run(result); + } } diff --git a/edit-sharp/Views/EditorView.cs b/edit-sharp/Views/EditorView.cs index 9e39bed..6e4517b 100644 --- a/edit-sharp/Views/EditorView.cs +++ b/edit-sharp/Views/EditorView.cs @@ -15,10 +15,8 @@ public class EditorView : ITextEditor, IEditorHost private readonly Label _statusBar; private bool _wordWrap = true; private readonly List _plugins; - private string _lineEnding = "Unknown"; - private string _encoding = "Unknown"; - private string _lineCount = "Unknown"; - private string? _charCount = "Unknown"; + private string? _detectedLineEnding = null; // Line ending from current editing session + private string _encoding = "UTF-8"; // Default encoding private bool _programmaticChange = false; // Flag to track programmatic changes private string _lastText = string.Empty; // Track last text state @@ -104,11 +102,14 @@ private bool IsModifierOnlyKey(Key key) private void OnUserTextChanged() { var content = TextView.Text.ToString(); - _lineCount = TextView.Lines.ToString(); - _charCount = content?.Length.ToString(); _hasUnsavedChanges = true; + + // Detect line ending from user input + DetectLineEndingFromInput(content); + TextChanged?.Invoke(); - UpdateStatus($"Editing {Path.GetFileName(FilePath)} - Unsaved changes"); + UpdateWindowTitle(); + UpdateStatus(); // Notify plugins foreach (var plugin in _plugins) @@ -117,6 +118,38 @@ private void OnUserTextChanged() } } + private void DetectLineEndingFromInput(string content) + { + if (string.IsNullOrEmpty(content)) + { + _detectedLineEnding = null; + return; + } + + // Only detect if we haven't detected yet or if content has line breaks + for (int i = 0; i < content.Length; i++) + { + if (content[i] == '\r') + { + if (i + 1 < content.Length && content[i + 1] == '\n') + { + _detectedLineEnding = "CRLF"; + return; + } + else + { + _detectedLineEnding = "CR"; + return; + } + } + else if (content[i] == '\n') + { + _detectedLineEnding = "LF"; + return; + } + } + } + public string? Text { get => TextView.Text.ToString(); @@ -127,6 +160,8 @@ public string? Text _lastText = value ?? string.Empty; _hasUnsavedChanges = false; _programmaticChange = false; + UpdateWindowTitle(); + UpdateStatus(); // Update status after setting text } } @@ -144,8 +179,11 @@ public void NewFile() Text = string.Empty; FilePath = string.Empty; + _detectedLineEnding = null; + _encoding = "UTF-8"; _hasUnsavedChanges = false; - UpdateStatus("New file created"); + UpdateWindowTitle(); + UpdateStatus(); } public void OpenFile(string? path = null) @@ -171,12 +209,11 @@ public void OpenFile(string? path = null) var content = reader.ReadToEnd(); Text = content; _encoding = reader.CurrentEncoding.EncodingName; - _lineCount = TextView.Lines.ToString(); - _charCount = content.Length.ToString(); - _lineEnding = DetectLineEnding(content); + _detectedLineEnding = DetectLineEndingFromFile(content); FilePath = path; _hasUnsavedChanges = false; - UpdateStatus($"Opened: {Path.GetFileName(path)}"); + UpdateWindowTitle(); + UpdateStatus(); FileOpened?.Invoke(); foreach (var plugin in _plugins) plugin.OnFileOpened(path); } @@ -191,7 +228,7 @@ public void OpenFile(string? path = null) } } - private static string DetectLineEnding(string content) + private static string DetectLineEndingFromFile(string content) { if (string.IsNullOrEmpty(content)) return "Unknown"; @@ -243,7 +280,8 @@ public void SaveFile() { File.WriteAllText(FilePath, Text); _hasUnsavedChanges = false; - UpdateStatus($"Saved: {Path.GetFileName(FilePath)}"); + UpdateWindowTitle(); + UpdateStatus(); FileSaved?.Invoke(); } catch (Exception ex) @@ -281,8 +319,10 @@ public void CloseFile() Text = string.Empty; FilePath = string.Empty; + _detectedLineEnding = null; _hasUnsavedChanges = false; - UpdateStatus("File closed"); + UpdateWindowTitle(); + UpdateStatus(); } public bool CheckUnsavedChanges() @@ -358,13 +398,50 @@ public void ShowError(string message) MessageBox.ErrorQuery(50, 7, "Error", message, "OK"); } - public void UpdateStatus(string message) + public void UpdateStatus(string? customMessage = null) { if (!_showStatusBar) return; + var lineCount = TextView.Lines; var charCount = TextView.Text.Length; + var lineEnding = _detectedLineEnding ?? "---"; + + // Build status parts + var parts = new List(); + + // Custom message or default status + if (!string.IsNullOrEmpty(customMessage)) + { + parts.Add(customMessage); + } + else if (!string.IsNullOrEmpty(FilePath)) + { + parts.Add(Path.GetFileName(FilePath)); + } + else + { + parts.Add("Untitled"); + } + + // Add stats + parts.Add($"Lines: {lineCount}"); + parts.Add($"Chars: {charCount}"); + parts.Add($"Encoding: {_encoding}"); + parts.Add($"Line Ending: {lineEnding}"); + + _statusBar.Text = string.Join(" | ", parts); + } - _statusBar.Text = $"{message} [Lines: {lineCount}, Chars: {charCount}, {_encoding}, {_lineEnding}]"; + private void UpdateWindowTitle() + { + var fileName = string.IsNullOrEmpty(FilePath) ? "Untitled" : Path.GetFileName(FilePath); + var modifiedIndicator = _hasUnsavedChanges ? "● " : ""; + _parentWindow.Title = $"{modifiedIndicator}{fileName} - Edit Sharp"; + } + + public void SetStatusMessage(string message) + { + UpdateStatus(message); } public T CreateDialog(string title, int width, int height) where T : Dialog, new() @@ -390,14 +467,10 @@ public void UpdateStatus(string message) { return TextView.Text.ToString(); } - - public void SetStatusMessage(string message) - { - UpdateStatus(message); - } + public void AddMenuItem(string menuPath, Action action) { - + } } \ No newline at end of file