@@ -76,8 +76,8 @@ struct TaskProvider: AppIntentTimelineProvider {
7676struct TaskWidgetEntryView : View {
7777 var entry : TaskProvider . Entry
7878 @Environment ( \. widgetFamily) var family
79-
80- // Parse tasks from JSON
79+
80+ // 1. DATA PARSING
8181 var parsedTasks : [ Task ] {
8282 guard let data = entry. tasks. data ( using: . utf8) else { return [ ] }
8383 do {
@@ -97,7 +97,6 @@ struct TaskWidgetEntryView: View {
9797 return [ ]
9898 }
9999
100- // Sort tasks by priority: H > M > L > None
101100 var sortedTasks : [ Task ] {
102101 return parsedTasks. sorted { task1, task2 in
103102 let priorityOrder = [ " H " : 0 , " M " : 1 , " L " : 2 , " N " : 3 ]
@@ -111,179 +110,150 @@ struct TaskWidgetEntryView: View {
111110 entry. themeMode == " dark "
112111 }
113112
113+ // 2. MAIN BODY LAYOUT
114114 var body : some View {
115115 ZStack {
116+ // Background Layer
116117 ( isDarkMode ? Color . black : Color ( white: 0.95 ) )
117118 . ignoresSafeArea ( )
118119
119- VStack ( alignment: . leading, spacing: 8 ) {
120+ // Content Layer
121+ VStack ( spacing: 0 ) {
122+
123+ // --- TOP BAR (Pinned) ---
120124 headerView
125+ . padding ( . top, 12 )
126+ . padding ( . horizontal, 16 )
127+ . padding ( . bottom, 8 ) // Slightly tighter to fit content
121128
129+ // --- LIST AREA ---
122130 if parsedTasks. isEmpty {
123131 emptyStateView
124132 } else {
125- switch family {
126- case . systemSmall :
127- smallTaskList
128- case . systemMedium :
129- mediumTaskList
130- case . systemLarge :
131- largeTaskList
132- default :
133- mediumTaskList
133+ VStack ( alignment : . leading , spacing : 0 ) {
134+ switch family {
135+ case . systemMedium :
136+ mediumTaskList
137+ case . systemLarge :
138+ largeTaskList
139+ default :
140+ mediumTaskList
141+ }
134142 }
143+ . padding ( . horizontal, 12 )
135144 }
145+
146+ // --- BOTTOM SPACER ---
147+ Spacer ( )
136148 }
137- . padding ( family == . systemLarge ? 12 : 10 )
138149 }
139150 . colorScheme ( isDarkMode ? . dark : . light)
140151 }
141152
153+ // 3. SUBVIEWS
154+
142155 var headerView : some View {
143156 HStack {
144- HStack ( spacing: 4 ) {
145- Image ( systemName: " checkmark.circle.fill " )
146- . foregroundColor ( . blue)
147- . imageScale ( family == . systemSmall ? . small : . medium)
148- Text ( " Taskwarrior " )
149- . font ( family == . systemSmall ? . caption. bold ( ) : . headline)
157+ HStack ( spacing: 6 ) {
158+ Image ( " taskwarrior " )
159+ . resizable ( )
160+ . aspectRatio ( contentMode: . fit)
161+ . frame ( width: 30 , height: 30 )
162+
163+
150164 }
151165 Spacer ( )
152- Image ( systemName: " plus.circle.fill " )
153- . foregroundColor ( . blue)
154- . imageScale ( family == . systemSmall ? . medium : . large)
155- . widgetURL ( URL ( string: " taskwarrior://addClicked " ) )
166+ Text ( " Taskwarrior " )
167+ . font ( . headline)
168+ Spacer ( )
169+
170+ // Add Button
171+ Link ( destination: URL ( string: " taskwarrior://addclicked " ) !) {
172+ Image ( systemName: " plus.circle.fill " )
173+ . foregroundColor ( . blue)
174+ . font ( . system( size: 26 ) )
175+ }
156176 }
157- . padding ( . bottom, 8 )
158177 }
159178
160179 var emptyStateView : some View {
161- VStack ( spacing: 4 ) {
180+ VStack ( spacing: 6 ) {
181+ Spacer ( )
162182 Image ( systemName: " checkmark.circle " )
163- . font ( . title2 )
164- . foregroundColor ( . secondary)
165- Text ( " No tasks available " )
183+ . font ( . largeTitle )
184+ . foregroundColor ( . secondary. opacity ( 0.5 ) )
185+ Text ( " No tasks pending " )
166186 . font ( . caption)
167187 . foregroundColor ( . secondary)
168- . multilineTextAlignment ( . center )
188+ Spacer ( )
169189 }
170- . frame ( maxWidth: . infinity, maxHeight: . infinity)
171190 }
172191
173- // Small widget list with deeplinks (shows 3 tasks)
174- var smallTaskList : some View {
175- VStack ( spacing: 4 ) {
176- ForEach ( Array ( sortedTasks. prefix ( 3 ) ) ) { task in
177- smallTaskRow ( task: task)
178- . widgetURL ( URL ( string: " taskwarrior://cardClicked?uuid= \( task. uuid) " ) )
179- }
180- if sortedTasks. count > 3 {
181- Text ( " + \( sortedTasks. count - 3 ) more " )
182- . font ( . caption2)
183- . foregroundColor ( . secondary)
184- . frame ( maxWidth: . infinity, alignment: . center)
185- }
186- }
187- }
192+ // -- List Variations --
188193
189- // Medium widget
194+ // MEDIUM: With urgency text removed, we can fit 3 items comfortably
190195 var mediumTaskList : some View {
191- VStack ( spacing: 4 ) {
192- ForEach ( Array ( sortedTasks. prefix ( 3 ) ) ) { task in
193- mediumTaskRow ( task: task)
194- . widgetURL ( URL ( string: " taskwarrior://cardClicked ?uuid= \( task. uuid) " ) )
196+ VStack ( spacing: 4 ) { // Compact spacing
197+ ForEach ( Array ( sortedTasks. prefix ( 2 ) ) ) { task in
198+ taskRow ( task: task)
199+ . widgetURL ( URL ( string: " taskwarrior://cardclicked ?uuid= \( task. uuid) " ) )
195200 }
196201
197- if sortedTasks. count > 3 {
198- Text ( " + \( sortedTasks. count - 3 ) more tasks " )
202+ if sortedTasks. count > 2 {
203+ Text ( " + \( sortedTasks. count - 2 ) more " )
199204 . font ( . caption2)
200205 . foregroundColor ( . secondary)
201206 . frame ( maxWidth: . infinity, alignment: . center)
202- . padding ( . top , 2 )
207+ . padding ( . trailing , 4 )
203208 }
204209 }
205210 }
206211
207- // Large widget
212+ // LARGE: With urgency text removed, we can fit 6 items
208213 var largeTaskList : some View {
209- VStack ( spacing: 6 ) {
210- // Display only top 4 tasks by priority
211- ForEach ( Array ( sortedTasks. prefix ( 4 ) ) ) { task in
212- largeTaskRow ( task: task)
213- . widgetURL ( URL ( string: " taskwarrior://cardClicked?uuid= \( task. uuid) " ) )
214+ VStack ( spacing: 7 ) {
215+ ForEach ( Array ( sortedTasks. prefix ( 6 ) ) ) { task in
216+ taskRow ( task: task)
217+ . widgetURL ( URL ( string: " taskwarrior://cardclicked?uuid= \( task. uuid) " ) )
214218 }
215219
216- if sortedTasks. count > 4 {
217- Text ( " + \( sortedTasks. count - 4 ) more tasks " )
220+ if sortedTasks. count > 6 {
221+ Text ( " + \( sortedTasks. count - 6 ) more tasks " )
218222 . font ( . caption)
219223 . foregroundColor ( . secondary)
220224 . frame ( maxWidth: . infinity, alignment: . center)
221- . padding ( . top, 4 )
225+ . padding ( . top, 2 )
222226 }
223227 }
224228 }
225229
226- func smallTaskRow( task: Task ) -> some View {
227- HStack ( spacing: 6 ) {
228- Circle ( )
229- . fill ( task. priorityColor)
230- . frame ( width: 8 , height: 8 )
231- Text ( task. description)
232- . font ( . caption2)
233- . lineLimit ( 1 )
234- . truncationMode ( . tail)
235- Spacer ( )
236- }
237- . padding ( . vertical, 4 )
238- . padding ( . horizontal, 6 )
239- . background (
240- RoundedRectangle ( cornerRadius: 4 )
241- . fill ( Color ( UIColor . secondarySystemBackground) )
242- )
243- }
230+ // -- Row Designs --
244231
245- func mediumTaskRow( task: Task ) -> some View {
246- HStack ( spacing: 8 ) {
247- Circle ( )
232+ // Unified Row Design (Clean, Single Line)
233+ func taskRow( task: Task ) -> some View {
234+ HStack ( spacing: 10 ) {
235+ // Colored Bar
236+ Capsule ( )
248237 . fill ( task. priorityColor)
249- . frame ( width: 8 , height: 8 )
238+ . frame ( width: 10 , height: 10 ) // Smaller height since text is single line
239+
240+ // Description (Single line, centered vertically)
250241 Text ( task. description)
251- . font ( . caption)
242+ . font ( . subheadline)
243+ . fontWeight ( . medium)
252244 . lineLimit ( 1 )
253- . truncationMode ( . tail)
254- Spacer ( )
255- }
256- . padding ( . vertical, 5 )
257- . padding ( . horizontal, 8 )
258- . background (
259- RoundedRectangle ( cornerRadius: 6 )
260- . fill ( Color ( UIColor . secondarySystemBackground) )
261- )
262- }
263-
264- func largeTaskRow( task: Task ) -> some View {
265- HStack ( spacing: 10 ) {
266- Circle ( )
267- . fill ( task. priorityColor)
268- . frame ( width: 10 , height: 10 )
269- VStack ( alignment: . leading, spacing: 2 ) {
270- Text ( task. description)
271- . font ( . subheadline)
272- . lineLimit ( 1 )
273- . truncationMode ( . tail)
274- Text ( task. urgency)
275- . font ( . caption2)
276- . foregroundColor ( . secondary)
277- }
245+ . foregroundColor ( . primary)
246+
278247 Spacer ( )
279248 }
280- . padding ( . vertical, 6 )
281- . padding ( . horizontal, 8 )
249+ // Compact Padding to fit more items
250+ . padding ( . vertical, 8 )
251+ . padding ( . horizontal, 12 )
282252 . background (
283- RoundedRectangle ( cornerRadius: 8 )
253+ RoundedRectangle ( cornerRadius: 12 )
284254 . fill ( Color ( UIColor . secondarySystemBackground) )
285255 )
286- . widgetURL ( URL ( string : " taskwarrior://cardClicked?uuid= \( task . uuid ) " ) )
256+ . fixedSize ( horizontal : false , vertical : true )
287257 }
288258}
289259
@@ -296,7 +266,7 @@ struct TasksWidget: Widget {
296266 }
297267 . configurationDisplayName ( " Tasks " )
298268 . description ( " Shows your pending tasks " )
299- . supportedFamilies ( [ . systemSmall , . systemMedium, . systemLarge] )
269+ . supportedFamilies ( [ . systemMedium, . systemLarge] )
300270 . contentMarginsDisabled ( )
301271 }
302272}
@@ -310,10 +280,4 @@ struct TasksWidget: Widget {
310280 themeMode: " light " ,
311281 configuration: ConfigurationAppIntent ( )
312282 )
313- TaskWidgetEntry (
314- date: . now,
315- tasks: " [{ \" description \" : \" Critical bug fix \" , \" urgency \" : \" urgency: 6.5 \" , \" uuid \" : \" abc \" , \" priority \" : \" H \" }, { \" description \" : \" Update docs \" , \" urgency \" : \" urgency: 2.8 \" , \" uuid \" : \" def \" , \" priority \" : \" M \" }, { \" description \" : \" Review PR \" , \" urgency \" : \" urgency: 4.5 \" , \" uuid \" : \" ghi \" , \" priority \" : \" H \" }, { \" description \" : \" Fix typos \" , \" urgency \" : \" urgency: 1.2 \" , \" uuid \" : \" jkl \" , \" priority \" : \" L \" }, { \" description \" : \" Add tests \" , \" urgency \" : \" urgency: 3.8 \" , \" uuid \" : \" mno \" , \" priority \" : \" M \" }, { \" description \" : \" Refactor code \" , \" urgency \" : \" urgency: 2.5 \" , \" uuid \" : \" pqr \" , \" priority \" : \" N \" }] " ,
316- themeMode: " dark " ,
317- configuration: ConfigurationAppIntent ( )
318- )
319283}
0 commit comments