2022-09-16 14:43:28 +03:00
import ' dart:convert ' ;
2022-09-08 18:25:19 +03:00
import ' dart:io ' ;
2022-09-13 17:24:06 +03:00
import ' dart:math ' as math ;
2022-10-09 14:27:30 +03:00
import ' dart:ui ' as ui ;
2022-09-08 18:25:19 +03:00
2022-08-26 18:28:08 +03:00
import ' package:flutter/material.dart ' ;
import ' package:flutter/services.dart ' ;
import ' package:flutter_hbb/models/chat_model.dart ' ;
2022-11-01 12:01:43 +03:00
import ' package:flutter_hbb/models/state_model.dart ' ;
2022-08-26 18:28:08 +03:00
import ' package:get/get.dart ' ;
2022-09-15 12:31:28 +03:00
import ' package:provider/provider.dart ' ;
2022-09-01 09:02:02 +03:00
import ' package:rxdart/rxdart.dart ' as rxdart ;
2022-10-08 12:27:30 +03:00
import ' package:desktop_multi_window/desktop_multi_window.dart ' ;
2022-10-09 14:27:30 +03:00
import ' package:window_size/window_size.dart ' as window_size ;
2022-08-26 18:28:08 +03:00
import ' ../../common.dart ' ;
import ' ../../mobile/widgets/dialog.dart ' ;
import ' ../../models/model.dart ' ;
import ' ../../models/platform_model.dart ' ;
2022-08-29 13:48:12 +03:00
import ' ../../common/shared_state.dart ' ;
2022-08-26 18:28:08 +03:00
import ' ./popup_menu.dart ' ;
2022-08-28 16:55:16 +03:00
import ' ./material_mod_popup_menu.dart ' as mod_menu ;
2022-08-26 18:28:08 +03:00
2022-11-10 09:32:22 +03:00
class MenubarState {
final kStoreKey = " remoteMenubarState " ;
late RxBool show ;
late RxBool _pin ;
MenubarState ( ) {
2022-11-10 16:25:12 +03:00
final s = bind . getLocalFlutterConfig ( k: kStoreKey ) ;
if ( s . isEmpty ) {
2022-11-10 09:32:22 +03:00
_initSet ( false , false ) ;
2022-11-10 16:25:12 +03:00
return ;
2022-11-10 09:32:22 +03:00
}
2022-11-10 16:25:12 +03:00
2022-11-10 09:32:22 +03:00
try {
2022-11-10 16:25:12 +03:00
final m = jsonDecode ( s ) ;
2022-11-10 09:32:22 +03:00
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 {
2022-11-10 16:25:12 +03:00
bind . setLocalFlutterConfig (
k: kStoreKey , v: jsonEncode ( { ' pin ' : _pin . value } ) ) ;
2022-11-10 09:32:22 +03:00
}
}
2022-08-26 18:28:08 +03:00
class _MenubarTheme {
static const Color commonColor = MyTheme . accent ;
2022-08-29 13:48:12 +03:00
// kMinInteractiveDimension
2022-09-23 07:20:40 +03:00
static const double height = 20.0 ;
2022-08-29 13:48:12 +03:00
static const double dividerHeight = 12.0 ;
2022-08-26 18:28:08 +03:00
}
class RemoteMenubar extends StatefulWidget {
final String id ;
final FFI ffi ;
2022-11-10 09:32:22 +03:00
final MenubarState state ;
2022-09-14 05:10:55 +03:00
final Function ( Function ( bool ) ) onEnterOrLeaveImageSetter ;
final Function ( ) onEnterOrLeaveImageCleaner ;
2022-08-26 18:28:08 +03:00
const RemoteMenubar ( {
Key ? key ,
required this . id ,
required this . ffi ,
2022-11-10 09:32:22 +03:00
required this . state ,
2022-09-14 05:10:55 +03:00
required this . onEnterOrLeaveImageSetter ,
required this . onEnterOrLeaveImageCleaner ,
2022-08-26 18:28:08 +03:00
} ) : super ( key: key ) ;
@ override
State < RemoteMenubar > createState ( ) = > _RemoteMenubarState ( ) ;
}
class _RemoteMenubarState extends State < RemoteMenubar > {
final Rx < Color > _hideColor = Colors . white12 . obs ;
2022-09-13 16:59:06 +03:00
final _rxHideReplay = rxdart . ReplaySubject < int > ( ) ;
bool _isCursorOverImage = false ;
2022-10-09 14:27:30 +03:00
window_size . Screen ? _screen ;
2022-08-26 18:28:08 +03:00
2022-11-01 12:01:43 +03:00
int get windowId = > stateGlobal . windowId ;
bool get isFullscreen = > stateGlobal . fullscreen ;
2022-09-13 16:59:06 +03:00
void _setFullscreen ( bool v ) {
2022-11-01 12:01:43 +03:00
stateGlobal . setFullscreen ( v ) ;
setState ( ( ) { } ) ;
2022-08-26 18:28:08 +03:00
}
2022-11-10 09:32:22 +03:00
RxBool get show = > widget . state . show ;
bool get pin = > widget . state . pin ;
2022-11-03 16:58:25 +03:00
2022-09-13 16:59:06 +03:00
@ override
2022-09-14 05:10:55 +03:00
initState ( ) {
2022-09-13 16:59:06 +03:00
super . initState ( ) ;
2022-09-14 05:10:55 +03:00
widget . onEnterOrLeaveImageSetter ( ( enter ) {
2022-09-13 16:59:06 +03:00
if ( enter ) {
_rxHideReplay . add ( 0 ) ;
_isCursorOverImage = true ;
} else {
_isCursorOverImage = false ;
}
} ) ;
_rxHideReplay
. throttleTime ( const Duration ( milliseconds: 5000 ) ,
trailing: true , leading: false )
. listen ( ( int v ) {
2022-11-10 09:32:22 +03:00
if ( ! pin & & show . isTrue & & _isCursorOverImage ) {
2022-11-03 16:58:25 +03:00
show . value = false ;
2022-09-13 16:59:06 +03:00
}
} ) ;
}
2022-09-14 05:10:55 +03:00
@ override
dispose ( ) {
super . dispose ( ) ;
widget . onEnterOrLeaveImageCleaner ( ) ;
}
2022-08-26 18:28:08 +03:00
@ override
Widget build ( BuildContext context ) {
return Align (
alignment: Alignment . topCenter ,
child: Obx (
2022-11-03 16:58:25 +03:00
( ) = > show . value ? _buildMenubar ( context ) : _buildShowHide ( context ) ) ,
2022-08-26 18:28:08 +03:00
) ;
}
Widget _buildShowHide ( BuildContext context ) {
2022-08-28 16:55:16 +03:00
return Obx ( ( ) = > Tooltip (
2022-11-03 16:58:25 +03:00
message: translate ( show . value ? ' Hide Menubar ' : ' Show Menubar ' ) ,
2022-09-14 05:10:55 +03:00
child: SizedBox (
width: 100 ,
height: 13 ,
child: TextButton (
2022-10-30 13:26:00 +03:00
onHover: ( bool v ) {
_hideColor . value = v ? Colors . white60 : Colors . white24 ;
} ,
onPressed: ( ) {
2022-11-03 16:58:25 +03:00
show . value = ! show . value ;
2022-10-30 13:26:00 +03:00
_hideColor . value = Colors . white24 ;
2022-11-03 16:58:25 +03:00
if ( show . isTrue ) {
2022-10-30 13:26:00 +03:00
_updateScreen ( ) ;
}
} ,
child: Obx ( ( ) = > Container (
decoration: BoxDecoration (
2022-09-14 05:10:55 +03:00
color: _hideColor . value ,
2022-10-30 13:26:00 +03:00
border: Border . all ( color: MyTheme . border ) ,
borderRadius: BorderRadius . all ( Radius . circular ( 5.0 ) ) ,
) ,
) . marginOnly ( bottom: 8.0 ) ) ,
) ) ) ) ;
2022-08-26 18:28:08 +03:00
}
2022-10-09 14:27:30 +03:00
_updateScreen ( ) async {
final v = await DesktopMultiWindow . invokeMethod ( 0 , " get_window_info " , " " ) ;
final String valueStr = v ;
if ( valueStr . isEmpty ) {
_screen = null ;
} else {
final screenMap = jsonDecode ( valueStr ) ;
_screen = window_size . Screen (
Rect . fromLTRB ( screenMap [ ' frame ' ] [ ' l ' ] , screenMap [ ' frame ' ] [ ' t ' ] ,
screenMap [ ' frame ' ] [ ' r ' ] , screenMap [ ' frame ' ] [ ' b ' ] ) ,
Rect . fromLTRB (
screenMap [ ' visibleFrame ' ] [ ' l ' ] ,
screenMap [ ' visibleFrame ' ] [ ' t ' ] ,
screenMap [ ' visibleFrame ' ] [ ' r ' ] ,
screenMap [ ' visibleFrame ' ] [ ' b ' ] ) ,
screenMap [ ' scaleFactor ' ] ) ;
}
}
2022-08-26 18:28:08 +03:00
Widget _buildMenubar ( BuildContext context ) {
final List < Widget > menubarItems = [ ] ;
if ( ! isWebDesktop ) {
2022-09-13 16:59:06 +03:00
menubarItems . add ( _buildPinMenubar ( context ) ) ;
2022-08-26 18:28:08 +03:00
menubarItems . add ( _buildFullscreen ( context ) ) ;
2022-09-08 17:18:02 +03:00
if ( widget . ffi . ffiModel . isPeerAndroid ) {
menubarItems . add ( IconButton (
tooltip: translate ( ' Mobile Actions ' ) ,
color: _MenubarTheme . commonColor ,
icon: const Icon ( Icons . build ) ,
onPressed: ( ) {
widget . ffi . dialogManager
. toggleMobileActionsOverlay ( ffi: widget . ffi ) ;
} ,
) ) ;
}
2022-08-26 18:28:08 +03:00
}
menubarItems . add ( _buildMonitor ( context ) ) ;
menubarItems . add ( _buildControl ( context ) ) ;
menubarItems . add ( _buildDisplay ( context ) ) ;
2022-09-05 17:18:29 +03:00
menubarItems . add ( _buildKeyboard ( context ) ) ;
2022-08-26 18:28:08 +03:00
if ( ! isWeb ) {
menubarItems . add ( _buildChat ( context ) ) ;
}
2022-09-15 12:31:28 +03:00
menubarItems . add ( _buildRecording ( context ) ) ;
2022-08-26 18:28:08 +03:00
menubarItems . add ( _buildClose ( context ) ) ;
return PopupMenuTheme (
2022-08-28 16:55:16 +03:00
data: const PopupMenuThemeData (
2022-08-26 18:28:08 +03:00
textStyle: TextStyle ( color: _MenubarTheme . commonColor ) ) ,
child: Column ( mainAxisSize: MainAxisSize . min , children: [
Container (
2022-10-30 13:26:00 +03:00
decoration: BoxDecoration (
color: Colors . white ,
border: Border . all ( color: MyTheme . border ) ,
borderRadius: BorderRadius . all ( Radius . circular ( 10.0 ) ) ,
) ,
2022-08-26 18:28:08 +03:00
child: Row (
mainAxisSize: MainAxisSize . min ,
children: menubarItems ,
) ) ,
_buildShowHide ( context ) ,
] ) ) ;
}
2022-09-13 16:59:06 +03:00
Widget _buildPinMenubar ( BuildContext context ) {
2022-09-13 17:24:06 +03:00
return Obx ( ( ) = > IconButton (
2022-11-10 09:32:22 +03:00
tooltip: translate ( pin ? ' Unpin menubar ' : ' Pin menubar ' ) ,
2022-09-13 17:24:06 +03:00
onPressed: ( ) {
2022-11-10 09:32:22 +03:00
widget . state . switchPin ( ) ;
2022-09-13 17:24:06 +03:00
} ,
icon: Obx ( ( ) = > Transform . rotate (
2022-11-10 09:32:22 +03:00
angle: pin ? math . pi / 4 : 0 ,
2022-09-13 17:24:06 +03:00
child: Icon (
Icons . push_pin ,
2022-11-10 09:32:22 +03:00
color: pin ? _MenubarTheme . commonColor : Colors . grey ,
2022-09-13 17:24:06 +03:00
) ) ) ,
) ) ;
2022-09-13 16:59:06 +03:00
}
2022-08-26 18:28:08 +03:00
Widget _buildFullscreen ( BuildContext context ) {
return IconButton (
tooltip: translate ( isFullscreen ? ' Exit Fullscreen ' : ' Fullscreen ' ) ,
onPressed: ( ) {
2022-09-13 16:59:06 +03:00
_setFullscreen ( ! isFullscreen ) ;
2022-08-26 18:28:08 +03:00
} ,
2022-11-01 12:01:43 +03:00
icon: isFullscreen
2022-08-28 16:55:16 +03:00
? const Icon (
2022-08-26 18:28:08 +03:00
Icons . fullscreen_exit ,
color: _MenubarTheme . commonColor ,
)
2022-08-28 16:55:16 +03:00
: const Icon (
2022-08-26 18:28:08 +03:00
Icons . fullscreen ,
color: _MenubarTheme . commonColor ,
2022-11-01 12:01:43 +03:00
) ,
2022-08-26 18:28:08 +03:00
) ;
}
Widget _buildChat ( BuildContext context ) {
return IconButton (
tooltip: translate ( ' Chat ' ) ,
onPressed: ( ) {
widget . ffi . chatModel . changeCurrentID ( ChatModel . clientModeID ) ;
widget . ffi . chatModel . toggleChatOverlay ( ) ;
} ,
2022-08-28 16:55:16 +03:00
icon: const Icon (
2022-08-26 18:28:08 +03:00
Icons . message ,
color: _MenubarTheme . commonColor ,
) ,
) ;
}
Widget _buildMonitor ( BuildContext context ) {
final pi = widget . ffi . ffiModel . pi ;
2022-08-28 16:55:16 +03:00
return mod_menu . PopupMenuButton (
2022-08-26 18:28:08 +03:00
tooltip: translate ( ' Select Monitor ' ) ,
padding: EdgeInsets . zero ,
2022-08-28 16:55:16 +03:00
position: mod_menu . PopupMenuPosition . under ,
2022-08-26 18:28:08 +03:00
icon: Stack (
alignment: Alignment . center ,
children: [
2022-08-28 16:55:16 +03:00
const Icon (
2022-08-26 18:28:08 +03:00
Icons . personal_video ,
color: _MenubarTheme . commonColor ,
) ,
Padding (
2022-08-28 16:55:16 +03:00
padding: const EdgeInsets . only ( bottom: 3.9 ) ,
2022-08-26 18:28:08 +03:00
child: Obx ( ( ) {
RxInt display = CurrentDisplayState . find ( widget . id ) ;
return Text (
" ${ display . value + 1 } / ${ pi . displays . length } " ,
2022-08-28 16:55:16 +03:00
style: const TextStyle (
color: _MenubarTheme . commonColor , fontSize: 8 ) ,
2022-08-26 18:28:08 +03:00
) ;
} ) ,
)
] ,
) ,
itemBuilder: ( BuildContext context ) {
final List < Widget > rowChildren = [ ] ;
for ( int i = 0 ; i < pi . displays . length ; i + + ) {
2022-08-29 13:48:12 +03:00
rowChildren . add (
Stack (
2022-08-26 18:28:08 +03:00
alignment: Alignment . center ,
children: [
2022-08-28 16:55:16 +03:00
const Icon (
2022-08-26 18:28:08 +03:00
Icons . personal_video ,
color: _MenubarTheme . commonColor ,
) ,
TextButton (
child: Container (
alignment: AlignmentDirectional . center ,
constraints:
2022-08-28 16:55:16 +03:00
const BoxConstraints ( minHeight: _MenubarTheme . height ) ,
2022-08-26 18:28:08 +03:00
child: Padding (
2022-08-28 16:55:16 +03:00
padding: const EdgeInsets . only ( bottom: 2.5 ) ,
2022-08-26 18:28:08 +03:00
child: Text (
( i + 1 ) . toString ( ) ,
2022-08-28 16:55:16 +03:00
style:
const TextStyle ( color: _MenubarTheme . commonColor ) ,
2022-08-26 18:28:08 +03:00
) ,
) ) ,
onPressed: ( ) {
RxInt display = CurrentDisplayState . find ( widget . id ) ;
if ( display . value ! = i ) {
bind . sessionSwitchDisplay ( id: widget . id , value: i ) ;
pi . currentDisplay = i ;
display . value = i ;
}
} ,
)
] ,
) ,
2022-08-29 13:48:12 +03:00
) ;
2022-08-26 18:28:08 +03:00
}
2022-08-28 16:55:16 +03:00
return < mod_menu . PopupMenuEntry < String > > [
mod_menu . PopupMenuItem < String > (
2022-08-26 18:28:08 +03:00
height: _MenubarTheme . height ,
padding: EdgeInsets . zero ,
child: Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: rowChildren ) ,
)
] ;
} ,
) ;
}
Widget _buildControl ( BuildContext context ) {
2022-08-28 16:55:16 +03:00
return mod_menu . PopupMenuButton (
2022-08-26 18:28:08 +03:00
padding: EdgeInsets . zero ,
2022-08-28 16:55:16 +03:00
icon: const Icon (
2022-08-26 18:28:08 +03:00
Icons . bolt ,
color: _MenubarTheme . commonColor ,
) ,
tooltip: translate ( ' Control Actions ' ) ,
2022-08-28 16:55:16 +03:00
position: mod_menu . PopupMenuPosition . under ,
2022-09-08 10:35:19 +03:00
itemBuilder: ( BuildContext context ) = > _getControlMenu ( context )
2022-08-26 18:28:08 +03:00
. map ( ( entry ) = > entry . build (
context ,
2022-08-28 16:55:16 +03:00
const MenuConfig (
2022-08-26 18:28:08 +03:00
commonColor: _MenubarTheme . commonColor ,
2022-08-29 13:48:12 +03:00
height: _MenubarTheme . height ,
dividerHeight: _MenubarTheme . dividerHeight ,
2022-08-26 18:28:08 +03:00
) ) )
2022-08-30 12:20:25 +03:00
. expand ( ( i ) = > i )
2022-08-26 18:28:08 +03:00
. toList ( ) ,
) ;
}
Widget _buildDisplay ( BuildContext context ) {
2022-09-16 14:43:28 +03:00
return FutureBuilder ( future: ( ) async {
final supportedHwcodec =
await bind . sessionSupportedHwcodec ( id: widget . id ) ;
return { ' supportedHwcodec ' : supportedHwcodec } ;
} ( ) , builder: ( context , snapshot ) {
if ( snapshot . hasData ) {
2022-10-08 12:27:30 +03:00
return Obx ( ( ) {
final remoteCount = RemoteCountState . find ( ) . value ;
return mod_menu . PopupMenuButton (
padding: EdgeInsets . zero ,
icon: const Icon (
Icons . tv ,
color: _MenubarTheme . commonColor ,
) ,
tooltip: translate ( ' Display Settings ' ) ,
position: mod_menu . PopupMenuPosition . under ,
itemBuilder: ( BuildContext context ) = >
_getDisplayMenu ( snapshot . data ! , remoteCount )
. map ( ( entry ) = > entry . build (
context ,
const MenuConfig (
commonColor: _MenubarTheme . commonColor ,
height: _MenubarTheme . height ,
dividerHeight: _MenubarTheme . dividerHeight ,
) ) )
. expand ( ( i ) = > i )
. toList ( ) ,
) ;
} ) ;
2022-09-16 14:43:28 +03:00
} else {
return const Offstage ( ) ;
}
} ) ;
2022-08-26 18:28:08 +03:00
}
2022-09-05 17:18:29 +03:00
Widget _buildKeyboard ( BuildContext context ) {
2022-10-16 07:32:52 +03:00
FfiModel ffiModel = Provider . of < FfiModel > ( context ) ;
if ( ffiModel . permissions [ ' keyboard ' ] = = false ) {
return Offstage ( ) ;
}
2022-09-05 17:18:29 +03:00
return mod_menu . PopupMenuButton (
padding: EdgeInsets . zero ,
icon: const Icon (
Icons . keyboard ,
color: _MenubarTheme . commonColor ,
) ,
tooltip: translate ( ' Keyboard Settings ' ) ,
position: mod_menu . PopupMenuPosition . under ,
itemBuilder: ( BuildContext context ) = > _getKeyboardMenu ( )
. map ( ( entry ) = > entry . build (
context ,
const MenuConfig (
commonColor: _MenubarTheme . commonColor ,
height: _MenubarTheme . height ,
dividerHeight: _MenubarTheme . dividerHeight ,
) ) )
. expand ( ( i ) = > i )
. toList ( ) ,
) ;
}
2022-09-15 12:31:28 +03:00
Widget _buildRecording ( BuildContext context ) {
2022-09-22 04:55:34 +03:00
return Consumer < FfiModel > ( builder: ( ( context , value , child ) {
if ( value . permissions [ ' recording ' ] ! = false ) {
return Consumer < RecordingModel > (
builder: ( context , value , child ) = > IconButton (
tooltip: value . start
? translate ( ' Stop session recording ' )
: translate ( ' Start session recording ' ) ,
onPressed: ( ) = > value . toggle ( ) ,
icon: Icon (
value . start
? Icons . pause_circle_filled
: Icons . videocam_outlined ,
color: _MenubarTheme . commonColor ,
) ,
) ) ;
} else {
return Offstage ( ) ;
}
} ) ) ;
2022-09-15 12:31:28 +03:00
}
2022-08-26 18:28:08 +03:00
Widget _buildClose ( BuildContext context ) {
return IconButton (
tooltip: translate ( ' Close ' ) ,
onPressed: ( ) {
2022-11-15 11:49:55 +03:00
clientClose ( widget . id , widget . ffi . dialogManager ) ;
2022-08-26 18:28:08 +03:00
} ,
2022-08-28 16:55:16 +03:00
icon: const Icon (
2022-08-26 18:28:08 +03:00
Icons . close ,
color: _MenubarTheme . commonColor ,
) ,
) ;
}
2022-09-08 10:35:19 +03:00
List < MenuEntryBase < String > > _getControlMenu ( BuildContext context ) {
2022-08-26 18:28:08 +03:00
final pi = widget . ffi . ffiModel . pi ;
final perms = widget . ffi . ffiModel . permissions ;
2022-09-23 07:20:40 +03:00
const EdgeInsets padding = EdgeInsets . only ( left: 14.0 , right: 5.0 ) ;
2022-08-26 18:28:08 +03:00
final List < MenuEntryBase < String > > displayMenu = [ ] ;
2022-09-08 10:35:19 +03:00
displayMenu . addAll ( [
MenuEntryButton < String > (
2022-09-23 07:20:40 +03:00
childBuilder: ( TextStyle ? style ) = > Container (
alignment: AlignmentDirectional . center ,
height: _MenubarTheme . height ,
child: Row (
children: [
Text (
translate ( ' OS Password ' ) ,
style: style ,
) ,
Expanded (
child: Align (
alignment: Alignment . centerRight ,
child: Transform . scale (
scale: 0.8 ,
child: IconButton (
padding: EdgeInsets . zero ,
icon: const Icon ( Icons . edit ) ,
onPressed: ( ) {
if ( Navigator . canPop ( context ) ) {
Navigator . pop ( context ) ;
}
showSetOSPassword (
widget . id , false , widget . ffi . dialogManager ) ;
} ) ) ,
) )
] ,
) ) ,
2022-09-08 10:35:19 +03:00
proc: ( ) {
showSetOSPassword ( widget . id , false , widget . ffi . dialogManager ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-09-08 10:35:19 +03:00
dismissOnClicked: true ,
) ,
MenuEntryButton < String > (
2022-08-26 18:28:08 +03:00
childBuilder: ( TextStyle ? style ) = > Text (
2022-09-08 10:35:19 +03:00
translate ( ' Transfer File ' ) ,
2022-08-26 18:28:08 +03:00
style: style ,
) ,
proc: ( ) {
2022-09-08 10:35:19 +03:00
connect ( context , widget . id , isFileTransfer: true ) ;
2022-08-26 18:28:08 +03:00
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-08-31 13:41:55 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ,
2022-09-08 10:35:19 +03:00
MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' TCP Tunneling ' ) ,
style: style ,
) ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-09-08 10:35:19 +03:00
proc: ( ) {
connect ( context , widget . id , isTcpTunneling: true ) ;
} ,
dismissOnClicked: true ,
) ,
] ) ;
// {handler.get_audit_server() && <li #note>{translate('Note')}</li>}
final auditServer = bind . sessionGetAuditServerSync ( id: widget . id ) ;
2022-09-08 18:25:19 +03:00
if ( auditServer . isNotEmpty ) {
displayMenu . add (
MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Note ' ) ,
style: style ,
) ,
proc: ( ) {
showAuditDialog ( widget . id , widget . ffi . dialogManager ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-09-08 18:25:19 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ,
2022-09-08 18:25:19 +03:00
) ;
}
2022-09-08 10:35:19 +03:00
displayMenu . add ( MenuEntryDivider ( ) ) ;
2022-08-26 18:28:08 +03:00
if ( perms [ ' keyboard ' ] ! = false ) {
if ( pi . platform = = ' Linux ' | | pi . sasEnabled ) {
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
2022-08-28 16:55:16 +03:00
' ${ translate ( " Insert " ) } Ctrl + Alt + Del ' ,
2022-08-26 18:28:08 +03:00
style: style ,
) ,
proc: ( ) {
bind . sessionCtrlAltDel ( id: widget . id ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-08-31 13:41:55 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ) ;
}
2022-09-08 10:35:19 +03:00
}
2022-10-16 07:32:52 +03:00
if ( perms [ " restart " ] ! = false & &
2022-09-08 10:35:19 +03:00
( pi . platform = = " Linux " | |
pi . platform = = " Windows " | |
pi . platform = = " Mac OS " ) ) {
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Restart Remote Device ' ) ,
style: style ,
) ,
proc: ( ) {
showRestartRemoteDevice ( pi , widget . id , gFFI . dialogManager ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-09-08 10:35:19 +03:00
dismissOnClicked: true ,
) ) ;
}
2022-08-26 18:28:08 +03:00
2022-09-08 10:35:19 +03:00
if ( perms [ ' keyboard ' ] ! = false ) {
2022-08-26 18:28:08 +03:00
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Insert Lock ' ) ,
style: style ,
) ,
proc: ( ) {
bind . sessionLockScreen ( id: widget . id ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-08-31 13:41:55 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ) ;
if ( pi . platform = = ' Windows ' ) {
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Obx ( ( ) = > Text (
translate (
2022-08-28 16:55:16 +03:00
' ${ BlockInputState . find ( widget . id ) . value ? " Unb " : " B " } lock user input ' ) ,
2022-08-26 18:28:08 +03:00
style: style ,
) ) ,
proc: ( ) {
RxBool blockInput = BlockInputState . find ( widget . id ) ;
bind . sessionToggleOption (
id: widget . id ,
2022-08-28 16:55:16 +03:00
value: ' ${ blockInput . value ? " un " : " " } block-input ' ) ;
2022-08-26 18:28:08 +03:00
blockInput . value = ! blockInput . value ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-08-31 13:41:55 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ) ;
}
}
2022-09-08 10:35:19 +03:00
if ( pi . version . isNotEmpty ) {
2022-08-26 18:28:08 +03:00
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
2022-09-08 10:35:19 +03:00
translate ( ' Refresh ' ) ,
2022-08-26 18:28:08 +03:00
style: style ,
) ,
proc: ( ) {
2022-09-08 10:35:19 +03:00
bind . sessionRefresh ( id: widget . id ) ;
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-09-08 10:35:19 +03:00
dismissOnClicked: true ,
) ) ;
}
if ( ! isWebDesktop ) {
// if (perms['keyboard'] != false && perms['clipboard'] != false) {
// displayMenu.add(MenuEntryButton<String>(
// childBuilder: (TextStyle? style) => Text(
// translate('Paste'),
// style: style,
// ),
// proc: () {
// () async {
// ClipboardData? data =
// await Clipboard.getData(Clipboard.kTextPlain);
// if (data != null && data.text != null) {
// bind.sessionInputString(id: widget.id, value: data.text ?? "");
// }
// }();
// },
2022-09-23 07:20:40 +03:00
// padding: padding,
2022-09-08 10:35:19 +03:00
// dismissOnClicked: true,
// ));
// }
displayMenu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Reset canvas ' ) ,
style: style ,
) ,
proc: ( ) {
widget . ffi . cursorModel . reset ( ) ;
2022-08-26 18:28:08 +03:00
} ,
2022-09-23 07:20:40 +03:00
padding: padding ,
2022-08-31 13:41:55 +03:00
dismissOnClicked: true ,
2022-08-26 18:28:08 +03:00
) ) ;
}
return displayMenu ;
}
2022-10-08 12:27:30 +03:00
bool _isWindowCanBeAdjusted ( int remoteCount ) {
2022-10-09 14:27:30 +03:00
if ( remoteCount ! = 1 ) {
return false ;
}
if ( _screen = = null ) {
return false ;
}
double scale = _screen ! . scaleFactor ;
double selfWidth = _screen ! . frame . width ;
double selfHeight = _screen ! . frame . height ;
2022-10-10 04:56:27 +03:00
if ( isFullscreen ) {
2022-10-09 14:27:30 +03:00
selfWidth = _screen ! . visibleFrame . width ;
selfHeight = _screen ! . visibleFrame . height ;
}
final canvasModel = widget . ffi . canvasModel ;
final displayWidth = canvasModel . getDisplayWidth ( ) ;
final displayHeight = canvasModel . getDisplayHeight ( ) ;
final requiredWidth = displayWidth +
( canvasModel . tabBarHeight + canvasModel . windowBorderWidth * 2 ) ;
final requiredHeight = displayHeight +
( canvasModel . tabBarHeight + canvasModel . windowBorderWidth * 2 ) ;
return selfWidth > ( requiredWidth * scale ) & &
selfHeight > ( requiredHeight * scale ) ;
2022-10-08 12:27:30 +03:00
}
List < MenuEntryBase < String > > _getDisplayMenu (
dynamic futureData , int remoteCount ) {
2022-09-23 07:20:40 +03:00
const EdgeInsets padding = EdgeInsets . only ( left: 18.0 , right: 8.0 ) ;
2022-08-26 18:28:08 +03:00
final displayMenu = [
2022-08-30 12:20:25 +03:00
MenuEntryRadios < String > (
2022-09-23 07:20:40 +03:00
text: translate ( ' Ratio ' ) ,
optionsGetter: ( ) = > [
MenuEntryRadioOption (
text: translate ( ' Scale original ' ) ,
value: ' original ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: translate ( ' Scale adaptive ' ) ,
value: ' adaptive ' ,
dismissOnClicked: true ,
) ,
] ,
curOptionGetter: ( ) async {
return await bind . sessionGetOption (
id: widget . id , arg: ' view-style ' ) ? ?
' adaptive ' ;
} ,
optionSetter: ( String oldValue , String newValue ) async {
await bind . sessionPeerOption (
id: widget . id , name: " view-style " , value: newValue ) ;
widget . ffi . canvasModel . updateViewStyle ( ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ,
2022-08-30 12:20:25 +03:00
MenuEntryDivider < String > ( ) ,
MenuEntryRadios < String > (
2022-09-23 07:20:40 +03:00
text: translate ( ' Scroll Style ' ) ,
optionsGetter: ( ) = > [
MenuEntryRadioOption (
text: translate ( ' ScrollAuto ' ) ,
value: ' scrollauto ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: translate ( ' Scrollbar ' ) ,
value: ' scrollbar ' ,
dismissOnClicked: true ,
) ,
] ,
curOptionGetter: ( ) async {
return await bind . sessionGetOption (
id: widget . id , arg: ' scroll-style ' ) ? ?
' ' ;
} ,
optionSetter: ( String oldValue , String newValue ) async {
await bind . sessionPeerOption (
id: widget . id , name: " scroll-style " , value: newValue ) ;
widget . ffi . canvasModel . updateScrollStyle ( ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ,
2022-08-30 12:20:25 +03:00
MenuEntryDivider < String > ( ) ,
MenuEntryRadios < String > (
2022-09-23 07:20:40 +03:00
text: translate ( ' Image Quality ' ) ,
optionsGetter: ( ) = > [
MenuEntryRadioOption (
text: translate ( ' Good image quality ' ) ,
value: ' best ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: translate ( ' Balanced ' ) ,
value: ' balanced ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: translate ( ' Optimize reaction time ' ) ,
value: ' low ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: translate ( ' Custom ' ) ,
value: ' custom ' ,
dismissOnClicked: true ) ,
] ,
curOptionGetter: ( ) async {
String quality =
await bind . sessionGetImageQuality ( id: widget . id ) ? ? ' balanced ' ;
if ( quality = = ' ' ) quality = ' balanced ' ;
return quality ;
} ,
optionSetter: ( String oldValue , String newValue ) async {
if ( oldValue ! = newValue ) {
await bind . sessionSetImageQuality ( id: widget . id , value: newValue ) ;
}
2022-10-19 05:19:49 +03:00
double qualityInitValue = 50 ;
double fpsInitValue = 30 ;
bool qualitySet = false ;
bool fpsSet = false ;
setCustomValues ( { double ? quality , double ? fps } ) async {
if ( quality ! = null ) {
qualitySet = true ;
await bind . sessionSetCustomImageQuality (
id: widget . id , value: quality . toInt ( ) ) ;
}
if ( fps ! = null ) {
fpsSet = true ;
await bind . sessionSetCustomFps ( id: widget . id , fps: fps . toInt ( ) ) ;
}
if ( ! qualitySet ) {
qualitySet = true ;
await bind . sessionSetCustomImageQuality (
id: widget . id , value: qualityInitValue . toInt ( ) ) ;
}
if ( ! fpsSet ) {
fpsSet = true ;
await bind . sessionSetCustomFps (
id: widget . id , fps: fpsInitValue . toInt ( ) ) ;
}
}
2022-09-23 07:20:40 +03:00
if ( newValue = = ' custom ' ) {
2022-10-19 05:19:49 +03:00
final btnClose = msgBoxButton ( translate ( ' Close ' ) , ( ) async {
await setCustomValues ( ) ;
2022-09-23 07:20:40 +03:00
widget . ffi . dialogManager . dismissAll ( ) ;
} ) ;
2022-10-19 05:19:49 +03:00
// quality
2022-09-23 07:20:40 +03:00
final quality =
await bind . sessionGetCustomImageQuality ( id: widget . id ) ;
2022-10-19 05:19:49 +03:00
qualityInitValue = quality ! = null & & quality . isNotEmpty
2022-09-23 07:20:40 +03:00
? quality [ 0 ] . toDouble ( )
: 50.0 ;
2022-10-19 05:19:49 +03:00
const qualityMinValue = 10.0 ;
const qualityMaxValue = 100.0 ;
if ( qualityInitValue < qualityMinValue ) {
qualityInitValue = qualityMinValue ;
2022-08-31 13:41:55 +03:00
}
2022-10-19 05:19:49 +03:00
if ( qualityInitValue > qualityMaxValue ) {
qualityInitValue = qualityMaxValue ;
2022-08-31 13:41:55 +03:00
}
2022-10-19 05:19:49 +03:00
final RxDouble qualitySliderValue = RxDouble ( qualityInitValue ) ;
final qualityRxReplay = rxdart . ReplaySubject < double > ( ) ;
qualityRxReplay
2022-09-23 07:20:40 +03:00
. throttleTime ( const Duration ( milliseconds: 1000 ) ,
trailing: true , leading: false )
. listen ( ( double v ) {
( ) async {
2022-10-19 05:19:49 +03:00
await setCustomValues ( quality: v ) ;
2022-09-23 07:20:40 +03:00
} ( ) ;
} ) ;
2022-10-19 05:19:49 +03:00
final qualitySlider = Obx ( ( ) = > Row (
children: [
Slider (
value: qualitySliderValue . value ,
min: qualityMinValue ,
max: qualityMaxValue ,
divisions: 90 ,
onChanged: ( double value ) {
qualitySliderValue . value = value ;
qualityRxReplay . add ( value ) ;
} ,
) ,
SizedBox (
width: 90 ,
child: Obx ( ( ) = > Text (
' ${ qualitySliderValue . value . round ( ) } % Bitrate ' ,
style: const TextStyle ( fontSize: 15 ) ,
) ) )
] ,
) ) ;
// fps
final fpsOption =
await bind . sessionGetOption ( id: widget . id , arg: ' custom-fps ' ) ;
fpsInitValue =
fpsOption = = null ? 30 : double . tryParse ( fpsOption ) ? ? 30 ;
if ( fpsInitValue < 10 | | fpsInitValue > 120 ) {
fpsInitValue = 30 ;
}
final RxDouble fpsSliderValue = RxDouble ( fpsInitValue ) ;
final fpsRxReplay = rxdart . ReplaySubject < double > ( ) ;
fpsRxReplay
. throttleTime ( const Duration ( milliseconds: 1000 ) ,
trailing: true , leading: false )
. listen ( ( double v ) {
( ) async {
await setCustomValues ( fps: v ) ;
} ( ) ;
2022-09-23 07:20:40 +03:00
} ) ;
2022-10-19 05:19:49 +03:00
bool ? direct ;
try {
direct = ConnectionTypeState . find ( widget . id ) . direct . value = =
ConnectionType . strDirect ;
} catch ( _ ) { }
final fpsSlider = Offstage (
offstage:
( await bind . mainIsUsingPublicServer ( ) & & direct ! = true ) | |
( await bind . versionToNumber (
v: widget . ffi . ffiModel . pi . version ) <
await bind . versionToNumber ( v: ' 1.2.0 ' ) ) ,
child: Row (
children: [
Obx ( ( ( ) = > Slider (
value: fpsSliderValue . value ,
min: 10 ,
max: 120 ,
divisions: 22 ,
onChanged: ( double value ) {
fpsSliderValue . value = value ;
fpsRxReplay . add ( value ) ;
} ,
) ) ) ,
SizedBox (
width: 90 ,
child: Obx ( ( ) {
final fps = fpsSliderValue . value . round ( ) ;
String text ;
if ( fps < 100 ) {
text = ' $ fps FPS ' ;
} else {
text = ' $ fps FPS ' ;
}
return Text (
text ,
2022-09-23 07:20:40 +03:00
style: const TextStyle ( fontSize: 15 ) ,
2022-10-19 05:19:49 +03:00
) ;
} ) )
] ,
) ,
) ;
final content = Column (
children: [ qualitySlider , fpsSlider ] ,
2022-09-23 07:20:40 +03:00
) ;
msgBoxCommon ( widget . ffi . dialogManager , ' Custom Image Quality ' ,
2022-10-19 05:19:49 +03:00
content , [ btnClose ] ) ;
2022-09-23 07:20:40 +03:00
}
} ,
padding: padding ,
) ,
2022-08-30 12:20:25 +03:00
MenuEntryDivider < String > ( ) ,
2022-09-16 14:43:28 +03:00
] ;
2022-10-08 12:27:30 +03:00
if ( _isWindowCanBeAdjusted ( remoteCount ) ) {
displayMenu . insert (
0 ,
MenuEntryDivider < String > ( ) ,
) ;
displayMenu . insert (
0 ,
MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Container (
child: Text (
2022-10-09 14:27:30 +03:00
translate ( ' Adjust Window ' ) ,
style: style ,
) ) ,
2022-10-08 12:27:30 +03:00
proc: ( ) {
( ) async {
2022-10-09 14:27:30 +03:00
await _updateScreen ( ) ;
if ( _screen ! = null ) {
2022-10-10 04:56:27 +03:00
_setFullscreen ( false ) ;
2022-10-09 14:27:30 +03:00
double scale = _screen ! . scaleFactor ;
final wndRect =
2022-11-01 12:01:43 +03:00
await WindowController . fromWindowId ( windowId ) . getFrame ( ) ;
2022-10-09 14:27:30 +03:00
final mediaSize = MediaQueryData . fromWindow ( ui . window ) . size ;
// On windows, wndRect is equal to GetWindowRect and mediaSize is equal to GetClientRect.
// https://stackoverflow.com/a/7561083
double magicWidth =
wndRect . right - wndRect . left - mediaSize . width * scale ;
double magicHeight =
wndRect . bottom - wndRect . top - mediaSize . height * scale ;
final canvasModel = widget . ffi . canvasModel ;
final width = ( canvasModel . getDisplayWidth ( ) +
canvasModel . windowBorderWidth * 2 ) *
scale +
magicWidth ;
final height = ( canvasModel . getDisplayHeight ( ) +
canvasModel . tabBarHeight +
canvasModel . windowBorderWidth * 2 ) *
scale +
magicHeight ;
double left = wndRect . left + ( wndRect . width - width ) / 2 ;
double top = wndRect . top + ( wndRect . height - height ) / 2 ;
Rect frameRect = _screen ! . frame ;
2022-11-01 12:01:43 +03:00
if ( ! isFullscreen ) {
2022-10-09 14:27:30 +03:00
frameRect = _screen ! . visibleFrame ;
}
if ( left < frameRect . left ) {
left = frameRect . left ;
}
if ( top < frameRect . top ) {
top = frameRect . top ;
}
if ( ( left + width ) > frameRect . right ) {
left = frameRect . right - width ;
}
if ( ( top + height ) > frameRect . bottom ) {
top = frameRect . bottom - height ;
}
2022-11-01 12:01:43 +03:00
await WindowController . fromWindowId ( windowId )
2022-10-09 14:27:30 +03:00
. setFrame ( Rect . fromLTWH ( left , top , width , height ) ) ;
}
2022-10-08 12:27:30 +03:00
} ( ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ,
) ;
}
2022-09-16 14:43:28 +03:00
/// Show Codec Preference
if ( bind . mainHasHwcodec ( ) ) {
final List < bool > codecs = [ ] ;
try {
final Map codecsJson = jsonDecode ( futureData [ ' supportedHwcodec ' ] ) ;
final h264 = codecsJson [ ' h264 ' ] ? ? false ;
final h265 = codecsJson [ ' h265 ' ] ? ? false ;
codecs . add ( h264 ) ;
codecs . add ( h265 ) ;
} finally { }
if ( codecs . length = = 2 & & ( codecs [ 0 ] | | codecs [ 1 ] ) ) {
displayMenu . add ( MenuEntryRadios < String > (
2022-09-23 07:20:40 +03:00
text: translate ( ' Codec Preference ' ) ,
optionsGetter: ( ) {
final list = [
MenuEntryRadioOption (
text: translate ( ' Auto ' ) ,
value: ' auto ' ,
dismissOnClicked: true ,
) ,
MenuEntryRadioOption (
text: ' VP9 ' ,
value: ' vp9 ' ,
dismissOnClicked: true ,
) ,
] ;
if ( codecs [ 0 ] ) {
list . add ( MenuEntryRadioOption (
text: ' H264 ' ,
value: ' h264 ' ,
dismissOnClicked: true ,
) ) ;
}
if ( codecs [ 1 ] ) {
list . add ( MenuEntryRadioOption (
text: ' H265 ' ,
value: ' h265 ' ,
dismissOnClicked: true ,
) ) ;
}
return list ;
} ,
curOptionGetter: ( ) async {
return await bind . sessionGetOption (
id: widget . id , arg: ' codec-preference ' ) ? ?
' auto ' ;
} ,
optionSetter: ( String oldValue , String newValue ) async {
await bind . sessionPeerOption (
id: widget . id , name: " codec-preference " , value: newValue ) ;
bind . sessionChangePreferCodec ( id: widget . id ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ) ;
2022-09-16 14:43:28 +03:00
}
}
/// Show remote cursor
displayMenu . add ( ( ) {
final state = ShowRemoteCursorState . find ( widget . id ) ;
return MenuEntrySwitch2 < String > (
2022-09-23 07:20:40 +03:00
switchType: SwitchType . scheckbox ,
text: translate ( ' Show remote cursor ' ) ,
getter: ( ) {
return state ;
} ,
setter: ( bool v ) async {
state . value = v ;
await bind . sessionToggleOption (
id: widget . id , value: ' show-remote-cursor ' ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ;
2022-09-16 14:43:28 +03:00
} ( ) ) ;
/// Show quality monitor
displayMenu . add ( MenuEntrySwitch < String > (
2022-09-23 07:20:40 +03:00
switchType: SwitchType . scheckbox ,
text: translate ( ' Show quality monitor ' ) ,
getter: ( ) async {
return bind . sessionGetToggleOptionSync (
id: widget . id , arg: ' show-quality-monitor ' ) ;
} ,
setter: ( bool v ) async {
await bind . sessionToggleOption (
id: widget . id , value: ' show-quality-monitor ' ) ;
widget . ffi . qualityMonitorModel . checkShowQualityMonitor ( widget . id ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ) ;
2022-08-26 18:28:08 +03:00
final perms = widget . ffi . ffiModel . permissions ;
final pi = widget . ffi . ffiModel . pi ;
if ( perms [ ' audio ' ] ! = false ) {
2022-09-23 07:20:40 +03:00
displayMenu
. add ( _createSwitchMenuEntry ( ' Mute ' , ' disable-audio ' , padding , true ) ) ;
2022-08-26 18:28:08 +03:00
}
2022-09-08 18:25:19 +03:00
if ( Platform . isWindows & &
pi . platform = = ' Windows ' & &
perms [ ' file ' ] ! = false ) {
displayMenu . add ( _createSwitchMenuEntry (
2022-09-23 07:20:40 +03:00
' Allow file copy and paste ' , ' enable-file-transfer ' , padding , true ) ) ;
2022-09-08 18:25:19 +03:00
}
2022-08-26 18:28:08 +03:00
if ( perms [ ' keyboard ' ] ! = false ) {
if ( perms [ ' clipboard ' ] ! = false ) {
2022-09-23 07:20:40 +03:00
displayMenu . add ( _createSwitchMenuEntry (
' Disable clipboard ' , ' disable-clipboard ' , padding , true ) ) ;
2022-08-26 18:28:08 +03:00
}
displayMenu . add ( _createSwitchMenuEntry (
2022-09-23 07:20:40 +03:00
' Lock after session end ' , ' lock-after-session-end ' , padding , true ) ) ;
2022-08-26 18:28:08 +03:00
if ( pi . platform = = ' Windows ' ) {
displayMenu . add ( MenuEntrySwitch2 < String > (
2022-09-23 07:20:40 +03:00
switchType: SwitchType . scheckbox ,
text: translate ( ' Privacy mode ' ) ,
getter: ( ) {
return PrivacyModeState . find ( widget . id ) ;
} ,
setter: ( bool v ) async {
await bind . sessionToggleOption (
id: widget . id , value: ' privacy-mode ' ) ;
} ,
padding: padding ,
dismissOnClicked: true ,
) ) ;
2022-08-26 18:28:08 +03:00
}
}
return displayMenu ;
}
2022-09-05 17:18:29 +03:00
List < MenuEntryBase < String > > _getKeyboardMenu ( ) {
final keyboardMenu = [
MenuEntryRadios < String > (
2022-09-23 07:20:40 +03:00
text: translate ( ' Ratio ' ) ,
optionsGetter: ( ) = > [
MenuEntryRadioOption ( text: translate ( ' Legacy mode ' ) , value: ' legacy ' ) ,
MenuEntryRadioOption ( text: translate ( ' Map mode ' ) , value: ' map ' ) ,
] ,
curOptionGetter: ( ) async {
return await bind . sessionGetKeyboardName ( id: widget . id ) ;
} ,
optionSetter: ( String oldValue , String newValue ) async {
await bind . sessionSetKeyboardMode (
id: widget . id , keyboardMode: newValue ) ;
widget . ffi . canvasModel . updateViewStyle ( ) ;
} ,
)
2022-09-05 17:18:29 +03:00
] ;
return keyboardMenu ;
}
2022-09-23 07:20:40 +03:00
MenuEntrySwitch < String > _createSwitchMenuEntry (
String text , String option , EdgeInsets ? padding , bool dismissOnClicked ) {
2022-08-26 18:28:08 +03:00
return MenuEntrySwitch < String > (
2022-09-23 07:20:40 +03:00
switchType: SwitchType . scheckbox ,
text: translate ( text ) ,
getter: ( ) async {
return bind . sessionGetToggleOptionSync ( id: widget . id , arg: option ) ;
} ,
setter: ( bool v ) async {
await bind . sessionToggleOption ( id: widget . id , value: option ) ;
} ,
padding: padding ,
dismissOnClicked: dismissOnClicked ,
) ;
2022-08-26 18:28:08 +03:00
}
}
void showSetOSPassword (
String id , bool login , OverlayDialogManager dialogManager ) async {
final controller = TextEditingController ( ) ;
var password = await bind . sessionGetOption ( id: id , arg: " os-password " ) ? ? " " ;
var autoLogin = await bind . sessionGetOption ( id: id , arg: " auto-login " ) ! = " " ;
controller . text = password ;
dialogManager . show ( ( setState , close ) {
2022-09-03 13:19:50 +03:00
submit ( ) {
var text = controller . text . trim ( ) ;
bind . sessionPeerOption ( id: id , name: " os-password " , value: text ) ;
bind . sessionPeerOption (
id: id , name: " auto-login " , value: autoLogin ? ' Y ' : ' ' ) ;
if ( text ! = " " & & login ) {
bind . sessionInputOsPassword ( id: id , value: text ) ;
}
close ( ) ;
}
2022-08-26 18:28:08 +03:00
return CustomAlertDialog (
2022-09-03 13:19:50 +03:00
title: Text ( translate ( ' OS Password ' ) ) ,
content: Column ( mainAxisSize: MainAxisSize . min , children: [
PasswordWidget ( controller: controller ) ,
CheckboxListTile (
contentPadding: const EdgeInsets . all ( 0 ) ,
dense: true ,
controlAffinity: ListTileControlAffinity . leading ,
title: Text (
translate ( ' Auto Login ' ) ,
2022-08-26 18:28:08 +03:00
) ,
2022-09-03 13:19:50 +03:00
value: autoLogin ,
onChanged: ( v ) {
if ( v = = null ) return ;
setState ( ( ) = > autoLogin = v ) ;
} ,
) ,
] ) ,
actions: [
TextButton (
style: flatButtonStyle ,
onPressed: close ,
child: Text ( translate ( ' Cancel ' ) ) ,
) ,
TextButton (
style: flatButtonStyle ,
onPressed: submit ,
child: Text ( translate ( ' OK ' ) ) ,
) ,
] ,
onSubmit: submit ,
onCancel: close ,
) ;
2022-08-26 18:28:08 +03:00
} ) ;
}
2022-09-08 10:35:19 +03:00
void showAuditDialog ( String id , dialogManager ) async {
final controller = TextEditingController ( ) ;
dialogManager . show ( ( setState , close ) {
submit ( ) {
var text = controller . text . trim ( ) ;
if ( text ! = " " ) {
bind . sessionSendNote ( id: id , note: text ) ;
}
close ( ) ;
}
late final focusNode = FocusNode (
onKey: ( FocusNode node , RawKeyEvent evt ) {
if ( evt . logicalKey . keyLabel = = ' Enter ' ) {
if ( evt is RawKeyDownEvent ) {
int pos = controller . selection . base . offset ;
controller . text =
' ${ controller . text . substring ( 0 , pos ) } \n ${ controller . text . substring ( pos ) } ' ;
controller . selection =
TextSelection . fromPosition ( TextPosition ( offset: pos + 1 ) ) ;
}
return KeyEventResult . handled ;
}
if ( evt . logicalKey . keyLabel = = ' Esc ' ) {
if ( evt is RawKeyDownEvent ) {
close ( ) ;
}
return KeyEventResult . handled ;
} else {
return KeyEventResult . ignored ;
}
} ,
) ;
return CustomAlertDialog (
title: Text ( translate ( ' Note ' ) ) ,
content: SizedBox (
width: 250 ,
height: 120 ,
child: TextField (
autofocus: true ,
keyboardType: TextInputType . multiline ,
textInputAction: TextInputAction . newline ,
decoration: const InputDecoration . collapsed (
hintText: " input note here " ,
) ,
// inputFormatters: [
// LengthLimitingTextInputFormatter(16),
// // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true)
// ],
maxLines: null ,
maxLength: 256 ,
controller: controller ,
focusNode: focusNode ,
) ) ,
actions: [
TextButton (
style: flatButtonStyle ,
onPressed: close ,
child: Text ( translate ( ' Cancel ' ) ) ,
) ,
TextButton (
style: flatButtonStyle ,
onPressed: submit ,
child: Text ( translate ( ' OK ' ) ) ,
) ,
] ,
onSubmit: submit ,
onCancel: close ,
) ;
} ) ;
}