@@ -9,99 +9,108 @@ import SwiftUI
99
1010struct SettingsView : View {
1111 @ObservedObject var settings = AppSettings . shared
12- @State private var newBlacklistItem : String = " "
12+ @State private var selectedProcess : String = " "
13+ var seenProcessNames : Set < String > = [ ]
14+
15+ private var availableProcesses : [ String ] {
16+ let existing = Set ( settings. blacklist)
17+ return seenProcessNames. subtracting ( existing) . sorted ( )
18+ }
1319
1420 var body : some View {
15- Form {
16- Section {
17- Picker (
18- NSLocalizedString ( " refresh_interval " , comment: " Refresh Interval " ) ,
19- selection: $settings. refreshInterval
20- ) {
21+ VStack ( alignment: . leading, spacing: 16 ) {
22+ // Section 1: Refresh interval
23+ GroupBox ( label: Label (
24+ NSLocalizedString ( " refresh_interval " , comment: " " ) ,
25+ systemImage: " clock "
26+ ) ) {
27+ Picker ( " " , selection: $settings. refreshInterval) {
2128 Text ( " 1s " ) . tag ( 1 )
2229 Text ( " 2s " ) . tag ( 2 )
2330 Text ( " 3s " ) . tag ( 3 )
2431 Text ( " 5s " ) . tag ( 5 )
2532 Text ( " 10s " ) . tag ( 10 )
2633 }
2734 . pickerStyle ( . segmented)
28- } header : {
29- Text ( NSLocalizedString ( " refresh_interval " , comment : " Refresh Interval " ) )
35+ . labelsHidden ( )
36+ . padding ( . top , 4 )
3037 }
3138
32- Section {
33- Picker (
34- NSLocalizedString ( " min_threshold " , comment: " Minimum Traffic Threshold " ) ,
35- selection: $settings. minTrafficThreshold
36- ) {
37- Text ( NSLocalizedString ( " show_all " , comment: " Show All " ) ) . tag ( 0 )
39+ // Section 2: Traffic threshold
40+ GroupBox ( label: Label (
41+ NSLocalizedString ( " min_threshold " , comment: " " ) ,
42+ systemImage: " speedometer "
43+ ) ) {
44+ Picker ( " " , selection: $settings. minTrafficThreshold) {
45+ Text ( NSLocalizedString ( " show_all " , comment: " " ) ) . tag ( 0 )
3846 Text ( " 1 KB/s " ) . tag ( 1024 )
3947 Text ( " 10 KB/s " ) . tag ( 10240 )
4048 Text ( " 100 KB/s " ) . tag ( 102400 )
4149 }
4250 . pickerStyle ( . segmented)
43- } header : {
44- Text ( NSLocalizedString ( " min_threshold " , comment : " Minimum Traffic Threshold " ) )
51+ . labelsHidden ( )
52+ . padding ( . top , 4 )
4553 }
4654
47- Section {
48- Text ( NSLocalizedString ( " blacklist_desc " , comment: " Blacklist description " ) )
49- . font ( . callout)
50- . foregroundColor ( . secondary)
51-
52- HStack {
53- TextField (
54- NSLocalizedString ( " app_name_placeholder " , comment: " App name placeholder " ) ,
55- text: $newBlacklistItem
56- )
57- . textFieldStyle ( . roundedBorder)
58- . onSubmit { addBlacklistItem ( ) }
59-
60- Button ( action: addBlacklistItem) {
61- Image ( systemName: " plus.circle.fill " )
55+ // Section 3: Process Blacklist
56+ GroupBox ( label: Label (
57+ NSLocalizedString ( " blacklist " , comment: " " ) ,
58+ systemImage: " nosign "
59+ ) ) {
60+ VStack ( alignment: . leading, spacing: 8 ) {
61+ // Pick from seen processes
62+ if !availableProcesses. isEmpty {
63+ Picker ( NSLocalizedString ( " select_process " , comment: " " ) , selection: $selectedProcess) {
64+ Text ( " — " ) . tag ( " " )
65+ ForEach ( availableProcesses, id: \. self) { name in
66+ Text ( name) . tag ( name)
67+ }
68+ }
69+ . onChange ( of: selectedProcess) { value in
70+ guard !value. isEmpty else { return }
71+ settings. addToBlacklist ( value)
72+ selectedProcess = " "
73+ }
6274 }
63- . disabled ( newBlacklistItem. trimmingCharacters ( in: . whitespaces) . isEmpty)
64- }
6575
66- if settings. blacklist. isEmpty {
67- Text ( NSLocalizedString ( " blacklist_empty " , comment: " No blacklisted apps " ) )
68- . foregroundColor ( . secondary)
69- . italic ( )
70- } else {
71- List {
72- ForEach ( settings. blacklist, id: \. self) { name in
73- HStack {
74- Text ( name)
75- Spacer ( )
76- Button ( action: { settings. removeFromBlacklist ( name) } ) {
77- Image ( systemName: " trash " )
78- . foregroundColor ( . red)
76+ // Blacklist items
77+ if settings. blacklist. isEmpty {
78+ Text ( NSLocalizedString ( " blacklist_empty " , comment: " " ) )
79+ . foregroundColor ( . secondary)
80+ . italic ( )
81+ . padding ( . vertical, 2 )
82+ } else {
83+ VStack ( spacing: 2 ) {
84+ ForEach ( settings. blacklist, id: \. self) { name in
85+ HStack {
86+ Text ( name)
87+ . lineLimit ( 1 )
88+ Spacer ( )
89+ Button ( action: { settings. removeFromBlacklist ( name) } ) {
90+ Image ( systemName: " xmark.circle.fill " )
91+ . foregroundColor ( . secondary)
92+ }
93+ . buttonStyle ( . plain)
7994 }
80- . buttonStyle ( . plain)
95+ . padding ( . horizontal, 8 )
96+ . padding ( . vertical, 4 )
97+ . background ( Color . primary. opacity ( 0.04 ) )
98+ . cornerRadius ( 4 )
8199 }
82100 }
83101 }
84- . frame ( minHeight: 60 , maxHeight: 160 )
85102 }
86- } header: {
87- Text ( NSLocalizedString ( " blacklist " , comment: " App Blacklist " ) )
103+ . padding ( . top, 4 )
88104 }
89105 }
90- . navigationTitle ( NSLocalizedString ( " settings " , comment : " Settings " ) )
91- . frame ( width: 400 , height : 420 )
106+ . padding ( 20 )
107+ . frame ( width: 360 )
92108 . fixedSize ( )
93109 }
94-
95- private func addBlacklistItem( ) {
96- let trimmed = newBlacklistItem. trimmingCharacters ( in: . whitespaces)
97- guard !trimmed. isEmpty else { return }
98- settings. addToBlacklist ( trimmed)
99- newBlacklistItem = " "
100- }
101110}
102111
103112struct SettingsView_Previews : PreviewProvider {
104113 static var previews : some View {
105- SettingsView ( )
114+ SettingsView ( seenProcessNames : [ " Chrome " , " Slack " , " Terminal " , " ClashX " , " Surge " , " Safari " ] )
106115 }
107116}
0 commit comments