diff --git a/Flow.Launcher.Test/Plugins/CalculatorTest.cs b/Flow.Launcher.Test/Plugins/CalculatorTest.cs index 4e40d3645b0..3f0dc53d113 100644 --- a/Flow.Launcher.Test/Plugins/CalculatorTest.cs +++ b/Flow.Launcher.Test/Plugins/CalculatorTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using Flow.Launcher.Plugin.Calculator; +using Flow.Launcher.Plugin.Calculator.Storage; using Mages.Core; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -17,7 +18,8 @@ public class CalculatorPluginTest DecimalSeparator = DecimalSeparator.UseSystemLocale, MaxDecimalPlaces = 10, ShowErrorMessage = false, // Make sure we return the empty results when error occurs - UseThousandsSeparator = true // Default value + UseThousandsSeparator = true, // Default value + EnableHistory = true, }; private readonly Engine _engine = new(new Configuration { @@ -40,6 +42,14 @@ public CalculatorPluginTest() if (engineField == null) Assert.Fail("Could not find static field 'MagesEngine' on Flow.Launcher.Plugin.Calculator.Main"); engineField.SetValue(null, _engine); + + SetHistory(new History()); + } + + [SetUp] + public void SetUp() + { + SetHistory(new History()); } [Test] @@ -80,6 +90,34 @@ public void ThousandsSeparatorTest_LargeNumber() ClassicAssert.AreEqual("1234567", result); } + [Test] + public void CalculationHistory_IsStoredWhenEnabled() + { + _settings.EnableHistory = true; + + _plugin.Query(new Plugin.Query { Search = "1+1" }); + WaitForHistoryDebounce(); + + ClassicAssert.AreEqual(1, GetHistory().Items.Count); + ClassicAssert.AreEqual("1+1", GetHistory().Items[0].Query); + ClassicAssert.AreEqual("2", GetHistory().Items[0].CopyText); + ClassicAssert.IsNotEmpty(GetHistory().Items[0].SubTitle); + } + + [Test] + public void CalculationHistory_IsNotReturnedWhenDisabled() + { + _settings.EnableHistory = true; + _plugin.Query(new Plugin.Query { Search = "1+1" }); + WaitForHistoryDebounce(); + + _settings.EnableHistory = false; + var results = _plugin.Query(new Plugin.Query { Search = "2+2" }); + + ClassicAssert.AreEqual(1, results.Count); + ClassicAssert.AreEqual("4", results[0].Title); + } + // Basic operations [TestCase(@"1+1", "2")] [TestCase(@"2-1", "1")] @@ -130,5 +168,36 @@ private string GetCalculationResult(string expression) }); return results.Count > 0 ? results[0].Title : string.Empty; } + + private void WaitForHistoryDebounce() + { + var flushPendingItemMethod = typeof(History).GetMethod("FlushPendingItem", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.That(flushPendingItemMethod, Is.Not.Null, + "Could not find method 'FlushPendingItem' on Flow.Launcher.Plugin.Calculator.Storage.History"); + + flushPendingItemMethod!.Invoke(GetHistory(), null); + } + + private History GetHistory() + { + var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.That(historyProperty, Is.Not.Null, + "Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main"); + + var history = historyProperty!.GetValue(_plugin) as History; + Assert.That(history, Is.Not.Null, + "Property 'History' on Flow.Launcher.Plugin.Calculator.Main' was not a calculator History instance"); + + return history!; + } + + private void SetHistory(History history) + { + var historyProperty = typeof(Main).GetProperty("History", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.That(historyProperty, Is.Not.Null, + "Could not find property 'History' on Flow.Launcher.Plugin.Calculator.Main"); + + historyProperty!.SetValue(_plugin, history); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs b/Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs new file mode 100644 index 00000000000..e14d7eaf089 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/HistoryCreationMode.cs @@ -0,0 +1,23 @@ +using Flow.Launcher.Localization.Attributes; + +namespace Flow.Launcher.Plugin.Calculator +{ + /// + /// Represents the different modes for saving calculator calculations into the history. + /// + [EnumLocalize] + public enum HistoryCreationMode + { + /// + /// Saves calculations into the history automatically as the query is typed. + /// + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_query))] + OnQuery, + + /// + /// Saves calculations into the history only when the action is executed (e.g. Enter is pressed). + /// + [EnumLocalizeKey(nameof(Localize.flowlauncher_plugin_calculator_history_creation_mode_on_enter))] + OnEnter + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs b/Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs new file mode 100644 index 00000000000..0b367456bb1 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/HistoryHelper.cs @@ -0,0 +1,101 @@ +using System; +using System.Globalization; +using Flow.Launcher.Plugin; +using Flow.Launcher.Plugin.Calculator.Storage; + +namespace Flow.Launcher.Plugin.Calculator +{ + /// + /// Helper class providing utility methods for formatting relative time strings and creating pending history items. + /// + internal static class HistoryHelper + { + /// + /// Formats a DateTime value into a localized relative time string (e.g. "just now", "5 minutes ago"). + /// + /// The plugin init context used to retrieve localized strings. + /// The time when the calculation was recorded. + /// A localized relative time delta string. + public static string GetTimeDeltaString(PluginInitContext context, DateTime calculatedAt) + { + var now = DateTime.Now; + var timeSpan = now - calculatedAt; + + if (timeSpan.TotalSeconds < 0) + { + timeSpan = TimeSpan.Zero; + } + + if (timeSpan.TotalSeconds < 60) + { + return context == null ? "just now" : Localize.flowlauncher_plugin_calculator_time_just_now(); + } + if (timeSpan.TotalMinutes < 60) + { + var minutes = (int)timeSpan.TotalMinutes; + if (minutes == 1) + { + return context == null ? "1 minute ago" : Localize.flowlauncher_plugin_calculator_time_minute_ago(); + } + return context == null ? $"{minutes} minutes ago" : Localize.flowlauncher_plugin_calculator_time_minutes_ago(minutes); + } + if (timeSpan.TotalHours < 24) + { + var hours = (int)timeSpan.TotalHours; + if (hours == 1) + { + return context == null ? "1 hour ago" : Localize.flowlauncher_plugin_calculator_time_hour_ago(); + } + return context == null ? $"{hours} hours ago" : Localize.flowlauncher_plugin_calculator_time_hours_ago(hours); + } + if (timeSpan.TotalDays < 30) + { + var days = (int)timeSpan.TotalDays; + if (days == 1) + { + return context == null ? "1 day ago" : Localize.flowlauncher_plugin_calculator_time_day_ago(); + } + return context == null ? $"{days} days ago" : Localize.flowlauncher_plugin_calculator_time_days_ago(days); + } + if (timeSpan.TotalDays < 365) + { + var months = (int)(timeSpan.TotalDays / 30); + if (months == 1) + { + return context == null ? "1 month ago" : Localize.flowlauncher_plugin_calculator_time_month_ago(); + } + return context == null ? $"{months} months ago" : Localize.flowlauncher_plugin_calculator_time_months_ago(months); + } + var years = (int)(timeSpan.TotalDays / 365); + if (years == 1) + { + return context == null ? "1 year ago" : Localize.flowlauncher_plugin_calculator_time_year_ago(); + } + return context == null ? $"{years} years ago" : Localize.flowlauncher_plugin_calculator_time_years_ago(years); + } + + /// + /// Creates a representing a calculation that is currently being typed by the user. + /// + /// The plugin init context. + /// The query result object. + /// The text representation of the calculation result. + /// The math expression string. + /// A new instance. + public static PendingHistoryItem CreatePendingHistoryItem(PluginInitContext context, Result result, string calcResult, string expression) + { + var calculatedAt = DateTime.Now; + var copyToClipboard = context == null + ? "Copy this number to the clipboard" + : Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(); + var timeDeltaStr = GetTimeDeltaString(context, calculatedAt); + var historySubtitle = context == null + ? string.Format(CultureInfo.CurrentCulture, "Calculated {0}", timeDeltaStr) + : Localize.flowlauncher_plugin_calculator_history_subtitle(timeDeltaStr); + var subtitle = + $"{calcResult} - {copyToClipboard}" + + $"\n{historySubtitle}"; + return new PendingHistoryItem(result, expression, result.Action, subtitle, calculatedAt); + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Images/history.png b/Plugins/Flow.Launcher.Plugin.Calculator/Images/history.png new file mode 100644 index 00000000000..58ebafd56a9 Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.Calculator/Images/history.png differ diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml index 5f9a3ca5a6c..e88b12df54a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/en.xaml @@ -17,4 +17,22 @@ Show thousands separator in results Copy failed, please try later Show error message when calculation fails - \ No newline at end of file + Enable History + {0} + just now + 1 minute ago + {0} minutes ago + 1 hour ago + {0} hours ago + 1 day ago + {0} days ago + 1 month ago + {0} months ago + 1 year ago + {0} years ago + History save mode + Save on query + Save on Enter + Warning: Due to the plugin's architecture, saving on query may register incomplete calculations in the history while you type. + Copy the result of this expression to the clipboard + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml index 6f3ab9540eb..37218c19177 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Languages/pt-br.xaml @@ -1,4 +1,4 @@ - + Calculadora @@ -15,4 +15,10 @@ Show thousands separator in results Copy failed, please try later Show error message when calculation fails + Habilitar Histórico + Calculado em {0} + Modo de salvar no histórico + Salvar por query + Salvar por enter + Aviso: Devido à arquitetura do plugin, salvar por query pode registrar cálculos incompletos no histórico enquanto você digita. diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs index 1b9a38e1819..11d7c127394 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Main.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Windows.Controls; +using Flow.Launcher.Plugin.Calculator.Storage; using Flow.Launcher.Plugin.Calculator.ViewModels; using Flow.Launcher.Plugin.Calculator.Views; using Mages.Core; @@ -24,8 +25,10 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider private const string Comma = ","; private const string Dot = "."; private const string IcoPath = "Images/calculator.png"; + private static readonly List EmptyResults = []; + private History History { get; set; } = null!; internal static PluginInitContext Context { get; private set; } = null!; private Settings _settings; @@ -35,6 +38,7 @@ public void Init(PluginInitContext context) { Context = context; _settings = context.API.LoadSettingJsonStorage(); + History = context.API.LoadSettingJsonStorage(); _viewModel = new SettingsViewModel(_settings); MagesEngine = new Engine(new Configuration @@ -57,7 +61,7 @@ public List Query(Query query) { var search = query.Search; bool isFunctionPresent = FunctionRegex.IsMatch(search); - + bool isValidResultToAddHistory = true; // Mages is case sensitive, so we need to convert all function names to lower case. search = FunctionRegex.Replace(search, m => m.Value.ToLowerInvariant()); @@ -119,43 +123,68 @@ public List Query(Query query) if (result.ToString() == "NaN") { result = Localize.flowlauncher_plugin_calculator_not_a_number(); + isValidResultToAddHistory = false; } if (result is Function) { result = Localize.flowlauncher_plugin_calculator_expression_not_complete(); + isValidResultToAddHistory = false; } if (!string.IsNullOrEmpty(result.ToString())) { decimal roundedResult = Math.Round(Convert.ToDecimal(result), _settings.MaxDecimalPlaces, MidpointRounding.AwayFromZero); string newResult = FormatResult(roundedResult); - - return - [ - new Result + + var results = new List(); + var resultObject = new Result + { + Title = newResult, + IcoPath = IcoPath, + Score = 300, + // Check context nullability for unit testing + SubTitle = Context == null + ? string.Empty + : Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(), + CopyText = newResult + }; + + resultObject.Action = BuildResultAction(resultObject, newResult, expression); + + if (_settings.EnableHistory && _settings.HistoryCreationMode == HistoryCreationMode.OnQuery) + { + var item = HistoryHelper.CreatePendingHistoryItem(Context, resultObject, newResult, expression); + History.AddOrUpdate(item); + } + results.Add(resultObject); + var historyItems = _settings.EnableHistory + ? History.GetItemsExcluding(expression) + : []; + var resultsToReturn = new List(); + if (_settings.EnableHistory) + { + foreach (var item in historyItems) { - Title = newResult, - IcoPath = IcoPath, - Score = 300, - // Check context nullability for unit testing - SubTitle = Context == null ? string.Empty : Localize.flowlauncher_plugin_calculator_copy_number_to_clipboard(), - CopyText = newResult, - Action = c => + var timeDeltaStr = HistoryHelper.GetTimeDeltaString(Context, item.CalculatedAt); + var historySubtitle = Context == null + ? string.Format(CultureInfo.CurrentCulture, "{0}", timeDeltaStr) + : Localize.flowlauncher_plugin_calculator_history_subtitle(timeDeltaStr); + resultsToReturn.Add(new Result { - try - { - Context.API.CopyToClipboard(newResult); - return true; - } - catch (ExternalException) - { - Context.API.ShowMsgBox(Localize.flowlauncher_plugin_calculator_failed_to_copy()); - return false; - } - } + Title = item.Query, + SubTitle = $"{item.CopyText} - {historySubtitle}", + IcoPath = item.IcoPath, + BadgeIcoPath = item.BadgeIcoPath, + ShowBadge = item.ShowBadge, + Score = item.Score, + CopyText = item.CopyText, + Action = item.Action + }); } - ]; + } + return results.Concat(resultsToReturn).ToList(); + } } catch (Exception) @@ -175,6 +204,60 @@ public List Query(Query query) return EmptyResults; } + + + private Func BuildResultAction + ( + Result result, + string newResult, + string expression + ) + { + + var action = + !_settings.EnableHistory || _settings.HistoryCreationMode == HistoryCreationMode.OnQuery + ? CreateClipboardAction(newResult) + : CreateClipboardActionWithHistory(result, newResult, expression); + + return action; + + } + + private Func CreateClipboardAction(string newResult) + { + return (_) => + { + try + { + Context.API.CopyToClipboard(newResult); + + return true; + } + catch (ExternalException) + { + Context.API.ShowMsgBox( + Localize.flowlauncher_plugin_calculator_failed_to_copy() + ); + return false; + } + }; + } + + private Func CreateClipboardActionWithHistory(Result resultObject, string newResult, string expression) + { + var baseAction = CreateClipboardAction(newResult); + return (actionContext) => + { + if (baseAction(actionContext)) + { + var item = new HistoryItem(resultObject, newResult, expression, DateTime.Now); + History.AddOrUpdate(item); + return true; + } + return false; + }; + } + private static string PowMatchEvaluator(Match m) { // m.Groups[1].Value will be `(...)` with parens diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs index 69cd17ed2da..e94b61581eb 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Settings.cs @@ -1,4 +1,4 @@ -namespace Flow.Launcher.Plugin.Calculator; +namespace Flow.Launcher.Plugin.Calculator; public class Settings { @@ -9,4 +9,8 @@ public class Settings public bool ShowErrorMessage { get; set; } = false; public bool UseThousandsSeparator { get; set; } = true; + + public bool EnableHistory { get; set; } = false; + + public HistoryCreationMode HistoryCreationMode { get; set; } = HistoryCreationMode.OnQuery; } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs new file mode 100644 index 00000000000..8d7779c5bc4 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/History.cs @@ -0,0 +1,142 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using System.Threading; + +namespace Flow.Launcher.Plugin.Calculator.Storage; + +/// +/// Manages the collection of calculation history items, providing methods to add, update, and retrieve items. +/// +/// Supports two creation modes: +/// +/// +/// On Query Mode: Uses with a debounce timer to save calculations automatically as the user types without logging incomplete states. +/// +/// +/// On Enter Mode: Adds or updates a standard immediately without any debounce when the calculation is executed. +/// +/// +/// +/// +public class History +{ + private const int MaxItems = 5; + private const int DebounceDelayMs = 800; + + /// + /// Gets or sets the list of saved history items. + /// + [JsonInclude] + public List Items { get; set; } = []; + + [JsonIgnore] + private readonly Lock _syncRoot = new(); + + [JsonIgnore] + private Timer _debounceTimer; + + [JsonIgnore] + private PendingHistoryItem _pendingItem; + + /// + /// Adds a completed history item immediately without any debounce, or updates its timestamp if the query already exists. + /// Used when HistoryCreationMode is set to OnEnter. + /// + /// The history item to add or update. + public void AddOrUpdate(HistoryItem item) + => AddOrUpdateInternal(item); + + /// + /// Unlocked internal helper to add a history item, or update its fields if the query already exists. + /// + /// The history item to add or update. + private void AddOrUpdateInternal(HistoryItem item) + { + var currentItem = Items.FirstOrDefault(x => x.Query == item.Query); + if (currentItem != null) + { + currentItem.CalculatedAt = item.CalculatedAt; + currentItem.Title = item.Title; + currentItem.SubTitle = item.SubTitle; + currentItem.CopyText = item.CopyText; + currentItem.Action = item.Action; + } + else + { + Add(item); + } + } + + /// + /// Schedules a pending history item to be added or updated after a debounce delay. + /// Used when HistoryCreationMode is set to OnQuery to prevent spamming the history with incomplete queries as the user types. + /// + /// The pending history item to be debounced. + public void AddOrUpdate(PendingHistoryItem pendingItem) + { + lock (_syncRoot) + { + _pendingItem = pendingItem; + + if (_debounceTimer == null) + { + _debounceTimer = new Timer(_ => FlushPendingItem(), null, DebounceDelayMs, Timeout.Infinite); + } + else + { + _debounceTimer.Change(DebounceDelayMs, Timeout.Infinite); + } + } + } + + /// + /// Retrieves a list of history items, sorted by calculation date in descending order, excluding the specified expression. + /// + /// The expression to exclude from the results. + /// A list of history items matching the criteria. + public List GetItemsExcluding(string expression) + { + lock (_syncRoot) + { + return Items + .Where(x => x.Query != expression) + .OrderByDescending(x => x.CalculatedAt) + .ToList(); + } + } + + /// + /// Adds a history item to the list, removing the oldest calculation if the maximum count is exceeded. + /// + /// The history item to add. + private void Add(HistoryItem item) + { + if (Items.Count >= MaxItems) + { + + Items = Items.OrderBy(x => x.CalculatedAt).ToList(); + Items.RemoveAt(0); + } + Items.Add(item); + } + + /// + /// Flushes the currently pending history item by adding it to the list. + /// + private void FlushPendingItem() + { + lock (_syncRoot) + { + if (_pendingItem == null) + { + return; + } + + var item = new HistoryItem(_pendingItem); + AddOrUpdateInternal(item); + + _pendingItem = null; + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs new file mode 100644 index 00000000000..94e3d5b5ad0 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/HistoryItem.cs @@ -0,0 +1,41 @@ +using System; +using System.Text.Json.Serialization; + +namespace Flow.Launcher.Plugin.Calculator.Storage; + +public class HistoryItem : Result +{ + public string Query { get; set; } = string.Empty; + public DateTime CalculatedAt { get; set; } + + [JsonIgnore] private const string BadgeIconPath = "Images/history.png"; + + public HistoryItem() + { + } + + public HistoryItem(Result item ,string result, string expression, DateTime calculatedAt) + { + CalculatedAt = calculatedAt; + Title = expression; + SubTitle = result; + Score = 300; + IcoPath = item.IcoPath; + Query = expression; + BadgeIcoPath = BadgeIconPath; + ShowBadge = true; + CopyText = item.Title; + Action = item.Action; + + } + + + /// + /// Initializes a new instance of the class from a . + /// + /// The pending history item to copy properties from. + public HistoryItem(PendingHistoryItem item) + : this(item.Result, item.SubTitle, item.Expression, item.CalculatedAt) + { + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs new file mode 100644 index 00000000000..972932199ae --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Storage/PendingHistoryItem.cs @@ -0,0 +1,14 @@ +using System; + +namespace Flow.Launcher.Plugin.Calculator.Storage; + +public sealed record PendingHistoryItem + ( + Result Result, + string Expression, + Func Action, + string SubTitle, + DateTime CalculatedAt + ); + + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs index 79236bdf8e5..8c78b697c6f 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.Calculator/ViewModels/SettingsViewModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; namespace Flow.Launcher.Plugin.Calculator.ViewModels; @@ -23,4 +23,36 @@ public DecimalSeparator SelectedDecimalSeparator } } } + + public bool EnableHistory + { + get => Settings.EnableHistory; + set + { + if (Settings.EnableHistory != value) + { + Settings.EnableHistory = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ShowHistoryQueryWarning)); + } + } + } + + public List AllHistoryCreationMode { get; } = HistoryCreationModeLocalized.GetValues(); + + public HistoryCreationMode SelectedHistoryCreationMode + { + get => Settings.HistoryCreationMode; + set + { + if (Settings.HistoryCreationMode != value) + { + Settings.HistoryCreationMode = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ShowHistoryQueryWarning)); + } + } + } + + public bool ShowHistoryQueryWarning => EnableHistory && SelectedHistoryCreationMode == HistoryCreationMode.OnQuery; } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml index 82f8eec60f3..ecb2f8fcc30 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Views/CalculatorSettings.xaml @@ -11,12 +11,19 @@ d:DesignWidth="800" mc:Ignorable="d"> + + + + + + + @@ -79,5 +86,49 @@ VerticalAlignment="Center" Content="{DynamicResource flowlauncher_plugin_calculator_show_error_message}" IsChecked="{Binding Settings.ShowErrorMessage, Mode=TwoWay}" /> + + + + + + +