Merge Dev

This commit is contained in:
DB p 2022-11-23 21:11:21 +09:00
commit 2887a17ceb
14 changed files with 420 additions and 290 deletions

View file

@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Flow.Launcher.Converters
{
public class DateTimeFormatToNowConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is not string format ? null : DateTime.Now.ToString(format);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View file

@ -250,13 +250,15 @@
IsHitTestVisible="False"
Style="{DynamicResource ClockPanel}">
<TextBlock
Style="{DynamicResource DateBox}"
Text="{Binding DateText}"
Visibility="{Binding Settings.UseDate, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
x:Name="ClockBox"
Style="{DynamicResource ClockBox}"
Text="{Binding ClockText}"
Visibility="{Binding Settings.UseClock, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
x:Name="DateBox"
Style="{DynamicResource DateBox}"
Text="{Binding DateText}"
Visibility="{Binding Settings.UseDate, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
<Canvas Style="{DynamicResource SearchIconPosition}">

View file

@ -30,6 +30,10 @@ using System.Windows.Data;
using ModernWpf.Controls;
using System.Drawing;
using System.Windows.Forms.Design.Behavior;
using System.Security.Cryptography;
using System.Runtime.CompilerServices;
using Microsoft.VisualBasic.Devices;
using Microsoft.FSharp.Data.UnitSystems.SI.UnitNames;
namespace Flow.Launcher
{
@ -105,7 +109,6 @@ namespace Flow.Launcher
// since the default main window visibility is visible
// so we need set focus during startup
QueryTextBox.Focus();
_viewModel.PropertyChanged += (o, e) =>
{
switch (e.PropertyName)
@ -243,7 +246,9 @@ namespace Flow.Launcher
Icon = Properties.Resources.app,
Visible = !_settings.HideNotifyIcon
};
contextMenu = new ContextMenu();
var openIcon = new FontIcon { Glyph = "\ue71e" };
var open = new MenuItem
{
@ -280,9 +285,11 @@ namespace Flow.Launcher
positionreset.Click += (o, e) => PositionReset();
settings.Click += (o, e) => App.API.OpenSettingDialog();
exit.Click += (o, e) => Close();
contextMenu.Items.Add(open);
gamemode.ToolTip = InternationalizationManager.Instance.GetTranslation("GameModeToolTip");
positionreset.ToolTip = InternationalizationManager.Instance.GetTranslation("PositionResetToolTip");
contextMenu.Items.Add(open);
contextMenu.Items.Add(gamemode);
contextMenu.Items.Add(positionreset);
contextMenu.Items.Add(settings);
@ -359,11 +366,14 @@ namespace Flow.Launcher
_animating = true;
UpdatePosition();
Storyboard sb = new Storyboard();
Storyboard windowsb = new Storyboard();
Storyboard clocksb = new Storyboard();
Storyboard iconsb = new Storyboard();
CircleEase easing = new CircleEase(); // or whatever easing class you want
CircleEase easing = new CircleEase();
easing.EasingMode = EasingMode.EaseInOut;
var da = new DoubleAnimation
var WindowOpacity = new DoubleAnimation
{
From = 0,
To = 1,
@ -371,33 +381,76 @@ namespace Flow.Launcher
FillBehavior = FillBehavior.Stop
};
var da2 = new DoubleAnimation
var WindowMotion = new DoubleAnimation
{
From = Top + 10,
To = Top,
Duration = TimeSpan.FromSeconds(0.25),
FillBehavior = FillBehavior.Stop
};
var da3 = new DoubleAnimation
{
var IconMotion = new DoubleAnimation
{
From = 12,
To = 0,
EasingFunction = easing,
Duration = TimeSpan.FromSeconds(0.36),
FillBehavior = FillBehavior.Stop
};
Storyboard.SetTarget(da, this);
Storyboard.SetTargetProperty(da, new PropertyPath(Window.OpacityProperty));
Storyboard.SetTargetProperty(da2, new PropertyPath(Window.TopProperty));
Storyboard.SetTargetProperty(da3, new PropertyPath(TopProperty));
sb.Children.Add(da);
sb.Children.Add(da2);
iconsb.Children.Add(da3);
sb.Completed += (_, _) => _animating = false;
};
var ClockOpacity = new DoubleAnimation
{
From = 0,
To = 1,
EasingFunction = easing,
Duration = TimeSpan.FromSeconds(0.36),
FillBehavior = FillBehavior.Stop
};
double TargetIconOpacity = SearchIcon.Opacity; // Animation Target Opacity from Style
var IconOpacity = new DoubleAnimation
{
From = 0,
To = TargetIconOpacity,
EasingFunction = easing,
Duration = TimeSpan.FromSeconds(0.36),
FillBehavior = FillBehavior.Stop
};
double right = ClockPanel.Margin.Right;
var thicknessAnimation = new ThicknessAnimation
{
From = new Thickness(0, 12, right, 0),
To = new Thickness(0, 0, right, 0),
EasingFunction = easing,
Duration = TimeSpan.FromSeconds(0.36),
FillBehavior = FillBehavior.Stop
};
Storyboard.SetTargetProperty(ClockOpacity, new PropertyPath(OpacityProperty));
Storyboard.SetTargetName(thicknessAnimation, "ClockPanel");
Storyboard.SetTargetProperty(thicknessAnimation, new PropertyPath(MarginProperty));
Storyboard.SetTarget(WindowOpacity, this);
Storyboard.SetTargetProperty(WindowOpacity, new PropertyPath(Window.OpacityProperty));
Storyboard.SetTargetProperty(WindowMotion, new PropertyPath(Window.TopProperty));
Storyboard.SetTargetProperty(IconMotion, new PropertyPath(TopProperty));
Storyboard.SetTargetProperty(IconOpacity, new PropertyPath(OpacityProperty));
clocksb.Children.Add(thicknessAnimation);
clocksb.Children.Add(ClockOpacity);
windowsb.Children.Add(WindowOpacity);
windowsb.Children.Add(WindowMotion);
iconsb.Children.Add(IconMotion);
iconsb.Children.Add(IconOpacity);
windowsb.Completed += (_, _) => _animating = false;
_settings.WindowLeft = Left;
_settings.WindowTop = Top;
if (QueryTextBox.Text.Length == 0)
{
clocksb.Begin(ClockPanel);
}
iconsb.Begin(SearchIcon);
sb.Begin(FlowMainWindow);
windowsb.Begin(FlowMainWindow);
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)

View file

@ -39,6 +39,7 @@
</Window.CommandBindings>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:BorderClipConverter x:Key="BorderClipConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:TextConverter x:Key="TextConverter" />
@ -1815,13 +1816,25 @@
</Grid.RowDefinitions>
<Border Grid.Row="0">
<TextBox
x:Name="QueryTextBox"
IsReadOnly="True"
Style="{DynamicResource QueryBoxStyle}"
Text="{DynamicResource hiThere}" />
</Border>
<StackPanel x:Name="ClockPanel" Style="{DynamicResource ClockPanel}">
<TextBlock x:Name="DateBox" Style="{DynamicResource DateBox}" />
<TextBlock x:Name="ClockBox" Style="{DynamicResource ClockBox}" />
<StackPanel
x:Name="ClockPanel"
IsHitTestVisible="False"
Style="{DynamicResource ClockPanel}">
<TextBlock
x:Name="ClockBox"
Style="{DynamicResource ClockBox}"
Text="{Binding ClockText}"
Visibility="{Binding Settings.UseClock, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
x:Name="DateBox"
Style="{DynamicResource DateBox}"
Text="{Binding DateText}"
Visibility="{Binding Settings.UseDate, Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
<Canvas Style="{DynamicResource SearchIconPosition}">
<Path
@ -2162,6 +2175,12 @@
Grid.Row="0"
Grid.Column="2"
Orientation="Horizontal">
<TextBlock
Margin="0,0,10,0"
VerticalAlignment="Center"
FontSize="14"
Foreground="{DynamicResource Color04B}"
Text="{Binding ClockText}" />
<ComboBox
x:Name="TimeFormat"
Grid.Column="2"
@ -2170,14 +2189,12 @@
VerticalAlignment="Center"
FontSize="14"
ItemsSource="{Binding TimeFormatList}"
SelectedValue="{Binding Settings.TimeFormat}"
SelectionChanged="PreviewClockAndDate" />
SelectedItem="{Binding TimeFormat}" />
<ui:ToggleSwitch
IsOn="{Binding UseClock, Mode=TwoWay}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}"
Style="{DynamicResource SideToggleSwitch}"
Toggled="PreviewClockAndDate" />
Style="{DynamicResource SideToggleSwitch}" />
</StackPanel>
<TextBlock Style="{StaticResource Glyph}">
&#xec92;
@ -2200,6 +2217,12 @@
Grid.Row="0"
Grid.Column="2"
Orientation="Horizontal">
<TextBlock
Margin="0,0,10,0"
VerticalAlignment="Center"
FontSize="14"
Foreground="{DynamicResource Color04B}"
Text="{Binding DateText}" />
<ComboBox
x:Name="DateFormat"
Grid.Column="2"
@ -2208,14 +2231,12 @@
VerticalAlignment="Center"
FontSize="14"
ItemsSource="{Binding DateFormatList}"
SelectedValue="{Binding Settings.DateFormat}"
SelectionChanged="PreviewClockAndDate" />
SelectedItem="{Binding DateFormat}" />
<ui:ToggleSwitch
IsOn="{Binding UseDate, Mode=TwoWay}"
OffContent="{DynamicResource disable}"
OnContent="{DynamicResource enable}"
Style="{DynamicResource SideToggleSwitch}"
Toggled="PreviewClockAndDate" />
Style="{DynamicResource SideToggleSwitch}" />
</StackPanel>
<TextBlock Style="{StaticResource Glyph}">
&#xe787;

View file

@ -10,17 +10,14 @@ using Flow.Launcher.ViewModel;
using ModernWpf;
using ModernWpf.Controls;
using System;
using System.Drawing.Printing;
using System.IO;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Navigation;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window;
using Button = System.Windows.Controls.Button;
using Control = System.Windows.Controls.Control;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
@ -44,6 +41,7 @@ namespace Flow.Launcher
API = api;
InitializePosition();
InitializeComponent();
}
#region General
@ -64,7 +62,6 @@ namespace Flow.Launcher
pluginStoreView.Filter = PluginStoreFilter;
InitializePosition();
ClockDisplay();
}
private void OnSelectPythonDirectoryClick(object sender, RoutedEventArgs e)
@ -514,34 +511,6 @@ namespace Flow.Launcher
}
}
private void PreviewClockAndDate(object sender, RoutedEventArgs e)
{
ClockDisplay();
}
public void ClockDisplay()
{
if (settings.UseClock)
{
ClockBox.Visibility = Visibility.Visible;
ClockBox.Text = DateTime.Now.ToString(settings.TimeFormat);
}
else
{
ClockBox.Visibility = Visibility.Collapsed;
}
if (settings.UseDate)
{
DateBox.Visibility = Visibility.Visible;
DateBox.Text = DateTime.Now.ToString(settings.DateFormat);
}
else
{
DateBox.Visibility = Visibility.Collapsed;
}
}
public void InitializePosition()
{
if (settings.SettingWindowTop >= 0 && settings.SettingWindowLeft >= 0)
@ -556,6 +525,7 @@ namespace Flow.Launcher
}
WindowState = settings.SettingWindowState;
}
public double WindowLeft()
{
var screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);

View file

@ -100,29 +100,39 @@
<Setter Property="Stroke" Value="Blue" />
</Style>
<Style x:Key="BaseClockPosition" TargetType="{x:Type Canvas}" />
<Style x:Key="BaseClockPanelPosition" TargetType="{x:Type Canvas}" />
<Style x:Key="BaseClockPanel" TargetType="{x:Type StackPanel}">
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Margin" Value="0,0,14,0" />
<Setter Property="Margin" Value="0,0,66,0" />
<Setter Property="Visibility" Value="Collapsed" />
</Style>
<Style x:Key="BaseClockBox" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="22" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Foreground" Value="#8f8f8f" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,2,0" />
<Setter Property="Margin" Value="0,0,0,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=DateBox, Path=Visibility}" Value="Visible">
<Setter Property="FontSize" Value="14" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="BaseDateBox" TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Foreground" Value="#8f8f8f" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,10,0" />
<Setter Property="Margin" Value="0,0,0,0" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ClockBox, Path=Visibility}" Value="Visible">
<Setter Property="FontSize" Value="14" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
@ -141,8 +151,8 @@
x:Key="ClockPanel"
BasedOn="{StaticResource BaseClockPanel}"
TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="0,0,66,0" />
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
@ -150,30 +160,19 @@
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible" />
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:0.2" />
To="1"
Duration="0:0:0.25" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="0.0"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
@ -397,6 +396,12 @@
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style
x:Key="ClockPanelPosition"
BasedOn="{StaticResource BaseClockPanelPosition}"
TargetType="{x:Type Canvas}">
<Setter Property="Margin" Value="0,2,66,0" />
</Style>
<Style
x:Key="ItemHotkeyStyle"
BasedOn="{StaticResource BaseItemHotkeyStyle}"

View file

@ -96,18 +96,31 @@
BasedOn="{StaticResource BaseClockBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#2C5595" />
<Setter Property="FontSize" Value="18" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=DateBox, Path=Visibility}" Value="Visible">
<Setter Property="FontSize" Value="18" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="DateBox"
BasedOn="{StaticResource BaseDateBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="Foreground" Value="#2C5595" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ClockBox, Path=Visibility}" Value="Visible">
<Setter Property="FontSize" Value="18" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="ClockPanel"
BasedOn="{StaticResource BaseClockPanel}"
BasedOn="{StaticResource ClockPanel}"
TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="0,0,22,0" />
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Orientation" Value="Horizontal" />
</Style>
</ResourceDictionary>

View file

@ -123,6 +123,7 @@
x:Key="DateBox"
BasedOn="{StaticResource BaseDateBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="#71114b" />
</Style>
</ResourceDictionary>

View file

@ -159,12 +159,12 @@
x:Key="ClockBox"
BasedOn="{StaticResource BaseClockBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#b2b2b2" />
<Setter Property="Foreground" Value="#818181" />
</Style>
<Style
x:Key="DateBox"
BasedOn="{StaticResource BaseDateBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#b2b2b2" />
<Setter Property="Foreground" Value="#818181" />
</Style>
</ResourceDictionary>

View file

@ -175,6 +175,7 @@
BasedOn="{StaticResource BaseClockBox}"
TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#acacac" />
<Setter Property="FontSize" Value="22" />
</Style>
<Style
x:Key="DateBox"

View file

@ -23,6 +23,7 @@ using ISavable = Flow.Launcher.Plugin.ISavable;
using System.IO;
using System.Collections.Specialized;
using CommunityToolkit.Mvvm.Input;
using System.Globalization;
namespace Flow.Launcher.ViewModel
{
@ -77,7 +78,6 @@ namespace Flow.Launcher.ViewModel
_userSelectedRecord = _userSelectedRecordStorage.Load();
_topMostRecord = _topMostRecordStorage.Load();
InitializeKeyCommands();
ContextMenu = new ResultsViewModel(Settings)
{
@ -167,160 +167,164 @@ namespace Flow.Launcher.ViewModel
}
}
private void InitializeKeyCommands()
[RelayCommand]
private async Task ReloadPluginDataAsync()
{
EscCommand = new RelayCommand(_ =>
Hide();
await PluginManager.ReloadDataAsync().ConfigureAwait(false);
Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully"));
}
[RelayCommand]
private void LoadHistory()
{
if (SelectedIsFromQueryResults())
{
if (!SelectedIsFromQueryResults())
{
SelectedResults = Results;
}
else
{
Hide();
}
});
ClearQueryCommand = new RelayCommand(_ =>
SelectedResults = History;
History.SelectedIndex = _history.Items.Count - 1;
}
else
{
if (!string.IsNullOrEmpty(QueryText))
{
ChangeQueryText(string.Empty);
// Push Event to UI SystemQuery has changed
//OnPropertyChanged(nameof(SystemQueryText));
}
});
SelectNextItemCommand = new RelayCommand(_ => { SelectedResults.SelectNextResult(); });
SelectPrevItemCommand = new RelayCommand(_ => { SelectedResults.SelectPrevResult(); });
SelectNextPageCommand = new RelayCommand(_ => { SelectedResults.SelectNextPage(); });
SelectPrevPageCommand = new RelayCommand(_ => { SelectedResults.SelectPrevPage(); });
SelectFirstResultCommand = new RelayCommand(_ => SelectedResults.SelectFirstResult());
StartHelpCommand = new RelayCommand(_ =>
SelectedResults = Results;
}
}
[RelayCommand]
private void LoadContextMenu()
{
if (SelectedIsFromQueryResults())
{
PluginManager.API.OpenUrl("https://github.com/Flow-Launcher/Flow.Launcher/wiki/Flow-Launcher/");
});
OpenSettingCommand = new RelayCommand(_ => { App.API.OpenSettingDialog(); });
OpenResultCommand = new AsyncRelayCommand(async _ =>
// When switch to ContextMenu from QueryResults, but no item being chosen, should do nothing
// i.e. Shift+Enter/Ctrl+O right after Alt + Space should do nothing
if (SelectedResults.SelectedItem != null)
SelectedResults = ContextMenu;
}
else
{
var results = SelectedResults;
SelectedResults = Results;
}
}
[RelayCommand]
private void Backspace(object index)
{
var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins);
var result = results.SelectedItem?.Result;
if (result == null)
// GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string
var path = FilesFolders.GetPreviousExistingDirectory((_) => true, query.Search.TrimEnd('\\'));
var actionKeyword = string.IsNullOrEmpty(query.ActionKeyword) ? string.Empty : $"{query.ActionKeyword} ";
ChangeQueryText($"{actionKeyword}{path}");
}
[RelayCommand]
private void AutocompleteQuery()
{
var result = SelectedResults.SelectedItem?.Result;
if (result != null && SelectedIsFromQueryResults()) // SelectedItem returns null if selection is empty.
{
var autoCompleteText = result.Title;
if (!string.IsNullOrEmpty(result.AutoCompleteText))
{
return;
autoCompleteText = result.AutoCompleteText;
}
var hideWindow = await result.ExecuteAsync(new ActionContext
else if (!string.IsNullOrEmpty(SelectedResults.SelectedItem?.QuerySuggestionText))
{
autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText;
}
var specialKeyState = GlobalHotkey.CheckModifiers();
if (specialKeyState.ShiftPressed)
{
autoCompleteText = result.SubTitle;
}
ChangeQueryText(autoCompleteText);
}
}
[RelayCommand]
private async Task OpenResultAsync(string index)
{
var results = SelectedResults;
if (index is not null)
{
results.SelectedIndex = int.Parse(index);
}
var result = results.SelectedItem?.Result;
if (result == null)
{
return;
}
var hideWindow = await result.ExecuteAsync(new ActionContext
{
SpecialKeyState = GlobalHotkey.CheckModifiers()
}).ConfigureAwait(false);
})
.ConfigureAwait(false);
if (hideWindow)
{
Hide();
}
if (SelectedIsFromQueryResults())
{
_userSelectedRecord.Add(result);
_history.Add(result.OriginQuery.RawQuery);
}
else
{
SelectedResults = Results;
}
});
AutocompleteQueryCommand = new RelayCommand(_ =>
{
var result = SelectedResults.SelectedItem?.Result;
if (result != null && SelectedIsFromQueryResults()) // SelectedItem returns null if selection is empty.
{
var autoCompleteText = result.Title;
if (!string.IsNullOrEmpty(result.AutoCompleteText))
{
autoCompleteText = result.AutoCompleteText;
}
else if (!string.IsNullOrEmpty(SelectedResults.SelectedItem?.QuerySuggestionText))
{
autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText;
}
var specialKeyState = GlobalHotkey.CheckModifiers();
if (specialKeyState.ShiftPressed)
{
autoCompleteText = result.SubTitle;
}
ChangeQueryText(autoCompleteText);
}
});
BackspaceCommand = new RelayCommand(index =>
{
var query = QueryBuilder.Build(QueryText.Trim(), PluginManager.NonGlobalPlugins);
// GetPreviousExistingDirectory does not require trailing '\', otherwise will return empty string
var path = FilesFolders.GetPreviousExistingDirectory((_) => true, query.Search.TrimEnd('\\'));
var actionKeyword = string.IsNullOrEmpty(query.ActionKeyword) ? string.Empty : $"{query.ActionKeyword} ";
ChangeQueryText($"{actionKeyword}{path}");
});
LoadContextMenuCommand = new RelayCommand(_ =>
{
if (SelectedIsFromQueryResults())
{
// When switch to ContextMenu from QueryResults, but no item being chosen, should do nothing
// i.e. Shift+Enter/Ctrl+O right after Alt + Space should do nothing
if (SelectedResults.SelectedItem != null)
SelectedResults = ContextMenu;
}
else
{
SelectedResults = Results;
}
});
LoadHistoryCommand = new RelayCommand(_ =>
{
if (SelectedIsFromQueryResults())
{
SelectedResults = History;
History.SelectedIndex = _history.Items.Count - 1;
}
else
{
SelectedResults = Results;
}
});
ReloadPluginDataCommand = new RelayCommand(_ =>
if (hideWindow)
{
Hide();
}
_ = PluginManager
.ReloadDataAsync()
.ContinueWith(_ =>
Application.Current.Dispatcher.Invoke(() =>
{
Notification.Show(
InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully")
);
}), TaskScheduler.Default)
.ConfigureAwait(false);
});
if (SelectedIsFromQueryResults())
{
_userSelectedRecord.Add(result);
_history.Add(result.OriginQuery.RawQuery);
}
else
{
SelectedResults = Results;
}
}
[RelayCommand]
private void OpenSetting()
{
App.API.OpenSettingDialog();
}
[RelayCommand]
private void SelectHelp()
{
PluginManager.API.OpenUrl("https://www.flowlauncher.com/docs/#/usage-tips");
}
[RelayCommand]
private void SelectFirstResult()
{
SelectedResults.SelectFirstResult();
}
[RelayCommand]
private void SelectPrevPage()
{
SelectedResults.SelectPrevPage();
}
[RelayCommand]
private void SelectNextPage()
{
SelectedResults.SelectNextPage();
}
[RelayCommand]
private void SelectPrevItem()
{
SelectedResults.SelectPrevResult();
}
[RelayCommand]
private void SelectNextItem()
{
SelectedResults.SelectNextResult();
}
[RelayCommand]
private void Esc()
{
if (!SelectedIsFromQueryResults())
{
SelectedResults = Results;
}
else
{
Hide();
}
}
#endregion
@ -328,9 +332,10 @@ namespace Flow.Launcher.ViewModel
#region ViewModel Properties
public Settings Settings { get; }
public object ClockText { get; private set; }
public string ClockText { get; private set; }
public string DateText { get; private set; }
public CultureInfo cultureInfo => new CultureInfo(Settings.Language);
private async Task RegisterClockAndDateUpdateAsync()
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
@ -338,9 +343,9 @@ namespace Flow.Launcher.ViewModel
while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
{
if (Settings.UseClock)
ClockText = DateTime.Now.ToString(Settings.TimeFormat);
ClockText = DateTime.Now.ToString(Settings.TimeFormat, cultureInfo);
if (Settings.UseDate)
DateText = DateTime.Now.ToString(Settings.DateFormat);
DateText = DateTime.Now.ToString(Settings.DateFormat, cultureInfo);
}
}
public ResultsViewModel Results { get; private set; }
@ -416,6 +421,7 @@ namespace Flow.Launcher.ViewModel
/// but we don't want to move cursor to end when query is updated from TextBox
/// </summary>
/// <param name="queryText"></param>
/// <param name="reQuery">Force query even when Query Text doesn't change</param>
public void ChangeQueryText(string queryText, bool reQuery = false)
{
if (QueryText != queryText)
@ -494,25 +500,6 @@ namespace Flow.Launcher.ViewModel
public string PluginIconPath { get; set; } = null;
public ICommand EscCommand { get; set; }
public ICommand BackspaceCommand { get; set; }
public ICommand SelectNextItemCommand { get; set; }
public ICommand SelectPrevItemCommand { get; set; }
public ICommand SelectNextPageCommand { get; set; }
public ICommand SelectPrevPageCommand { get; set; }
public ICommand SelectFirstResultCommand { get; set; }
public ICommand StartHelpCommand { get; set; }
public ICommand LoadContextMenuCommand { get; set; }
public ICommand LoadHistoryCommand { get; set; }
public ICommand OpenResultCommand { get; set; }
public ICommand OpenSettingCommand { get; set; }
public ICommand ReloadPluginDataCommand { get; set; }
public ICommand ClearQueryCommand { get; private set; }
public ICommand CopyToClipboard { get; set; }
public ICommand AutocompleteQueryCommand { get; set; }
public string OpenResultCommandModifiers { get; private set; }
public string Image => Constant.QueryTextBoxIconImagePath;

View file

@ -21,6 +21,7 @@ using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Input;
using System.Globalization;
namespace Flow.Launcher.ViewModel
{
@ -46,6 +47,18 @@ namespace Flow.Launcher.ViewModel
case nameof(Settings.WindowSize):
OnPropertyChanged(nameof(WindowWidthSize));
break;
case nameof(Settings.UseDate):
case nameof(Settings.DateFormat):
OnPropertyChanged(nameof(DateText));
break;
case nameof(Settings.UseClock):
case nameof(Settings.TimeFormat):
OnPropertyChanged(nameof(ClockText));
break;
case nameof(Settings.Language):
OnPropertyChanged(nameof(ClockText));
OnPropertyChanged(nameof(DateText));
break;
}
};
}
@ -56,7 +69,7 @@ namespace Flow.Launcher.ViewModel
{
await _updater.UpdateAppAsync(App.API, false);
}
public bool AutoUpdates
{
get => Settings.AutoUpdates;
@ -71,6 +84,8 @@ namespace Flow.Launcher.ViewModel
}
}
public CultureInfo cultureInfo => new CultureInfo(Settings.Language);
public bool StartFlowLauncherOnSystemStartup
{
get => Settings.StartFlowLauncherOnSystemStartup;
@ -157,7 +172,10 @@ namespace Flow.Launcher.ViewModel
{
var key = $"LastQuery{e}";
var display = _translater.GetTranslation(key);
var m = new LastQueryMode { Display = display, Value = e, };
var m = new LastQueryMode
{
Display = display, Value = e,
};
modes.Add(m);
}
return modes;
@ -302,11 +320,11 @@ namespace Flow.Launcher.ViewModel
}
}
private IList<PluginStoreItemViewModel> LabelMaker(IList<UserPlugin> list)
private IList<PluginStoreItemViewModel> LabelMaker(IList<UserPlugin> list)
{
return list.Select(p=>new PluginStoreItemViewModel(p))
return list.Select(p => new PluginStoreItemViewModel(p))
.OrderByDescending(p => p.Category == PluginStoreItemViewModel.NewRelease)
.ThenByDescending(p=>p.Category == PluginStoreItemViewModel.RecentlyUpdated)
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.RecentlyUpdated)
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.None)
.ThenByDescending(p => p.Category == PluginStoreItemViewModel.Installed)
.ToList();
@ -340,15 +358,14 @@ namespace Flow.Launcher.ViewModel
internal void DisplayPluginQuery(string queryToDisplay, PluginPair plugin, int actionKeywordPosition = 0)
{
var actionKeyword = plugin.Metadata.ActionKeywords.Count == 0
? string.Empty
var actionKeyword = plugin.Metadata.ActionKeywords.Count == 0
? string.Empty
: plugin.Metadata.ActionKeywords[actionKeywordPosition];
App.API.ChangeQuery($"{actionKeyword} {queryToDisplay}");
App.API.ShowMainWindow();
}
#endregion
#region theme
@ -413,8 +430,7 @@ namespace Flow.Launcher.ViewModel
var display = _translater.GetTranslation(key);
var m = new ColorScheme
{
Display = display,
Value = e,
Display = display, Value = e,
};
modes.Add(m);
}
@ -422,8 +438,6 @@ namespace Flow.Launcher.ViewModel
}
}
public class SearchWindowPosition
{
public string Display { get; set; }
@ -440,32 +454,63 @@ namespace Flow.Launcher.ViewModel
{
var key = $"SearchWindowPosition{e}";
var display = _translater.GetTranslation(key);
var m = new SearchWindowPosition { Display = display, Value = e, };
var m = new SearchWindowPosition
{
Display = display, Value = e,
};
modes.Add(m);
}
return modes;
}
}
public List<string> TimeFormatList { get; set; } = new List<string>()
public List<string> TimeFormatList { get; } = new()
{
"h:mm",
"hh:mm",
"H:mm",
"HH:mm",
"tt h:mm",
"tt hh:mm",
"h:mm tt",
"hh:mm tt"
};
public List<string> DateFormatList { get; set; } = new List<string>()
public List<string> DateFormatList { get; } = new()
{
"MM'/'dd dddd",
"MM'/'dd ddd",
"MM'/'dd",
"MM'-'dd",
"MMMM', 'dd",
"dd'/'MM",
"dd'-'MM",
"ddd MM'/'dd",
"dddd MM'/'dd",
"dddd"
"dddd",
"ddd dd'/'MM",
"dddd dd'/'MM",
"dddd dd', 'MMMM",
"dd', 'MMMM"
};
public string TimeFormat
{
get { return Settings.TimeFormat; }
set { Settings.TimeFormat = value; }
}
public string DateFormat
{
get { return Settings.DateFormat; }
set { Settings.DateFormat = value; }
}
public string ClockText => DateTime.Now.ToString(TimeFormat, cultureInfo);
public string DateText => DateTime.Now.ToString(DateFormat, cultureInfo);
public double WindowWidthSize
{
get => Settings.WindowSize;
@ -707,7 +752,7 @@ namespace Flow.Launcher.ViewModel
string deleteWarning = string.Format(
InternationalizationManager.Instance.GetTranslation("deleteCustomShortcutWarning"),
item?.Key, item?.Value);
item.Key, item.Value);
if (MessageBox.Show(deleteWarning, InternationalizationManager.Instance.GetTranslation("delete"),
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
@ -773,34 +818,37 @@ namespace Flow.Launcher.ViewModel
}
}
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
public string CheckLogFolder
{
get
get
{
var dirInfo = new DirectoryInfo(Path.Combine(DataLocation.DataDirectory(), Constant.Logs, Constant.Version));
long size = dirInfo.EnumerateFiles("*", SearchOption.AllDirectories).Sum(file => file.Length);
return _translater.GetTranslation("clearlogfolder") + " (" + FormatBytes(size) + ")" ;
return _translater.GetTranslation("clearlogfolder") + " (" + FormatBytes(size) + ")";
}
}
internal void ClearLogFolder()
{
var directory = new DirectoryInfo(
Path.Combine(
DataLocation.DataDirectory(),
Constant.Logs,
Constant.Version));
Path.Combine(
DataLocation.DataDirectory(),
Constant.Logs,
Constant.Version));
directory.EnumerateFiles()
.ToList()
.ForEach(x => x.Delete());
.ToList()
.ForEach(x => x.Delete());
}
internal string FormatBytes(long bytes)
{
const int scale = 1024;
string[] orders = new string[] { "GB", "MB", "KB", "Bytes" };
string[] orders = new string[]
{
"GB", "MB", "KB", "Bytes"
};
long max = (long)Math.Pow(scale, orders.Length - 1);
foreach (string order in orders)
@ -812,6 +860,7 @@ namespace Flow.Launcher.ViewModel
}
return "0 Bytes";
}
#endregion
}
}

View file

@ -97,8 +97,8 @@ namespace Flow.Launcher.Plugin.Program.Programs
}
// To initialize the app description
public String description = String.Empty;
public string description = string.Empty;
public string arguments = string.Empty;
// Retrieve the target path using Shell Link
public string retrieveTargetPath(string path)
@ -122,13 +122,16 @@ namespace Flow.Launcher.Plugin.Program.Programs
buffer = new StringBuilder(MAX_PATH);
((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
description = buffer.ToString();
buffer.Clear();
((IShellLinkW)link).GetArguments(buffer, MAX_PATH);
arguments = buffer.ToString();
}
// To release unmanaged memory
Marshal.ReleaseComObject(link);
return target;
}
}
}
}

View file

@ -26,11 +26,11 @@ namespace Flow.Launcher.Plugin.Program.Programs
public string UniqueIdentifier { get => _uid; set => _uid = value == null ? string.Empty : value.ToLowerInvariant(); } // For path comparison
public string IcoPath { get; set; }
/// <summary>
/// Path of the file. It's the path of .lnk or .url for .lnk and .url.
/// Path of the file. It's the path of .lnk and .url for .lnk and .url files.
/// </summary>
public string FullPath { get; set; }
/// <summary>
/// Path of the excutable for .lnk, or the URL for .url.
/// Path of the excutable for .lnk, or the URL for .url. Arguments are included if any.
/// </summary>
public string LnkResolvedPath { get; set; }
/// <summary>
@ -185,7 +185,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
{
var info = new ProcessStartInfo
{
FileName = ExecutablePath,
FileName = FullPath,
WorkingDirectory = ParentDirectory,
Verb = "runas",
UseShellExecute = true
@ -275,6 +275,12 @@ namespace Flow.Launcher.Plugin.Program.Programs
program.LnkResolvedPath = Path.GetFullPath(target);
program.ExecutableName = Path.GetFileName(target);
var args = _helper.arguments;
if(!string.IsNullOrEmpty(args))
{
program.LnkResolvedPath += " " + args;
}
var description = _helper.description;
if (!string.IsNullOrEmpty(description))
{