This commit is contained in:
Meik
2025-11-11 11:03:42 +01:00
commit dc3e8a2e4c
582 changed files with 191465 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
<UserControl x:Class="FasdDesktopUi.Basics.UserControls.CustomSearchResultCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FasdDesktopUi.Basics.UserControls"
xmlns:vc="clr-namespace:FasdDesktopUi.Basics.Converter"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="CustomSearchResultUc">
<UserControl.Resources>
<vc:NullValueToVisibilityConverter x:Key="NullToVisibility" />
<vc:LanguageDefinitionsConverter x:Key="LanguageConverter" />
</UserControl.Resources>
<Border x:Name="MainBorder" x:FieldModifier="private">
<Grid x:Name="MainGrid" x:FieldModifier="private">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="HeaderTextBlock" x:FieldModifier="private"
Grid.Column="0"
Style="{DynamicResource DetailsPage.DataHistory.TitleColumn.OverviewTitle}"
HorizontalAlignment="Left"
Margin="5 0 0 5"/>
<CheckBox x:Name="ShowDetailsCheckBox" x:FieldModifier="private"
Style="{DynamicResource ToggleSwitch}"
IsChecked="{Binding ElementName=CustomSearchResultUc, Path=ShowSearchResultDetails}"
Grid.Column="1"
Margin="5 0"
VerticalAlignment="Center"
ToolTip="{Binding Converter={StaticResource LanguageConverter}, ConverterParameter=SearchBar.ShowDetails}"
Cursor="Hand"
Checked="ShowDetailsCheckBox_Checked"
Unchecked="ShowDetailsCheckBox_Checked"/>
<ScrollViewer x:Name="MainScrollViewer" x:FieldModifier="private"
VerticalScrollBarVisibility="Auto"
Grid.Row="1"
Grid.ColumnSpan="2">
<StackPanel x:Name="MainStackPanel" x:FieldModifier="private"
Grid.IsSharedSizeScope="True"/>
</ScrollViewer>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,496 @@
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Basics.UserControls.SearchResult;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.UserControls
{
public partial class CustomSearchResultCollection : UserControl
{
private Dictionary<enumFasdInformationClass, SearchResultCategory> _searchCategories;
#region IndexOfSelectedResultItem
private int _indexOfSelectedResultItem = int.MinValue;
public int IndexOfSelectedResultItem
{
get { return _indexOfSelectedResultItem; }
set
{
if (_indexOfSelectedResultItem == value)
return;
if (value < -1)
_indexOfSelectedResultItem = int.MinValue;
else if (value == -1)
_indexOfSelectedResultItem = GetSearchResultCount() - 1;
else if (value == GetSearchResultCount())
_indexOfSelectedResultItem = 0;
else
_indexOfSelectedResultItem = value;
HandleIndexOfSelectedResultItemChanged();
}
}
#endregion
private cMenuDataBase _lastSelectedMenuData = null;
public bool ShowSearchResultDetails
{
get { return cFasdCockpitConfig.Instance.ShowSearchResultDetails; }
set
{
cFasdCockpitConfig.Instance.ShowSearchResultDetails = value;
cFasdCockpitConfig.Instance.Save("ShowSearchResultDetails");
}
}
public CustomSearchResultCollection()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
AddHandler(CustomEventManager.IndexChangedEvent, new CustomEventManager.IndexChangedHandlerDelegate(UpdateIndexOfSelectedResultItem));
AddHandler(CustomEventManager.MenuDataChangedEvent, new CustomEventManager.MenuDataChangedHandlerDelegate((_, args) => _lastSelectedMenuData = args.MenuData));
}
private void UpdateIndexOfSelectedResultItem(object sender, IndexEventArgs args)
{
try
{
if (!(args.OriginalSource is SearchResultCategory category))
return;
var indexOfSender = _searchCategories.Values.ToList().IndexOf(category);
int updatedIndex = args.NewValue;
for (int i = 0; i < indexOfSender; i++)
{
updatedIndex += _searchCategories.Values.ToList()[i].GetItemCount();
}
IndexOfSelectedResultItem = updatedIndex;
}
catch (Exception ex)
{
LogException(ex);
}
}
public bool IsEmpty() => MainStackPanel.Children.Count == 0;
internal int GetSearchResultCount()
{
int itemCount = 0;
foreach (var resultCategory in _searchCategories.Values)
{
itemCount += resultCategory.GetItemCount();
}
return itemCount;
}
private void HandleIndexOfSelectedResultItemChanged()
{
try
{
int totalResultItemCount = 0;
foreach (var category in _searchCategories.Values)
{
int categoryItemCount = category.GetItemCount();
if (IndexOfSelectedResultItem > totalResultItemCount)
category.HiglightItemAt(-1);
if (IndexOfSelectedResultItem < totalResultItemCount + categoryItemCount)
category.HiglightItemAt(IndexOfSelectedResultItem - totalResultItemCount);
totalResultItemCount += categoryItemCount;
}
}
catch (Exception E)
{
LogException(E);
}
}
public void SetHeaderText(string headerText, bool hideDetailsCheckbox = false)
{
try
{
HeaderTextBlock.Text = headerText;
HeaderTextBlock.Visibility = string.IsNullOrEmpty(headerText) ? Visibility.Collapsed : Visibility.Visible;
ShowDetailsCheckBox.Visibility = string.IsNullOrEmpty(headerText) || hideDetailsCheckbox ? Visibility.Collapsed : Visibility.Visible;
}
catch (Exception E)
{
LogException(E);
}
}
private void ProcessNewItems()
{
UpdateCornerRadius();
UpdateSearchResultDetailsShown(ShowSearchResultDetails);
}
public void ShowLoadingTextItem(string itemText)
{
try
{
ClearControl();
var menuItem = new CustomMenuItem(false) { MenuData = new cMenuDataLoading(itemText) };
MainStackPanel.Children.Add(menuItem);
ProcessNewItems();
}
catch (Exception E)
{
LogException(E);
}
}
public void ShowSearchResults(cFilteredResults SearchResults, string headerText, ISearchUiProvider searchUiProvider)
{
try
{
ClearControl();
SetHeaderText(headerText);
if (SearchResults?.Results is null || SearchResults.Results.Count == 0)
ProcessNoSearchResults();
else
ProcessSearchResults();
ProcessNewItems();
}
catch (Exception E)
{
LogException(E);
}
void ProcessNoSearchResults()
{
var resultItemControl = new CustomMenuItem(false)
{
MenuData = new cMenuDataBase()
{
MenuText = cMultiLanguageSupport.GetItem("SearchBar.NoResults"),
MenuIcon = new IconData(enumInternIcons.menuBar_search_noResults)
}
};
MainStackPanel.Children.Add(resultItemControl);
}
void ProcessSearchResults()
{
if (!_searchCategories.TryGetValue(enumFasdInformationClass.Main, out var mainCategory))
return;
List<cMenuDataBase> menuDataList = new List<cMenuDataBase>();
mainCategory.UpdateSearchResults(SearchResults.Results, searchUiProvider);
UpdatePendingInformationClasses(new HashSet<enumFasdInformationClass>());
}
}
public void ShowSearchHistory()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
ClearControl();
SetHeaderText(cMultiLanguageSupport.GetItem("Searchbar.History.Title"), true);
if (cSearchManager.Instance.HistoryList.Count <= 0)
{
this.Visibility = Visibility.Collapsed;
return;
}
this.Visibility = Visibility.Visible;
foreach (var historyEntry in cSearchManager.Instance.HistoryList)
{
var menuData = new cMenuDataBase() { MenuText = historyEntry.DisplayText, UiAction = new cUiProcessSearchHistoryEntry(historyEntry) };
if (historyEntry.isSeen)
{
menuData.MenuIcon = new IconData(MaterialIcons.MaterialIconType.ic_visibility);
menuData.MenuIconSize = 0.75;
}
MainStackPanel.Children.Add(new CustomMenuItem(true) { MenuData = menuData });
}
ProcessNewItems();
}
catch (Exception E)
{
LogException(E);
}
finally
{
LogMethodEnd(CM);
}
}
public void UpdateSearchRelations(ILookup<enumFasdInformationClass, cMenuDataBase> relations)
{
try
{
if (_searchCategories is null)
return;
int indexOfMenuData = -1;
int indexOfMenuDataCategory = -1;
foreach (var relation in relations)
{
if (!_searchCategories.TryGetValue(relation.Key, out var category))
continue;
category.UpdateSearchRelations(relation);
if (indexOfMenuData != -1)
continue;
indexOfMenuData = GetIndexOf(relation.Select(value => value.Data), _lastSelectedMenuData?.Data);
if (indexOfMenuData != -1)
indexOfMenuDataCategory = GetIndexOf(_searchCategories.Values, category);
}
ProcessNewItems();
if (indexOfMenuData == -1)
return;
IndexOfSelectedResultItem = indexOfMenuData + _searchCategories.Take(indexOfMenuDataCategory).Sum(category => category.Value.GetItemCount());
}
catch (Exception ex)
{
LogException(ex);
}
// even if not ideal, seems to be the most performant solution for retrieving the index of IEnumerable
int GetIndexOf(IEnumerable<object> data, object value)
{
int index = 0;
foreach (var item in data)
{
if (item.Equals(value))
return index;
index++;
}
return -1;
}
}
private void ClearControl()
{
MainStackPanel.Children.Clear();
var mainCategory = new SearchResultCategory() { IsPending = false };
_searchCategories = new Dictionary<enumFasdInformationClass, SearchResultCategory>() { [enumFasdInformationClass.Main] = mainCategory };
MainStackPanel.Children.Add(mainCategory);
_lastSelectedMenuData = null;
IndexOfSelectedResultItem = int.MinValue;
HandleIndexOfSelectedResultItemChanged();
}
public void SelectCurrentResultItem()
{
try
{
int totalResultItemCount = 0;
foreach (var category in _searchCategories.Values)
{
int categoryItemCount = category.GetItemCount();
if (IndexOfSelectedResultItem < totalResultItemCount + categoryItemCount)
{
category.SelectItemAt(IndexOfSelectedResultItem - totalResultItemCount);
return;
}
totalResultItemCount += categoryItemCount;
}
}
catch (Exception E)
{
LogException(E);
}
}
private void UpdateCornerRadius()
{
try
{
const double cornerRadius = 10;
if (MainStackPanel.Children.Count <= 0)
return;
if (MainStackPanel.Children[0] is CustomMenuItem resultItemFirst)
{
var tempCornerRadius = resultItemFirst.MenuItemBorder.CornerRadius;
tempCornerRadius.TopLeft = cornerRadius;
tempCornerRadius.TopRight = cornerRadius;
resultItemFirst.MenuItemBorder.CornerRadius = tempCornerRadius;
}
if (MainStackPanel.Children[MainStackPanel.Children.Count - 1] is CustomMenuItem resultItemLast)
{
var tempCornerRadiusLast = resultItemLast.MenuItemBorder.CornerRadius;
tempCornerRadiusLast.BottomLeft = cornerRadius;
tempCornerRadiusLast.BottomRight = cornerRadius;
resultItemLast.MenuItemBorder.CornerRadius = tempCornerRadiusLast;
}
}
catch (Exception E)
{
LogException(E);
}
}
private void ShowDetailsCheckBox_Checked(object sender, RoutedEventArgs e)
{
if (!(sender is CheckBox checkBox))
return;
cFasdCockpitConfig.Instance.ShowSearchResultDetails = checkBox.IsChecked == true;
cFasdCockpitConfig.Instance.Save("ShowSearchResultDetails");
UpdateSearchResultDetailsShown(checkBox.IsChecked);
}
private void UpdateSearchResultDetailsShown(bool? showDetailHeadings)
{
try
{
if (showDetailHeadings is null)
return;
if (MainStackPanel?.Children is null || MainStackPanel.Children.Count <= 0)
return;
foreach (var searchResult in MainStackPanel.Children.OfType<CustomMenuItem>())
{
searchResult.ShowDetailHeadings(showDetailHeadings.Value);
}
foreach (var searchResult in MainStackPanel.Children.OfType<SearchResultCategory>())
{
searchResult.ShowDetailHeadings(showDetailHeadings.Value);
}
}
catch (Exception ex)
{
LogException(ex);
}
}
public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses)
{
try
{
MainStackPanel.Children.Clear();
_searchCategories = new Dictionary<enumFasdInformationClass, SearchResultCategory>();
foreach (var informationClass in informationClasses.Reverse())
{
var searchResultCategory = new SearchResultCategory()
{
Title = string.Format(cMultiLanguageSupport.GetItem("Searchbar.Relations.SearchFor"), informationClass),
IsPending = true,
TitleIcon = GetIconFrom(informationClass),
Margin = new Thickness(0, 7.5, 0, 0)
};
_searchCategories.Add(informationClass, searchResultCategory);
MainStackPanel.Children.Add(searchResultCategory);
}
}
catch (Exception ex)
{
LogException(ex);
}
IconData GetIconFrom(enumFasdInformationClass informationClass)
{
switch (informationClass)
{
case enumFasdInformationClass.Computer:
return new IconData(enumInternIcons.misc_computer);
case enumFasdInformationClass.User:
return new IconData(enumInternIcons.misc_user);
case enumFasdInformationClass.Ticket:
return new IconData(enumInternIcons.menuBar_mail);
case enumFasdInformationClass.VirtualSession:
return new IconData(MaterialIcons.MaterialIconType.ic_cloud_queue);
case enumFasdInformationClass.MobileDevice:
return new IconData(MaterialIcons.MaterialIconType.ic_smartphone);
default:
return new IconData(enumInternIcons.f4sd);
}
}
}
public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses)
{
try
{
if (_searchCategories is null)
return;
var finishedInformationClasses = _searchCategories.Where(category => !informationClasses.Contains(category.Key)).Select(category => category.Value);
foreach (var pendingCategory in finishedInformationClasses)
{
pendingCategory.IsPending = false;
if (pendingCategory.GetItemCount() == 0)
pendingCategory.Margin = new Thickness(0);
else
pendingCategory.Margin = new Thickness(0, 7.5, 0, 0);
}
if (IndexOfSelectedResultItem != int.MinValue)
return;
if (_searchCategories.Values.Any(c => c.IsPending))
return;
IndexOfSelectedResultItem = _searchCategories.Values.Sum(c => c.GetItemCount()) - 1;
}
catch (Exception ex)
{
LogException(ex);
}
}
}
}

View File

@@ -0,0 +1,48 @@
<UserControl x:Class="FasdDesktopUi.Basics.UserControls.SearchResult.SearchResultCategory"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FasdDesktopUi.Basics.UserControls.SearchResult"
xmlns:uc="clr-namespace:FasdDesktopUi.Basics.UserControls"
xmlns:ico="clr-namespace:FasdDesktopUi.Basics.UserControls.AdaptableIcon;assembly=F4SD-AdaptableIcon"
mc:Ignorable="d"
d:DesignHeight="80"
d:DesignWidth="450"
x:Name="SearchResultCategoryUc">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
</UserControl.Resources>
<StackPanel>
<Border Background="{DynamicResource BackgroundColor.Menu.SubCategory}"
CornerRadius="7.5"
Visibility="{Binding ElementName=SearchResultCategoryUc, Path=IsPending, Converter={StaticResource BoolToVisibility}}">
<DockPanel>
<ico:AdaptableIcon SelectedInternIcon="{Binding ElementName=SearchResultCategoryUc, Path=TitleIcon.Intern}"
SelectedMaterialIcon="{Binding ElementName=SearchResultCategoryUc, Path=TitleIcon.Material}"
Style="{DynamicResource Menu.CategoryBase.Icon.NoHover}"
IconHeight="22.5"
IconWidth="22.5"
Margin="10 2.5"
DockPanel.Dock="Left" />
<ico:AdaptableIcon SelectedInternGif="loadingPoints"
IconHeight="40"
IconWidth="40"
Margin="10 2.5"
DockPanel.Dock="Right" />
<TextBlock Text="{Binding ElementName=SearchResultCategoryUc, Path=Title}"
Style="{DynamicResource Menu.CategoryBase.TextBlock.NoHover}"
DockPanel.Dock="Left" />
</DockPanel>
</Border>
<uc:CustomMenu x:Name="ResultMenu"
x:FieldModifier="private" />
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,91 @@
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UiActions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace FasdDesktopUi.Basics.UserControls.SearchResult
{
public partial class SearchResultCategory : UserControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(SearchResultCategory), new PropertyMetadata(string.Empty));
public bool IsPending
{
get { return (bool)GetValue(IsPendingProperty); }
set { SetValue(IsPendingProperty, value); }
}
public static readonly DependencyProperty IsPendingProperty =
DependencyProperty.Register("IsPending", typeof(bool), typeof(SearchResultCategory), new PropertyMetadata(false));
public IconData TitleIcon
{
get { return (IconData)GetValue(TitleIconProperty); }
set { SetValue(TitleIconProperty, value); }
}
public static readonly DependencyProperty TitleIconProperty =
DependencyProperty.Register("TitleIcon", typeof(IconData), typeof(SearchResultCategory), new PropertyMetadata(new IconData(enumInternIcons.f4sd)));
public SearchResultCategory()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
AddHandler(CustomEventManager.IndexChangedEvent, new CustomEventManager.IndexChangedHandlerDelegate(HandleSelectedIndexChanged));
}
private void HandleSelectedIndexChanged(object sender, IndexEventArgs args)
{
if (args.OriginalSource == this)
return;
CustomEventManager.RaiseIndexChangedEvent(this, args.OldValue, args.NewValue);
}
public void UpdateSearchResults(cFasdApiSearchResultCollection results, ISearchUiProvider uiSearchProvider)
{
ResultMenu.MenuDataList = results.Select(searchResult =>
{
var requiredInformationClasses = new List<enumFasdInformationClass>() { cF4sdIdentityEntry.GetFromSearchResult(searchResult.Value.First().Type) };
bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(requiredInformationClasses);
var menuData = new cMenuDataSearchResult(searchResult.Key, uiSearchProvider, searchResult.Value) as cMenuDataBase;
menuData.UiAction.DisplayType = isEnabled ? Enums.enumActionDisplayType.enabled : Enums.enumActionDisplayType.disabled;
menuData.UiAction.Description = isEnabled ? string.Empty : cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard");
menuData.UiAction.AlternativeDescription = isEnabled ? string.Empty : cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard");
return menuData;
}).ToList();
}
public void UpdateSearchRelations(IEnumerable<cMenuDataBase> relations) => ResultMenu.MenuDataList = relations.ToList();
internal void ShowDetailHeadings(bool showDetailHeadings) => ResultMenu.ShowDetailHeading(showDetailHeadings);
internal int GetItemCount() => ResultMenu.MenuDataList.Count;
internal void HiglightItemAt(int index) => ResultMenu.HightlightItemAt(index);
internal void SelectItemAt(int index) => ResultMenu.SelectItemAt(index);
}
}