Skip to content
Draft
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ winget install WinQuickLook --source msstore

That's it! WinQuickLook will run in the background, ready whenever you need it.

## 🛠️ Development

The repository keeps the current WPF app and the Windows App SDK / WinUI 3 app separated:

- `src/WinQuickLook.App` - WPF app entry point and WPF preview UI.
- `src/WinQuickLook.Core` - WPF app preview handlers and shared WPF-era services.
- `src/WinQuickLook.App.WinUI` - WinUI app entry point, tray integration, Explorer selection, and WinUI preview window.
- `src/WinQuickLook.Preview` - UI-independent preview provider layer used by the WinUI app.
- `src/WinQuickLook.Shell` - WPF-free shell integration used by the WinUI app.

Run the WinUI 3 app during vNext development:

```bash
dotnet run --project src/WinQuickLook.App.WinUI/WinQuickLook.App.WinUI.csproj -p:Platform=x64
```

## 💡 Use Cases

- **Quick Document Review** - Scan through multiple documents without opening Word, PDF readers, etc.
Expand Down
12 changes: 12 additions & 0 deletions WinQuickLook.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,22 @@
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src\WinQuickLook.App.WinUI/WinQuickLook.App.WinUI.csproj">
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src\WinQuickLook.Core/WinQuickLook.Core.csproj">
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src\WinQuickLook.Preview/WinQuickLook.Preview.csproj">
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src\WinQuickLook.Shell/WinQuickLook.Shell.csproj">
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src\WinQuickLook.CsWin32/WinQuickLook.CsWin32.csproj">
<Platform Solution="*|arm64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
Expand Down
13 changes: 13 additions & 0 deletions src/WinQuickLook.App.WinUI/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Application
x:Class="WinQuickLook.App.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<controls:XamlControlsResources />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
159 changes: 159 additions & 0 deletions src/WinQuickLook.App.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
using System;
using System.IO;
using System.Threading;

using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;

using Windows.Win32.UI.Input.KeyboardAndMouse;

using WinQuickLook.Messaging;
using WinQuickLook.Preview;
using WinQuickLook.Providers;

using WinRT.Interop;

namespace WinQuickLook.App.WinUI;

public partial class App
{
private readonly Mutex _mutex = new(false, AppParameters.Title);
private readonly LowLevelKeyboardHook _keyboardHook = new();
private readonly ShellExplorerProvider _shellExplorerProvider = new();
private readonly IPreviewProvider _previewProvider = AggregatePreviewProvider.CreateDefault();
private readonly DispatcherQueue _dispatcherQueue;

private Window? _messageWindow;
private PreviewWindow? _previewWindow;
private NativeNotifyIcon? _notifyIcon;
private bool _mutexAcquired;
private bool _disposed;

public App()
{
InitializeComponent();

_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
AppDomain.CurrentDomain.ProcessExit += (_, _) => DisposeServices();
}

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
if (!_mutex.WaitOne(0, false))
{
Exit();

return;
}

_mutexAcquired = true;

ConfigureNotifyIcon();
ConfigureKeyboardHook();
}

private void ConfigureNotifyIcon()
{
_messageWindow = new Window();

var hwnd = WindowNative.GetWindowHandle(_messageWindow);

WindowComposition.SetCloaked(hwnd, true);
_messageWindow.Activate();
NativeNotifyIcon.HideWindow(hwnd);

_notifyIcon = new NativeNotifyIcon(
hwnd,
AppParameters.Title,
() => _dispatcherQueue.TryEnqueue(PerformPreview),
() => _dispatcherQueue.TryEnqueue(ExitApplication));
}

private void ConfigureKeyboardHook()
{
_keyboardHook.PerformKeyDown = vkCode =>
{
switch (vkCode)
{
case VIRTUAL_KEY.VK_SPACE:
_dispatcherQueue.TryEnqueue(PerformPreview);
break;
case VIRTUAL_KEY.VK_ESCAPE:
_dispatcherQueue.TryEnqueue(ClosePreview);
break;
}
};

_keyboardHook.Start();
}

private void PerformPreview()
{
if (_previewWindow is not null)
{
ClosePreview();

return;
}

var fileSystemInfo = _shellExplorerProvider.GetSelectedItem();

if (fileSystemInfo is null || !_previewProvider.CanPreview(fileSystemInfo))
{
return;
}

var request = fileSystemInfo is FileInfo fileInfo
? PreviewRequestFactory.CreateWithDirectorySiblings(fileInfo.FullName, _previewProvider)
: PreviewRequestFactory.Create(fileSystemInfo.FullName);

_previewWindow = new PreviewWindow(_previewProvider, request);
_previewWindow.Closed += (_, _) => _previewWindow = null;
_previewWindow.Activate();
}

private void ClosePreview()
{
var previewWindow = _previewWindow;

if (previewWindow is null)
{
return;
}

_previewWindow = null;
previewWindow.Close();
}

private void ExitApplication()
{
DisposeServices();
Exit();
}

private void DisposeServices()
{
if (_disposed)
{
return;
}

ClosePreview();
_keyboardHook.Dispose();

if (_notifyIcon is not null)
{
_notifyIcon.Dispose();
_notifyIcon = null;
}

if (_mutexAcquired)
{
_mutex.ReleaseMutex();
_mutexAcquired = false;
}

_mutex.Dispose();
_disposed = true;
}
}
6 changes: 6 additions & 0 deletions src/WinQuickLook.App.WinUI/AppParameters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace WinQuickLook.App.WinUI;

public static class AppParameters
{
public const string Title = "WinQuickLook";
}
Loading