feat: add D-pad navigation for phones
This commit is contained in:
parent
414f8e8484
commit
b3df7fba9f
6 changed files with 380 additions and 31 deletions
|
|
@ -49,6 +49,8 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
|||
Timer? _debounce;
|
||||
int? _highlighted; // Note picked with the arrow keys, opened by Enter.
|
||||
final _highlightedKey = GlobalKey();
|
||||
final _listFocus = FocusNode();
|
||||
final _menuKey = GlobalKey<PopupMenuButtonState<String>>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -73,6 +75,7 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
|||
void dispose() {
|
||||
_debounce?.cancel();
|
||||
searchController.dispose();
|
||||
_listFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -387,6 +390,38 @@ Happy note taking! ✨
|
|||
});
|
||||
}
|
||||
|
||||
void _enterList() {
|
||||
if (filteredNotes.isEmpty) return;
|
||||
_listFocus.requestFocus();
|
||||
if (_highlighted == null) _moveHighlight(1);
|
||||
}
|
||||
|
||||
void _leaveList() {
|
||||
setState(() => _highlighted = null);
|
||||
searchFocusNode.requestFocus();
|
||||
}
|
||||
|
||||
void _listUp() {
|
||||
if ((_highlighted ?? 0) == 0) {
|
||||
_leaveList();
|
||||
} else {
|
||||
_moveHighlight(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Acts like tapping the blaster: clears the search, or opens the menu.
|
||||
void _pressBlaster() {
|
||||
if (searchFocusNode.hasFocus && searchController.text.isNotEmpty) {
|
||||
searchController.clear();
|
||||
return;
|
||||
}
|
||||
openMenuWithFocus(_menuKey);
|
||||
}
|
||||
|
||||
void _openHighlighted() {
|
||||
if (_highlighted != null) _openNote(filteredNotes[_highlighted!]);
|
||||
}
|
||||
|
||||
void _focusSearch() {
|
||||
searchFocusNode.requestFocus();
|
||||
searchController.selection = TextSelection(
|
||||
|
|
@ -415,6 +450,7 @@ Happy note taking! ✨
|
|||
print("❌ Failed to open external editor: $e");
|
||||
}
|
||||
} else {
|
||||
final fromList = _listFocus.hasFocus;
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
|
|
@ -425,9 +461,15 @@ Happy note taking! ✨
|
|||
),
|
||||
),
|
||||
);
|
||||
_loadNotes(); // ✅ Reload notes after returning
|
||||
setState(() {}); // ✅ Force UI refresh
|
||||
searchFocusNode.requestFocus();
|
||||
await _loadNotes(); // ✅ Reload notes after returning
|
||||
if (!mounted) return;
|
||||
final index = filteredNotes.indexWhere((n) => n.path == noteFile.path);
|
||||
if (fromList && index != -1) {
|
||||
setState(() => _highlighted = index);
|
||||
_listFocus.requestFocus();
|
||||
} else {
|
||||
searchFocusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -751,12 +793,19 @@ Happy note taking! ✨
|
|||
title: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: CallbackShortcuts(
|
||||
bindings: {
|
||||
const SingleActivator(LogicalKeyboardKey.arrowDown): () =>
|
||||
_moveHighlight(1),
|
||||
const SingleActivator(LogicalKeyboardKey.arrowUp): () =>
|
||||
_moveHighlight(-1),
|
||||
},
|
||||
bindings: dpadNavigation
|
||||
? {
|
||||
const SingleActivator(LogicalKeyboardKey.arrowDown):
|
||||
_enterList,
|
||||
const SingleActivator(LogicalKeyboardKey.arrowRight):
|
||||
_pressBlaster,
|
||||
}
|
||||
: {
|
||||
const SingleActivator(LogicalKeyboardKey.arrowDown): () =>
|
||||
_moveHighlight(1),
|
||||
const SingleActivator(LogicalKeyboardKey.arrowUp): () =>
|
||||
_moveHighlight(-1),
|
||||
},
|
||||
child: TextField(
|
||||
focusNode: searchFocusNode,
|
||||
enableInteractiveSelection: true,
|
||||
|
|
@ -794,6 +843,7 @@ Happy note taking! ✨
|
|||
}
|
||||
},
|
||||
child: PopupMenuButton<String>(
|
||||
key: _menuKey,
|
||||
enabled: searchController.text.isEmpty,
|
||||
icon: SvgPicture.asset(
|
||||
"assets/blaster.svg",
|
||||
|
|
@ -805,6 +855,12 @@ Happy note taking! ✨
|
|||
case 'Toggle Tags':
|
||||
widget.toggleTags();
|
||||
break;
|
||||
case 'Include Subdirectories':
|
||||
_toggleIncludeSubdirectories(!_includeSubdirectories);
|
||||
break;
|
||||
case 'Include File Content':
|
||||
_toggleIncludeFileContent(!_includeFileContent);
|
||||
break;
|
||||
case 'Toggle Theme':
|
||||
setState(() {
|
||||
widget.toggleTheme(!widget.isDarkMode);
|
||||
|
|
@ -857,10 +913,6 @@ Happy note taking! ✨
|
|||
child: ListTile(
|
||||
leading: Icon(_includeSubdirectories ? Icons.file_copy_outlined : Icons.folder_copy_outlined),
|
||||
title: Text(_includeSubdirectories ? 'Exclude Subdirectories' : 'Search Subdirectories'),
|
||||
onTap: () {
|
||||
_toggleIncludeSubdirectories(!_includeSubdirectories);
|
||||
Navigator.pop(context); // Close menu after toggle
|
||||
},
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
|
|
@ -872,10 +924,6 @@ Happy note taking! ✨
|
|||
title: Text(
|
||||
_includeFileContent ? 'Exclude File Content' : 'Search File Content',
|
||||
),
|
||||
onTap: () {
|
||||
_toggleIncludeFileContent(!_includeFileContent);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
|
|
@ -932,7 +980,20 @@ Happy note taking! ✨
|
|||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
child: CallbackShortcuts(
|
||||
bindings: {
|
||||
const SingleActivator(LogicalKeyboardKey.arrowDown): () =>
|
||||
_moveHighlight(1),
|
||||
const SingleActivator(LogicalKeyboardKey.arrowUp): _listUp,
|
||||
const SingleActivator(LogicalKeyboardKey.arrowLeft): _leaveList,
|
||||
const SingleActivator(LogicalKeyboardKey.arrowRight):
|
||||
_pressBlaster,
|
||||
const SingleActivator(LogicalKeyboardKey.select): _openHighlighted,
|
||||
const SingleActivator(LogicalKeyboardKey.enter): _openHighlighted,
|
||||
},
|
||||
child: Focus(
|
||||
focusNode: _listFocus,
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification scrollInfo) {
|
||||
if (!_isLoadingMore && scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent - 100) {
|
||||
_loadMoreNotes(); // ✅ Lazy load when user scrolls
|
||||
|
|
@ -1007,6 +1068,8 @@ Happy note taking! ✨
|
|||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue