choose keyboard layout type, mid commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2022-12-27 16:45:13 +08:00
parent 5b3b7bd3c0
commit 48e684335e
11 changed files with 345 additions and 10 deletions

View File

@ -23,6 +23,7 @@ import '../../models/model.dart';
import '../../models/platform_model.dart';
import '../../common/shared_state.dart';
import '../widgets/remote_menubar.dart';
import '../widgets/kb_layout_type_chooser.dart';
bool _isCustomCursorInited = false;
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
@ -95,6 +96,10 @@ class _RemotePageState extends State<RemotePage>
_initStates(widget.id);
_ffi = FFI();
Get.put(_ffi, tag: widget.id);
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
showKBLayoutTypeChooserIfNeeded(
_ffi.ffiModel.pi.platform, _ffi.dialogManager);
});
_ffi.start(widget.id);
WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);

View File

@ -0,0 +1,227 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import '../../common.dart';
typedef KBChoosedCallback = bool Function(String);
const double _kImageMarginVertical = 6.0;
const double _kImageMarginHorizental = 10.0;
const double _kImageBoarderWidth = 4.0;
const double _kImagePaddingWidth = 4.0;
const Color _kImageBorderColor = Color.fromARGB(125, 202, 247, 2);
const double _kBorderRadius = 6.0;
const String _kKBLayoutTypeISO = 'ISO';
const String _kKBLayoutTypeNotISO = 'Not ISO';
const _kKBLayoutImageMap = {
_kKBLayoutTypeISO: 'KB_LAYOUT_ISO',
_kKBLayoutTypeNotISO: 'KB_LAYOUT_NOT_ISO',
};
class _KBImage extends StatelessWidget {
final String kbLayoutType;
final double imageWidth;
final RxString choosedType;
const _KBImage({
Key? key,
required this.kbLayoutType,
required this.imageWidth,
required this.choosedType,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_kBorderRadius),
border: Border.all(
color: choosedType.value == kbLayoutType
? _kImageBorderColor
: Colors.transparent,
width: _kImageBoarderWidth,
),
),
margin: EdgeInsets.symmetric(
horizontal: _kImageMarginHorizental,
vertical: _kImageMarginVertical,
),
padding: EdgeInsets.all(_kImagePaddingWidth),
child: SvgPicture.asset(
'assets/${_kKBLayoutImageMap[kbLayoutType] ?? ""}.svg',
width: imageWidth -
_kImageMarginHorizental * 2 -
_kImagePaddingWidth * 2 -
_kImageBoarderWidth * 2,
),
);
});
}
}
class _KBChooser extends StatelessWidget {
final String kbLayoutType;
final double imageWidth;
final RxString choosedType;
final KBChoosedCallback cb;
const _KBChooser({
Key? key,
required this.kbLayoutType,
required this.imageWidth,
required this.choosedType,
required this.cb,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
TextButton(
onPressed: () {
choosedType.value = kbLayoutType;
},
child: _KBImage(
kbLayoutType: kbLayoutType,
imageWidth: imageWidth,
choosedType: choosedType,
),
style: TextButton.styleFrom(padding: EdgeInsets.zero),
),
TextButton(
child: Row(
children: [
Obx(() => Radio(
splashRadius: 0,
value: kbLayoutType,
groupValue: choosedType.value,
onChanged: (String? newValue) {
if (newValue != null) {
if (cb(newValue)) {
choosedType.value = newValue;
}
}
},
)),
Text(kbLayoutType),
],
),
onPressed: () {
if (cb(kbLayoutType)) {
choosedType.value = kbLayoutType;
}
},
),
],
);
}
}
class KBLayoutTypeChooser extends StatelessWidget {
final RxString choosedType;
final double width;
final double height;
final double dividerWidth;
final KBChoosedCallback cb;
KBLayoutTypeChooser({
Key? key,
required this.choosedType,
required this.width,
required this.height,
required this.dividerWidth,
required this.cb,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final imageWidth = width / 2 - dividerWidth;
return Container(
color: Colors.white,
child: SizedBox(
width: width,
height: height,
child: Center(
child: Row(
children: [
_KBChooser(
kbLayoutType: _kKBLayoutTypeISO,
imageWidth: imageWidth,
choosedType: choosedType,
cb: cb,
),
VerticalDivider(
width: dividerWidth * 2,
),
_KBChooser(
kbLayoutType: _kKBLayoutTypeNotISO,
imageWidth: imageWidth,
choosedType: choosedType,
cb: cb,
),
],
),
),
),
);
}
}
RxString KBLayoutType = ''.obs;
String getLocalPlatformForKBLayoutType(String peerPlatform) {
String localPlatform = '';
if (peerPlatform != 'Mac OS') {
return localPlatform;
}
if (Platform.isWindows) {
localPlatform = 'Windows';
} else if (Platform.isLinux) {
localPlatform = 'Linux';
}
// to-do: web desktop support ?
return localPlatform;
}
showKBLayoutTypeChooserIfNeeded(
String peerPlatform,
OverlayDialogManager dialogManager,
) async {
final localPlatform = getLocalPlatformForKBLayoutType(peerPlatform);
if (localPlatform == '') {
return;
}
KBLayoutType.value = bind.getLocalKbLayoutType();
if (KBLayoutType.value == _kKBLayoutTypeISO ||
KBLayoutType.value == _kKBLayoutTypeNotISO) {
return;
}
showKBLayoutTypeChooser(localPlatform, dialogManager);
}
showKBLayoutTypeChooser(
String localPlatform,
OverlayDialogManager dialogManager,
) {
dialogManager.show((setState, close) {
return CustomAlertDialog(
title:
Text('${translate('Select local keyboard type')} ($localPlatform)'),
content: KBLayoutTypeChooser(
choosedType: KBLayoutType,
width: 360,
height: 200,
dividerWidth: 4.0,
cb: (String v) {
bind.setLocalKbLayoutType(kbLayoutType: v);
KBLayoutType.value = bind.getLocalKbLayoutType();
return v == KBLayoutType.value;
}),
actions: [msgBoxButton(translate('Close'), close)],
onCancel: close,
);
});
}

View File

@ -9,7 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
final kMidButtonPadding = const EdgeInsets.fromLTRB(15, 0, 15, 0);
final _kMidButtonPadding = const EdgeInsets.fromLTRB(15, 0, 15, 0);
class _IconOP extends StatelessWidget {
final String icon;
@ -53,7 +53,7 @@ class ButtonOP extends StatelessWidget {
Expanded(
child: Container(
height: height,
padding: kMidButtonPadding,
padding: _kMidButtonPadding,
child: Obx(() => ElevatedButton(
style: ElevatedButton.styleFrom(
primary: curOP.value.isEmpty || curOP.value == op
@ -315,7 +315,7 @@ class LoginWidgetUserPass extends StatelessWidget {
height: 8.0,
),
Container(
padding: kMidButtonPadding,
padding: _kMidButtonPadding,
child: Row(
children: [
ConstrainedBox(
@ -343,7 +343,7 @@ class LoginWidgetUserPass extends StatelessWidget {
height: 8.0,
),
Container(
padding: kMidButtonPadding,
padding: _kMidButtonPadding,
child: Row(
children: [
ConstrainedBox(
@ -377,7 +377,7 @@ class LoginWidgetUserPass extends StatelessWidget {
Expanded(
child: Container(
height: 38,
padding: kMidButtonPadding,
padding: _kMidButtonPadding,
child: Obx(() => ElevatedButton(
style: curOP.value.isEmpty || curOP.value == 'rustdesk'
? null

View File

@ -22,6 +22,7 @@ import '../../models/platform_model.dart';
import '../../common/shared_state.dart';
import './popup_menu.dart';
import './material_mod_popup_menu.dart' as mod_menu;
import './kb_layout_type_chooser.dart';
class MenubarState {
final kStoreKey = 'remoteMenubarState';
@ -1187,7 +1188,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
}
List<MenuEntryBase<String>> _getKeyboardMenu() {
final keyboardMenu = [
final List<MenuEntryBase<String>> keyboardMenu = [
MenuEntryRadios<String>(
text: translate('Ratio'),
optionsGetter: () => [
@ -1203,7 +1204,55 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
},
)
];
final localPlatform =
getLocalPlatformForKBLayoutType(widget.ffi.ffiModel.pi.platform);
if (localPlatform != '') {
keyboardMenu.add(MenuEntryDivider());
keyboardMenu.add(
MenuEntryButton<String>(
childBuilder: (TextStyle? style) => Container(
alignment: AlignmentDirectional.center,
height: _MenubarTheme.height,
child: Row(
children: [
Obx(() => RichText(
text: TextSpan(
text: '${translate('Local keyboard type')}: ',
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: KBLayoutType.value,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
)),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Transform.scale(
scale: 0.8,
child: IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.settings),
onPressed: () {
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
showKBLayoutTypeChooser(
localPlatform, widget.ffi.dialogManager);
},
),
),
))
],
)),
proc: () {},
padding: EdgeInsets.zero,
dismissOnClicked: false,
),
);
}
return keyboardMenu;
}

View File

@ -381,12 +381,22 @@ class ImageModel with ChangeNotifier {
WeakReference<FFI> parent;
final List<Function(String)> _callbacksOnFirstImage = [];
ImageModel(this.parent);
addCallbackOnFirstImage(Function(String) cb) =>
_callbacksOnFirstImage.add(cb);
onRgba(Uint8List rgba) {
if (_waitForImage[id]!) {
_waitForImage[id] = false;
parent.target?.dialogManager.dismissAll();
if (isDesktop) {
for (final cb in _callbacksOnFirstImage) {
cb(id);
}
}
}
final pid = parent.target?.id;
ui.decodeImageFromPixels(

View File

@ -998,6 +998,8 @@ pub struct LocalConfig {
#[serde(default)]
remote_id: String, // latest used one
#[serde(default)]
kb_layout_type: String,
#[serde(default)]
size: Size,
#[serde(default)]
pub fav: Vec<String>,
@ -1017,6 +1019,14 @@ impl LocalConfig {
Config::store_(self, "_local");
}
pub fn get_kb_layout_type() -> String {
LOCAL_CONFIG.read().unwrap().kb_layout_type.clone()
}
pub fn set_kb_layout_type(kb_layout_type: String) {
LOCAL_CONFIG.write().unwrap().kb_layout_type = kb_layout_type
}
pub fn get_size() -> Size {
LOCAL_CONFIG.read().unwrap().size
}

View File

@ -181,6 +181,14 @@ pub fn set_local_flutter_config(k: String, v: String) {
ui_interface::set_local_flutter_config(k, v);
}
pub fn get_local_kb_layout_type() -> SyncReturn<String> {
SyncReturn(ui_interface::get_kb_layout_type())
}
pub fn set_local_kb_layout_type(kb_layout_type: String) {
ui_interface::set_kb_layout_type(kb_layout_type)
}
pub fn session_get_view_style(id: String) -> Option<String> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_view_style())

View File

@ -6,7 +6,7 @@ use crate::flutter::FlutterHandler;
#[cfg(not(feature = "flutter"))]
use crate::ui::remote::SciterHandler;
use crate::ui_session_interface::Session;
use hbb_common::{log, message_proto::*};
use hbb_common::{log, message_proto::*, config::LocalConfig};
use rdev::{Event, EventType, Key};
#[cfg(any(target_os = "windows", target_os = "macos"))]
use std::sync::atomic::{AtomicBool, Ordering};
@ -620,7 +620,13 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
#[cfg(target_os = "windows")]
let keycode = match peer.as_str() {
"windows" => event.scan_code,
"macos" => rdev::win_scancode_to_macos_code(event.scan_code)?,
"macos" => {
if LocalConfig::get_kb_layout_type() == "ISO" {
rdev::win_scancode_to_macos_iso_code(event.scan_code)?
} else {
rdev::win_scancode_to_macos_code(event.scan_code)?
}
},
_ => rdev::win_scancode_to_linux_code(event.scan_code)?,
};
#[cfg(target_os = "macos")]
@ -632,7 +638,13 @@ pub fn map_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Option<KeyEv
#[cfg(target_os = "linux")]
let keycode = match peer.as_str() {
"windows" => rdev::linux_code_to_win_scancode(event.code as _)?,
"macos" => rdev::linux_code_to_macos_code(event.code as _)?,
"macos" => {
if LocalConfig::get_kb_layout_type() == "ISO" {
rdev::linux_code_to_macos_iso_code(event.scan_code)?
} else {
rdev::linux_code_to_macos_code(event.code as _)?
}
},
_ => event.code as _,
};
#[cfg(any(target_os = "android", target_os = "ios"))]

View File

@ -407,5 +407,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Group", "小组"),
("Search", "搜索"),
("Closed manually by the web console", "被web控制台手动关闭"),
("Local keyboard type", "本地键盘类型"),
("Select local keyboard type", "请选择本地键盘类型"),
].iter().cloned().collect();
}

View File

@ -406,5 +406,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Group", "小組"),
("Search", "搜索"),
("Closed manually by the web console", "被web控制台手動關閉"),
("Local keyboard type", "本地鍵盤類型"),
("Select local keyboard type", "請選擇本地鍵盤類型"),
].iter().cloned().collect();
}

View File

@ -202,6 +202,16 @@ pub fn set_local_flutter_config(key: String, value: String) {
LocalConfig::set_flutter_config(key, value);
}
#[inline]
pub fn get_kb_layout_type() -> String {
LocalConfig::get_kb_layout_type()
}
#[inline]
pub fn set_kb_layout_type(kb_layout_type: String) {
LocalConfig::set_kb_layout_type(kb_layout_type);
}
#[inline]
pub fn peer_has_password(id: String) -> bool {
!PeerConfig::load(&id).password.is_empty()