Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions app/components-react/editor/elements/SourceSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ class SourceSelectorController {
* clicking on the source selector selects both sources
*/
if (
this.isDualOutputActive &&
this.dualOutputService.views.hasNodeMap(this.scene.id) &&
this.dualOutputService.views.activeDisplays.horizontal &&
this.dualOutputService.views.activeDisplays.vertical
Expand Down Expand Up @@ -504,6 +505,7 @@ class SourceSelectorController {
* in the vertical display, convert the vertical node id to the horizontal node id.
*/
if (
this.isDualOutputActive &&
this.dualOutputService.views.activeDisplays.horizontal &&
this.dualOutputService.views.activeDisplays.vertical
) {
Expand Down
2 changes: 1 addition & 1 deletion app/services/dual-output/dual-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class DualOutputViews extends ViewHandler<IDualOutputServiceState> {
}

get onlyVerticalDisplayActive() {
return this.activeDisplays.vertical && !this.activeDisplays.horizontal;
return this.dualOutputMode && this.activeDisplays.vertical && !this.activeDisplays.horizontal;
}

get platformsDualStreaming() {
Expand Down
4 changes: 1 addition & 3 deletions app/services/scenes/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,7 @@ export class Scene {
getSourceSelectorNodes(): TSceneNode[] {
let nodes = this.getNodes();

const populateWithVerticalNodes =
!this.dualOutputService.views.activeDisplays.horizontal &&
this.dualOutputService.views.activeDisplays.vertical;
const populateWithVerticalNodes = this.dualOutputService.views.onlyVerticalDisplayActive;

nodes = nodes.filter(node => {
// if only the vertical display is active
Expand Down
73 changes: 73 additions & 0 deletions test/regular/api/dual-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,84 @@ import { getApiClient } from '../../helpers/api-client';
import { test, useWebdriver, TExecutionContext } from '../../helpers/webdriver';
import { ScenesService, Scene, SceneItem } from 'services/scenes';
import { VideoSettingsService } from 'services/settings-v2/video';
import { SelectionService } from 'services/api/external-api/selection';
import { click, focusMain, waitForDisplayed } from '../../helpers/modules/core';

// not a react hook
// eslint-disable-next-line react-hooks/rules-of-hooks
useWebdriver();

async function setDualOutputMode(status: boolean) {
const client = await getApiClient();
// The RPC fallback exposes this private method; its loading-mode decorator returns a promise.
const dualOutput = client.getResource<{
setDualOutputMode(status: boolean, skipShowVideoSettings: boolean): Promise<void>;
}>('DualOutputService');

// Exercise the normal mode transition without requiring a provider login.
await dualOutput.setDualOutputMode(status, true);
await focusMain();
}

for (const horizontalVisible of [false, true]) {
Comment thread
aleksandr-voitenko marked this conversation as resolved.
Outdated
test(`Selection after disabling dual output with horizontal ${
horizontalVisible ? 'visible' : 'hidden'
}`, async t => {
const client = await getApiClient();
const scenesService = client.getResource<ScenesService>('ScenesService');
const dualOutputService = client.getResource<DualOutputService>('DualOutputService');
const selection = client.getResource<SelectionService>('SelectionService');
const scene = scenesService.createScene('Selection transition');
scenesService.makeSceneActive(scene.id);
const horizontalItem = scene.createAndAddSource('Selection target', 'color_source');
horizontalItem.fitToScreen();

await setDualOutputMode(true);
dualOutputService.toggleDisplay(horizontalVisible, 'horizontal');
const verticalItem = scene.getItems().find(item => item.display === 'vertical');
t.truthy(verticalItem, 'Dual output created a vertical partner');
t.deepEqual(
scene.getSourceSelectorNodes().map(node => node.id),
[horizontalVisible ? horizontalItem.id : verticalItem.id],
'Dual output source rows follow the visible displays',
);

await setDualOutputMode(false);
await waitForDisplayed('#horizontal-display');
t.deepEqual(
scene.getSourceSelectorNodes().map(node => node.id),
[horizontalItem.id],
'Single output source rows always use horizontal items',
);

await click('[data-name="Selection target"]');
t.deepEqual(selection.getIds(), [horizontalItem.id], 'List selects only the visible item');

selection.reset();
await t.context.app.client.waitUntil(async () => {
const selectedRows = await t.context.app.client.$$(
'.ant-tree-node-selected [data-name="Selection target"]',
);
return selectedRows.length === 0;
});
await click('#horizontal-display');
await waitForDisplayed('.ant-tree-node-selected [data-name="Selection target"]');
t.deepEqual(selection.getIds(), [horizontalItem.id], 'Canvas selection highlights the list');

await setDualOutputMode(true);
t.is(
dualOutputService.state.videoSettings.activeDisplays.horizontal,
horizontalVisible,
'The saved horizontal display preference is preserved',
);
t.deepEqual(
scene.getSourceSelectorNodes().map(node => node.id),
[horizontalVisible ? horizontalItem.id : verticalItem.id],
'Re-enabling dual output restores the corresponding source rows',
);
});
}

function confirmDualOutputSources(t: TExecutionContext, scene: Scene) {
const numSceneItems = scene
.getItems()
Expand Down
Loading