initial
This commit is contained in:
204
src/Views/MainWindow.axaml.cs
Normal file
204
src/Views/MainWindow.axaml.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
using AvaloniaEdit;
|
||||
using MarkdownEditor.Constants;
|
||||
using MarkdownEditor.ViewModels;
|
||||
|
||||
namespace MarkdownEditor.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Main application window.
|
||||
/// Handles initialization and coordination between the view and view model.
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private bool _isClosingConfirmed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MainWindow"/> class.
|
||||
/// </summary>
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Subscribe to pointer pressed to capture selection before command executes
|
||||
this.AddHandler(Button.PointerPressedEvent, OnButtonPointerPressed, Avalonia.Interactivity.RoutingStrategies.Tunnel);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnOpened(EventArgs e)
|
||||
{
|
||||
base.OnOpened(e);
|
||||
|
||||
if (DataContext is MainWindowViewModel viewModel)
|
||||
{
|
||||
viewModel.StorageProvider = StorageProvider;
|
||||
// Restore session after StorageProvider is set
|
||||
_ = viewModel.RestoreSessionAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
if (DataContext is not MainWindowViewModel viewModel)
|
||||
{
|
||||
base.OnClosing(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've already confirmed the close (from a previous call), just save session and close
|
||||
if (_isClosingConfirmed)
|
||||
{
|
||||
await viewModel.SaveSessionAsync();
|
||||
base.OnClosing(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for unsaved changes
|
||||
if (viewModel.HasUnsavedChanges())
|
||||
{
|
||||
// Cancel the close event to prevent immediate closing
|
||||
e.Cancel = true;
|
||||
|
||||
// Show save confirmation dialog
|
||||
var unsavedCount = viewModel.GetUnsavedChangesCount();
|
||||
var message = unsavedCount == 1
|
||||
? AppConstants.Messages.SaveChangesBeforeClose
|
||||
: string.Format(AppConstants.Messages.SaveChangesBeforeCloseMultiple, unsavedCount);
|
||||
|
||||
var dialog = new SaveConfirmationDialog(message);
|
||||
await dialog.ShowDialog<bool>(this);
|
||||
var result = dialog.Result;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case SaveConfirmationResult.Save:
|
||||
// User wants to save - save all unsaved changes
|
||||
var saved = await viewModel.SaveAllUnsavedChangesAsync();
|
||||
if (saved)
|
||||
{
|
||||
// All files saved successfully - mark as confirmed and close
|
||||
_isClosingConfirmed = true;
|
||||
await viewModel.SaveSessionAsync();
|
||||
Close();
|
||||
}
|
||||
// If save was cancelled, keep the window open (e.Cancel is already true)
|
||||
// Don't call base.OnClosing since we're cancelling
|
||||
return;
|
||||
|
||||
case SaveConfirmationResult.DontSave:
|
||||
// User doesn't want to save - mark as confirmed and close
|
||||
_isClosingConfirmed = true;
|
||||
await viewModel.SaveSessionAsync();
|
||||
Close();
|
||||
// Don't call base.OnClosing here - Close() will trigger OnClosing again
|
||||
return;
|
||||
|
||||
case SaveConfirmationResult.Cancel:
|
||||
default:
|
||||
// User cancelled - keep the window open (e.Cancel is already true)
|
||||
// Don't call base.OnClosing since we're cancelling
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No unsaved changes - just save session and close normally
|
||||
await viewModel.SaveSessionAsync();
|
||||
}
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles pointer pressed events to capture text selection before formatting commands execute.
|
||||
/// This is necessary because toolbar buttons take focus, losing the text selection.
|
||||
/// </summary>
|
||||
private void OnButtonPointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
|
||||
{
|
||||
// Walk up the visual tree to find the Button
|
||||
Button? button = null;
|
||||
if (e.Source is Control sourceControl)
|
||||
{
|
||||
button = sourceControl.GetVisualAncestors().OfType<Button>().FirstOrDefault();
|
||||
}
|
||||
|
||||
// Only process toolbar buttons
|
||||
if (button != null && button.Classes.Contains("toolbar-btn"))
|
||||
{
|
||||
// Find the TextEditor and update selection in ViewModel before command executes
|
||||
if (DataContext is MainWindowViewModel mainViewModel && mainViewModel.SelectedTab != null)
|
||||
{
|
||||
var textEditor = this.GetVisualDescendants().OfType<TextEditor>()
|
||||
.FirstOrDefault(te => te.IsVisible);
|
||||
|
||||
if (textEditor != null)
|
||||
{
|
||||
mainViewModel.SelectedTab.SelectionStart = textEditor.SelectionStart;
|
||||
mainViewModel.SelectedTab.SelectionEnd = textEditor.SelectionStart + textEditor.SelectionLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the Insert Hyperlink button click.
|
||||
/// </summary>
|
||||
private async void OnInsertHyperlink(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is MarkdownTabViewModel tabViewModel)
|
||||
{
|
||||
CaptureSelection();
|
||||
|
||||
var dialog = new UrlInputDialog("Insert Hyperlink", "Enter the URL for the link:");
|
||||
var result = await dialog.ShowDialog<bool>(this);
|
||||
|
||||
if (dialog.IsConfirmed && !string.IsNullOrWhiteSpace(dialog.Url))
|
||||
{
|
||||
tabViewModel.InsertHyperlink(dialog.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the Insert Image button click.
|
||||
/// </summary>
|
||||
private async void OnInsertImage(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.Tag is MarkdownTabViewModel tabViewModel)
|
||||
{
|
||||
CaptureSelection();
|
||||
|
||||
var dialog = new UrlInputDialog("Insert Image", "Enter the URL for the image:");
|
||||
var result = await dialog.ShowDialog<bool>(this);
|
||||
|
||||
if (dialog.IsConfirmed && !string.IsNullOrWhiteSpace(dialog.Url))
|
||||
{
|
||||
tabViewModel.InsertImage(dialog.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures the current text selection from the visible text editor.
|
||||
/// </summary>
|
||||
private void CaptureSelection()
|
||||
{
|
||||
if (DataContext is MainWindowViewModel mainViewModel && mainViewModel.SelectedTab != null)
|
||||
{
|
||||
var textEditor = this.GetVisualDescendants().OfType<TextEditor>()
|
||||
.FirstOrDefault(te => te.IsVisible);
|
||||
|
||||
if (textEditor != null)
|
||||
{
|
||||
mainViewModel.SelectedTab.SelectionStart = textEditor.SelectionStart;
|
||||
mainViewModel.SelectedTab.SelectionEnd = textEditor.SelectionStart + textEditor.SelectionLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user