Skip to content

Commit

Permalink
drag & drop files onto supported text fields
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurHeitmann committed Sep 7, 2024
1 parent f61bc98 commit c6d2a56
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 29 deletions.
3 changes: 2 additions & 1 deletion lib/stateManagement/openFiles/types/SmdFileData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import '../../../fileTypeUtils/smd/smdReader.dart';
import '../../../fileTypeUtils/smd/smdWriter.dart';
import '../../../widgets/filesView/FileType.dart';
import '../../../widgets/filesView/types/genericTable/tableEditor.dart';
import '../../../widgets/propEditors/propTextField.dart';
import '../../Property.dart';
import '../../changesExporter.dart';
import '../../hasUuid.dart';
Expand Down Expand Up @@ -165,7 +166,7 @@ class SmdData extends ListNotifier<SmdEntryData> with CustomTableConfig, Undoabl
key: Key(entry.uuid),
cells: [
PropCellConfig(prop: entry.id),
PropCellConfig(prop: entry.text, allowMultiline: true),
PropCellConfig(prop: entry.text, options: const PropTFOptions(isMultiline: true)),
],
);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/stateManagement/openFiles/types/TmdFileData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import '../../../fileTypeUtils/tmd/tmdReader.dart';
import '../../../fileTypeUtils/tmd/tmdWriter.dart';
import '../../../widgets/filesView/FileType.dart';
import '../../../widgets/filesView/types/genericTable/tableEditor.dart';
import '../../../widgets/propEditors/propTextField.dart';
import '../../Property.dart';
import '../../changesExporter.dart';
import '../../hasUuid.dart';
Expand Down Expand Up @@ -165,7 +166,7 @@ class TmdData extends ListNotifier<TmdEntryData> with CustomTableConfig, Undoabl
key: Key(entry.uuid),
cells: [
PropCellConfig(prop: entry.id),
PropCellConfig(prop: entry.text, allowMultiline: true),
PropCellConfig(prop: entry.text, options: const PropTFOptions(isMultiline: true)),
],
);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/filesView/fileTabView.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ class _FileTabViewState extends ChangeNotifierState<FileTabView> {
children: [
for (var (i, file) in widget.viewArea.files.indexed)
IndexedStackIsVisible(
key: Key(file.uuid),
isVisible: i == currentFileIndex,
child: ConstrainedBox(
key: Key(file.uuid),
constraints: const BoxConstraints.expand(),
child: FocusTraversalGroup(
child: makeFileEditor(file),
Expand Down
6 changes: 3 additions & 3 deletions lib/widgets/filesView/types/SaveSlotDataEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class _InventoryTableConfig with CustomTableConfig {
key: Key(items[i].uuid),
cells: [
TextCellConfig(i.toString()),
PropCellConfig(prop: items[i].id, autocompleteOptions: _getItemIdAutocomplete),
PropCellConfig(prop: items[i].id, options: const PropTFOptions(autocompleteOptions: _getItemIdAutocomplete)),
PropCellConfig(prop: items[i].count),
PropCellConfig(prop: items[i].isActive),
]
Expand Down Expand Up @@ -279,7 +279,7 @@ class _WeaponTableConfig with CustomTableConfig {
key: Key(weapons[i].uuid),
cells: [
TextCellConfig(weapons[i].index.toString().padLeft(2, " ") + (i < _weaponsByIndex.length ? " (${_weaponsByIndex[i].item2})" : "")),
PropCellConfig(prop: weapons[i].id, autocompleteOptions: _getWeaponIdAutocomplete),
PropCellConfig(prop: weapons[i].id, options: const PropTFOptions(autocompleteOptions: _getWeaponIdAutocomplete)),
PropCellConfig(prop: weapons[i].level),
PropCellConfig(prop: weapons[i].isNew),
PropCellConfig(prop: weapons[i].hasNewStory),
Expand Down Expand Up @@ -394,7 +394,7 @@ class _StringListTableConfig with CustomTableConfig {
cells: [
PropCellConfig(
prop: strings[index].text,
autocompleteOptions: autocompleteOptions,
options: PropTFOptions(autocompleteOptions: autocompleteOptions),
),
CustomWidgetCellConfig(
IconButton(
Expand Down
12 changes: 3 additions & 9 deletions lib/widgets/filesView/types/genericTable/tableEditor.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

import 'dart:async';

import 'package:flutter/material.dart';

import '../../../../stateManagement/Property.dart';
Expand All @@ -11,7 +9,6 @@ import '../../../misc/nestedContextMenu.dart';
import '../../../propEditors/UnderlinePropTextField.dart';
import '../../../propEditors/propEditorFactory.dart';
import '../../../propEditors/propTextField.dart';
import '../../../propEditors/textFieldAutocomplete.dart';
import '../../../propEditors/transparentPropTextField.dart';
import '../../../theme/customTheme.dart';
import 'tableExporter.dart';
Expand All @@ -23,19 +20,16 @@ abstract class CellConfig {

class PropCellConfig extends CellConfig {
final Prop prop;
final bool allowMultiline;
final FutureOr<Iterable<AutocompleteConfig>> Function()? autocompleteOptions;
final PropTFOptions options;

PropCellConfig({ required this.prop, this.allowMultiline = false, this.autocompleteOptions });
PropCellConfig({ required this.prop, this.options = const PropTFOptions() });

@override
Widget makeWidget() => makePropEditor<TransparentPropTextField>(
prop,
PropTFOptions(
options.copyWith(
constraints: const BoxConstraints(minWidth: double.infinity, minHeight: 30),
isMultiline: allowMultiline,
useIntrinsicWidth: false,
autocompleteOptions: autocompleteOptions,
),
);

Expand Down
3 changes: 2 additions & 1 deletion lib/widgets/filesView/types/wtaWtpEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../../../stateManagement/Property.dart';
import '../../../stateManagement/listNotifier.dart';
import '../../../stateManagement/openFiles/types/WtaWtpData.dart';
import '../../../utils/utils.dart';
import '../../propEditors/propTextField.dart';
import '../../theme/customTheme.dart';
import 'genericTable/tableEditor.dart';

Expand Down Expand Up @@ -53,7 +54,7 @@ class _TexturesTableConfig with CustomTableConfig {
key: Key(textures[index].uuid),
cells: [
PropCellConfig(prop: textures[index].id),
PropCellConfig(prop: textures[index].path),
PropCellConfig(prop: textures[index].path, options: const PropTFOptions(isFilePath: true)),
CustomWidgetCellConfig(IconButton(
icon: const Icon(Icons.folder, size: 20),
onPressed: () => _selectTexture(index),
Expand Down
6 changes: 3 additions & 3 deletions lib/widgets/layout/searchPanel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class _SearchPanelState extends State<SearchPanel> {
const SizedBox()
],
),
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false)),
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false, isFolderPath: true)),
makePropEditor(extensions, const PropTFOptions(hintText: "Extensions (.xml, .rb, ...)", useIntrinsicWidth: false)),
].map((e) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
Expand Down Expand Up @@ -346,7 +346,7 @@ class _SearchPanelState extends State<SearchPanel> {
],
),
if (!useIndexedData.value) ...[
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false)),
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false, isFolderPath: true)),
]
].map((e) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
Expand All @@ -360,7 +360,7 @@ class _SearchPanelState extends State<SearchPanel> {
Widget _makeEstSearchOptions() {
return Column(
children: [
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false)),
makePropEditor(path, const PropTFOptions(hintText: "Path", useIntrinsicWidth: false, isFolderPath: true)),
...estOptionsNamed.map((opt) => RowSeparated(
crossAxisAlignment: CrossAxisAlignment.center,
separatorWidth: 5,
Expand Down
18 changes: 16 additions & 2 deletions lib/widgets/misc/dropTargetBuilder.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import 'dart:math';

import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';

Expand All @@ -15,15 +17,27 @@ class DropTargetBuilder extends StatefulWidget {
}

class _DropTargetBuilderState extends State<DropTargetBuilder> {
static int _dropping = 0;

bool isDropping = false;

@override
Widget build(BuildContext context) {
return DropTarget(
enable: ModalRoute.of(context)!.isCurrent && IndexedStackIsVisible.of(context) != false,
onDragEntered: (_) => setState(() => isDropping = true),
onDragExited: (_) => setState(() => isDropping = false),
onDragEntered: (_) {
_dropping += 1;
isDropping = true;
setState(() {});
},
onDragExited: (_) {
_dropping = max(0, _dropping - 1);
isDropping = false;
setState(() {});
},
onDragDone: (details) {
if (_dropping > 0)
return;
widget.onDrop(details.files.map((f) => f.path).toList());
},
child: widget.builder(context, isDropping),
Expand Down
5 changes: 4 additions & 1 deletion lib/widgets/misc/imagePreviewBuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';

import '../../fileTypeUtils/textures/ddsConverter.dart';
import '../../utils/utils.dart';

enum ImagePreviewState {
loading,
Expand Down Expand Up @@ -57,7 +58,9 @@ class _ImagePreviewBuilderState extends State<ImagePreviewBuilder> {
exists = true;
var sw = Stopwatch()..start();
image = texToPng(widget.path, maxHeight: widget.maxHeight, verbose: false)
..then((_) => print("Loaded image in ${sw.elapsedMilliseconds}ms"));
..then((_) {
debugOnly(() => print("Loaded image in ${sw.elapsedMilliseconds}ms"));
});
setState(() {});
}

Expand Down
8 changes: 4 additions & 4 deletions lib/widgets/misc/preferencesEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class _PreferencesEditorState extends ChangeNotifierState<PreferencesEditor> {
prop: exportPathProp,
onValid: (value) => exportPathProp.value = value,
validatorOnChange: (value) => value.isEmpty || Directory(value).existsSync() ? null : "Directory does not exist",
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30)),
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30), isFolderPath: true),
),
),
makeFilePickerButton(
Expand Down Expand Up @@ -162,7 +162,7 @@ class _PreferencesEditorState extends ChangeNotifierState<PreferencesEditor> {
prop: path,
onValid: (value) => widget.prefs.indexingPaths!.setPath(path, value),
validatorOnChange: (value) => Directory(value).existsSync() ? null : "Directory does not exist",
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30)),
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30), isFolderPath: true),
),
),
makeFilePickerButton(
Expand Down Expand Up @@ -263,7 +263,7 @@ class _PreferencesEditorState extends ChangeNotifierState<PreferencesEditor> {
prop: widget.prefs.waiExtractDir!,
onValid: (value) => widget.prefs.waiExtractDir!.value = value,
validatorOnChange: (value) => value.isEmpty || Directory(value).existsSync() ? null : "Directory does not exist",
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30)),
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30), isFolderPath: true),
),
),
makeFilePickerButton(
Expand All @@ -282,7 +282,7 @@ class _PreferencesEditorState extends ChangeNotifierState<PreferencesEditor> {
prop: widget.prefs.wwiseCliPath!,
onValid: (value) => widget.prefs.wwiseCliPath!.value = value.isNotEmpty ? findWwiseCliExe(value)! : "",
validatorOnChange: (value) => value.isEmpty || findWwiseCliExe(value) != null ? null : "Directory does not exist",
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30)),
options: const PropTFOptions(constraints: BoxConstraints(minHeight: 30), isFolderPath: true, isFilePath: true),
),
),
makeFilePickerButton(
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/propEditors/UnderlinePropTextField.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class _UnderlinePropTextFieldState extends PropTextFieldState {
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: getTheme(context).propBorderColor!, width: 2)),
),
child: intrinsicWidthWrapper(
child: applyWrappers(
child: ConstrainedBox(
constraints: widget.options.constraints,
child: Row(
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/propEditors/primaryPropTextField.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class _PrimaryPropTextFieldState extends PropTextFieldState {
child: Material(
color: getTheme(context).formElementBgColor,
borderRadius: BorderRadius.circular(8.0),
child: intrinsicWidthWrapper(
child: applyWrappers(
child: ConstrainedBox(
constraints: widget.options.constraints,
child: Row(
Expand Down
61 changes: 61 additions & 0 deletions lib/widgets/propEditors/propTextField.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';

import '../../stateManagement/Property.dart';
import '../../utils/utils.dart';
import '../misc/ChangeNotifierWidget.dart';
import '../misc/TextFieldFocusNode.dart';
import '../misc/dropTargetBuilder.dart';
import '../theme/customTheme.dart';
import 'DoubleClickablePropTextField.dart';
import 'UnderlinePropTextField.dart';
import 'primaryPropTextField.dart';
Expand All @@ -18,6 +21,8 @@ class PropTFOptions {
final BoxConstraints constraints;
final bool isMultiline;
final bool useIntrinsicWidth;
final bool isFolderPath;
final bool isFilePath;
final String? hintText;
final FutureOr<Iterable<AutocompleteConfig>> Function()? autocompleteOptions;

Expand All @@ -26,6 +31,8 @@ class PropTFOptions {
this.constraints = const BoxConstraints(minWidth: 50),
this.isMultiline = false,
this.useIntrinsicWidth = true,
this.isFolderPath = false,
this.isFilePath = false,
this.hintText,
this.autocompleteOptions,
});
Expand All @@ -35,6 +42,8 @@ class PropTFOptions {
BoxConstraints? constraints,
bool? isMultiline,
bool? useIntrinsicWidth,
bool? isFolderPath,
bool? isFilePath,
String? hintText,
FutureOr<Iterable<AutocompleteConfig>> Function()? autocompleteOptions,
}) {
Expand All @@ -43,6 +52,8 @@ class PropTFOptions {
constraints: constraints ?? this.constraints,
isMultiline: isMultiline ?? this.isMultiline,
useIntrinsicWidth: useIntrinsicWidth ?? this.useIntrinsicWidth,
isFolderPath: isFolderPath ?? this.isFolderPath,
isFilePath: isFilePath ?? this.isFilePath,
hintText: hintText ?? this.hintText,
autocompleteOptions: autocompleteOptions ?? this.autocompleteOptions,
);
Expand Down Expand Up @@ -178,7 +189,57 @@ abstract class PropTextFieldState<P extends Prop> extends ChangeNotifierState<Pr
onValid(text);
}

Widget applyWrappers({ required Widget child }) {
return pathDropTargetWrapper(
child: intrinsicWidthWrapper(
child: child,
),
);
}

Widget intrinsicWidthWrapper({ required Widget child }) => widget.options.useIntrinsicWidth
? IntrinsicWidth(child: child)
: child;

Widget pathDropTargetWrapper({ required Widget child }) => widget.options.isFolderPath || widget.options.isFilePath
? DropTargetBuilder(
onDrop: (files) async {
var file = files.first;
if (!widget.options.isFolderPath && await Directory(file).exists()) {
showToast("Expected a file, not a folder");
return;
}
if (!widget.options.isFilePath && await File(file).exists()) {
showToast("Expected a folder, not a file");
return;
}

controller.text = file;
onValid(file);
},
builder: (context, isDropping) => Stack(
children: [
child,
if (isDropping)
Positioned.fill(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: DecoratedBox(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: getTheme(context).editorBackgroundColor!.withOpacity(0.75),
blurRadius: 10,
spreadRadius: 5,
),
],
),
),
),
),
],
),
)
: child;

}
2 changes: 1 addition & 1 deletion lib/widgets/propEditors/transparentPropTextField.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class _TransparentPropTextFieldState extends PropTextFieldState {
notifier: widget.prop,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: intrinsicWidthWrapper(
child: applyWrappers(
child: ConstrainedBox(
constraints: widget.options.constraints,
child: Row(
Expand Down

0 comments on commit c6d2a56

Please sign in to comment.