mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #532 from AppFlowy-IO/feat/grid_keyboard_shortcut
Feat/grid keyboard shortcut
This commit is contained in:
commit
fbf7c9c9b8
@ -24,6 +24,9 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
|||||||
url: cellData?.url ?? "",
|
url: cellData?.url ?? "",
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
updateURL: (String url) {
|
||||||
|
cellContext.saveCellData(url, deduplicate: true);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -53,6 +56,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
|||||||
@freezed
|
@freezed
|
||||||
class URLCellEvent with _$URLCellEvent {
|
class URLCellEvent with _$URLCellEvent {
|
||||||
const factory URLCellEvent.initial() = _InitialCell;
|
const factory URLCellEvent.initial() = _InitialCell;
|
||||||
|
const factory URLCellEvent.updateURL(String url) = _UpdateURL;
|
||||||
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
|
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import 'layout/sizes.dart';
|
|||||||
import 'widgets/row/grid_row.dart';
|
import 'widgets/row/grid_row.dart';
|
||||||
import 'widgets/footer/grid_footer.dart';
|
import 'widgets/footer/grid_footer.dart';
|
||||||
import 'widgets/header/grid_header.dart';
|
import 'widgets/header/grid_header.dart';
|
||||||
|
import 'widgets/shortcuts.dart';
|
||||||
import 'widgets/toolbar/grid_toolbar.dart';
|
import 'widgets/toolbar/grid_toolbar.dart';
|
||||||
|
|
||||||
class GridPage extends StatefulWidget {
|
class GridPage extends StatefulWidget {
|
||||||
@ -40,7 +41,7 @@ class _GridPageState extends State<GridPage> {
|
|||||||
return state.loadingState.map(
|
return state.loadingState.map(
|
||||||
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
|
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
|
||||||
finish: (result) => result.successOrFail.fold(
|
finish: (result) => result.successOrFail.fold(
|
||||||
(_) => const FlowyGrid(),
|
(_) => const GridShortcuts(child: FlowyGrid()),
|
||||||
(err) => FlowyErrorPage(err.toString()),
|
(err) => FlowyErrorPage(err.toString()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -18,8 +18,8 @@ abstract class GridCellAccessory implements Widget {
|
|||||||
|
|
||||||
typedef AccessoryBuilder = List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext);
|
typedef AccessoryBuilder = List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext);
|
||||||
|
|
||||||
abstract class AccessoryWidget extends Widget {
|
abstract class CellAccessory extends Widget {
|
||||||
const AccessoryWidget({Key? key}) : super(key: key);
|
const CellAccessory({Key? key}) : super(key: key);
|
||||||
|
|
||||||
// The hover will show if the onFocus's value is true
|
// The hover will show if the onFocus's value is true
|
||||||
ValueNotifier<bool>? get isFocus;
|
ValueNotifier<bool>? get isFocus;
|
||||||
@ -28,7 +28,7 @@ abstract class AccessoryWidget extends Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AccessoryHover extends StatefulWidget {
|
class AccessoryHover extends StatefulWidget {
|
||||||
final AccessoryWidget child;
|
final CellAccessory child;
|
||||||
final EdgeInsets contentPadding;
|
final EdgeInsets contentPadding;
|
||||||
const AccessoryHover({
|
const AccessoryHover({
|
||||||
required this.child,
|
required this.child,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
|
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
@ -8,6 +9,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'cell_accessory.dart';
|
import 'cell_accessory.dart';
|
||||||
|
import 'cell_shortcuts.dart';
|
||||||
import 'checkbox_cell.dart';
|
import 'checkbox_cell.dart';
|
||||||
import 'date_cell/date_cell.dart';
|
import 'date_cell/date_cell.dart';
|
||||||
import 'number_cell.dart';
|
import 'number_cell.dart';
|
||||||
@ -48,7 +50,7 @@ class BlankCell extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget, CellContainerFocustable {
|
abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellFocustable, CellShortcuts {
|
||||||
GridCellWidget({Key? key}) : super(key: key);
|
GridCellWidget({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -58,31 +60,47 @@ abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget,
|
|||||||
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
|
List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus();
|
final GridCellFocusListener beginFocus = GridCellFocusListener();
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Map<CellKeyboardKey, CellKeyboardAction> shortcutHandlers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GridCellState<T extends GridCellWidget> extends State<T> {
|
abstract class GridCellState<T extends GridCellWidget> extends State<T> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
widget.requestBeginFocus.setListener(() => requestBeginFocus());
|
widget.beginFocus.setListener(() => requestBeginFocus());
|
||||||
|
widget.shortcutHandlers[CellKeyboardKey.onCopy] = () => onCopy();
|
||||||
|
widget.shortcutHandlers[CellKeyboardKey.onInsert] = () {
|
||||||
|
Clipboard.getData("text/plain").then((data) {
|
||||||
|
final s = data?.text;
|
||||||
|
if (s is String) {
|
||||||
|
onInsert(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant T oldWidget) {
|
void didUpdateWidget(covariant T oldWidget) {
|
||||||
if (oldWidget != this) {
|
if (oldWidget != this) {
|
||||||
widget.requestBeginFocus.setListener(() => requestBeginFocus());
|
widget.beginFocus.setListener(() => requestBeginFocus());
|
||||||
}
|
}
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
widget.requestBeginFocus.removeAllListener();
|
widget.beginFocus.removeAllListener();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestBeginFocus();
|
void requestBeginFocus();
|
||||||
|
|
||||||
|
String? onCopy() => null;
|
||||||
|
|
||||||
|
void onInsert(String value) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCellState<T> {
|
abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCellState<T> {
|
||||||
@ -90,6 +108,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
widget.shortcutHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus();
|
||||||
_listenOnFocusNodeChanged();
|
_listenOnFocusNodeChanged();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@ -104,6 +123,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
widget.shortcutHandlers.clear();
|
||||||
focusNode.removeAllListener();
|
focusNode.removeAllListener();
|
||||||
focusNode.dispose();
|
focusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -127,7 +147,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
|
|||||||
Future<void> focusChanged() async {}
|
Future<void> focusChanged() async {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GridCellRequestBeginFocus extends ChangeNotifier {
|
class GridCellFocusListener extends ChangeNotifier {
|
||||||
VoidCallback? _listener;
|
VoidCallback? _listener;
|
||||||
|
|
||||||
void setListener(VoidCallback listener) {
|
void setListener(VoidCallback listener) {
|
||||||
@ -194,9 +214,8 @@ class CellStateNotifier extends ChangeNotifier {
|
|||||||
bool get onEnter => _onEnter;
|
bool get onEnter => _onEnter;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class CellContainerFocustable {
|
abstract class CellFocustable {
|
||||||
// Listen on the requestBeginFocus if the
|
GridCellFocusListener get beginFocus;
|
||||||
GridCellRequestBeginFocus get requestBeginFocus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CellContainer extends StatelessWidget {
|
class CellContainer extends StatelessWidget {
|
||||||
@ -220,7 +239,7 @@ class CellContainer extends StatelessWidget {
|
|||||||
child: Selector<CellStateNotifier, bool>(
|
child: Selector<CellStateNotifier, bool>(
|
||||||
selector: (context, notifier) => notifier.isFocus,
|
selector: (context, notifier) => notifier.isFocus,
|
||||||
builder: (context, isFocus, _) {
|
builder: (context, isFocus, _) {
|
||||||
Widget container = Center(child: child);
|
Widget container = Center(child: GridCellShortcuts(child: child));
|
||||||
child.isFocus.addListener(() {
|
child.isFocus.addListener(() {
|
||||||
Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.isFocus.value;
|
Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.isFocus.value;
|
||||||
});
|
});
|
||||||
@ -235,7 +254,7 @@ class CellContainer extends StatelessWidget {
|
|||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => child.requestBeginFocus.notify(),
|
onTap: () => child.beginFocus.notify(),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(maxWidth: width, minHeight: 46),
|
constraints: BoxConstraints(maxWidth: width, minHeight: 46),
|
||||||
decoration: _makeBoxDecoration(context, isFocus),
|
decoration: _makeBoxDecoration(context, isFocus),
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
typedef CellKeyboardAction = dynamic Function();
|
||||||
|
|
||||||
|
enum CellKeyboardKey {
|
||||||
|
onEnter,
|
||||||
|
onCopy,
|
||||||
|
onInsert,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CellShortcuts extends Widget {
|
||||||
|
const CellShortcuts({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
Map<CellKeyboardKey, CellKeyboardAction> get shortcutHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellShortcuts extends StatelessWidget {
|
||||||
|
final CellShortcuts child;
|
||||||
|
const GridCellShortcuts({required this.child, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Shortcuts(
|
||||||
|
shortcuts: {
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.enter): const GridCellEnterIdent(),
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyC): const GridCellCopyIntent(),
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyV): const GridCellInsertIntent(),
|
||||||
|
},
|
||||||
|
child: Actions(
|
||||||
|
actions: {
|
||||||
|
GridCellEnterIdent: GridCellEnterAction(child: child),
|
||||||
|
GridCellCopyIntent: GridCellCopyAction(child: child),
|
||||||
|
GridCellInsertIntent: GridCellInsertAction(child: child),
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellEnterIdent extends Intent {
|
||||||
|
const GridCellEnterIdent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellEnterAction extends Action<GridCellEnterIdent> {
|
||||||
|
final CellShortcuts child;
|
||||||
|
GridCellEnterAction({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void invoke(covariant GridCellEnterIdent intent) {
|
||||||
|
final callback = child.shortcutHandlers[CellKeyboardKey.onEnter];
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellCopyIntent extends Intent {
|
||||||
|
const GridCellCopyIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellCopyAction extends Action<GridCellCopyIntent> {
|
||||||
|
final CellShortcuts child;
|
||||||
|
GridCellCopyAction({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void invoke(covariant GridCellCopyIntent intent) {
|
||||||
|
final callback = child.shortcutHandlers[CellKeyboardKey.onCopy];
|
||||||
|
if (callback == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final s = callback();
|
||||||
|
if (s is String) {
|
||||||
|
Clipboard.setData(ClipboardData(text: s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellInsertIntent extends Intent {
|
||||||
|
const GridCellInsertIntent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridCellInsertAction extends Action<GridCellInsertIntent> {
|
||||||
|
final CellShortcuts child;
|
||||||
|
GridCellInsertAction({required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void invoke(covariant GridCellInsertIntent intent) {
|
||||||
|
final callback = child.shortcutHandlers[CellKeyboardKey.onInsert];
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,6 @@ class _CheckboxCellState extends GridCellState<CheckboxCell> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
final cellContext = widget.cellContextBuilder.build();
|
final cellContext = widget.cellContextBuilder.build();
|
||||||
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
|
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,4 +58,13 @@ class _CheckboxCellState extends GridCellState<CheckboxCell> {
|
|||||||
void requestBeginFocus() {
|
void requestBeginFocus() {
|
||||||
_cellBloc.add(const CheckboxCellEvent.select());
|
_cellBloc.add(const CheckboxCellEvent.select());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? onCopy() {
|
||||||
|
if (_cellBloc.state.isSelected) {
|
||||||
|
return "Yes";
|
||||||
|
} else {
|
||||||
|
return "No";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,10 @@ class DateCell extends GridCellWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DateCell> createState() => _DateCellState();
|
GridCellState<DateCell> createState() => _DateCellState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DateCellState extends State<DateCell> {
|
class _DateCellState extends GridCellState<DateCell> {
|
||||||
late DateCellBloc _cellBloc;
|
late DateCellBloc _cellBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -89,4 +89,10 @@ class _DateCellState extends State<DateCell> {
|
|||||||
_cellBloc.close();
|
_cellBloc.close();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void requestBeginFocus() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? onCopy() => _cellBloc.state.dateStr;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -81,4 +80,14 @@ class _NumberCellState extends GridFocusNodeCellState<NumberCell> {
|
|||||||
String contentFromState(NumberCellState state) {
|
String contentFromState(NumberCellState state) {
|
||||||
return state.content.fold((l) => l, (r) => "");
|
return state.content.fold((l) => l, (r) => "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? onCopy() {
|
||||||
|
return _cellBloc.state.content.fold((content) => content, (r) => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInsert(String value) {
|
||||||
|
_cellBloc.add(NumberCellEvent.updateCell(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,4 +92,12 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? onCopy() => _cellBloc.state.content;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInsert(String value) {
|
||||||
|
_cellBloc.add(TextCellEvent.updateText(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
|
|
||||||
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
||||||
final GridURLCellContext cellContext;
|
final GridURLCellContext cellContext;
|
||||||
const URLCellEditor({required this.cellContext, Key? key}) : super(key: key);
|
final VoidCallback completed;
|
||||||
|
const URLCellEditor({required this.cellContext, required this.completed, Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<URLCellEditor> createState() => _URLCellEditorState();
|
State<URLCellEditor> createState() => _URLCellEditorState();
|
||||||
@ -16,10 +17,12 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
|||||||
static void show(
|
static void show(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
GridURLCellContext cellContext,
|
GridURLCellContext cellContext,
|
||||||
|
VoidCallback completed,
|
||||||
) {
|
) {
|
||||||
FlowyOverlay.of(context).remove(identifier());
|
FlowyOverlay.of(context).remove(identifier());
|
||||||
final editor = URLCellEditor(
|
final editor = URLCellEditor(
|
||||||
cellContext: cellContext,
|
cellContext: cellContext,
|
||||||
|
completed: completed,
|
||||||
);
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -46,6 +49,11 @@ class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate {
|
|||||||
bool asBarrier() {
|
bool asBarrier() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didRemove() {
|
||||||
|
completed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _URLCellEditorState extends State<URLCellEditor> {
|
class _URLCellEditorState extends State<URLCellEditor> {
|
||||||
|
@ -131,10 +131,13 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
Future<void> _openUrlOrEdit(String url) async {
|
Future<void> _openUrlOrEdit(String url) async {
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
if (url.isNotEmpty && await canLaunchUrl(uri)) {
|
||||||
|
widget.isFocus.value = false;
|
||||||
await launchUrl(uri);
|
await launchUrl(uri);
|
||||||
} else {
|
} else {
|
||||||
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
||||||
URLCellEditor.show(context, cellContext);
|
URLCellEditor.show(context, cellContext, () {
|
||||||
|
widget.isFocus.value = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +145,14 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
void requestBeginFocus() {
|
void requestBeginFocus() {
|
||||||
_openUrlOrEdit(_cellBloc.state.url);
|
_openUrlOrEdit(_cellBloc.state.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? onCopy() => _cellBloc.state.content;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInsert(String value) {
|
||||||
|
_cellBloc.add(URLCellEvent.updateURL(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
||||||
@ -161,7 +172,7 @@ class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onTap() {
|
void onTap() {
|
||||||
URLCellEditor.show(anchorContext, cellContext);
|
URLCellEditor.show(anchorContext, cellContext, () {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class _RowDetailCell extends StatelessWidget {
|
|||||||
|
|
||||||
final gesture = GestureDetector(
|
final gesture = GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onTap: () => cell.requestBeginFocus.notify(),
|
onTap: () => cell.beginFocus.notify(),
|
||||||
child: AccessoryHover(
|
child: AccessoryHover(
|
||||||
child: cell,
|
child: cell,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class GridShortcuts extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const GridShortcuts({required this.child, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Shortcuts(
|
||||||
|
shortcuts: bindKeys([]),
|
||||||
|
child: Actions(
|
||||||
|
dispatcher: LoggingActionDispatcher(),
|
||||||
|
actions: const {},
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<ShortcutActivator, Intent> bindKeys(List<LogicalKeyboardKey> keys) {
|
||||||
|
return {for (var key in keys) LogicalKeySet(key): KeyboardKeyIdent(key)};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Type, Action<Intent>> bindActions() {
|
||||||
|
return {
|
||||||
|
KeyboardKeyIdent: KeyboardBindingAction(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyboardKeyIdent extends Intent {
|
||||||
|
final KeyboardKey key;
|
||||||
|
|
||||||
|
const KeyboardKeyIdent(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyboardBindingAction extends Action<KeyboardKeyIdent> {
|
||||||
|
KeyboardBindingAction();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void invoke(covariant KeyboardKeyIdent intent) {
|
||||||
|
// print(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggingActionDispatcher extends ActionDispatcher {
|
||||||
|
@override
|
||||||
|
Object? invokeAction(
|
||||||
|
covariant Action<Intent> action,
|
||||||
|
covariant Intent intent, [
|
||||||
|
BuildContext? context,
|
||||||
|
]) {
|
||||||
|
// print('Action invoked: $action($intent) from $context');
|
||||||
|
super.invokeAction(action, intent, context);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user