Files
LlamaCasty/ytLive.Tests/VerticalChatCameraLayoutTests.cs
T
gramps 3a77662a95 vv webcam Unit 2: the Chat scene in the vertical tier - rect only, tile or one whole 607x540 area
The creator's locked table (2026-10-17) gives the Chat scene its own rules in
the 9:16 tier: RECT ONLY - the circle option is not offered - and a size that
is either the normal small tile or ENLARGED to fill one whole 607x540 area
(top or bottom). Unit 1 shipped the per-scene placement; this unit is the Chat
scene's own variant of it.

The rule lives on the MODEL, not on the button:
- Models/SceneElement.cs: ClipShape and ToggleClipShape() are now virtual.
- Models/WebcamSceneConfig.cs: VerticalShapeLocked (set per scene by the VM)
  OVERRIDES the ClipShape property, so no writer can slip a circle in - the
  toggle, the Shape button and the placement swap all go through it. The lock
  is cleared BEFORE the 16:9 layout comes back, so the desktop shape returns.
- ViewModels/MainViewModel.Webcam.cs: CanToggleClipShape (the circle is NOT
  offered in the vertical Chat layout - the Shape button disappears, it is not
  merely greyed out), IsChatSceneCamera, ResizeWebcamInVerticalTier.
- Models/VerticalCameraLayout.cs: FillsArea (the "enlarged" state, derived -
  no new column) and SizeFor - the Chat camera's TWO sizes, the tile or the
  whole area, whichever is nearer the requested width, so the resize handle
  has two detents instead of any size in between.
- An enlarged camera FILLS the area its centre is in (top or bottom), so
  dragging it up/down swaps the areas.
- The Chat camera is auto-tiled like the Live one when the vertical tier is
  picked - otherwise the vertical chat stream has no camera at all.
- Controls/PreviewPane.xaml.cs: the resize routes through the VM; the drag
  assigns, lets the model clamp into the band, then snaps (so the enlarged
  camera can be dragged from the top area to the bottom one).

Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs - the ONE Good Dog test for
the unit: the vertical tier is picked -> the Chat camera comes into the band as
a small RECT tile -> the circle option is not offered and cannot be set even on
the model -> dragged up and enlarged it fills the TOP area exactly -> back to
16:9 the desktop layout is restored exactly and the circle option is offered
again. 430/430 tests pass, 0 warnings.

Research (the creator's table is the established practice, not our invention):
- Streamlabs' vertical guide: "splitting the screen in half horizontally,
  placing the camera feed in the top half and the main content on the bottom
  (or vice versa) may be the bet", and a just-chatting vertical wants the
  camera "to take up most of the screen"
  (streamlabs.com/content-hub/post/overlays-vertical-and-horizontal-streaming)
- Bloopbot's vertical overlay docs describe the same per-layer model we use at
  scene granularity: "move or resize a layer on Vertical and it stops being
  Auto-placed. It keeps the place you gave it, and the horizontal layout is not
  touched" (bloopbot.com/docs/vertical-overlays-twitch-dual-format) - our
  swap-at-tier-change, stored per scene.
- Landmine recorded: "about the bottom 560 px of the vertical video can be
  covered by Twitch's chat and player controls on phones" (Bloopbot) - the Chat
  camera's enlarged BOTTOM area may be half-hidden by the platform chrome;
  TASK 53's safe-zone work decides that.

Memory in the same commit: ai.md (the Chat rules, the shape lock, the tests),
TASKS.md + TASKS/task-57-vertical-camera-layout.md (TASK 57 complete), HANDOFF.md,
MyMistakes.md (3 recipes: lock the model property not the UI button; two detents
for a discrete size set; the edit tool cannot match an oldString with an em
dash), Models/ViewModels/Controls index rows.
2026-10-21 19:23:29 -07:00

115 lines
4.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IO;
using System.Linq;
using Microsoft.Data.Sqlite;
using Xunit;
using ytLive.Models;
using ytLive.ViewModels;
namespace ytLive.Tests;
/// <summary>
/// TASK 57, the "vv" feature, Unit 2 — the vertical (9:16) tier's CHAT scene
/// camera.
///
/// Unit 1 gave every scene its own vertical placement. Unit 2 is the Chat
/// scene's own rules (the creator's table, 2026-10-17): in the vertical tier
/// the Chat camera is **rect only — the circle option is not offered** — and
/// its size is either the normal small tile or **enlarged to fill one whole
/// 607×540 area** (the top one or the bottom one, by where the camera is).
///
/// One test, the whole story (the Good Dog rule): the vertical tier is picked →
/// the Chat camera comes into the band as a small RECT tile → the circle option
/// is not offered and cannot be set even by the model → the camera is dragged up
/// and enlarged, and it fills the TOP area exactly → switching back to 16:9
/// restores the desktop layout exactly (and the circle option is offered again).
/// </summary>
[Collection("RealApp")]
public sealed class VerticalChatCameraLayoutTests
{
private readonly RealAppHost _app;
public VerticalChatCameraLayoutTests(RealAppHost app) => _app = app;
[Fact]
public void Vertical_Tier_Chat_Camera_Is_Rect_Only_And_Enlarges_To_One_Area()
{
_app.Run(Run);
}
private void Run()
{
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-vv-chat-{Guid.NewGuid():N}.db");
MainViewModel.LayoutPathOverride = tempDb;
try
{
var vm = new MainViewModel();
var chat = vm.Scenes.First(s => s.Name == "Chat");
var camera = new WebcamSceneConfig
{
WebcamId = "webcam-1",
Name = "Webcam",
X = 120,
Y = 120,
Width = 640,
Height = 360,
};
chat.Elements.Add(camera);
// The 16:9 placement: the camera sits where the desktop layout put it.
Assert.Equal(120, camera.X);
Assert.Equal(120, camera.Y);
Assert.Equal(640, camera.Width);
// ── The vertical tier is picked ───────────────────────────────────
vm.SelectedQuality = vm.QualityOptions[^1];
Assert.True(vm.IsVerticalTier);
var band = vm.VerticalBand;
// The Chat camera came INTO the band as a small tile...
Assert.Equal(VerticalCameraLayout.TileWidth, camera.Width);
Assert.Equal(VerticalCameraLayout.TileHeight, camera.Height);
Assert.True(camera.X >= band.Left, $"camera X {camera.X} is left of the band {band.Left}");
Assert.True(camera.X + camera.Width <= band.Right, "the camera hangs over the band's right edge");
Assert.True(camera.Y >= band.Top, "the camera hangs over the band's top edge");
Assert.True(camera.Y + camera.Height <= band.Bottom, "the camera hangs over the band's bottom edge");
// ── The circle option is not offered ─────────────────────────────
vm.SelectedElement = camera;
Assert.False(vm.CanToggleClipShape);
Assert.Equal(ClipShape.Traditional, camera.ClipShape);
// ...and it cannot be slipped in behind the UI's back either.
camera.ToggleClipShape();
Assert.Equal(ClipShape.Traditional, camera.ClipShape);
// ── Drag it up, then enlarge it: it fills the TOP area ──────────
vm.MoveWebcamInVerticalTier(camera, band.Left + 40, band.Top + 40);
vm.ResizeWebcamInVerticalTier(camera, band.Width, band.Height);
Assert.Equal(band.Left, camera.X, 3);
Assert.Equal(band.Top, camera.Y, 3);
Assert.Equal(band.Width, camera.Width, 3);
Assert.Equal(band.Height / 2, camera.Height, 3);
// ── Back to 16:9: the desktop layout is restored exactly ─────────
vm.SelectedQuality = vm.QualityOptions[0];
Assert.False(vm.IsVerticalTier);
Assert.Equal(120, camera.X);
Assert.Equal(120, camera.Y);
Assert.Equal(640, camera.Width);
Assert.Equal(360, camera.Height);
// The circle option is offered again outside the vertical tier.
Assert.True(vm.CanToggleClipShape);
}
finally
{
MainViewModel.LayoutPathOverride = null;
SqliteConnection.ClearAllPools();
try { File.Delete(tempDb); } catch { /* best-effort cleanup */ }
}
}
}