diff --git a/.github/workflows/windows_ci_cd.yaml b/.github/workflows/windows_ci_cd.yaml index 2f3f1e1..7c7c196 100644 --- a/.github/workflows/windows_ci_cd.yaml +++ b/.github/workflows/windows_ci_cd.yaml @@ -1,7 +1,7 @@ on: - workflow_dispatch - # push: - # branches: [main] + # workflow_dispatch + push: + branches: [main] name: Windows diff --git a/lib/App/Controllers/Project/Single/project_single_config_controller.dart b/lib/App/Controllers/Project/Single/project_single_config_controller.dart index 12f91a2..4ed0e86 100644 --- a/lib/App/Controllers/Project/Single/project_single_config_controller.dart +++ b/lib/App/Controllers/Project/Single/project_single_config_controller.dart @@ -1,6 +1,7 @@ import 'package:flutterpp/App/Controllers/Project/Single/project_single_controller.dart'; import 'package:flutterpp/App/Services/Cmd/cmd_init_getx_mvc_services.dart'; import 'package:flutterpp/App/Views/Global/build_overlay.dart'; +import 'package:flutterpp/Config/app_print.dart'; import 'package:get/get.dart'; class ProjectSingleConfigController extends GetxController { @@ -26,12 +27,17 @@ class ProjectSingleConfigController extends GetxController { Future startConfig() async { if (useController.projectLocalPath.isEmpty) return; - await Get.showOverlay( - asyncFunction: () async { - await _cmd.init(useController.projectLocalPath); - await useController.checkIfFlutterPPProject(); - }, - loadingWidget: const BuildOverlay(), - ); + + try { + await Get.showOverlay( + asyncFunction: () async { + await _cmd.init(useController.projectLocalPath); + await useController.checkIfFlutterPPProject(); + }, + loadingWidget: const BuildOverlay(), + ); + } catch (e) { + AppPrint.printError('Error starting project config: $e'); + } } } diff --git a/lib/App/Providers/Cmd/cmd_read_create_dir_provider.dart b/lib/App/Providers/Cmd/cmd_read_create_dir_provider.dart index 5e67db0..f934941 100644 --- a/lib/App/Providers/Cmd/cmd_read_create_dir_provider.dart +++ b/lib/App/Providers/Cmd/cmd_read_create_dir_provider.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutterpp/Config/app_print.dart'; import 'package:get/get.dart'; -import 'package:process_run/process_run.dart'; +import 'package:process_run/cmd_run.dart'; class CmdReadCreateDirProvider { // list directory @@ -12,14 +12,28 @@ class CmdReadCreateDirProvider { String? option, }) async { try { - String command = GetPlatform.isWindows ? 'dir' : 'ls'; + Directory directory = Directory(path); - var result = await runExecutableArguments(command, [option ?? '', path]); + // check if directory exist + if (!await directory.exists()) { + AppPrint.print('❌directory not exist'); + return null; + } + + List list = directory.listSync(recursive: false); List res = []; - for (var item in result.outLines) { - if (item == '$path:') continue; - res.add(item); + for (var item in list) { + String name = ''; + + // get the last part of the path + if (GetPlatform.isWindows) { + name = item.path.split(r'\').last; + } else { + name = item.path.split('/').last; + } + + res.add(name); } return res; @@ -31,7 +45,18 @@ class CmdReadCreateDirProvider { Future createDirectory(String path) async { try { - ProcessResult res = await runExecutableArguments('mkdir', ['-p', path]); + //ProcessResult res = = await runExecutableArguments('mkdir', ['-p', path]); + + ProcessCmd cmd = ProcessCmd( + 'mkdir', + [ + GetPlatform.isWindows ? '' : '-p', + path, + ], + runInShell: true, + ); + ProcessResult res = await runCmd(cmd); + AppPrint.print('mkdir: ${res.stdout}'); } catch (e) { AppPrint.print('Error creating directory: $e'); @@ -43,7 +68,17 @@ class CmdReadCreateDirProvider { String command = GetPlatform.isWindows ? 'type nul' : 'touch'; try { - await runExecutableArguments('touch', [path]); + // await runExecutableArguments('touch', [path]); + + ProcessCmd cmd = ProcessCmd( + command, + [path], + runInShell: true, + ); + + ProcessResult res = await runCmd(cmd); + + AppPrint.print('touch: ${res.stdout}'); } catch (e) { AppPrint.print('Error creating file: $e'); } diff --git a/lib/App/Providers/Device/file_maneger_provider.dart b/lib/App/Providers/Device/file_maneger_provider.dart index ef5b61c..29c5cb8 100644 --- a/lib/App/Providers/Device/file_maneger_provider.dart +++ b/lib/App/Providers/Device/file_maneger_provider.dart @@ -271,9 +271,13 @@ class FileManegerProvider { return; } - // open folder - - ProcessResult res = await Process.run(command, [location]); + // open directory + ProcessResult res = await Process.run( + command, + [location], + workingDirectory: location, + runInShell: true, + ); AppPrint.print(res.stdout); }, diff --git a/lib/App/Services/Cmd/cmd_read_create_dir_services.dart b/lib/App/Services/Cmd/cmd_read_create_dir_services.dart index 2393fcf..eb9025a 100644 --- a/lib/App/Services/Cmd/cmd_read_create_dir_services.dart +++ b/lib/App/Services/Cmd/cmd_read_create_dir_services.dart @@ -1,4 +1,5 @@ import 'package:flutterpp/App/Providers/Cmd/cmd_read_create_dir_provider.dart'; +import 'package:flutterpp/Config/app_print.dart'; import 'package:json2yaml/json2yaml.dart'; class CmdReadCreateDirServices { @@ -8,12 +9,18 @@ class CmdReadCreateDirServices { Future isFlutterPPProject(String path) async { List? res = await _dirProvider.listDirectory(path); - // check if theres a .flutterpp file - if (res != null) { - return res.contains('flutterpp.yaml'); + // if res is null + if (res == null) { + AppPrint.print('res is null'); + return false; + } + + for (var item in res) { + AppPrint.print('item: $item'); } - return false; + // check if theres a .flutterpp file + return res.contains('flutterpp.yaml'); } // read all files in a directory diff --git a/lib/App/Views/Global/build_appbar.dart b/lib/App/Views/Global/build_appbar.dart index b1b4bb5..7b829ef 100644 --- a/lib/App/Views/Global/build_appbar.dart +++ b/lib/App/Views/Global/build_appbar.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutterpp/App/Views/Global/build_windows_buttons.dart'; import 'package:get/get.dart'; +import 'package:window_manager/window_manager.dart'; class BuildAppBar extends StatelessWidget implements PreferredSizeWidget { final bool? hasBackButton; @@ -16,63 +18,130 @@ class BuildAppBar extends StatelessWidget implements PreferredSizeWidget { @override Size get preferredSize => const Size.fromHeight(30); + @override + Widget build(BuildContext context) { + if (GetPlatform.isWindows) { + return DragToMoveArea( + child: _BuildAppBar( + preferredSize: preferredSize, + hasBackButton: hasBackButton, + onBack: onBack, + title: title, + ), + ); + } + + return _BuildAppBar( + preferredSize: preferredSize, + hasBackButton: hasBackButton, + onBack: onBack, + title: title, + ); + } +} + +class _BuildAppBar extends StatelessWidget { + const _BuildAppBar({ + super.key, + required this.preferredSize, + required this.hasBackButton, + required this.onBack, + required this.title, + }); + + final Size preferredSize; + final bool? hasBackButton; + final VoidCallback? onBack; + final String? title; + @override Widget build(BuildContext context) { return AppBar( toolbarHeight: preferredSize.height, - title: SizedBox( + title: const SizedBox( width: double.infinity, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, + ), + leading: const SizedBox.shrink(), + flexibleSpace: Container( + height: preferredSize.height, + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(color: Colors.grey, width: 0.2), + ), + ), + child: Stack( + alignment: Alignment.center, children: [ - if (hasBackButton != null && hasBackButton!) - SizedBox( - height: preferredSize.height, - width: 0, - child: VerticalDivider(color: Colors.grey.withOpacity(0.35)), - ), - if (hasBackButton != null && hasBackButton!) - InkWell( - onTap: onBack ?? () => Get.back(), - child: Container( - height: preferredSize.height, - width: preferredSize.height, - decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.1), - ), - child: Icon( - Icons.arrow_back, - color: Get.theme.colorScheme.secondary, - size: 15, - ), - ), + Positioned( + left: GetPlatform.isWindows ? 0 : 70, + child: BuildLeading( + hasBackButton: hasBackButton, + preferredSize: preferredSize, + onBack: onBack, ), - SizedBox( - height: preferredSize.height, - width: 0, - child: VerticalDivider(color: Colors.grey.withOpacity(0.35)), ), - Expanded( - child: Center( - child: Text( - title ?? 'title', - style: Get.textTheme.bodySmall?.copyWith( - fontStyle: FontStyle.normal, - ), - ), + Text( + title ?? 'title', + style: Get.textTheme.bodySmall?.copyWith( + fontStyle: FontStyle.normal, ), ), + if (GetPlatform.isWindows) + const Positioned(right: 0, child: BuildWindowsButtons()) ], ), ), - leading: const SizedBox.shrink(), - flexibleSpace: Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(color: Colors.grey, width: 0.2), + ); + } +} + +class BuildLeading extends StatelessWidget { + const BuildLeading({ + super.key, + required this.hasBackButton, + required this.preferredSize, + required this.onBack, + }); + + final bool? hasBackButton; + final Size preferredSize; + final VoidCallback? onBack; + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (hasBackButton != null && hasBackButton! & GetPlatform.isMacOS) + SizedBox( + height: preferredSize.height, + width: 0, + child: VerticalDivider(color: Colors.grey.withOpacity(0.35)), + ), + if (hasBackButton != null && hasBackButton!) + InkWell( + onTap: onBack ?? () => Get.back(), + child: Container( + height: preferredSize.height, + width: preferredSize.height, + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + ), + child: Icon( + Icons.arrow_back, + color: Get.theme.colorScheme.secondary, + size: 15, + ), + ), + ), + SizedBox( + height: preferredSize.height, + width: 0, + child: VerticalDivider( + color: Colors.grey.withOpacity(0.35), ), ), - ), + ], ); } } diff --git a/lib/App/Views/Global/build_appbar_platform_button.dart b/lib/App/Views/Global/build_appbar_platform_button.dart new file mode 100644 index 0000000..9305598 --- /dev/null +++ b/lib/App/Views/Global/build_appbar_platform_button.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:heroicons/heroicons.dart'; + +class BuildAppbarPlatformButton extends StatelessWidget { + final bool hover; + final double size; + final VoidCallback? onTap; + final Color beforeHoverColor, afterHoverColor; + final HeroIcons icon; + const BuildAppbarPlatformButton({ + super.key, + required this.hover, + required this.size, + this.onTap, + required this.beforeHoverColor, + required this.afterHoverColor, + required this.icon, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: AnimatedContainer( + height: size, + width: size, + duration: const Duration(milliseconds: 100), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: hover ? afterHoverColor : beforeHoverColor, + ), + child: Visibility( + visible: hover, + child: HeroIcon( + icon, + size: 10, + color: Colors.black54, + ), + ), + ), + ); + } +} diff --git a/lib/App/Views/Global/build_hover_widget.dart b/lib/App/Views/Global/build_hover_widget.dart new file mode 100644 index 0000000..574d218 --- /dev/null +++ b/lib/App/Views/Global/build_hover_widget.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class BuildHoverWidget extends StatefulWidget { + // function takes a bool value and returns a widget + final Widget Function(bool)? child; + const BuildHoverWidget({ + super.key, + this.child, + }); + + @override + State createState() => _BuildHoverWidgetState(); +} + +class _BuildHoverWidgetState extends State { + bool isHover = false; + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) { + setState(() { + isHover = true; + }); + }, + onExit: (_) { + setState(() { + isHover = false; + }); + }, + child: widget.child?.call(isHover) ?? const SizedBox.shrink(), + ); + } +} diff --git a/lib/App/Views/Global/build_layout.dart b/lib/App/Views/Global/build_layout.dart index 5614af4..c17af43 100644 --- a/lib/App/Views/Global/build_layout.dart +++ b/lib/App/Views/Global/build_layout.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutterpp/App/Controllers/Global/app_update_controller.dart'; import 'package:flutterpp/App/Views/Global/build_rail.dart'; +import 'package:flutterpp/App/Views/Global/build_windows_buttons.dart'; import 'package:get/get.dart'; import 'package:sizer/sizer.dart'; import 'package:updat/theme/chips/floating_with_silent_download.dart'; import 'package:updat/updat.dart'; +import 'package:window_manager/window_manager.dart'; class BuildLayout extends StatelessWidget { final List> tabs; @@ -27,7 +29,9 @@ class BuildLayout extends StatelessWidget { children: [ Column( children: [ - BuildMacAppbar(tabs: tabs), + if (GetPlatform.isWindows) + DragToMoveArea(child: BuildLayoutAppbar(tabs: tabs)), + if (GetPlatform.isMacOS) BuildLayoutAppbar(tabs: tabs), Expanded( child: Row( children: [ @@ -88,9 +92,9 @@ class BuildAppUpdateWidget extends StatelessWidget { } } -class BuildMacAppbar extends StatelessWidget { +class BuildLayoutAppbar extends StatelessWidget { final List> tabs; - const BuildMacAppbar({super.key, required this.tabs}); + const BuildLayoutAppbar({super.key, required this.tabs}); @override Widget build(BuildContext context) { @@ -106,16 +110,24 @@ class BuildMacAppbar extends StatelessWidget { ), ), ), - child: Center( - child: Text( - tabs - .firstWhere((el) => el['isActive'] == true)['title'] - .toString() - .capitalizeFirst!, - style: Get.textTheme.titleSmall!.copyWith( - fontSize: 12, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + tabs + .firstWhere((el) => el['isActive'] == true)['title'] + .toString() + .capitalizeFirst!, + style: Get.textTheme.titleSmall!.copyWith( + fontSize: 12, + ), ), - ), + if (GetPlatform.isWindows) + const Positioned( + right: 0, + child: BuildWindowsButtons(), + ), + ], ), ); } diff --git a/lib/App/Views/Global/build_windows_buttons.dart b/lib/App/Views/Global/build_windows_buttons.dart new file mode 100644 index 0000000..ca28ea9 --- /dev/null +++ b/lib/App/Views/Global/build_windows_buttons.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutterpp/App/Views/Global/build_appbar_platform_button.dart'; +import 'package:flutterpp/App/Views/Global/build_hover_widget.dart'; +import 'package:flutterpp/Config/app_window_config.dart'; +import 'package:get/get.dart'; +import 'package:heroicons/heroicons.dart'; + +class BuildWindowsButtons extends StatelessWidget { + const BuildWindowsButtons({ + super.key, + }); + + @override + Widget build(BuildContext context) { + final win = AppWindowConfig().getWindowManager; + final systemTray = AppWindowConfig().getSystemTray; + + return BuildHoverWidget( + child: (hover) => Row( + children: [ + // BuildAppbarPlatformButton( + // size: 12, + // hover: hover, + // icon: HeroIcons.arrowsPointingOut, + // onTap: () async { + // await win.setFullScreen(true); + // }, + // afterHoverColor: Colors.green, + // beforeHoverColor: + // Get.theme.colorScheme.onBackground.withOpacity(0.4), + // ), + // const SizedBox(width: 6), + BuildAppbarPlatformButton( + size: 12, + hover: hover, + icon: HeroIcons.minus, + onTap: () async => await win.minimize(), + afterHoverColor: Colors.yellow, + beforeHoverColor: + Get.theme.colorScheme.onBackground.withOpacity(0.4), + ), + const SizedBox(width: 6), + BuildAppbarPlatformButton( + size: 12, + hover: hover, + icon: HeroIcons.xMark, + afterHoverColor: Colors.red, + onTap: () async { + await win.close(); + await systemTray.destroy(); + }, + beforeHoverColor: + Get.theme.colorScheme.onBackground.withOpacity(0.4), + ), + const SizedBox(width: 6), + ], + ), + ); + } +} diff --git a/lib/App/Views/Pages/Project/Pages/project_single_page.dart b/lib/App/Views/Pages/Project/Pages/project_single_page.dart index 8f3f11d..c3f016e 100644 --- a/lib/App/Views/Pages/Project/Pages/project_single_page.dart +++ b/lib/App/Views/Pages/Project/Pages/project_single_page.dart @@ -7,6 +7,7 @@ import 'package:flutterpp/App/Views/Pages/Project/Widgets/build_single_project_n import 'package:flutterpp/App/Views/Pages/Project/Widgets/build_single_project_start_config.dart'; import 'package:flutterpp/App/Views/Pages/Project/Widgets/build_single_project_tab_view.dart'; import 'package:flutterpp/App/Views/Pages/Project/Widgets/build_single_project_tap_header.dart'; +import 'package:flutterpp/Config/app_print.dart'; import 'package:get/get.dart'; class ProjectSinglePage extends GetView { @@ -62,6 +63,7 @@ class BuildProjectSinglePage extends StatelessWidget { @override Widget build(BuildContext context) { + AppPrint.printDebug('project local path: ${controller.projectLocalPath}'); return Column( children: [ BuildSingleProjectHeader(controller: controller), diff --git a/lib/Config/app_window_config.dart b/lib/Config/app_window_config.dart index 57aab1d..edfe54d 100644 --- a/lib/Config/app_window_config.dart +++ b/lib/Config/app_window_config.dart @@ -4,6 +4,14 @@ import 'package:system_tray/system_tray.dart'; import 'package:window_manager/window_manager.dart'; class AppWindowConfig { + // window manager + final WindowManager windowManager = WindowManager.instance; + WindowManager get getWindowManager => windowManager; + + // system tray + final SystemTray systemTray = SystemTray(); + SystemTray get getSystemTray => systemTray; + // config Future config() async { // app icon @@ -11,8 +19,6 @@ class AppWindowConfig { ? 'assets/images/fav_logo.ico' : 'assets/images/fav_logo.png'; - final SystemTray systemTray = SystemTray(); - // We first init the systray menu await systemTray.initSystemTray( title: "", diff --git a/lib/main.dart b/lib/main.dart index 2a27e2b..046b27e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,6 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:get/get.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sizer/sizer.dart'; -import 'package:window_manager/window_manager.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -22,9 +21,7 @@ Future main() async { runApp( DefaultAssetBundle( bundle: SentryAssetBundle(), - child: GetPlatform.isWindows - ? const DragToMoveArea(child: MyApp()) - : const MyApp(), + child: const MyApp(), ), ); }, diff --git a/pubspec.yaml b/pubspec.yaml index 10caceb..db408c7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutterpp description: A new Flutter project. publish_to: "none" -version: 0.0.5 +version: 0.0.6 environment: sdk: ">=2.18.6 <3.0.0"