Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add taskbar progress bar #2757

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions Bloxstrap/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
using Microsoft.Win32;

using Bloxstrap.AppData;
using System.Windows.Shell;
using Bloxstrap.UI.Elements.Bootstrapper.Base;

namespace Bloxstrap
{
public class Bootstrapper
{
#region Properties
private const int ProgressBarMaximum = 10000;


private const double TaskbarProgressMaximumWpf = 1; // this can not be changed. keep it at 1.
private const int TaskbarProgressMaximumWinForms = WinFormsDialogBase.TaskbarProgressMaximum;

private const string AppSettings =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<Settings>\r\n" +
Expand All @@ -43,6 +48,8 @@ public class Bootstrapper

private bool _isInstalling = false;
private double _progressIncrement;
private double _taskbarProgressIncrement;
private double _taskbarProgressMaximum;
private long _totalDownloadedBytes = 0;

private bool _mustUpgrade => String.IsNullOrEmpty(AppData.State.VersionGuid) || File.Exists(AppData.LockFilePath) || !File.Exists(AppData.ExecutablePath);
Expand Down Expand Up @@ -74,13 +81,20 @@ private void UpdateProgressBar()
if (Dialog is null)
return;

// UI progress
int progressValue = (int)Math.Floor(_progressIncrement * _totalDownloadedBytes);

// bugcheck: if we're restoring a file from a package, it'll incorrectly increment the progress beyond 100
// too lazy to fix properly so lol
progressValue = Math.Clamp(progressValue, 0, ProgressBarMaximum);

Dialog.ProgressValue = progressValue;

// taskbar progress
double taskbarProgressValue = _taskbarProgressIncrement * _totalDownloadedBytes;
taskbarProgressValue = Math.Clamp(taskbarProgressValue, 0, _taskbarProgressMaximum);

Dialog.TaskbarProgressValue = taskbarProgressValue;
}

private void HandleConnectionError(Exception exception)
Expand Down Expand Up @@ -584,11 +598,20 @@ private async Task UpgradeRoblox()
// TODO: cancelling needs to always be enabled
Dialog.CancelEnabled = true;
Dialog.ProgressStyle = ProgressBarStyle.Continuous;
Dialog.TaskbarProgressState = TaskbarItemProgressState.Normal;

Dialog.ProgressMaximum = ProgressBarMaximum;

// compute total bytes to download
_progressIncrement = (double)ProgressBarMaximum / _versionPackageManifest.Sum(package => package.PackedSize);
int totalPackedSize = _versionPackageManifest.Sum(package => package.PackedSize);
_progressIncrement = (double)ProgressBarMaximum / totalPackedSize;

if (Dialog is WinFormsDialogBase)
_taskbarProgressMaximum = (double)TaskbarProgressMaximumWinForms;
else
_taskbarProgressMaximum = (double)TaskbarProgressMaximumWpf;

_taskbarProgressIncrement = _taskbarProgressMaximum / (double)totalPackedSize;
}

var extractionTasks = new List<Task>();
Expand Down Expand Up @@ -619,6 +642,7 @@ private async Task UpgradeRoblox()
await Task.Delay(1000);

Dialog.ProgressStyle = ProgressBarStyle.Marquee;
Dialog.TaskbarProgressState = TaskbarItemProgressState.Indeterminate;
SetStatus(Strings.Bootstrapper_Status_Configuring);
}

Expand Down
25 changes: 25 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/Base/WinFormsDialogBase.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using System.Windows.Forms;
using System.Windows.Shell;

using Bloxstrap.UI.Utility;

namespace Bloxstrap.UI.Elements.Bootstrapper.Base
{
public class WinFormsDialogBase : Form, IBootstrapperDialog
{
public const int TaskbarProgressMaximum = 100;

public Bloxstrap.Bootstrapper? Bootstrapper { get; set; }

private bool _isClosing;
Expand All @@ -15,6 +18,8 @@ public class WinFormsDialogBase : Form, IBootstrapperDialog
protected virtual ProgressBarStyle _progressStyle { get; set; }
protected virtual int _progressValue { get; set; }
protected virtual int _progressMaximum { get; set; }
protected virtual TaskbarItemProgressState _taskbarProgressState { get; set; }
protected virtual double _taskbarProgressValue { get; set; }
protected virtual bool _cancelEnabled { get; set; }

public string Message
Expand Down Expand Up @@ -65,6 +70,26 @@ public int ProgressValue
}
}

public TaskbarItemProgressState TaskbarProgressState
{
get => _taskbarProgressState;
set
{
_taskbarProgressState = value;
TaskbarProgress.SetProgressState(Process.GetCurrentProcess().MainWindowHandle, value);
}
}

public double TaskbarProgressValue
{
get => _taskbarProgressValue;
set
{
_taskbarProgressValue = value;
TaskbarProgress.SetProgressValue(Process.GetCurrentProcess().MainWindowHandle, (int)value, TaskbarProgressMaximum);
}
}

public bool CancelEnabled
{
get => _cancelEnabled;
Expand Down
5 changes: 5 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/ByfronDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
AllowsTransparency="True"
Background="Transparent"
Closing="Window_Closing">

<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressState="{Binding Path=TaskbarProgressState}" ProgressValue="{Binding Path=TaskbarProgressValue}" />
</Window.TaskbarItemInfo>

<Border CornerRadius="10" BorderBrush="#33393B3D" Background="{Binding Background}" BorderThickness="{Binding DialogBorder}">
<Grid>
<Image Source="{Binding ByfronLogoLocation}" RenderOptions.BitmapScalingMode="HighQuality" Width="114" Height="108" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="17,13,0,0" />
Expand Down
21 changes: 21 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/ByfronDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shell;

using Bloxstrap.UI.Elements.Bootstrapper.Base;
using Bloxstrap.UI.ViewModels.Bootstrapper;
Expand Down Expand Up @@ -65,6 +66,26 @@ public int ProgressValue
}
}

public TaskbarItemProgressState TaskbarProgressState
{
get => _viewModel.TaskbarProgressState;
set
{
_viewModel.TaskbarProgressState = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressState));
}
}

public double TaskbarProgressValue
{
get => _viewModel.TaskbarProgressValue;
set
{
_viewModel.TaskbarProgressValue = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressValue));
}
}

public bool CancelEnabled
{
get => _viewModel.CancelEnabled;
Expand Down
4 changes: 4 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/ClassicFluentDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">

<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressState="{Binding Path=TaskbarProgressState}" ProgressValue="{Binding Path=TaskbarProgressValue}" />
</Window.TaskbarItemInfo>

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
Expand Down
25 changes: 21 additions & 4 deletions Bloxstrap/UI/Elements/Bootstrapper/ClassicFluentDialog.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.ComponentModel;
using System.Windows.Forms;

using Wpf.Ui.Appearance;
using Wpf.Ui.Mvvm.Contracts;
using Wpf.Ui.Mvvm.Services;
using System.Windows.Shell;

using Bloxstrap.UI.ViewModels.Bootstrapper;
using Bloxstrap.UI.Elements.Bootstrapper.Base;
Expand Down Expand Up @@ -62,6 +59,26 @@ public int ProgressValue
}
}

public TaskbarItemProgressState TaskbarProgressState
{
get => _viewModel.TaskbarProgressState;
set
{
_viewModel.TaskbarProgressState = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressState));
}
}

public double TaskbarProgressValue
{
get => _viewModel.TaskbarProgressValue;
set
{
_viewModel.TaskbarProgressValue = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressValue));
}
}

public bool CancelEnabled
{
get => _viewModel.CancelEnabled;
Expand Down
4 changes: 4 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/FluentDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
WindowStyle="None"
mc:Ignorable="d">

<Window.TaskbarItemInfo>
<TaskbarItemInfo ProgressState="{Binding Path=TaskbarProgressState}" ProgressValue="{Binding Path=TaskbarProgressValue}" />
</Window.TaskbarItemInfo>

<!-- Background is for Aero theme only -->
<Grid Background="{Binding Path=BackgroundColourBrush, Mode=OneTime}">
<!-- Allow for drag -->
Expand Down
21 changes: 21 additions & 0 deletions Bloxstrap/UI/Elements/Bootstrapper/FluentDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.Windows.Threading;

namespace Bloxstrap.UI.Elements.Bootstrapper
Expand Down Expand Up @@ -72,6 +73,26 @@ public int ProgressValue
}
}

public TaskbarItemProgressState TaskbarProgressState
{
get => _viewModel.TaskbarProgressState;
set
{
_viewModel.TaskbarProgressState = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressState));
}
}

public double TaskbarProgressValue
{
get => _viewModel.TaskbarProgressValue;
set
{
_viewModel.TaskbarProgressValue = value;
_viewModel.OnPropertyChanged(nameof(_viewModel.TaskbarProgressValue));
}
}

public bool CancelEnabled
{
get => _viewModel.CancelEnabled;
Expand Down
3 changes: 3 additions & 0 deletions Bloxstrap/UI/IBootstrapperDialog.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Windows.Forms;
using System.Windows.Shell;

namespace Bloxstrap.UI
{
Expand All @@ -10,6 +11,8 @@ public interface IBootstrapperDialog
ProgressBarStyle ProgressStyle { get; set; }
int ProgressValue { get; set; }
int ProgressMaximum { get; set; }
TaskbarItemProgressState TaskbarProgressState { get; set; }
double TaskbarProgressValue { get; set; }
bool CancelEnabled { get; set; }

void ShowBootstrapper();
Expand Down
90 changes: 90 additions & 0 deletions Bloxstrap/UI/Utility/TaskbarProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Runtime.InteropServices;
using System.Windows.Shell;

namespace Bloxstrap.UI.Utility
{
// Modified from https://github.com/PowerShell/PSReadLine/blob/e9122d38e932614393ff61faf57d6518990d7226/PSReadLine/PlatformWindows.cs#L704
internal static class TaskbarProgress
{
private enum TaskbarStates
{
NoProgress = 0,
Indeterminate = 0x1,
Normal = 0x2,
Error = 0x4,
Paused = 0x8,
}

[ComImport()]
[Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface ITaskbarList3
{
// ITaskbarList
[PreserveSig]
int HrInit();

[PreserveSig]
int AddTab(IntPtr hwnd);

[PreserveSig]
int DeleteTab(IntPtr hwnd);

[PreserveSig]
int ActivateTab(IntPtr hwnd);

[PreserveSig]
int SetActiveAlt(IntPtr hwnd);

// ITaskbarList2
[PreserveSig]
int MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);

// ITaskbarList3
[PreserveSig]
int SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);

[PreserveSig]
int SetProgressState(IntPtr hwnd, TaskbarStates state);

// N.B. for copy/pasters: we've left out the rest of the ITaskbarList3 methods...
}

[ComImport()]
[Guid("56fdf344-fd6d-11d0-958a-006097c9a090")]
[ClassInterface(ClassInterfaceType.None)]
private class TaskbarInstance
{
}

private static Lazy<ITaskbarList3> _taskbarInstance = new Lazy<ITaskbarList3>(() => (ITaskbarList3)new TaskbarInstance());

private static TaskbarStates ConvertEnum(TaskbarItemProgressState state)
{
return state switch
{
TaskbarItemProgressState.None => TaskbarStates.NoProgress,
TaskbarItemProgressState.Indeterminate => TaskbarStates.Indeterminate,
TaskbarItemProgressState.Normal => TaskbarStates.Normal,
TaskbarItemProgressState.Error => TaskbarStates.Error,
TaskbarItemProgressState.Paused => TaskbarStates.Paused,
_ => throw new Exception($"Unrecognised TaskbarItemProgressState: {state}")
};
}

private static int SetProgressState(IntPtr windowHandle, TaskbarStates taskbarState)
{
return _taskbarInstance.Value.SetProgressState(windowHandle, taskbarState);
}

public static int SetProgressState(IntPtr windowHandle, TaskbarItemProgressState taskbarState)
{
return SetProgressState(windowHandle, ConvertEnum(taskbarState));
}

public static int SetProgressValue(IntPtr windowHandle, int progressValue, int progressMax)
{
return _taskbarInstance.Value.SetProgressValue(windowHandle, (ulong)progressValue, (ulong)progressMax);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shell;

using CommunityToolkit.Mvvm.Input;

Expand All @@ -19,6 +20,9 @@ public class BootstrapperDialogViewModel : NotifyPropertyChangedViewModel
public int ProgressMaximum { get; set; } = 0;
public int ProgressValue { get; set; } = 0;

public TaskbarItemProgressState TaskbarProgressState { get; set; } = TaskbarItemProgressState.Indeterminate;
public double TaskbarProgressValue { get; set; } = 0;

public bool CancelEnabled { get; set; } = false;
public Visibility CancelButtonVisibility => CancelEnabled ? Visibility.Visible : Visibility.Collapsed;

Expand Down