[fix] keep stream menus open while suspending remote input

This commit is contained in:
dijunkun
2026-11-11 05:30:18 +08:00
parent 69d4394717
commit 14fa95beeb
4 changed files with 52 additions and 23 deletions
@@ -48,6 +48,7 @@
#include "features/gallery/gallery_workspace.h"
#include "features/gallery/preview_bandwidth.h"
#include "features/gallery/preview_resolution.h"
#include "features/input/stream_keyboard_focus.h"
#include "filesystem_utf8.h"
#include "localization.h"
#include "platform.h"
@@ -3803,19 +3804,10 @@ void GuiApplication::SyncXWaylandWindowActivation() {
#endif
void GuiApplication::SetStreamKeyboardFocus(bool focused) {
// Local dialogs own input even while the native window is active.
if (focused && ui_ && ui_->stream &&
(*ui_->stream)->get_modal_visible()) {
focused = false;
}
if (focus_on_stream_window_ == focused) {
return;
}
focus_on_stream_window_ = focused;
if (!focused) {
ReleaseControl();
}
const bool local_input_active = ui_ && ui_->stream &&
(*ui_->stream)->get_local_input_active();
UpdateStreamKeyboardFocus(focused, local_input_active, focus_on_stream_window_,
[this](bool preserve_local_ui) { ReleaseControl(preserve_local_ui); });
}
void GuiApplication::SyncStreamKeyboardFocus() {
@@ -5665,7 +5657,7 @@ void GuiApplication::SyncAudioPlayback() {
}
}
void GuiApplication::ReleasePressedInput() {
void GuiApplication::ReleasePressedInput(bool preserve_local_ui) {
keyboard_.ForceReleasePressedKeys();
for (int button : ui_->pressed_mouse_buttons) {
RemoteAction action{};
@@ -5678,13 +5670,16 @@ void GuiApplication::ReleasePressedInput() {
SendRemoteInput(action);
}
ui_->pressed_mouse_buttons.clear();
if (ui_->stream) (*ui_->stream)->invoke_reset_input_state();
if (ui_->stream) {
if (preserve_local_ui) (*ui_->stream)->invoke_reset_pressed_input();
else (*ui_->stream)->invoke_reset_input_state();
}
}
void GuiApplication::ReleaseControl() {
void GuiApplication::ReleaseControl(bool preserve_local_ui) {
const auto gallery_target = ui_->gallery_control_id;
devices_.StopKeyboardCapturer();
ReleasePressedInput();
ReleasePressedInput(preserve_local_ui);
gallery_group_control_id_.clear();
ui_->wall->set_gallery_group_controlling(false);
ui_->wall->set_gallery_remote_cursor_shape(
@@ -84,8 +84,8 @@ private:
void DeviceGroupAction(const std::string& action, const std::string& value,
bool gallery_window);
bool SaveDeviceGroups(const std::string& new_group, bool gallery_window);
void ReleasePressedInput();
void ReleaseControl();
void ReleasePressedInput(bool preserve_local_ui = false);
void ReleaseControl(bool preserve_local_ui = false);
void SyncAudioPlayback();
void SaveGalleryWindowState();
void SyncAnnouncements();
@@ -0,0 +1,26 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-11-11
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _STREAM_KEYBOARD_FOCUS_H_
#define _STREAM_KEYBOARD_FOCUS_H_
namespace crossdesk {
// Native window focus and ownership of keyboard input are different: a local
// menu keeps the window active while temporarily suspending remote input.
// Publish the new state before releasing input, since UI callbacks can reenter.
template <typename Release>
void UpdateStreamKeyboardFocus(bool focused, bool local_input_active,
bool& remote_focused, Release release) {
focused = focused && !local_input_active;
if (remote_focused == focused) return;
remote_focused = focused;
if (!focused) release(local_input_active);
}
} // namespace crossdesk
#endif
+12 -4
View File
@@ -423,6 +423,10 @@ export component StreamWindow inherits Window {
private property <length> disconnect-menu-y: root.control-panel-y(root.disconnect-menu-height);
private property <bool> control-menu-open:
root.display-menu-open || root.shortcut-menu-open || root.video-menu-open || root.disconnect-menu-open;
out property <bool> local-input-active: root.modal-visible || root.control-menu-open;
changed control-menu-open => {
root.keyboard-focus-changed(!root.control-menu-open && !root.modal-visible && input-focus.has-focus);
}
changed selected-tab => {
root.close-control-menus();
root.cancel-remote-pointer();
@@ -589,10 +593,14 @@ export component StreamWindow inherits Window {
// Native focus/session changes release input in C++; clear the matching UI
// ownership too, including when replacing the only tab (same tab index).
public function reset-input-state() {
public function reset-pressed-input() {
root.remote-left-down = false;
root.remote-right-down = false;
root.remote-middle-down = false;
}
public function reset-input-state() {
root.reset-pressed-input();
root.close-control-menus();
root.control-drag-pending = false;
root.end-control-drag();
@@ -1039,10 +1047,10 @@ export component StreamWindow inherits Window {
input-focus := FocusScope {
focus-on-click: true;
focus-gained => { root.keyboard-focus-changed(true); }
focus-gained => { root.keyboard-focus-changed(!root.control-menu-open && !root.modal-visible); }
focus-lost => { root.keyboard-focus-changed(false); }
key-pressed(event) => {
if !root.modal-visible {
if !root.modal-visible && !root.control-menu-open {
root.key-input(event.text, true, event.modifiers.control, event.modifiers.alt, event.modifiers.shift, event.modifiers.meta);
}
accept
@@ -1053,7 +1061,7 @@ export component StreamWindow inherits Window {
if event.text == Key.Escape || event.text == Key.Return {
if root.privacy-unlock-visible { root.dismiss-privacy-unlock(); } else { root.dismiss-remote-update(); }
}
} else {
} else if !root.control-menu-open {
root.key-input(event.text, false, event.modifiers.control, event.modifiers.alt, event.modifiers.shift, event.modifiers.meta);
}
accept