Compare commits

...

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
f3e6bc207a Add architecture documentation for PDF fullscreen toggle fix
Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>
2026-02-05 15:31:56 +00:00
copilot-swe-agent[bot]
d2bdda9554 Add testing documentation for PDF fullscreen toggle fix
Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>
2026-02-05 15:31:13 +00:00
copilot-swe-agent[bot]
3a77770aad Add fullscreen toggle support for PDF viewer in Peek
- Handle ContainsFullScreenElementChanged event in BrowserControl
- Propagate fullscreen state changes through FilePreview to MainWindow
- Toggle window presenter between FullScreen and Default modes
- Fixes issue where PDF fullscreen button didn't allow toggling back

Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>
2026-02-05 15:30:13 +00:00
copilot-swe-agent[bot]
01920d371d Initial plan 2026-02-05 15:17:07 +00:00
7 changed files with 272 additions and 0 deletions

126
ARCHITECTURE_NOTES.md Normal file
View File

@@ -0,0 +1,126 @@
# PDF Fullscreen Toggle Fix - Architecture
## Event Flow Diagram
```
User clicks PDF fullscreen button
WebView2 (CoreWebView2)
ContainsFullScreenElement = true/false
ContainsFullScreenElementChanged event fires
┌─────────────────────────────────────────────────┐
│ BrowserControl.xaml.cs │
│ CoreWebView2_ContainsFullScreenElementChanged │
│ → FullScreenChanged?.Invoke(isFullScreen) │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ FilePreview.xaml.cs │
│ BrowserPreview_FullScreenChanged │
│ → FullScreenChanged?.Invoke(this, ...) │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ MainWindow.xaml.cs │
│ FilePreviewer_FullScreenChanged │
│ → AppWindow.SetPresenter(FullScreen/Default) │
└─────────────────────────────────────────────────┘
Window toggles
FullScreen ↔ Windowed mode
```
## Component Responsibilities
### BrowserControl
- **Role**: WebView2 wrapper, direct interface with CoreWebView2
- **Responsibility**: Listen to WebView2 events, expose them as custom events
- **Key Methods**:
- `CoreWebView2_ContainsFullScreenElementChanged()` - Event handler
- Raises `FullScreenChanged` event with boolean parameter
### FilePreview
- **Role**: Preview coordinator, manages different preview types
- **Responsibility**: Route events from preview controls to main window
- **Key Methods**:
- `BrowserPreview_FullScreenChanged()` - Event handler
- Propagates `FullScreenChanged` event upward
### MainWindow
- **Role**: Application window, manages window-level behavior
- **Responsibility**: Handle window presentation mode changes
- **Key Methods**:
- `FilePreviewer_FullScreenChanged()` - Event handler
- Calls `AppWindow.SetPresenter()` to change window mode
## Key APIs Used
### CoreWebView2.ContainsFullScreenElementChanged
- **Type**: Event
- **When**: Fires when PDF viewer's fullscreen button is clicked
- **Property**: `CoreWebView2.ContainsFullScreenElement` (bool)
- **Documentation**: Part of Microsoft.Web.WebView2.Core namespace
### AppWindow.SetPresenter()
- **Type**: Method
- **Parameter**: `AppWindowPresenterKind` enum
- **Values**:
- `AppWindowPresenterKind.FullScreen` - True fullscreen, hides titlebar
- `AppWindowPresenterKind.Default` - Normal overlapped window
- **Documentation**: Part of Microsoft.UI.Windowing namespace (WinUI 3)
## Design Decisions
### Why Event Propagation?
Instead of directly accessing MainWindow from BrowserControl:
1. **Separation of Concerns**: BrowserControl shouldn't know about MainWindow
2. **Reusability**: BrowserControl can be used in other contexts
3. **Testability**: Each component can be tested independently
4. **Maintainability**: Clear data flow, easy to debug
### Why AppWindow.SetPresenter?
Alternative approaches considered:
1. ~~Manual hiding of titlebar~~ - Complex, doesn't handle all edge cases
2. ~~Maximize window~~ - Not true fullscreen, taskbar still visible
3. **AppWindow.SetPresenter** ✓ - Native WinUI 3 API, handles everything correctly
### Why Not Just Hide TitleBar?
Setting `ExtendsContentIntoTitleBar = false` and hiding the titlebar isn't sufficient:
- Window still has borders
- Taskbar remains visible
- Window doesn't cover entire screen
- Need to manually restore all settings when exiting
`AppWindowPresenterKind.FullScreen` handles all of this automatically.
## Edge Cases Handled
1. **Rapid toggling**: Events are properly subscribed/unsubscribed
2. **Navigation between files**: Each file gets its own fullscreen state
3. **Escape key**: Existing behavior preserved (closes Peek)
4. **Non-PDF files**: Event only fires for PDF viewer, others unaffected
5. **Dispose/cleanup**: Properly unsubscribe from events to prevent leaks
## Future Enhancements
Potential improvements for consideration:
1. **Remember fullscreen preference**: Store user's last fullscreen state
2. **Keyboard shortcut**: Add F11 or similar to toggle fullscreen
3. **Context menu option**: Right-click menu item for fullscreen
4. **Status indicator**: Show fullscreen status in UI
5. **Per-file-type settings**: Remember fullscreen preference per file type
## Related Issues
This fix addresses:
- [Peek] How do I un-fullscreen a PDF? (Main issue)
- Any future WebView2 fullscreen requirements in PowerToys
- Pattern for handling WebView2 element state changes
## References
- WebView2 API: https://learn.microsoft.com/en-us/microsoft-edge/webview2/
- WinUI 3 AppWindow: https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.windowing.appwindow
- PowerToys Peek: https://github.com/microsoft/PowerToys/tree/main/src/modules/peek

105
TESTING_NOTES.md Normal file
View File

@@ -0,0 +1,105 @@
# Testing Notes for PDF Fullscreen Toggle Fix
## Issue
[Peek] How do I un-fullscreen a PDF? (#Issue number to be filled)
## Summary of Changes
Fixed the PDF fullscreen toggle functionality in Peek. When viewing PDFs in Peek, clicking the fullscreen button now properly toggles between fullscreen and windowed modes.
## Files Changed
1. `src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs`
- Added `FullScreenChangedHandler` delegate and `FullScreenChanged` event
- Subscribed to `CoreWebView2.ContainsFullScreenElementChanged` event
- Added event handler `CoreWebView2_ContainsFullScreenElementChanged`
2. `src/modules/peek/Peek.FilePreviewer/FilePreview.xaml`
- Wired up `FullScreenChanged` event handler to BrowserControl
3. `src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs`
- Added `FullScreenChanged` event
- Added `BrowserPreview_FullScreenChanged` event handler to propagate state
4. `src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml`
- Wired up `FullScreenChanged` event handler to FilePreview
5. `src/modules/peek/Peek.UI/PeekXAML/MainWindow.xaml.cs`
- Added `FilePreviewer_FullScreenChanged` event handler
- Toggles between `AppWindowPresenterKind.FullScreen` and `AppWindowPresenterKind.Default`
## Manual Testing Steps
### Prerequisites
1. Build PowerToys with the changes
2. Have a PDF file ready for testing
### Test Case 1: Enter and Exit Fullscreen
1. Open File Explorer
2. Select a PDF file
3. Press `Ctrl+Space` to open Peek
4. Verify PDF is displayed correctly
5. **Click the fullscreen button in the PDF viewer** (usually in bottom-right corner)
6. **EXPECTED**: Window enters fullscreen mode, titlebar disappears
7. **Click the fullscreen button again**
8. **EXPECTED**: Window exits fullscreen mode, titlebar reappears
### Test Case 2: Escape Key Still Works
1. Open a PDF in Peek
2. Click the fullscreen button to enter fullscreen
3. Press `Escape` key
4. **EXPECTED**: Window exits fullscreen AND Peek closes (existing behavior)
### Test Case 3: Multiple Toggle Cycles
1. Open a PDF in Peek
2. Click fullscreen button (enter fullscreen)
3. Click fullscreen button (exit fullscreen)
4. Click fullscreen button (enter fullscreen again)
5. Click fullscreen button (exit fullscreen again)
6. **EXPECTED**: All toggles work correctly without any stuck states
### Test Case 4: Navigation Between Files
1. Select multiple PDF files in File Explorer
2. Open Peek with `Ctrl+Space`
3. Enter fullscreen mode
4. Use arrow keys to navigate to next/previous PDF
5. **EXPECTED**: Fullscreen state persists across file navigation
### Test Case 5: Non-PDF Files
1. Open a non-PDF file (e.g., image, text file) in Peek
2. **EXPECTED**: No fullscreen button appears, behavior unchanged
## Expected Behavior
### Before Fix
- Clicking fullscreen button hides titlebar but doesn't properly put window in fullscreen
- Clicking fullscreen button again does nothing (stuck in pseudo-fullscreen)
- Only way to exit is pressing Escape (which closes Peek entirely)
### After Fix
- Clicking fullscreen button properly enters fullscreen mode
- Titlebar automatically hides
- Window fills entire screen
- Clicking fullscreen button again exits fullscreen
- Titlebar reappears
- Window returns to previous size/position
## Technical Details
The fix leverages WebView2's `ContainsFullScreenElementChanged` event which fires when:
1. User clicks the PDF viewer's fullscreen button (enters fullscreen)
2. User clicks the fullscreen button again (exits fullscreen)
The event chain is:
1. `CoreWebView2.ContainsFullScreenElementChanged` fires in BrowserControl
2. BrowserControl raises `FullScreenChanged` event
3. FilePreview propagates the event to MainWindow
4. MainWindow calls `AppWindow.SetPresenter()` with appropriate presenter kind
## Verification
After testing, verify:
- [ ] PDF fullscreen button enters fullscreen mode
- [ ] PDF fullscreen button exits fullscreen mode (toggle works)
- [ ] Titlebar visibility toggles correctly
- [ ] Window size changes correctly
- [ ] Escape key still closes Peek when in fullscreen
- [ ] Navigation between PDFs maintains fullscreen state
- [ ] No regressions with other file types

View File

@@ -43,10 +43,14 @@ namespace Peek.FilePreviewer.Controls
public delegate void DOMContentLoadedHandler(CoreWebView2? sender, CoreWebView2DOMContentLoadedEventArgs? args);
public delegate void FullScreenChangedHandler(bool isFullScreen);
public event NavigationCompletedHandler? NavigationCompleted;
public event DOMContentLoadedHandler? DOMContentLoaded;
public event FullScreenChangedHandler? FullScreenChanged;
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source),
typeof(Uri),
@@ -111,6 +115,7 @@ namespace Peek.FilePreviewer.Controls
{
PreviewBrowser.CoreWebView2.DOMContentLoaded -= CoreWebView2_DOMContentLoaded;
PreviewBrowser.CoreWebView2.ContextMenuRequested -= CoreWebView2_ContextMenuRequested;
PreviewBrowser.CoreWebView2.ContainsFullScreenElementChanged -= CoreWebView2_ContainsFullScreenElementChanged;
RemoveResourceFilter();
}
}
@@ -211,6 +216,7 @@ namespace Peek.FilePreviewer.Controls
PreviewBrowser.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
PreviewBrowser.CoreWebView2.NewWindowRequested += CoreWebView2_NewWindowRequested;
PreviewBrowser.CoreWebView2.ContextMenuRequested += CoreWebView2_ContextMenuRequested;
PreviewBrowser.CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
}
catch (Exception ex)
{
@@ -438,5 +444,11 @@ namespace Peek.FilePreviewer.Controls
dataPackage.SetText(OpenUriDialogContent.Text);
Clipboard.SetContent(dataPackage);
}
private void CoreWebView2_ContainsFullScreenElementChanged(CoreWebView2 sender, object args)
{
// Notify listeners about fullscreen state change
FullScreenChanged?.Invoke(sender.ContainsFullScreenElement);
}
}
}

View File

@@ -87,6 +87,7 @@
CustomContextMenu="{x:Bind BrowserPreviewer.CustomContextMenu, Mode=OneWay}"
DOMContentLoaded="BrowserPreview_DOMContentLoaded"
FlowDirection="LeftToRight"
FullScreenChanged="BrowserPreview_FullScreenChanged"
IsDevFilePreview="{x:Bind BrowserPreviewer.IsDevFilePreview, Mode=OneWay}"
NavigationCompleted="PreviewBrowser_NavigationCompleted"
Source="{x:Bind BrowserPreviewer.Preview, Mode=OneWay}"

View File

@@ -34,6 +34,8 @@ namespace Peek.FilePreviewer
public event EventHandler<PreviewSizeChangedArgs>? PreviewSizeChanged;
public event EventHandler<bool>? FullScreenChanged;
public static readonly DependencyProperty ItemProperty =
DependencyProperty.Register(
nameof(Item),
@@ -318,6 +320,12 @@ namespace Peek.FilePreviewer
}
}
private void BrowserPreview_FullScreenChanged(bool isFullScreen)
{
// Propagate fullscreen state change to parent (MainWindow)
FullScreenChanged?.Invoke(this, isFullScreen);
}
private void ShellPreviewHandlerPreview_HandlerLoaded(object sender, EventArgs e)
{
if (ShellPreviewHandlerPreviewer != null)

View File

@@ -47,6 +47,7 @@
<fp:FilePreview
Grid.Row="1"
FullScreenChanged="FilePreviewer_FullScreenChanged"
Item="{x:Bind ViewModel.CurrentItem, Mode=OneWay}"
NumberOfFiles="{x:Bind ViewModel.DisplayItemCount, Mode=OneWay}"
PreviewSizeChanged="FilePreviewer_PreviewSizeChanged"

View File

@@ -265,6 +265,25 @@ namespace Peek.UI
WindowHelpers.BringToForeground(this.GetWindowHandle());
}
/// <summary>
/// Handle FullScreenChanged event to toggle the window fullscreen mode.
/// </summary>
/// <param name="sender">object</param>
/// <param name="isFullScreen">bool indicating if fullscreen is requested</param>
private void FilePreviewer_FullScreenChanged(object sender, bool isFullScreen)
{
if (isFullScreen)
{
// Enter fullscreen mode
AppWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
}
else
{
// Exit fullscreen mode and return to default overlapped window
AppWindow.SetPresenter(AppWindowPresenterKind.Default);
}
}
private Size GetMonitorMaxContentSize(Size monitorSize, double scaling)
{
var titleBarHeight = TitleBarControl.ActualHeight;