Skip to content

Better UI , better performance and added split screen feature#34

Open
anirudhqwerty wants to merge 15 commits intoawaescher:mainfrom
anirudhqwerty:main
Open

Better UI , better performance and added split screen feature#34
anirudhqwerty wants to merge 15 commits intoawaescher:mainfrom
anirudhqwerty:main

Conversation

@anirudhqwerty
Copy link
Copy Markdown

No description provided.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR overhauls the StageManager Windows app with UI improvements, performance optimizations, and a new split-screen feature. It replaces the previous window show/hide strategy pattern with direct BringToTop()/Focus() calls, introduces thread-safe concurrency primitives (SemaphoreSlim, ConcurrentDictionary, locks), modernizes the UI with Mica backdrop and animations, and adds Ctrl+Click split-screen support via a new WindowLayoutManager.

Changes:

  • Added split-screen layout feature (ToggleSplit, WindowLayoutManager.SplitScreen) allowing multiple scenes to be tiled side-by-side via Ctrl+Click
  • Replaced Dictionary with ConcurrentDictionary, added SemaphoreSlim transition locks and _stateLock for thread safety across SceneManager and WindowsManager
  • Modernized UI with Mica backdrop, animated scene cards, rounded thumbnails, and replaced the overlap-check timer with a simpler mouse-position-based sidebar flyover

Reviewed changes

Copilot reviewed 40 out of 51 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
StageManager/SceneManager.cs Core rewrite: thread-safe scene management, split-screen support, changed grouping key from process name to window handle
StageManager/MainWindow.xaml.cs Sidebar flyover logic, Mica backdrop, Ctrl+Click split, replaced timer with mouse-based visibility
StageManager/MainWindow.xaml Complete UI redesign with animated cards, shadows, rounded corners, ScrollViewer
StageManager/Native/WindowLayoutManager.cs New file: DPI-aware split-screen layout computation and atomic window positioning
StageManager/Native/WindowsManager.cs Switched to ConcurrentDictionary, proper hook cleanup, removed mouse hook thread
StageManager/Native/WindowsWindow.cs Icon caching, UWP process resolution, expanded ignore lists, nullable annotations
StageManager/Strategies/ScreenOffsetWindowStrategy.cs Partial implementation: Show works, Hide is empty
StageManager/Strategies/NormalizeAndMinimizeWindowStrategy.cs Show changed to BringToTop, Hide emptied
StageManager/DwmThumbnail.xaml.cs Thumbnail cleanup on Unload, handle re-registration, DPI rounding fixes
StageManager/Desktop.cs Registry key caching, icon visibility cache
StageManager/Native/Interop/NativeMethods.cs Added MonitorFromWindow, DwmSetWindowAttribute, Win32Size struct
StageManager/AutoStart.cs Fixed registry key disposal with using
StageManager/StageManager.csproj Added CA1416 suppression, Microsoft.Xaml.Behaviors.Wpf package
ReadMe.md Documented split-screen, marked multi-monitor as complete
.github/workflows/dotnet-desktop.yml Updated to .NET 8, actions v4, conditional release steps
.dotnet_home/** Accidentally committed .NET telemetry/cache files
Other files Minor cleanups: nullable annotations, comment removal, formatting

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 29 to 31
public void Hide(IWindow window)
{
throw new System.NotImplementedException();
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both ScreenOffsetWindowStrategy.Hide and NormalizeAndMinimizeWindowStrategy.Hide have empty bodies — the implementation was removed but no replacement was added. The Hide method previously called Win32.ShowWindow(window.Handle, Win32.SW.SW_MINIMIZE) for the normalize strategy and should have stored the position and moved the window off-screen for the offset strategy (using the declared OFF_SCREEN_OFFSET constant and _originalPositions dictionary). With empty Hide methods, the WindowStrategy pattern is effectively broken — Show does something but Hide is a no-op. While it appears that WindowStrategy is no longer called from SceneManager, this makes both strategy classes dead/incomplete code.

Copilot uses AI. Check for mistakes.
public event EventHandler<SceneChangedEventArgs>? SceneChanged;
public event EventHandler<CurrentSceneSelectionChangedEventArgs>? CurrentSceneSelectionChanged;

private IWindowStrategy WindowStrategy { get; } = new NormalizeAndMinimizeWindowStrategy();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WindowStrategy is declared and initialized but never used anywhere in the new SceneManager code. The old code called WindowStrategy.Show(w) and WindowStrategy.Hide(o) during scene switching, but those calls have been removed and replaced with direct BringToTop()/Focus() calls. This makes the WindowStrategy property and the entire IWindowStrategy abstraction dead code. Either remove the unused property or restore the strategy-based show/hide pattern if window hiding is still desired.

Copilot uses AI. Check for mistakes.
{
var existentScene = FindSceneForProcess(GetWindowGroupKey(window));
var scene = existentScene ?? new Scene(window.ProcessName, window);
if (object.Equals(scene, _current)) return;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SwitchTo reads _current (line 179) outside of any lock before acquiring _transitionLock. This creates a race condition: another thread could change _current between the check on line 179 and the lock acquisition on line 181, causing the method to either skip a needed transition or proceed with stale data. The equality check should be moved inside the locked region.

Copilot uses AI. Check for mistakes.

// Cache physical screen dimensions once at startup
var primaryScreen = System.Windows.Forms.Screen.PrimaryScreen;
_cachedPhysicalScreenHeight = primaryScreen.Bounds.Height;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.Windows.Forms.Screen.PrimaryScreen can return null (it's annotated as nullable in .NET 6+). Accessing .Bounds.Height directly will throw a NullReferenceException if no primary screen is detected. Add a null check or use a fallback value.

Suggested change
_cachedPhysicalScreenHeight = primaryScreen.Bounds.Height;
_cachedPhysicalScreenHeight = primaryScreen?.Bounds.Height ?? (int)SystemParameters.PrimaryScreenHeight;

Copilot uses AI. Check for mistakes.
Comment thread ReadMe.md
|**Product stage**||
|virtual desktop support (pin window)|⬜|
|multi-monitor support||
|multi-monitor support||
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ReadMe claims "multi-monitor support" is now complete (✅), but the implementation has some single-monitor assumptions. In MainWindow.xaml.cs, _cachedPhysicalScreenHeight is cached from PrimaryScreen at startup and never updated, meaning the flyover trigger zone (lines 349-350) will use the wrong height on non-primary monitors. The WindowLayoutManager.SplitScreen does correctly use MonitorFromWindow per window, but the sidebar activation logic is primary-monitor-only.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1 @@
61e865d1-0113-41a6-8217-422fb0204ee1 No newline at end of file
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .dotnet_home/ directory containing .NET telemetry data, machine IDs, and sentinel files has been committed to the repository. These files contain local build environment artifacts (telemetry payloads, machine identifiers) that should not be tracked in version control. They should be removed from the PR and added to .gitignore.

Suggested change
61e865d1-0113-41a6-8217-422fb0204ee1
# MachineId cache intentionally cleared; do not commit user-level cache data.

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +174
private void WindowsManager_WindowCreated(IWindow window, bool firstCreate)
{
var scene = FindSceneForWindow(window);
if (scene is null)
Scene scene;
lock (_stateLock)
{
scene = new Scene(GetWindowGroupKey(window), window);
_scenes.Add(scene);
SceneChanged?.Invoke(this, new SceneChangedEventArgs(scene, window, ChangeType.Created));
}

await SwitchTo(scene);
RaiseOnCapturedContext(() =>
SceneChanged?.Invoke(this, new SceneChangedEventArgs(scene, window, ChangeType.Created)));

SwitchTo(scene).SafeFireAndForget();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WindowsManager_WindowCreated handler always creates a new Scene for every window, using window.Handle.ToString() as the group key (line 391). This means every window gets its own scene, and new windows from an already-tracked process are never added to an existing scene. The previous code used FindSceneForProcess(GetWindowGroupKey(window)) to group windows of the same process into the same scene. This is a significant behavioral change — all windows will now appear as separate scenes in the sidebar, which seems inconsistent with the purpose of a "stage manager" that groups related windows.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +10
using System.Windows.Forms;

namespace StageManager.Strategies
{
internal class ScreenOffsetWindowStrategy : IWindowStrategy
{
private const int OFF_SCREEN_OFFSET = 32000;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The System.Windows.Forms namespace is imported but unused in this file. Only Win32 and ConcurrentDictionary types are actually used. Additionally, the constant OFF_SCREEN_OFFSET is declared but never referenced.

Copilot uses AI. Check for mistakes.
Dispatcher.BeginInvoke(() => Mode = WindowMode.OffScreen);
}


Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra blank line here (two consecutive blank lines). Minor formatting inconsistency.

Suggested change

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants