2023-04-23 20:53:51 +08:00
import ' dart:convert ' ;
2023-04-20 18:10:06 +08:00
import ' package:flutter/material.dart ' ;
2023-04-20 20:57:47 +08:00
import ' package:flutter/services.dart ' ;
2023-04-24 18:45:22 +08:00
import ' package:flutter_hbb/common.dart ' ;
2023-04-20 20:57:47 +08:00
import ' package:flutter_hbb/models/model.dart ' ;
2023-04-20 18:10:06 +08:00
import ' package:provider/provider.dart ' ;
2023-04-24 18:45:22 +08:00
import ' package:get/get.dart ' ;
2023-04-21 21:40:34 +08:00
// to-do: do not depend on desktop
2023-04-20 20:57:47 +08:00
import ' package:flutter_hbb/desktop/widgets/remote_toolbar.dart ' ;
import ' package:flutter_hbb/models/platform_model.dart ' ;
2023-04-20 18:10:06 +08:00
2023-05-10 18:58:45 +08:00
import ' ../manager.dart ' ;
import ' ../model.dart ' ;
import ' ../common.dart ' ;
2023-04-20 18:10:06 +08:00
2023-04-24 18:45:22 +08:00
// dup to flutter\lib\desktop\pages\desktop_setting_page.dart
const double _kCheckBoxLeftMargin = 10 ;
2023-04-20 22:53:43 +08:00
class LocationItem extends StatelessWidget {
2023-04-20 18:10:06 +08:00
final String peerId ;
2023-04-20 20:57:47 +08:00
final FFI ffi ;
final String location ;
2023-04-20 18:10:06 +08:00
final LocationModel locationModel ;
2023-04-24 18:45:22 +08:00
final bool isMenu ;
2023-04-20 18:10:06 +08:00
2023-04-20 22:53:43 +08:00
LocationItem ( {
2023-04-20 18:10:06 +08:00
Key ? key ,
required this . peerId ,
2023-04-20 20:57:47 +08:00
required this . ffi ,
required this . location ,
2023-04-20 18:10:06 +08:00
required this . locationModel ,
2023-04-24 18:45:22 +08:00
required this . isMenu ,
2023-04-20 18:10:06 +08:00
} ) : super ( key: key ) ;
2023-04-20 20:57:47 +08:00
bool get isEmpty = > locationModel . isEmpty ;
2023-04-24 18:45:22 +08:00
static Widget createLocationItem (
String peerId , FFI ffi , String location , bool isMenu ) {
final model = getLocationModel ( location ) ;
return model = = null
? Container ( )
: LocationItem (
peerId: peerId ,
ffi: ffi ,
location: location ,
locationModel: model ,
isMenu: isMenu ,
) ;
2023-04-20 22:53:43 +08:00
}
2023-04-20 18:10:06 +08:00
@ override
Widget build ( BuildContext context ) {
return ChangeNotifierProvider . value (
value: locationModel ,
child: Consumer < LocationModel > ( builder: ( context , model , child ) {
return Column (
2023-04-20 22:53:43 +08:00
children: model . pluginModels . entries
. map ( ( entry ) = > _buildPluginItem ( entry . key , entry . value ) )
. toList ( ) ,
) ;
} ) ,
) ;
}
Widget _buildPluginItem ( PluginId id , PluginModel model ) = > PluginItem (
pluginId: id ,
peerId: peerId ,
ffi: ffi ,
location: location ,
pluginModel: model ,
2023-04-24 18:45:22 +08:00
isMenu: isMenu ,
2023-04-20 22:53:43 +08:00
) ;
}
class PluginItem extends StatelessWidget {
final PluginId pluginId ;
final String peerId ;
2023-04-24 18:45:22 +08:00
final FFI ? ffi ;
2023-04-20 22:53:43 +08:00
final String location ;
final PluginModel pluginModel ;
2023-04-28 11:44:52 +08:00
final bool isMenu ;
2023-04-20 22:53:43 +08:00
PluginItem ( {
Key ? key ,
required this . pluginId ,
required this . peerId ,
2023-04-24 18:45:22 +08:00
this . ffi ,
2023-04-20 22:53:43 +08:00
required this . location ,
required this . pluginModel ,
2023-04-24 18:45:22 +08:00
required this . isMenu ,
2023-04-21 23:25:00 +08:00
} ) : super ( key: key ) ;
2023-04-20 22:53:43 +08:00
bool get isEmpty = > pluginModel . isEmpty ;
@ override
Widget build ( BuildContext context ) {
2023-04-21 23:25:00 +08:00
return ChangeNotifierProvider . value (
value: pluginModel ,
child: Consumer < PluginModel > (
builder: ( context , pluginModel , child ) {
2023-04-21 21:40:34 +08:00
return Column (
children: pluginModel . uiList . map ( ( ui ) = > _buildItem ( ui ) ) . toList ( ) ,
) ;
} ,
) ,
2023-04-20 18:10:06 +08:00
) ;
}
2023-04-20 20:57:47 +08:00
Widget _buildItem ( UiType ui ) {
2023-04-24 18:45:22 +08:00
Widget ? child ;
2023-04-20 20:57:47 +08:00
switch ( ui . runtimeType ) {
case UiButton:
2023-04-24 18:45:22 +08:00
if ( isMenu ) {
if ( ffi ! = null ) {
child = _buildMenuButton ( ui as UiButton , ffi ! ) ;
}
} else {
child = _buildButton ( ui as UiButton ) ;
}
2023-04-21 21:40:34 +08:00
break ;
2023-04-20 20:57:47 +08:00
case UiCheckbox:
2023-04-24 18:45:22 +08:00
if ( isMenu ) {
if ( ffi ! = null ) {
child = _buildCheckboxMenuButton ( ui as UiCheckbox , ffi ! ) ;
}
} else {
child = _buildCheckbox ( ui as UiCheckbox ) ;
}
2023-04-21 21:40:34 +08:00
break ;
2023-04-20 20:57:47 +08:00
default :
2023-04-24 18:45:22 +08:00
break ;
2023-04-20 20:57:47 +08:00
}
2023-04-21 21:40:34 +08:00
// to-do: add plugin icon and tooltip
2023-04-24 18:45:22 +08:00
return child ? ? Container ( ) ;
2023-04-20 20:57:47 +08:00
}
2023-04-24 18:45:22 +08:00
Widget _buildButton ( UiButton ui ) {
return TextButton (
2023-04-21 21:40:34 +08:00
onPressed: ( ) = > bind . pluginEvent (
id: pluginId ,
peer: peerId ,
event: _makeEvent ( ui . key ) ,
) ,
2023-04-20 20:57:47 +08:00
child: Text ( ui . text ) ,
) ;
}
2023-04-24 18:45:22 +08:00
Widget _buildCheckbox ( UiCheckbox ui ) {
getChild ( OptionModel model ) {
final v = _getOption ( model , ui . key ) ;
if ( v = = null ) {
// session or plugin not found
return Container ( ) ;
2023-04-21 21:40:34 +08:00
}
2023-04-24 18:45:22 +08:00
onChanged ( bool value ) {
bind . pluginEvent (
id: pluginId ,
peer: peerId ,
event: _makeEvent ( ui . key , v: value ) ,
) ;
}
final value = ConfigItem . isTrue ( v ) ;
return GestureDetector (
child: Row (
children: [
Checkbox (
value: value ,
onChanged: ( _ ) = > onChanged ( ! value ) ,
) . marginOnly ( right: 5 ) ,
Expanded (
child: Text ( translate ( ui . text ) ) ,
)
] ,
) . marginOnly ( left: _kCheckBoxLeftMargin ) ,
onTap: ( ) = > onChanged ( ! value ) ,
) ;
2023-04-21 21:40:34 +08:00
}
2023-04-24 18:45:22 +08:00
return ChangeNotifierProvider . value (
value: getOptionModel ( location , pluginId , peerId , ui . key ) ,
child: Consumer < OptionModel > (
builder: ( context , model , child ) = > getChild ( model ) ,
) ,
) ;
2023-04-21 23:25:00 +08:00
}
2023-04-24 18:45:22 +08:00
Widget _buildCheckboxMenuButton ( UiCheckbox ui , FFI ffi ) {
2023-04-21 23:25:00 +08:00
getChild ( OptionModel model ) {
2023-04-24 18:45:22 +08:00
final v = _getOption ( model , ui . key ) ;
2023-04-21 23:25:00 +08:00
if ( v = = null ) {
// session or plugin not found
return Container ( ) ;
}
return CkbMenuButton (
value: ConfigItem . isTrue ( v ) ,
onChanged: ( v ) {
if ( v ! = null ) {
bind . pluginEvent (
id: pluginId ,
peer: peerId ,
event: _makeEvent ( ui . key , v: v ) ,
) ;
}
} ,
// to-do: RustDesk translate or plugin translate ?
child: Text ( ui . text ) ,
ffi: ffi ,
) ;
2023-04-20 22:53:43 +08:00
}
2023-04-21 23:25:00 +08:00
return ChangeNotifierProvider . value (
2023-04-24 18:45:22 +08:00
value: getOptionModel ( location , pluginId , peerId , ui . key ) ,
2023-04-21 23:25:00 +08:00
child: Consumer < OptionModel > (
2023-04-24 18:45:22 +08:00
builder: ( context , model , child ) = > getChild ( model ) ,
2023-04-21 23:25:00 +08:00
) ,
2023-04-20 20:57:47 +08:00
) ;
}
2023-04-24 18:45:22 +08:00
Widget _buildMenuButton ( UiButton ui , FFI ffi ) {
return MenuButton (
onPressed: ( ) = > bind . pluginEvent (
id: pluginId ,
peer: peerId ,
event: _makeEvent ( ui . key ) ,
) ,
// to-do: support trailing icon, but it will cause tree shake error.
// ```
// This application cannot tree shake icons fonts. It has non-constant instances of IconData at the following locations:
// Target release_macos_bundle_flutter_assets failed: Exception: Avoid non-constant invocations of IconData or try to build again with --no-tree-shake-icons.
// ```
//
// trailingIcon: Icon(
// IconData(int.parse(ui.icon, radix: 16), fontFamily: 'MaterialIcons')),
//
// to-do: RustDesk translate or plugin translate ?
child: Text ( ui . text ) ,
ffi: ffi ,
) ;
}
Uint8List _makeEvent (
String key , {
bool ? v ,
} ) {
final event = MsgFromUi (
id: pluginId ,
2023-05-09 19:47:26 +08:00
name: pluginManager . getPlugin ( pluginId ) ? . meta . name ? ? ' ' ,
2023-04-24 18:45:22 +08:00
location: location ,
key: key ,
value:
v ! = null ? ( v ? ConfigItem . trueValue : ConfigItem . falseValue ) : ' ' ,
action: ' ' ,
) ;
return Uint8List . fromList ( event . toString ( ) . codeUnits ) ;
}
String ? _getOption ( OptionModel model , String key ) {
var v = model . value ;
if ( v = = null ) {
2023-04-27 14:41:53 +08:00
try {
if ( peerId . isEmpty ) {
v = bind . pluginGetSharedOption ( id: pluginId , key: key ) ;
} else {
v = bind . pluginGetSessionOption ( id: pluginId , peer: peerId , key: key ) ;
}
} catch ( e ) {
debugPrint ( ' Failed to get option " $ key ", $ e ' ) ;
v = null ;
2023-04-24 18:45:22 +08:00
}
}
return v ;
}
2023-04-20 18:10:06 +08:00
}
2023-04-20 22:53:43 +08:00
2023-06-06 07:39:44 +08:00
void handleReloading ( Map < String , dynamic > evt ) {
2023-04-20 22:53:43 +08:00
if ( evt [ ' id ' ] = = null | | evt [ ' location ' ] = = null ) {
return ;
}
2023-04-23 20:53:51 +08:00
try {
2023-05-10 18:58:45 +08:00
final uiList = < UiType > [ ] ;
for ( var e in json . decode ( evt [ ' ui ' ] as String ) ) {
final ui = UiType . create ( e ) ;
if ( ui ! = null ) {
uiList . add ( ui ) ;
}
}
if ( uiList . isNotEmpty ) {
addLocationUi ( evt [ ' location ' ] ! , evt [ ' id ' ] ! , uiList ) ;
2023-04-23 20:53:51 +08:00
}
} catch ( e ) {
debugPrint ( ' Failed handleReloading, json decode of ui, $ e ' ) ;
}
2023-04-21 21:40:34 +08:00
}
2023-06-06 07:39:44 +08:00
void handleOption ( Map < String , dynamic > evt ) {
2023-04-21 21:40:34 +08:00
updateOption (
evt [ ' location ' ] , evt [ ' id ' ] , evt [ ' peer ' ] ? ? ' ' , evt [ ' key ' ] , evt [ ' value ' ] ) ;
2023-04-20 22:53:43 +08:00
}