diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 76395be9d..21cbe45b9 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -28,14 +28,14 @@ class RemotePage extends StatefulWidget { RemotePage({ Key? key, required this.id, + required this.menubarState, }) : super(key: key); final String id; + final MenubarState menubarState; final SimpleWrapper?> _lastState = SimpleWrapper(null); FFI get ffi => (_lastState.value! as _RemotePageState)._ffi; - RxBool get showMenubar => - (_lastState.value! as _RemotePageState)._showMenubar; @override State createState() { @@ -50,7 +50,6 @@ class _RemotePageState extends State Timer? _timer; String keyboardMode = "legacy"; final _cursorOverImage = false.obs; - final _showMenubar = false.obs; late RxBool _showRemoteCursor; late RxBool _remoteCursorMoved; late RxBool _keyboardEnabled; @@ -239,7 +238,7 @@ class _RemotePageState extends State paints.add(RemoteMenubar( id: widget.id, ffi: _ffi, - show: _showMenubar, + state: widget.menubarState, onEnterOrLeaveImageSetter: (func) => _onEnterOrLeaveImage4Menubar = func, onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Menubar = null, )); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 3068f2db7..4f7e9c5aa 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -9,6 +9,7 @@ import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; +import 'package:flutter_hbb/desktop/widgets/remote_menubar.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart' as mod_menu; @@ -43,9 +44,12 @@ class _ConnectionTabPageState extends State { static const IconData selectedIcon = Icons.desktop_windows_sharp; static const IconData unselectedIcon = Icons.desktop_windows_outlined; + late MenubarState _menubarState; + var connectionMap = RxList.empty(growable: true); _ConnectionTabPageState(Map params) { + _menubarState = MenubarState(); RemoteCountState.init(); final peerId = params['id']; if (peerId != null) { @@ -59,6 +63,7 @@ class _ConnectionTabPageState extends State { page: RemotePage( key: ValueKey(peerId), id: peerId, + menubarState: _menubarState, ), )); _update_remote_count(); @@ -88,7 +93,11 @@ class _ConnectionTabPageState extends State { selectedIcon: selectedIcon, unselectedIcon: unselectedIcon, onTabCloseButton: () => tabController.closeBy(id), - page: RemotePage(key: ValueKey(id), id: id), + page: RemotePage( + key: ValueKey(id), + id: id, + menubarState: _menubarState, + ), )); } else if (call.method == "onDestroy") { tabController.clear(); @@ -99,6 +108,12 @@ class _ConnectionTabPageState extends State { }); } + @override + void dispose() { + super.dispose(); + _menubarState.save(); + } + @override Widget build(BuildContext context) { final tabWidget = Container( @@ -177,7 +192,7 @@ class _ConnectionTabPageState extends State { ); } - // to-do: some dup code to ../widgets/remote_menubar + // Note: Some dup code to ../widgets/remote_menubar Widget _tabMenuBuilder(String key, CancelFunc cancelFunc) { final List> menu = []; const EdgeInsets padding = EdgeInsets.only(left: 8.0, right: 5.0); @@ -187,7 +202,6 @@ class _ConnectionTabPageState extends State { final ffi = remotePage.ffi; final pi = ffi.ffiModel.pi; final perms = ffi.ffiModel.permissions; - final showMenuBar = remotePage.showMenubar; menu.addAll([ MenuEntryButton( childBuilder: (TextStyle? style) => Text( @@ -202,11 +216,12 @@ class _ConnectionTabPageState extends State { ), MenuEntryButton( childBuilder: (TextStyle? style) => Obx(() => Text( - translate(showMenuBar.isTrue ? 'Hide Menubar' : 'Show Menubar'), + translate( + _menubarState.show.isTrue ? 'Hide Menubar' : 'Show Menubar'), style: style, )), proc: () { - showMenuBar.value = !showMenuBar.value; + _menubarState.switchShow(); cancelFunc(); }, padding: padding, diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 26cc26ddd..be04cb2ba 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart' as rxdart; @@ -21,6 +22,69 @@ import '../../common/shared_state.dart'; import './popup_menu.dart'; import './material_mod_popup_menu.dart' as mod_menu; +class MenubarState { + final kStoreKey = "remoteMenubarState"; + late RxBool show; + late RxBool _pin; + + MenubarState() { + final s = Get.find().getString(kStoreKey); + if (s == null) { + _initSet(false, false); + } + try { + final m = jsonDecode(s!); + if (m == null) { + _initSet(false, false); + } else { + _initSet(m['pin'] ?? false, m['pin'] ?? false); + } + } catch (e) { + debugPrint('Failed to decode menubar state ${e.toString()}'); + _initSet(false, false); + } + } + + _initSet(bool s, bool p) { + show = RxBool(s); + _pin = RxBool(p); + } + + bool get pin => _pin.value; + + switchShow() async { + show.value = !show.value; + } + + setShow(bool v) async { + if (show.value != v) { + show.value = v; + } + } + + switchPin() async { + _pin.value = !_pin.value; + // Save everytime changed, as this func will not be called frequently + await save(); + } + + setPin(bool v) async { + if (_pin.value != v) { + _pin.value = v; + // Save everytime changed, as this func will not be called frequently + await save(); + } + } + + save() async { + final success = await Get.find() + .setString(kStoreKey, jsonEncode({'pin': _pin.value}.toString())); + if (!success) { + debugPrint('Failed to save remote menu bar state'); + } + } +} + class _MenubarTheme { static const Color commonColor = MyTheme.accent; // kMinInteractiveDimension @@ -31,7 +95,7 @@ class _MenubarTheme { class RemoteMenubar extends StatefulWidget { final String id; final FFI ffi; - final RxBool show; + final MenubarState state; final Function(Function(bool)) onEnterOrLeaveImageSetter; final Function() onEnterOrLeaveImageCleaner; @@ -39,7 +103,7 @@ class RemoteMenubar extends StatefulWidget { Key? key, required this.id, required this.ffi, - required this.show, + required this.state, required this.onEnterOrLeaveImageSetter, required this.onEnterOrLeaveImageCleaner, }) : super(key: key); @@ -51,7 +115,6 @@ class RemoteMenubar extends StatefulWidget { class _RemoteMenubarState extends State { final Rx _hideColor = Colors.white12.obs; final _rxHideReplay = rxdart.ReplaySubject(); - final _pinMenubar = false.obs; bool _isCursorOverImage = false; window_size.Screen? _screen; @@ -63,7 +126,8 @@ class _RemoteMenubarState extends State { setState(() {}); } - RxBool get show => widget.show; + RxBool get show => widget.state.show; + bool get pin => widget.state.pin; @override initState() { @@ -82,7 +146,7 @@ class _RemoteMenubarState extends State { .throttleTime(const Duration(milliseconds: 5000), trailing: true, leading: false) .listen((int v) { - if (_pinMenubar.isFalse && show.isTrue && _isCursorOverImage) { + if (!pin && show.isTrue && _isCursorOverImage) { show.value = false; } }); @@ -196,18 +260,15 @@ class _RemoteMenubarState extends State { Widget _buildPinMenubar(BuildContext context) { return Obx(() => IconButton( - tooltip: - translate(_pinMenubar.isTrue ? 'Unpin menubar' : 'Pin menubar'), + tooltip: translate(pin ? 'Unpin menubar' : 'Pin menubar'), onPressed: () { - _pinMenubar.value = !_pinMenubar.value; + widget.state.switchPin(); }, icon: Obx(() => Transform.rotate( - angle: _pinMenubar.isTrue ? math.pi / 4 : 0, + angle: pin ? math.pi / 4 : 0, child: Icon( Icons.push_pin, - color: _pinMenubar.isTrue - ? _MenubarTheme.commonColor - : Colors.grey, + color: pin ? _MenubarTheme.commonColor : Colors.grey, ))), )); } diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index f6e3820b9..dab21fcb6 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -8,6 +8,7 @@ class StateGlobal { bool _fullscreen = false; final RxBool _showTabBar = true.obs; final RxDouble _resizeEdgeSize = 8.0.obs; + final RxBool showRemoteMenuBar = false.obs; int get windowId => _windowId; bool get fullscreen => _fullscreen;