Merge pull request #3370 from Jack251970/history_preview1

Support Preview Panel for History Results
This commit is contained in:
Jack Ye 2025-03-26 12:26:41 +08:00 committed by GitHub
commit 350276dbf5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 193 additions and 122 deletions

View file

@ -32,6 +32,7 @@ namespace Flow.Launcher.Infrastructure
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png");
public static readonly string ImageIcon = Path.Combine(ImagesDirectory, "image.png");
public static readonly string HistoryIcon = Path.Combine(ImagesDirectory, "history.png");
public static string PythonPath;
public static string NodePath;

View file

@ -216,7 +216,7 @@
<Border MouseDown="OnMouseDown" Style="{DynamicResource WindowBorderStyle}">
<StackPanel Orientation="Vertical">
<Grid>
<Grid x:Name="QueryBoxArea">
<Border MinHeight="30" Style="{DynamicResource QueryBoxBgStyle}">
<Grid>
<TextBox
@ -339,7 +339,7 @@
Y2="0" />
</Grid>
<Grid ClipToBounds="True">
<Grid x:Name="MiddleSeparatorArea" ClipToBounds="True">
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
@ -379,7 +379,7 @@
</ContentControl>
</Grid>
<Border Style="{DynamicResource WindowRadius}">
<Border x:Name="ResultPreviewAreaBoarder" Style="{DynamicResource WindowRadius}">
<Border.Clip>
<MultiBinding Converter="{StaticResource BorderClipConverter}">
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
@ -387,12 +387,14 @@
<Binding Path="CornerRadius" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Border.Clip>
<Grid>
<Grid x:Name="ResultPreviewArea">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="80" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="0.85*" MinWidth="244" />
</Grid.ColumnDefinitions>
<StackPanel
x:Name="ResultArea"
Grid.Column="0"
@ -419,7 +421,9 @@
RightClickResultCommand="{Binding RightClickResultCommand}" />
</ContentControl>
</StackPanel>
<GridSplitter
x:Name="PreviewMiddleSeparator"
Grid.Column="1"
Margin="0"
HorizontalAlignment="Center"
@ -433,6 +437,7 @@
</ControlTemplate>
</GridSplitter.Template>
</GridSplitter>
<Grid
x:Name="Preview"
Grid.Column="2"
@ -442,7 +447,7 @@
<Border
MinHeight="380"
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
DataContext="{Binding PreviewSelectedItem, Mode=OneWay}"
Visibility="{Binding ShowDefaultPreview}">
<Grid
Margin="0 0 10 5"
@ -519,7 +524,7 @@
MaxHeight="{Binding ElementName=ResultListBox, Path=ActualHeight}"
Padding="0 0 10 10"
d:DataContext="{d:DesignInstance vm:ResultViewModel}"
DataContext="{Binding SelectedItem, ElementName=ResultListBox}"
DataContext="{Binding PreviewSelectedItem, Mode=OneWay}"
Visibility="{Binding ShowCustomizedPreview}">
<ContentControl Content="{Binding Result.PreviewPanel.Value}" />
</Border>

View file

@ -326,7 +326,7 @@ namespace Flow.Launcher
e.Handled = true;
break;
case Key.Right:
if (_viewModel.SelectedIsFromQueryResults()
if (_viewModel.QueryResultsSelected()
&& QueryTextBox.CaretIndex == QueryTextBox.Text.Length
&& !string.IsNullOrEmpty(QueryTextBox.Text))
{
@ -336,7 +336,7 @@ namespace Flow.Launcher
break;
case Key.Left:
if (!_viewModel.SelectedIsFromQueryResults() && QueryTextBox.CaretIndex == 0)
if (!_viewModel.QueryResultsSelected() && QueryTextBox.CaretIndex == 0)
{
_viewModel.EscCommand.Execute(null);
e.Handled = true;
@ -346,7 +346,7 @@ namespace Flow.Launcher
case Key.Back:
if (specialKeyState.CtrlPressed)
{
if (_viewModel.SelectedIsFromQueryResults()
if (_viewModel.QueryResultsSelected()
&& QueryTextBox.Text.Length > 0
&& QueryTextBox.CaretIndex == QueryTextBox.Text.Length)
{

View file

@ -390,6 +390,7 @@
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
@ -431,12 +432,12 @@
</DataTrigger>
</Style.Triggers>
</Style>
<Style
x:Key="PreviewBorderStyle"
BasedOn="{StaticResource BasePreviewBorderStyle}"
TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Gray" />
</Style>
<Style x:Key="PreviewArea" TargetType="{x:Type Grid}">
@ -446,8 +447,8 @@
<MultiDataTrigger.Conditions>
<!--
<Condition Binding="{Binding ElementName=ResultListBox, Path=Visibility}" Value="Collapsed" />
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />-->
<Condition Binding="{Binding ElementName=ContextMenu, Path=Visibility}" Value="Collapsed" />-->
<Condition Binding="{Binding ElementName=History, Path=Visibility}" Value="Collapsed" />
<Condition Binding="{Binding ElementName=ResultListBox, Path=Items.Count}" Value="0" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>

View file

@ -49,6 +49,8 @@ namespace Flow.Launcher.ViewModel
private ChannelWriter<ResultsForUpdate> _resultsUpdateChannelWriter;
private Task _resultsViewUpdateTask;
private readonly IReadOnlyList<Result> _emptyResult = new List<Result>();
#endregion
#region Constructor
@ -162,7 +164,21 @@ namespace Flow.Launcher.ViewModel
switch (args.PropertyName)
{
case nameof(Results.SelectedItem):
UpdatePreview();
_selectedItemFromQueryResults = true;
PreviewSelectedItem = Results.SelectedItem;
_ = UpdatePreviewAsync();
break;
}
};
History.PropertyChanged += (_, args) =>
{
switch (args.PropertyName)
{
case nameof(History.SelectedItem):
_selectedItemFromQueryResults = false;
PreviewSelectedItem = History.SelectedItem;
_ = UpdatePreviewAsync();
break;
}
};
@ -265,7 +281,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void LoadHistory()
{
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
SelectedResults = History;
History.SelectedIndex = _history.Items.Count - 1;
@ -279,7 +295,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
public void ReQuery()
{
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
_ = QueryResultsAsync(isReQuery: true);
}
@ -320,7 +336,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void LoadContextMenu()
{
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
// 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
@ -350,7 +366,7 @@ namespace Flow.Launcher.ViewModel
private void AutocompleteQuery()
{
var result = SelectedResults.SelectedItem?.Result;
if (result != null && SelectedIsFromQueryResults()) // SelectedItem returns null if selection is empty.
if (result != null && QueryResultsSelected()) // SelectedItem returns null if selection is empty.
{
var autoCompleteText = result.Title;
@ -402,7 +418,7 @@ namespace Flow.Launcher.ViewModel
})
.ConfigureAwait(false);
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
_userSelectedRecord.Add(result);
// origin query is null when user select the context menu item directly of one item from query list
@ -481,7 +497,7 @@ namespace Flow.Launcher.ViewModel
{
if (_history.Items.Count > 0
&& QueryText == string.Empty
&& SelectedIsFromQueryResults())
&& QueryResultsSelected())
{
lastHistoryIndex = 1;
ReverseHistory();
@ -501,7 +517,7 @@ namespace Flow.Launcher.ViewModel
[RelayCommand]
private void Esc()
{
if (!SelectedIsFromQueryResults())
if (!QueryResultsSelected())
{
SelectedResults = Results;
}
@ -513,7 +529,7 @@ namespace Flow.Launcher.ViewModel
public void BackToQueryResults()
{
if (!SelectedIsFromQueryResults())
if (!QueryResultsSelected())
{
SelectedResults = Results;
}
@ -644,13 +660,16 @@ namespace Flow.Launcher.ViewModel
private ResultsViewModel SelectedResults
{
get { return _selectedResults; }
get => _selectedResults;
set
{
var isReturningFromQueryResults = QueryResultsSelected();
var isReturningFromContextMenu = ContextMenuSelected();
var isReturningFromHistory = HistorySelected();
_selectedResults = value;
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
Results.Visibility = Visibility.Visible;
ContextMenu.Visibility = Visibility.Collapsed;
History.Visibility = Visibility.Collapsed;
@ -668,10 +687,27 @@ namespace Flow.Launcher.ViewModel
{
ChangeQueryText(_queryTextBeforeLeaveResults);
}
// If we are returning from history and we have not set select item yet,
// we need to clear the preview selected item
if (isReturningFromHistory && _selectedItemFromQueryResults.HasValue && (!_selectedItemFromQueryResults.Value))
{
PreviewSelectedItem = null;
}
}
else
{
Results.Visibility = Visibility.Collapsed;
if (HistorySelected())
{
ContextMenu.Visibility = Visibility.Collapsed;
History.Visibility = Visibility.Visible;
}
else
{
ContextMenu.Visibility = Visibility.Visible;
History.Visibility = Visibility.Collapsed;
}
_queryTextBeforeLeaveResults = QueryText;
// Because of Fody's optimization
@ -686,6 +722,16 @@ namespace Flow.Launcher.ViewModel
{
QueryText = string.Empty;
}
if (HistorySelected())
{
// If we are returning from query results and we have not set select item yet,
// we need to clear the preview selected item
if (isReturningFromQueryResults && _selectedItemFromQueryResults.HasValue && _selectedItemFromQueryResults.Value)
{
PreviewSelectedItem = null;
}
}
}
_selectedResults.Visibility = Visibility.Visible;
@ -785,6 +831,22 @@ namespace Flow.Launcher.ViewModel
#region Preview
private static readonly int ResultAreaColumnPreviewShown = 1;
private static readonly int ResultAreaColumnPreviewHidden = 3;
private bool? _selectedItemFromQueryResults;
private ResultViewModel _previewSelectedItem;
public ResultViewModel PreviewSelectedItem
{
get => _previewSelectedItem;
set
{
_previewSelectedItem = value;
OnPropertyChanged();
}
}
public bool InternalPreviewVisible
{
get
@ -803,18 +865,14 @@ namespace Flow.Launcher.ViewModel
}
}
private static readonly int ResultAreaColumnPreviewShown = 1;
private static readonly int ResultAreaColumnPreviewHidden = 3;
public int ResultAreaColumn { get; set; } = ResultAreaColumnPreviewShown;
// This is not a reliable indicator of whether external preview is visible due to the
// ability of manually closing/exiting the external preview program which, does not inform flow that
// preview is no longer available.
public bool ExternalPreviewVisible { get; set; } = false;
public bool ExternalPreviewVisible { get; private set; }
private void ShowPreview()
private async Task ShowPreviewAsync()
{
var useExternalPreview = PluginManager.UseExternalPreview();
@ -825,13 +883,15 @@ namespace Flow.Launcher.ViewModel
// Internal preview may still be on when user switches to external
if (InternalPreviewVisible)
HideInternalPreview();
OpenExternalPreview(path);
_ = OpenExternalPreviewAsync(path);
break;
case true
when !CanExternalPreviewSelectedResult(out var _):
if (ExternalPreviewVisible)
CloseExternalPreview();
await CloseExternalPreviewAsync();
ShowInternalPreview();
break;
@ -844,7 +904,7 @@ namespace Flow.Launcher.ViewModel
private void HidePreview()
{
if (PluginManager.UseExternalPreview())
CloseExternalPreview();
_ = CloseExternalPreviewAsync();
if (InternalPreviewVisible)
HideInternalPreview();
@ -859,31 +919,31 @@ namespace Flow.Launcher.ViewModel
}
else
{
ShowPreview();
_ = ShowPreviewAsync();
}
}
private void OpenExternalPreview(string path, bool sendFailToast = true)
private async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true)
{
_ = PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
await PluginManager.OpenExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
ExternalPreviewVisible = true;
}
private void CloseExternalPreview()
private async Task CloseExternalPreviewAsync()
{
_ = PluginManager.CloseExternalPreviewAsync().ConfigureAwait(false);
await PluginManager.CloseExternalPreviewAsync().ConfigureAwait(false);
ExternalPreviewVisible = false;
}
private static void SwitchExternalPreview(string path, bool sendFailToast = true)
private static async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true)
{
_ = PluginManager.SwitchExternalPreviewAsync(path,sendFailToast).ConfigureAwait(false);
await PluginManager.SwitchExternalPreviewAsync(path, sendFailToast).ConfigureAwait(false);
}
private void ShowInternalPreview()
{
ResultAreaColumn = ResultAreaColumnPreviewShown;
Results.SelectedItem?.LoadPreviewImage();
PreviewSelectedItem?.LoadPreviewImage();
}
private void HideInternalPreview()
@ -897,20 +957,18 @@ namespace Flow.Launcher.ViewModel
{
case true
when PluginManager.AllowAlwaysPreview() && CanExternalPreviewSelectedResult(out var path):
OpenExternalPreview(path);
_ = OpenExternalPreviewAsync(path);
break;
case true:
ShowInternalPreview();
break;
case false:
HidePreview();
break;
}
}
private void UpdatePreview()
private async Task UpdatePreviewAsync()
{
switch (PluginManager.UseExternalPreview())
{
@ -918,44 +976,48 @@ namespace Flow.Launcher.ViewModel
when CanExternalPreviewSelectedResult(out var path):
if (ExternalPreviewVisible)
{
SwitchExternalPreview(path, false);
_ = SwitchExternalPreviewAsync(path, false);
}
else if (InternalPreviewVisible)
{
HideInternalPreview();
OpenExternalPreview(path);
_ = OpenExternalPreviewAsync(path);
}
break;
case true
when !CanExternalPreviewSelectedResult(out var _):
if (ExternalPreviewVisible)
{
CloseExternalPreview();
await CloseExternalPreviewAsync();
ShowInternalPreview();
}
break;
case false
when InternalPreviewVisible:
Results.SelectedItem?.LoadPreviewImage();
PreviewSelectedItem?.LoadPreviewImage();
break;
}
}
private bool CanExternalPreviewSelectedResult(out string path)
{
path = Results.SelectedItem?.Result?.Preview.FilePath;
path = QueryResultsPreviewed() ? Results.SelectedItem?.Result?.Preview.FilePath : string.Empty;
return !string.IsNullOrEmpty(path);
}
private bool QueryResultsPreviewed()
{
var previewed = PreviewSelectedItem == Results.SelectedItem;
return previewed;
}
#endregion
#region Query
public void Query(bool isReQuery = false)
private void Query(bool isReQuery = false)
{
if (SelectedIsFromQueryResults())
if (QueryResultsSelected())
{
_ = QueryResultsAsync(isReQuery);
}
@ -1027,6 +1089,11 @@ namespace Flow.Launcher.ViewModel
Title = string.Format(title, h.Query),
SubTitle = string.Format(time, h.ExecutedDateTime),
IcoPath = "Images\\history.png",
Preview = new Result.PreviewInfo
{
PreviewImagePath = Constant.HistoryIcon,
Description = string.Format(time, h.ExecutedDateTime)
},
OriginQuery = new Query { RawQuery = h.Query },
Action = _ =>
{
@ -1053,8 +1120,6 @@ namespace Flow.Launcher.ViewModel
}
}
private readonly IReadOnlyList<Result> _emptyResult = new List<Result>();
private async Task QueryResultsAsync(bool isReQuery = false, bool reSelect = true)
{
_updateSource?.Cancel();
@ -1318,7 +1383,7 @@ namespace Flow.Launcher.ViewModel
return menu;
}
internal bool SelectedIsFromQueryResults()
internal bool QueryResultsSelected()
{
var selected = SelectedResults == Results;
return selected;
@ -1352,6 +1417,18 @@ namespace Flow.Launcher.ViewModel
}
}
/// <summary>
/// Checks if Flow Launcher should ignore any hotkeys
/// </summary>
public bool ShouldIgnoreHotkeys()
{
return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
}
#endregion
#region Public Methods
public void Show()
{
Application.Current.Dispatcher.Invoke(() =>
@ -1388,10 +1465,10 @@ namespace Flow.Launcher.ViewModel
if (ExternalPreviewVisible)
{
CloseExternalPreview();
await CloseExternalPreviewAsync();
}
if (!SelectedIsFromQueryResults())
if (!QueryResultsSelected())
{
SelectedResults = Results;
}
@ -1401,10 +1478,7 @@ namespace Flow.Launcher.ViewModel
{
ChangeQueryText(string.Empty);
await Task.Delay(1); // Wait for one frame to ensure UI reflects changes
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.MainWindow.UpdateLayout(); // Force UI update
});
Application.Current.Dispatcher.Invoke(Application.Current.MainWindow.UpdateLayout); // Force UI update
}
switch (Settings.LastQueryMode)
@ -1463,17 +1537,8 @@ namespace Flow.Launcher.ViewModel
#pragma warning restore VSTHRD100 // Avoid async void methods
/// <summary>
/// Checks if Flow Launcher should ignore any hotkeys
/// Save history, user selected records and top most records
/// </summary>
public bool ShouldIgnoreHotkeys()
{
return Settings.IgnoreHotkeysOnFullscreen && Win32Helper.IsForegroundWindowFullscreen() || GameModeStatus;
}
#endregion
#region Public Methods
public void Save()
{
_historyItemsStorage.Save();

View file

@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing.Text;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
@ -6,25 +9,20 @@ using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.IO;
using System.Drawing.Text;
using System.Collections.Generic;
namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
private static PrivateFontCollection fontCollection = new();
private static Dictionary<string, string> fonts = new();
private static readonly PrivateFontCollection FontCollection = new();
private static readonly Dictionary<string, string> Fonts = new();
public ResultViewModel(Result result, Settings settings)
{
Settings = settings;
if (result == null)
{
return;
}
if (result == null) return;
Result = result;
if (Result.Glyph is { FontFamily: not null } glyph)
@ -39,20 +37,20 @@ namespace Flow.Launcher.ViewModel
fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath);
}
if (fonts.ContainsKey(fontFamilyPath))
if (Fonts.TryGetValue(fontFamilyPath, out var value))
{
Glyph = glyph with
{
FontFamily = fonts[fontFamilyPath]
FontFamily = value
};
}
else
{
fontCollection.AddFontFile(fontFamilyPath);
fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
FontCollection.AddFontFile(fontFamilyPath);
Fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{FontCollection.Families[^1].Name}";
Glyph = glyph with
{
FontFamily = fonts[fontFamilyPath]
FontFamily = Fonts[fontFamilyPath]
};
}
}
@ -61,7 +59,6 @@ namespace Flow.Launcher.ViewModel
Glyph = glyph;
}
}
}
public Settings Settings { get; }
@ -95,14 +92,10 @@ namespace Flow.Launcher.ViewModel
get
{
if (PreviewImageAvailable)
{
return Visibility.Visible;
}
else
{
// Fall back to icon
return ShowIcon;
}
// Fall back to icon
return ShowIcon;
}
}
@ -111,9 +104,8 @@ namespace Flow.Launcher.ViewModel
get
{
if (Result.RoundedIcon)
{
return IconXY / 2;
}
return IconXY;
}
@ -148,31 +140,40 @@ namespace Flow.Launcher.ViewModel
? Result.SubTitle
: Result.SubTitleToolTip;
private volatile bool ImageLoaded;
private volatile bool PreviewImageLoaded;
private volatile bool _imageLoaded;
private volatile bool _previewImageLoaded;
private ImageSource image = ImageLoader.LoadingImage;
private ImageSource previewImage = ImageLoader.LoadingImage;
private ImageSource _image = ImageLoader.LoadingImage;
private ImageSource _previewImage = ImageLoader.LoadingImage;
public ImageSource Image
{
get
{
if (!ImageLoaded)
if (!_imageLoaded)
{
ImageLoaded = true;
_imageLoaded = true;
_ = LoadImageAsync();
}
return image;
return _image;
}
private set => image = value;
private set => _image = value;
}
public ImageSource PreviewImage
{
get => previewImage;
private set => previewImage = value;
get
{
if (!_previewImageLoaded)
{
_previewImageLoaded = true;
_ = LoadPreviewImageAsync();
}
return _previewImage;
}
private set => _previewImage = value;
}
/// <summary>
@ -188,8 +189,7 @@ namespace Flow.Launcher.ViewModel
{
try
{
var image = icon();
return image;
return icon();
}
catch (Exception e)
{
@ -208,7 +208,7 @@ namespace Flow.Launcher.ViewModel
var iconDelegate = Result.Icon;
if (ImageLoader.TryGetValue(imagePath, false, out ImageSource img))
{
image = img;
_image = img;
}
else
{
@ -223,7 +223,7 @@ namespace Flow.Launcher.ViewModel
var iconDelegate = Result.Preview.PreviewDelegate ?? Result.Icon;
if (ImageLoader.TryGetValue(imagePath, true, out ImageSource img))
{
previewImage = img;
_previewImage = img;
}
else
{
@ -234,13 +234,10 @@ namespace Flow.Launcher.ViewModel
public void LoadPreviewImage()
{
if (ShowDefaultPreview == Visibility.Visible)
if (ShowDefaultPreview == Visibility.Visible && !_previewImageLoaded && ShowPreviewImage == Visibility.Visible)
{
if (!PreviewImageLoaded && ShowPreviewImage == Visibility.Visible)
{
PreviewImageLoaded = true;
_ = LoadPreviewImageAsync();
}
_previewImageLoaded = true;
_ = LoadPreviewImageAsync();
}
}

View file

@ -1,6 +1,4 @@
using System;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
@ -10,6 +8,8 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
namespace Flow.Launcher.ViewModel
{
@ -28,6 +28,7 @@ namespace Flow.Launcher.ViewModel
Results = new ResultCollection();
BindingOperations.EnableCollectionSynchronization(Results, _collectionLock);
}
public ResultsViewModel(Settings settings) : this()
{
_settings = settings;
@ -219,7 +220,6 @@ namespace Flow.Launcher.ViewModel
if (newRawResults.Count == 0)
return Results;
var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings));
return Results.Where(r => r.Result.PluginID != resultId)
@ -241,6 +241,7 @@ namespace Flow.Launcher.ViewModel
#endregion
#region FormattedText Dependency Property
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(Inline),
@ -259,8 +260,7 @@ namespace Flow.Launcher.ViewModel
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null) return;
if (d is not TextBlock textBlock) return;
var inline = (Inline)e.NewValue;
@ -269,6 +269,7 @@ namespace Flow.Launcher.ViewModel
textBlock.Inlines.Add(inline);
}
#endregion
public class ResultCollection : List<ResultViewModel>, INotifyCollectionChanged
@ -279,7 +280,6 @@ namespace Flow.Launcher.ViewModel
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
@ -297,6 +297,7 @@ namespace Flow.Launcher.ViewModel
// wpf use DirectX / double buffered already, so just reset all won't cause ui flickering
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void AddAll(List<ResultViewModel> Items)
{
for (int i = 0; i < Items.Count; i++)
@ -308,6 +309,7 @@ namespace Flow.Launcher.ViewModel
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, i));
}
}
public void RemoveAll(int Capacity = 512)
{
Clear();