autofocus setting
This commit is contained in:
parent
f87e554df0
commit
f0cf7b6af2
1 changed files with 208 additions and 113 deletions
321
lib/main.dart
321
lib/main.dart
|
|
@ -17,6 +17,7 @@ class AppSettings {
|
||||||
bool colorIcons; // true = color icons; false = monochrome icons
|
bool colorIcons; // true = color icons; false = monochrome icons
|
||||||
bool darkTheme;
|
bool darkTheme;
|
||||||
bool sortAlphabetically;
|
bool sortAlphabetically;
|
||||||
|
bool autoFocus; // new
|
||||||
|
|
||||||
AppSettings({
|
AppSettings({
|
||||||
this.showTags = true,
|
this.showTags = true,
|
||||||
|
|
@ -24,6 +25,7 @@ class AppSettings {
|
||||||
this.colorIcons = true,
|
this.colorIcons = true,
|
||||||
this.darkTheme = false,
|
this.darkTheme = false,
|
||||||
this.sortAlphabetically = false,
|
this.sortAlphabetically = false,
|
||||||
|
this.autoFocus = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,14 +45,13 @@ class _MyAppState extends State<MyApp> {
|
||||||
|
|
||||||
Future<void> _loadSettings() async {
|
Future<void> _loadSettings() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
bool? savedSorting = prefs.getBool('sortAlphabetically');
|
|
||||||
print('Saved sorting: $savedSorting'); // Debug print
|
|
||||||
setState(() {
|
setState(() {
|
||||||
settings.showTags = prefs.getBool('showTags') ?? true;
|
settings.showTags = prefs.getBool('showTags') ?? true;
|
||||||
settings.showIcons = prefs.getBool('showIcons') ?? true;
|
settings.showIcons = prefs.getBool('showIcons') ?? true;
|
||||||
settings.colorIcons = prefs.getBool('colorIcons') ?? true;
|
settings.colorIcons = prefs.getBool('colorIcons') ?? true;
|
||||||
settings.darkTheme = prefs.getBool('darkTheme') ?? false;
|
settings.darkTheme = prefs.getBool('darkTheme') ?? false;
|
||||||
settings.sortAlphabetically = prefs.getBool('sortAlphabetically') ?? false;
|
settings.sortAlphabetically = prefs.getBool('sortAlphabetically') ?? false;
|
||||||
|
settings.autoFocus = prefs.getBool('autoFocus') ?? true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +69,7 @@ class _MyAppState extends State<MyApp> {
|
||||||
_updateSetting('colorIcons', settings.colorIcons);
|
_updateSetting('colorIcons', settings.colorIcons);
|
||||||
_updateSetting('darkTheme', settings.darkTheme);
|
_updateSetting('darkTheme', settings.darkTheme);
|
||||||
_updateSetting('sortAlphabetically', settings.sortAlphabetically);
|
_updateSetting('sortAlphabetically', settings.sortAlphabetically);
|
||||||
|
_updateSetting('autoFocus', settings.autoFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -123,6 +125,7 @@ class AppLauncherScreen extends StatefulWidget {
|
||||||
|
|
||||||
class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
|
final FocusNode searchFocusNode = FocusNode();
|
||||||
List<AppInfo> apps = [];
|
List<AppInfo> apps = [];
|
||||||
List<AppInfo> filteredApps = [];
|
List<AppInfo> filteredApps = [];
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
|
@ -132,6 +135,11 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadInstalledApps();
|
_loadInstalledApps();
|
||||||
searchController.addListener(_filterApps);
|
searchController.addListener(_filterApps);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (widget.settings.autoFocus) {
|
||||||
|
FocusScope.of(context).requestFocus(searchFocusNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadInstalledApps() async {
|
Future<void> _loadInstalledApps() async {
|
||||||
|
|
@ -143,11 +151,27 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
|
|
||||||
List<AppInfo> loadedApps = installedApps.map((app) {
|
List<AppInfo> loadedApps = installedApps.map((app) {
|
||||||
final lastUsedMillis = prefs.getInt("lastUsed_${app.packageName}");
|
final lastUsedMillis = prefs.getInt("lastUsed_${app.packageName}");
|
||||||
|
final storedTags = prefs.getString("tags_${app.packageName}");
|
||||||
|
List<String> tags = [];
|
||||||
|
|
||||||
|
if (storedTags != null && storedTags.trim().isNotEmpty) {
|
||||||
|
// Use the stored user-edited tags.
|
||||||
|
tags = storedTags.split(" ");
|
||||||
|
} else {
|
||||||
|
// Fall back to using the auto-generated category tag.
|
||||||
|
if (app.category != null) {
|
||||||
|
String categoryStr = app.category.toString().split('.').last;
|
||||||
|
if (categoryStr.toLowerCase() != 'undefined') {
|
||||||
|
tags.add('@' + categoryStr.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return AppInfo(
|
return AppInfo(
|
||||||
icon: app is ApplicationWithIcon ? app.icon : null,
|
icon: app is ApplicationWithIcon ? app.icon : null,
|
||||||
name: app.appName,
|
name: app.appName,
|
||||||
packageName: app.packageName,
|
packageName: app.packageName,
|
||||||
tags: [],
|
tags: tags,
|
||||||
lastUsed: lastUsedMillis != null
|
lastUsed: lastUsedMillis != null
|
||||||
? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis)
|
? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis)
|
||||||
: null,
|
: null,
|
||||||
|
|
@ -156,9 +180,9 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
apps = loadedApps;
|
apps = loadedApps;
|
||||||
|
filteredApps = apps;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
// Force initial sorting (by last used if sortAlphabetically is false)
|
|
||||||
_filterApps();
|
_filterApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,13 +268,15 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
setState(() {
|
setState(() {
|
||||||
app.tags
|
app.tags
|
||||||
..clear()
|
..clear()
|
||||||
..addAll(
|
..addAll(tagController.text.split(" ").where((t) => t.isNotEmpty));
|
||||||
tagController.text.split(" ").where((t) => t.isNotEmpty));
|
|
||||||
});
|
});
|
||||||
|
// Persist the updated tags.
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString("tags_${app.packageName}", app.tags.join(" "));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_filterApps();
|
_filterApps();
|
||||||
},
|
},
|
||||||
|
|
@ -265,6 +291,37 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<InlineSpan> _formatTags(String content) {
|
||||||
|
final tagPattern = RegExp(r'([#@+][a-zA-Z0-9_]+)');
|
||||||
|
final Set<String> tagSet = {}; // Avoid duplicate tags
|
||||||
|
|
||||||
|
for (final match in tagPattern.allMatches(content)) {
|
||||||
|
tagSet.add(match.group(0)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort tags: +tags before @tags before #tags
|
||||||
|
final sortedTags = tagSet.toList()
|
||||||
|
..sort((a, b) {
|
||||||
|
if (a.startsWith('+') && !b.startsWith('+')) return -1;
|
||||||
|
if (b.startsWith('+') && !a.startsWith('+')) return 1;
|
||||||
|
if (a.startsWith('@') && !b.startsWith('@')) return -1;
|
||||||
|
if (b.startsWith('@') && !a.startsWith('@')) return 1;
|
||||||
|
return a.compareTo(b); // Default alphabetical order
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortedTags.map((tag) {
|
||||||
|
Color tagColor = Colors.white;
|
||||||
|
if (tag.startsWith('#')) tagColor = Colors.pinkAccent;
|
||||||
|
if (tag.startsWith('+')) tagColor = Colors.cyan;
|
||||||
|
if (tag.startsWith('@')) tagColor = Colors.orange;
|
||||||
|
|
||||||
|
return TextSpan(
|
||||||
|
text: "$tag ",
|
||||||
|
style: TextStyle(color: tagColor, fontWeight: FontWeight.bold),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildAppIcon(AppInfo app) {
|
Widget _buildAppIcon(AppInfo app) {
|
||||||
Widget iconWidget;
|
Widget iconWidget;
|
||||||
if (app.icon != null) {
|
if (app.icon != null) {
|
||||||
|
|
@ -298,6 +355,7 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: TextField(
|
title: TextField(
|
||||||
|
focusNode: widget.settings.autoFocus ? searchFocusNode : null,
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Search apps...',
|
hintText: 'Search apps...',
|
||||||
|
|
@ -308,118 +366,147 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
actions: [
|
actions: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: PopupMenuButton<String>(
|
child: GestureDetector(
|
||||||
enabled: searchController.text.isEmpty,
|
onTap: () {
|
||||||
icon: SvgPicture.asset(
|
if (searchController.text.isNotEmpty) {
|
||||||
"assets/plasmoid.svg",
|
searchController.clear();
|
||||||
width: 30,
|
|
||||||
color: Colors.orange,
|
|
||||||
),
|
|
||||||
onSelected: (String value) {
|
|
||||||
switch (value) {
|
|
||||||
case 'Toggle Tags':
|
|
||||||
widget.onSettingsChanged(AppSettings(
|
|
||||||
showTags: !widget.settings.showTags,
|
|
||||||
showIcons: widget.settings.showIcons,
|
|
||||||
colorIcons: widget.settings.colorIcons,
|
|
||||||
darkTheme: widget.settings.darkTheme,
|
|
||||||
sortAlphabetically: widget.settings.sortAlphabetically,
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
case 'Toggle Icons':
|
|
||||||
widget.onSettingsChanged(AppSettings(
|
|
||||||
showTags: widget.settings.showTags,
|
|
||||||
showIcons: !widget.settings.showIcons,
|
|
||||||
colorIcons: widget.settings.colorIcons,
|
|
||||||
darkTheme: widget.settings.darkTheme,
|
|
||||||
sortAlphabetically: widget.settings.sortAlphabetically,
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
case 'Toggle Color Icons':
|
|
||||||
widget.onSettingsChanged(AppSettings(
|
|
||||||
showTags: widget.settings.showTags,
|
|
||||||
showIcons: widget.settings.showIcons,
|
|
||||||
colorIcons: !widget.settings.colorIcons,
|
|
||||||
darkTheme: widget.settings.darkTheme,
|
|
||||||
sortAlphabetically: widget.settings.sortAlphabetically,
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
case 'Toggle Theme':
|
|
||||||
widget.onSettingsChanged(AppSettings(
|
|
||||||
showTags: widget.settings.showTags,
|
|
||||||
showIcons: widget.settings.showIcons,
|
|
||||||
colorIcons: widget.settings.colorIcons,
|
|
||||||
darkTheme: !widget.settings.darkTheme,
|
|
||||||
sortAlphabetically: widget.settings.sortAlphabetically,
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
case 'Toggle Sorting':
|
|
||||||
widget.onSettingsChanged(AppSettings(
|
|
||||||
showTags: widget.settings.showTags,
|
|
||||||
showIcons: widget.settings.showIcons,
|
|
||||||
colorIcons: widget.settings.colorIcons,
|
|
||||||
darkTheme: widget.settings.darkTheme,
|
|
||||||
sortAlphabetically: !widget.settings.sortAlphabetically,
|
|
||||||
));
|
|
||||||
_filterApps();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) => [
|
child: PopupMenuButton<String>(
|
||||||
PopupMenuItem<String>(
|
enabled: searchController.text.isEmpty,
|
||||||
value: 'Toggle Tags',
|
icon: SvgPicture.asset(
|
||||||
child: ListTile(
|
"assets/plasmoid.svg",
|
||||||
leading: Icon(widget.settings.showTags
|
width: 30,
|
||||||
? Icons.short_text
|
color: Colors.orange,
|
||||||
: Icons.tag),
|
|
||||||
title: Text(widget.settings.showTags
|
|
||||||
? 'Hide Tags'
|
|
||||||
: 'Show Tags'),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
PopupMenuItem<String>(
|
onSelected: (String value) {
|
||||||
value: 'Toggle Icons',
|
switch (value) {
|
||||||
child: ListTile(
|
case 'Toggle Tags':
|
||||||
leading: Icon(widget.settings.showIcons
|
widget.onSettingsChanged(AppSettings(
|
||||||
? Icons.apps
|
showTags: !widget.settings.showTags,
|
||||||
: Icons.apps_outlined),
|
showIcons: widget.settings.showIcons,
|
||||||
title: Text(widget.settings.showIcons
|
colorIcons: widget.settings.colorIcons,
|
||||||
? 'Hide Icons'
|
darkTheme: widget.settings.darkTheme,
|
||||||
: 'Show Icons'),
|
sortAlphabetically: widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case 'Toggle Icons':
|
||||||
|
widget.onSettingsChanged(AppSettings(
|
||||||
|
showTags: widget.settings.showTags,
|
||||||
|
showIcons: !widget.settings.showIcons,
|
||||||
|
colorIcons: widget.settings.colorIcons,
|
||||||
|
darkTheme: widget.settings.darkTheme,
|
||||||
|
sortAlphabetically: widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case 'Toggle Color Icons':
|
||||||
|
widget.onSettingsChanged(AppSettings(
|
||||||
|
showTags: widget.settings.showTags,
|
||||||
|
showIcons: widget.settings.showIcons,
|
||||||
|
colorIcons: !widget.settings.colorIcons,
|
||||||
|
darkTheme: widget.settings.darkTheme,
|
||||||
|
sortAlphabetically: widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case 'Toggle Theme':
|
||||||
|
widget.onSettingsChanged(AppSettings(
|
||||||
|
showTags: widget.settings.showTags,
|
||||||
|
showIcons: widget.settings.showIcons,
|
||||||
|
colorIcons: widget.settings.colorIcons,
|
||||||
|
darkTheme: !widget.settings.darkTheme,
|
||||||
|
sortAlphabetically: widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case 'Toggle Sorting':
|
||||||
|
widget.onSettingsChanged(AppSettings(
|
||||||
|
showTags: widget.settings.showTags,
|
||||||
|
showIcons: widget.settings.showIcons,
|
||||||
|
colorIcons: widget.settings.colorIcons,
|
||||||
|
darkTheme: widget.settings.darkTheme,
|
||||||
|
sortAlphabetically: !widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
_filterApps();
|
||||||
|
break;
|
||||||
|
case 'Toggle AutoFocus':
|
||||||
|
widget.onSettingsChanged(AppSettings(
|
||||||
|
showTags: widget.settings.showTags,
|
||||||
|
showIcons: widget.settings.showIcons,
|
||||||
|
colorIcons: widget.settings.colorIcons,
|
||||||
|
darkTheme: widget.settings.darkTheme,
|
||||||
|
sortAlphabetically: widget.settings.sortAlphabetically,
|
||||||
|
autoFocus: !widget.settings.autoFocus,
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: (BuildContext context) => [
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Toggle Tags',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(widget.settings.showTags
|
||||||
|
? Icons.short_text
|
||||||
|
: Icons.tag),
|
||||||
|
title: Text(widget.settings.showTags
|
||||||
|
? 'Hide Tags'
|
||||||
|
: 'Show Tags'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
PopupMenuItem<String>(
|
||||||
PopupMenuItem<String>(
|
value: 'Toggle Icons',
|
||||||
value: 'Toggle Color Icons',
|
child: ListTile(
|
||||||
child: ListTile(
|
leading: Icon(widget.settings.showIcons
|
||||||
leading: Icon(Icons.palette),
|
? Icons.apps
|
||||||
title: Text(widget.settings.colorIcons
|
: Icons.apps_outlined),
|
||||||
? 'Use Monochrome Icons'
|
title: Text(widget.settings.showIcons
|
||||||
: 'Use Color Icons'),
|
? 'Hide Icons'
|
||||||
|
: 'Show Icons'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
PopupMenuItem<String>(
|
||||||
PopupMenuItem<String>(
|
value: 'Toggle Color Icons',
|
||||||
value: 'Toggle Theme',
|
child: ListTile(
|
||||||
child: ListTile(
|
leading: Icon(Icons.palette),
|
||||||
leading: Icon(widget.settings.darkTheme
|
title: Text(widget.settings.colorIcons
|
||||||
? Icons.light_mode
|
? 'Use Monochrome Icons'
|
||||||
: Icons.dark_mode),
|
: 'Use Color Icons'),
|
||||||
title: Text(widget.settings.darkTheme
|
),
|
||||||
? 'Switch to Light Mode'
|
|
||||||
: 'Switch to Dark Mode'),
|
|
||||||
),
|
),
|
||||||
),
|
PopupMenuItem<String>(
|
||||||
PopupMenuItem<String>(
|
value: 'Toggle Theme',
|
||||||
value: 'Toggle Sorting',
|
child: ListTile(
|
||||||
child: ListTile(
|
leading: Icon(widget.settings.darkTheme
|
||||||
leading: Icon(widget.settings.sortAlphabetically
|
? Icons.light_mode
|
||||||
? Icons.access_time
|
: Icons.dark_mode),
|
||||||
: Icons.sort_by_alpha),
|
title: Text(widget.settings.darkTheme
|
||||||
title: Text(widget.settings.sortAlphabetically
|
? 'Switch to Light Mode'
|
||||||
? 'Sort Alphabetically'
|
: 'Switch to Dark Mode'),
|
||||||
: 'Sort by Last Used'),
|
),
|
||||||
),
|
),
|
||||||
),
|
PopupMenuItem<String>(
|
||||||
],
|
value: 'Toggle Sorting',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(widget.settings.sortAlphabetically
|
||||||
|
? Icons.access_time
|
||||||
|
: Icons.sort_by_alpha),
|
||||||
|
title: Text(widget.settings.sortAlphabetically
|
||||||
|
? 'Sort Alphabetically'
|
||||||
|
: 'Sort by Last Used'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Toggle AutoFocus',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.center_focus_strong),
|
||||||
|
title: Text(widget.settings.autoFocus ? 'Disable Auto Focus' : 'Enable Auto Focus'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -450,7 +537,15 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> {
|
||||||
widget.settings.showIcons ? _buildAppIcon(app) : null,
|
widget.settings.showIcons ? _buildAppIcon(app) : null,
|
||||||
title: Text(app.name),
|
title: Text(app.name),
|
||||||
subtitle:
|
subtitle:
|
||||||
widget.settings.showTags ? Text(app.tags.join(" ")) : null,
|
widget.settings.showTags
|
||||||
|
? RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: DefaultTextStyle.of(context).style,
|
||||||
|
children: _formatTags(app.tags.join(' ')), // ✅ Use stored tags
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
onTap: () => _launchApp(app),
|
onTap: () => _launchApp(app),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue