file transfer search bar pop_menu show Windows drives
This commit is contained in:
parent
e23fa8c806
commit
0c976a6644
@ -107,7 +107,7 @@ class DraggableChatWindow extends StatelessWidget {
|
|||||||
icon: IconFont.close,
|
icon: IconFont.close,
|
||||||
onTap: chatModel.hideChatWindowOverlay,
|
onTap: chatModel.hideChatWindowOverlay,
|
||||||
isClose: true,
|
isClose: true,
|
||||||
size: 32,
|
boxSize: 32,
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -6,6 +6,7 @@ import 'package:desktop_drop/desktop_drop.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
|
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/models/file_model.dart';
|
import 'package:flutter_hbb/models/file_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -455,7 +456,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
icon: const Icon(Icons.restart_alt_rounded)),
|
icon: const Icon(Icons.restart_alt_rounded)),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete_forever_outlined),
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
model.jobTable.removeAt(index);
|
model.jobTable.removeAt(index);
|
||||||
@ -742,32 +743,98 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
openDirectory(path, isLocal: isLocal);
|
openDirectory(path, isLocal: isLocal);
|
||||||
});
|
});
|
||||||
breadCrumbScrollToEnd(isLocal);
|
breadCrumbScrollToEnd(isLocal);
|
||||||
|
final locationBarKey = GlobalKey(debugLabel: "locationBarKey");
|
||||||
|
|
||||||
return items.isEmpty
|
return items.isEmpty
|
||||||
? Offstage()
|
? Offstage()
|
||||||
: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
: Row(
|
||||||
Expanded(
|
key: locationBarKey,
|
||||||
child: BreadCrumb(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
items: items,
|
children: [
|
||||||
divider: Text("/").paddingSymmetric(horizontal: 4.0),
|
Expanded(
|
||||||
overflow: ScrollableOverflow(
|
child: BreadCrumb(
|
||||||
controller: getBreadCrumbScrollController(isLocal)),
|
items: items,
|
||||||
)),
|
divider: Text("/",
|
||||||
DropdownButton<String>(
|
style: TextStyle(color: Theme.of(context).hintColor))
|
||||||
isDense: true,
|
.paddingSymmetric(horizontal: 2.0),
|
||||||
underline: Offstage(),
|
overflow: ScrollableOverflow(
|
||||||
items: [
|
controller: getBreadCrumbScrollController(isLocal)),
|
||||||
// TODO: favourite
|
)),
|
||||||
DropdownMenuItem(
|
ActionIcon(
|
||||||
child: Text('/'),
|
message: "",
|
||||||
value: '/',
|
icon: Icons.arrow_drop_down,
|
||||||
)
|
onTap: () async {
|
||||||
],
|
final renderBox = locationBarKey.currentContext
|
||||||
onChanged: (path) {
|
?.findRenderObject() as RenderBox;
|
||||||
if (path is String && path.isNotEmpty) {
|
locationBarKey.currentContext?.size;
|
||||||
openDirectory(path, isLocal: isLocal);
|
|
||||||
}
|
final size = renderBox.size;
|
||||||
})
|
final offset = renderBox.localToGlobal(Offset.zero);
|
||||||
]);
|
|
||||||
|
final x = offset.dx;
|
||||||
|
final y = offset.dy + size.height + 1;
|
||||||
|
|
||||||
|
final peerPlatform = (await bind.sessionGetPlatform(
|
||||||
|
id: _ffi.id, isRemote: !isLocal))
|
||||||
|
.toLowerCase();
|
||||||
|
final List<MenuEntryBase> menuItems;
|
||||||
|
if (peerPlatform == "windows") {
|
||||||
|
menuItems = [];
|
||||||
|
final loadingTag =
|
||||||
|
_ffi.dialogManager.showLoading("Waiting");
|
||||||
|
try {
|
||||||
|
final fd =
|
||||||
|
await model.fetchDirectory("/", isLocal, false);
|
||||||
|
for (var entry in fd.entries) {
|
||||||
|
menuItems.add(MenuEntryButton(
|
||||||
|
childBuilder: (TextStyle? style) => Text(
|
||||||
|
entry.name,
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
proc: () {
|
||||||
|
openDirectory(entry.name, isLocal: isLocal);
|
||||||
|
Get.back();
|
||||||
|
}));
|
||||||
|
menuItems.add(MenuEntryDivider());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_ffi.dialogManager.dismissByTag(loadingTag);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
menuItems = [
|
||||||
|
MenuEntryButton(
|
||||||
|
childBuilder: (TextStyle? style) => Text(
|
||||||
|
'/',
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
proc: () {
|
||||||
|
openDirectory('/', isLocal: isLocal);
|
||||||
|
Get.back();
|
||||||
|
}),
|
||||||
|
MenuEntryDivider()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_menu.showMenu(
|
||||||
|
context: context,
|
||||||
|
position: RelativeRect.fromLTRB(x, y, x, y),
|
||||||
|
elevation: 4,
|
||||||
|
items: menuItems
|
||||||
|
.map((e) => e.build(
|
||||||
|
context,
|
||||||
|
MenuConfig(
|
||||||
|
commonColor:
|
||||||
|
CustomPopupMenuTheme.commonColor,
|
||||||
|
height: CustomPopupMenuTheme.height,
|
||||||
|
dividerHeight:
|
||||||
|
CustomPopupMenuTheme.dividerHeight,
|
||||||
|
boxWidth: size.width)))
|
||||||
|
.expand((i) => i)
|
||||||
|
.toList());
|
||||||
|
},
|
||||||
|
iconSize: 20,
|
||||||
|
)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BreadCrumbItem> getPathBreadCrumbItems(
|
List<BreadCrumbItem> getPathBreadCrumbItems(
|
||||||
|
@ -99,12 +99,14 @@ class MenuConfig {
|
|||||||
|
|
||||||
final double height;
|
final double height;
|
||||||
final double dividerHeight;
|
final double dividerHeight;
|
||||||
|
final double? boxWidth;
|
||||||
final Color commonColor;
|
final Color commonColor;
|
||||||
|
|
||||||
const MenuConfig(
|
const MenuConfig(
|
||||||
{required this.commonColor,
|
{required this.commonColor,
|
||||||
this.height = kMinInteractiveDimension,
|
this.height = kMinInteractiveDimension,
|
||||||
this.dividerHeight = 16.0});
|
this.dividerHeight = 16.0,
|
||||||
|
this.boxWidth});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MenuEntryBase<T> {
|
abstract class MenuEntryBase<T> {
|
||||||
@ -190,49 +192,51 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
return mod_menu.PopupMenuItem(
|
return mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
child: TextButton(
|
child: Container(
|
||||||
child: Container(
|
width: conf.boxWidth,
|
||||||
padding: padding,
|
child: TextButton(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
child: Container(
|
||||||
constraints:
|
padding: padding,
|
||||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
alignment: AlignmentDirectional.centerStart,
|
||||||
child: Row(
|
constraints: BoxConstraints(
|
||||||
children: [
|
minHeight: conf.height, maxHeight: conf.height),
|
||||||
Text(
|
child: Row(
|
||||||
opt.text,
|
children: [
|
||||||
style: TextStyle(
|
Text(
|
||||||
color: Theme.of(context).textTheme.titleLarge?.color,
|
opt.text,
|
||||||
fontSize: MenuConfig.fontSize,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.normal),
|
color: Theme.of(context).textTheme.titleLarge?.color,
|
||||||
|
fontSize: MenuConfig.fontSize,
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: MenuConfig.iconScale,
|
||||||
|
child: Obx(() => opt.value == curOption.value
|
||||||
|
? IconButton(
|
||||||
|
padding: const EdgeInsets.fromLTRB(
|
||||||
|
8.0, 0.0, 8.0, 0.0),
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
focusColor: Colors.transparent,
|
||||||
|
onPressed: () {},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: conf.commonColor,
|
||||||
|
))
|
||||||
|
: const SizedBox.shrink()),
|
||||||
|
))),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: Align(
|
onPressed: () {
|
||||||
alignment: Alignment.centerRight,
|
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
child: Transform.scale(
|
Navigator.pop(context);
|
||||||
scale: MenuConfig.iconScale,
|
}
|
||||||
child: Obx(() => opt.value == curOption.value
|
setOption(opt.value);
|
||||||
? IconButton(
|
},
|
||||||
padding: const EdgeInsets.fromLTRB(
|
)),
|
||||||
8.0, 0.0, 8.0, 0.0),
|
|
||||||
hoverColor: Colors.transparent,
|
|
||||||
focusColor: Colors.transparent,
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: conf.commonColor,
|
|
||||||
))
|
|
||||||
: const SizedBox.shrink()),
|
|
||||||
))),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
setOption(opt.value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,48 +289,50 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
return mod_menu.PopupMenuItem(
|
return mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
child: TextButton(
|
child: Container(
|
||||||
child: Container(
|
width: conf.boxWidth,
|
||||||
padding: padding,
|
child: TextButton(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
child: Container(
|
||||||
constraints:
|
padding: padding,
|
||||||
BoxConstraints(minHeight: conf.height, maxHeight: conf.height),
|
alignment: AlignmentDirectional.centerStart,
|
||||||
child: Row(
|
constraints: BoxConstraints(
|
||||||
children: [
|
minHeight: conf.height, maxHeight: conf.height),
|
||||||
Text(
|
child: Row(
|
||||||
opt.text,
|
children: [
|
||||||
style: TextStyle(
|
Text(
|
||||||
color: Theme.of(context).textTheme.titleLarge?.color,
|
opt.text,
|
||||||
fontSize: MenuConfig.fontSize,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.normal),
|
color: Theme.of(context).textTheme.titleLarge?.color,
|
||||||
|
fontSize: MenuConfig.fontSize,
|
||||||
|
fontWeight: FontWeight.normal),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Transform.scale(
|
||||||
|
scale: MenuConfig.iconScale,
|
||||||
|
child: Obx(() => opt.value == curOption.value
|
||||||
|
? IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
focusColor: Colors.transparent,
|
||||||
|
onPressed: () {},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: conf.commonColor,
|
||||||
|
))
|
||||||
|
: const SizedBox.shrink())),
|
||||||
|
)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: Align(
|
onPressed: () {
|
||||||
alignment: Alignment.centerRight,
|
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
child: Transform.scale(
|
Navigator.pop(context);
|
||||||
scale: MenuConfig.iconScale,
|
}
|
||||||
child: Obx(() => opt.value == curOption.value
|
setOption(opt.value);
|
||||||
? IconButton(
|
},
|
||||||
padding: EdgeInsets.zero,
|
)),
|
||||||
hoverColor: Colors.transparent,
|
|
||||||
focusColor: Colors.transparent,
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: conf.commonColor,
|
|
||||||
))
|
|
||||||
: const SizedBox.shrink())),
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (opt.dismissOnClicked && Navigator.canPop(context)) {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
setOption(opt.value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,55 +407,57 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
|||||||
mod_menu.PopupMenuItem(
|
mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
child: TextButton(
|
child: Container(
|
||||||
child: Container(
|
width: conf.boxWidth,
|
||||||
padding: padding,
|
child: TextButton(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
child: Container(
|
||||||
height: conf.height,
|
padding: padding,
|
||||||
child: Row(children: [
|
alignment: AlignmentDirectional.centerStart,
|
||||||
Obx(() => Text(
|
height: conf.height,
|
||||||
text,
|
child: Row(children: [
|
||||||
style: textStyle!.value,
|
Obx(() => Text(
|
||||||
)),
|
text,
|
||||||
Expanded(
|
style: textStyle!.value,
|
||||||
child: Align(
|
)),
|
||||||
alignment: Alignment.centerRight,
|
Expanded(
|
||||||
child: Transform.scale(
|
child: Align(
|
||||||
scale: MenuConfig.iconScale,
|
alignment: Alignment.centerRight,
|
||||||
child: Obx(() {
|
child: Transform.scale(
|
||||||
if (switchType == SwitchType.sswitch) {
|
scale: MenuConfig.iconScale,
|
||||||
return Switch(
|
child: Obx(() {
|
||||||
value: curOption.value,
|
if (switchType == SwitchType.sswitch) {
|
||||||
onChanged: (v) {
|
return Switch(
|
||||||
if (super.dismissOnClicked &&
|
value: curOption.value,
|
||||||
Navigator.canPop(context)) {
|
onChanged: (v) {
|
||||||
Navigator.pop(context);
|
if (super.dismissOnClicked &&
|
||||||
}
|
Navigator.canPop(context)) {
|
||||||
setOption(v);
|
Navigator.pop(context);
|
||||||
},
|
}
|
||||||
);
|
setOption(v);
|
||||||
} else {
|
},
|
||||||
return Checkbox(
|
);
|
||||||
value: curOption.value,
|
} else {
|
||||||
onChanged: (v) {
|
return Checkbox(
|
||||||
if (super.dismissOnClicked &&
|
value: curOption.value,
|
||||||
Navigator.canPop(context)) {
|
onChanged: (v) {
|
||||||
Navigator.pop(context);
|
if (super.dismissOnClicked &&
|
||||||
}
|
Navigator.canPop(context)) {
|
||||||
setOption(v);
|
Navigator.pop(context);
|
||||||
},
|
}
|
||||||
);
|
setOption(v);
|
||||||
}
|
},
|
||||||
})),
|
);
|
||||||
))
|
}
|
||||||
])),
|
})),
|
||||||
onPressed: () {
|
))
|
||||||
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
])),
|
||||||
Navigator.pop(context);
|
onPressed: () {
|
||||||
}
|
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
setOption(!curOption.value);
|
Navigator.pop(context);
|
||||||
},
|
}
|
||||||
),
|
setOption(!curOption.value);
|
||||||
|
},
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -606,7 +614,9 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal);
|
fontWeight: FontWeight.normal);
|
||||||
super.enabled ??= true.obs;
|
super.enabled ??= true.obs;
|
||||||
return Obx(() => TextButton(
|
return Obx(() => Container(
|
||||||
|
width: conf.boxWidth,
|
||||||
|
child: TextButton(
|
||||||
onPressed: super.enabled!.value
|
onPressed: super.enabled!.value
|
||||||
? () {
|
? () {
|
||||||
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
@ -623,7 +633,7 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
child: childBuilder(
|
child: childBuilder(
|
||||||
super.enabled!.value ? enabledStyle : disabledStyle),
|
super.enabled!.value ? enabledStyle : disabledStyle),
|
||||||
),
|
),
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -811,14 +811,16 @@ class ActionIcon extends StatelessWidget {
|
|||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Function() onTap;
|
final Function() onTap;
|
||||||
final bool isClose;
|
final bool isClose;
|
||||||
final double? size;
|
final double iconSize;
|
||||||
|
final double boxSize;
|
||||||
const ActionIcon(
|
const ActionIcon(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.isClose,
|
this.isClose = false,
|
||||||
this.size})
|
this.iconSize = _kActionIconSize,
|
||||||
|
this.boxSize = _kTabBarHeight - 1})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -834,14 +836,14 @@ class ActionIcon extends StatelessWidget {
|
|||||||
onHover: (value) => hover.value = value,
|
onHover: (value) => hover.value = value,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: size ?? (_kTabBarHeight - 1),
|
height: boxSize,
|
||||||
width: size ?? (_kTabBarHeight - 1),
|
width: boxSize,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
icon,
|
icon,
|
||||||
color: hover.value && isClose
|
color: hover.value && isClose
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: MyTheme.tabbar(context).unSelectedIconColor,
|
: MyTheme.tabbar(context).unSelectedIconColor,
|
||||||
size: _kActionIconSize,
|
size: iconSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -221,7 +221,7 @@ void runConnectionManagerScreen() async {
|
|||||||
windowManager.setOpacity(1)
|
windowManager.setOpacity(1)
|
||||||
]);
|
]);
|
||||||
// ensure
|
// ensure
|
||||||
windowManager.setAlignment(Alignment.topRight);
|
windowManager.setAlignment(Alignment.topRight);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +402,10 @@ class FileModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<FileDirectory> fetchDirectory(path, isLocal, showHidden) async {
|
||||||
|
return await _fileFetcher.fetchDirectory(path, isLocal, showHidden);
|
||||||
|
}
|
||||||
|
|
||||||
void pushHistory(bool isLocal) {
|
void pushHistory(bool isLocal) {
|
||||||
final history = isLocal ? localHistory : remoteHistory;
|
final history = isLocal ? localHistory : remoteHistory;
|
||||||
final currPath = isLocal ? currentLocalDir.path : currentRemoteDir.path;
|
final currPath = isLocal ? currentLocalDir.path : currentRemoteDir.path;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user