From 81e20d618b3da6660630ff40cf6afb56ab0a69f9 Mon Sep 17 00:00:00 2001 From: Peter Kirmeier Date: Sun, 4 Dec 2022 01:24:30 +0100 Subject: [PATCH] Refactored menu window fading Improve TaskbarLogo (still flickers on load but only very shortly) --- Business/Menus.cs | 45 +++++---- Helpers/Fading.cs | 157 ------------------------------ UserInterface/Menu.xaml | 18 +++- UserInterface/Menu.xaml.cs | 147 ++++++++++++++++------------ UserInterface/TaskbarLogo.xaml.cs | 31 +++--- 5 files changed, 135 insertions(+), 263 deletions(-) delete mode 100644 Helpers/Fading.cs diff --git a/Business/Menus.cs b/Business/Menus.cs index 6e01083..ec63164 100644 --- a/Business/Menus.cs +++ b/Business/Menus.cs @@ -386,6 +386,16 @@ namespace SystemTrayMenu.Business return menuData; } + internal static void OpenFolder(string? path = null) + { + if (string.IsNullOrEmpty(path)) + { + path = Config.Path; + } + + Log.ProcessStart(path); + } + internal void SwitchOpenCloseByTaskbarItem() { SwitchOpenClose(true); @@ -476,16 +486,6 @@ namespace SystemTrayMenu.Business } } - internal static void OpenFolder(string? path = null) - { - if (string.IsNullOrEmpty(path)) - { - path = Config.Path; - } - - Log.ProcessStart(path); - } - private static void LoadMenu(object senderDoWork, DoWorkEventArgs eDoWork) { string path; @@ -1000,21 +1000,24 @@ namespace SystemTrayMenu.Business if (menuPrevious != null) { // Clean up menu status IsMenuOpen for previous one - ListView dgvPrevious = menuPrevious.GetDataGridView(); - foreach (ListViewItemData item in dgvPrevious.Items) + ListView? dgvPrevious = menuPrevious.GetDataGridView(); + if (dgvPrevious != null) { - RowData rowDataToClear = item.data; - if (rowDataToClear == (RowData)menuToShow.Tag) + foreach (ListViewItemData item in dgvPrevious.Items) { - rowDataToClear.IsMenuOpen = keepOrSetIsMenuOpen; + RowData rowDataToClear = item.data; + if (rowDataToClear == (RowData)menuToShow.Tag) + { + rowDataToClear.IsMenuOpen = keepOrSetIsMenuOpen; + } + else + { + rowDataToClear.IsMenuOpen = false; + } } - else - { - rowDataToClear.IsMenuOpen = false; - } - } - RefreshSelection(dgvPrevious); + RefreshSelection(dgvPrevious); + } // Hide old menu foreach (Menu menuToClose in menus.Where( diff --git a/Helpers/Fading.cs b/Helpers/Fading.cs deleted file mode 100644 index c987524..0000000 --- a/Helpers/Fading.cs +++ /dev/null @@ -1,157 +0,0 @@ -// -// Copyright (c) PlaceholderCompany. All rights reserved. -// - -namespace SystemTrayMenu.Helper -{ - using System; - using System.Windows.Threading; - - public class Fading - { - private const int Interval100FPS = 10; // 100fps=>1s/100fps=~10ms - - private const double StepIn = 0.20; - private const double StepOut = 0.10; - private const double Transparent = 0.80; - private const double TransparentMinus = 0.60; // Transparent - StepIn - private const double TransparentPlus = 0.85; // Transparent + StepOut - private const double Shown = 1.00; - private const double ShownMinus = 0.80; // Shown - StepIn - - private readonly DispatcherTimer timer = new(DispatcherPriority.Render); - private FadingState state = FadingState.Idle; - private double opacity; - private bool visible; - - internal Fading() - { - timer.Interval = TimeSpan.FromMilliseconds(Interval100FPS); - timer.Tick += (sender, e) => FadeStep(); - } - - internal event Action Hide; - - internal event Action Show; - - internal event EventHandler ChangeOpacity; - - internal enum FadingState - { - Idle, - Show, - ShowTransparent, - Hide, - } - - internal bool IsHiding => state == FadingState.Hide; - - internal void Fade(FadingState state) - { - StartStopTimer(state); - } - - private void StartStopTimer(FadingState newState) - { - if (newState == FadingState.Idle) - { - state = newState; - timer.Stop(); - } - else - { - state = newState; - timer.Start(); - } - } - - private void FadeStep() - { - switch (state) - { - case FadingState.Show: - if (!visible) - { - visible = true; - Show?.Invoke(); - opacity = 0; - ChangeOpacity?.Invoke(this, opacity); - } - else if (Properties.Settings.Default.UseFading && - opacity < ShownMinus) - { - opacity += StepIn; - ChangeOpacity?.Invoke(this, opacity); - } - else - { - if (!Properties.Settings.Default.UseFading) - { - // #393 provoke a redraw for the CS_DROPSHADOW to work - opacity = ShownMinus; - ChangeOpacity?.Invoke(this, opacity); - } - - opacity = Shown; - ChangeOpacity?.Invoke(this, opacity); - StartStopTimer(FadingState.Idle); - } - - break; - case FadingState.ShowTransparent: - if (!visible) - { - visible = true; - Show?.Invoke(); - opacity = 0; - ChangeOpacity?.Invoke(this, opacity); - } - else if (Properties.Settings.Default.UseFading && - opacity < TransparentMinus) - { - opacity += StepIn; - ChangeOpacity?.Invoke(this, opacity); - } - else if (Properties.Settings.Default.UseFading && - opacity > TransparentPlus) - { - opacity -= StepOut; - ChangeOpacity?.Invoke(this, opacity); - } - else - { - opacity = Transparent; - ChangeOpacity?.Invoke(this, opacity); - StartStopTimer(FadingState.Idle); - } - - break; - case FadingState.Hide: - if (Properties.Settings.Default.UseFading && - opacity > StepOut) - { - opacity -= StepOut; - ChangeOpacity?.Invoke(this, opacity); - } - else if (visible) - { - opacity = 0; - ChangeOpacity?.Invoke(this, opacity); - visible = false; - Hide?.Invoke(); - StartStopTimer(FadingState.Idle); - } - else - { - StartStopTimer(FadingState.Idle); - } - - break; - case FadingState.Idle: - default: - StartStopTimer(FadingState.Idle); - break; - } - } - } -} diff --git a/UserInterface/Menu.xaml b/UserInterface/Menu.xaml index 971dddd..907182c 100644 --- a/UserInterface/Menu.xaml +++ b/UserInterface/Menu.xaml @@ -16,13 +16,23 @@ - + - + Storyboard.TargetProperty="Opacity" + From="0.0" To="1.0" Duration="0:0:0.5" + Completed="FadeIn_Completed"/> + + + + + + + diff --git a/UserInterface/Menu.xaml.cs b/UserInterface/Menu.xaml.cs index e70544c..fd2fc05 100644 --- a/UserInterface/Menu.xaml.cs +++ b/UserInterface/Menu.xaml.cs @@ -26,13 +26,18 @@ namespace SystemTrayMenu.UserInterface /// public partial class Menu : Window { + private const int CornerRadius = 10; + + private static readonly RoutedEvent FadeInEvent = EventManager.RegisterRoutedEvent( + nameof(FadeIn), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Menu)); + + private static readonly RoutedEvent FadeOutEvent = EventManager.RegisterRoutedEvent( + nameof(FadeOut), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Menu)); + #if TODO // SEARCH public const string RowFilterShowAll = "[SortIndex] LIKE '%0%'"; #endif - private const int CornerRadius = 10; - - private readonly Fading fading = new(); - private bool isShowing; + private bool isFading; private bool directionToRight; private bool mouseDown; private Point lastLocation; @@ -48,56 +53,10 @@ namespace SystemTrayMenu.UserInterface timerUpdateIcons.Tick += TimerUpdateIcons_Tick; Closed += (_, _) => { - fading.Fade(Fading.FadingState.Idle); timerUpdateIcons.Stop(); isClosed = true; // TODO WPF Replace Forms wrapper }; - Opacity = 0D; - fading.ChangeOpacity += Fading_ChangeOpacity; - void Fading_ChangeOpacity(object? sender, double newOpacity) - { - if (newOpacity != Opacity && !IsDisposed && !Disposing) - { - Opacity = newOpacity; - } - } - - fading.Show += Fading_Show; - void Fading_Show() - { - try - { - isShowing = true; - Visibility = Visibility.Visible; - isShowing = false; - timerUpdateIcons.Start(); - } - catch (ObjectDisposedException) - { - Visibility = Visibility.Hidden; - isShowing = false; - Log.Info($"Could not open menu, old menu was disposing," + - $" IsDisposed={IsDisposed}"); - } - - if (Visibility == Visibility.Visible) - { - if (Level == 0) - { - Activate(); - Show(); - } - else - { - ShowActivated = false; - Show(); - } - } - } - - fading.Hide += Hide; - InitializeComponent(); Level = level; @@ -220,6 +179,8 @@ namespace SystemTrayMenu.UserInterface Loaded += (sender, e) => { NativeMethods.HideFromAltTab(this); + + RaiseEvent(new(routedEvent: FadeInEvent)); }; Closed += (sender, e) => @@ -263,6 +224,18 @@ namespace SystemTrayMenu.UserInterface internal event Action? CellMouseClick; + private event RoutedEventHandler FadeIn + { + add { AddHandler(FadeInEvent, value); } + remove { RemoveHandler(FadeInEvent, value); } + } + + private event RoutedEventHandler FadeOut + { + add { AddHandler(FadeOutEvent, value); } + remove { RemoveHandler(FadeOutEvent, value); } + } + internal enum StartLocation { Predecessor, @@ -283,13 +256,14 @@ namespace SystemTrayMenu.UserInterface internal string? FolderPath { get; set; } - internal bool IsUsable => Visibility == Visibility.Visible && !fading.IsHiding && !IsDisposed && !Disposing; + internal bool IsUsable => Visibility == Visibility.Visible && !isFading && !IsDisposed && !Disposing; #if TODO // TOUCH internal bool ScrollbarVisible { get; private set; } private ListView tableLayoutPanelDgvAndScrollbar => dgv; // TODO WPF Remove and replace with dgv #endif + internal void ResetSearchText() { textBoxSearch.Text = string.Empty; @@ -450,21 +424,58 @@ namespace SystemTrayMenu.UserInterface } } - internal void ShowWithFade() - { - fading.Fade(Fading.FadingState.Show); - } + internal void ShowWithFade() => Fading_Show(false); - internal void ShowTransparent() + internal void ShowTransparent() => Fading_Show(true); + + internal void Fading_Show(bool transparency) { - fading.Fade(Fading.FadingState.ShowTransparent); + timerUpdateIcons.Start(); + + if (Level == 0) + { + Activate(); + } + else + { + ShowActivated = false; + } + + Opacity = 0D; + Show(); + + if (Settings.Default.UseFading) + { + isFading = true; + if (transparency) + { + // TODO: FADING: Instead setting of opacity 100% only go up to 80% (Temporarily go to 100% as well) + RaiseEvent(new(routedEvent: FadeInEvent)); + } + else + { + RaiseEvent(new(routedEvent: FadeInEvent)); + } + } + else + { + Opacity = transparency ? 0.80D : 1D; + FadeIn_Completed(this, new()); + } } internal void HideWithFade() { - if (!isShowing) + if (Settings.Default.UseFading) { - fading.Fade(Fading.FadingState.Hide); + isFading = true; + + // TODO: FADING: Instead starting at opacity 100% it should start with 80% due to transparency setting + RaiseEvent(new(routedEvent: FadeOutEvent)); + } + else + { + FadeOut_Completed(this, new()); } } @@ -645,7 +656,7 @@ namespace SystemTrayMenu.UserInterface case StartLocation.Predecessor: RowData trigger = (RowData)Tag; - ListView dgv = menuPredecessor!.GetDataGridView()!; + ListView dgv = menuPredecessor!.GetDataGridView() !; // Set position on same height as the selected row from predecessor y = menuPredecessor.Location.Y; @@ -758,7 +769,7 @@ namespace SystemTrayMenu.UserInterface UpdateLayout(); SizeToContent = SizeToContent.Manual; #if TODO // SEARCH - dgvHeightSet = false; + dgvHeightSet = false; #endif } } @@ -770,9 +781,20 @@ namespace SystemTrayMenu.UserInterface labelStatus.Content = $"{filesAndFoldersCount} {Translator.GetText(elements)}"; } + private void FadeIn_Completed(object sender, EventArgs e) + { + isFading = false; + } + + private void FadeOut_Completed(object sender, EventArgs e) + { + isFading = false; + Hide(); + } + private void HandlePreviewKeyDown(object sender, KeyEventArgs e) { - searchPanel.Visibility= Visibility.Visible; + searchPanel.Visibility = Visibility.Visible; ModifierKeys modifiers = Keyboard.Modifiers; switch (e.Key) @@ -881,6 +903,7 @@ namespace SystemTrayMenu.UserInterface } double factorIconSizeInPercent = Properties.Settings.Default.IconSizeInPercent / 100f; + // IcoWidth 100% = 21px, 175% is 33, +3+2 is padding from ColumnIcon double icoWidth = (16 * Scaling.FactorByDpi) + 5; Resources["ColumnIconWidth"] = (double)(int)((icoWidth * factorIconSizeInPercent * Scaling.Factor) + 0.5); diff --git a/UserInterface/TaskbarLogo.xaml.cs b/UserInterface/TaskbarLogo.xaml.cs index 99de113..49cd4d5 100644 --- a/UserInterface/TaskbarLogo.xaml.cs +++ b/UserInterface/TaskbarLogo.xaml.cs @@ -12,15 +12,12 @@ namespace SystemTrayMenu.UserInterface using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; - using System.Windows.Threading; /// /// Logic of Taskbar window. /// public partial class TaskbarLogo : Window { - private DispatcherTimer? moveOutOfScreenTimer = null; - public TaskbarLogo() { InitializeComponent(); @@ -49,32 +46,28 @@ namespace SystemTrayMenu.UserInterface Title = myname; Closed += (_, _) => Application.Current.Shutdown(); - Deactivated += (_, _) => SetStateNormal(); - Activated += (_, _) => + Deactivated += SetStateNormal; + Activated += (object? sender, EventArgs e) => { - SetStateNormal(); + SetStateNormal(sender, e); Activate(); UpdateLayout(); Focus(); - moveOutOfScreenTimer = new DispatcherTimer( - TimeSpan.FromMilliseconds(500), - DispatcherPriority.Loaded, - (s, e) => - { - // Do this after loading because Top may be invalid at the beginning - // and when initial rendering is out of screen it will never be actually painted. - // This makes sure logo is rendered once and then window is moved. - Top += SystemParameters.VirtualScreenHeight; - ((DispatcherTimer)s!).IsEnabled = false; // only once - }, - Dispatcher.CurrentDispatcher); }; + ContentRendered += MoveOutOfScreen; + } + + private void MoveOutOfScreen(object? sender, EventArgs e) + { + // Do this only once + ContentRendered -= MoveOutOfScreen; + Top += SystemParameters.VirtualScreenHeight; } /// /// This ensures that next click on taskbaritem works as activate event/click event. /// - private void SetStateNormal() + private void SetStateNormal(object? sender, EventArgs e) { if (IsActive) {