opt: DesktopTab tabs handle mouse wheel, add maxLabelWidth constraint, update cm

This commit is contained in:
csf 2022-10-20 23:10:26 +09:00
parent afa94d5907
commit 94c8b117ef
2 changed files with 94 additions and 56 deletions

View File

@ -124,6 +124,7 @@ class ConnectionManagerState extends State<ConnectionManager> {
showMinimize: true, showMinimize: true,
showClose: true, showClose: true,
controller: serverModel.tabController, controller: serverModel.tabController,
maxLabelWidth: 100,
pageViewBuilder: (pageView) => Row(children: [ pageViewBuilder: (pageView) => Row(children: [
Expanded(child: pageView), Expanded(child: pageView),
Consumer<ChatModel>( Consumer<ChatModel>(

View File

@ -3,12 +3,14 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' hide TabBarTheme; import 'package:flutter/material.dart' hide TabBarTheme;
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/main.dart'; import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
import 'package:scroll_pos/scroll_pos.dart'; import 'package:scroll_pos/scroll_pos.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@ -132,7 +134,8 @@ class DesktopTabController {
if (val.scrollController.hasClients && if (val.scrollController.hasClients &&
val.scrollController.canScroll && val.scrollController.canScroll &&
val.scrollController.itemCount > index) { val.scrollController.itemCount > index) {
val.scrollController.scrollToItem(index, center: true, animate: true); val.scrollController
.scrollToItem(index, center: false, animate: true);
} }
})); }));
}); });
@ -188,10 +191,15 @@ class DesktopTab extends StatelessWidget {
final Future<bool> Function()? onWindowCloseButton; final Future<bool> Function()? onWindowCloseButton;
final TabBuilder? tabBuilder; final TabBuilder? tabBuilder;
final LabelGetter? labelGetter; final LabelGetter? labelGetter;
final double? maxLabelWidth;
final DesktopTabController controller; final DesktopTabController controller;
Rx<DesktopTabState> get state => controller.state; Rx<DesktopTabState> get state => controller.state;
final isMaximized = false.obs; final isMaximized = false.obs;
final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50));
/// [_lastClickTime], help to handle double click
int _lastClickTime = DateTime.now().millisecondsSinceEpoch;
late final DesktopTabType tabType; late final DesktopTabType tabType;
late final bool isMainWindow; late final bool isMainWindow;
@ -211,6 +219,7 @@ class DesktopTab extends StatelessWidget {
this.onWindowCloseButton, this.onWindowCloseButton,
this.tabBuilder, this.tabBuilder,
this.labelGetter, this.labelGetter,
this.maxLabelWidth,
}) : super(key: key) { }) : super(key: key) {
tabType = controller.tabType; tabType = controller.tabType;
isMainWindow = isMainWindow =
@ -292,46 +301,72 @@ class DesktopTab extends StatelessWidget {
Widget _buildBar() { Widget _buildBar() {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Expanded(
children: [ child: GestureDetector(
Offstage( // custom double tap handler
offstage: !Platform.isMacOS, onTap: showMaximize
child: const SizedBox( ? () {
width: 78, final current = DateTime.now().millisecondsSinceEpoch;
)), final elapsed = current - _lastClickTime;
GestureDetector( _lastClickTime = current;
onDoubleTap: showMaximize if (elapsed < kDesktopDoubleClickTimeMilli) {
? () => toggleMaximize(isMainWindow) // onDoubleTap
.then((value) => isMaximized.value = value) toggleMaximize(isMainWindow)
.then((value) => isMaximized.value = value);
}
}
: null, : null,
onPanStart: (_) => startDragging(isMainWindow), onPanStart: (_) => startDragging(isMainWindow),
child: Row(children: [ child: Row(
Offstage( children: [
offstage: !showLogo, Offstage(
child: SvgPicture.asset( offstage: !Platform.isMacOS,
'assets/logo.svg', child: const SizedBox(
width: 16, width: 78,
height: 16, )),
)), Row(children: [
Offstage( Offstage(
offstage: !showTitle, offstage: !showLogo,
child: const Text( child: SvgPicture.asset(
"RustDesk", 'assets/logo.svg',
style: TextStyle(fontSize: 13), width: 16,
).marginOnly(left: 2)) height: 16,
]).marginOnly( )),
left: 5, Offstage(
right: 10, offstage: !showTitle,
)), child: const Text(
_ListView( "RustDesk",
controller: controller, style: TextStyle(fontSize: 13),
onTabClose: onTabClose, ).marginOnly(left: 2))
tabBuilder: tabBuilder, ]).marginOnly(
labelGetter: labelGetter, left: 5,
), right: 10,
], ),
), Expanded(
child: Listener(
// handle mouse wheel
onPointerSignal: (e) {
if (e is PointerScrollEvent) {
final sc =
controller.state.value.scrollController;
if (!sc.canScroll) return;
_scrollDebounce.call(() {
sc.animateTo(sc.offset + e.scrollDelta.dy,
duration: Duration(milliseconds: 200),
curve: Curves.ease);
});
}
},
child: _ListView(
controller: controller,
onTabClose: onTabClose,
tabBuilder: tabBuilder,
labelGetter: labelGetter,
maxLabelWidth: maxLabelWidth))),
],
))),
WindowActionPanel( WindowActionPanel(
isMainWindow: isMainWindow, isMainWindow: isMainWindow,
tabType: tabType, tabType: tabType,
@ -435,14 +470,9 @@ class WindowActionPanelState extends State<WindowActionPanel>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Row(
child: Row( mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Expanded(
child: GestureDetector(
onDoubleTap: widget.showMaximize ? _toggleMaximize : null,
onPanStart: (_) => startDragging(widget.isMainWindow),
)),
Offstage(offstage: widget.tail == null, child: widget.tail), Offstage(offstage: widget.tail == null, child: widget.tail),
Offstage( Offstage(
offstage: !widget.showMinimize, offstage: !widget.showMinimize,
@ -489,7 +519,7 @@ class WindowActionPanelState extends State<WindowActionPanel>
isClose: true, isClose: true,
)), )),
], ],
)); );
} }
void _toggleMaximize() { void _toggleMaximize() {
@ -580,13 +610,13 @@ Future<bool> closeConfirmDialog() async {
return res == true; return res == true;
} }
// ignore: must_be_immutable
class _ListView extends StatelessWidget { class _ListView extends StatelessWidget {
final DesktopTabController controller; final DesktopTabController controller;
final Function(String key)? onTabClose; final Function(String key)? onTabClose;
final TabBuilder? tabBuilder; final TabBuilder? tabBuilder;
final LabelGetter? labelGetter; final LabelGetter? labelGetter;
final double? maxLabelWidth;
Rx<DesktopTabState> get state => controller.state; Rx<DesktopTabState> get state => controller.state;
@ -594,7 +624,8 @@ class _ListView extends StatelessWidget {
{required this.controller, {required this.controller,
required this.onTabClose, required this.onTabClose,
this.tabBuilder, this.tabBuilder,
this.labelGetter}); this.labelGetter,
this.maxLabelWidth});
/// Check whether to show ListView /// Check whether to show ListView
/// ///
@ -645,6 +676,7 @@ class _ListView extends StatelessWidget {
themeConf, themeConf,
); );
}, },
maxLabelWidth: maxLabelWidth,
); );
}).toList())); }).toList()));
} }
@ -661,6 +693,7 @@ class _Tab extends StatefulWidget {
final Function() onSelected; final Function() onSelected;
final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)? final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)?
tabBuilder; tabBuilder;
final double? maxLabelWidth;
const _Tab({ const _Tab({
Key? key, Key? key,
@ -673,6 +706,7 @@ class _Tab extends StatefulWidget {
required this.selected, required this.selected,
required this.onClose, required this.onClose,
required this.onSelected, required this.onSelected,
this.maxLabelWidth,
}) : super(key: key); }) : super(key: key);
@override @override
@ -697,14 +731,17 @@ class _TabState extends State<_Tab> with RestorationMixin {
: MyTheme.tabbar(context).unSelectedTabIconColor, : MyTheme.tabbar(context).unSelectedTabIconColor,
).paddingOnly(right: 5)); ).paddingOnly(right: 5));
final labelWidget = Obx(() { final labelWidget = Obx(() {
return Text( return ConstrainedBox(
translate(widget.label.value), constraints: BoxConstraints(maxWidth: widget.maxLabelWidth ?? 200),
textAlign: TextAlign.center, child: Text(
style: TextStyle( translate(widget.label.value),
color: isSelected textAlign: TextAlign.center,
? MyTheme.tabbar(context).selectedTextColor style: TextStyle(
: MyTheme.tabbar(context).unSelectedTextColor), color: isSelected
); ? MyTheme.tabbar(context).selectedTextColor
: MyTheme.tabbar(context).unSelectedTextColor),
overflow: TextOverflow.ellipsis,
));
}); });
if (widget.tabBuilder == null) { if (widget.tabBuilder == null) {