advanced-calculator/src/AdvancedCalculator/ViewModels/MainViewModel.cs

146 lines
4.3 KiB
C#

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using AdvancedCalculator.Models;
using AdvancedCalculator.Services;
using CSMic;
using System;
using System.Reflection;
namespace AdvancedCalculator.ViewModels;
public partial class MainViewModel : ViewModelBase
{
private readonly ICalculatorService _calculatorService;
public event EventHandler<string>? CopyRequested;
public MainViewModel()
: this(new CalculatorService())
{
}
public MainViewModel(ICalculatorService calculatorService)
{
_calculatorService = calculatorService;
History = new ObservableCollection<HistoryItem>();
Variables = new ObservableCollection<VariableItem>();
}
[ObservableProperty]
private string _inputText = string.Empty;
[ObservableProperty]
private bool _isFunctionsPanelOpen;
[ObservableProperty]
private int _selectedHistoryIndex = -1;
// Caret position in the input TextBox
[ObservableProperty]
private int _caretIndex;
public ObservableCollection<HistoryItem> History { get; }
public ObservableCollection<VariableItem> Variables { get; }
// App version string, sourced from AssemblyInformationalVersion
public string AppVersion { get; } =
typeof(MainViewModel).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion ?? "0.0.0";
[RelayCommand]
private void ToggleFunctions()
{
IsFunctionsPanelOpen = !IsFunctionsPanelOpen;
}
// Insert helpers for touch: appends tokens to the input box
[RelayCommand]
private void InsertVariable(VariableItem variableItem)
{
if (variableItem == null) return;
if (string.IsNullOrWhiteSpace(variableItem.VariableName))
return;
InsertToken(variableItem.VariableName);
}
[RelayCommand]
private void InsertFunction(FunctionDefinitionItem functionDefintionItem)
{
if (functionDefintionItem == null) return;
if (string.IsNullOrWhiteSpace(functionDefintionItem.FunctionName))
return;
InsertToken(functionDefintionItem.FunctionName + "()");
// Place caret inside the parentheses
if (!string.IsNullOrEmpty(InputText))
{
CaretIndex = Math.Max(0, InputText.Length - 1);
}
// Close the functions flyout after insertion for smoother UX
IsFunctionsPanelOpen = false;
}
private void InsertToken(string token)
{
if (string.IsNullOrEmpty(InputText))
{
InputText = token;
return;
}
if (!char.IsWhiteSpace(InputText[^1]))
InputText += " ";
InputText += token;
}
[RelayCommand(AllowConcurrentExecutions = false)]
private async Task Submit()
{
var text = InputText?.Trim();
if (string.IsNullOrEmpty(text))
return;
var result = await _calculatorService.InterpretAsync(text);
History.Add(new HistoryItem { Input = text, Output = result.Output });
Variables.Clear();
foreach (var v in result.Variables)
{
Variables.Add(v);
}
InputText = string.Empty;
SelectedHistoryIndex = History.Count - 1;
}
// Copy helpers (MVVM-pure): build text in VM, View handles clipboard via CopyRequested
[RelayCommand]
private void CopyHistoryInput(HistoryItem? item)
{
if (item is null || string.IsNullOrWhiteSpace(item.Input)) return;
CopyRequested?.Invoke(this, item.Input);
}
[RelayCommand]
private void CopyHistoryOutput(HistoryItem? item)
{
if (item is null || string.IsNullOrWhiteSpace(item.Output)) return;
CopyRequested?.Invoke(this, item.Output);
}
[RelayCommand]
private void CopyHistoryBoth(HistoryItem? item)
{
if (item is null) return;
var input = item.Input?.Trim();
var output = item.Output?.Trim();
if (string.IsNullOrEmpty(input) && string.IsNullOrEmpty(output)) return;
if (string.IsNullOrEmpty(input)) { CopyRequested?.Invoke(this, output!); return; }
if (string.IsNullOrEmpty(output)) { CopyRequested?.Invoke(this, input!); return; }
CopyRequested?.Invoke(this, $"{input} = {output}");
}
}