Avoid relayouting capsule on every audio tick
The capsule window now tracks its last applied geometry and only resizes/repositions when the monitor or translation state actually changes. That keeps steady-state level updates from churning the native window manager while preserving the current placement behavior when geometry really does change. Constraint: Keep the fix minimal and avoid introducing a broader window-management abstraction. Rejected: Leave the unconditional relayout in place | it adds avoidable compositor churn during recording. Confidence: high Scope-risk: narrow Directive: Any future capsule geometry change should update the cached geometry key, not reintroduce unconditional relayout. Tested: cargo test --manifest-path src-tauri/Cargo.toml --lib -- --nocapture Not-tested: Live compositor/jitter behavior on Windows/macOS/Linux.
This commit is contained in:
1 parent
320c97485c
commit
1dfcf7f302
1 file changed
+40
-1
@@ -111,6 +111,9 @@ struct Inner {
|
||||
qa_hotkey: Mutex<Option<QaHotkeyMonitor>>,
|
||||
/// QA 单独的 session 状态,与 dictation 的 SessionPhase 不冲突。
|
||||
qa_state: Mutex<QaSessionState>,
|
||||
/// 最近一次应用到 capsule 窗口的几何状态。避免录音 level tick 反复触发
|
||||
/// resize / reposition。
|
||||
capsule_layout: Mutex<Option<CapsuleLayoutState>>,
|
||||
/// QA 用的 ASR 句柄(始终是 Volcengine 流式)。
|
||||
qa_asr: Mutex<Option<Arc<VolcengineStreamingASR>>>,
|
||||
/// QA 用的 Recorder 句柄。
|
||||
@@ -181,6 +184,7 @@ impl Coordinator {
|
||||
translation_modifier_seen: AtomicBool::new(false),
|
||||
qa_hotkey: Mutex::new(None),
|
||||
qa_state: Mutex::new(QaSessionState::default()),
|
||||
capsule_layout: Mutex::new(None),
|
||||
qa_asr: Mutex::new(None),
|
||||
qa_recorder: Mutex::new(None),
|
||||
}),
|
||||
@@ -2451,7 +2455,7 @@ fn emit_capsule(
|
||||
let show_capsule = inner.prefs.get().show_capsule;
|
||||
if let Some(window) = app.get_webview_window("capsule") {
|
||||
let visible = !matches!(state, CapsuleState::Idle);
|
||||
let _ = crate::position_capsule_bottom_center(&window, payload.translation);
|
||||
maybe_position_capsule_bottom_center(inner, &window, payload.translation);
|
||||
if show_capsule && visible {
|
||||
if cfg!(target_os = "windows") {
|
||||
if !show_capsule_window_no_activate() {
|
||||
@@ -2472,6 +2476,41 @@ fn emit_capsule(
|
||||
let _ = app.emit_to("capsule", "capsule:state", payload);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct CapsuleLayoutState {
|
||||
translation_active: bool,
|
||||
monitor_x: i32,
|
||||
monitor_y: i32,
|
||||
monitor_width: u32,
|
||||
monitor_height: u32,
|
||||
scale_bits: u64,
|
||||
}
|
||||
|
||||
fn maybe_position_capsule_bottom_center<R: tauri::Runtime>(
|
||||
inner: &Arc<Inner>,
|
||||
window: &tauri::WebviewWindow<R>,
|
||||
translation_active: bool,
|
||||
) {
|
||||
let Some(monitor) = window.current_monitor().ok().flatten() else {
|
||||
return;
|
||||
};
|
||||
let next = CapsuleLayoutState {
|
||||
translation_active,
|
||||
monitor_x: monitor.position().x,
|
||||
monitor_y: monitor.position().y,
|
||||
monitor_width: monitor.size().width,
|
||||
monitor_height: monitor.size().height,
|
||||
scale_bits: monitor.scale_factor().to_bits(),
|
||||
};
|
||||
let mut last = inner.capsule_layout.lock();
|
||||
if last.as_ref() == Some(&next) {
|
||||
return;
|
||||
}
|
||||
*last = Some(next);
|
||||
drop(last);
|
||||
let _ = crate::position_capsule_bottom_center(window, translation_active);
|
||||
}
|
||||
|
||||
// ─────────────────────────── audio bridge ───────────────────────────
|
||||
|
||||
struct DeferredAsrBridge {
|
||||
|
||||
Reference in new issue
Block a user