Skip to content

Commit 596f263

Browse files
committed
Test editorFocusNode and stuff
1 parent 351b240 commit 596f263

10 files changed

Lines changed: 262 additions & 188 deletions

File tree

lib/common/constants/constants.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ final logger = AppLogger();
2828
/// Codec to encode and decode Markdown files in fleather.
2929
const parchmentMarkdownCodec = ParchmentMarkdownCodec();
3030

31-
/// Focus node of the note content text editor.
32-
final editorFocusNode = FocusNode(debugLabel: 'Editor focus node');
33-
3431
/// Utilities for the Storage Access Framework (SAF) APIs.
3532
final safUtil = SafUtil();
3633

lib/common/navigation/app_bars/notes/editor_app_bar.dart

Lines changed: 91 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -155,100 +155,105 @@ class _BackAppBarState extends ConsumerState<EditorAppBar> {
155155
return EmptyPlaceholder();
156156
}
157157

158-
final editorController = fleatherControllerNotifier.value;
159158
final showEditorModeButton = PreferenceKey.editorModeButton.getPreferenceOrDefault();
160159
final enableLabels = PreferenceKey.enableLabels.getPreferenceOrDefault();
161160

162161
return ValueListenableBuilder(
163-
valueListenable: editorHasFocusNotifier,
164-
builder: (context, editorHasFocus, child) {
162+
valueListenable: fleatherControllerNotifier,
163+
builder: (context, editorController, child) {
165164
return ValueListenableBuilder(
166-
valueListenable: isEditorInEditModeNotifier,
167-
builder: (context, isEditMode, child) {
168-
return AppBar(
169-
leading: BackButton(),
170-
actions: [
171-
if (note.status == NoteStatus.available) ...[
172-
if (note.type == NoteType.richText) ...[
173-
ValueListenableBuilder(
174-
valueListenable: fleatherControllerCanUndoNotifier,
175-
builder: (context, canUndo, child) {
176-
final enableUndo = editorHasFocus &&
177-
canUndo &&
178-
editorController != null &&
179-
editorController.canUndo &&
180-
isEditMode;
165+
valueListenable: editorHasFocusNotifier,
166+
builder: (context, editorHasFocus, child) {
167+
return ValueListenableBuilder(
168+
valueListenable: isEditorInEditModeNotifier,
169+
builder: (context, isEditMode, child) {
170+
return AppBar(
171+
leading: BackButton(),
172+
actions: [
173+
if (note.status == NoteStatus.available) ...[
174+
if (note.type == NoteType.richText) ...[
175+
ValueListenableBuilder(
176+
valueListenable: fleatherControllerCanUndoNotifier,
177+
builder: (context, canUndo, child) {
178+
final enableUndo = editorHasFocus &&
179+
canUndo &&
180+
editorController != null &&
181+
editorController.canUndo &&
182+
isEditMode;
181183

182-
return IconButton(
183-
icon: const Icon(Icons.undo),
184-
tooltip: l.tooltip_undo,
185-
onPressed: enableUndo ? undo : null,
186-
);
187-
},
188-
),
189-
ValueListenableBuilder(
190-
valueListenable: fleatherControllerCanRedoNotifier,
191-
builder: (context, canRedo, child) {
192-
final enableRedo = editorHasFocus && canRedo && isEditMode;
184+
return IconButton(
185+
icon: const Icon(Icons.undo),
186+
tooltip: l.tooltip_undo,
187+
onPressed: enableUndo ? undo : null,
188+
);
189+
},
190+
),
191+
ValueListenableBuilder(
192+
valueListenable: fleatherControllerCanRedoNotifier,
193+
builder: (context, canRedo, child) {
194+
final enableRedo = editorHasFocus && canRedo && isEditMode;
193195

194-
return IconButton(
195-
icon: const Icon(Icons.redo),
196-
tooltip: l.tooltip_redo,
197-
onPressed: enableRedo ? redo : null,
198-
);
199-
},
200-
),
201-
],
202-
if (showEditorModeButton)
203-
ValueListenableBuilder(
204-
valueListenable: isEditorInEditModeNotifier,
205-
builder: (context, isEditMode, child) => IconButton(
206-
icon: Icon(isEditMode ? Icons.visibility : Icons.edit),
207-
tooltip:
208-
isEditMode ? l.tooltip_fab_toggle_editor_mode_read : l.tooltip_fab_toggle_editor_mode_edit,
209-
onPressed: switchMode,
196+
return IconButton(
197+
icon: const Icon(Icons.redo),
198+
tooltip: l.tooltip_redo,
199+
onPressed: enableRedo ? redo : null,
200+
);
201+
},
202+
),
203+
],
204+
if (showEditorModeButton)
205+
ValueListenableBuilder(
206+
valueListenable: isEditorInEditModeNotifier,
207+
builder: (context, isEditMode, child) => IconButton(
208+
icon: Icon(isEditMode ? Icons.visibility : Icons.edit),
209+
tooltip: isEditMode
210+
? l.tooltip_fab_toggle_editor_mode_read
211+
: l.tooltip_fab_toggle_editor_mode_edit,
212+
onPressed: switchMode,
213+
),
214+
),
215+
PopupMenuButton<EditorAvailableMenuOption>(
216+
itemBuilder: (context) => ([
217+
EditorAvailableMenuOption.copy.popupMenuItem(context),
218+
EditorAvailableMenuOption.share.popupMenuItem(context),
219+
const PopupMenuDivider(),
220+
EditorAvailableMenuOption.togglePin.popupMenuItem(context, alternative: note.pinned),
221+
if (enableLabels) EditorAvailableMenuOption.selectLabels.popupMenuItem(context),
222+
const PopupMenuDivider(),
223+
EditorAvailableMenuOption.archive.popupMenuItem(context),
224+
EditorAvailableMenuOption.delete.popupMenuItem(context),
225+
const PopupMenuDivider(),
226+
EditorAvailableMenuOption.about.popupMenuItem(context),
227+
]),
228+
onSelected: onAvailableMenuOptionSelected,
229+
),
230+
],
231+
if (note.status == NoteStatus.archived)
232+
PopupMenuButton<EditorArchivedMenuOption>(
233+
itemBuilder: (context) => ([
234+
EditorArchivedMenuOption.copy.popupMenuItem(context),
235+
EditorArchivedMenuOption.share.popupMenuItem(context),
236+
const PopupMenuDivider(),
237+
EditorArchivedMenuOption.unarchive.popupMenuItem(context),
238+
const PopupMenuDivider(),
239+
EditorArchivedMenuOption.about.popupMenuItem(context),
240+
]),
241+
onSelected: onArchivedMenuOptionSelected,
210242
),
211-
),
212-
PopupMenuButton<EditorAvailableMenuOption>(
213-
itemBuilder: (context) => ([
214-
EditorAvailableMenuOption.copy.popupMenuItem(context),
215-
EditorAvailableMenuOption.share.popupMenuItem(context),
216-
const PopupMenuDivider(),
217-
EditorAvailableMenuOption.togglePin.popupMenuItem(context, alternative: note.pinned),
218-
if (enableLabels) EditorAvailableMenuOption.selectLabels.popupMenuItem(context),
219-
const PopupMenuDivider(),
220-
EditorAvailableMenuOption.archive.popupMenuItem(context),
221-
EditorAvailableMenuOption.delete.popupMenuItem(context),
222-
const PopupMenuDivider(),
223-
EditorAvailableMenuOption.about.popupMenuItem(context),
224-
]),
225-
onSelected: onAvailableMenuOptionSelected,
226-
),
227-
],
228-
if (note.status == NoteStatus.archived)
229-
PopupMenuButton<EditorArchivedMenuOption>(
230-
itemBuilder: (context) => ([
231-
EditorArchivedMenuOption.copy.popupMenuItem(context),
232-
EditorArchivedMenuOption.share.popupMenuItem(context),
233-
const PopupMenuDivider(),
234-
EditorArchivedMenuOption.unarchive.popupMenuItem(context),
235-
const PopupMenuDivider(),
236-
EditorArchivedMenuOption.about.popupMenuItem(context),
237-
]),
238-
onSelected: onArchivedMenuOptionSelected,
239-
),
240-
if (note.status == NoteStatus.deleted)
241-
PopupMenuButton<EditorDeletedMenuOption>(
242-
itemBuilder: (context) => ([
243-
EditorDeletedMenuOption.restore.popupMenuItem(context),
244-
EditorDeletedMenuOption.deletePermanently.popupMenuItem(context),
245-
const PopupMenuDivider(),
246-
EditorDeletedMenuOption.about.popupMenuItem(context),
247-
]),
248-
onSelected: onDeletedMenuOptionSelected,
249-
),
250-
Padding(padding: Paddings.appBarActionsEnd),
251-
],
243+
if (note.status == NoteStatus.deleted)
244+
PopupMenuButton<EditorDeletedMenuOption>(
245+
itemBuilder: (context) => ([
246+
EditorDeletedMenuOption.restore.popupMenuItem(context),
247+
EditorDeletedMenuOption.deletePermanently.popupMenuItem(context),
248+
const PopupMenuDivider(),
249+
EditorDeletedMenuOption.about.popupMenuItem(context),
250+
]),
251+
onSelected: onDeletedMenuOptionSelected,
252+
),
253+
Padding(padding: Paddings.appBarActionsEnd),
254+
],
255+
);
256+
},
252257
);
253258
},
254259
);

lib/navigation/navigator_utils.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class NavigatorUtils {
2929

3030
/// Pushes the notes editor route with its parameters [readOnly] and a [isNewNote].
3131
static void pushNotesEditor(BuildContext context, bool readOnly, bool isNewNote) {
32-
isEditorInEditModeNotifier.value = !PreferenceKey.openEditorReadingMode.getPreferenceOrDefault();
32+
// If this is a new note, force the editing mode
33+
isEditorInEditModeNotifier.value = isNewNote || !PreferenceKey.openEditorReadingMode.getPreferenceOrDefault();
3334

3435
push(
3536
context,

lib/pages/editor/editor_page.dart

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
33
import 'package:flutter_riverpod/flutter_riverpod.dart';
44
import 'package:gap/gap.dart';
55

6-
import '../../common/constants/constants.dart';
76
import '../../common/navigation/app_bars/notes/editor_app_bar.dart';
87
import '../../common/navigation/page_scaffold.dart';
98
import '../../common/navigation/top_navigation.dart';
@@ -19,6 +18,7 @@ import 'widgets/markdown/markdown_editor.dart';
1918
import 'widgets/plain_text/plain_text_editor.dart';
2019
import 'widgets/rich_text/rich_text_editor.dart';
2120
import 'widgets/rich_text/rich_text_editor_toolbar.dart';
21+
import 'widgets/text_editor.dart';
2222
import 'widgets/title_editor.dart';
2323

2424
/// Editor page.
@@ -41,8 +41,15 @@ class NotesEditorPage extends ConsumerStatefulWidget {
4141
}
4242

4343
class _EditorState extends ConsumerState<NotesEditorPage> {
44+
/// Focus node of the note content text editor currently in use.
45+
FocusNode? _editorFocusNode;
46+
47+
void setupEditorFocus(FocusNode? editorFocusNode) {
48+
_editorFocusNode = editorFocusNode;
49+
}
50+
4451
void requestEditorFocus() {
45-
editorFocusNode.requestFocus();
52+
_editorFocusNode?.requestFocus();
4653
}
4754

4855
@override
@@ -55,6 +62,12 @@ class _EditorState extends ConsumerState<NotesEditorPage> {
5562
return ValueListenableBuilder(
5663
valueListenable: currentNoteNotifier,
5764
builder: (context, currentNote, child) {
65+
// Moved up fleatherController here so that it survives isEditorInEditMode changes
66+
// otherwise the cursor will reset to the front when switching between edit/reading mode
67+
final fleatherController =
68+
currentNote is RichTextNote ? FleatherController(document: currentNote.document) : null;
69+
fleatherControllerNotifier.value = fleatherController;
70+
5871
return ValueListenableBuilder(
5972
valueListenable: isEditorInEditModeNotifier,
6073
builder: (context, isEditorInEditMode, child) {
@@ -67,7 +80,7 @@ class _EditorState extends ConsumerState<NotesEditorPage> {
6780
final showLabelsList =
6881
enableLabels && showLabelsListInEditorPage && currentNote.labelsVisibleSorted.isNotEmpty;
6982

70-
Widget contentEditor;
83+
TextEditor contentEditor;
7184
Widget? toolbar;
7285
switch (currentNote) {
7386
case PlainTextNote note:
@@ -76,16 +89,16 @@ class _EditorState extends ConsumerState<NotesEditorPage> {
7689
isNewNote: widget.isNewNote,
7790
readOnly: readOnly,
7891
autofocus: autofocus,
92+
setupFocusNode: setupEditorFocus,
7993
);
8094
case RichTextNote note:
81-
final fleatherController = FleatherController(document: note.document);
82-
fleatherControllerNotifier.value = fleatherController;
8395
contentEditor = RichTextEditor(
8496
note: note,
85-
fleatherController: fleatherController,
97+
fleatherController: fleatherController!,
8698
isNewNote: widget.isNewNote,
8799
readOnly: readOnly,
88100
autofocus: autofocus,
101+
setupFocusNode: setupEditorFocus,
89102
);
90103
toolbar = RichTextEditorToolbar(
91104
fleatherController: fleatherController,
@@ -96,12 +109,14 @@ class _EditorState extends ConsumerState<NotesEditorPage> {
96109
isNewNote: widget.isNewNote,
97110
readOnly: readOnly,
98111
autofocus: autofocus,
112+
setupFocusNode: setupEditorFocus,
99113
);
100114
case ChecklistNote note:
101115
contentEditor = ChecklistEditor(
102116
note: note,
103117
isNewNote: widget.isNewNote,
104118
readOnly: readOnly,
119+
setupFocusNode: setupEditorFocus,
105120
);
106121
}
107122

lib/pages/editor/widgets/checklist/checklist_editor.dart

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,22 @@ import '../../../../models/note/note.dart';
66
import '../../../../models/note/note_status.dart';
77
import '../../../../providers/notes/notes_provider.dart';
88
import '../../../../providers/notifiers/notifiers.dart';
9+
import '../text_editor.dart';
910

1011
/// Checklist editor.
11-
class ChecklistEditor extends ConsumerWidget {
12+
class ChecklistEditor extends TextEditor {
1213
/// Editor allowing to edit the checklist content of a [ChecklistNote].
1314
const ChecklistEditor({
1415
super.key,
1516
required this.note,
16-
required this.isNewNote,
17-
required this.readOnly,
17+
required super.isNewNote,
18+
required super.readOnly,
19+
super.setupFocusNode,
1820
});
1921

2022
/// The note to display.
2123
final ChecklistNote note;
2224

23-
/// Whether the note was just created.
24-
final bool isNewNote;
25-
26-
/// Whether the text fields are read only.
27-
final bool readOnly;
28-
2925
/// Called when an item of the checklist changes with the new [checklistLines].
3026
void onChecklistChanged(WidgetRef ref, List<ChecklistLine> checklistLines) {
3127
ChecklistNote newNote = note
@@ -35,7 +31,7 @@ class ChecklistEditor extends ConsumerWidget {
3531
ref.read(notesProvider(status: NoteStatus.available, label: currentLabelFilter).notifier).edit(newNote);
3632
}
3733

38-
@override
34+
/// Stateless [build].
3935
Widget build(BuildContext context, WidgetRef ref) {
4036
return Column(
4137
children: [
@@ -50,4 +46,16 @@ class ChecklistEditor extends ConsumerWidget {
5046
],
5147
);
5248
}
49+
50+
/// Boilerplate from [ConsumerWidget] from which [ChecklistEditor] originally extends.
51+
@override
52+
ConsumerState<ChecklistEditor> createState() => _ChecklistEditorState();
53+
}
54+
55+
/// Stateless stuff
56+
class _ChecklistEditorState extends TextEditorState<ChecklistEditor> {
57+
@override
58+
Widget build(BuildContext context) {
59+
return widget.build(context, ref);
60+
}
5361
}

0 commit comments

Comments
 (0)