@@ -12,37 +12,27 @@ class SettingsScreen extends StatefulWidget {
1212}
1313
1414class _SettingsScreenState extends State <SettingsScreen > {
15- late final TextEditingController _safeguardCtrl;
16- late final SettingsProvider _settingsRef;
17-
18- void _syncSafeguardFromProvider () {
19- final t = _settingsRef.safeguardProcessNames;
20- if (_safeguardCtrl.text != t) {
21- _safeguardCtrl.value = TextEditingValue (
22- text: t,
23- selection: TextSelection .collapsed (offset: t.length),
24- );
25- }
26- }
15+ late final TextEditingController _manualSafeguardCtrl;
2716
2817 @override
2918 void initState () {
3019 super .initState ();
31- _settingsRef = context.read <SettingsProvider >();
32- _safeguardCtrl = TextEditingController (text: _settingsRef.safeguardProcessNames);
33- _settingsRef.addListener (_syncSafeguardFromProvider);
20+ _manualSafeguardCtrl = TextEditingController ();
3421 }
3522
3623 @override
3724 void dispose () {
38- _settingsRef.removeListener (_syncSafeguardFromProvider);
39- _safeguardCtrl.dispose ();
25+ _manualSafeguardCtrl.dispose ();
4026 super .dispose ();
4127 }
4228
4329 @override
4430 Widget build (BuildContext context) {
4531 final settings = context.watch <SettingsProvider >();
32+ final engine = context.watch <EngineProvider >();
33+ final pickNames = settings.safeguardPickList (
34+ engine.processes.map ((p) => p.name),
35+ );
4636
4737 return Column (
4838 crossAxisAlignment: CrossAxisAlignment .stretch,
@@ -140,8 +130,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
140130 const SizedBox (height: 8 ),
141131 Text (
142132 'After an alert fires, the engine may end matching processes '
143- '(graceful terminate) to reduce load. One name per line; '
144- 'include .exe or omit it (e.g. chrome or chrome.exe) . '
133+ '(graceful terminate) to reduce load. Choose names from what '
134+ 'the engine currently sees, or add another name manually . '
145135 'Use only for apps you accept losing unsaved work.' ,
146136 style: TextStyle (
147137 color: AppTheme .textMutedFor (context),
@@ -152,6 +142,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
152142 const SizedBox (height: 12 ),
153143 Card (
154144 child: Column (
145+ crossAxisAlignment: CrossAxisAlignment .stretch,
155146 children: [
156147 SwitchListTile (
157148 title: Text (
@@ -170,22 +161,120 @@ class _SettingsScreenState extends State<SettingsScreen> {
170161 onChanged: settings.setSafeguardEnabled,
171162 ),
172163 Padding (
173- padding: const EdgeInsets .fromLTRB (16 , 0 , 16 , 16 ),
174- child: TextField (
175- controller: _safeguardCtrl,
176- maxLines: 5 ,
177- enabled: settings.safeguardEnabled,
178- decoration: InputDecoration (
179- labelText: 'Process names' ,
180- hintText: 'e.g.\n SomeHeavyApp.exe\n AnotherApp' ,
181- alignLabelWithHint: true ,
182- border: const OutlineInputBorder (),
183- ),
164+ padding: const EdgeInsets .fromLTRB (16 , 0 , 16 , 8 ),
165+ child: Row (
166+ children: [
167+ Text (
168+ 'Safe to close (select processes)' ,
169+ style: TextStyle (
170+ color: AppTheme .textSecondaryFor (context),
171+ fontSize: 13 ,
172+ fontWeight: FontWeight .w600,
173+ ),
174+ ),
175+ const Spacer (),
176+ TextButton .icon (
177+ onPressed: ! engine.connected
178+ ? null
179+ : () => engine.refreshProcesses (),
180+ icon: const Icon (Icons .refresh, size: 18 ),
181+ label: const Text ('Refresh list' ),
182+ ),
183+ ],
184+ ),
185+ ),
186+ Padding (
187+ padding: const EdgeInsets .symmetric (horizontal: 16 ),
188+ child: Text (
189+ ! engine.connected
190+ ? 'Connect to the engine to load process names from this PC.'
191+ : pickNames.isEmpty
192+ ? 'No processes yet — tap Refresh after the engine runs a few seconds.'
193+ : 'Checked names are allowed for safeguard termination.' ,
184194 style: TextStyle (
185- color: AppTheme .textPrimaryFor (context),
186- fontSize: 13 ,
195+ color: AppTheme .textMutedFor (context),
196+ fontSize: 11 ,
197+ height: 1.3 ,
187198 ),
188- onChanged: settings.setSafeguardProcessNames,
199+ ),
200+ ),
201+ const SizedBox (height: 8 ),
202+ SizedBox (
203+ height: 200 ,
204+ child: ListView .builder (
205+ padding: const EdgeInsets .fromLTRB (8 , 0 , 8 , 8 ),
206+ itemCount: pickNames.length,
207+ itemBuilder: (context, i) {
208+ final name = pickNames[i];
209+ return CheckboxListTile (
210+ dense: true ,
211+ enabled: settings.safeguardEnabled,
212+ value: settings.safeguardHasName (name),
213+ onChanged: settings.safeguardEnabled
214+ ? (v) => settings.toggleSafeguardProcessName (
215+ name,
216+ v ?? false ,
217+ )
218+ : null ,
219+ title: Text (
220+ name,
221+ style: TextStyle (
222+ color: AppTheme .textPrimaryFor (context),
223+ fontSize: 13 ,
224+ ),
225+ maxLines: 1 ,
226+ overflow: TextOverflow .ellipsis,
227+ ),
228+ );
229+ },
230+ ),
231+ ),
232+ Padding (
233+ padding: const EdgeInsets .fromLTRB (16 , 0 , 16 , 16 ),
234+ child: Row (
235+ crossAxisAlignment: CrossAxisAlignment .start,
236+ children: [
237+ Expanded (
238+ child: TextField (
239+ controller: _manualSafeguardCtrl,
240+ enabled: settings.safeguardEnabled,
241+ decoration: InputDecoration (
242+ labelText: 'Add other process name' ,
243+ hintText: 'e.g. MyApp.exe' ,
244+ border: const OutlineInputBorder (),
245+ isDense: true ,
246+ ),
247+ style: TextStyle (
248+ color: AppTheme .textPrimaryFor (context),
249+ fontSize: 13 ,
250+ ),
251+ onSubmitted: settings.safeguardEnabled
252+ ? (_) {
253+ settings.addSafeguardProcessNameLine (
254+ _manualSafeguardCtrl.text,
255+ );
256+ _manualSafeguardCtrl.clear ();
257+ }
258+ : null ,
259+ ),
260+ ),
261+ const SizedBox (width: 8 ),
262+ Padding (
263+ padding: const EdgeInsets .only (top: 8 ),
264+ child: IconButton .filledTonal (
265+ tooltip: 'Add name' ,
266+ onPressed: ! settings.safeguardEnabled
267+ ? null
268+ : () {
269+ settings.addSafeguardProcessNameLine (
270+ _manualSafeguardCtrl.text,
271+ );
272+ _manualSafeguardCtrl.clear ();
273+ },
274+ icon: const Icon (Icons .add, size: 22 ),
275+ ),
276+ ),
277+ ],
189278 ),
190279 ),
191280 ],
@@ -261,7 +350,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
261350 const SizedBox (height: 24 ),
262351 FilledButton .icon (
263352 onPressed: () async {
264- settings.setSafeguardProcessNames (_safeguardCtrl.text);
265353 await settings.save ();
266354 if (! context.mounted) return ;
267355 final eng = context.read <EngineProvider >();
0 commit comments