native style
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
4faf0a3d35
commit
a10487c840
BIN
flutter/assets/tabbar.ttf
Normal file
BIN
flutter/assets/tabbar.ttf
Normal file
Binary file not shown.
@ -8,7 +8,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart';
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:get/instance_manager.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@ -38,6 +38,88 @@ final iconAudio = MemoryImage(Uint8List.fromList(base64Decode(
|
|||||||
final iconFile = MemoryImage(Uint8List.fromList(base64Decode(
|
final iconFile = MemoryImage(Uint8List.fromList(base64Decode(
|
||||||
'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg==')));
|
'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg==')));
|
||||||
|
|
||||||
|
class IconFont {
|
||||||
|
static const _family = 'iconfont';
|
||||||
|
IconFont._();
|
||||||
|
|
||||||
|
static const IconData max = IconData(0xe606, fontFamily: _family);
|
||||||
|
static const IconData restore = IconData(0xe607, fontFamily: _family);
|
||||||
|
static const IconData close = IconData(0xe668, fontFamily: _family);
|
||||||
|
static const IconData min = IconData(0xe609, fontFamily: _family);
|
||||||
|
static const IconData add = IconData(0xe664, fontFamily: _family);
|
||||||
|
static const IconData menu = IconData(0xe628, fontFamily: _family);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||||
|
const ColorThemeExtension({
|
||||||
|
required this.bg,
|
||||||
|
required this.grayBg,
|
||||||
|
required this.text,
|
||||||
|
required this.lightText,
|
||||||
|
required this.lighterText,
|
||||||
|
required this.border,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Color? bg;
|
||||||
|
final Color? grayBg;
|
||||||
|
final Color? text;
|
||||||
|
final Color? lightText;
|
||||||
|
final Color? lighterText;
|
||||||
|
final Color? border;
|
||||||
|
|
||||||
|
static const light = ColorThemeExtension(
|
||||||
|
bg: Color(0xFFFFFFFF),
|
||||||
|
grayBg: Color(0xFFEEEEEE),
|
||||||
|
text: Color(0xFF222222),
|
||||||
|
lightText: Color(0xFF666666),
|
||||||
|
lighterText: Color(0xFF888888),
|
||||||
|
border: Color(0xFFCCCCCC),
|
||||||
|
);
|
||||||
|
|
||||||
|
static const dark = ColorThemeExtension(
|
||||||
|
bg: Color(0xFF252525),
|
||||||
|
grayBg: Color(0xFF141414),
|
||||||
|
text: Color(0xFFFFFFFF),
|
||||||
|
lightText: Color(0xFF999999),
|
||||||
|
lighterText: Color(0xFF777777),
|
||||||
|
border: Color(0xFF555555),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ThemeExtension<ColorThemeExtension> copyWith(
|
||||||
|
{Color? bg,
|
||||||
|
Color? grayBg,
|
||||||
|
Color? text,
|
||||||
|
Color? lightText,
|
||||||
|
Color? lighterText,
|
||||||
|
Color? border}) {
|
||||||
|
return ColorThemeExtension(
|
||||||
|
bg: bg ?? this.bg,
|
||||||
|
grayBg: grayBg ?? this.grayBg,
|
||||||
|
text: text ?? this.text,
|
||||||
|
lightText: lightText ?? this.lightText,
|
||||||
|
lighterText: lighterText ?? this.lighterText,
|
||||||
|
border: border ?? this.border,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ThemeExtension<ColorThemeExtension> lerp(
|
||||||
|
ThemeExtension<ColorThemeExtension>? other, double t) {
|
||||||
|
if (other is! ColorThemeExtension) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return ColorThemeExtension(
|
||||||
|
bg: Color.lerp(bg, other.bg, t),
|
||||||
|
grayBg: Color.lerp(grayBg, other.grayBg, t),
|
||||||
|
text: Color.lerp(text, other.text, t),
|
||||||
|
lightText: Color.lerp(lightText, other.lightText, t),
|
||||||
|
lighterText: Color.lerp(lighterText, other.lighterText, t),
|
||||||
|
border: Color.lerp(border, other.border, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyTheme {
|
class MyTheme {
|
||||||
MyTheme._();
|
MyTheme._();
|
||||||
|
|
||||||
@ -52,20 +134,37 @@ class MyTheme {
|
|||||||
static const Color darkGray = Color(0xFFB9BABC);
|
static const Color darkGray = Color(0xFFB9BABC);
|
||||||
static const Color cmIdColor = Color(0xFF21790B);
|
static const Color cmIdColor = Color(0xFF21790B);
|
||||||
static const Color dark = Colors.black87;
|
static const Color dark = Colors.black87;
|
||||||
static const Color disabledTextLight = Color(0xFF888888);
|
|
||||||
static const Color disabledTextDark = Color(0xFF777777);
|
|
||||||
|
|
||||||
static ThemeData lightTheme = ThemeData(
|
static ThemeData lightTheme = ThemeData(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
tabBarTheme: TabBarTheme(labelColor: Colors.black87),
|
tabBarTheme: TabBarTheme(
|
||||||
|
labelColor: Colors.black87,
|
||||||
|
),
|
||||||
|
// backgroundColor: Color(0xFFFFFFFF),
|
||||||
|
).copyWith(
|
||||||
|
extensions: <ThemeExtension<dynamic>>[
|
||||||
|
ColorThemeExtension.light,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
static ThemeData darkTheme = ThemeData(
|
static ThemeData darkTheme = ThemeData(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
tabBarTheme: TabBarTheme(labelColor: Colors.white70));
|
tabBarTheme: TabBarTheme(
|
||||||
|
labelColor: Colors.white70,
|
||||||
|
),
|
||||||
|
// backgroundColor: Color(0xFF252525)
|
||||||
|
).copyWith(
|
||||||
|
extensions: <ThemeExtension<dynamic>>[
|
||||||
|
ColorThemeExtension.dark,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
static ColorThemeExtension color(BuildContext context) {
|
||||||
|
return Theme.of(context).extension<ColorThemeExtension>()!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDarkTheme() {
|
bool isDarkTheme() {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const double kDesktopRemoteTabBarHeight = 48.0;
|
const double kDesktopRemoteTabBarHeight = 28.0;
|
||||||
const String kAppTypeMain = "main";
|
const String kAppTypeMain = "main";
|
||||||
const String kAppTypeDesktopRemote = "remote";
|
const String kAppTypeDesktopRemote = "remote";
|
||||||
const String kAppTypeDesktopFileTransfer = "file transfer";
|
const String kAppTypeDesktopFileTransfer = "file transfer";
|
||||||
|
@ -48,15 +48,16 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
buildServerInfo(context),
|
||||||
child: buildServerInfo(context),
|
VerticalDivider(
|
||||||
flex: 1,
|
width: 1,
|
||||||
|
thickness: 1,
|
||||||
),
|
),
|
||||||
Flexible(
|
Expanded(
|
||||||
child: buildServerBoard(context),
|
child: buildServerBoard(context),
|
||||||
flex: 4,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -66,6 +67,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: gFFI.serverModel,
|
value: gFFI.serverModel,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
width: 200,
|
||||||
|
color: MyTheme.color(context).bg,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
buildTip(context),
|
buildTip(context),
|
||||||
@ -78,44 +81,48 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildServerBoard(BuildContext context) {
|
buildServerBoard(BuildContext context) {
|
||||||
return Column(
|
return Container(
|
||||||
children: [
|
color: MyTheme.color(context).grayBg,
|
||||||
Expanded(child: ConnectionPage()),
|
child: ConnectionPage(),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildIDBoard(BuildContext context) {
|
buildIDBoard(BuildContext context) {
|
||||||
final model = gFFI.serverModel;
|
final model = gFFI.serverModel;
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
height: 52,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 3,
|
width: 2,
|
||||||
height: 70,
|
|
||||||
decoration: BoxDecoration(color: MyTheme.accent),
|
decoration: BoxDecoration(color: MyTheme.accent),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Container(
|
||||||
|
height: 15,
|
||||||
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
translate("ID"),
|
translate("ID"),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18, fontWeight: FontWeight.w500),
|
fontSize: 14,
|
||||||
|
color: MyTheme.color(context).lightText),
|
||||||
),
|
),
|
||||||
buildPopupMenu(context)
|
buildPopupMenu(context)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
GestureDetector(
|
),
|
||||||
|
Flexible(
|
||||||
|
child: GestureDetector(
|
||||||
onDoubleTap: () {
|
onDoubleTap: () {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
ClipboardData(text: model.serverId.text));
|
ClipboardData(text: model.serverId.text));
|
||||||
@ -124,7 +131,15 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: model.serverId,
|
controller: model.serverId,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
)),
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
),
|
||||||
|
).marginOnly(bottom: 5),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -136,7 +151,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
|
|
||||||
Widget buildPopupMenu(BuildContext context) {
|
Widget buildPopupMenu(BuildContext context) {
|
||||||
var position;
|
var position;
|
||||||
return GestureDetector(
|
RxBool hover = false.obs;
|
||||||
|
return InkWell(
|
||||||
onTapDown: (detail) {
|
onTapDown: (detail) {
|
||||||
final x = detail.globalPosition.dx;
|
final x = detail.globalPosition.dx;
|
||||||
final y = detail.globalPosition.dy;
|
final y = detail.globalPosition.dy;
|
||||||
@ -221,31 +237,57 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
onSelectMenu(v);
|
onSelectMenu(v);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Icon(Icons.more_vert_outlined));
|
child: Obx(
|
||||||
|
() => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(90),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: hover.value
|
||||||
|
? MyTheme.color(context).grayBg!
|
||||||
|
: MyTheme.color(context).bg!,
|
||||||
|
spreadRadius: 2)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.more_vert_outlined,
|
||||||
|
size: 20,
|
||||||
|
color: hover.value
|
||||||
|
? MyTheme.color(context).text
|
||||||
|
: MyTheme.color(context).lightText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onHover: (value) => hover.value = value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPasswordBoard(BuildContext context) {
|
buildPasswordBoard(BuildContext context) {
|
||||||
final model = gFFI.serverModel;
|
final model = gFFI.serverModel;
|
||||||
|
RxBool refreshHover = false.obs;
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.symmetric(vertical: 4.0, horizontal: 16.0),
|
margin: EdgeInsets.symmetric(vertical: 12, horizontal: 16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 3,
|
width: 2,
|
||||||
height: 70,
|
height: 52,
|
||||||
decoration: BoxDecoration(color: MyTheme.accent),
|
decoration: BoxDecoration(color: MyTheme.accent),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
translate("Password"),
|
translate("Password"),
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
|
style: TextStyle(
|
||||||
|
fontSize: 14, color: MyTheme.color(context).lightText),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -262,12 +304,25 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: model.serverPasswd,
|
controller: model.serverPasswd,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 15),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
InkWell(
|
||||||
icon: Icon(Icons.refresh),
|
child: Obx(
|
||||||
onPressed: () => bind.mainUpdateTemporaryPassword(),
|
() => Icon(
|
||||||
|
Icons.refresh,
|
||||||
|
color: refreshHover.value
|
||||||
|
? MyTheme.color(context).text
|
||||||
|
: Color(0xFFDDDDDD),
|
||||||
|
size: 22,
|
||||||
|
).marginOnly(right: 5),
|
||||||
|
),
|
||||||
|
onTap: () => bind.mainUpdateTemporaryPassword(),
|
||||||
|
onHover: (value) => refreshHover.value = value,
|
||||||
),
|
),
|
||||||
FutureBuilder<Widget>(
|
FutureBuilder<Widget>(
|
||||||
future: buildPasswordPopupMenu(context),
|
future: buildPasswordPopupMenu(context),
|
||||||
@ -282,7 +337,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
).marginOnly(bottom: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -294,7 +349,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
|
|
||||||
Future<Widget> buildPasswordPopupMenu(BuildContext context) async {
|
Future<Widget> buildPasswordPopupMenu(BuildContext context) async {
|
||||||
var position;
|
var position;
|
||||||
return GestureDetector(
|
RxBool editHover = false.obs;
|
||||||
|
return InkWell(
|
||||||
onTapDown: (detail) {
|
onTapDown: (detail) {
|
||||||
final x = detail.globalPosition.dx;
|
final x = detail.globalPosition.dx;
|
||||||
final y = detail.globalPosition.dy;
|
final y = detail.globalPosition.dy;
|
||||||
@ -365,7 +421,12 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
setPasswordDialog();
|
setPasswordDialog();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Icon(Icons.edit));
|
onHover: (value) => editHover.value = value,
|
||||||
|
child: Obx(() => Icon(Icons.edit,
|
||||||
|
size: 22,
|
||||||
|
color: editHover.value
|
||||||
|
? MyTheme.color(context).text
|
||||||
|
: Color(0xFFDDDDDD))));
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTip(BuildContext context) {
|
buildTip(BuildContext context) {
|
||||||
@ -377,7 +438,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
translate("Your Desktop"),
|
translate("Your Desktop"),
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.normal, fontSize: 19),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 8.0,
|
height: 8.0,
|
||||||
@ -385,7 +446,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
Text(
|
Text(
|
||||||
translate("desk_tip"),
|
translate("desk_tip"),
|
||||||
overflow: TextOverflow.clip,
|
overflow: TextOverflow.clip,
|
||||||
style: TextStyle(fontSize: 14),
|
style: TextStyle(
|
||||||
|
fontSize: 12, color: MyTheme.color(context).lighterText),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -394,13 +456,17 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
|
|
||||||
buildControlPanel(BuildContext context) {
|
buildControlPanel(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
width: 320,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(10), color: MyTheme.white),
|
borderRadius: BorderRadius.circular(10), color: MyTheme.white),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(translate("Control Remote Desktop")),
|
Text(
|
||||||
|
translate("Control Remote Desktop"),
|
||||||
|
style: TextStyle(fontWeight: FontWeight.normal, fontSize: 19),
|
||||||
|
),
|
||||||
Form(
|
Form(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -409,6 +475,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
FilteringTextInputFormatter.allow(RegExp(r"[0-9]"))
|
FilteringTextInputFormatter.allow(RegExp(r"[0-9]"))
|
||||||
],
|
],
|
||||||
|
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w400),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
|
@ -71,6 +71,7 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: Row(
|
body: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
@ -88,6 +89,8 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
),
|
),
|
||||||
const VerticalDivider(thickness: 1, width: 1),
|
const VerticalDivider(thickness: 1, width: 1),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
color: MyTheme.color(context).grayBg,
|
||||||
child: PageView(
|
child: PageView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
children: [
|
children: [
|
||||||
@ -99,6 +102,7 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
_About(),
|
_About(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -269,8 +273,8 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
AbsorbPointer(
|
AbsorbPointer(
|
||||||
absorbing: locked,
|
absorbing: locked,
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
permissions(),
|
permissions(context),
|
||||||
password(),
|
password(context),
|
||||||
whitelist(),
|
whitelist(),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
@ -280,24 +284,26 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
).marginOnly(bottom: _kListViewBottomMargin);
|
).marginOnly(bottom: _kListViewBottomMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget permissions() {
|
Widget permissions(context) {
|
||||||
bool enabled = !locked;
|
bool enabled = !locked;
|
||||||
return _Card(title: 'Permissions', children: [
|
return _Card(title: 'Permissions', children: [
|
||||||
_OptionCheckBox('Enable Keyboard/Mouse', 'enable-keyboard',
|
_OptionCheckBox(context, 'Enable Keyboard/Mouse', 'enable-keyboard',
|
||||||
enabled: enabled),
|
enabled: enabled),
|
||||||
_OptionCheckBox('Enable Clipboard', 'enable-clipboard', enabled: enabled),
|
_OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard',
|
||||||
_OptionCheckBox('Enable File Transfer', 'enable-file-transfer',
|
|
||||||
enabled: enabled),
|
enabled: enabled),
|
||||||
_OptionCheckBox('Enable Audio', 'enable-audio', enabled: enabled),
|
_OptionCheckBox(context, 'Enable File Transfer', 'enable-file-transfer',
|
||||||
_OptionCheckBox('Enable Remote Restart', 'enable-remote-restart',
|
|
||||||
enabled: enabled),
|
enabled: enabled),
|
||||||
_OptionCheckBox('Enable remote configuration modification',
|
_OptionCheckBox(context, 'Enable Audio', 'enable-audio',
|
||||||
|
enabled: enabled),
|
||||||
|
_OptionCheckBox(context, 'Enable Remote Restart', 'enable-remote-restart',
|
||||||
|
enabled: enabled),
|
||||||
|
_OptionCheckBox(context, 'Enable remote configuration modification',
|
||||||
'allow-remote-config-modification',
|
'allow-remote-config-modification',
|
||||||
enabled: enabled),
|
enabled: enabled),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget password() {
|
Widget password(BuildContext context) {
|
||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: gFFI.serverModel,
|
value: gFFI.serverModel,
|
||||||
child: Consumer<ServerModel>(builder: ((context, model, child) {
|
child: Consumer<ServerModel>(builder: ((context, model, child) {
|
||||||
@ -316,6 +322,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
String currentValue = values[keys.indexOf(model.verificationMethod)];
|
String currentValue = values[keys.indexOf(model.verificationMethod)];
|
||||||
List<Widget> radios = values
|
List<Widget> radios = values
|
||||||
.map((value) => _Radio<String>(
|
.map((value) => _Radio<String>(
|
||||||
|
context,
|
||||||
value: value,
|
value: value,
|
||||||
groupValue: currentValue,
|
groupValue: currentValue,
|
||||||
label: value,
|
label: value,
|
||||||
@ -343,7 +350,8 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _disabledTextColor(onChanged != null)),
|
color: _disabledTextColor(
|
||||||
|
context, onChanged != null)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 10),
|
).paddingSymmetric(horizontal: 10),
|
||||||
@ -408,7 +416,7 @@ class _ConnectionState extends State<_Connection>
|
|||||||
_Button('ID/Relay Server', changeServer, enabled: enabled),
|
_Button('ID/Relay Server', changeServer, enabled: enabled),
|
||||||
]),
|
]),
|
||||||
_Card(title: 'Service', children: [
|
_Card(title: 'Service', children: [
|
||||||
_OptionCheckBox('Enable Service', 'stop-service',
|
_OptionCheckBox(context, 'Enable Service', 'stop-service',
|
||||||
reverse: true, enabled: enabled),
|
reverse: true, enabled: enabled),
|
||||||
// TODO: Not implemented
|
// TODO: Not implemented
|
||||||
// _option_check('Always connected via relay', 'allow-always-relay', enabled: enabled),
|
// _option_check('Always connected via relay', 'allow-always-relay', enabled: enabled),
|
||||||
@ -416,10 +424,11 @@ class _ConnectionState extends State<_Connection>
|
|||||||
// reverse: true, enabled: enabled),
|
// reverse: true, enabled: enabled),
|
||||||
]),
|
]),
|
||||||
_Card(title: 'TCP Tunneling', children: [
|
_Card(title: 'TCP Tunneling', children: [
|
||||||
_OptionCheckBox('Enable TCP Tunneling', 'enable-tunnel',
|
_OptionCheckBox(
|
||||||
|
context, 'Enable TCP Tunneling', 'enable-tunnel',
|
||||||
enabled: enabled),
|
enabled: enabled),
|
||||||
]),
|
]),
|
||||||
direct_ip(),
|
direct_ip(context),
|
||||||
_Card(title: 'Proxy', children: [
|
_Card(title: 'Proxy', children: [
|
||||||
_Button('Socks5 Proxy', changeSocks5Proxy, enabled: enabled),
|
_Button('Socks5 Proxy', changeSocks5Proxy, enabled: enabled),
|
||||||
]),
|
]),
|
||||||
@ -430,12 +439,12 @@ class _ConnectionState extends State<_Connection>
|
|||||||
]).marginOnly(bottom: _kListViewBottomMargin);
|
]).marginOnly(bottom: _kListViewBottomMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget direct_ip() {
|
Widget direct_ip(BuildContext context) {
|
||||||
TextEditingController controller = TextEditingController();
|
TextEditingController controller = TextEditingController();
|
||||||
var update = () => setState(() {});
|
var update = () => setState(() {});
|
||||||
RxBool apply_enabled = false.obs;
|
RxBool apply_enabled = false.obs;
|
||||||
return _Card(title: 'Direct IP Access', children: [
|
return _Card(title: 'Direct IP Access', children: [
|
||||||
_OptionCheckBox('Enable Direct IP Access', 'direct-server',
|
_OptionCheckBox(context, 'Enable Direct IP Access', 'direct-server',
|
||||||
update: update, enabled: !locked),
|
update: update, enabled: !locked),
|
||||||
_futureBuilder(
|
_futureBuilder(
|
||||||
future: () async {
|
future: () async {
|
||||||
@ -509,7 +518,7 @@ class _DisplayState extends State<_Display> with AutomaticKeepAliveClientMixin {
|
|||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
_Card(title: 'Adaptive Bitrate', children: [
|
_Card(title: 'Adaptive Bitrate', children: [
|
||||||
_OptionCheckBox('Adaptive Bitrate', 'enable-abr'),
|
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'),
|
||||||
]),
|
]),
|
||||||
hwcodec(),
|
hwcodec(),
|
||||||
],
|
],
|
||||||
@ -523,7 +532,8 @@ class _DisplayState extends State<_Display> with AutomaticKeepAliveClientMixin {
|
|||||||
return Offstage(
|
return Offstage(
|
||||||
offstage: !(data as bool),
|
offstage: !(data as bool),
|
||||||
child: _Card(title: 'Hardware Codec', children: [
|
child: _Card(title: 'Hardware Codec', children: [
|
||||||
_OptionCheckBox('Enable hardware codec', 'enable-hwcodec'),
|
_OptionCheckBox(
|
||||||
|
context, 'Enable hardware codec', 'enable-hwcodec'),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -592,6 +602,7 @@ class _AudioState extends State<_Audio> with AutomaticKeepAliveClientMixin {
|
|||||||
);
|
);
|
||||||
deviceWidget.addAll([
|
deviceWidget.addAll([
|
||||||
_Radio<_AudioInputType>(
|
_Radio<_AudioInputType>(
|
||||||
|
context,
|
||||||
value: _AudioInputType.Specify,
|
value: _AudioInputType.Specify,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'Specify device',
|
label: 'Specify device',
|
||||||
@ -606,6 +617,7 @@ class _AudioState extends State<_Audio> with AutomaticKeepAliveClientMixin {
|
|||||||
}
|
}
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
_Radio<_AudioInputType>(
|
_Radio<_AudioInputType>(
|
||||||
|
context,
|
||||||
value: _AudioInputType.Mute,
|
value: _AudioInputType.Mute,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'Mute',
|
label: 'Mute',
|
||||||
@ -615,6 +627,7 @@ class _AudioState extends State<_Audio> with AutomaticKeepAliveClientMixin {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
_Radio(
|
_Radio(
|
||||||
|
context,
|
||||||
value: _AudioInputType.Standard,
|
value: _AudioInputType.Standard,
|
||||||
groupValue: groupValue,
|
groupValue: groupValue,
|
||||||
label: 'Use standard device',
|
label: 'Use standard device',
|
||||||
@ -743,15 +756,11 @@ Widget _Card({required String title, required List<Widget> children}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color? _disabledTextColor(bool enabled) {
|
Color? _disabledTextColor(BuildContext context, bool enabled) {
|
||||||
return enabled
|
return enabled ? null : MyTheme.color(context).lighterText;
|
||||||
? null
|
|
||||||
: isDarkTheme()
|
|
||||||
? MyTheme.disabledTextDark
|
|
||||||
: MyTheme.disabledTextLight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _OptionCheckBox(String label, String key,
|
Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||||
{Function()? update = null, bool reverse = false, bool enabled = true}) {
|
{Function()? update = null, bool reverse = false, bool enabled = true}) {
|
||||||
return _futureBuilder(
|
return _futureBuilder(
|
||||||
future: bind.mainGetOption(key: key),
|
future: bind.mainGetOption(key: key),
|
||||||
@ -778,7 +787,7 @@ Widget _OptionCheckBox(String label, String key,
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
translate(label),
|
translate(label),
|
||||||
style: TextStyle(color: _disabledTextColor(enabled)),
|
style: TextStyle(color: _disabledTextColor(context, enabled)),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -790,7 +799,7 @@ Widget _OptionCheckBox(String label, String key,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _Radio<T>(
|
Widget _Radio<T>(BuildContext context,
|
||||||
{required T value,
|
{required T value,
|
||||||
required T groupValue,
|
required T groupValue,
|
||||||
required String label,
|
required String label,
|
||||||
@ -811,7 +820,7 @@ Widget _Radio<T>(
|
|||||||
child: Text(translate(label),
|
child: Text(translate(label),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _kContentFontSize,
|
fontSize: _kContentFontSize,
|
||||||
color: _disabledTextColor(enabled)))
|
color: _disabledTextColor(context, enabled)))
|
||||||
.marginOnly(left: 5),
|
.marginOnly(left: 5),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -30,7 +30,11 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: MyTheme.color(context).border!)),
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
DesktopTabBar(
|
DesktopTabBar(
|
||||||
@ -55,6 +59,7 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
))),
|
))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
Flexible(flex: 3, child: body(isLocal: true)),
|
Flexible(flex: 3, child: body(isLocal: true)),
|
||||||
|
@ -181,10 +181,11 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
_ffi.inputKey(label, down: down, press: press ?? false);
|
_ffi.inputKey(label, down: down, press: press ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildBody(FfiModel ffiModel) {
|
Widget buildBody(BuildContext context, FfiModel ffiModel) {
|
||||||
final hasDisplays = ffiModel.pi.displays.length > 0;
|
final hasDisplays = ffiModel.pi.displays.length > 0;
|
||||||
final keyboard = ffiModel.permissions['keyboard'] != false;
|
final keyboard = ffiModel.permissions['keyboard'] != false;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
// resizeToAvoidBottomInset: true,
|
// resizeToAvoidBottomInset: true,
|
||||||
floatingActionButton: _showBar
|
floatingActionButton: _showBar
|
||||||
? null
|
? null
|
||||||
@ -229,7 +230,8 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
ChangeNotifierProvider.value(value: _ffi.canvasModel),
|
ChangeNotifierProvider.value(value: _ffi.canvasModel),
|
||||||
],
|
],
|
||||||
child: Consumer<FfiModel>(
|
child: Consumer<FfiModel>(
|
||||||
builder: (context, ffiModel, _child) => buildBody(ffiModel))));
|
builder: (context, ffiModel, _child) =>
|
||||||
|
buildBody(context, ffiModel))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getRawPointerAndKeyBody(Widget child) {
|
Widget getRawPointerAndKeyBody(Widget child) {
|
||||||
|
@ -13,7 +13,7 @@ import 'package:scroll_pos/scroll_pos.dart';
|
|||||||
const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
|
const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
|
||||||
const double _kIconSize = 18;
|
const double _kIconSize = 18;
|
||||||
const double _kDividerIndent = 10;
|
const double _kDividerIndent = 10;
|
||||||
const double _kAddIconSize = _kTabBarHeight - 15;
|
const double _kActionIconSize = 12;
|
||||||
final _tabBarKey = GlobalKey();
|
final _tabBarKey = GlobalKey();
|
||||||
|
|
||||||
void closeTab(String? id) {
|
void closeTab(String? id) {
|
||||||
@ -79,6 +79,10 @@ class DesktopTabBar extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: _kTabBarHeight,
|
height: _kTabBarHeight,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: _kTabBarHeight - 1,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -87,9 +91,19 @@ class DesktopTabBar extends StatelessWidget {
|
|||||||
Offstage(
|
Offstage(
|
||||||
offstage: !mainTab,
|
offstage: !mainTab,
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
Image.asset('assets/logo.ico'),
|
Image.asset(
|
||||||
Text("RustDesk").paddingOnly(left: 5),
|
'assets/logo.ico',
|
||||||
]).paddingSymmetric(horizontal: 12, vertical: 5),
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"RustDesk",
|
||||||
|
style: TextStyle(fontSize: 13),
|
||||||
|
).marginOnly(left: 2),
|
||||||
|
]).marginOnly(
|
||||||
|
left: 5,
|
||||||
|
right: 10,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -121,23 +135,27 @@ class DesktopTabBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: onAddSetting == null,
|
offstage: onAddSetting == null,
|
||||||
child: Tooltip(
|
child: _ActionIcon(
|
||||||
message: translate("Settings"),
|
message: 'Settings',
|
||||||
child: InkWell(
|
icon: IconFont.menu,
|
||||||
child: Icon(
|
theme: _theme,
|
||||||
Icons.menu,
|
|
||||||
color: _theme.unSelectedIconColor,
|
|
||||||
),
|
|
||||||
onTap: () => onAddSetting?.call(),
|
onTap: () => onAddSetting?.call(),
|
||||||
).paddingOnly(right: 10),
|
is_close: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
WindowActionPanel(
|
WindowActionPanel(
|
||||||
mainTab: mainTab,
|
mainTab: mainTab,
|
||||||
color: _theme.unSelectedIconColor,
|
theme: _theme,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
thickness: 1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,23 +174,20 @@ class DesktopTabBar extends StatelessWidget {
|
|||||||
|
|
||||||
class WindowActionPanel extends StatelessWidget {
|
class WindowActionPanel extends StatelessWidget {
|
||||||
final bool mainTab;
|
final bool mainTab;
|
||||||
final Color color;
|
final _Theme theme;
|
||||||
|
|
||||||
const WindowActionPanel(
|
const WindowActionPanel(
|
||||||
{Key? key, required this.mainTab, required this.color})
|
{Key? key, required this.mainTab, required this.theme})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Tooltip(
|
_ActionIcon(
|
||||||
message: translate("Minimize"),
|
message: 'Minimize',
|
||||||
child: InkWell(
|
icon: IconFont.min,
|
||||||
child: Icon(
|
theme: theme,
|
||||||
Icons.minimize,
|
|
||||||
color: color,
|
|
||||||
).paddingSymmetric(horizontal: 5),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
windowManager.minimize();
|
windowManager.minimize();
|
||||||
@ -180,45 +195,50 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
WindowController.fromWindowId(windowId!).minimize();
|
WindowController.fromWindowId(windowId!).minimize();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
is_close: false,
|
||||||
),
|
),
|
||||||
),
|
FutureBuilder(builder: (context, snapshot) {
|
||||||
Tooltip(
|
RxBool is_maximized = false.obs;
|
||||||
message: translate("Maximize"),
|
|
||||||
child: InkWell(
|
|
||||||
child: Icon(
|
|
||||||
Icons.rectangle_outlined,
|
|
||||||
color: color,
|
|
||||||
size: 20,
|
|
||||||
).paddingSymmetric(horizontal: 5),
|
|
||||||
onTap: () {
|
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
windowManager.isMaximized().then((maximized) {
|
windowManager.isMaximized().then((maximized) {
|
||||||
if (maximized) {
|
is_maximized.value = maximized;
|
||||||
windowManager.unmaximize();
|
|
||||||
} else {
|
|
||||||
windowManager.maximize();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final wc = WindowController.fromWindowId(windowId!);
|
final wc = WindowController.fromWindowId(windowId!);
|
||||||
wc.isMaximized().then((maximized) {
|
wc.isMaximized().then((maximized) {
|
||||||
if (maximized) {
|
is_maximized.value = maximized;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Obx(
|
||||||
|
() => _ActionIcon(
|
||||||
|
message: is_maximized.value ? "Restore" : "Maximize",
|
||||||
|
icon: is_maximized.value ? IconFont.restore : IconFont.max,
|
||||||
|
theme: theme,
|
||||||
|
onTap: () {
|
||||||
|
if (mainTab) {
|
||||||
|
if (is_maximized.value) {
|
||||||
|
windowManager.unmaximize();
|
||||||
|
} else {
|
||||||
|
windowManager.maximize();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final wc = WindowController.fromWindowId(windowId!);
|
||||||
|
if (is_maximized.value) {
|
||||||
wc.unmaximize();
|
wc.unmaximize();
|
||||||
} else {
|
} else {
|
||||||
wc.maximize();
|
wc.maximize();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
is_maximized.value = !is_maximized.value;
|
||||||
},
|
},
|
||||||
|
is_close: false,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
Tooltip(
|
}),
|
||||||
message: translate("Close"),
|
_ActionIcon(
|
||||||
child: InkWell(
|
message: 'Close',
|
||||||
child: Icon(
|
icon: IconFont.close,
|
||||||
Icons.close,
|
theme: theme,
|
||||||
color: color,
|
|
||||||
).paddingSymmetric(horizontal: 5),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
windowManager.close();
|
windowManager.close();
|
||||||
@ -226,15 +246,16 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
WindowController.fromWindowId(windowId!).close();
|
WindowController.fromWindowId(windowId!).close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
is_close: true,
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
class _ListView extends StatelessWidget {
|
class _ListView extends StatelessWidget {
|
||||||
late Rx<PageController> controller;
|
final Rx<PageController> controller;
|
||||||
final ScrollPosController scrollController;
|
final ScrollPosController scrollController;
|
||||||
final RxList<TabInfo> tabInfos;
|
final RxList<TabInfo> tabInfos;
|
||||||
final Rx<int> selected;
|
final Rx<int> selected;
|
||||||
@ -327,9 +348,7 @@ class _Tab extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool is_selected = index == selected;
|
bool is_selected = index == selected;
|
||||||
bool show_divider = index != selected - 1 && index != selected;
|
bool show_divider = index != selected - 1 && index != selected;
|
||||||
return Stack(
|
return Ink(
|
||||||
children: [
|
|
||||||
Ink(
|
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onHover: (hover) => _hover.value = hover,
|
onHover: (hover) => _hover.value = hover,
|
||||||
onTap: () => onSelected(),
|
onTap: () => onSelected(),
|
||||||
@ -383,18 +402,6 @@ class _Tab extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
height: 2,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
color:
|
|
||||||
is_selected ? theme.indicatorColor : Colors.transparent),
|
|
||||||
))
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,19 +416,13 @@ class _AddButton extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Ink(
|
return _ActionIcon(
|
||||||
height: _kTabBarHeight,
|
message: 'New Connection',
|
||||||
child: InkWell(
|
icon: IconFont.add,
|
||||||
customBorder: const CircleBorder(),
|
theme: theme,
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""),
|
rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""),
|
||||||
child: Icon(
|
is_close: false);
|
||||||
Icons.add_sharp,
|
|
||||||
size: _kAddIconSize,
|
|
||||||
color: theme.unSelectedIconColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +461,46 @@ class _CloseButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ActionIcon extends StatelessWidget {
|
||||||
|
final String message;
|
||||||
|
final IconData icon;
|
||||||
|
final _Theme theme;
|
||||||
|
final Function() onTap;
|
||||||
|
final bool is_close;
|
||||||
|
const _ActionIcon({
|
||||||
|
Key? key,
|
||||||
|
required this.message,
|
||||||
|
required this.icon,
|
||||||
|
required this.theme,
|
||||||
|
required this.onTap,
|
||||||
|
required this.is_close,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
RxBool hover = false.obs;
|
||||||
|
return Obx(() => Tooltip(
|
||||||
|
message: translate(message),
|
||||||
|
child: InkWell(
|
||||||
|
hoverColor: is_close ? Colors.red : theme.hoverColor,
|
||||||
|
onHover: (value) => hover.value = value,
|
||||||
|
child: Container(
|
||||||
|
height: _kTabBarHeight - 1,
|
||||||
|
width: _kTabBarHeight - 1,
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
color: hover.value && is_close
|
||||||
|
? Colors.white
|
||||||
|
: theme.unSelectedIconColor,
|
||||||
|
size: _kActionIconSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: onTap,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _Theme {
|
class _Theme {
|
||||||
late Color unSelectedtabIconColor;
|
late Color unSelectedtabIconColor;
|
||||||
late Color selectedtabIconColor;
|
late Color selectedtabIconColor;
|
||||||
@ -468,7 +509,7 @@ class _Theme {
|
|||||||
late Color selectedIconColor;
|
late Color selectedIconColor;
|
||||||
late Color unSelectedIconColor;
|
late Color unSelectedIconColor;
|
||||||
late Color dividerColor;
|
late Color dividerColor;
|
||||||
late Color indicatorColor;
|
late Color hoverColor;
|
||||||
|
|
||||||
_Theme.light() {
|
_Theme.light() {
|
||||||
unSelectedtabIconColor = Color.fromARGB(255, 162, 203, 241);
|
unSelectedtabIconColor = Color.fromARGB(255, 162, 203, 241);
|
||||||
@ -478,7 +519,7 @@ class _Theme {
|
|||||||
selectedIconColor = Color.fromARGB(255, 26, 26, 26);
|
selectedIconColor = Color.fromARGB(255, 26, 26, 26);
|
||||||
unSelectedIconColor = Color.fromARGB(255, 96, 96, 96);
|
unSelectedIconColor = Color.fromARGB(255, 96, 96, 96);
|
||||||
dividerColor = Color.fromARGB(255, 238, 238, 238);
|
dividerColor = Color.fromARGB(255, 238, 238, 238);
|
||||||
indicatorColor = MyTheme.accent;
|
hoverColor = Colors.grey.withOpacity(0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
_Theme.dark() {
|
_Theme.dark() {
|
||||||
@ -489,6 +530,6 @@ class _Theme {
|
|||||||
selectedIconColor = Color.fromARGB(255, 215, 215, 215);
|
selectedIconColor = Color.fromARGB(255, 215, 215, 215);
|
||||||
unSelectedIconColor = Color.fromARGB(255, 255, 255, 255);
|
unSelectedIconColor = Color.fromARGB(255, 255, 255, 255);
|
||||||
dividerColor = Color.fromARGB(255, 64, 64, 64);
|
dividerColor = Color.fromARGB(255, 64, 64, 64);
|
||||||
indicatorColor = MyTheme.accent;
|
hoverColor = Colors.black26;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,9 @@ flutter:
|
|||||||
- family: GestureIcons
|
- family: GestureIcons
|
||||||
fonts:
|
fonts:
|
||||||
- asset: assets/gestures.ttf
|
- asset: assets/gestures.ttf
|
||||||
|
- family: IconFont
|
||||||
|
fonts:
|
||||||
|
- asset: assets/tabbar.ttf
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user