a lightweight, interval-based utility to combat digital strain through "Ma" (intentional pauses) for the eyes and body.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(ui): refined interval editing layout

+356 -302
+356 -302
ui/settings.slint
··· 1 - import { SpinBox, Slider, LineEdit } from "std-widgets.slint"; 1 + import { SpinBox, Slider, LineEdit, ScrollView } from "std-widgets.slint"; 2 2 import "../assets/fonts/Nunito-Regular.ttf"; 3 3 import "../assets/fonts/Nunito-Medium.ttf"; 4 4 import "../assets/fonts/Nunito-SemiBold.ttf"; ··· 250 250 } 251 251 252 252 253 + // Styled text input 254 + component PaperInput { 255 + in-out property <string> text: ""; 256 + in property <string> placeholder-text: ""; 257 + in property <string> field-label: ""; 258 + callback edited(string); 259 + 260 + height: 32px; 261 + accessible-role: text-input; 262 + accessible-label: root.field-label; 263 + accessible-value: root.text; 264 + 265 + Rectangle { 266 + border-radius: 6px; 267 + background: ti.has-focus ? #23202808 : #FFFFFF60; 268 + border-width: 1px; 269 + border-color: ti.has-focus ? #8A9A8260 : #2320281A; 270 + animate background { duration: 120ms; } 271 + animate border-color { duration: 120ms; } 272 + clip: true; 273 + 274 + // Placeholder text when empty and unfocused 275 + if root.text == "" && !ti.has-focus: Text { 276 + x: 10px; 277 + text: root.placeholder-text; 278 + font-size: 13px; 279 + color: #23202848; 280 + vertical-alignment: center; 281 + } 282 + 283 + HorizontalLayout { 284 + padding-left: 10px; 285 + padding-right: 10px; 286 + 287 + ti := TextInput { 288 + horizontal-stretch: 1; 289 + text <=> root.text; 290 + font-size: 13px; 291 + font-weight: 500; 292 + color: #232028; 293 + horizontal-alignment: left; 294 + selection-background-color: #8A9A8230; 295 + selection-foreground-color: #232028; 296 + vertical-alignment: center; 297 + edited => { 298 + root.edited(self.text); 299 + } 300 + } 301 + } 302 + } 303 + } 304 + 253 305 // ── Interval card (Profile tab) ─────────────────────────────────────────────── 254 306 component IntervalCard { 255 307 in property <int> index; ··· 264 316 callback break-secs-changed(int); 265 317 callback label-changed(string); 266 318 267 - height: 82px; 268 - 269 319 Rectangle { 270 320 border-radius: 8px; 271 321 background: #FFFFFF80; 272 322 border-width: 1px; 273 323 border-color: #2320281A; 274 324 275 - VerticalLayout { 276 - padding-left: 16px; 277 - padding-right: 12px; 278 - padding-top: 10px; 279 - padding-bottom: 10px; 280 - spacing: 6px; 325 + HorizontalLayout { 326 + padding-left: 18px; 327 + padding-right: 14px; 328 + padding-top: 12px; 329 + padding-bottom: 12px; 330 + spacing: 18px; 281 331 282 - // Row 1 — work interval 283 - HorizontalLayout { 332 + // Left column — mirrors right column row heights so index 333 + // stays flush with the PaperInput in row 1 334 + VerticalLayout { 284 335 spacing: 8px; 285 - alignment: start; 336 + width: 20px; 286 337 287 338 Text { 288 339 text: root.index; 289 - font-size: 16px; 290 - color: #5A7055; 291 - width: 20px; 340 + font-family: "Shippori Mincho"; 341 + font-size: 28px; 342 + color: #232028CC; 343 + height: 34px; 292 344 horizontal-alignment: center; 293 - vertical-alignment: center; 345 + vertical-alignment: bottom; 294 346 } 295 347 296 - Text { 297 - text: "every"; 298 - font-size: 12px; 299 - color: #232028AA; 300 - vertical-alignment: center; 348 + Rectangle { 349 + height: 28px; 301 350 } 351 + } 302 352 303 - NumberField { 304 - width: 84px; 305 - value: root.work-mins; 306 - minimum: 1; 307 - maximum: 240; 308 - field-label: "work interval minutes"; 309 - edited(v) => { 310 - root.work-mins-changed(v); 311 - } 312 - } 353 + // Right column — label + interval 354 + VerticalLayout { 355 + spacing: 8px; 313 356 314 - Text { 315 - text: "min"; 316 - font-size: 12px; 317 - color: #232028AA; 318 - vertical-alignment: center; 319 - } 320 - } 357 + // Row 1: label input + cancel 358 + HorizontalLayout { 359 + spacing: 8px; 321 360 322 - // Row 2 — break duration + label 323 - HorizontalLayout { 324 - spacing: 8px; 325 - alignment: start; 361 + PaperInput { 362 + width: 200px; 363 + text: root.label; 364 + placeholder-text: "break name"; 365 + field-label: "break label"; 366 + edited(v) => { 367 + root.label-changed(v); 368 + } 369 + } 326 370 327 - Rectangle { width: 28px; } 371 + Rectangle { 372 + horizontal-stretch: 1; 373 + } 328 374 329 - Text { 330 - text: "pause"; 331 - font-size: 12px; 332 - color: #232028AA; 333 - vertical-alignment: center; 334 - } 375 + ta-rm := TouchArea { 376 + width: 24px; 377 + height: 32px; 378 + accessible-role: button; 379 + accessible-label: "Remove interval"; 380 + clicked => { 381 + root.remove-clicked(); 382 + } 335 383 336 - NumberField { 337 - width: 76px; 338 - value: root.break-mins; 339 - minimum: 0; 340 - maximum: 120; 341 - field-label: "break minutes"; 342 - edited(v) => { 343 - root.break-mins-changed(v); 384 + Text { 385 + text: "×"; 386 + font-size: 14px; 387 + color: ta-rm.has-hover ? #232028 : #23202866; 388 + horizontal-alignment: center; 389 + vertical-alignment: center; 390 + animate color { duration: 120ms; } 391 + } 344 392 } 345 393 } 346 394 347 - Text { 348 - text: "min"; 349 - font-size: 12px; 350 - color: #232028AA; 351 - vertical-alignment: center; 352 - } 395 + // Row 2: timing, indented to align with PaperInput's inner text 396 + HorizontalLayout { 397 + spacing: 8px; 398 + alignment: start; 353 399 354 - NumberField { 355 - width: 76px; 356 - value: root.break-secs; 357 - minimum: 0; 358 - maximum: 59; 359 - field-label: "break seconds"; 360 - edited(v) => { 361 - root.break-secs-changed(v); 400 + Rectangle { 401 + width: 4px; 362 402 } 363 - } 364 403 365 - Text { 366 - text: "sec"; 367 - font-size: 12px; 368 - color: #232028AA; 369 - vertical-alignment: center; 370 - } 404 + Text { 405 + text: "every"; 406 + font-size: 12px; 407 + color: #232028AA; 408 + vertical-alignment: center; 409 + } 371 410 372 - Text { 373 - text: "·"; 374 - font-size: 12px; 375 - color: #23202888; 376 - vertical-alignment: center; 377 - } 411 + NumberField { 412 + width: 80px; 413 + value: root.work-mins; 414 + minimum: 1; 415 + maximum: 240; 416 + field-label: "work interval minutes"; 417 + edited(v) => { 418 + root.work-mins-changed(v); 419 + } 420 + } 421 + 422 + Text { 423 + text: "min · pause"; 424 + font-size: 12px; 425 + color: #232028AA; 426 + vertical-alignment: center; 427 + } 378 428 379 - LineEdit { 380 - width: 120px; 381 - height: 28px; 382 - placeholder-text: "label"; 383 - text: root.label; 384 - edited(v) => { 385 - root.label-changed(v); 429 + NumberField { 430 + width: 72px; 431 + value: root.break-mins; 432 + minimum: 0; 433 + maximum: 120; 434 + field-label: "break minutes"; 435 + edited(v) => { 436 + root.break-mins-changed(v); 437 + } 386 438 } 387 - } 388 439 389 - Rectangle { horizontal-stretch: 1; } 440 + Text { 441 + text: "min"; 442 + font-size: 12px; 443 + color: #232028AA; 444 + vertical-alignment: center; 445 + } 390 446 391 - ta-rm := TouchArea { 392 - width: 28px; 393 - height: 28px; 394 - accessible-role: button; 395 - accessible-label: "Remove interval"; 396 - clicked => { 397 - root.remove-clicked(); 447 + NumberField { 448 + width: 72px; 449 + value: root.break-secs; 450 + minimum: 0; 451 + maximum: 59; 452 + field-label: "break seconds"; 453 + edited(v) => { 454 + root.break-secs-changed(v); 455 + } 398 456 } 399 457 400 458 Text { 401 - text: "×"; 402 - font-size: 15px; 403 - color: ta-rm.has-hover ? #232028 : #232028AA; 404 - horizontal-alignment: center; 459 + text: "sec"; 460 + font-size: 12px; 461 + color: #232028AA; 405 462 vertical-alignment: center; 406 - animate color { duration: 120ms; } 407 463 } 408 464 } 409 465 } ··· 459 515 export component SettingsWindow inherits Window { 460 516 title: "ioma — Settings"; 461 517 width: 760px; 462 - height: 540px; 518 + height: 680px; 463 519 background: #F3EFE7; 464 520 default-font-family: "Nunito"; 465 521 ··· 933 989 } 934 990 } 935 991 936 - if root.active-tab == "profile": VerticalLayout { 937 - padding-left: 28px; 938 - padding-right: 28px; 939 - padding-top: 24px; 940 - padding-bottom: 16px; 941 - spacing: 0px; 992 + if root.active-tab == "profile": ScrollView { 942 993 vertical-stretch: 1; 943 994 944 - Text { 945 - text: "Break cadence"; 946 - font-size: 13px; 947 - font-weight: 500; 948 - color: #232028; 949 - } 995 + VerticalLayout { 996 + padding-left: 28px; 997 + padding-right: 28px; 998 + padding-top: 24px; 999 + padding-bottom: 16px; 1000 + spacing: 0px; 1001 + 1002 + Text { 1003 + text: "Break cadence"; 1004 + font-size: 13px; 1005 + font-weight: 500; 1006 + color: #232028; 1007 + } 950 1008 951 - Text { 952 - text: "Each row is an interval. ioma cycles through them in order."; 953 - font-size: 11.5px; 954 - color: #232028AA; 955 - } 1009 + Text { 1010 + text: "Each row is an interval. ioma cycles through them in order."; 1011 + font-size: 11.5px; 1012 + color: #232028AA; 1013 + } 956 1014 957 - Rectangle { 958 - height: 14px; 959 - } 1015 + Rectangle { 1016 + height: 14px; 1017 + } 960 1018 961 - // Interval rows 1019 + // Interval rows 962 1020 for level[i] in root.levels: VerticalLayout { 963 - spacing: 0px; 1021 + spacing: 0px; 964 1022 965 - IntervalCard { 966 - index: i + 1; 967 - work-mins: level.work-mins; 968 - break-mins: level.break-mins; 969 - break-secs: level.break-extra-secs; 970 - label: level.label; 971 - remove-clicked => { 972 - root.level-removed(i); 973 - } 974 - work-mins-changed(v) => { 975 - root.level-changed(i, { 976 - work-mins: v, 977 - break-mins: level.break-mins, 978 - break-extra-secs: level.break-extra-secs, 979 - label: level.label 980 - }); 1023 + IntervalCard { 1024 + index: i + 1; 1025 + work-mins: level.work-mins; 1026 + break-mins: level.break-mins; 1027 + break-secs: level.break-extra-secs; 1028 + label: level.label; 1029 + remove-clicked => { 1030 + root.level-removed(i); 1031 + } 1032 + work-mins-changed(v) => { 1033 + root.level-changed(i, { 1034 + work-mins: v, 1035 + break-mins: level.break-mins, 1036 + break-extra-secs: level.break-extra-secs, 1037 + label: level.label 1038 + }); 1039 + } 1040 + break-mins-changed(v) => { 1041 + root.level-changed(i, { 1042 + work-mins: level.work-mins, 1043 + break-mins: v, 1044 + break-extra-secs: level.break-extra-secs, 1045 + label: level.label 1046 + }); 1047 + } 1048 + break-secs-changed(v) => { 1049 + root.level-changed(i, { 1050 + work-mins: level.work-mins, 1051 + break-mins: level.break-mins, 1052 + break-extra-secs: v, 1053 + label: level.label 1054 + }); 1055 + } 1056 + label-changed(v) => { 1057 + root.level-changed(i, { 1058 + work-mins: level.work-mins, 1059 + break-mins: level.break-mins, 1060 + break-extra-secs: level.break-extra-secs, 1061 + label: v 1062 + }); 1063 + } 981 1064 } 982 - break-mins-changed(v) => { 983 - root.level-changed(i, { 984 - work-mins: level.work-mins, 985 - break-mins: v, 986 - break-extra-secs: level.break-extra-secs, 987 - label: level.label 988 - }); 1065 + 1066 + Rectangle { 1067 + height: 10px; 989 1068 } 990 - break-secs-changed(v) => { 991 - root.level-changed(i, { 992 - work-mins: level.work-mins, 993 - break-mins: level.break-mins, 994 - break-extra-secs: v, 995 - label: level.label 996 - }); 1069 + } 1070 + 1071 + // Add interval 1072 + add-ta := TouchArea { 1073 + height: 42px; 1074 + clicked => { 1075 + root.level-added(); 997 1076 } 998 - label-changed(v) => { 999 - root.level-changed(i, { 1000 - work-mins: level.work-mins, 1001 - break-mins: level.break-mins, 1002 - break-extra-secs: level.break-extra-secs, 1003 - label: v 1004 - }); 1077 + 1078 + Rectangle { 1079 + border-radius: 8px; 1080 + background: add-ta.has-hover ? #FFFFFF60 : transparent; 1081 + border-width: 1px; 1082 + border-color: #23202828; 1083 + // dashed look via inner text (Slint has no native dashed borders) 1084 + 1085 + Text { 1086 + text: "+ Add interval"; 1087 + font-size: 12px; 1088 + color: add-ta.has-hover ? #232028 : #23202880; 1089 + horizontal-alignment: center; 1090 + vertical-alignment: center; 1091 + animate color { duration: 120ms; } 1092 + } 1005 1093 } 1006 1094 } 1007 1095 1008 1096 Rectangle { 1009 - height: 10px; 1097 + height: 20px; 1010 1098 } 1011 - } 1012 1099 1013 - // Add interval 1014 - add-ta := TouchArea { 1015 - height: 42px; 1016 - clicked => { 1017 - root.level-added(); 1100 + Rectangle { 1101 + height: 1px; 1102 + background: #2320281A; 1018 1103 } 1019 1104 1020 1105 Rectangle { 1021 - border-radius: 8px; 1022 - background: add-ta.has-hover ? #FFFFFF60 : transparent; 1023 - border-width: 1px; 1024 - border-color: #23202828; 1025 - // dashed look via inner text (Slint has no native dashed borders) 1026 - 1027 - Text { 1028 - text: "+ Add interval"; 1029 - font-size: 12px; 1030 - color: add-ta.has-hover ? #232028 : #23202880; 1031 - horizontal-alignment: center; 1032 - vertical-alignment: center; 1033 - animate color { duration: 120ms; } 1034 - } 1106 + height: 20px; 1035 1107 } 1036 - } 1037 1108 1038 - Rectangle { 1039 - height: 20px; 1040 - } 1041 - 1042 - Rectangle { 1043 - height: 1px; 1044 - background: #2320281A; 1045 - } 1109 + // Long rest section 1110 + HorizontalLayout { 1111 + spacing: 24px; 1046 1112 1047 - Rectangle { 1048 - height: 20px; 1049 - } 1113 + VerticalLayout { 1114 + alignment: center; 1115 + spacing: 3px; 1050 1116 1051 - // Long rest section 1052 - HorizontalLayout { 1053 - spacing: 24px; 1117 + Text { 1118 + text: "Long rest"; 1119 + font-size: 13px; 1120 + font-weight: 500; 1121 + color: #232028; 1122 + } 1054 1123 1055 - VerticalLayout { 1056 - alignment: center; 1057 - spacing: 3px; 1124 + Text { 1125 + text: "A longer pause after several cycles."; 1126 + font-size: 11.5px; 1127 + color: #232028AA; 1128 + } 1129 + } 1058 1130 1059 - Text { 1060 - text: "Long rest"; 1061 - font-size: 13px; 1062 - font-weight: 500; 1063 - color: #232028; 1131 + Rectangle { 1132 + horizontal-stretch: 1; 1064 1133 } 1065 1134 1066 - Text { 1067 - text: "A longer pause after several cycles."; 1068 - font-size: 11.5px; 1069 - color: #232028AA; 1135 + PaperToggle { 1136 + checked <=> root.long-break-enabled; 1070 1137 } 1071 1138 } 1072 1139 1073 1140 Rectangle { 1074 - horizontal-stretch: 1; 1141 + height: 12px; 1075 1142 } 1076 1143 1077 - PaperToggle { 1078 - checked <=> root.long-break-enabled; 1079 - } 1080 - } 1144 + VerticalLayout { 1145 + spacing: 0px; 1146 + opacity: root.long-break-enabled ? 1.0 : 0.38; 1147 + animate opacity { duration: 200ms; } 1081 1148 1082 - Rectangle { 1083 - height: 12px; 1084 - } 1149 + HorizontalLayout { 1150 + spacing: 8px; 1151 + alignment: start; 1085 1152 1086 - VerticalLayout { 1087 - spacing: 0px; 1088 - opacity: root.long-break-enabled ? 1.0 : 0.38; 1089 - animate opacity { duration: 200ms; } 1153 + Text { 1154 + text: "after"; 1155 + font-size: 12.5px; 1156 + color: #232028AA; 1157 + vertical-alignment: center; 1158 + } 1090 1159 1091 - HorizontalLayout { 1092 - spacing: 8px; 1093 - alignment: start; 1094 - 1095 - Text { 1096 - text: "after"; 1097 - font-size: 12.5px; 1098 - color: #232028AA; 1099 - vertical-alignment: center; 1100 - } 1160 + NumberField { 1161 + width: 80px; 1162 + value <=> root.long-break-after-cycles; 1163 + minimum: 2; 1164 + maximum: 20; 1165 + field-label: "cycles before long rest"; 1166 + } 1101 1167 1102 - NumberField { 1103 - width: 80px; 1104 - value <=> root.long-break-after-cycles; 1105 - minimum: 2; 1106 - maximum: 20; 1107 - field-label: "cycles before long rest"; 1108 - } 1168 + Text { 1169 + text: "cycles, take"; 1170 + font-size: 12.5px; 1171 + color: #232028AA; 1172 + vertical-alignment: center; 1173 + } 1109 1174 1110 - Text { 1111 - text: "cycles, take"; 1112 - font-size: 12.5px; 1113 - color: #232028AA; 1114 - vertical-alignment: center; 1115 - } 1175 + NumberField { 1176 + width: 80px; 1177 + value <=> root.long-break-duration-mins; 1178 + minimum: 1; 1179 + maximum: 120; 1180 + field-label: "long rest duration minutes"; 1181 + } 1116 1182 1117 - NumberField { 1118 - width: 80px; 1119 - value <=> root.long-break-duration-mins; 1120 - minimum: 1; 1121 - maximum: 120; 1122 - field-label: "long rest duration minutes"; 1123 - } 1183 + Text { 1184 + text: "min"; 1185 + font-size: 12.5px; 1186 + color: #232028AA; 1187 + vertical-alignment: center; 1188 + } 1124 1189 1125 - Text { 1126 - text: "min"; 1127 - font-size: 12.5px; 1128 - color: #232028AA; 1129 - vertical-alignment: center; 1130 - } 1190 + Text { 1191 + text: "·"; 1192 + font-size: 12.5px; 1193 + color: #23202888; 1194 + vertical-alignment: center; 1195 + } 1131 1196 1132 - Text { 1133 - text: "—"; 1134 - font-size: 12.5px; 1135 - color: #23202888; 1136 - vertical-alignment: center; 1197 + PaperInput { 1198 + width: 110px; 1199 + text <=> root.long-break-label; 1200 + placeholder-text: "Long rest"; 1201 + field-label: "long rest label"; 1202 + } 1137 1203 } 1138 1204 1139 - LineEdit { 1140 - width: 100px; 1141 - height: 28px; 1142 - placeholder-text: "Long rest"; 1143 - text <=> root.long-break-label; 1205 + Rectangle { 1206 + height: 10px; 1144 1207 } 1145 - } 1146 1208 1147 - Rectangle { 1148 - height: 10px; 1149 - } 1150 - 1151 - HorizontalLayout { 1152 - spacing: 8px; 1153 - alignment: start; 1209 + HorizontalLayout { 1210 + spacing: 8px; 1211 + alignment: start; 1154 1212 1155 - Text { 1156 - text: "Reset after"; 1157 - font-size: 11.5px; 1158 - color: #232028AA; 1159 - vertical-alignment: center; 1160 - } 1213 + Text { 1214 + text: "Reset after"; 1215 + font-size: 11.5px; 1216 + color: #232028AA; 1217 + vertical-alignment: center; 1218 + } 1161 1219 1162 - NumberField { 1163 - width: 80px; 1164 - value <=> root.long-break-gap-mins; 1165 - minimum: 1; 1166 - maximum: 120; 1167 - field-label: "idle reset threshold minutes"; 1168 - } 1220 + NumberField { 1221 + width: 80px; 1222 + value <=> root.long-break-gap-mins; 1223 + minimum: 1; 1224 + maximum: 120; 1225 + field-label: "idle reset threshold minutes"; 1226 + } 1169 1227 1170 - Text { 1171 - text: "min away."; 1172 - font-size: 11.5px; 1173 - color: #232028AA; 1174 - vertical-alignment: center; 1228 + Text { 1229 + text: "min away."; 1230 + font-size: 11.5px; 1231 + color: #232028AA; 1232 + vertical-alignment: center; 1233 + } 1175 1234 } 1176 1235 } 1177 - } 1178 - 1179 - // Spacer 1180 - Rectangle { 1181 - vertical-stretch: 1; 1182 1236 } 1183 1237 } 1184 1238