fix(vst-gui): borderless fx-gui window + anchored re-show (mouse stable)
- RenderFxJob: offscreen fx-gui window now WS_POPUP (borderless) so GetWindowRect == client rect; anchored placement over the embed panel is exact. WS_OVERLAPPEDWINDOW put the client ~31px lower/right -> cursor jumped on hover and clicks hit the webview (relay rejected). - AdjustWindowRect sizes to the window's own style (no caption for POPUP). - FxGuiServer: mousemove only SetCursorPos while dragging (buttons!=0); hover no longer teleports the cursor. - FxGuiServer /show: skip SetWindowPos/resize when rect unchanged (frontend now re-POSTs /show every ~500ms to re-anchor on scroll/resize). - app.jsx VstGuiEmbed: drop shownRef one-shot; re-POST /show each refresh. - Rebuilt bridge (6 locations) + Tauri release (embedded sidecar) + v=202608190002.
This commit is contained in:
@@ -11909,7 +11909,6 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
|
||||
const boxRef = React.useRef(null);
|
||||
const deadRef = React.useRef(false);
|
||||
const screenRectRef = React.useRef(null);
|
||||
const shownRef = React.useRef(false);
|
||||
const [dead, setDead] = React.useState(false);
|
||||
const post = (payload) => { try { fetch(url + '/input', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }).catch(() => {}); } catch (e) {} };
|
||||
React.useEffect(() => {
|
||||
@@ -11936,10 +11935,10 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
|
||||
let alive = true;
|
||||
const maybeShow = () => {
|
||||
const r = screenRectRef.current;
|
||||
if (!shownRef.current && r && r.w >= 4 && r.h >= 4) {
|
||||
shownRef.current = true;
|
||||
// Show the native window anchored over the panel (bridge stays
|
||||
// alive; hidden again by /hide on unmount).
|
||||
if (r && r.w >= 4 && r.h >= 4) {
|
||||
// Re-POST /show on every refresh (~500ms) so the native window
|
||||
// re-anchors over the panel on scroll/drag/resize. Idempotent: the
|
||||
// bridge skips SetWindowPos/resize when nothing moved.
|
||||
try { fetch(url + '/show', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rect: r }) }).catch(() => {}); } catch (e) {}
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -50,7 +50,7 @@
|
||||
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
|
||||
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
|
||||
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608190001" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608190002" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -405,7 +405,10 @@ void relayInput(HWND hwnd, const json_object_s* o) {
|
||||
if (!outside) ClientToScreen(target, &pt);
|
||||
realClick(pt.x, pt.y, true, button);
|
||||
} else if (type == "mousemove") {
|
||||
if (g_dance) {
|
||||
// Drag only: moving the cursor on plain hover fights the webview
|
||||
// (the plugin window sits under the pointer while anchored, so
|
||||
// hover reaches it naturally — no SetCursorPos needed).
|
||||
if (g_dance && buttons != 0) {
|
||||
POINT pt = { x, y };
|
||||
ClientToScreen(target, &pt);
|
||||
SetCursorPos(pt.x, pt.y);
|
||||
@@ -545,12 +548,20 @@ void runHttpServer(HWND hwnd, std::atomic<bool>& stop) {
|
||||
}
|
||||
if (sroot) std::free(sroot);
|
||||
if (rectOk) {
|
||||
SetWindowPos(hwnd, HWND_TOP, g_embedRect.left, g_embedRect.top,
|
||||
g_embedRect.right - g_embedRect.left,
|
||||
g_embedRect.bottom - g_embedRect.top,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
fxGuiRequestResize(hwnd, g_embedRect.right - g_embedRect.left,
|
||||
g_embedRect.bottom - g_embedRect.top);
|
||||
const int cw = g_embedRect.right - g_embedRect.left;
|
||||
const int ch = g_embedRect.bottom - g_embedRect.top;
|
||||
RECT cur = {};
|
||||
GetWindowRect(hwnd, &cur);
|
||||
const bool same = cur.left == g_embedRect.left && cur.top == g_embedRect.top &&
|
||||
cur.right - cur.left == cw && cur.bottom - cur.top == ch;
|
||||
// Frontend re-POSTs /show every ~500ms to re-anchor on scroll;
|
||||
// skip both when nothing moved so the plugin view is not
|
||||
// re-laid-out in a loop. Re-show after /hide regardless.
|
||||
if (!same || !IsWindowVisible(hwnd))
|
||||
SetWindowPos(hwnd, HWND_TOP, g_embedRect.left, g_embedRect.top,
|
||||
cw, ch, SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
if (cur.right - cur.left != cw || cur.bottom - cur.top != ch)
|
||||
fxGuiRequestResize(hwnd, cw, ch);
|
||||
g_dance = true;
|
||||
g_anchored = true;
|
||||
g_parkUntil = 0;
|
||||
|
||||
@@ -667,7 +667,9 @@ public:
|
||||
int w = rect.right - rect.left;
|
||||
int h = rect.bottom - rect.top;
|
||||
RECT r = { 0, 0, w, h };
|
||||
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, FALSE);
|
||||
// Size to the window's own style: borderless (WS_POPUP) needs no
|
||||
// caption/border adjustment.
|
||||
AdjustWindowRect(&r, GetWindowLongPtrA(hwnd, GWL_STYLE), FALSE);
|
||||
SetWindowPos(hwnd, nullptr, 0, 0, r.right - r.left, r.bottom - r.top,
|
||||
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
view->onSize(&rect); // some Skia/OpenGL editors skip first paint
|
||||
@@ -1192,7 +1194,11 @@ static int fxGuiCreateWindow(const std::string& name, bool offscreen,
|
||||
const int x = offscreen ? -20000 : CW_USEDEFAULT;
|
||||
const int y = offscreen ? -20000 : CW_USEDEFAULT;
|
||||
const DWORD exStyle = offscreen ? WS_EX_TOOLWINDOW : 0;
|
||||
const DWORD style = (offscreen ? (WS_OVERLAPPEDWINDOW & ~WS_MINIMIZEBOX) : WS_OVERLAPPEDWINDOW)
|
||||
// Borderless (WS_POPUP): GetWindowRect == client rect, so anchored
|
||||
// placement over the embed panel is exact. A captioned/bordered window
|
||||
// (WS_OVERLAPPEDWINDOW) put the client area ~31px lower/right of the
|
||||
// requested rect -> cursor jumped on hover and clicks landed on the webview.
|
||||
const DWORD style = (offscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW)
|
||||
| WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
||||
HWND hwnd = CreateWindowExA(exStyle, kFxGuiWndClass,
|
||||
(name.empty() ? "VST FX" : name).c_str(),
|
||||
|
||||
Reference in New Issue
Block a user