mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 03:36:44 +02:00
[PT Run] TimeDate plugin (#16662)
* create plugin * Update plugin code * fix deps * last changes * unix * new results and small changes * Update settings name * make spellcheck happy * new time/date formats * add comment * code cleanup, installer, signing pipeline * fix unix result * UnitTests * spell fix * Update tests, Timestamp query feature * new formats * last changes * last changes * unit tests and fixes * cjhanges and fixes * fix installer * fix settings class init * context menu * fix tests * add settings tests * update/fix DateTimeResult tests * small improvements * update pipeline * enable analyzer * fixes and improvements * spell fix * dev docs * doc fixes * spell fix * last changes * changes and fixes * fixes and test updates * improvements * last changes * try to fix tests * remove obsolete code * add info to test log * fix search * tag fix * tests * change tests * update dev docs * fix spelling * fix culture for ui strings * improvements based on feedback * improve global search * improve text * docs improvement * add settings note * fix and update tests * fix spelling
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal class AvailableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the time/date value
|
||||
/// </summary>
|
||||
internal string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text used for the subtitle and as search term
|
||||
/// </summary>
|
||||
internal string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an an alternative search tag that will be evaluated if label doesn't match. For example we like to show the era on searches for 'year' too.
|
||||
/// </summary>
|
||||
internal string AlternativeSearchTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the type of result
|
||||
/// </summary>
|
||||
internal ResultIconType IconType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path to the icon
|
||||
/// </summary>
|
||||
/// <param name="theme">Theme</param>
|
||||
/// <returns>Path</returns>
|
||||
internal string GetIconPath(string theme)
|
||||
{
|
||||
return IconType switch
|
||||
{
|
||||
ResultIconType.Time => $"Images\\time.{theme}.png",
|
||||
ResultIconType.Date => $"Images\\calendar.{theme}.png",
|
||||
ResultIconType.DateTime => $"Images\\timeDate.{theme}.png",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal enum ResultIconType
|
||||
{
|
||||
Time,
|
||||
Date,
|
||||
DateTime,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal class AvailableResultsList
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list with all available date time formats
|
||||
/// </summary>
|
||||
/// <param name="isKeywordSearch">Is this a search with plugin activation keyword or not</param>
|
||||
/// <param name="timeLong">Required for UnitTest: Show time in long format</param>
|
||||
/// <param name="dateLong">Required for UnitTest: Show date in long format</param>
|
||||
/// <param name="timestamp">Required for UnitTest: Use custom <see cref="DateTime"/> object to calculate results</param>
|
||||
/// <returns>List of results</returns>
|
||||
internal static List<AvailableResult> GetList(bool isKeywordSearch, bool? timeLong = null, bool? dateLong = null, DateTime? timestamp = null)
|
||||
{
|
||||
List<AvailableResult> results = new List<AvailableResult>();
|
||||
bool timeExtended = timeLong ?? TimeDateSettings.Instance.TimeWithSeconds;
|
||||
bool dateExtended = dateLong ?? TimeDateSettings.Instance.DateWithWeekday;
|
||||
bool isSystemDateTime = timestamp == null;
|
||||
Calendar calendar = CultureInfo.CurrentCulture.Calendar;
|
||||
DateTime dateTimeNow = timestamp ?? DateTime.Now;
|
||||
DateTime dateTimeNowUtc = dateTimeNow.ToUniversalTime();
|
||||
|
||||
results.AddRange(new[]
|
||||
{
|
||||
// This range is reserved for the following three results: Time, Date, Now
|
||||
// Don't add any new result in this range! For new results, please use the next range.
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Time, timeExtended, dateExtended), CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Time,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagTimeNow"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Date, timeExtended, dateExtended), CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Date,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagDateNow"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.DateTime, timeExtended, dateExtended), CultureInfo.CurrentCulture),
|
||||
Label = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_DateAndTime", "Microsoft_plugin_timedate_Now"),
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
});
|
||||
|
||||
if (isKeywordSearch || !TimeDateSettings.Instance.OnlyDateTimeNowGlobal)
|
||||
{
|
||||
// We use long instead of int for unix time stamp because int ist to small after 03:14:07 UTC 2038-01-19
|
||||
long unixTimestamp = (long)dateTimeNowUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
int weekOfYear = calendar.GetWeekOfYear(dateTimeNow, DateTimeFormatInfo.CurrentInfo.CalendarWeekRule, DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek);
|
||||
string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow));
|
||||
string eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow));
|
||||
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.Time, timeExtended, dateExtended), CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_TimeUtc,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, string.Empty, "Microsoft_plugin_timedate_SearchTagTimeNow"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString(TimeAndDateHelper.GetStringFormat(FormatStringType.DateTime, timeExtended, dateExtended), CultureInfo.CurrentCulture),
|
||||
Label = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_DateAndTimeUtc", "Microsoft_plugin_timedate_NowUtc"),
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = unixTimestamp.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Unix,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Hour.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Hour,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Minute.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Minute,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Second.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Second,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Millisecond.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Millisecond,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagTime"),
|
||||
IconType = ResultIconType.Time,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTimeFormatInfo.CurrentInfo.GetDayName(dateTimeNow.DayOfWeek),
|
||||
Label = Resources.Microsoft_plugin_timedate_Day,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = TimeAndDateHelper.GetNumberOfDayInWeek(dateTimeNow).ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayOfWeek,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Day.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayOfMonth,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.DayOfYear.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayOfYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = TimeAndDateHelper.GetWeekOfMonth(dateTimeNow).ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_WeekOfMonth,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = weekOfYear.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_WeekOfYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTimeFormatInfo.CurrentInfo.GetMonthName(dateTimeNow.Month),
|
||||
Label = Resources.Microsoft_plugin_timedate_Month,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Month.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_MonthOfYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString("M", CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayMonth,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = calendar.GetYear(dateTimeNow).ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Year,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = era,
|
||||
Label = Resources.Microsoft_plugin_timedate_Era,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagEra"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = era != eraShort ? eraShort : string.Empty, // Setting value to empty string if 'era == eraShort'. This result will be filtered later.
|
||||
Label = Resources.Microsoft_plugin_timedate_EraAbbreviation,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagEra"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString("Y", CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_MonthYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.Ticks.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString("u"),
|
||||
Label = Resources.Microsoft_plugin_timedate_UniversalTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString("s"),
|
||||
Label = Resources.Microsoft_plugin_timedate_Iso8601,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Iso8601Utc,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Iso8601Zone,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString("yyyy-MM-ddTHH:mm:ss'Z'", CultureInfo.InvariantCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_Iso8601ZoneUtc,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToString("R"),
|
||||
Label = Resources.Microsoft_plugin_timedate_Rfc1123,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Return only results where value is not empty
|
||||
// This can happen, for example, when we can't read the 'era' or when 'era == era abbreviation' and we set value explicitly to an empty string.
|
||||
return results.Where(x => !string.IsNullOrEmpty(x.Value)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
using Wox.Plugin;
|
||||
using Wox.Plugin.Logger;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal static class ResultHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the string based on the requested type
|
||||
/// </summary>
|
||||
/// <param name="isSystemTimeDate">Does the user search for system date/time?</param>
|
||||
/// <param name="stringId">Id of the string. (Example: `MyString` for `MyString` and `MyStringNow`)</param>
|
||||
/// <param name="stringIdNow">Optional string id for now case</param>
|
||||
/// <returns>The string from the resource file, or <see cref="string.Empty"/> otherwise.</returns>
|
||||
internal static string SelectStringFromResources(bool isSystemTimeDate, string stringId, string stringIdNow = default)
|
||||
{
|
||||
if (!isSystemTimeDate)
|
||||
{
|
||||
return Resources.ResourceManager.GetString(stringId, CultureInfo.CurrentUICulture) ?? string.Empty;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(stringIdNow))
|
||||
{
|
||||
return Resources.ResourceManager.GetString(stringIdNow, CultureInfo.CurrentUICulture) ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Resources.ResourceManager.GetString(stringId + "Now", CultureInfo.CurrentUICulture) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the given text to the clipboard
|
||||
/// </summary>
|
||||
/// <param name="text">The text to copy to the clipboard</param>
|
||||
/// <returns><see langword="true"/>The text successful copy to the clipboard, otherwise <see langword="false"/></returns>
|
||||
/// <remarks>Code copied from TimeZone plugin</remarks>
|
||||
internal static bool CopyToClipBoard(in string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.Clear();
|
||||
Clipboard.SetText(text);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Exception("Can't copy to clipboard", exception, typeof(ResultHelper));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a tool tip for the alternative search tags
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="AvailableResult"/>.</param>
|
||||
/// <returns>New <see cref="ToolTipData"/> object or null if <see cref="AvailableResult.AlternativeSearchTag"/> is empty.</returns>
|
||||
internal static ToolTipData GetSearchTagToolTip(AvailableResult result, out Visibility visibility)
|
||||
{
|
||||
switch (string.IsNullOrEmpty(result.AlternativeSearchTag))
|
||||
{
|
||||
case true:
|
||||
visibility = Visibility.Hidden;
|
||||
return null;
|
||||
default:
|
||||
visibility = Visibility.Visible;
|
||||
return new ToolTipData(Resources.Microsoft_plugin_timedate_ToolTipAlternativeSearchTag, result.AlternativeSearchTag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a result with an error message that only numbers can't be parsed
|
||||
/// </summary>
|
||||
/// <returns>Element of type <see cref="Result"/>.</returns>
|
||||
internal static Result CreateNumberErrorResult(string theme) => new Result()
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_timedate_ErrorResultTitle,
|
||||
SubTitle = Resources.Microsoft_plugin_timedate_ErrorResultSubTitle,
|
||||
IcoPath = $"Images\\Warning.{theme}.png",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Plugin;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// SearchController: Class tot hold the search method that filter available date time formats
|
||||
/// Extra class to simplify code in <see cref="Main"/> class
|
||||
/// </summary>
|
||||
internal static class SearchController
|
||||
{
|
||||
/// <summary>
|
||||
/// Var that holds the delimiter between format and date
|
||||
/// </summary>
|
||||
private const string InputDelimiter = "::";
|
||||
|
||||
/// <summary>
|
||||
/// A list of conjunctions that we ignore on search
|
||||
/// </summary>
|
||||
private static readonly string[] _conjunctionList = Resources.Microsoft_plugin_timedate_Search_ConjunctionList.Split("; ");
|
||||
|
||||
/// <summary>
|
||||
/// Searches for results
|
||||
/// </summary>
|
||||
/// <param name="query">Search query object</param>
|
||||
/// <returns>List of Wox <see cref="Result"/>s.</returns>
|
||||
internal static List<Result> ExecuteSearch(Query query, string iconTheme)
|
||||
{
|
||||
List<AvailableResult> availableFormats = new List<AvailableResult>();
|
||||
List<Result> results = new List<Result>();
|
||||
bool isKeywordSearch = !string.IsNullOrEmpty(query.ActionKeyword);
|
||||
bool isEmptySearchInput = string.IsNullOrEmpty(query.Search);
|
||||
string searchTerm = query.Search;
|
||||
|
||||
// Empty search without keyword => return no results
|
||||
if (!isKeywordSearch && isEmptySearchInput)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
// Conjunction search without keyword => return no results
|
||||
// (This improves the results on global queries.)
|
||||
if (!isKeywordSearch && _conjunctionList.Any(x => x.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
// Switch search type
|
||||
if (isEmptySearchInput)
|
||||
{
|
||||
// Return all results for system time/date on empty keyword search
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
|
||||
}
|
||||
else if (Regex.IsMatch(searchTerm, @".+" + Regex.Escape(InputDelimiter) + @".+"))
|
||||
{
|
||||
// Search for specified format with specified time/date value
|
||||
var userInput = searchTerm.Split(InputDelimiter);
|
||||
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp))
|
||||
{
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
searchTerm = userInput[0];
|
||||
}
|
||||
}
|
||||
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp))
|
||||
{
|
||||
// Return all formats for specified time/date value
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
searchTerm = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Search for specified format with system time/date
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
|
||||
}
|
||||
|
||||
// Check searchTerm after getting results to select type of result list
|
||||
if (string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
// Generate list with all results
|
||||
foreach (var f in availableFormats)
|
||||
{
|
||||
results.Add(new Result
|
||||
{
|
||||
Title = f.Value,
|
||||
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
|
||||
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
|
||||
ToolTipVisibility = v,
|
||||
IcoPath = f.GetIconPath(iconTheme),
|
||||
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
|
||||
ContextData = f,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate filtered list of results
|
||||
foreach (var f in availableFormats)
|
||||
{
|
||||
var resultMatchScore = GetMatchScore(searchTerm, f.Label, f.AlternativeSearchTag, !isKeywordSearch);
|
||||
|
||||
if (resultMatchScore > 0)
|
||||
{
|
||||
results.Add(new Result
|
||||
{
|
||||
Title = f.Value,
|
||||
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
|
||||
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
|
||||
ToolTipVisibility = v,
|
||||
IcoPath = f.GetIconPath(iconTheme),
|
||||
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
|
||||
Score = resultMatchScore,
|
||||
ContextData = f,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If search term is only a number that can't be parsed return an error message
|
||||
if (!isEmptySearchInput && results.Count == 0 && searchTerm.Any(char.IsNumber) && Regex.IsMatch(searchTerm, @"\w+\d+$") &&
|
||||
!searchTerm.Contains(InputDelimiter) && !searchTerm.Any(char.IsWhiteSpace) && !searchTerm.Any(char.IsPunctuation))
|
||||
{
|
||||
// Without plugin key word show only if message is not hidden by setting
|
||||
if (isKeywordSearch || !TimeDateSettings.Instance.HideNumberMessageOnGlobalQuery)
|
||||
{
|
||||
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the format for a match with the user query and returns the score.
|
||||
/// </summary>
|
||||
/// <param name="query">The user query.</param>
|
||||
/// <param name="label">The label of the format.</param>
|
||||
/// <param name="tags">The search tag list as string.</param>
|
||||
/// <param name="isGlobalSearch">Is this a global search?</param>
|
||||
/// <returns>The score for the result.</returns>
|
||||
private static int GetMatchScore(string query, string label, string tags, bool isGlobalSearch)
|
||||
{
|
||||
// The query is global and the first word don't match any word in the label or tags => Return score of zero
|
||||
if (isGlobalSearch)
|
||||
{
|
||||
char[] chars = new char[] { ' ', ',', ';', '(', ')' };
|
||||
string queryFirstWord = query.Split(chars)[0];
|
||||
string[] words = $"{label} {tags}".Split(chars);
|
||||
|
||||
if (!words.Any(x => x.Trim().Equals(queryFirstWord, StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get match for label (or for tags if label score is <1)
|
||||
int score = StringMatcher.FuzzySearch(query, label).Score;
|
||||
if (score < 1)
|
||||
{
|
||||
foreach (string t in tags.Split(";"))
|
||||
{
|
||||
var tagScore = StringMatcher.FuzzySearch(query, t.Trim()).Score / 2;
|
||||
if (tagScore > score)
|
||||
{
|
||||
score = tagScore / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal static class TimeAndDateHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the format for the time string
|
||||
/// </summary>
|
||||
/// <param name="targetFormat">Type of format</param>
|
||||
/// <param name="timeLong">Show date with weekday and name of month (long format)</param>
|
||||
/// <param name="dateLong">Show time with seconds (long format)</param>
|
||||
/// <returns>String that identifies the time/date format (<see href="https://docs.microsoft.com/en-us/dotnet/api/system.datetime.tostring"/>)</returns>
|
||||
internal static string GetStringFormat(FormatStringType targetFormat, bool timeLong, bool dateLong)
|
||||
{
|
||||
switch (targetFormat)
|
||||
{
|
||||
case FormatStringType.Time:
|
||||
return timeLong ? "T" : "t";
|
||||
case FormatStringType.Date:
|
||||
return dateLong ? "D" : "d";
|
||||
case FormatStringType.DateTime:
|
||||
if (timeLong & dateLong)
|
||||
{
|
||||
return "F"; // Friday, October 31, 2008 5:04:32 PM
|
||||
}
|
||||
else if (timeLong & !dateLong)
|
||||
{
|
||||
return "G"; // 10/31/2008 5:04:32 PM
|
||||
}
|
||||
else if (!timeLong & dateLong)
|
||||
{
|
||||
return "f"; // Friday, October 31, 2008 5:04 PM
|
||||
}
|
||||
else
|
||||
{
|
||||
// (!timeLong & !dateLong)
|
||||
return "g"; // 10/31/2008 5:04 PM
|
||||
}
|
||||
|
||||
default:
|
||||
return string.Empty; // Windows default based on current culture settings
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number week in the month (Used code from 'David Morton' from <see href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/bf504bba-85cb-492d-a8f7-4ccabdf882cb/get-week-number-for-month"/>)
|
||||
/// </summary>
|
||||
/// <param name="date">date</param>
|
||||
/// <returns>Number of week in the month</returns>
|
||||
internal static int GetWeekOfMonth(DateTime date)
|
||||
{
|
||||
DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);
|
||||
|
||||
while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
|
||||
{
|
||||
date = date.AddDays(1);
|
||||
}
|
||||
|
||||
return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of the day in the week
|
||||
/// </summary>
|
||||
/// <param name="date">Date</param>
|
||||
/// <returns>Number of the day in the week</returns>
|
||||
internal static int GetNumberOfDayInWeek(DateTime date)
|
||||
{
|
||||
int daysInWeek = 7;
|
||||
int adjustment = 1; // We count from 1 to 7 and not from 0 to 6
|
||||
int formatSettingFirstDayOfWeek = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
|
||||
|
||||
return ((int)(date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert input string to a <see cref="DateTime"/> object in local time
|
||||
/// </summary>
|
||||
/// <param name="input">String with date/time</param>
|
||||
/// <param name="timestamp">The new <see cref="DateTime"/> object</param>
|
||||
/// <returns>True on success, otherwise false</returns>
|
||||
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp)
|
||||
{
|
||||
if (DateTime.TryParse(input, out timestamp))
|
||||
{
|
||||
// Known date/time format
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^u\d+") && input.Length <= 12 && long.TryParse(input.TrimStart('u'), out long secondsInt))
|
||||
{
|
||||
// unix time stamp
|
||||
// we use long instead of int because int ist to small after 03:14:07 UTC 2038-01-19
|
||||
timestamp = new DateTime(1970, 1, 1).AddSeconds(secondsInt).ToLocalTime();
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^ft\d+") && long.TryParse(input.TrimStart("ft".ToCharArray()), out long secondsLong))
|
||||
{
|
||||
// windows file time
|
||||
timestamp = new DateTime(secondsLong);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of time/date format
|
||||
/// </summary>
|
||||
internal enum FormatStringType
|
||||
{
|
||||
Time,
|
||||
Date,
|
||||
DateTime,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Additional settings for the WindowWalker plugin.
|
||||
/// </summary>
|
||||
/// <remarks>Some code parts reused from TimeZone plugin.</remarks>
|
||||
internal sealed class TimeDateSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Are the class properties initialized with default values
|
||||
/// </summary>
|
||||
private readonly bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// An instance of the class <see cref="TimeDateSettings"></see>
|
||||
/// </summary>
|
||||
private static TimeDateSettings instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to show only the time and date in global results or not
|
||||
/// </summary>
|
||||
internal bool OnlyDateTimeNowGlobal { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to show the time with seconds or not
|
||||
/// </summary>
|
||||
internal bool TimeWithSeconds { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the date with the weekday or not
|
||||
/// </summary>
|
||||
internal bool DateWithWeekday { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to hide the number input error message on global results
|
||||
/// </summary>
|
||||
internal bool HideNumberMessageOnGlobalQuery { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeDateSettings"/> class.
|
||||
/// Private constructor to make sure there is never more than one instance of this class
|
||||
/// </summary>
|
||||
private TimeDateSettings()
|
||||
{
|
||||
// Init class properties with default values
|
||||
UpdateSettings(null);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance property of this class that makes sure that the first instance gets created
|
||||
/// and that all the requests end up at that one instance.
|
||||
/// The benefit of this is that we don't need additional variables/parameters
|
||||
/// to communicate the settings between plugin's classes/methods.
|
||||
/// We can simply access this one instance, whenever we need the actual settings.
|
||||
/// </summary>
|
||||
internal static TimeDateSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new TimeDateSettings();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list with all additional plugin options.
|
||||
/// </summary>
|
||||
/// <returns>A list with all additional plugin options.</returns>
|
||||
internal static List<PluginAdditionalOption> GetAdditionalOptions()
|
||||
{
|
||||
var optionList = new List<PluginAdditionalOption>
|
||||
{
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
|
||||
Key = nameof(OnlyDateTimeNowGlobal),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal,
|
||||
Value = true,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
|
||||
Key = nameof(TimeWithSeconds),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingTimeWithSeconds,
|
||||
Value = false,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
// ToDo: When description property is implemented (#15853), move the note in brackets to description.
|
||||
Key = nameof(DateWithWeekday),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingDateWithWeekday,
|
||||
Value = false,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(HideNumberMessageOnGlobalQuery),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery,
|
||||
Value = false,
|
||||
},
|
||||
};
|
||||
|
||||
return optionList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update this settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings for all power launcher plugins.</param>
|
||||
internal void UpdateSettings(PowerLauncherPluginSettings settings)
|
||||
{
|
||||
if ((settings is null || settings.AdditionalOptions is null) & _initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnlyDateTimeNowGlobal = GetSettingOrDefault(settings, nameof(OnlyDateTimeNowGlobal));
|
||||
TimeWithSeconds = GetSettingOrDefault(settings, nameof(TimeWithSeconds));
|
||||
DateWithWeekday = GetSettingOrDefault(settings, nameof(DateWithWeekday));
|
||||
HideNumberMessageOnGlobalQuery = GetSettingOrDefault(settings, nameof(HideNumberMessageOnGlobalQuery));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return one <see cref="bool"/> setting of the given settings list with the given name.
|
||||
/// </summary>
|
||||
/// <param name="settings">The object that contain all settings.</param>
|
||||
/// <param name="name">The name of the setting.</param>
|
||||
/// <returns>A settings value.</returns>
|
||||
private static bool GetSettingOrDefault(PowerLauncherPluginSettings settings, string name)
|
||||
{
|
||||
var option = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == name);
|
||||
|
||||
// If a setting isn't available, we use the value defined in the method GetAdditionalOptions() as fallback.
|
||||
// We can use First() instead of FirstOrDefault() because the values must exist. Otherwise, we made a mistake when defining the settings.
|
||||
return option?.Value ?? GetAdditionalOptions().First(x => x.Key == name).Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user