Web source: crop capture to actual content bounds (anchor 0,0); slider click-to-position; web URI X clears; tab order
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
@@ -26,6 +27,10 @@ public sealed class WebView2Manager : IDisposable
|
||||
public required WebView2 Control { get; init; }
|
||||
public required DispatcherTimer CaptureTimer { get; init; }
|
||||
public VideoFrame? LatestFrame;
|
||||
public double ViewportCssW;
|
||||
public double ViewportCssH;
|
||||
public double ContentCssW;
|
||||
public double ContentCssH;
|
||||
public bool Disposed;
|
||||
public bool Initialized;
|
||||
}
|
||||
@@ -127,6 +132,7 @@ public sealed class WebView2Manager : IDisposable
|
||||
"document.documentElement.style.overflow='hidden';" +
|
||||
"document.body.style.background='transparent';" +
|
||||
"document.body.style.overflow='hidden';");
|
||||
_ = QueryContentBoundsAsync(source.Id);
|
||||
_ = CaptureFrame(source.Id);
|
||||
};
|
||||
|
||||
@@ -153,6 +159,35 @@ public sealed class WebView2Manager : IDisposable
|
||||
catch (Exception ex) { AppLog.Write($"WebView2Manager: navigate failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
// The page's real content rect (scrollWidth/Height) can be smaller than the
|
||||
// viewport for widgets that don't fill it — the leftover area is dead space
|
||||
// that must not stretch across the selection box. Crop the capture to the
|
||||
// content bounds (anchored at 0,0) so the box shows exactly the widget.
|
||||
private async Task QueryContentBoundsAsync(string sourceId)
|
||||
{
|
||||
if (!_sessions.TryGetValue(sourceId, out var session) || session.Disposed) return;
|
||||
try
|
||||
{
|
||||
if (session.Control.CoreWebView2 is not { } cws) return;
|
||||
var json = await cws.ExecuteScriptAsync(
|
||||
"JSON.stringify({w:document.documentElement.scrollWidth||0," +
|
||||
"h:document.documentElement.scrollHeight||0," +
|
||||
"vw:window.innerWidth||0,vh:window.innerHeight||0})");
|
||||
if (string.IsNullOrEmpty(json) || json == "null") return;
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
session.ViewportCssW = root.GetProperty("vw").GetDouble();
|
||||
session.ViewportCssH = root.GetProperty("vh").GetDouble();
|
||||
session.ContentCssW = root.GetProperty("w").GetDouble();
|
||||
session.ContentCssH = root.GetProperty("h").GetDouble();
|
||||
_ = CaptureFrame(sourceId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLog.Write($"WebView2Manager: content bounds query failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CaptureFrame(string sourceId)
|
||||
{
|
||||
if (!_sessions.TryGetValue(sourceId, out var session) || session.Disposed) return;
|
||||
@@ -187,11 +222,31 @@ public sealed class WebView2Manager : IDisposable
|
||||
formatted.CopyPixels(pixels, pixW * 4, 0);
|
||||
|
||||
var stride = pixW * 4;
|
||||
var wb = new WriteableBitmap(pixW, pixH, 96, 96, PixelFormats.Bgra32, null);
|
||||
wb.WritePixels(new Int32Rect(0, 0, pixW, pixH), pixels, stride, 0);
|
||||
|
||||
int cropW = pixW, cropH = pixH;
|
||||
if (session.ViewportCssW >= 1 && session.ViewportCssH >= 1 &&
|
||||
session.ContentCssW >= 1 && session.ContentCssH >= 1)
|
||||
{
|
||||
var sx = pixW / session.ViewportCssW;
|
||||
var sy = pixH / session.ViewportCssH;
|
||||
cropW = (int)Math.Clamp(Math.Round(session.ContentCssW * sx), 1, pixW);
|
||||
cropH = (int)Math.Clamp(Math.Round(session.ContentCssH * sy), 1, pixH);
|
||||
}
|
||||
|
||||
var outPixels = pixels;
|
||||
if (cropW != pixW || cropH != pixH)
|
||||
{
|
||||
var dstStride = cropW * 4;
|
||||
outPixels = new byte[dstStride * cropH];
|
||||
for (var r = 0; r < cropH; r++)
|
||||
Buffer.BlockCopy(pixels, r * stride, outPixels, r * dstStride, dstStride);
|
||||
}
|
||||
|
||||
var wb = new WriteableBitmap(cropW, cropH, 96, 96, PixelFormats.Bgra32, null);
|
||||
wb.WritePixels(new Int32Rect(0, 0, cropW, cropH), outPixels, cropW * 4, 0);
|
||||
wb.Freeze();
|
||||
|
||||
session.LatestFrame = new VideoFrame(pixW, pixH, pixels);
|
||||
session.LatestFrame = new VideoFrame(cropW, cropH, outPixels);
|
||||
|
||||
_ = _dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user