refact: remote toolbar show/hide (#13843)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-12-19 20:45:22 +08:00
committed by GitHub
parent 3e0688ab63
commit d6463f95b9
6 changed files with 73 additions and 37 deletions

View File

@@ -120,6 +120,7 @@ const String kOptionApproveMode = "approve-mode";
const String kOptionAllowNumericOneTimePassword = const String kOptionAllowNumericOneTimePassword =
"allow-numeric-one-time-password"; "allow-numeric-one-time-password";
const String kOptionCollapseToolbar = "collapse_toolbar"; const String kOptionCollapseToolbar = "collapse_toolbar";
const String kOptionHideToolbar = "hide-toolbar";
const String kOptionShowRemoteCursor = "show_remote_cursor"; const String kOptionShowRemoteCursor = "show_remote_cursor";
const String kOptionFollowRemoteCursor = "follow_remote_cursor"; const String kOptionFollowRemoteCursor = "follow_remote_cursor";
const String kOptionFollowRemoteWindow = "follow_remote_window"; const String kOptionFollowRemoteWindow = "follow_remote_window";

View File

@@ -509,7 +509,6 @@ class _RemotePageState extends State<RemotePage>
() => _ffi.ffiModel.pi.isSet.isFalse () => _ffi.ffiModel.pi.isSet.isFalse
? Container(color: Colors.transparent) ? Container(color: Colors.transparent)
: Obx(() { : Obx(() {
widget.toolbarState.initShow(sessionId);
_ffi.textureModel.updateCurrentDisplay(peerDisplay.value); _ffi.textureModel.updateCurrentDisplay(peerDisplay.value);
return ImagePaint( return ImagePaint(
id: widget.id, id: widget.id,

View File

@@ -251,11 +251,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
MenuEntryButton<String>( MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Obx(() => Text( childBuilder: (TextStyle? style) => Obx(() => Text(
translate( translate(
toolbarState.show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), toolbarState.hide.isTrue ? 'Show Toolbar' : 'Hide Toolbar'),
style: style, style: style,
)), )),
proc: () { proc: () {
toolbarState.switchShow(sessionId); toolbarState.switchHide(sessionId);
cancelFunc(); cancelFunc();
}, },
padding: padding, padding: padding,

View File

@@ -465,7 +465,6 @@ class _ViewCameraPageState extends State<ViewCameraPage>
() => _ffi.ffiModel.pi.isSet.isFalse () => _ffi.ffiModel.pi.isSet.isFalse
? Container(color: Colors.transparent) ? Container(color: Colors.transparent)
: Obx(() { : Obx(() {
widget.toolbarState.initShow(sessionId);
_ffi.textureModel.updateCurrentDisplay(peerDisplay.value); _ffi.textureModel.updateCurrentDisplay(peerDisplay.value);
return ImagePaint( return ImagePaint(
id: widget.id, id: widget.id,

View File

@@ -250,11 +250,11 @@ class _ViewCameraTabPageState extends State<ViewCameraTabPage> {
MenuEntryButton<String>( MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Obx(() => Text( childBuilder: (TextStyle? style) => Obx(() => Text(
translate( translate(
toolbarState.show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), toolbarState.hide.isTrue ? 'Show Toolbar' : 'Hide Toolbar'),
style: style, style: style,
)), )),
proc: () { proc: () {
toolbarState.switchShow(sessionId); toolbarState.switchHide(sessionId);
cancelFunc(); cancelFunc();
}, },
padding: padding, padding: padding,

View File

@@ -31,8 +31,12 @@ import 'package:flutter_hbb/common/widgets/custom_scale_base.dart';
class ToolbarState { class ToolbarState {
late RxBool _pin; late RxBool _pin;
bool isShowInited = false; RxBool collapse = false.obs;
RxBool show = false.obs; RxBool hide = false.obs;
// Track initialization state to prevent flickering
final RxBool initialized = false.obs;
bool _isInitializing = false;
ToolbarState() { ToolbarState() {
_pin = RxBool(false); _pin = RxBool(false);
@@ -53,19 +57,39 @@ class ToolbarState {
bool get pin => _pin.value; bool get pin => _pin.value;
switchShow(SessionID sessionId) async { /// Initialize all toolbar states from session options.
bind.sessionToggleOption( /// This should be called once when the toolbar is first created.
sessionId: sessionId, value: kOptionCollapseToolbar); Future<void> init(SessionID sessionId) async {
show.value = !show.value; if (initialized.value || _isInitializing) return;
_isInitializing = true;
try {
// Load both states in parallel for better performance
final results = await Future.wait([
bind.sessionGetToggleOption(
sessionId: sessionId, arg: kOptionCollapseToolbar),
bind.sessionGetToggleOption(
sessionId: sessionId, arg: kOptionHideToolbar),
]);
collapse.value = results[0] ?? false;
hide.value = results[1] ?? false;
} finally {
_isInitializing = false;
initialized.value = true;
}
} }
initShow(SessionID sessionId) async { switchCollapse(SessionID sessionId) async {
if (!isShowInited) { bind.sessionToggleOption(
show.value = !(await bind.sessionGetToggleOption( sessionId: sessionId, value: kOptionCollapseToolbar);
sessionId: sessionId, arg: kOptionCollapseToolbar) ?? collapse.value = !collapse.value;
false); }
isShowInited = true;
} // Switch hide state for entire toolbar visibility
switchHide(SessionID sessionId) async {
bind.sessionToggleOption(sessionId: sessionId, value: kOptionHideToolbar);
hide.value = !hide.value;
} }
switchPin() async { switchPin() async {
@@ -237,7 +261,8 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
// setState(() {}); // setState(() {});
} }
RxBool get show => widget.state.show; RxBool get collapse => widget.state.collapse;
RxBool get hide => widget.state.hide;
bool get pin => widget.state.pin; bool get pin => widget.state.pin;
PeerInfo get pi => widget.ffi.ffiModel.pi; PeerInfo get pi => widget.ffi.ffiModel.pi;
@@ -258,6 +283,8 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
arg: 'remote-menubar-drag-x') ?? arg: 'remote-menubar-drag-x') ??
'0.5') ?? '0.5') ??
0.5; 0.5;
// Initialize toolbar states (collapse, hide) from session options
widget.state.init(widget.ffi.sessionId);
}); });
_debouncerHide = Debouncer<int>( _debouncerHide = Debouncer<int>(
@@ -277,8 +304,8 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
} }
_debouncerHideProc(int v) { _debouncerHideProc(int v) {
if (!pin && show.isTrue && _isCursorOverImage && _dragging.isFalse) { if (!pin && collapse.isFalse && _isCursorOverImage && _dragging.isFalse) {
show.value = false; collapse.value = true;
} }
} }
@@ -291,17 +318,27 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Obx(() {
alignment: Alignment.topCenter, // Wait for initialization to complete to prevent flickering
child: Obx(() => show.value if (!widget.state.initialized.value) {
? _buildToolbar(context) return const SizedBox.shrink();
: _buildDraggableShowHide(context)), }
); // If toolbar is hidden, return empty widget
if (hide.value) {
return const SizedBox.shrink();
}
return Align(
alignment: Alignment.topCenter,
child: collapse.isFalse
? _buildToolbar(context)
: _buildDraggableCollapse(context),
);
});
} }
Widget _buildDraggableShowHide(BuildContext context) { Widget _buildDraggableCollapse(BuildContext context) {
return Obx(() { return Obx(() {
if (show.isTrue && _dragging.isFalse) { if (collapse.isFalse && _dragging.isFalse) {
triggerAutoHide(); triggerAutoHide();
} }
final borderRadius = BorderRadius.vertical( final borderRadius = BorderRadius.vertical(
@@ -398,7 +435,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
), ),
), ),
), ),
_buildDraggableShowHide(context), _buildDraggableCollapse(context),
], ],
); );
} }
@@ -2491,7 +2528,7 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
double left = 0.0; double left = 0.0;
double right = 1.0; double right = 1.0;
RxBool get show => widget.toolbarState.show; RxBool get collapse => widget.toolbarState.collapse;
@override @override
initState() { initState() {
@@ -2614,20 +2651,20 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
)), )),
buttonWrapper( buttonWrapper(
() => setState(() { () => setState(() {
widget.toolbarState.switchShow(widget.sessionId); widget.toolbarState.switchCollapse(widget.sessionId);
}), }),
Obx((() => Tooltip( Obx((() => Tooltip(
message: message: translate(
translate(show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), collapse.isFalse ? 'Hide Toolbar' : 'Show Toolbar'),
child: Icon( child: Icon(
show.isTrue ? Icons.expand_less : Icons.expand_more, collapse.isFalse ? Icons.expand_less : Icons.expand_more,
size: iconSize, size: iconSize,
), ),
))), ))),
), ),
if (isWebDesktop) if (isWebDesktop)
Obx(() { Obx(() {
if (show.isTrue) { if (collapse.isFalse) {
return Offstage(); return Offstage();
} else { } else {
return buttonWrapper( return buttonWrapper(