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 =
"allow-numeric-one-time-password";
const String kOptionCollapseToolbar = "collapse_toolbar";
const String kOptionHideToolbar = "hide-toolbar";
const String kOptionShowRemoteCursor = "show_remote_cursor";
const String kOptionFollowRemoteCursor = "follow_remote_cursor";
const String kOptionFollowRemoteWindow = "follow_remote_window";

View File

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

View File

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

View File

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

View File

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

View File

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