using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Platform.Storage; using System; using System.IO; using System.Threading.Tasks; using Avalonia.Media; namespace NotePad { public partial class MainWindow : Window { private string? _currentFilePath; private string? _currentFileName; private bool _isModified; private string? _originalText; public MainWindow() { InitializeComponent(); UpdateTitle(); var textBox = this.FindControl("EditorTextBox")!; textBox.TextChanged += OnTextChanged; Closing += OnWindowClosing; } private void UpdateTitle() { var modified = _isModified ? "*" : ""; Title = $"{modified}{_currentFileName ?? "Untitled"} - Notepad"; } private void OnTextChanged(object? sender, EventArgs e) { var currentText = this.FindControl("EditorTextBox")!.Text ?? string.Empty; _isModified = currentText != _originalText; UpdateTitle(); } private async void OnWindowClosing(object? sender, WindowClosingEventArgs e) { if (_isModified) { e.Cancel = true; var result = await ShowSavePrompt(); if (result == SavePromptResult.Save) { await PerformSave(); if (!_isModified) // Only close if save succeeded { Closing -= OnWindowClosing; Close(); } } else if (result == SavePromptResult.DontSave) { Closing -= OnWindowClosing; Close(); } // Cancel = do nothing } } private async Task ShowSavePrompt() { var dialog = new Window { Title = "Notepad", Width = 400, Height = 150, WindowStartupLocation = WindowStartupLocation.CenterOwner, CanResize = false }; var result = SavePromptResult.Cancel; var panel = new StackPanel { Margin = new Thickness(20) }; var message = new TextBlock { Text = $"Do you want to save changes to {_currentFileName ?? "Untitled"}?", TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 0, 0, 20) }; var buttonPanel = new StackPanel { Orientation = Avalonia.Layout.Orientation.Horizontal, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, Spacing = 10 }; var yesButton = new Button { Content = "Yes", Width = 80 }; yesButton.Click += (s, e) => { result = SavePromptResult.Save; dialog.Close(); }; var noButton = new Button { Content = "No", Width = 80 }; noButton.Click += (s, e) => { result = SavePromptResult.DontSave; dialog.Close(); }; var cancelButton = new Button { Content = "Cancel", Width = 80 }; cancelButton.Click += (s, e) => { result = SavePromptResult.Cancel; dialog.Close(); }; buttonPanel.Children.Add(yesButton); buttonPanel.Children.Add(noButton); buttonPanel.Children.Add(cancelButton); panel.Children.Add(message); panel.Children.Add(buttonPanel); dialog.Content = panel; await dialog.ShowDialog(this); return result; } private enum SavePromptResult { Save, DontSave, Cancel } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } // New File private async void OnNewClick(object? sender, RoutedEventArgs e) { if (_isModified) { var result = await ShowSavePrompt(); if (result == SavePromptResult.Save) { await PerformSave(); if (_isModified) return; // Save was cancelled } else if (result == SavePromptResult.Cancel) { return; } } this.FindControl("EditorTextBox")!.Text = string.Empty; _currentFilePath = null; _currentFileName = null; _originalText = string.Empty; _isModified = false; UpdateTitle(); } // Open File private async void OnOpenClick(object? sender, RoutedEventArgs e) { if (_isModified) { var result = await ShowSavePrompt(); if (result == SavePromptResult.Save) { await PerformSave(); if (_isModified) return; // Save was cancelled } else if (result == SavePromptResult.Cancel) { return; } } var topLevel = GetTopLevel(this); if (topLevel == null) return; var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "Open", AllowMultiple = false, FileTypeFilter = new[] { FilePickerFileTypes.TextPlain, FilePickerFileTypes.All } }); if (files?.Count > 0) { var file = files[0]; _currentFilePath = file.TryGetLocalPath(); _currentFileName = file.Name; await using var stream = await file.OpenReadAsync(); using var reader = new StreamReader(stream); var text = await reader.ReadToEndAsync(); this.FindControl("EditorTextBox")!.Text = text; _originalText = text; _isModified = false; UpdateTitle(); } } // Save File (if path exists, else Save As) private async void OnSaveClick(object? sender, RoutedEventArgs e) { await PerformSave(); } private async Task PerformSave() { if (string.IsNullOrEmpty(_currentFilePath)) { await SaveAs(); } else { await SaveToPath(_currentFilePath); } } // Save As private async void OnSaveAsClick(object? sender, RoutedEventArgs e) { await SaveAs(); } private async Task SaveAs() { var topLevel = GetTopLevel(this); if (topLevel == null) return; var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { Title = "Save As", DefaultExtension = "txt", FileTypeChoices = new[] { FilePickerFileTypes.TextPlain, FilePickerFileTypes.All }, SuggestedFileName = _currentFileName ?? "Untitled.txt" }); if (file != null) { _currentFilePath = file.TryGetLocalPath(); _currentFileName = file.Name; await SaveToPath(_currentFilePath!); UpdateTitle(); } } private async Task SaveToPath(string path) { var text = this.FindControl("EditorTextBox")!.Text ?? string.Empty; await File.WriteAllTextAsync(path, text); _originalText = text; _isModified = false; UpdateTitle(); } // Exit private void OnExitClick(object? sender, RoutedEventArgs e) { Close(); } // Word Wrap Toggle private void OnWordWrapToggle(object? sender, RoutedEventArgs e) { var checkBox = this.FindControl("WordWrapCheckBox")!; var textBox = this.FindControl("EditorTextBox")!; checkBox.IsChecked = !checkBox.IsChecked; textBox.TextWrapping = checkBox.IsChecked == true ? TextWrapping.Wrap : TextWrapping.NoWrap; } // Edit Menu private void OnUndoClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; textBox.Undo(); } private void OnCutClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; textBox.Cut(); } private void OnCopyClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; textBox.Copy(); } private void OnPasteClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; textBox.Paste(); } private void OnDeleteClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; var selectionStart = textBox.SelectionStart; var selectionEnd = textBox.SelectionEnd; if (selectionStart != selectionEnd) { var text = textBox.Text ?? string.Empty; textBox.Text = text.Remove(selectionStart, selectionEnd - selectionStart); textBox.SelectionStart = selectionStart; textBox.SelectionEnd = selectionStart; } } private void OnSelectAllClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; textBox.SelectAll(); } private void OnTimeDateClick(object? sender, RoutedEventArgs e) { var textBox = this.FindControl("EditorTextBox")!; var timeDate = DateTime.Now.ToString("h:mm tt M/d/yyyy"); var position = textBox.SelectionStart; var text = textBox.Text ?? string.Empty; textBox.Text = text.Insert(position, timeDate); textBox.SelectionStart = position + timeDate.Length; } // Help Menu private async void OnAboutClick(object? sender, RoutedEventArgs e) { var dialog = new Window { Title = "About Notepad", Width = 350, Height = 200, WindowStartupLocation = WindowStartupLocation.CenterOwner, CanResize = false }; var panel = new StackPanel { Margin = new Thickness(20) }; var title = new TextBlock { Text = "Notepad", FontSize = 16, FontWeight = FontWeight.Bold, Margin = new Thickness(0, 0, 0, 10) }; var info = new TextBlock { Text = "A simple text editor built with Avalonia", TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 0, 0, 20) }; var okButton = new Button { Content = "OK", Width = 80, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center }; okButton.Click += (s, e) => dialog.Close(); panel.Children.Add(title); panel.Children.Add(info); panel.Children.Add(okButton); dialog.Content = panel; await dialog.ShowDialog(this); } } }