Merge pull request #2658 from fufesou/feat/choose_keyboard_layout_type
Feat/choose keyboard layout type
This commit is contained in:
commit
efc12d036e
1
flutter/assets/kb_layout_iso.svg
Normal file
1
flutter/assets/kb_layout_iso.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261 253" width="261" height="253"><path fill="#0ff" d="m1 217c0-5.5 4.5-10 10-10h60c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-60c-5.5 0-10-4.5-10-10z"/><path fill="#487997" d="m89 216c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#c0ff00" d="m3 166c0-5.5 4.5-10 10-10h34c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-34c-5.5 0-10-4.5-10-10z"/><path fill="#00f" d="m63 166c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m140 215c0-5.5 4.5-10 10-10h26c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-26c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m190 215c0-5.5 4.5-10 10-10h26c5.5 0 10 4.5 10 10v28c0 5.5-4.5 10-10 10h-26c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m112 166c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m165 165c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m216 164c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m0 115c0-5.5 4.5-10 10-10h61c5.5 0 10 4.5 10 10v23c0 5.5-4.5 10-10 10h-61c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m90 116c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m139 116c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m191 115c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-23c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m2 62c0-5.5 4.5-10 10-10h50c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-50c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m79 62c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m131 64c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m182 63c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m0 10c0-5.5 4.5-10 10-10h28c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10h-28c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m54 11c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m105 12c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m156 13c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path d="m16.7 171.9v1.9q-1.1-0.5-2.1-0.8-1-0.2-1.9-0.2-1.7 0-2.5 0.6-0.9 0.6-0.9 1.8 0 0.9 0.6 1.4 0.6 0.5 2.2 0.8l1.2 0.3q2.2 0.4 3.2 1.4 1.1 1.1 1.1 2.9 0 2.1-1.4 3.2-1.5 1.1-4.2 1.1-1 0-2.2-0.3-1.2-0.2-2.4-0.6v-2.1q1.2 0.7 2.3 1 1.2 0.4 2.3 0.4 1.7 0 2.6-0.7 0.9-0.6 0.9-1.9 0-1.1-0.6-1.7-0.7-0.6-2.2-0.9l-1.2-0.2q-2.2-0.4-3.2-1.4-1-0.9-1-2.6 0-1.9 1.4-3 1.3-1.1 3.7-1.1 1.1 0 2.1 0.1 1.1 0.2 2.2 0.6zm13 7.5v6.6h-1.8v-6.5q0-1.6-0.6-2.4-0.6-0.7-1.8-0.7-1.5 0-2.3 0.9-0.9 0.9-0.9 2.5v6.2h-1.8v-15.2h1.8v6q0.7-1 1.5-1.5 0.9-0.5 2.1-0.5 1.8 0 2.8 1.2 1 1.1 1 3.4zm3.6 6.6v-10.9h1.8v10.9zm0-12.9v-2.3h1.7v2.3zm9.4-2.3h1.7v1.5h-1.7q-0.9 0-1.3 0.4-0.4 0.4-0.4 1.4v1h3v1.4h-3v9.5h-1.8v-9.5h-1.7v-1.4h1.7v-0.8q0-1.8 0.8-2.7 0.9-0.8 2.7-0.8zm2.9 1.2h1.8v3.1h3.7v1.4h-3.7v5.9q0 1.3 0.3 1.7 0.4 0.4 1.5 0.4h1.9v1.5h-1.9q-2.1 0-2.8-0.8-0.8-0.8-0.8-2.8v-5.9h-1.4v-1.4h1.4z"/></svg>
|
After Width: | Height: | Size: 3.4 KiB |
1
flutter/assets/kb_layout_not_iso.svg
Normal file
1
flutter/assets/kb_layout_not_iso.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 253" width="260" height="253"><path fill="#c0ff00" d="m0 167c0-5.5 4.5-10 10-10h90c5.5 0 10 4.5 10 10v23c0 5.5-4.5 10-10 10h-90c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m3 63c0-5.5 4.5-10 10-10h46c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-46c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m1 114c0-5.5 4.5-10 10-10h62c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-62c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m114 166c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v24c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m90 117c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v22c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m81 64c0-5.5 4.5-10 10-10h22c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-22c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m54 10c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m2 10c0-5.5 4.5-10 10-10h26c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10h-26c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m2 216c0-5.5 4.5-10 10-10h59c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-59c-5.5 0-10-4.5-10-10z"/><path fill="#487997" d="m89 217c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-23c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m141 217c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#0ff" d="m191 216c0-5.5 4.5-10 10-10h23c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-23c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m166 164c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v27c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m215 165c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m138 111c0-5.5 4.5-10 10-10h28c5.5 0 10 4.5 10 10v29c0 5.5-4.5 10-10 10h-28c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m192 112c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v29c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m129 64c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m182 62c0-5.5 4.5-10 10-10h25c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-25c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m105 11c0-5.5 4.5-10 10-10h24c5.5 0 10 4.5 10 10v26c0 5.5-4.5 10-10 10h-24c-5.5 0-10-4.5-10-10z"/><path fill="#00ffa8" d="m154 12c0-5.5 4.5-10 10-10h27c5.5 0 10 4.5 10 10v25c0 5.5-4.5 10-10 10h-27c-5.5 0-10-4.5-10-10z"/><path d="m42.7 172.9v1.9q-1.1-0.5-2.1-0.8-1-0.2-1.9-0.2-1.7 0-2.5 0.6-0.9 0.6-0.9 1.8 0 0.9 0.6 1.4 0.6 0.5 2.2 0.8l1.2 0.3q2.2 0.4 3.2 1.4 1.1 1.1 1.1 2.9 0 2.1-1.4 3.2-1.5 1.1-4.2 1.1-1 0-2.2-0.3-1.2-0.2-2.4-0.6v-2.1q1.2 0.7 2.3 1 1.2 0.4 2.3 0.4 1.7 0 2.6-0.7 0.9-0.6 0.9-1.9 0-1.1-0.6-1.7-0.7-0.6-2.2-0.9l-1.2-0.2q-2.2-0.4-3.2-1.4-1-0.9-1-2.6 0-1.9 1.4-3 1.3-1.1 3.7-1.1 1.1 0 2.1 0.1 1.1 0.2 2.2 0.6zm13 7.5v6.6h-1.8v-6.5q0-1.6-0.6-2.4-0.6-0.7-1.8-0.7-1.5 0-2.3 0.9-0.9 0.9-0.9 2.5v6.2h-1.8v-15.2h1.8v6q0.7-1 1.5-1.5 0.9-0.5 2.1-0.5 1.8 0 2.8 1.2 1 1.1 1 3.4zm3.6 6.6v-10.9h1.8v10.9zm0-12.9v-2.3h1.7v2.3zm9.4-2.3h1.7v1.5h-1.7q-0.9 0-1.3 0.4-0.4 0.4-0.4 1.4v1h3v1.4h-3v9.5h-1.8v-9.5h-1.7v-1.4h1.7v-0.8q0-1.8 0.8-2.7 0.9-0.8 2.7-0.8zm2.9 1.2h1.8v3.1h3.7v1.4h-3.7v5.9q0 1.3 0.3 1.7 0.4 0.4 1.5 0.4h1.9v1.5h-1.9q-2.1 0-2.8-0.8-0.8-0.8-0.8-2.8v-5.9h-1.4v-1.4h1.4z"/></svg>
|
After Width: | Height: | Size: 3.3 KiB |
@ -23,6 +23,7 @@ import '../../models/model.dart';
|
|||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../common/shared_state.dart';
|
import '../../common/shared_state.dart';
|
||||||
import '../widgets/remote_menubar.dart';
|
import '../widgets/remote_menubar.dart';
|
||||||
|
import '../widgets/kb_layout_type_chooser.dart';
|
||||||
|
|
||||||
bool _isCustomCursorInited = false;
|
bool _isCustomCursorInited = false;
|
||||||
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
|
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
|
||||||
@ -95,6 +96,10 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
_initStates(widget.id);
|
_initStates(widget.id);
|
||||||
_ffi = FFI();
|
_ffi = FFI();
|
||||||
Get.put(_ffi, tag: widget.id);
|
Get.put(_ffi, tag: widget.id);
|
||||||
|
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
|
||||||
|
showKBLayoutTypeChooserIfNeeded(
|
||||||
|
_ffi.ffiModel.pi.platform, _ffi.dialogManager);
|
||||||
|
});
|
||||||
_ffi.start(widget.id);
|
_ffi.start(widget.id);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||||
|
224
flutter/lib/desktop/widgets/kb_layout_type_chooser.dart
Normal file
224
flutter/lib/desktop/widgets/kb_layout_type_chooser.dart
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
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 = Future<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) {
|
||||||
|
onChanged(String? v) async {
|
||||||
|
if (v != null) {
|
||||||
|
if (await cb(v)) {
|
||||||
|
choosedType.value = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
onChanged(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: onChanged,
|
||||||
|
)),
|
||||||
|
Text(kbLayoutType),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
onChanged(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 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) async {
|
||||||
|
await bind.setLocalKbLayoutType(kbLayoutType: v);
|
||||||
|
KBLayoutType.value = bind.getLocalKbLayoutType();
|
||||||
|
return v == KBLayoutType.value;
|
||||||
|
}),
|
||||||
|
actions: [msgBoxButton(translate('Close'), close)],
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -9,7 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
|
|
||||||
import '../../common.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 {
|
class _IconOP extends StatelessWidget {
|
||||||
final String icon;
|
final String icon;
|
||||||
@ -53,7 +53,7 @@ class ButtonOP extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: height,
|
height: height,
|
||||||
padding: kMidButtonPadding,
|
padding: _kMidButtonPadding,
|
||||||
child: Obx(() => ElevatedButton(
|
child: Obx(() => ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
primary: curOP.value.isEmpty || curOP.value == op
|
primary: curOP.value.isEmpty || curOP.value == op
|
||||||
@ -315,7 +315,7 @@ class LoginWidgetUserPass extends StatelessWidget {
|
|||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: kMidButtonPadding,
|
padding: _kMidButtonPadding,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
@ -343,7 +343,7 @@ class LoginWidgetUserPass extends StatelessWidget {
|
|||||||
height: 8.0,
|
height: 8.0,
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: kMidButtonPadding,
|
padding: _kMidButtonPadding,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
@ -377,7 +377,7 @@ class LoginWidgetUserPass extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 38,
|
height: 38,
|
||||||
padding: kMidButtonPadding,
|
padding: _kMidButtonPadding,
|
||||||
child: Obx(() => ElevatedButton(
|
child: Obx(() => ElevatedButton(
|
||||||
style: curOP.value.isEmpty || curOP.value == 'rustdesk'
|
style: curOP.value.isEmpty || curOP.value == 'rustdesk'
|
||||||
? null
|
? null
|
||||||
|
@ -22,6 +22,7 @@ import '../../models/platform_model.dart';
|
|||||||
import '../../common/shared_state.dart';
|
import '../../common/shared_state.dart';
|
||||||
import './popup_menu.dart';
|
import './popup_menu.dart';
|
||||||
import './material_mod_popup_menu.dart' as mod_menu;
|
import './material_mod_popup_menu.dart' as mod_menu;
|
||||||
|
import './kb_layout_type_chooser.dart';
|
||||||
|
|
||||||
class MenubarState {
|
class MenubarState {
|
||||||
final kStoreKey = 'remoteMenubarState';
|
final kStoreKey = 'remoteMenubarState';
|
||||||
@ -1187,7 +1188,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<MenuEntryBase<String>> _getKeyboardMenu() {
|
List<MenuEntryBase<String>> _getKeyboardMenu() {
|
||||||
final keyboardMenu = [
|
final List<MenuEntryBase<String>> keyboardMenu = [
|
||||||
MenuEntryRadios<String>(
|
MenuEntryRadios<String>(
|
||||||
text: translate('Ratio'),
|
text: translate('Ratio'),
|
||||||
optionsGetter: () => [
|
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;
|
return keyboardMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,12 +381,22 @@ class ImageModel with ChangeNotifier {
|
|||||||
|
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
|
final List<Function(String)> _callbacksOnFirstImage = [];
|
||||||
|
|
||||||
ImageModel(this.parent);
|
ImageModel(this.parent);
|
||||||
|
|
||||||
|
addCallbackOnFirstImage(Function(String) cb) =>
|
||||||
|
_callbacksOnFirstImage.add(cb);
|
||||||
|
|
||||||
onRgba(Uint8List rgba) {
|
onRgba(Uint8List rgba) {
|
||||||
if (_waitForImage[id]!) {
|
if (_waitForImage[id]!) {
|
||||||
_waitForImage[id] = false;
|
_waitForImage[id] = false;
|
||||||
parent.target?.dialogManager.dismissAll();
|
parent.target?.dialogManager.dismissAll();
|
||||||
|
if (isDesktop) {
|
||||||
|
for (final cb in _callbacksOnFirstImage) {
|
||||||
|
cb(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final pid = parent.target?.id;
|
final pid = parent.target?.id;
|
||||||
ui.decodeImageFromPixels(
|
ui.decodeImageFromPixels(
|
||||||
|
@ -998,6 +998,8 @@ pub struct LocalConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
remote_id: String, // latest used one
|
remote_id: String, // latest used one
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
kb_layout_type: String,
|
||||||
|
#[serde(default)]
|
||||||
size: Size,
|
size: Size,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub fav: Vec<String>,
|
pub fav: Vec<String>,
|
||||||
@ -1017,6 +1019,16 @@ impl LocalConfig {
|
|||||||
Config::store_(self, "_local");
|
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) {
|
||||||
|
let mut config = LOCAL_CONFIG.write().unwrap();
|
||||||
|
config.kb_layout_type = kb_layout_type;
|
||||||
|
config.store();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_size() -> Size {
|
pub fn get_size() -> Size {
|
||||||
LOCAL_CONFIG.read().unwrap().size
|
LOCAL_CONFIG.read().unwrap().size
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,14 @@ pub fn set_local_flutter_config(k: String, v: String) {
|
|||||||
ui_interface::set_local_flutter_config(k, v);
|
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> {
|
pub fn session_get_view_style(id: String) -> Option<String> {
|
||||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||||
Some(session.get_view_style())
|
Some(session.get_view_style())
|
||||||
|
@ -6,7 +6,7 @@ use crate::flutter::FlutterHandler;
|
|||||||
#[cfg(not(feature = "flutter"))]
|
#[cfg(not(feature = "flutter"))]
|
||||||
use crate::ui::remote::SciterHandler;
|
use crate::ui::remote::SciterHandler;
|
||||||
use crate::ui_session_interface::Session;
|
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};
|
use rdev::{Event, EventType, Key};
|
||||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
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")]
|
#[cfg(target_os = "windows")]
|
||||||
let keycode = match peer.as_str() {
|
let keycode = match peer.as_str() {
|
||||||
"windows" => event.scan_code,
|
"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)?,
|
_ => rdev::win_scancode_to_linux_code(event.scan_code)?,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "macos")]
|
#[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")]
|
#[cfg(target_os = "linux")]
|
||||||
let keycode = match peer.as_str() {
|
let keycode = match peer.as_str() {
|
||||||
"windows" => rdev::linux_code_to_win_scancode(event.code as _)?,
|
"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 _,
|
_ => event.code as _,
|
||||||
};
|
};
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
|
@ -407,5 +407,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Group", "小组"),
|
("Group", "小组"),
|
||||||
("Search", "搜索"),
|
("Search", "搜索"),
|
||||||
("Closed manually by the web console", "被web控制台手动关闭"),
|
("Closed manually by the web console", "被web控制台手动关闭"),
|
||||||
|
("Local keyboard type", "本地键盘类型"),
|
||||||
|
("Select local keyboard type", "请选择本地键盘类型"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -406,5 +406,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Group", "小組"),
|
("Group", "小組"),
|
||||||
("Search", "搜索"),
|
("Search", "搜索"),
|
||||||
("Closed manually by the web console", "被web控制台手動關閉"),
|
("Closed manually by the web console", "被web控制台手動關閉"),
|
||||||
|
("Local keyboard type", "本地鍵盤類型"),
|
||||||
|
("Select local keyboard type", "請選擇本地鍵盤類型"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -202,6 +202,16 @@ pub fn set_local_flutter_config(key: String, value: String) {
|
|||||||
LocalConfig::set_flutter_config(key, value);
|
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]
|
#[inline]
|
||||||
pub fn peer_has_password(id: String) -> bool {
|
pub fn peer_has_password(id: String) -> bool {
|
||||||
!PeerConfig::load(&id).password.is_empty()
|
!PeerConfig::load(&id).password.is_empty()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user