[Dev file previewer]Various improvements (#18259)

* Made file too big string variable

* Performance improvements

* Add progress bar to indicate loading

* Added Logging

* Added name to log files

* Push

* Updated expect.txt

* Push

* * Fixes small bug I sometimes encountered by unloading the application
* Fixes bug where sometimes the loading bar kept stuck (on "file is too big" screen

* Update expect.txt

* Resolved review comments
Added LogTrace() function

* Unifying tasks

* Removed unneccesary log message

* * Added margin to loading bar and text.
* Changed color of background to monaco dark skin color
* Centred loading bar

* Changed logger path

* Changed log path

* Fixed align of loading label

* Fix label size and position

Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
This commit is contained in:
Aaron Junker 2022-08-16 19:32:49 +02:00 коммит произвёл GitHub
Родитель 733041ba2b
Коммит 405d79e72f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 273 добавлений и 42 удалений

2
.github/actions/spell-check/expect.txt поставляемый
Просмотреть файл

@ -601,7 +601,7 @@ FILESUBTYPE
FILESYSPATH
filesystem
FILETIME
FILETYPE
filetype
FILEVERSION
Filtergraph
Filterkeyboard

Просмотреть файл

@ -25,6 +25,22 @@ namespace PTSettingsHelper
return result;
}
std::wstring get_local_low_folder_location()
{
PWSTR local_app_path;
winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, &local_app_path));
std::wstring result{ local_app_path };
CoTaskMemFree(local_app_path);
result += L"\\Microsoft\\PowerToys";
std::filesystem::path save_path(result);
if (!std::filesystem::exists(save_path))
{
std::filesystem::create_directories(save_path);
}
return result;
}
std::wstring get_module_save_folder_location(std::wstring_view powertoy_key)
{
std::wstring result = get_root_save_folder_location();

Просмотреть файл

@ -12,6 +12,7 @@ namespace PTSettingsHelper
std::wstring get_module_save_file_location(std::wstring_view powertoy_key);
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
std::wstring get_root_save_folder_location();
std::wstring get_local_low_folder_location();
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings);
json::JsonObject load_module_settings(std::wstring_view powertoy_name);

Просмотреть файл

@ -58,6 +58,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
this.Unload();
}
}

Просмотреть файл

@ -5,10 +5,13 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using Common;
using Microsoft.PowerToys.PreviewHandler.Monaco.Helpers;
using Microsoft.PowerToys.PreviewHandler.Monaco.Properties;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
@ -43,18 +46,50 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
/// </summary>
private Label _loading;
/// <summary>
/// Loading progress bar
/// </summary>
private ProgressBar _loadingBar;
/// <summary>
/// Grey background
/// </summary>
private Label _loadingBackground;
/// <summary>
/// Name of the virtual host
/// </summary>
public const string VirtualHostName = "PowerToysLocalMonaco";
/// <summary>
/// HTML code passed to the file
/// </summary>
#nullable enable
private string? _html;
#nullable disable
/// <summary>
/// Id for monaco language
/// </summary>
private string _vsCodeLangSet;
/// <summary>
/// The content of the previewing file in base64
/// </summary>
private string _base64FileCode;
[STAThread]
public override void DoPreview<T>(T dataSource)
{
Logger.LogTrace();
base.DoPreview(dataSource);
// Sets background color
new Task(SetBackground).Start();
// Starts loading screen
InitializeLoadingScreen();
new Task(InitializeLoadingScreen).Start();
// New webview2 element
_webView = new WebView2();
@ -70,10 +105,14 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
if (fileSize < _settings.MaxFileSize)
{
Task initializeIndexFileAndSelectedFileTask = new Task(() => { InitializeIndexFileAndSelectedFile(filePath); });
initializeIndexFileAndSelectedFileTask.Start();
try
{
InvokeOnControlThread(() =>
{
Logger.LogInfo("Create WebView2 environment");
ConfiguredTaskAwaitable<CoreWebView2Environment>.ConfiguredTaskAwaiter
webView2EnvironmentAwaiter = CoreWebView2Environment
.CreateAsync(userDataFolder: System.Environment.GetEnvironmentVariable("USERPROFILE") +
@ -81,6 +120,8 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
.ConfigureAwait(true).GetAwaiter();
webView2EnvironmentAwaiter.OnCompleted(() =>
{
_loadingBar.Value = 60;
this.Update();
InvokeOnControlThread(async () =>
{
try
@ -91,49 +132,43 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
}
_webView2Environment = webView2EnvironmentAwaiter.GetResult();
var vsCodeLangSet = FileHandler.GetLanguage(Path.GetExtension(filePath));
string fileContent;
using (StreamReader fileReader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
fileContent = fileReader.ReadToEnd();
fileReader.Close();
}
var base64FileCode = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(fileContent));
string html;
// prepping index html to load in
using (StreamReader htmlFileReader = new StreamReader(new FileStream(Settings.AssemblyDirectory + "\\index.html", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
html = htmlFileReader.ReadToEnd();
htmlFileReader.Close();
}
html = html.Replace("[[PT_LANG]]", vsCodeLangSet, StringComparison.InvariantCulture);
html = html.Replace("[[PT_WRAP]]", _settings.Wrap ? "1" : "0", StringComparison.InvariantCulture);
html = html.Replace("[[PT_THEME]]", Settings.GetTheme(), StringComparison.InvariantCulture);
html = html.Replace("[[PT_CODE]]", base64FileCode, StringComparison.InvariantCulture);
html = html.Replace("[[PT_URL]]", VirtualHostName, StringComparison.InvariantCulture);
_loadingBar.Value = 70;
this.Update();
// Initialize WebView
try
{
await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true);
// Wait until html is loaded
initializeIndexFileAndSelectedFileTask.Wait();
_webView.CoreWebView2.SetVirtualHostNameToFolderMapping(VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow);
_webView.NavigateToString(html);
Logger.LogInfo("Navigates to string of HTML file");
_webView.NavigateToString(_html);
_webView.NavigationCompleted += WebView2Init;
_webView.Height = this.Height;
_webView.Width = this.Width;
Controls.Add(_webView);
_webView.SendToBack();
_loadingBar.Value = 100;
this.Update();
}
catch (NullReferenceException)
catch (NullReferenceException e)
{
Logger.LogError("NullReferenceException catched. Skipping exception.", e);
}
}
catch (WebView2RuntimeNotFoundException)
catch (WebView2RuntimeNotFoundException e)
{
Logger.LogWarning("WebView2 was not found:");
Logger.LogWarning(e.Message);
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
// WebView2 not installed message
Label errorMessage = new Label();
@ -160,6 +195,8 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
InvokeOnControlThread(() =>
{
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label text = new Label();
text.Text = Resources.Exception_Occurred;
text.Text += e.Message;
@ -168,6 +205,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
text.Width = 500;
text.Height = 10000;
Controls.Add(text);
Logger.LogError(e.Message);
});
}
@ -175,11 +213,15 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
}
else
{
Logger.LogInfo("File is too big to display. Showing error message");
InvokeOnControlThread(() =>
{
Controls.Remove(_loading);
_loadingBar.Dispose();
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
Label errorMessage = new Label();
errorMessage.Text = Resources.Max_File_Size_Error;
errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture);
errorMessage.Width = 500;
errorMessage.Height = 50;
Controls.Add(errorMessage);
@ -194,6 +236,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
{
_webView.Height = this.Height;
_webView.Width = this.Width;
this.Update();
}
/// <summary>
@ -205,6 +248,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
// Checks if already navigated
if (!_hasNavigated)
{
Logger.LogInfo("Setting WebView2 settings");
CoreWebView2Settings settings = (sender as WebView2).CoreWebView2.Settings;
#if DEBUG
@ -234,10 +278,17 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
// Disable status bar
settings.IsStatusBarEnabled = false;
Logger.LogInfo("Remove loading elements");
Controls.Remove(_loading);
Controls.Remove(_loadingBar);
Controls.Remove(_loadingBackground);
#if DEBUG
_webView.CoreWebView2.OpenDevToolsWindow();
Logger.LogInfo("Opened Dev Tools window, because solution was built in debug mode");
#endif
_loadingBar.Value = 80;
this.Update();
}
}
@ -251,6 +302,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
if (_hasNavigated)
{
e.Cancel = false;
Logger.LogInfo("Stopped navigation from user");
}
// If it has navigated to index.html it stops further navigations
@ -260,24 +312,86 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
}
}
private void InitializeLoadingScreen()
private void SetBackground()
{
Logger.LogTrace();
InvokeOnControlThread(() =>
{
this.BackColor = Settings.BackgroundColor;
});
}
private void InitializeLoadingScreen()
{
Logger.LogTrace();
InvokeOnControlThread(() =>
{
_loadingBackground = new Label();
_loadingBackground.BackColor = Settings.BackgroundColor;
_loadingBackground.Width = this.Width;
_loadingBackground.Height = this.Height;
Controls.Add(_loadingBackground);
_loadingBackground.BringToFront();
_loadingBar = new ProgressBar();
_loadingBar.Width = this.Width - 10;
_loadingBar.Location = new Point(5, this.Height / 2);
_loadingBar.Maximum = 100;
_loadingBar.Value = 10;
Controls.Add(_loadingBar);
_loading = new Label();
_loading.Text = Resources.Loading_Screen_Message;
_loading.Width = this.Width;
_loading.Height = this.Height;
_loading.Height = 45;
_loading.Location = new Point(0, _loadingBar.Location.Y - _loading.Height);
_loading.TextAlign = ContentAlignment.TopCenter;
_loading.Font = new Font("MS Sans Serif", 16, FontStyle.Bold);
_loading.ForeColor = Settings.TextColor;
_loading.BackColor = Settings.BackgroundColor;
Controls.Add(_loading);
_loading.BringToFront();
_loadingBar.BringToFront();
this.Update();
});
Logger.LogInfo("Loading screen initialized");
}
private void InitializeIndexFileAndSelectedFile(string filePath)
{
Logger.LogInfo("Starting getting monaco language id out of filetype");
_vsCodeLangSet = FileHandler.GetLanguage(Path.GetExtension(filePath));
using (StreamReader fileReader = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
Logger.LogInfo("Starting reading requested file");
var fileContent = fileReader.ReadToEnd();
fileReader.Close();
_base64FileCode = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(fileContent));
Logger.LogInfo("Reading requested file ended");
}
// prepping index html to load in
using (StreamReader htmlFileReader = new StreamReader(new FileStream(Settings.AssemblyDirectory + "\\index.html", FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
Logger.LogInfo("Starting reading HTML source file");
_html = htmlFileReader.ReadToEnd();
htmlFileReader.Close();
Logger.LogInfo("Reading HTML source file ended");
}
_html = _html.Replace("[[PT_LANG]]", _vsCodeLangSet, StringComparison.InvariantCulture);
_html = _html.Replace("[[PT_WRAP]]", _settings.Wrap ? "1" : "0", StringComparison.InvariantCulture);
_html = _html.Replace("[[PT_THEME]]", Settings.GetTheme(), StringComparison.InvariantCulture);
_html = _html.Replace("[[PT_CODE]]", _base64FileCode, StringComparison.InvariantCulture);
_html = _html.Replace("[[PT_URL]]", VirtualHostName, StringComparison.InvariantCulture);
}
private async void DownloadLink_Click(object sender, EventArgs e)
{
await Launcher.LaunchUriAsync(new Uri("https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section"));
Logger.LogTrace();
}
}
}

Просмотреть файл

@ -118,22 +118,22 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Max_File_Size_Error" xml:space="preserve">
<value>This file is too big to preview.
Max file size: 50KB</value>
<comment>Error message which is shown when the file is too big.</comment>
<value>This file is too big to preview.
Max file size: %1KB</value>
<comment>Error message which is shown when the file is too big. %1 gets replaced by a value. Leave KB (means Kilobyte)</comment>
</data>
<data name="Exception_Occurred" xml:space="preserve">
<value>Exception occurred:
<value>Exception occurred:
</value>
<comment>Will be shown when an exception occurred.</comment>
<comment>Will be shown when an exception occurred.</comment>
</data>
<data name="Loading_Screen_Message" xml:space="preserve">
<value>Loading...</value>
<value>Loading...</value>
</data>
<data name="WebView2_Not_Installed_Message" xml:space="preserve">
<value>WebView2 not installed or found.</value>
<value>WebView2 not installed or found.</value>
</data>
<data name="Download_WebView2" xml:space="preserve">
<value>Download WebView2 to display this file.</value>
<value>Download WebView2 to display this file.</value>
</data>
</root>

Просмотреть файл

@ -53,7 +53,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco
{
if (GetTheme() == "dark")
{
return Color.DimGray;
return System.Drawing.ColorTranslator.FromHtml("#1e1e1e");
}
else
{

Просмотреть файл

@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using interop;
namespace Microsoft.PowerToys.PreviewHandler.Monaco.Helpers
{
public static class Logger
{
private static readonly IFileSystem _fileSystem = new FileSystem();
private static readonly string ApplicationLogPath = System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\logs\\FileExplorer_localLow\\Monaco";
static Logger()
{
if (!_fileSystem.Directory.Exists(ApplicationLogPath))
{
_fileSystem.Directory.CreateDirectory(ApplicationLogPath);
}
// Using InvariantCulture since this is used for a log file name
var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Monaco-log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
public static void LogError(string message)
{
Log(message, "ERROR");
}
public static void LogError(string message, Exception ex)
{
Log(
message + Environment.NewLine +
ex?.Message + Environment.NewLine +
"Inner exception: " + Environment.NewLine +
ex?.InnerException?.Message + Environment.NewLine +
"Stack trace: " + Environment.NewLine +
ex?.StackTrace,
"ERROR");
}
public static void LogWarning(string message)
{
Log(message, "WARNING");
}
public static void LogInfo(string message)
{
Log(message, "INFO");
}
private static void Log(string message, string type)
{
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
Trace.Indent();
Trace.WriteLine(GetCallerInfo());
Trace.WriteLine(message);
Trace.Unindent();
}
public static void LogTrace()
{
Log(string.Empty, "Trace");
}
private static string GetCallerInfo()
{
StackTrace stackTrace = new StackTrace();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType.Name;
return "[Method]: " + methodName?.Name + " [Class]: " + className;
}
}
}

Просмотреть файл

@ -297,7 +297,10 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
}
auto settingsRootPath = PTSettingsHelper::get_root_save_folder_location();
settingsRootPath = settingsRootPath + L"\\";
settingsRootPath += L"\\";
auto localLowPath = PTSettingsHelper::get_local_low_folder_location();
localLowPath += L"\\logs\\";
const auto tempDir = temp_directory_path();
auto reportDir = temp_directory_path() / "PowerToys\\";
@ -314,12 +317,24 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
// Remove updates folder contents
DeleteFolder(reportDir / "Updates");
}
catch (...)
{
printf("Failed to copy PowerToys folder\n");
return 1;
}
try
{
copy(localLowPath, reportDir, copy_options::recursive);
}
catch (...)
{
printf("Failed to copy logs saved in LocalLow\n");
return 1;
}
#ifndef _DEBUG
InstallationFolder::ReportStructure(reportDir);
#endif