diff --git a/lib/features/workspace/pages/desktop/workspace_desktop.dart b/lib/features/workspace/pages/desktop/workspace_desktop.dart index fd39b6a..54ac060 100644 --- a/lib/features/workspace/pages/desktop/workspace_desktop.dart +++ b/lib/features/workspace/pages/desktop/workspace_desktop.dart @@ -1,7 +1,6 @@ // lib/features/workspace/pages/workspace_desktop.dart (Fully Modified) - -import 'package:cookethflow/core/helpers/responsive_layout.helper.dart' as rh; import 'package:cookethflow/core/providers/supabase_provider.dart'; +import 'package:cookethflow/core/utils/enums.dart'; import 'package:cookethflow/features/workspace/pages/canvas_page.dart'; import 'package:cookethflow/features/workspace/providers/canvas_provider.dart'; import 'package:cookethflow/features/workspace/providers/workspace_provider.dart'; @@ -10,6 +9,7 @@ import 'package:cookethflow/features/workspace/widgets/node_editing_toolbox.dart import 'package:cookethflow/features/workspace/widgets/toolbar.dart'; import 'package:cookethflow/features/workspace/widgets/undo_redo_button.dart'; import 'package:cookethflow/features/workspace/widgets/workspace_drawer.dart'; +import 'package:cookethflow/features/workspace/widgets/workspace_shortcuts.dart'; // shortcut file import 'package:cookethflow/features/workspace/widgets/zoom_control_button.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -17,78 +17,169 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:cookethflow/features/workspace/widgets/object_text_editor.dart'; import 'package:vector_math/vector_math_64.dart' as vector_math; -class WorkspaceDesktop extends StatelessWidget { +class WorkspaceDesktop extends StatefulWidget { const WorkspaceDesktop({super.key}); @override - Widget build(BuildContext context) { - bool isDesktop = - rh.ResponsiveLayoutHelper.getDeviceType(context) == - rh.DeviceType.desktop; - - return Consumer2(builder: (context, provider,suprovider, child) { - return Scaffold( - backgroundColor: provider.currentWorkspaceColor, - body: Padding( - padding: EdgeInsets.symmetric(horizontal: 40.w, vertical: 40.h), - child: Stack( - clipBehavior: Clip.none, - children: [ - const CanvasPage(), - - const WorkspaceDrawer(), - SizedBox(width: 20.w), - Positioned(top: 0, left: 0.21.sw, child: UndoRedoButton(su: suprovider,)), - // MODIFIED: Pass the WorkspaceProvider instance to the button - Positioned(top: 0, right: 0.001.sw, child: ExportProjectButton(su: suprovider, wp: provider)), - - Positioned(right: 0, top: 0.10.sh, child: ToolBar()), - - Positioned( - bottom: 0.h, - right: 0.w, - child: ZoomControlButton(), - ), + _WorkspaceDesktopState createState() => _WorkspaceDesktopState(); +} - Consumer2( - builder: (context, workspaceProvider, canvasProvider, child) { - return ListenableBuilder( - listenable: canvasProvider.transformationController, - builder: (context, child) { - if (workspaceProvider.shouldShowObjectToolbox) { - final selectedObject = workspaceProvider.canvasObjects[ - workspaceProvider.currentlySelectedObjectId!]!; - final objectBounds = selectedObject.getBounds(); - final matrix = - canvasProvider.transformationController.value; +class _WorkspaceDesktopState extends State { + final FocusNode _focusNode = FocusNode(); - final transformedTopCenter = matrix.transform3( - vector_math.Vector3(objectBounds.topCenter.dx, - objectBounds.topCenter.dy, 0)); + @override + void initState() { + super.initState(); + // Request focus when the widget is first built + _focusNode.requestFocus(); + } - final screenPosition = Offset( - transformedTopCenter.x, transformedTopCenter.y); + @override + void dispose() { + // Clean up the focus node when the widget is disposed + _focusNode.dispose(); + super.dispose(); + } - const double toolboxWidth = 240; - const double toolboxHeight = 48; + @override + Widget build(BuildContext context) { + // Define actions + final Map> actions = { + PointerIntent: CallbackAction( + onInvoke: (intent) { + final provider = Provider.of( + context, + listen: false, + ); + provider.changeDrawMode(DrawMode.pointer); + return null; + }, + ), + PanIntent: CallbackAction( + onInvoke: (intent) { + final provider = Provider.of( + context, + listen: false, + ); + provider.changeDrawMode(DrawMode.hand); + return null; + }, + ), + TextIntent: CallbackAction( + onInvoke: (intent) { + final provider = Provider.of( + context, + listen: false, + ); + provider.changeDrawMode(DrawMode.textBox); + return null; + }, + ), + }; - return Positioned( - left: screenPosition.dx - (toolboxWidth / 2), - top: screenPosition.dy - toolboxHeight - 15, - child: const NodeEditingToolbox(), - ); - } - return const SizedBox.shrink(); - }, - ); - }, + return Consumer2( + builder: (context, provider, suprovider, child) { + return Shortcuts( + shortcuts: workspaceShortCut, + child: Actions( + actions: actions, + child: Focus( + focusNode: _focusNode, + child: Scaffold( + backgroundColor: provider.currentWorkspaceColor, + body: GestureDetector( + onTap: () { + // This ensures focus is regained when the user taps on the canvas + _focusNode.requestFocus(); + }, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 40.w, + vertical: 40.h, + ), + child: Stack( + clipBehavior: Clip.none, + children: [ + const CanvasPage(), + const WorkspaceDrawer(), + SizedBox(width: 20.w), + Positioned( + top: 0, + left: 0.21.sw, + child: UndoRedoButton(su: suprovider), + ), + Positioned( + top: 0, + right: 0.001.sw, + child: ExportProjectButton( + su: suprovider, + wp: provider, + ), + ), + Positioned(right: 0, top: 0.10.sh, child: ToolBar()), + Positioned( + bottom: 0.h, + right: 0.w, + child: ZoomControlButton(), + ), + Consumer2( + builder: ( + context, + workspaceProvider, + canvasProvider, + child, + ) { + return ListenableBuilder( + listenable: + canvasProvider.transformationController, + builder: (context, child) { + if (workspaceProvider.shouldShowObjectToolbox) { + final selectedObject = + workspaceProvider + .canvasObjects[workspaceProvider + .currentlySelectedObjectId!]!; + final objectBounds = + selectedObject.getBounds(); + final matrix = + canvasProvider + .transformationController + .value; + final transformedTopCenter = matrix + .transform3( + vector_math.Vector3( + objectBounds.topCenter.dx, + objectBounds.topCenter.dy, + 0, + ), + ); + final screenPosition = Offset( + transformedTopCenter.x, + transformedTopCenter.y, + ); + const double toolboxWidth = 240; + const double toolboxHeight = 48; + return Positioned( + left: + screenPosition.dx - (toolboxWidth / 2), + top: screenPosition.dy - toolboxHeight - 15, + child: const NodeEditingToolbox(), + ); + } + return const SizedBox.shrink(); + }, + ); + }, + ), + const ObjectTextEditor(), + ], + ), + ), + ), ), - - const ObjectTextEditor(), - ], + ), ), - ), - ); - }); + ); + }, + ); } -} \ No newline at end of file +} diff --git a/lib/features/workspace/widgets/workspace_shortcuts.dart b/lib/features/workspace/widgets/workspace_shortcuts.dart new file mode 100644 index 0000000..0e1e2bc --- /dev/null +++ b/lib/features/workspace/widgets/workspace_shortcuts.dart @@ -0,0 +1,21 @@ +// it is the file to control the keyboard shortcuts +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + + +// define intents +class PointerIntent extends Intent {} + +class PanIntent extends Intent {} + +class TextIntent extends Intent {} + +// define shortcuts + +final Map workspaceShortCut = { + LogicalKeySet(LogicalKeyboardKey.keyP): PointerIntent(), + LogicalKeySet(LogicalKeyboardKey.keyA): PanIntent(), + LogicalKeySet(LogicalKeyboardKey.keyT): TextIntent(), +}; + + diff --git a/pubspec.lock b/pubspec.lock index c65cb80..d988715 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" ffi: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" jwt_decode: dependency: transitive description: @@ -569,26 +569,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -1054,10 +1054,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -1158,10 +1158,10 @@ packages: dependency: "direct main" description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -1219,5 +1219,5 @@ packages: source: hosted version: "2.1.0" sdks: - dart: ">=3.7.2 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.27.0"