Skip to content

feat: per-screen app toggling for multi-monitor setups#378

Open
ninjaeon wants to merge 6 commits into
flyingpie:masterfrom
ninjaeon:feature/per-screen-apps
Open

feat: per-screen app toggling for multi-monitor setups#378
ninjaeon wants to merge 6 commits into
flyingpie:masterfrom
ninjaeon:feature/per-screen-apps

Conversation

@ninjaeon

Copy link
Copy Markdown

Summary

Allow up to one WTQ app open per screen simultaneously. When activating a WTQ app on a screen, only close apps on that same screen — apps on different screens remain open.

Previously, toggling any app would close whatever app was currently open globally, regardless of which screen it was on. This made multi-monitor workflows frustrating: toggling a terminal on your secondary monitor would dismiss the terminal on your primary monitor.

Changes

Core Logic (WtqService.cs)

  • OnAppToggledEventAsync now checks whether PerScreenApps is enabled
  • When enabled, calls GetOpenOnScreen(screenRect) instead of GetOpen(), so only apps on the same screen are closed before opening the new one
  • Falls back to the original single-app-global behavior when the feature flag is off

Screen Tracking

  • WtqApp.CurrentScreenRect — set to the screen rectangle when the app opens, cleared when it closes
  • WtqAppToggledEvent.ScreenRect — carried from the hotkey handler (gets screen with cursor) through to the service layer

Repository

  • IWtqAppRepo.GetAllOpen() / GetOpenOnScreen(Rectangle) — new methods on the repo interface and implementation
  • Screen matching uses Rectangle.IntersectsWith to compare screen rects

Feature Flag

  • FeatureFlags.PerScreenApps — defaults to true (new behavior is opt-out)
  • Toggle is in the Monitor section of Global Settings GUI

Tests

  • WtqAppRepoPerScreenTest — unit tests for per-screen open/close behavior

Files Changed (8 files)

File Change
FeatureFlags.cs Add PerScreenApps flag (default true)
WtqApp.cs Add CurrentScreenRect property
IWtqAppRepo.cs Add GetAllOpen() and GetOpenOnScreen()
WtqAppRepo.cs Implement screen-matching logic
WtqService.cs Refactor OnAppToggledEventAsync for per-screen logic
WtqAppToggledEvent.cs Add ScreenRect property
WtqHotkeyService.cs Pass screen rect to toggle event
_Index.razor (Global) Add PerScreenApps checkbox in Monitor section

Testing

  • 189/190 unit tests pass (1 pre-existing skip)
  • Manual testing on dual-monitor setup: toggling on screen 2 leaves screen 1 apps open and vice versa

ninjaeon and others added 5 commits April 20, 2026 13:28
Add per-screen app management to allow up to one WTQ app open per screen
simultaneously. When toggling an app on one screen, apps on other screens
remain open instead of being automatically closed.

Key changes:
- WtqApp: Add CurrentScreenRect property to track which screen an app
  is displayed on (set on toggle-on, cleared on toggle-off)
- IWtqAppRepo/WtqAppRepo: Add GetAllOpen() and GetOpenOnScreen(Rectangle)
  methods for per-screen app queries
- WtqService: Refactor OnAppToggledEventAsync to use screen-aware logic.
  In per-screen mode (default), toggling an app only closes other apps
  on the same screen. Apps on different monitors stay open.
- FeatureFlags: Add PerScreenApps flag (default: true) to allow opting
  back into the legacy single-app-global behavior
- Unit tests for screen intersection logic and feature flag defaults

The legacy behavior (always close the previous app regardless of screen)
is preserved when PerScreenApps is set to false in the feature flags.

Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Add a checkbox for the PerScreenApps feature flag in the Behavior
section of Global Settings, so users can toggle the per-screen app
behavior from the WTQ GUI without editing wtq.jsonc manually.

Also adds [Display] and [DefaultValue] attributes to both feature
flag properties (PerScreenApps and SharpHook) for proper label
rendering in the settings UI.

Co-Authored-By: Craft Agent <agents-noreply@craft.do>
When the user's wtq.jsonc doesn't contain a FeatureFlags section,
the FeatureFlags property on WtqOptions is null. The PerScreenApps
checkbox was using the null-forgiving operator (!), which crashes
Blazor's render circuit at runtime and breaks all page navigation.

Fix by initializing FeatureFlags to a default instance in Reload()
if it's null.

Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Move the PerScreenApps checkbox from the Behavior fieldset to the
Monitor fieldset where it logically belongs alongside Prefer monitor
and Monitor index.

Fix awkward line breaks in the PerScreenApps XML doc comment that
split sentences mid-phrase in the rendered UI description.

Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Copilot AI review requested due to automatic review settings April 23, 2026 07:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds per-monitor (“per-screen”) app toggling so opening an app only closes other WTQ apps on the same screen, improving multi-monitor workflows while keeping a legacy global-single-app fallback via a feature flag.

Changes:

  • Introduces FeatureFlags.PerScreenApps (default true) and exposes it in Global Settings UI.
  • Tracks per-app screen placement via WtqApp.CurrentScreenRect and adds repo querying for open apps by screen (GetOpenOnScreen, GetAllOpen).
  • Refactors WtqService.OnAppToggledEventAsync to close apps per-screen when the flag is enabled, and adds unit tests around the rectangle intersection assumptions.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/20-Services/Wtq.Services.UI/Pages/GlobalSettings/_Index.razor Initializes FeatureFlags if missing and adds a UI toggle for per-screen apps.
src/10-Core/Wtq/WtqService.cs Implements per-screen toggle behavior behind PerScreenApps flag and adds new dependencies.
src/10-Core/Wtq/WtqApp.cs Tracks the screen rectangle where the app is currently open via CurrentScreenRect.
src/10-Core/Wtq/Services/WtqAppRepo.cs Adds methods to query all open apps and the open app on a given screen.
src/10-Core/Wtq/Services/IWtqAppRepo.cs Extends repo interface with GetAllOpen() and GetOpenOnScreen(Rectangle).
src/10-Core/Wtq/Configuration/WtqSharedOptions.cs Minor docstring formatting change.
src/10-Core/Wtq/Configuration/FeatureFlags.cs Adds PerScreenApps feature flag (default enabled) and display metadata.
src/10-Core/Wtq.UnitTest/Services/WtqAppRepoPerScreenTest.cs Adds tests asserting Rectangle.IntersectsWith semantics and default flag value.

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

Comment thread src/10-Core/Wtq/WtqService.cs Outdated
Comment thread src/10-Core/Wtq/WtqService.cs Outdated
Comment thread src/10-Core/Wtq.UnitTest/Services/WtqAppRepoPerScreenTest.cs Outdated
1. Use IWtqTargetScreenRectProvider instead of cursor position for per-screen toggle resolution. This respects the app's PreferMonitor setting (Primary, AtIndex, WithCursor) so the 'same screen' determination matches where the app will actually open.

2. Change GetOpenOnScreen to return IEnumerable<WtqApp> instead of WtqApp? and close all matching apps on the same screen. This handles edge cases where multiple apps have stale or missing CurrentScreenRect data.

3. Improve tests: add predicate-level tests for multi-app per-screen behavior and empty-result handling. Clarify that tests validate the intersection assumptions the repo method relies on.
@flyingpie

Copy link
Copy Markdown
Owner

@ninjaeon Thank you for sending the PR!

I do agree with the feature in general (giving each monitor their own independent "scope"), and I feel that there's also a connection to the other PR, about having different app settings for different monitors.

Though, I'd like to think a bit about the implementation, as it's too duct-taped on imho, and I think we can do better, making the concept a proper first class citizen.

I'll get back to this soon.

@flyingpie

Copy link
Copy Markdown
Owner

Some initial work that touches this feature has been done here, where multiple apps can be active at the same time:

#380

The next step is to have an additional setting value for "Exclusive", such as "WithinMonitor" or something.

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.

3 participants