Merge pull request #3207 from NicKoehler/modern-menubar

Modernized menu bar
This commit is contained in:
RustDesk
2023-02-15 21:17:49 +08:00
committed by GitHub
16 changed files with 217 additions and 125 deletions

View File

@@ -22,7 +22,10 @@ import 'package:bot_toast/bot_toast.dart';
import '../../models/platform_model.dart';
class _MenuTheme {
static const Color commonColor = MyTheme.accent;
static const Color blueColor = MyTheme.button;
static const Color hoverBlueColor = MyTheme.accent;
static const Color redColor = Colors.redAccent;
static const Color hoverRedColor = Colors.red;
// kMinInteractiveDimension
static const double height = 20.0;
static const double dividerHeight = 12.0;
@@ -280,7 +283,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: _MenuTheme.commonColor,
commonColor: _MenuTheme.blueColor,
height: _MenuTheme.height,
dividerHeight: _MenuTheme.dividerHeight,
)))

View File

@@ -5,6 +5,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/desktop/widgets/menu_button.dart';
// Examples can assume:
// enum Commands { heroAndScholar, hurricaneCame }
@@ -1391,22 +1393,20 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
onTap: widget.enabled ? showButtonMenu : null,
onHover: widget.onHover,
canRequestFocus: _canRequestFocus,
radius: widget.splashRadius,
enableFeedback: enableFeedback,
child: widget.child,
),
);
}
return IconButton(
return MenuButton(
icon: widget.icon ?? Icon(Icons.adaptive.more),
padding: widget.padding,
splashRadius: widget.splashRadius,
iconSize: widget.iconSize ?? iconTheme.size ?? _kDefaultIconSize,
tooltip:
widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
onPressed: widget.enabled ? showButtonMenu : null,
enableFeedback: enableFeedback,
color: MyTheme.button,
hoverColor: MyTheme.accent,
);
}
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
class MenuButton extends StatefulWidget {
final GestureTapCallback? onPressed;
final Color color;
final Color hoverColor;
final Color? splashColor;
final Widget icon;
final String? tooltip;
final EdgeInsetsGeometry padding;
final bool enableFeedback;
const MenuButton({
super.key,
required this.onPressed,
required this.color,
required this.hoverColor,
required this.icon,
this.splashColor,
this.tooltip = "",
this.padding = const EdgeInsets.symmetric(horizontal: 3, vertical: 6),
this.enableFeedback = true,
});
@override
State<MenuButton> createState() => _MenuButtonState();
}
class _MenuButtonState extends State<MenuButton> {
bool _isHover = false;
@override
Widget build(BuildContext context) {
return Padding(
padding: widget.padding,
child: Tooltip(
message: widget.tooltip,
child: Material(
type: MaterialType.transparency,
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: _isHover ? widget.hoverColor : widget.color,
),
child: InkWell(
onHover: (val) {
setState(() {
_isHover = val;
});
},
borderRadius: BorderRadius.circular(5),
splashColor: widget.splashColor,
enableFeedback: widget.enableFeedback,
onTap: widget.onPressed,
child: widget.icon,
),
),
),
),
);
}
}

View File

@@ -5,6 +5,7 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/desktop/widgets/menu_button.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/consts.dart';
@@ -94,7 +95,10 @@ class MenubarState {
}
class _MenubarTheme {
static const Color commonColor = MyTheme.accent;
static const Color blueColor = MyTheme.button;
static const Color hoverBlueColor = MyTheme.accent;
static const Color redColor = Colors.redAccent;
static const Color hoverRedColor = Colors.red;
// kMinInteractiveDimension
static const double height = 20.0;
static const double dividerHeight = 12.0;
@@ -411,7 +415,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
if (widget.ffi.ffiModel.isPeerAndroid) {
menubarItems.add(IconButton(
tooltip: translate('Mobile Actions'),
color: _MenubarTheme.commonColor,
color: _MenubarTheme.blueColor,
icon: const Icon(Icons.build),
onPressed: () {
widget.ffi.dialogManager
@@ -431,55 +435,65 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
menubarItems.add(_buildRecording(context));
menubarItems.add(_buildClose(context));
return PopupMenuTheme(
data: const PopupMenuThemeData(
textStyle: TextStyle(color: _MenubarTheme.commonColor)),
child: Column(mainAxisSize: MainAxisSize.min, children: [
data: const PopupMenuThemeData(
textStyle: TextStyle(color: _MenubarTheme.blueColor)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: MyTheme.border),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(10),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: menubarItems,
),
)),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 3),
...menubarItems,
SizedBox(width: 3)
],
),
),
),
_buildDraggableShowHide(context),
]));
],
),
);
}
Widget _buildPinMenubar(BuildContext context) {
return Obx(() => IconButton(
tooltip: translate(pin ? 'Unpin menubar' : 'Pin menubar'),
onPressed: () {
widget.state.switchPin();
},
icon: Obx(() => Transform.rotate(
angle: pin ? math.pi / 4 : 0,
child: Icon(
Icons.push_pin,
color: pin ? _MenubarTheme.commonColor : Colors.grey,
))),
));
return Obx(
() => MenuButton(
tooltip: translate(pin ? 'Unpin menubar' : 'Pin menubar'),
onPressed: () {
widget.state.switchPin();
},
icon: SvgPicture.asset(
pin ? "assets/pinned.svg" : "assets/unpinned.svg",
color: Colors.white,
),
color: pin ? _MenubarTheme.blueColor : Colors.grey[800]!,
hoverColor: pin ? _MenubarTheme.hoverBlueColor : Colors.grey[850]!,
),
);
}
Widget _buildFullscreen(BuildContext context) {
return IconButton(
return MenuButton(
tooltip: translate(isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'),
onPressed: () {
_setFullscreen(!isFullscreen);
},
icon: isFullscreen
? const Icon(
Icons.fullscreen_exit,
color: _MenubarTheme.commonColor,
)
: const Icon(
Icons.fullscreen,
color: _MenubarTheme.commonColor,
),
icon: SvgPicture.asset(
isFullscreen ? "assets/fullscreen_exit.svg" : "assets/fullscreen.svg",
color: Colors.white,
),
color: _MenubarTheme.blueColor,
hoverColor: _MenubarTheme.hoverBlueColor,
);
}
@@ -487,14 +501,13 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
final pi = widget.ffi.ffiModel.pi;
return mod_menu.PopupMenuButton(
tooltip: translate('Select Monitor'),
padding: EdgeInsets.zero,
position: mod_menu.PopupMenuPosition.under,
icon: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.personal_video,
color: _MenubarTheme.commonColor,
SvgPicture.asset(
"assets/display.svg",
color: Colors.white,
),
Padding(
padding: const EdgeInsets.only(bottom: 3.9),
@@ -502,8 +515,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
RxInt display = CurrentDisplayState.find(widget.id);
return Text(
'${display.value + 1}/${pi.displays.length}',
style: const TextStyle(
color: _MenubarTheme.commonColor, fontSize: 8),
style: const TextStyle(color: Colors.white, fontSize: 8),
);
}),
)
@@ -516,23 +528,25 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.personal_video,
color: _MenubarTheme.commonColor,
SvgPicture.asset(
"assets/display.svg",
color: Colors.white,
),
TextButton(
child: Container(
alignment: AlignmentDirectional.center,
constraints:
const BoxConstraints(minHeight: _MenubarTheme.height),
child: Padding(
padding: const EdgeInsets.only(bottom: 2.5),
child: Text(
(i + 1).toString(),
style:
const TextStyle(color: _MenubarTheme.commonColor),
alignment: AlignmentDirectional.center,
constraints:
const BoxConstraints(minHeight: _MenubarTheme.height),
child: Padding(
padding: const EdgeInsets.only(bottom: 2.5),
child: Text(
(i + 1).toString(),
style: TextStyle(
color: Colors.white,
),
)),
),
),
),
onPressed: () {
if (Navigator.canPop(context)) {
Navigator.pop(context);
@@ -567,9 +581,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Widget _buildControl(BuildContext context) {
return mod_menu.PopupMenuButton(
padding: EdgeInsets.zero,
icon: const Icon(
Icons.bolt,
color: _MenubarTheme.commonColor,
icon: SvgPicture.asset(
"assets/actions.svg",
color: Colors.white,
),
tooltip: translate('Control Actions'),
position: mod_menu.PopupMenuPosition.under,
@@ -577,7 +591,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: _MenubarTheme.commonColor,
commonColor: _MenubarTheme.blueColor,
height: _MenubarTheme.height,
dividerHeight: _MenubarTheme.dividerHeight,
)))
@@ -599,9 +613,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
final remoteCount = RemoteCountState.find().value;
return mod_menu.PopupMenuButton(
padding: EdgeInsets.zero,
icon: const Icon(
Icons.tv,
color: _MenubarTheme.commonColor,
icon: SvgPicture.asset(
"assets/display.svg",
color: Colors.white,
),
tooltip: translate('Display Settings'),
position: mod_menu.PopupMenuPosition.under,
@@ -611,7 +625,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: _MenubarTheme.commonColor,
commonColor: _MenubarTheme.blueColor,
height: _MenubarTheme.height,
dividerHeight: _MenubarTheme.dividerHeight,
)))
@@ -632,9 +646,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
}
return mod_menu.PopupMenuButton(
padding: EdgeInsets.zero,
icon: const Icon(
Icons.keyboard,
color: _MenubarTheme.commonColor,
icon: SvgPicture.asset(
"assets/keyboard.svg",
color: Colors.white,
),
tooltip: translate('Keyboard Settings'),
position: mod_menu.PopupMenuPosition.under,
@@ -642,7 +656,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: _MenubarTheme.commonColor,
commonColor: _MenubarTheme.blueColor,
height: _MenubarTheme.height,
dividerHeight: _MenubarTheme.dividerHeight,
)))
@@ -655,23 +669,22 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
return Consumer<FfiModel>(builder: ((context, value, child) {
if (value.permissions['recording'] != false) {
return Consumer<RecordingModel>(
builder: (context, value, child) => IconButton(
tooltip: value.start
? translate('Stop session recording')
: translate('Start session recording'),
onPressed: () => value.toggle(),
icon: value.start
? Icon(
Icons.pause_circle_filled,
color: _MenubarTheme.commonColor,
)
: SvgPicture.asset(
"assets/record_screen.svg",
color: _MenubarTheme.commonColor,
width: Theme.of(context).iconTheme.size ?? 22.0,
height: Theme.of(context).iconTheme.size ?? 22.0,
),
));
builder: (context, value, child) => MenuButton(
tooltip: value.start
? translate('Stop session recording')
: translate('Start session recording'),
onPressed: () => value.toggle(),
icon: SvgPicture.asset(
"assets/rec.svg",
color: Colors.white,
),
color:
value.start ? _MenubarTheme.redColor : _MenubarTheme.blueColor,
hoverColor: value.start
? _MenubarTheme.hoverRedColor
: _MenubarTheme.hoverBlueColor,
),
);
} else {
return Offstage();
}
@@ -679,15 +692,17 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
}
Widget _buildClose(BuildContext context) {
return IconButton(
return MenuButton(
tooltip: translate('Close'),
onPressed: () {
clientClose(widget.id, widget.ffi.dialogManager);
},
icon: const Icon(
Icons.close,
color: _MenubarTheme.commonColor,
icon: SvgPicture.asset(
"assets/close.svg",
color: Colors.white,
),
color: _MenubarTheme.redColor,
hoverColor: _MenubarTheme.hoverRedColor,
);
}
@@ -699,9 +714,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
padding: EdgeInsets.zero,
icon: SvgPicture.asset(
"assets/chat.svg",
color: _MenubarTheme.commonColor,
width: Theme.of(context).iconTheme.size ?? 24.0,
height: Theme.of(context).iconTheme.size ?? 24.0,
color: Colors.white,
),
tooltip: translate('Chat'),
position: mod_menu.PopupMenuPosition.under,
@@ -709,7 +722,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
.map((entry) => entry.build(
context,
const MenuConfig(
commonColor: _MenubarTheme.commonColor,
commonColor: _MenubarTheme.blueColor,
height: _MenubarTheme.height,
dividerHeight: _MenubarTheme.dividerHeight,
)))
@@ -721,26 +734,15 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Widget _getVoiceCallIcon() {
switch (widget.ffi.chatModel.voiceCallStatus.value) {
case VoiceCallStatus.waitingForResponse:
return IconButton(
onPressed: () {
widget.ffi.chatModel.closeVoiceCall(widget.id);
},
icon: SvgPicture.asset(
"assets/voice_call_waiting.svg",
color: Colors.red,
width: Theme.of(context).iconTheme.size ?? 20.0,
height: Theme.of(context).iconTheme.size ?? 20.0,
));
return SvgPicture.asset(
"assets/call_wait.svg",
color: Colors.white,
);
case VoiceCallStatus.connected:
return IconButton(
onPressed: () {
widget.ffi.chatModel.closeVoiceCall(widget.id);
},
icon: Icon(
Icons.phone_disabled_rounded,
color: Colors.red,
size: Theme.of(context).iconTheme.size ?? 22.0,
),
return SvgPicture.asset(
"assets/call_end.svg",
color: Colors.white,
);
default:
return const Offstage();
@@ -764,11 +766,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
final tooltipText = _getVoiceCallTooltip();
return tooltipText == null
? const Offstage()
: IconButton(
padding: EdgeInsets.zero,
: MenuButton(
icon: _getVoiceCallIcon(),
tooltip: translate(tooltipText),
onPressed: () => bind.sessionRequestVoiceCall(id: widget.id),
color: _MenubarTheme.redColor,
hoverColor: _MenubarTheme.hoverRedColor,
);
},
);
@@ -1754,7 +1757,7 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
child: Icon(
Icons.drag_indicator,
size: 20,
color: Colors.grey,
color: Colors.grey[800],
),
feedback: widget,
onDragStarted: (() {
@@ -1807,7 +1810,9 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: MyTheme.border),
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(5),
),
),
child: SizedBox(
height: 20,