notepad/NotePad/MainWindow.axaml.cs

374 lines
12 KiB
C#
Raw Permalink Normal View History

2026-02-14 14:15:52 +00:00
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<TextBox>("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<TextBox>("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<SavePromptResult> 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<TextBox>("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<TextBox>("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<TextBox>("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<CheckBox>("WordWrapCheckBox")!;
var textBox = this.FindControl<TextBox>("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<TextBox>("EditorTextBox")!;
textBox.Undo();
}
private void OnCutClick(object? sender, RoutedEventArgs e)
{
var textBox = this.FindControl<TextBox>("EditorTextBox")!;
textBox.Cut();
}
private void OnCopyClick(object? sender, RoutedEventArgs e)
{
var textBox = this.FindControl<TextBox>("EditorTextBox")!;
textBox.Copy();
}
private void OnPasteClick(object? sender, RoutedEventArgs e)
{
var textBox = this.FindControl<TextBox>("EditorTextBox")!;
textBox.Paste();
}
private void OnDeleteClick(object? sender, RoutedEventArgs e)
{
var textBox = this.FindControl<TextBox>("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<TextBox>("EditorTextBox")!;
textBox.SelectAll();
}
private void OnTimeDateClick(object? sender, RoutedEventArgs e)
{
var textBox = this.FindControl<TextBox>("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);
}
}
}