Skip to content

Commit

Permalink
Implement clipboard, closes #422
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Jul 16, 2023
1 parent ad5eb00 commit 74abe84
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 32 deletions.
4 changes: 4 additions & 0 deletions api/lib/src/helpers/asset_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extension AssetFileTypeHelper on AssetFileType {
return ['pdf'];
case AssetFileType.svg:
return ['svg'];
case AssetFileType.page:
return [];
}
}

Expand All @@ -26,6 +28,8 @@ extension AssetFileTypeHelper on AssetFileType {
return 'application/pdf';
case AssetFileType.svg:
return 'image/svg+xml';
case AssetFileType.page:
return 'application/json';
}
}

Expand Down
2 changes: 1 addition & 1 deletion api/lib/src/models/asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'data.dart';
part 'asset.freezed.dart';
part 'asset.g.dart';

enum AssetFileType { note, image, pdf, svg }
enum AssetFileType { note, page, image, pdf, svg }

@freezed
class AssetLocation with _$AssetLocation {
Expand Down
31 changes: 31 additions & 0 deletions app/lib/actions/paste.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:butterfly/bloc/document_bloc.dart';
import 'package:butterfly_api/butterfly_api.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lw_sysinfo/lw_sysinfo.dart';

import '../services/import.dart';

class PasteIntent extends Intent {
final BuildContext context;

const PasteIntent(this.context);
}

class PasteAction extends Action<PasteIntent> {
PasteAction();

@override
void invoke(PasteIntent intent) {
final bloc = intent.context.read<DocumentBloc>();
final state = bloc.state;
if (state is! DocumentLoadSuccess) return;
final clipboard = intent.context.read<ClipboardManager>().getContent();
if (clipboard == null) return;
final importService = intent.context.read<ImportService>();
try {
importService.import(AssetFileType.values.byName(clipboard.type),
clipboard.data, state.data);
} catch (_) {}
}
}
1 change: 0 additions & 1 deletion app/lib/bloc/document_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class DocumentBloc extends ReplayBloc<DocumentEvent, DocumentState> {
SettingsCubit settingsCubit,
NoteData initial,
AssetLocation location,
Renderer<Background> background,
List<Renderer<PadElement>> renderer, [
AssetService? assetService,
DocumentPage? page,
Expand Down
22 changes: 22 additions & 0 deletions app/lib/dialogs/elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ class ElementsDialog extends StatelessWidget {
child: ListView(
shrinkWrap: true,
children: [
MenuItemButton(
onPressed: () {
Navigator.of(context).pop(true);
context
.read<CurrentIndexCubit>()
.fetchHandler<HandHandler>()
?.copySelection(context, true);
},
leadingIcon: const PhosphorIcon(PhosphorIconsLight.scissors),
child: Text(AppLocalizations.of(context).cut),
),
MenuItemButton(
onPressed: () {
Navigator.of(context).pop(true);
context
.read<CurrentIndexCubit>()
.fetchHandler<HandHandler>()
?.copySelection(context, false);
},
leadingIcon: const PhosphorIcon(PhosphorIconsLight.copy),
child: Text(AppLocalizations.of(context).copy),
),
MenuItemButton(
leadingIcon: const PhosphorIcon(PhosphorIconsLight.copy),
onPressed: () {
Expand Down
28 changes: 28 additions & 0 deletions app/lib/handlers/hand.dart
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,28 @@ class HandHandler extends Handler<HandPainter> {
: null);
}

void copySelection(BuildContext context, [bool cut = false]) {
final bloc = context.read<DocumentBloc>();
final state = bloc.state;
if (state is! DocumentLoadSuccess) return;
if (cut) {
bloc.add(ElementsRemoved(_selected.map((r) => r.element).toList()));
}
final clipboard = (
type: AssetFileType.page.name,
data: Uint8List.fromList(
utf8.encode(
json.encode(
DocumentPage(content: _selected.map((e) => e.element).toList())
.toJson()),
),
),
);
context.read<ClipboardManager>().setContent(clipboard);
_selected.clear();
bloc.refresh();
}

@override
Map<Type, Action<Intent>> getActions(BuildContext context) {
final bloc = context.read<DocumentBloc>();
Expand All @@ -599,6 +621,12 @@ class HandHandler extends Handler<HandPainter> {
bloc.refresh();
return null;
}),
CopySelectionTextIntent:
CallbackAction<CopySelectionTextIntent>(onInvoke: (intent) {
copySelection(context, intent.collapseSelection);
return null;
}),
...super.getActions(context),
};
}
}
9 changes: 8 additions & 1 deletion app/lib/handlers/handler.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';

Expand All @@ -24,8 +25,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:lw_sysinfo/lw_sysinfo.dart';
import 'package:phosphor_flutter/phosphor_flutter.dart';

import '../actions/paste.dart';
import '../api/open.dart';
import '../cubits/current_index.dart';
import '../dialogs/camera.dart';
Expand Down Expand Up @@ -222,7 +225,11 @@ abstract class Handler<T> {

void dispose(DocumentBloc bloc) {}

Map<Type, Action<Intent>> getActions(BuildContext context) => {};
Map<Type, Action<Intent>> getActions(BuildContext context) => {
PasteTextIntent: CallbackAction<PasteTextIntent>(
onInvoke: (intent) =>
Actions.maybeInvoke(context, PasteIntent(context))),
};

MouseCursor? get cursor => null;
}
Expand Down
3 changes: 2 additions & 1 deletion app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -513,5 +513,6 @@
"toolbarPosition": "Toolbar position",
"rotate": "Rotate",
"spacer": "Spacer",
"navigationRail": "Navigation rail"
"navigationRail": "Navigation rail",
"cut": "Cut"
}
4 changes: 4 additions & 0 deletions app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:go_router/go_router.dart';
import 'package:lw_sysinfo/lw_sysinfo.dart';
import 'package:material_leap/l10n/leap_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart';
Expand Down Expand Up @@ -96,6 +97,7 @@ Future<void> main([List<String> args = const []]) async {
final argParser = ArgParser();
argParser.addOption('path', abbr: 'p');
final result = argParser.parse(args);
final clipboardManager = await SysInfo.getClipboardManager();
GeneralFileSystem.dataPath = result['path'];
runApp(
MultiRepositoryProvider(
Expand All @@ -104,6 +106,8 @@ Future<void> main([List<String> args = const []]) async {
create: (context) => DocumentFileSystem.fromPlatform()),
RepositoryProvider(
create: (context) => TemplateFileSystem.fromPlatform()),
RepositoryProvider<ClipboardManager>(
create: (context) => clipboardManager),
],
child: ButterflyApp(
prefs: prefs,
Expand Down
41 changes: 30 additions & 11 deletions app/lib/services/import.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class ImportService {
return importSvg(bytes, document, position);
case AssetFileType.pdf:
return importPdf(bytes, document, position, true);
default:
return Future.value();
case AssetFileType.page:
return importPage(bytes, document, position);
}
}

Expand Down Expand Up @@ -123,24 +123,43 @@ class ImportService {
if (position == null) {
return data;
}
final firstPos = position;
final docPage = data.getPage();
if (docPage == null) return null;
final areas = docPage.areas
return _importPage(data.getPage(), document, position) ??
data.createDocument(
createdAt: DateTime.now(),
);
}

NoteData? importPage(Uint8List bytes, NoteData document, [Offset? position]) {
try {
final firstPos = position ?? Offset.zero;
final page = DocumentPage.fromJson(json.decode(utf8.decode(bytes)));
return _importPage(page, document, firstPos);
} catch (e) {
showDialog(
context: context,
builder: (context) =>
UnknownImportConfirmationDialog(message: e.toString()),
);
}
return null;
}

NoteData? _importPage(DocumentPage? page, NoteData document,
[Offset? position]) {
final firstPos = position ?? Offset.zero;
if (page == null) return null;
final areas = page.areas
.map((e) => e.copyWith(position: e.position + firstPos.toPoint()))
.toList();
final content = docPage.content
final content = page.content
.map((e) =>
Renderer.fromInstance(e)
.transform(position: firstPos, relative: true)
?.element ??
e)
.toList();
return _submit(document,
elements: content, areas: areas, choosePosition: true) ??
data.createDocument(
createdAt: DateTime.now(),
);
elements: content, areas: areas, choosePosition: true);
}

Future<bool> _importTemplate(NoteData template) async {
Expand Down
33 changes: 17 additions & 16 deletions app/lib/views/main.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
import 'package:butterfly/actions/areas.dart';
import 'package:butterfly/actions/background.dart';
import 'package:butterfly/actions/change_path.dart';
import 'package:butterfly/actions/color_palette.dart';
import 'package:butterfly/actions/export.dart';
import 'package:butterfly/actions/image_export.dart';
import 'package:butterfly/actions/new.dart';
import 'package:butterfly/actions/pdf_export.dart';
import 'package:butterfly/actions/redo.dart';
import 'package:butterfly/actions/save.dart';
import 'package:butterfly/actions/settings.dart';
import 'package:butterfly/actions/svg_export.dart';
import 'package:butterfly/actions/undo.dart';
import 'package:butterfly/api/file_system/file_system.dart';
import 'package:butterfly/bloc/document_bloc.dart';
import 'package:butterfly/cubits/current_index.dart';
Expand All @@ -33,11 +20,25 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../actions/areas.dart';
import '../actions/background.dart';
import '../actions/change_painter.dart';
import '../actions/change_path.dart';
import '../actions/color_palette.dart';
import '../actions/exit.dart';
import '../actions/export.dart';
import '../actions/image_export.dart';
import '../actions/new.dart';
import '../actions/next.dart';
import '../actions/packs.dart';
import '../actions/paste.dart';
import '../actions/pdf_export.dart';
import '../actions/previous.dart';
import '../actions/redo.dart';
import '../actions/save.dart';
import '../actions/settings.dart';
import '../actions/svg_export.dart';
import '../actions/undo.dart';
import '../main.dart';
import '../models/viewport.dart';
import '../services/asset.dart';
Expand Down Expand Up @@ -85,6 +86,7 @@ class _ProjectPageState extends State<ProjectPage> {
ExitIntent: ExitAction(),
NextIntent: NextAction(),
PreviousIntent: PreviousAction(),
PasteIntent: PasteAction(),
};

@override
Expand Down Expand Up @@ -124,7 +126,6 @@ class _ProjectPageState extends State<ProjectPage> {
settingsCubit,
document,
widget.location ?? const AssetLocation(path: ''),
BoxBackgroundRenderer(const BoxBackground()),
[],
);
_bloc?.load();
Expand Down Expand Up @@ -205,9 +206,9 @@ class _ProjectPageState extends State<ProjectPage> {
setState(() {
_transformCubit = TransformCubit();
_currentIndexCubit = CurrentIndexCubit(settingsCubit, _transformCubit!,
CameraViewport.unbaked(ToolRenderer()), null);
CameraViewport.unbaked(ToolRenderer(), background), null);
_bloc = DocumentBloc(_currentIndexCubit!, settingsCubit, document!,
location!, background, renderers, assetService, page, pageName);
location!, renderers, assetService, page, pageName);
_importService = ImportService(context, _bloc);
});
} catch (e, stackTrace) {
Expand Down
9 changes: 9 additions & 0 deletions app/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
lw_sysinfo:
dependency: "direct main"
description:
path: "packages/lw_sysinfo"
ref: "205347161111273fd40d371c70ab6342bdcabf5c"
resolved-ref: "205347161111273fd40d371c70ab6342bdcabf5c"
url: "https://github.com/LinwoodDev/dart_pkgs.git"
source: git
version: "0.0.1"
markdown:
dependency: "direct main"
description:
Expand Down
5 changes: 5 additions & 0 deletions app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ dependencies:
url: https://github.com/LinwoodDev/dart_pkgs.git
ref: 75a6afbbe765b628529c0e13b4117917ea0fd8e3
path: packages/material_leap
lw_sysinfo:
git:
url: https://github.com/LinwoodDev/dart_pkgs.git
ref: 205347161111273fd40d371c70ab6342bdcabf5c
path: packages/lw_sysinfo
flutter_localized_locales: ^2.0.4
dynamic_color: ^1.6.6
popover: ^0.2.8+2
Expand Down
4 changes: 3 additions & 1 deletion fastlane/metadata/android/en-US/changelogs/69.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Add del to delete selected to hand painter ([#435](https://github.com/LinwoodDev/Butterfly/issues/435))
* Add svg icon to asset file type svg
* Add asset painter ([#431](https://github.com/LinwoodDev/Butterfly/issues/431))
* Add clipboard (cut, copy, paste) ([#422](https://github.com/LinwoodDev/Butterfly/issues/422))
* Fix mobile ui
* Fix hand tool corner selection not working on mobile
* Fix waypoints not updating in navigator
* Fix waypoints not updating in navigator
* Fix background won't be loaded

0 comments on commit 74abe84

Please sign in to comment.