@@ -2,14 +2,20 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
22import 'package:flowy_infra_ui/style_widget/hover.dart' ;
33import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
44import 'package:flutter/widgets.dart' ;
5+ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart' ;
6+ import 'package:flowy_infra/theme.dart' ;
7+ import 'package:flutter/material.dart' ;
8+ import 'package:provider/provider.dart' ;
9+ import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart' ;
10+ import 'package:styled_widget/styled_widget.dart' ;
511import 'checkbox_cell.dart' ;
612import 'date_cell.dart' ;
713import 'number_cell.dart' ;
814import 'selection_cell/selection_cell.dart' ;
915import 'text_cell.dart' ;
1016
1117GridCellWidget buildGridCellWidget (GridCell gridCell, GridCellCache cellCache, {GridCellStyle ? style}) {
12- final key = ValueKey (gridCell.rowId + gridCell.field.id );
18+ final key = ValueKey (gridCell.cellId () );
1319
1420 final cellContextBuilder = GridCellContextBuilder (gridCell: gridCell, cellCache: cellCache);
1521
@@ -51,9 +57,157 @@ abstract class GridCellWidget extends HoverWidget {
5157}
5258
5359class GridCellRequestFocusNotifier extends ChangeNotifier {
60+ VoidCallback ? _listener;
61+
62+ @override
63+ void addListener (VoidCallback listener) {
64+ if (_listener != null ) {
65+ removeListener (_listener! );
66+ }
67+
68+ _listener = listener;
69+ super .addListener (listener);
70+ }
71+
72+ void removeAllListener () {
73+ if (_listener != null ) {
74+ removeListener (_listener! );
75+ }
76+ }
77+
5478 void notify () {
5579 notifyListeners ();
5680 }
5781}
5882
5983abstract class GridCellStyle {}
84+
85+ class CellSingleFocusNode extends FocusNode {
86+ VoidCallback ? _listener;
87+
88+ void setSingleListener (VoidCallback listener) {
89+ if (_listener != null ) {
90+ removeListener (_listener! );
91+ }
92+
93+ _listener = listener;
94+ super .addListener (listener);
95+ }
96+
97+ void removeSingleListener () {
98+ if (_listener != null ) {
99+ removeListener (_listener! );
100+ }
101+ }
102+ }
103+
104+ class CellStateNotifier extends ChangeNotifier {
105+ bool _isFocus = false ;
106+ bool _onEnter = false ;
107+
108+ set isFocus (bool value) {
109+ if (_isFocus != value) {
110+ _isFocus = value;
111+ notifyListeners ();
112+ }
113+ }
114+
115+ set onEnter (bool value) {
116+ if (_onEnter != value) {
117+ _onEnter = value;
118+ notifyListeners ();
119+ }
120+ }
121+
122+ bool get isFocus => _isFocus;
123+
124+ bool get onEnter => _onEnter;
125+ }
126+
127+ class CellContainer extends StatelessWidget {
128+ final GridCellWidget child;
129+ final Widget ? expander;
130+ final double width;
131+ final RegionStateNotifier rowStateNotifier;
132+ const CellContainer ({
133+ Key ? key,
134+ required this .child,
135+ required this .width,
136+ required this .rowStateNotifier,
137+ this .expander,
138+ }) : super (key: key);
139+
140+ @override
141+ Widget build (BuildContext context) {
142+ return ChangeNotifierProxyProvider <RegionStateNotifier , CellStateNotifier >(
143+ create: (_) => CellStateNotifier (),
144+ update: (_, row, cell) => cell! ..onEnter = row.onEnter,
145+ child: Selector <CellStateNotifier , bool >(
146+ selector: (context, notifier) => notifier.isFocus,
147+ builder: (context, isFocus, _) {
148+ Widget container = Center (child: child);
149+ child.onFocus.addListener (() {
150+ Provider .of <CellStateNotifier >(context, listen: false ).isFocus = child.onFocus.value;
151+ });
152+
153+ if (expander != null ) {
154+ container = _CellEnterRegion (child: container, expander: expander! );
155+ }
156+
157+ return GestureDetector (
158+ behavior: HitTestBehavior .translucent,
159+ onTap: () => child.requestFocus.notify (),
160+ child: Container (
161+ constraints: BoxConstraints (maxWidth: width, minHeight: 46 ),
162+ decoration: _makeBoxDecoration (context, isFocus),
163+ padding: GridSize .cellContentInsets,
164+ child: container,
165+ ),
166+ );
167+ },
168+ ),
169+ );
170+ }
171+
172+ BoxDecoration _makeBoxDecoration (BuildContext context, bool isFocus) {
173+ final theme = context.watch <AppTheme >();
174+ if (isFocus) {
175+ final borderSide = BorderSide (color: theme.main1, width: 1.0 );
176+ return BoxDecoration (border: Border .fromBorderSide (borderSide));
177+ } else {
178+ final borderSide = BorderSide (color: theme.shader5, width: 1.0 );
179+ return BoxDecoration (border: Border (right: borderSide, bottom: borderSide));
180+ }
181+ }
182+ }
183+
184+ class _CellEnterRegion extends StatelessWidget {
185+ final Widget child;
186+ final Widget expander;
187+ const _CellEnterRegion ({required this .child, required this .expander, Key ? key}) : super (key: key);
188+
189+ @override
190+ Widget build (BuildContext context) {
191+ return Selector <CellStateNotifier , bool >(
192+ selector: (context, notifier) => notifier.onEnter,
193+ builder: (context, onEnter, _) {
194+ List <Widget > children = [child];
195+ if (onEnter) {
196+ children.add (expander.positioned (right: 0 ));
197+ }
198+
199+ return MouseRegion (
200+ cursor: SystemMouseCursors .click,
201+ onEnter: (p) => Provider .of <CellStateNotifier >(context, listen: false ).onEnter = true ,
202+ onExit: (p) => Provider .of <CellStateNotifier >(context, listen: false ).onEnter = false ,
203+ child: Stack (
204+ alignment: AlignmentDirectional .center,
205+ fit: StackFit .expand,
206+ // alignment: AlignmentDirectional.centerEnd,
207+ children: children,
208+ ),
209+ );
210+ },
211+ );
212+ }
213+ }
0 commit comments