diff --git a/src/RepoM.App/ActionMenuCore/RepositoryTagsFactoryV2.cs b/src/RepoM.App/ActionMenuCore/RepositoryTagsFactoryV2.cs index 1bcb1a9d..d3ac879b 100644 --- a/src/RepoM.App/ActionMenuCore/RepositoryTagsFactoryV2.cs +++ b/src/RepoM.App/ActionMenuCore/RepositoryTagsFactoryV2.cs @@ -27,8 +27,5 @@ public RepositoryTagsFactoryV2( _filename = fileSystem.Path.Combine(appDataPathProvider.AppDataPath, "TagsV2.yaml"); } - public Task> GetTagsAsync(Repository repository) - { - return _newStyleActionMenuFactory.GetTagsAsync(repository, _filename); - } -} \ No newline at end of file + public Task> GetTagsAsync(Repository repository) => _newStyleActionMenuFactory.GetTagsAsync(repository,_filename); +} diff --git a/src/RepoM.App/App.xaml b/src/RepoM.App/App.xaml index d47a215c..f6282052 100644 --- a/src/RepoM.App/App.xaml +++ b/src/RepoM.App/App.xaml @@ -1,109 +1,50 @@ - + - - + - + - + + + + + + - - - + + + + + + + - + - - - - - - + + - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/RepoM.App/App.xaml.cs b/src/RepoM.App/App.xaml.cs index c8770aaf..2b441951 100644 --- a/src/RepoM.App/App.xaml.cs +++ b/src/RepoM.App/App.xaml.cs @@ -8,20 +8,21 @@ namespace RepoM.App; using System.Threading; using System.Windows; using Hardcodet.Wpf.TaskbarNotification; -using RepoM.Api.Git; -using RepoM.Api.IO; -using RepoM.App.i18n; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using RepoM.Api; +using RepoM.Api.Git; +using RepoM.Api.IO; using RepoM.Api.Plugins; +using RepoM.App.i18n; using RepoM.App.Plugins; +using RepoM.App.Services; +using RepoM.App.Services.HotKey; using Serilog; using Serilog.Core; -using ILogger = Microsoft.Extensions.Logging.ILogger; -using RepoM.App.Services; +using Application = System.Windows.Application; using Container = SimpleInjector.Container; -using RepoM.App.Services.HotKey; -using RepoM.Api; +using ILogger = Microsoft.Extensions.Logging.ILogger; /// /// Interaction logic for App.xaml @@ -35,6 +36,29 @@ public partial class App : Application private HotKeyService? _hotKeyService; private WindowSizeService? _windowSizeService; + //private static UiApplication? _uiApplication; + + private static App? _app; + + private Window? _mainWindow; + public Window? MainWindowInstance + { + get => _app?.MainWindow ?? _mainWindow; + set + { + if (_app != null) + { + _app.MainWindow = value; + } + + _mainWindow = value; + } + } + + + /// + /// Main program start point + /// [STAThread] public static void Main() { @@ -44,11 +68,15 @@ public static void Main() } Thread.CurrentThread.Name ??= "UI"; - var app = new App(); - app.InitializeComponent(); - app.Run(); + _app = new App(); + _app.InitializeComponent(); + _app.Run(); } + /// + /// OnStartup event + /// + /// protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); @@ -85,7 +113,7 @@ protected override async void OnStartup(StartupEventArgs e) EnsureStartup ensureStartup = Bootstrapper.Container.GetInstance(); await ensureStartup.EnsureFilesAsync().ConfigureAwait(true); - + UseRepositoryMonitor(Bootstrapper.Container); _moduleService = Bootstrapper.Container.GetInstance(); @@ -104,18 +132,22 @@ protected override async void OnStartup(StartupEventArgs e) logger.LogError(exception, "Could not start all modules."); } } - + + /// + /// OnExit Event + /// + /// protected override void OnExit(ExitEventArgs e) { _windowSizeService?.Unregister(); - + _moduleService?.StopAsync().GetAwaiter().GetResult(); _hotKeyService?.Unregister(); -// #pragma warning disable CA1416 // Validate platform compatibility + // #pragma warning disable CA1416 // Validate platform compatibility _notifyIcon?.Dispose(); -// #pragma warning restore CA1416 // Validate platform compatibility + // #pragma warning restore CA1416 // Validate platform compatibility ReleaseAndDisposeMutex(); @@ -199,5 +231,5 @@ private static void ReleaseAndDisposeMutex() } } - public static string? AvailableUpdate { get; private set; } = null; -} \ No newline at end of file + public static string? AvailableUpdate { get; private set; } +} diff --git a/src/RepoM.App/Controls/AcrylicContextMenu.cs b/src/RepoM.App/Controls/AcrylicContextMenu.cs index b730a7cd..26ba62b2 100644 --- a/src/RepoM.App/Controls/AcrylicContextMenu.cs +++ b/src/RepoM.App/Controls/AcrylicContextMenu.cs @@ -1,15 +1,13 @@ namespace RepoM.App.Controls; -using System.Windows; using System.Windows.Controls; -using RepoM.App.Services; public class AcrylicContextMenu : ContextMenu { - protected override void OnOpened(RoutedEventArgs e) - { - base.OnOpened(e); + //protected override void OnOpened(RoutedEventArgs e) + //{ + // base.OnOpened(e); - AcrylicHelper.EnableBlur(this); - } + // AcrylicHelper.EnableBlur(this); + //} } \ No newline at end of file diff --git a/src/RepoM.App/Controls/AcrylicMenuItem.cs b/src/RepoM.App/Controls/AcrylicMenuItem.cs index ce024f8f..429cd40d 100644 --- a/src/RepoM.App/Controls/AcrylicMenuItem.cs +++ b/src/RepoM.App/Controls/AcrylicMenuItem.cs @@ -1,54 +1,48 @@ namespace RepoM.App.Controls; -using System; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using RepoM.App.Services; - -public class AcrylicMenuItem : MenuItem +public class AcrylicMenuItem : Wpf.Ui.Controls.MenuItem { - private static readonly Brush _solidColorBrush = new SolidColorBrush(Color.FromArgb(80, 0, 0, 0)); - - protected override void OnSubmenuOpened(RoutedEventArgs e) - { - base.OnSubmenuOpened(e); - - Dispatcher.BeginInvoke((Action)BlurSubMenu); - } - - private void BlurSubMenu() - { - DependencyObject firstSubItem = ItemContainerGenerator.ContainerFromIndex(0); - - if (firstSubItem == null) - { - return; - } - - if (VisualTreeHelper.GetParent(firstSubItem) is not Visual container) - { - return; - } - - DependencyObject parent = container; - var borderIndex = 0; - - while (parent != null) - { - if (parent is Border b) - { - // only put color on the first border (transparent colors will add up otherwise) - b.Background = borderIndex == 0 - ? _solidColorBrush - : Brushes.Transparent; - - borderIndex++; - } - - parent = VisualTreeHelper.GetParent(parent); - } - - AcrylicHelper.EnableBlur(container); - } -} \ No newline at end of file + //private static readonly Brush _solidColorBrush = new SolidColorBrush(Color.FromArgb(80, 0, 0, 0)); + + //protected override void OnSubmenuOpened(RoutedEventArgs e) + //{ + // base.OnSubmenuOpened(e); + + // Dispatcher.BeginInvoke((Action)BlurSubMenu); + //} + + //private void BlurSubMenu() + //{ + // //DependencyObject firstSubItem = ItemContainerGenerator.ContainerFromIndex(0); + + // //if (firstSubItem == null) + // //{ + // // return; + // //} + + // //if (VisualTreeHelper.GetParent(firstSubItem) is not Visual container) + // //{ + // // return; + // //} + + // //DependencyObject parent = container; + // //var borderIndex = 0; + + // //while (parent != null) + // //{ + // // if (parent is Border b) + // // { + // // // only put color on the first border (transparent colors will add up otherwise) + // // //b.Background = borderIndex == 0 + // // // ? _solidColorBrush + // // // : Brushes.Transparent; + + // // borderIndex++; + // // } + + // // parent = VisualTreeHelper.GetParent(parent); + // //} + + // //AcrylicHelper.EnableBlur(container); + //} +} diff --git a/src/RepoM.App/Controls/ZTextBox.cs b/src/RepoM.App/Controls/ZTextBox.cs index e3a6deee..7504da3a 100644 --- a/src/RepoM.App/Controls/ZTextBox.cs +++ b/src/RepoM.App/Controls/ZTextBox.cs @@ -2,10 +2,19 @@ namespace RepoM.App.Controls; using System; using System.Collections.Generic; -using System.Windows.Controls; using System.Windows.Input; -public class ZTextBox : TextBox +/// +/// The ZTextBox class is a custom control that extends the TextBox class in WPF. It adds additional functionality to handle specific key events. Here's a brief explanation: +/// • Namespace: RepoM.App.Controls +/// • Base Class: TextBox +/// • Event: Finish - This event is triggered when certain keys (Down, Return, Enter) are pressed. +/// • Key Handling: +/// • Clears the text when the Escape key is pressed. +/// • Triggers the Finish event when any of the keys in the FinisherKeys list are pressed. +/// This custom control can be useful in scenarios where you need to perform specific actions based on key presses within a text box. +/// +public class ZTextBox : Wpf.Ui.Controls.TextBox { public event EventHandler? Finish; @@ -24,10 +33,10 @@ protected override void OnKeyUp(KeyEventArgs e) } } - private static List FinisherKeys { get; } = new(3) - { + private static List FinisherKeys { get; } = + [ Key.Down, Key.Return, Key.Enter, - }; -} \ No newline at end of file + ]; +} diff --git a/src/RepoM.App/Converters/UtcToHumanizedLocalDateTimeConverter.cs b/src/RepoM.App/Converters/UtcToHumanizedLocalDateTimeConverter.cs index e75e9226..d1ebcaff 100644 --- a/src/RepoM.App/Converters/UtcToHumanizedLocalDateTimeConverter.cs +++ b/src/RepoM.App/Converters/UtcToHumanizedLocalDateTimeConverter.cs @@ -5,18 +5,28 @@ namespace RepoM.App.Converters; using System.Windows.Data; using RepoM.Api.Common; +/// +/// The UtcToHumanizedLocalDateTimeConverter class is a WPF value converter that +/// converts a UTC DateTime to a human-readable local time string. +/// It implements the IValueConverter interface and uses a HardcodededMiniHumanizer to format the date. +/// The Convert method handles the conversion, while the ConvertBack method is not implemented. +/// public class UtcToHumanizedLocalDateTimeConverter : IValueConverter { private static readonly HardcodededMiniHumanizer _humanizer = new(SystemClock.Instance); - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - DateTime date = DateTime.SpecifyKind(DateTime.Parse(value?.ToString() ?? string.Empty), DateTimeKind.Utc).ToLocalTime(); + DateTime parse; + var ok = DateTime.TryParse(value?.ToString() ?? string.Empty, CultureInfo.InvariantCulture, out parse); + if (!ok) + { + return null; + } + var date = DateTime.SpecifyKind(parse, DateTimeKind.Utc).ToLocalTime(); return _humanizer.HumanizeTimestamp(date); + } - public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException(); +} diff --git a/src/RepoM.App/Converters/UtcToLocalDateTimeConverter.cs b/src/RepoM.App/Converters/UtcToLocalDateTimeConverter.cs index c31eef15..7fcc5ffb 100644 --- a/src/RepoM.App/Converters/UtcToLocalDateTimeConverter.cs +++ b/src/RepoM.App/Converters/UtcToLocalDateTimeConverter.cs @@ -4,15 +4,30 @@ namespace RepoM.App.Converters; using System.Globalization; using System.Windows.Data; +/// +/// Converts a UTC DateTime to local DateTime. +/// public class UtcToLocalDateTimeConverter : IValueConverter { - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - return DateTime.SpecifyKind(DateTime.Parse(value?.ToString() ?? string.Empty), DateTimeKind.Utc).ToLocalTime(); - } + /// + /// Converts a UTC DateTime to local DateTime. + /// + /// The UTC DateTime value to convert. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A local DateTime value. + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => + DateTime.SpecifyKind(DateTime.Parse(value?.ToString() ?? string.Empty, CultureInfo.InvariantCulture), DateTimeKind.Utc).ToLocalTime(); - public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } -} \ No newline at end of file + /// + /// Converts a value back. This method is not implemented. + /// + /// The value that is produced by the binding target. + /// The type to convert to. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value. + /// Always thrown. + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException(); +} diff --git a/src/RepoM.App/MainWindow.xaml b/src/RepoM.App/MainWindow.xaml index da122d30..78a395ba 100644 --- a/src/RepoM.App/MainWindow.xaml +++ b/src/RepoM.App/MainWindow.xaml @@ -1,411 +1,507 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RepoM.App/MainWindow.xaml.cs b/src/RepoM.App/MainWindow.xaml.cs index 847fec59..db28e25a 100644 --- a/src/RepoM.App/MainWindow.xaml.cs +++ b/src/RepoM.App/MainWindow.xaml.cs @@ -2,6 +2,7 @@ namespace RepoM.App; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.IO.Abstractions; @@ -13,12 +14,12 @@ namespace RepoM.App; using System.Windows.Data; using System.Windows.Forms; using System.Windows.Input; +using System.Windows.Media; using Microsoft.Extensions.Logging; using RepoM.ActionMenu.Interface.UserInterface; using RepoM.Api.Common; using RepoM.Api.Git; using RepoM.Api.RepositoryActions; -using RepoM.App.Controls; using RepoM.App.Plugins; using RepoM.App.RepositoryActions; using RepoM.App.RepositoryFiltering; @@ -28,14 +29,14 @@ namespace RepoM.App; using RepoM.Core.Plugin.Common; using RepoM.Core.Plugin.RepositoryActions.Commands; using RepoM.Core.Plugin.RepositoryFiltering.Clause; -using SourceChord.FluentWPF; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; using Control = System.Windows.Controls.Control; using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MenuItem = Wpf.Ui.Controls.MenuItem; +using TextBlock = System.Windows.Controls.TextBlock; -/// -/// Interaction logic for MainWindow.xaml -/// -public partial class MainWindow +public partial class MainWindow : FluentWindow { private volatile bool _refreshDelayed; private DateTime _timeOfLastRefresh = DateTime.MinValue; @@ -68,6 +69,7 @@ public MainWindow( ILogger logger, IUserMenuActionMenuFactory userMenuActionFactory) { + _repositoryFilteringManager = repositoryFilteringManager ?? throw new ArgumentNullException(nameof(repositoryFilteringManager)); _repositoryMatcher = repositoryMatcher ?? throw new ArgumentNullException(nameof(repositoryMatcher)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -80,8 +82,6 @@ public MainWindow( InitializeComponent(); - SetAcrylicWindowStyle(this, AcrylicWindowStyle.None); - var orderingsViewModel = new OrderingsViewModel(repositoryComparerManager, threadDispatcher); var queryParsersViewModel = new QueryParsersViewModel(_repositoryFilteringManager, threadDispatcher); var filterViewModel = new FiltersViewModel(_repositoryFilteringManager, threadDispatcher); @@ -102,8 +102,8 @@ public MainWindow( _monitor.OnScanStateChanged += OnScanStateChanged; ShowScanningState(_monitor.Scanning); } - - lstRepositories.ItemsSource = aggregator.Repositories; + + LstRepositories.ItemsSource = aggregator.Repositories; var view = (ListCollectionView)CollectionViewSource.GetDefaultView(aggregator.Repositories); ((ICollectionView)view).CollectionChanged += View_CollectionChanged; @@ -113,22 +113,54 @@ public MainWindow( repositoryFilteringManager.SelectedQueryParserChanged += (_, _) => view.Refresh(); repositoryFilteringManager.SelectedFilterChanged += (_, _) => view.Refresh(); + + + ApplicationThemeManager.ApplySystemTheme(true); // Applies the system theme for Apps, not for + ApplicationAccentColorManager.ApplySystemAccent(); + WindowBackdrop.ApplyBackdrop(this, WindowBackdropType.Mica); + SystemThemeWatcher.Watch(this); + + //Loaded += OnLoaded; + + // TODO: DELETE THIS LINE + //ApplicationThemeManager.Apply(ApplicationTheme.Light); + + ApplicationThemeManager.Changed += OnAppThemeChange; + PlaceFormByTaskBarLocation(); + + } + + private void OnAppThemeChange(ApplicationTheme currentapplicationtheme, Color systemaccent) + { + // TODO: IMPLEMENT FUNCTION TO CHANGE SETTINGS + //throw new NotImplementedException(); + } + + private void Window_SizeChanged(object sender, SizeChangedEventArgs e) + { + PlaceFormByTaskBarLocation(); + } + + void OnLoaded(object sender, RoutedEventArgs args) + { + } - private void View_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + private void View_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { - // use the list's itemsource directly, this one is not filtered (otherwise searching in the UI without matches could lead to the "no repositories yet"-screen) - var hasRepositories = lstRepositories.ItemsSource.OfType().Any(); - tbNoRepositories.Visibility = hasRepositories ? Visibility.Hidden : Visibility.Visible; + // use the list's items source directly, this one is not filtered (otherwise searching in the UI without matches could lead to the "no repositories yet"-screen) + var hasRepositories = LstRepositories.ItemsSource.OfType().Any(); + TbNoRepositories.SetCurrentValue(VisibilityProperty, hasRepositories ? Visibility.Hidden : Visibility.Visible); } protected override void OnActivated(EventArgs e) { + base.OnActivated(e); ShowUpdateIfAvailable(); - txtFilter.Focus(); - txtFilter.SelectAll(); + TxtFilter.Focus(); + TxtFilter.SelectAll(); } protected override void OnDeactivated(EventArgs e) @@ -156,7 +188,7 @@ protected override void OnPreviewKeyDown(KeyEventArgs e) return; } - var isFilterActive = txtFilter.IsFocused && !string.IsNullOrEmpty(txtFilter.Text); + var isFilterActive = TxtFilter.IsFocused && !string.IsNullOrEmpty(TxtFilter.Text); if (!isFilterActive) { Hide(); @@ -175,8 +207,8 @@ public void ShowAndActivate() } Activate(); - txtFilter.Focus(); - txtFilter.SelectAll(); + TxtFilter.Focus(); + TxtFilter.SelectAll(); }); } @@ -187,7 +219,7 @@ private void OnScanStateChanged(object? sender, bool isScanning) private async void LstRepositories_MouseDoubleClick(object? sender, MouseButtonEventArgs e) { - // prevent doubleclicks from scrollbars and other non-data areas + // prevent double clicks from scrollbars and other non-data areas if (e.OriginalSource is not (Grid or TextBlock)) { return; @@ -219,7 +251,7 @@ private async void LstRepositories_ContextMenuOpening(object? sender, ContextMen e.Handled = true; } } - + private async Task LstRepositoriesContextMenuOpeningWrapperAsync(ContextMenu ctxMenu) { try @@ -231,16 +263,16 @@ private async Task LstRepositoriesContextMenuOpeningWrapperAsync(ContextMe _logger.LogError(e, "Could not create menu."); ctxMenu.Items.Clear(); - ctxMenu.Items.Add(new AcrylicMenuItem - { - Header = "Error", - IsEnabled = false, - }); - ctxMenu.Items.Add(new AcrylicMenuItem - { - Header = e.Message, - IsEnabled = false, - }); + ctxMenu.Items.Add(new MenuItem + { + Header = "Error", + IsEnabled = false, + }); + ctxMenu.Items.Add(new MenuItem + { + Header = e.Message, + IsEnabled = false, + }); return false; } @@ -248,30 +280,19 @@ private async Task LstRepositoriesContextMenuOpeningWrapperAsync(ContextMe private async Task LstRepositoriesContextMenuOpeningAsync(ContextMenu ctxMenu) { - if (lstRepositories.SelectedItem is not RepositoryViewModel vm) + if (LstRepositories.SelectedItem is not RepositoryViewModel vm) { return false; } var items = new List(); - // ItemCollection items = ctxMenu.Items; - // ItemCollection items = new ItemCollection(); - //items.Clear(); - - // foreach (var item in ctxMenu.Items) - // { - // if (item is Control c) - // { - // c.IsEnabled = false; - // } - // } ctxMenu.Items.Clear(); - ctxMenu.Items.Add(new AcrylicMenuItem - { - Header = "Loading ..", - IsEnabled = true, - }); + ctxMenu.Items.Add(new MenuItem + { + Header = "Loading ..", + IsEnabled = true, + }); await foreach (UserInterfaceRepositoryActionBase action in _userMenuActionFactory.CreateMenuAsync(vm.Repository).ConfigureAwait(true)) { @@ -282,7 +303,7 @@ private async Task LstRepositoriesContextMenuOpeningAsync(ContextMenu ctxM items.Add(new Separator()); } } - else if (action is DeferredSubActionsUserInterfaceRepositoryAction deferredAction) + else if (action is DeferredSubActionsUserInterfaceRepositoryAction) { Control? controlItem = CreateMenuItemNewStyleAsync(action, vm); if (controlItem != null) @@ -290,7 +311,7 @@ private async Task LstRepositoriesContextMenuOpeningAsync(ContextMenu ctxM items.Add(controlItem); } } - else if (action is UserInterfaceRepositoryAction uiAction) + else if (action is UserInterfaceRepositoryAction) { Control? controlItem = CreateMenuItemNewStyleAsync(action, vm); if (controlItem != null) @@ -308,7 +329,7 @@ private async Task LstRepositoriesContextMenuOpeningAsync(ContextMenu ctxM return true; } - + private async void LstRepositories_KeyDown(object? sender, KeyEventArgs e) { @@ -322,7 +343,7 @@ private async void LstRepositories_KeyDown(object? sender, KeyEventArgs e) { Console.WriteLine(exception); } - + return; } @@ -350,10 +371,10 @@ private async void LstRepositories_KeyDown(object? sender, KeyEventArgs e) } } } - + private async Task InvokeActionOnCurrentRepositoryAsync() { - if (lstRepositories.SelectedItem is not RepositoryViewModel selectedView) + if (LstRepositories.SelectedItem is not RepositoryViewModel selectedView) { return; } @@ -390,15 +411,21 @@ private async Task InvokeActionOnCurrentRepositoryAsync() private void HelpButton_Click(object sender, RoutedEventArgs e) { - transitionerMain.SelectedIndex = transitionerMain.SelectedIndex == 0 ? 1 : 0; + if (RepoGrid.Visibility == Visibility.Visible) + { + RepoGrid.SetCurrentValue(VisibilityProperty, Visibility.Collapsed); + HelpScrollViewer.SetCurrentValue(VisibilityProperty, Visibility.Visible); + } + else + { + RepoGrid.SetCurrentValue(VisibilityProperty, Visibility.Visible); + HelpScrollViewer.SetCurrentValue(VisibilityProperty, Visibility.Collapsed); + } } private void MenuButton_Click(object sender, RoutedEventArgs e) { - if (MenuButton.ContextMenu != null) - { - MenuButton.ContextMenu.IsOpen = true; - } + MenuButton.ContextMenu?.SetCurrentValue(ContextMenu.IsOpenProperty, true); } private void ScanButton_Click(object sender, RoutedEventArgs e) @@ -423,9 +450,9 @@ private void CustomizeContextMenu_Click(object sender, RoutedEventArgs e) if (_fileSystem.Directory.Exists(directoryName)) { Process.Start(new ProcessStartInfo(directoryName) - { - UseShellExecute = true, - }); + { + UseShellExecute = true, + }); } } @@ -456,48 +483,34 @@ private void SponsorButton_Click(object sender, RoutedEventArgs e) private static void Navigate(string url) { Process.Start(new ProcessStartInfo(url) - { - UseShellExecute = true, - }); + { + UseShellExecute = true, + }); } private void PlaceFormByTaskBarLocation() { - Point position = GetTopLeftPlaceFormByTaskBarLocation( + Point position = TaskBar.GetWindowPlacement( SystemParameters.WorkArea, - Height, - Width, + ActualHeight, + ActualWidth, Screen.PrimaryScreen); - Left = position.X; - Top = position.Y; - } - - private static Point GetTopLeftPlaceFormByTaskBarLocation(Rect workArea, double height, double width, Screen? primaryScreen) - { - var topY = workArea.Top; - var bottomY = workArea.Height - height; - var leftX = workArea.Left; - var rightX = workArea.Width - width; - return TaskBarLocator.GetTaskBarLocation(primaryScreen) switch - { - TaskBarLocator.TaskBarLocation.Top => new Point(rightX, topY), - TaskBarLocator.TaskBarLocation.Left => new Point(leftX, bottomY), - TaskBarLocator.TaskBarLocation.Bottom or TaskBarLocator.TaskBarLocation.Right => new Point(rightX, bottomY), - _ => new Point(rightX, bottomY), - }; + SetCurrentValue(LeftProperty, position.X); + SetCurrentValue(TopProperty, position.Y); } + private void ShowUpdateIfAvailable() { var updateHint = _translationService.Translate("Update hint", App.AvailableUpdate ?? "?.?"); - UpdateButton.Visibility = App.AvailableUpdate == null ? Visibility.Hidden : Visibility.Visible; - UpdateButton.ToolTip = App.AvailableUpdate == null ? "" : updateHint; - UpdateButton.Tag = App.AvailableUpdate; + UpdateButton.SetCurrentValue(VisibilityProperty, App.AvailableUpdate == null ? Visibility.Hidden : Visibility.Visible); + UpdateButton.SetCurrentValue(ToolTipProperty, App.AvailableUpdate == null ? "" : updateHint); + UpdateButton.SetCurrentValue(TagProperty, App.AvailableUpdate); var parent = (Grid)UpdateButton.Parent; - parent.ColumnDefinitions[Grid.GetColumn(UpdateButton)].Width = App.AvailableUpdate == null ? new GridLength(0) : GridLength.Auto; + parent.ColumnDefinitions[Grid.GetColumn(UpdateButton)].SetCurrentValue(ColumnDefinition.WidthProperty, App.AvailableUpdate == null ? new GridLength(0) : GridLength.Auto); } private Control? /*MenuItem*/ CreateMenuItem(RepositoryActionBase action, RepositoryViewModel? affectedViews = null) @@ -513,13 +526,13 @@ private void ShowUpdateIfAvailable() return null; } - Action clickAction = (object clickSender, object clickArgs) => + Action clickAction = (clickSender, clickArgs) => { if (repositoryAction?.Action is null or NullRepositoryCommand) { return; } - + // run actions in the UI async to not block it if (repositoryAction.ExecutionCausesSynchronizing) { @@ -533,11 +546,11 @@ private void ShowUpdateIfAvailable() } }; - var item = new AcrylicMenuItem - { - Header = repositoryAction.Name, - IsEnabled = repositoryAction.CanExecute, - }; + var item = new MenuItem + { + Header = repositoryAction.Name, + IsEnabled = repositoryAction.CanExecute, + }; item.Click += new RoutedEventHandler(clickAction); // this is a deferred submenu. We want to make sure that the context menu can pop up @@ -597,7 +610,7 @@ void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) return null; } - Action clickAction = (object clickSender, object clickArgs) => + Action clickAction = (clickSender, clickArgs) => { if (repositoryAction.RepositoryCommand is null or NullRepositoryCommand) { @@ -617,7 +630,7 @@ void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) } }; - var item = new AcrylicMenuItem + var item = new MenuItem { Header = repositoryAction.Name, IsEnabled = repositoryAction.CanExecute, @@ -637,7 +650,7 @@ async void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) { item.SubmenuOpened -= SelfDetachingEventHandler; item.Items.Clear(); - + foreach (UserInterfaceRepositoryActionBase subAction in await deferredRepositoryAction.GetAsync().ConfigureAwait(true)) { Control? controlItem = CreateMenuItemNewStyleAsync(subAction); @@ -672,7 +685,7 @@ async void SelfDetachingEventHandler(object _, RoutedEventArgs evtArgs) // this is a template submenu item to enable submenus under the current // menu item. this item gets removed when the real subitems are created item.Items.Add("Loading.."); - + async void SelfDetachingEventHandler1(object _, RoutedEventArgs evtArgs) { item.SubmenuOpened -= SelfDetachingEventHandler1; @@ -721,10 +734,10 @@ private static void SetVmSynchronizing(RepositoryViewModel? affectedVm, bool syn private void ShowScanningState(bool isScanning) { - ScanMenuItem.IsEnabled = !isScanning; - ScanMenuItem.Header = isScanning + ScanMenuItem.SetCurrentValue(MenuItem.IsEnabledProperty, !isScanning); + ScanMenuItem.SetCurrentValue(HeaderedItemsControl.HeaderProperty, isScanning ? _translationService.Translate("Scanning") - : _translationService.Translate("ScanComputer"); + : _translationService.Translate("ScanComputer")); } protected override void OnKeyDown(KeyEventArgs e) @@ -733,23 +746,13 @@ protected override void OnKeyDown(KeyEventArgs e) if (e.Key == Key.F && Keyboard.IsKeyDown(Key.LeftCtrl)) { - txtFilter.Focus(); - txtFilter.SelectAll(); - } - - if (e.Key == Key.Down && txtFilter.IsFocused) - { - lstRepositories.Focus(); + TxtFilter.Focus(); + TxtFilter.SelectAll(); } - // show/hide the titlebar to move the window for screenshots, for example - if (e.Key == Key.F11) + if (e.Key == Key.Down && TxtFilter.IsFocused) { - AcrylicWindowStyle currentStyle = GetAcrylicWindowStyle(this); - AcrylicWindowStyle newStyle = currentStyle == AcrylicWindowStyle.None - ? AcrylicWindowStyle.Normal - : AcrylicWindowStyle.None; - SetAcrylicWindowStyle(this, newStyle); + LstRepositories.Focus(); } // keep window open on deactivate to make screenshots, for example @@ -785,13 +788,13 @@ private void OnTxtFilterTextChanged(object? sender, TextChangedEventArgs e) } // Refresh the view - ICollectionView view = CollectionViewSource.GetDefaultView(lstRepositories.ItemsSource); + ICollectionView view = CollectionViewSource.GetDefaultView(LstRepositories.ItemsSource); view.Refresh(); } private bool FilterRepositories(object item) { - var query = txtFilter.Text.Trim(); + var query = TxtFilter.Text.Trim(); if (_refreshDelayed) { @@ -827,7 +830,7 @@ private bool FilterRepositories(object item) { return false; } - + if (string.IsNullOrWhiteSpace(query)) { return true; @@ -851,16 +854,23 @@ private bool FilterRepositories(object item) private void TxtFilter_Finish(object sender, EventArgs e) { - lstRepositories.Focus(); - if (lstRepositories.Items.Count <= 0) + LstRepositories.Focus(); + if (LstRepositories.Items.Count <= 0) { return; } - lstRepositories.SelectedIndex = 0; - var item = (ListBoxItem)lstRepositories.ItemContainerGenerator.ContainerFromIndex(0); + LstRepositories.SetCurrentValue(Selector.SelectedIndexProperty, 0); + var item = (ListBoxItem)LstRepositories.ItemContainerGenerator.ContainerFromIndex(0); item?.Focus(); } - public bool IsShown => Visibility == Visibility.Visible && IsActive; + public bool IsShown + { + get + { + return Visibility == Visibility.Visible && IsActive; + } + } + } \ No newline at end of file diff --git a/src/RepoM.App/NotifyIconResources.xaml b/src/RepoM.App/NotifyIconResources.xaml index 39e087e7..49fc7024 100644 --- a/src/RepoM.App/NotifyIconResources.xaml +++ b/src/RepoM.App/NotifyIconResources.xaml @@ -1,40 +1,47 @@ - + - - - - - - - - - + + + + + + + + + - - + + - - - - - + + + + + \ No newline at end of file diff --git a/src/RepoM.App/RepoM.App.csproj b/src/RepoM.App/RepoM.App.csproj index 524fa33b..79c4575f 100644 --- a/src/RepoM.App/RepoM.App.csproj +++ b/src/RepoM.App/RepoM.App.csproj @@ -4,7 +4,7 @@ WinExe RepoM false - True + False true true false @@ -16,6 +16,7 @@ App.ico True + latest @@ -34,6 +35,8 @@ + + @@ -50,11 +53,15 @@ - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/RepoM.App/Services/AcrylicHelper.cs b/src/RepoM.App/Services/AcrylicHelper.cs index bd2dc61d..92a64594 100644 --- a/src/RepoM.App/Services/AcrylicHelper.cs +++ b/src/RepoM.App/Services/AcrylicHelper.cs @@ -1,16 +1,14 @@ namespace RepoM.App.Services; -using System.Windows; -using System.Windows.Interop; using System.Windows.Media; public static class AcrylicHelper { public static void EnableBlur(Visual visual) { - if (PresentationSource.FromVisual(visual) is HwndSource hwnd) - { - WindowsCompositionHelper.EnableBlur(hwnd.Handle); - } + //if (PresentationSource.FromVisual(visual) is HwndSource hwnd) + //{ + // WindowsCompositionHelper.EnableBlur(hwnd.Handle); + //} } } \ No newline at end of file diff --git a/src/RepoM.App/Services/TaskBarLocator.cs b/src/RepoM.App/Services/TaskBar.cs similarity index 52% rename from src/RepoM.App/Services/TaskBarLocator.cs rename to src/RepoM.App/Services/TaskBar.cs index d2d10b55..e34d5683 100644 --- a/src/RepoM.App/Services/TaskBarLocator.cs +++ b/src/RepoM.App/Services/TaskBar.cs @@ -1,8 +1,9 @@ namespace RepoM.App.Services; +using System.Windows; using System.Windows.Forms; -public static class TaskBarLocator +public static class TaskBar { public enum TaskBarLocation { @@ -32,4 +33,20 @@ public static TaskBarLocation GetTaskBarLocation(Screen? primaryScreen) ? TaskBarLocation.Left : TaskBarLocation.Right; } + + public static Point GetWindowPlacement(Rect workArea, double height, double width, Screen? primaryScreen) + { + var topY = workArea.Top; + var bottomY = workArea.Height - height; + var leftX = workArea.Left; + var rightX = workArea.Width - width; + + return GetTaskBarLocation(primaryScreen) switch + { + TaskBarLocation.Top => new Point(rightX, topY), + TaskBarLocation.Left => new Point(leftX, bottomY), + TaskBarLocation.Bottom or TaskBarLocation.Right => new Point(rightX, bottomY), + _ => new Point(rightX, bottomY), + }; + } } \ No newline at end of file diff --git a/src/RepoM.App/Services/WindowSizeService.cs b/src/RepoM.App/Services/WindowSizeService.cs index fa08cb0c..5449941e 100644 --- a/src/RepoM.App/Services/WindowSizeService.cs +++ b/src/RepoM.App/Services/WindowSizeService.cs @@ -107,6 +107,7 @@ public void Register() .Throttle(ThrottleWindowSizeChanged) .Subscribe(sizeChangedEvent => { + _appSettings.UpdateMenuSize( _currentResolution, // Yes, This possibliy can go wrong new MenuSize diff --git a/tests/RepoM.App.Tests/Services/TaskBarLocatorTests.cs b/tests/RepoM.App.Tests/Services/TaskBarTests.cs similarity index 57% rename from tests/RepoM.App.Tests/Services/TaskBarLocatorTests.cs rename to tests/RepoM.App.Tests/Services/TaskBarTests.cs index 570c138c..b67cb7e6 100644 --- a/tests/RepoM.App.Tests/Services/TaskBarLocatorTests.cs +++ b/tests/RepoM.App.Tests/Services/TaskBarTests.cs @@ -4,7 +4,7 @@ namespace RepoM.App.Tests.Services; using RepoM.App.Services; using Xunit; -public class TaskBarLocatorTests +public class TaskBarTests { [Fact] public void GetTaskBarLocation_ShouldReturnBottom_WhenNoScreenGiven() @@ -12,9 +12,9 @@ public void GetTaskBarLocation_ShouldReturnBottom_WhenNoScreenGiven() // arrange // act - TaskBarLocator.TaskBarLocation result = TaskBarLocator.GetTaskBarLocation(null); + TaskBar.TaskBarLocation result = TaskBar.GetTaskBarLocation(null); // assert - result.Should().Be(TaskBarLocator.TaskBarLocation.Bottom); + result.Should().Be(TaskBar.TaskBarLocation.Bottom); } } \ No newline at end of file