Fix/custom client styles (#7373)

* Fix. qs styles

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Move logo.svg to icon.svg

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Refact. Custom client, connection status ui.

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client ui. Disable settings, hide "Change password"

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, logo align center

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, refact, outgoing ui

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, outgoing, settings icon

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, powered by RustDesk

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Custom client, remove unused SizeBox

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* Update config.rs

* Update flutter_ffi.rs

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
fufesou
2024-03-14 11:36:14 +08:00
committed by GitHub
parent a314f59db9
commit 5253d67e04
49 changed files with 350 additions and 124 deletions

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1522,7 +1522,10 @@ class LastWindowPosition {
}
String get windowFramePrefix =>
bind.isIncomingOnly() ? "${kWindowPrefix}qs_" : kWindowPrefix;
kWindowPrefix +
(bind.isIncomingOnly()
? "incoming_"
: (bind.isOutgoingOnly() ? "outgoing_" : ""));
/// Save window position and size on exit
/// Note that windowId must be provided if it's subwindow
@@ -1793,11 +1796,11 @@ Future<bool> restoreWindowPosition(WindowType type,
}
if (lpos.isMaximized == true) {
await restorePos();
if (!bind.isIncomingOnly()) {
if (!(bind.isIncomingOnly() || bind.isOutgoingOnly())) {
await windowManager.maximize();
}
} else {
if (!bind.isIncomingOnly()) {
if (!bind.isIncomingOnly() || bind.isOutgoingOnly()) {
await windowManager.setSize(size);
}
await restorePos();
@@ -3079,25 +3082,46 @@ Color? disabledTextColor(BuildContext context, bool enabled) {
: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6);
}
Widget loadLogo(double size) {
return Image.asset('assets/logo.png',
// max 200 x 40
Widget? loadLogo() {
bool isFound = true;
final image = Image.asset(
'assets/logo.png',
fit: BoxFit.contain,
errorBuilder: (ctx, error, stackTrace) {
isFound = false;
return Container();
},
);
if (isFound) {
return Container(
constraints: BoxConstraints(maxWidth: 200, maxHeight: 40),
child: image,
).marginOnly(bottom: 10);
} else {
return null;
}
}
Widget loadIcon(double size) {
return Image.asset('assets/icon.png',
width: size,
height: size,
errorBuilder: (ctx, error, stackTrace) => SvgPicture.asset(
'assets/logo.svg',
'assets/icon.svg',
width: size,
height: size,
));
}
var desktopQsHomeLeftPaneSize = Size(280, 300);
Size getDesktopQsHomeSize() {
final magicWidth = 11.0;
var imcomingOnlyHomeSize = Size(280, 300);
Size getIncomingOnlyHomeSize() {
final magicWidth = Platform.isWindows ? 11.0 : 0.0;
final magicHeight = 8.0;
return desktopQsHomeLeftPaneSize +
return imcomingOnlyHomeSize +
Offset(magicWidth, kDesktopRemoteTabBarHeight + magicHeight);
}
Size getDesktopQsSettingsSize() {
Size getIncomingOnlySettingsSize() {
return Size(768, 600);
}

View File

@@ -21,7 +21,10 @@ import '../../models/platform_model.dart';
import '../widgets/button.dart';
class OnlineStatusWidget extends StatefulWidget {
const OnlineStatusWidget({Key? key}) : super(key: key);
const OnlineStatusWidget({Key? key, this.onSvcStatusChanged})
: super(key: key);
final VoidCallback? onSvcStatusChanged;
@override
State<OnlineStatusWidget> createState() => _OnlineStatusWidgetState();
@@ -34,7 +37,7 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
Timer? _updateTimer;
double get em => 14.0;
double get height => em * 3;
double? get height => bind.isIncomingOnly() ? null : em * 3;
void onUsePublicServerGuide() {
const url = "https://rustdesk.com/pricing.html";
@@ -79,15 +82,10 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
: Color.fromARGB(255, 224, 79, 95)),
),
).marginSymmetric(horizontal: em),
Text(
_svcStopped.value
? translate("Service is not running")
: stateGlobal.svcStatus.value == SvcStatus.connecting
? translate("connecting_status")
: stateGlobal.svcStatus.value == SvcStatus.notReady
? translate("not_ready_status")
: translate('Ready'),
style: TextStyle(fontSize: em)),
Container(
width: bind.isIncomingOnly() ? 240 : null,
child: _buildConnStatusMsg(),
),
// stop
Offstage(
offstage: !_svcStopped.value,
@@ -102,7 +100,8 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
.marginOnly(left: em),
),
// ready && public
Flexible(
// No need to show the guide if is custom client.
if (!bind.isIncomingOnly()) Flexible(
child: Offstage(
offstage: !(!_svcStopped.value &&
stateGlobal.svcStatus.value == SvcStatus.ready &&
@@ -137,6 +136,20 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
).paddingOnly(right: bind.isIncomingOnly() ? 8 : 0);
}
_buildConnStatusMsg() {
widget.onSvcStatusChanged?.call();
return Text(
_svcStopped.value
? translate("Service is not running")
: stateGlobal.svcStatus.value == SvcStatus.connecting
? translate("connecting_status")
: stateGlobal.svcStatus.value == SvcStatus.notReady
? translate("not_ready_status")
: translate('Ready'),
style: TextStyle(fontSize: em),
);
}
updateStatus() async {
final status =
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
@@ -260,8 +273,8 @@ class _ConnectionPageState extends State<ConnectionPage>
Expanded(child: PeerTabPage()),
],
).paddingOnly(left: 12.0)),
const Divider(height: 1),
OnlineStatusWidget()
if (!bind.isOutgoingOnly()) const Divider(height: 1),
if (!bind.isOutgoingOnly()) OnlineStatusWidget()
],
);
}

View File

@@ -13,6 +13,7 @@ import 'package:flutter_hbb/desktop/pages/connection_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/plugin/ui_manager.dart';
@@ -51,38 +52,43 @@ class _DesktopHomePageState extends State<DesktopHomePage>
Timer? _updateTimer;
bool isCardClosed = false;
RxBool _editHover = false.obs;
final GlobalKey _childKey = GlobalKey();
bool _isInHomePage() {
final controller = Get.find<DesktopTabController>();
return controller.state.value.selected == 0;
}
@override
Widget build(BuildContext context) {
super.build(context);
final children = [buildLeftPane(context)];
if (!bind.isIncomingOnly()) {
children.addAll([
const VerticalDivider(width: 1),
Expanded(child: buildRightPane(context)),
]);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
children: [
buildLeftPane(context),
if (!bind.isIncomingOnly()) const VerticalDivider(width: 1),
if (!bind.isIncomingOnly()) Expanded(child: buildRightPane(context)),
],
);
}
Widget buildLeftPane(BuildContext context) {
final children = <Widget>[
buildTip(context),
buildIDBoard(context),
buildPasswordBoard(context),
if (!bind.isOutgoingOnly()) buildIDBoard(context),
if (!bind.isOutgoingOnly()) buildPasswordBoard(context),
FutureBuilder<Widget>(
future: buildHelpCards(),
builder: (_, data) {
if (data.hasData) {
if (bind.isIncomingOnly()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
if (_isInHomePage()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
}
return data.data!;
} else {
@@ -97,10 +103,19 @@ class _DesktopHomePageState extends State<DesktopHomePage>
Divider(),
Container(
margin: EdgeInsets.fromLTRB(0, 0, 8, 6),
child: OnlineStatusWidget(),
child: OnlineStatusWidget(
onSvcStatusChanged: () {
if (_isInHomePage()) {
Future.delayed(Duration(milliseconds: 300), () {
_updateWindowSize();
});
}
},
),
),
]);
}
final textColor = Theme.of(context).textTheme.titleLarge?.color;
return ChangeNotifierProvider.value(
value: gFFI.serverModel,
child: Container(
@@ -108,13 +123,45 @@ class _DesktopHomePageState extends State<DesktopHomePage>
color: Theme.of(context).colorScheme.background,
child: DesktopScrollWrapper(
scrollController: _leftPaneScrollController,
child: SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
key: _childKey,
children: children,
),
child: Stack(
children: [
SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
key: _childKey,
children: children,
),
),
if (bind.isOutgoingOnly() && !bind.isDisableSettings())
Positioned(
child: Divider(),
bottom: 26,
left: 0,
right: 0,
),
if (bind.isOutgoingOnly() && !bind.isDisableSettings())
Positioned(
bottom: 6,
left: 12,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
child: Obx(
() => Icon(
Icons.settings,
color: _editHover.value
? textColor
: Colors.grey.withOpacity(0.5),
size: 22,
),
),
onTap: () => DesktopSettingPage.switch2page(1),
onHover: (value) => _editHover.value = value,
),
),
)
],
),
),
),
@@ -197,6 +244,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
Widget buildPopupMenu(BuildContext context) {
if (bind.isDisableSettings()) {
return Offstage();
}
final textColor = Theme.of(context).textTheme.titleLarge?.color;
RxBool hover = false.obs;
return InkWell(
@@ -287,21 +338,22 @@ class _DesktopHomePageState extends State<DesktopHomePage>
)))),
onHover: (value) => refreshHover.value = value,
).marginOnly(right: 8, top: 4),
InkWell(
child: Obx(
() => Tooltip(
message: translate('Change Password'),
child: Icon(
Icons.edit,
color: editHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
if (!bind.isDisableSettings())
InkWell(
child: Obx(
() => Tooltip(
message: translate('Change Password'),
child: Icon(
Icons.edit,
color: editHover.value
? textColor
: Color(0xFFDDDDDD),
size: 22,
)).marginOnly(right: 8, top: 4),
),
onTap: () => DesktopSettingPage.switch2page(1),
onHover: (value) => editHover.value = value,
),
onTap: () => DesktopSettingPage.switch2page(1),
onHover: (value) => editHover.value = value,
),
],
),
],
@@ -314,6 +366,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
buildTip(BuildContext context) {
final logo = loadLogo();
return Padding(
padding:
const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 5),
@@ -321,29 +374,68 @@ class _DesktopHomePageState extends State<DesktopHomePage>
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translate("Your Desktop"),
style: Theme.of(context).textTheme.titleLarge,
// style: TextStyle(
// // color: MyTheme.color(context).text,
// fontWeight: FontWeight.normal,
// fontSize: 19),
Align(
alignment: Alignment.center,
child: logo == null ? Offstage() : logo.marginOnly(bottom: 0.0),
),
Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
translate("Your Desktop"),
style: Theme.of(context).textTheme.titleLarge,
// style: TextStyle(
// // color: MyTheme.color(context).text,
// fontWeight: FontWeight.normal,
// fontSize: 19),
),
),
if (bind.isCustomClient())
Align(
alignment: Alignment.centerRight,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
launchUrl(Uri.parse('https://rustdesk.com'));
},
child: Text(
translate("powered by RustDesk"),
overflow: TextOverflow.clip,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: Colors.grey.withOpacity(0.7)),
),
),
),
),
],
),
SizedBox(
height: 10.0,
),
Text(
translate("desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
)
if (!bind.isOutgoingOnly())
Text(
translate("desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
if (bind.isOutgoingOnly())
Text(
translate("outgoing_only_desk_tip"),
overflow: TextOverflow.clip,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
);
}
Future<Widget> buildHelpCards() async {
if (updateUrl.isNotEmpty &&
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
!isCardClosed &&
bind.mainUriPrefixSync().contains('rustdesk')) {
return buildInstallCard(
@@ -357,7 +449,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});
}
if (Platform.isWindows) {
if (Platform.isWindows && !bind.isDisableInstallation()) {
if (!bind.mainIsInstalled()) {
return buildInstallCard("", "install_tip", "Install", () async {
await rustDeskWinManager.closeAllSubWindows();
@@ -372,7 +464,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
});
}
} else if (Platform.isMacOS) {
if (!bind.mainIsCanScreenRecording(prompt: false)) {
if (!(bind.isOutgoingOnly() ||
bind.mainIsCanScreenRecording(prompt: false))) {
return buildInstallCard("Permissions", "config_screen", "Configure",
() async {
bind.mainIsCanScreenRecording(prompt: true);
@@ -407,6 +500,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
// });
// }
} else if (Platform.isLinux) {
if (bind.isOutgoingOnly()) {
return Container();
}
final LinuxCards = <Widget>[];
if (bind.isSelinuxEnforcing()) {
// Check is SELinux enforcing, but show user a tip of is SELinux enabled for simple.
@@ -473,8 +569,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
return Stack(
children: [
Container(
margin:
EdgeInsets.fromLTRB(0, marginTop, 0, bind.isIncomingOnly() ? marginTop : 0),
margin: EdgeInsets.fromLTRB(
0, marginTop, 0, bind.isIncomingOnly() ? marginTop : 0),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
@@ -701,10 +797,17 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
_updateWindowSize() {
RenderBox renderBox =
_childKey.currentContext?.findRenderObject() as RenderBox;
desktopQsHomeLeftPaneSize = renderBox.size;
windowManager.setSize(getDesktopQsHomeSize());
RenderObject? renderObject = _childKey.currentContext?.findRenderObject();
if (renderObject == null) {
return;
}
if (renderObject is RenderBox) {
final size = renderObject.size;
if (size != imcomingOnlyHomeSize) {
imcomingOnlyHomeSize = size;
windowManager.setSize(getIncomingOnlyHomeSize());
}
}
}
@override

View File

@@ -106,39 +106,32 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
List<_TabInfo> _settingTabs() {
final List<_TabInfo> settingTabs = <_TabInfo>[
_TabInfo('General', Icons.settings_outlined, Icons.settings),
_TabInfo('Security', Icons.enhanced_encryption_outlined,
Icons.enhanced_encryption),
if (!bind.isOutgoingOnly())
_TabInfo('Security', Icons.enhanced_encryption_outlined,
Icons.enhanced_encryption),
_TabInfo('Network', Icons.link_outlined, Icons.link),
_TabInfo('Account', Icons.person_outline, Icons.person),
if (!bind.isIncomingOnly())
_TabInfo(
'Display', Icons.desktop_windows_outlined, Icons.desktop_windows),
if (!bind.isIncomingOnly() && bind.pluginFeatureIsEnabled())
_TabInfo('Plugin', Icons.extension_outlined, Icons.extension),
if (!bind.isDisableAccount())
_TabInfo('Account', Icons.person_outline, Icons.person),
_TabInfo('About', Icons.info_outline, Icons.info)
];
if (!bind.isIncomingOnly()) {
settingTabs.insert(
3,
_TabInfo('Display', Icons.desktop_windows_outlined,
Icons.desktop_windows));
if (bind.pluginFeatureIsEnabled()) {
settingTabs.insert(
4, _TabInfo('Plugin', Icons.extension_outlined, Icons.extension));
}
}
return settingTabs;
}
List<Widget> _children() {
final children = [
_General(),
_Safety(),
if (!bind.isOutgoingOnly()) _Safety(),
_Network(),
_Account(),
if (!bind.isIncomingOnly()) _Display(),
if (!bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) _Plugin(),
if (!bind.isDisableAccount()) _Account(),
_About(),
];
if (!bind.isIncomingOnly()) {
children.insert(3, _Display());
if (bind.pluginFeatureIsEnabled()) {
children.insert(4, _Plugin());
}
}
return children;
}
@@ -309,6 +302,10 @@ class _GeneralState extends State<_General> {
}
Widget service() {
if (bind.isOutgoingOnly()) {
return const Offstage();
}
return _Card(title: 'Service', children: [
Obx(() => _Button(serviceStop.value ? 'Start' : 'Stop', () {
() async {
@@ -324,18 +321,14 @@ class _GeneralState extends State<_General> {
}
Widget other() {
final children = <Widget>[];
if (!bind.isIncomingOnly()) {
children.add(_OptionCheckBox(context,
'Confirm before closing multiple tabs', 'enable-confirm-closing-tabs',
isServer: false));
}
children.addAll([
final children = <Widget>[
if (!bind.isIncomingOnly())
_OptionCheckBox(context, 'Confirm before closing multiple tabs',
'enable-confirm-closing-tabs',
isServer: false),
_OptionCheckBox(context, 'Adaptive bitrate', 'enable-abr'),
wallpaper()
]);
if (!bind.isIncomingOnly()) {
children.addAll([
wallpaper(),
if (!bind.isIncomingOnly()) ...[
_OptionCheckBox(
context,
'Open connection in new tab',
@@ -354,8 +347,8 @@ class _GeneralState extends State<_General> {
'enable-check-update',
isServer: false,
)
]);
}
],
];
if (bind.mainShowOption(key: 'allow-linux-headless')) {
children.add(_OptionCheckBox(
context, 'Allow linux headless', 'allow-linux-headless'));
@@ -364,6 +357,10 @@ class _GeneralState extends State<_General> {
}
Widget wallpaper() {
if (bind.isOutgoingOnly()) {
return const Offstage();
}
return futureBuilder(future: () async {
final support = await bind.mainSupportRemoveWallpaper();
return support;
@@ -411,6 +408,10 @@ class _GeneralState extends State<_General> {
}
Widget audio(BuildContext context) {
if (bind.isOutgoingOnly()) {
return const Offstage();
}
String getDefault() {
if (Platform.isWindows) return translate('System Sound');
return '';

View File

@@ -57,10 +57,10 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
if (bind.isIncomingOnly()) {
tabController.onSelected = (key) {
if (key == kTabLabelHomePage) {
windowManager.setSize(getDesktopQsHomeSize());
windowManager.setSize(getIncomingOnlyHomeSize());
windowManager.setResizable(false);
} else {
windowManager.setSize(getDesktopQsSettingsSize());
windowManager.setSize(getIncomingOnlySettingsSize());
windowManager.setResizable(true);
}
};
@@ -81,7 +81,7 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
body: DesktopTab(
controller: tabController,
tail: Offstage(
offstage: bind.isIncomingOnly(),
offstage: bind.isIncomingOnly() || bind.isDisableSettings(),
child: ActionIcon(
message: 'Settings',
icon: IconFont.menu,

View File

@@ -327,7 +327,7 @@ class _AppIcon extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 4.0),
child: loadLogo(30),
child: loadIcon(30),
);
}
}

View File

@@ -165,7 +165,8 @@ class DesktopTabController {
}));
}
});
if ((isDesktop && bind.isIncomingOnly()) || callOnSelected) {
if ((isDesktop && (bind.isIncomingOnly() || bind.isOutgoingOnly())) ||
callOnSelected) {
if (state.value.tabs.length > index) {
final key = state.value.tabs[index].key;
onSelected?.call(key);
@@ -399,7 +400,7 @@ class DesktopTab extends StatelessWidget {
child: Row(children: [
Offstage(
offstage: !showLogo,
child: loadLogo(16),
child: loadIcon(16),
),
Offstage(
offstage: !showTitle,

View File

@@ -25,8 +25,6 @@ class PeerTabModel with ChangeNotifier {
'Recent sessions',
'Favorites',
'Discovered',
'Address book',
'Group',
];
final List<IconData> icons = [
Icons.access_time_filled,
@@ -51,6 +49,13 @@ class PeerTabModel with ChangeNotifier {
String get lastId => _lastId;
PeerTabModel(this.parent) {
if (!(bind.isDisableAb() || bind.isDisableAccount())) {
tabNames.add('Address book');
}
if (!bind.isDisableAccount()) {
tabNames.add('Group');
}
// visible
try {
final option = bind.getLocalFlutterOption(k: 'peer-tab-visible');