diff --git a/src/Notepads/Controls/TextEditor/ITextEditor.cs b/src/Notepads/Controls/TextEditor/ITextEditor.cs index b8dc4cce6..414969350 100644 --- a/src/Notepads/Controls/TextEditor/ITextEditor.cs +++ b/src/Notepads/Controls/TextEditor/ITextEditor.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------- // Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved. // See LICENSE file in the project root for license information. // --------------------------------------------------------------------------------------------- @@ -74,6 +74,8 @@ void Init(TextFile textFile, string GetText(); + string GetSelectedText(); + void StartCheckingFileStatusPeriodically(); void StopCheckingFileStatus(); diff --git a/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs b/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs index dd30c6a90..122613601 100644 --- a/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs +++ b/src/Notepads/Controls/TextEditor/TextEditor.xaml.cs @@ -1,4 +1,4 @@ -namespace Notepads.Controls.TextEditor +namespace Notepads.Controls.TextEditor { using System; using System.Collections.Generic; @@ -312,6 +312,13 @@ public string GetText() return TextEditorCore.GetText(); } + public string GetSelectedText() + { + return TextEditorCore.Document.Selection.StartPosition == TextEditorCore.Document.Selection.EndPosition + ? string.Empty + : TextEditorCore.Document.Selection.Text; + } + // Make sure this method is thread safe public TextEditorStateMetaData GetTextEditorStateMetaData() { diff --git a/src/Notepads/Core/INotepadsCore.cs b/src/Notepads/Core/INotepadsCore.cs index c6f953414..dc84b9f15 100644 --- a/src/Notepads/Core/INotepadsCore.cs +++ b/src/Notepads/Core/INotepadsCore.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------- // Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved. // See LICENSE file in the project root for license information. // --------------------------------------------------------------------------------------------- @@ -31,6 +31,7 @@ public interface INotepadsCore event EventHandler TextEditorFontZoomFactorChanged; event EventHandler TextEditorEncodingChanged; event EventHandler TextEditorLineEndingChanged; + event EventHandler TextEditorTextChanging; event EventHandler TextEditorModeChanged; event EventHandler TextEditorMovedToAnotherAppInstance; event EventHandler> StorageItemsDropped; diff --git a/src/Notepads/Core/NotepadsCore.cs b/src/Notepads/Core/NotepadsCore.cs index 8f1a7b2f7..dbe62d3d8 100644 --- a/src/Notepads/Core/NotepadsCore.cs +++ b/src/Notepads/Core/NotepadsCore.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------- // Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved. // See LICENSE file in the project root for license information. // --------------------------------------------------------------------------------------------- @@ -42,6 +42,7 @@ public class NotepadsCore : INotepadsCore public event EventHandler TextEditorFontZoomFactorChanged; public event EventHandler TextEditorEncodingChanged; public event EventHandler TextEditorLineEndingChanged; + public event EventHandler TextEditorTextChanging; public event EventHandler TextEditorModeChanged; public event EventHandler TextEditorMovedToAnotherAppInstance; public event EventHandler> StorageItemsDropped; @@ -199,6 +200,7 @@ public ITextEditor CreateTextEditor( textEditor.Loaded += TextEditor_Loaded; textEditor.Unloaded += TextEditor_Unloaded; textEditor.SelectionChanged += TextEditor_OnSelectionChanged; + textEditor.TextChanging += TextEditor_OnTextChanging; textEditor.FontZoomFactorChanged += TextEditor_OnFontZoomFactorChanged; textEditor.KeyDown += TextEditorKeyDown; textEditor.ModificationStateChanged += TextEditor_OnEditorModificationStateChanged; @@ -238,6 +240,7 @@ public void DeleteTextEditor(ITextEditor textEditor) textEditor.Unloaded -= TextEditor_Unloaded; textEditor.KeyDown -= TextEditorKeyDown; textEditor.SelectionChanged -= TextEditor_OnSelectionChanged; + textEditor.TextChanging -= TextEditor_OnTextChanging; textEditor.FontZoomFactorChanged -= TextEditor_OnFontZoomFactorChanged; textEditor.ModificationStateChanged -= TextEditor_OnEditorModificationStateChanged; textEditor.ModeChanged -= TextEditor_OnModeChanged; @@ -521,6 +524,12 @@ private void TextEditor_OnSelectionChanged(object sender, EventArgs e) TextEditorSelectionChanged?.Invoke(this, textEditor); } + private void TextEditor_OnTextChanging(object sender, EventArgs e) + { + if (!(sender is ITextEditor textEditor)) return; + TextEditorTextChanging?.Invoke(this, textEditor); + } + private void TextEditor_OnFontZoomFactorChanged(object sender, EventArgs e) { if (!(sender is ITextEditor textEditor)) return; diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.StatusBar.cs b/src/Notepads/Views/MainPage/NotepadsMainPage.StatusBar.cs index b5001164d..987d37e9f 100644 --- a/src/Notepads/Views/MainPage/NotepadsMainPage.StatusBar.cs +++ b/src/Notepads/Views/MainPage/NotepadsMainPage.StatusBar.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------- // Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved. // See LICENSE file in the project root for license information. // --------------------------------------------------------------------------------------------- @@ -40,6 +40,7 @@ private void SetupStatusBar(ITextEditor textEditor) UpdatePathIndicator(textEditor); UpdateEditorModificationIndicator(textEditor); UpdateLineColumnIndicator(textEditor); + UpdateWordCountIndicator(textEditor); UpdateFontZoomIndicator(textEditor); UpdateLineEndingIndicator(textEditor.GetLineEnding()); UpdateEncodingIndicator(textEditor.GetEncoding()); @@ -161,6 +162,65 @@ private void UpdateLineColumnIndicator(ITextEditor textEditor) selectedCount, wordSelected); } + private void UpdateWordCountIndicator(ITextEditor textEditor) + { + if (StatusBar == null) return; + + var fullText = textEditor.GetText(); + var totalWordCount = CountWords(fullText); + + var selectedText = textEditor.GetSelectedText(); + if (!string.IsNullOrEmpty(selectedText)) + { + var selectedWordCount = CountWords(selectedText); + WordCountIndicator.Text = string.Format( + "Words: {0} | Selected: {1}", + totalWordCount.ToString("N0", CultureInfo.CurrentCulture), + selectedWordCount.ToString("N0", CultureInfo.CurrentCulture)); + } + else + { + WordCountIndicator.Text = string.Format( + "Words: {0}", + totalWordCount.ToString("N0", CultureInfo.CurrentCulture)); + } + } + + /// + /// Counts words in the given text using character iteration for efficiency. + /// A word is defined as a contiguous sequence of non-whitespace characters. + /// + private static int CountWords(string text) + { + if (string.IsNullOrEmpty(text)) return 0; + + int wordCount = 0; + bool inWord = false; + + for (int i = 0; i < text.Length; i++) + { + if (char.IsWhiteSpace(text[i])) + { + if (inWord) + { + wordCount++; + inWord = false; + } + } + else + { + inWord = true; + } + } + + if (inWord) + { + wordCount++; + } + + return wordCount; + } + private void UpdateFontZoomIndicator(ITextEditor textEditor) { if (StatusBar == null) return; diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml index e9bc197b7..a93a30fdc 100644 --- a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml +++ b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml @@ -1,4 +1,4 @@ - + - + + + + @@ -580,7 +586,7 @@ - @@ -617,7 +623,7 @@ - @@ -651,7 +657,7 @@ - diff --git a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs index 423b32613..4259890a8 100644 --- a/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs +++ b/src/Notepads/Views/MainPage/NotepadsMainPage.xaml.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------------------- // Copyright (c) 2019-2024, Jiaqi (0x7c13) Liu. All rights reserved. // See LICENSE file in the project root for license information. // --------------------------------------------------------------------------------------------- @@ -62,7 +62,8 @@ private INotepadsCore NotepadsCore _notepadsCore.TextEditorSaved += OnTextEditorSaved; _notepadsCore.TextEditorMovedToAnotherAppInstance += OnTextEditorMovedToAnotherAppInstance; _notepadsCore.TextEditorRenamed += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) SetupStatusBar(editor); }; - _notepadsCore.TextEditorSelectionChanged += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) UpdateLineColumnIndicator(editor); }; + _notepadsCore.TextEditorSelectionChanged += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) { UpdateLineColumnIndicator(editor); UpdateWordCountIndicator(editor); } }; + _notepadsCore.TextEditorTextChanging += (_, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) UpdateWordCountIndicator(editor); }; _notepadsCore.TextEditorFontZoomFactorChanged += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) UpdateFontZoomIndicator(editor); }; _notepadsCore.TextEditorEncodingChanged += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) UpdateEncodingIndicator(editor.GetEncoding()); }; _notepadsCore.TextEditorLineEndingChanged += (sender, editor) => { if (NotepadsCore.GetSelectedTextEditor() == editor) { UpdateLineEndingIndicator(editor.GetLineEnding()); UpdateLineColumnIndicator(editor); } };