-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
-
336
-
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
-
447
-
448
-
449
-
450
-
451
-
452
-
453
-
454
-
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
-
490
-
491
-
492
-
493
-
494
-
495
-
496
-
497
-
498
-
499
-
500
-
501
-
502
-
503
-
504
-
505
-
506
-
507
-
508
-
509
-
510
-
511
-
512
-
513
-
514
-
515
-
516
-
517
-
518
-
519
-
520
-
521
-
522
-
523
-
524
-
525
-
526
-
527
-
528
-
529
-
530
-
531
-
532
-
533
-
534
-
535
-
536
-
537
-
538
-
539
-
540
-
541
-
542
-
543
-
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
-
555
-
556
-
557
-
558
-
559
-
560
-
561
-
562
-
563
-
564
-
565
-
566
-
567
-
568
-
569
-
570
-
571
-
572
-
573
-
574
-
575
-
576
-
577
-
578
-
579
-
580
-
581
-
582
-
583
-
584
-
585
-
586
-
587
-
588
-
589
-
590
-
591
-
592
-
593
-
594
-
595
-
596
-
597
-
598
-
599
-
600
-
601
-
602
-
603
-
604
-
605
-
606
-
607
-
608
-
609
-
610
-
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
-
619
-
620
-
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
-
633
-
634
-
635
-
636
-
637
-
638
-
639
-
640
-
641
-
642
-
643
-
644
-
645
-
646
-
647
-
648
-
649
-
650
-
651
-
652
-
653
-
654
-
655
-
656
-
657
-
658
-
659
-
660
-
661
-
662
-
663
-
664
-
665
-
666
-
667
-
668
-
669
-
670
-
671
-
672
-
673
-
674
-
675
-
676
-
677
-
678
-
679
-
680
-
681
-
682
-
683
-
684
-
685
-
686
-
687
-
688
-
689
-
690
-
691
-
692
-
693
-
694
-
695
-
696
-
697
-
698
-
699
-
700
-
701
-
702
-
703
-
704
-
705
-
706
-
707
-
708
-
709
-
710
-
711
-
712
-
713
-
714
-
715
-
716
-
717
-
718
-
719
-
720
-
721
-
722
-
723
-
724
-
725
-
726
-
727
-
728
-
729
-
730
-
731
-
732
-
733
-
734
-
735
-
736
-
737
-
738
-
739
-
740
-
741
-
742
-
743
-
744
-
745
-
746
-
747
-
748
-
749
-
750
-
751
-
752
-
753
-
754
-
755
-
756
-
757
-
758
-
759
-
760
-
761
-
762
-
763
-- Copyright 2026 Shota FUJI
--
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- SPDX-License-Identifier: MPL-2.0
module Parameters.App exposing (Model, Msg, init, update, view)
import Dict exposing (Dict)
import Html exposing (hr, input, label, node, p, span, text)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Html.LivingStandard exposing (..)
import Length exposing (Length, toMM)
import Parameters exposing (ColorSchema(..), Parameters, colorSchemaToString, defaultFixedLoop, defaultFreeLoop)
import Parameters.Constraints exposing (NumberConstraints, constraints)
import Parameters.Key as Key exposing (Key(..))
import Parameters.Parser exposing (..)
-- MODEL
type alias Fields =
Dict String String
type alias Errors =
Dict String Error
type alias Model =
{ fields : Fields
, errors : Errors
, parameters : Parameters
}
init : Parameters -> Model
init params =
let
fields =
Dict.fromList
[ ( Key.toString LugWidth, String.fromFloat (toMM params.lugWidth) )
, ( Key.toString SurfaceThickness, String.fromFloat (toMM params.thickness) )
, ( Key.toString LiningThickness, String.fromFloat (toMM params.lining) )
, ( Key.toString BuckleHoleCount, String.fromInt params.longPiece.buckleHole.count )
, ( Key.toString BuckleHoleDiameter, String.fromFloat (toMM params.longPiece.buckleHole.diameter) )
, ( Key.toString LoopThickness, String.fromFloat (toMM params.shortPiece.loops.thickness) )
, ( Key.toString HasFixedLoop
, if params.shortPiece.loops.fixed == Nothing then
"false"
else
"true"
)
, ( Key.toString FixedLoopWidth
, params.shortPiece.loops.fixed |> Maybe.withDefault defaultFixedLoop |> .width |> toMM |> String.fromFloat
)
, ( Key.toString FixedLoopPlay
, params.shortPiece.loops.fixed |> Maybe.withDefault defaultFixedLoop |> .play |> toMM |> String.fromFloat
)
, ( Key.toString HasFreeLoop
, if params.shortPiece.loops.free == Nothing then
"false"
else
"true"
)
, ( Key.toString FreeLoopWidth
, params.shortPiece.loops.free |> Maybe.withDefault defaultFreeLoop |> .width |> toMM |> String.fromFloat
)
, ( Key.toString FreeLoopPlay
, params.shortPiece.loops.free |> Maybe.withDefault defaultFreeLoop |> .play |> toMM |> String.fromFloat
)
, ( Key.toString FreeLoopOverlap
, params.shortPiece.loops.free |> Maybe.withDefault defaultFreeLoop |> .overlap |> toMM |> String.fromFloat
)
, ( Key.toString CanvasMargin, String.fromFloat (toMM params.rendering.margin) )
, ( Key.toString LineWidth, String.fromFloat (toMM params.rendering.lineWidth) )
, ( Key.toString ColorSchema, colorSchemaToString params.rendering.colorSchema )
]
in
{ fields = fields
, errors =
case parse fields of
Ok _ ->
Dict.empty
Err errors ->
errors
, parameters = params
}
-- UPDATE
type Msg
= FieldChanged Key String
| ColorSchemaChanged ColorSchema
parseField : Key -> (String -> Result Error a) -> Fields -> Result Error a
parseField key f fields =
Dict.get (Key.toString key) fields
|> Maybe.withDefault ""
|> f
mkErrors : List ( Key, Maybe Error ) -> Errors
mkErrors list =
list
|> List.filterMap (\( key, error ) -> Maybe.map (\e -> ( Key.toString key, e )) error)
|> Dict.fromList
getError : Result a b -> Maybe a
getError r =
case r of
Err e ->
Just e
_ ->
Nothing
parseFixedLoop : Fields -> Result Errors (Maybe Parameters.FixedLoop)
parseFixedLoop fields =
case parseField HasFixedLoop parseBool fields of
Ok True ->
case
( parseField FixedLoopWidth (parseLength constraints.shortPiece.loops.fixed.width) fields
, parseField FixedLoopPlay (parseLength constraints.shortPiece.loops.fixed.play) fields
)
of
( Ok width, Ok play ) ->
let
base =
Parameters.defaultFixedLoop
in
Ok (Just { base | width = width, play = play })
( width, play ) ->
Err
(mkErrors
[ ( FixedLoopWidth, getError width )
, ( FixedLoopPlay, getError play )
]
)
_ ->
Ok Nothing
parseFreeLoop : Fields -> Result Errors (Maybe Parameters.FreeLoop)
parseFreeLoop fields =
case parseField HasFreeLoop parseBool fields of
Ok True ->
case
( parseField FreeLoopWidth (parseLength constraints.shortPiece.loops.free.width) fields
, parseField FreeLoopPlay (parseLength constraints.shortPiece.loops.free.play) fields
, parseField FreeLoopOverlap (parseLength constraints.shortPiece.loops.free.overlap) fields
)
of
( Ok width, Ok play, Ok overlap ) ->
Ok (Just { width = width, play = play, overlap = overlap })
( width, play, overlap ) ->
Err
(mkErrors
[ ( FreeLoopWidth, getError width )
, ( FreeLoopPlay, getError play )
, ( FreeLoopOverlap, getError overlap )
]
)
_ ->
Ok Nothing
parseLoops : Fields -> Result Errors Parameters.Loops
parseLoops fields =
case ( parseFixedLoop fields, parseFreeLoop fields ) of
( Ok fixed, Ok free ) ->
let
base =
Parameters.default.shortPiece.loops
in
Ok { base | fixed = fixed, free = free }
( fixed, free ) ->
Err
(Dict.union
(getError fixed |> Maybe.withDefault Dict.empty)
(getError free |> Maybe.withDefault Dict.empty)
)
parseShortPiece : Fields -> Result Errors Parameters.ShortPiece
parseShortPiece fields =
case parseLoops fields of
Ok loops ->
let
base =
Parameters.default.shortPiece
in
Ok { base | loops = loops }
Err errs ->
Err errs
parseBuckleHole : Fields -> Result Errors Parameters.BuckleHole
parseBuckleHole fields =
case
( parseField BuckleHoleCount (parseInt constraints.longPiece.buckleHole.count) fields
, parseField BuckleHoleDiameter (parseLength constraints.longPiece.buckleHole.diameter) fields
)
of
( Ok count, Ok diameter ) ->
let
base =
Parameters.default.longPiece.buckleHole
in
Ok { base | count = count, diameter = diameter }
( count, diameter ) ->
Err
(mkErrors
[ ( BuckleHoleCount, getError count )
, ( BuckleHoleDiameter, getError diameter )
]
)
parseLongPiece : Fields -> Result Errors Parameters.LongPiece
parseLongPiece fields =
case parseBuckleHole fields of
Ok buckleHole ->
let
base =
Parameters.default.longPiece
in
Ok { base | buckleHole = buckleHole }
Err errors ->
Err errors
parseCanvas : Fields -> Result Errors Parameters.Rendering
parseCanvas fields =
case
( parseField CanvasMargin (parseLength constraints.rendering.margin) fields
, parseField LineWidth (parseLength constraints.rendering.lineWidth) fields
, parseField ColorSchema parseColorSchema fields
)
of
( Ok margin, Ok lineWidth, Ok colorSchema ) ->
let
base =
Parameters.default.rendering
in
Ok { base | margin = margin, lineWidth = lineWidth, colorSchema = colorSchema }
( margin, lineWidth, colorSchema ) ->
Err
(mkErrors
[ ( CanvasMargin, getError margin )
, ( LineWidth, getError lineWidth )
, ( ColorSchema, getError colorSchema )
]
)
parse : Fields -> Result Errors Parameters
parse fields =
case
( ( parseField LugWidth (parseLength constraints.lugWidth) fields
, parseField LiningThickness (parseLength constraints.lining) fields
, parseField SurfaceThickness (parseLength constraints.thickness) fields
)
, ( parseLongPiece fields
, parseShortPiece fields
)
, parseCanvas fields
)
of
( ( Ok lugWidth, Ok lining, Ok thickness ), ( Ok longPiece, Ok shortPiece ), Ok rendering ) ->
let
base =
Parameters.default
in
Ok
{ base
| lugWidth = lugWidth
, lining = lining
, thickness = thickness
, longPiece = longPiece
, shortPiece = shortPiece
, rendering = rendering
}
( ( lugWidth, lining, thickness ), ( longPiece, shortPiece ), rendering ) ->
[ mkErrors [ ( LugWidth, getError lugWidth ) ]
, mkErrors [ ( LiningThickness, getError lining ) ]
, mkErrors [ ( SurfaceThickness, getError thickness ) ]
, getError longPiece |> Maybe.withDefault Dict.empty
, getError shortPiece |> Maybe.withDefault Dict.empty
, getError rendering |> Maybe.withDefault Dict.empty
]
|> List.map Dict.toList
|> List.concat
|> Dict.fromList
|> Err
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FieldChanged key value ->
let
fields =
Dict.insert (Key.toString key) value model.fields
in
case parse fields of
Ok newParams ->
( { model
| fields = fields
, errors = Dict.empty
, parameters = newParams
}
, Cmd.none
)
Err errors ->
( { model | fields = fields, errors = errors }, Cmd.none )
ColorSchemaChanged schema ->
let
{ parameters } =
model
{ rendering } =
parameters
in
( { model | parameters = { parameters | rendering = { rendering | colorSchema = schema } } }
, Cmd.none
)
-- VIEW
lengthFieldAttrs : NumberConstraints Length -> List (Html.Attribute msg)
lengthFieldAttrs { min, max } =
[ case min of
Just x ->
toMM x
|> String.fromFloat
|> Html.Attributes.min
Nothing ->
class ""
, case max of
Just x ->
toMM x
|> String.fromFloat
|> Html.Attributes.max
Nothing ->
class ""
, attribute "inputmode" "numeric"
, required True
]
intFieldAttrs : NumberConstraints Int -> List (Html.Attribute msg)
intFieldAttrs { min, max } =
[ case min of
Just x ->
Html.Attributes.min (String.fromInt x)
Nothing ->
class ""
, case max of
Just x ->
Html.Attributes.max (String.fromInt x)
Nothing ->
class ""
, attribute "inputmode" "numeric"
, required True
]
ariaInvalid : Bool -> Html.Attribute msg
ariaInvalid invalid =
attribute "aria-invalid"
(if invalid then
"true"
else
"false"
)
ariaDescribedBy : List String -> Html.Attribute msg
ariaDescribedBy ids =
case ids of
[] ->
class ""
_ ->
ids
|> String.join " "
|> attribute "aria-describedby"
errorId : Key -> String
errorId key =
Key.toString key ++ "_error"
errorText : Error -> List (Html.Html msg)
errorText e =
case e of
MissingValue ->
[ text "This field is required." ]
BelowMin n ->
[ text ("Value must be greater than or equals to " ++ String.fromFloat n ++ ".") ]
AboveMax n ->
[ text ("Value must be less than or equals to " ++ String.fromFloat n ++ ".") ]
NotALength ->
[ text "Invalid length value. Set an integer or a floating point number." ]
NotAnInt ->
[ text "Invalid integer value. This field only accepts integer value." ]
NotABool ->
[ text "Invalid bool value. Value must be either \"true\" or \"false\"." ]
NonexistentVariant str ->
[ text ("\"" ++ str ++ "\" is not a valid choice. Did you modify program's source code?") ]
descriptionId : Key -> String
descriptionId key =
Key.toString key ++ "_description"
type alias NumberFieldProps msg =
{ key : Key
, title : List (Html.Html msg)
, description : List (Html.Html msg)
, unit : Maybe String
, attrs : List (Html.Attribute msg)
, disabled : Bool
}
numberField : Model -> NumberFieldProps Msg -> Html.Html Msg
numberField model { key, title, description, attrs, unit, disabled } =
node "x-field"
[ if disabled then
attribute "disabled" ""
else
class ""
]
[ label [ for (Key.toString key), slot "title" ] title
, p
[ id (descriptionId key), slot "description" ]
description
, node "x-number-input"
[]
[ input
(id (Key.toString key)
:: value (model.fields |> Dict.get (Key.toString key) |> Maybe.withDefault "")
:: onInput (FieldChanged key)
:: ariaInvalid (not (Dict.get (Key.toString key) model.errors == Nothing))
:: ariaDescribedBy [ errorId key, descriptionId key ]
:: Html.Attributes.disabled disabled
:: attrs
)
[]
, case unit of
Just u ->
span [ slot "unit" ] [ text u ]
Nothing ->
text ""
]
, p
[ id (errorId key), slot "error" ]
(Dict.get (Key.toString key) model.errors
|> Maybe.map errorText
|> Maybe.withDefault []
)
]
type alias ChoiceFieldProps msg =
{ key : Key
, title : List (Html.Html msg)
, description : List (Html.Html msg)
, attrs : List (Html.Attribute msg)
, choices :
List
{ value : String
, label : List (Html.Html msg)
, description : List (Html.Html msg)
}
}
choiceField : Model -> ChoiceFieldProps Msg -> Html.Html Msg
choiceField model { key, title, description, attrs, choices } =
node "x-field"
[]
(span [ slot "title" ] title
:: p [ slot "description" ] description
:: List.map
(\choice ->
let
id =
Key.toString key ++ "_" ++ choice.value
choiceDescriptionId =
id ++ "__description"
checked =
Dict.get (Key.toString key) model.fields
|> Maybe.withDefault ""
|> (==) choice.value
in
node "x-radio-box"
[ if checked then
attribute "checked" ""
else
class ""
]
[ Html.label [ for id, slot "label" ] choice.label
, Html.p [ Html.Attributes.id choiceDescriptionId, slot "description" ] description
, input
(type_ "radio"
:: Html.Attributes.id id
:: Html.Attributes.name (Key.toString key)
:: Html.Attributes.value choice.value
:: Html.Attributes.checked checked
:: Html.Events.onCheck (\_ -> FieldChanged key choice.value)
:: ariaDescribedBy [ choiceDescriptionId ]
:: attrs
)
[]
]
)
choices
)
type alias GroupProps msg =
{ title : List (Html.Html msg)
, description : Maybe (List (Html.Html msg))
}
group : GroupProps Msg -> List (Html.Html Msg) -> Html.Html Msg
group { title, description } children =
node "x-field-group"
[]
(p [ slot "title" ] title
:: (description |> Maybe.map (p [ slot "description" ]) |> Maybe.withDefault (text ""))
:: children
)
view : Model -> List (Html.Html Msg)
view model =
[ group
{ title = [ text "General" ], description = Nothing }
[ numberField model
{ key = LugWidth
, title = [ text "Lug width" ]
, description =
[ text "This will be the final width of your strap. "
, text "You can use a size smaller than your lug width to create a play."
]
, unit = Just "mm"
, disabled = False
, attrs = step "1.0" :: lengthFieldAttrs constraints.lugWidth
}
, numberField model
{ key = SurfaceThickness
, title = [ text "Surface Leather Thickness" ]
, description = [ text "Thickness of surface leather." ]
, unit = Just "mm"
, disabled = False
, attrs = step "0.1" :: lengthFieldAttrs constraints.thickness
}
, numberField model
{ key = LiningThickness
, title = [ text "Lining Leather Thickness" ]
, description =
[ text "Thickness of lining leather. Set to 0 to disable lining generation."
]
, unit = Just "mm"
, disabled = False
, attrs = step "0.1" :: lengthFieldAttrs constraints.lining
}
]
, hr [] []
, group
{ title = [ text "Buckle / Clasp" ], description = Nothing }
[ numberField model
{ key = BuckleHoleCount
, title = [ text "Hole Count" ]
, description = [ text "Set 0 to disable buckle holes generation." ]
, unit = Nothing
, disabled = False
, attrs = step "1" :: intFieldAttrs constraints.longPiece.buckleHole.count
}
, numberField model
{ key = BuckleHoleDiameter
, title = [ text "Hole Diameter" ]
, description =
[ text "Diameter of buckle holes. You can leave the default value if you're going to use the center mark." ]
, unit = Just "mm"
, disabled = model.parameters.longPiece.buckleHole.count == 0
, attrs =
step "1.0" :: lengthFieldAttrs constraints.longPiece.buckleHole.diameter
}
]
, hr [] []
, group
{ title = [ text "Loops" ], description = Nothing }
[ choiceField model
{ key = HasFixedLoop
, title = [ text "Fixed Loop" ]
, description = [ text "Whether to draw fixed loop. Certain kind of deployant buckles don't require a fixed loop." ]
, attrs = []
, choices =
[ { label = [ text "Enabled" ]
, description = [ text "Output fixed loop." ]
, value = "true"
}
, { label = [ text "Disabled" ]
, description = [ text "Do not output fixed loop." ]
, value = "false"
}
]
}
, numberField model
{ key = FixedLoopWidth
, title = [ text "Fixed Loop Width" ]
, description = [ text "Width of the fixed loop." ]
, unit = Just "mm"
, disabled = not (Dict.get (Key.toString HasFixedLoop) model.fields == Just "true")
, attrs =
step "0.1" :: lengthFieldAttrs constraints.shortPiece.loops.fixed.width
}
, numberField model
{ key = FixedLoopPlay
, title = [ text "Fixed Loop Play" ]
, description = [ text "Extra length to add to the fixed loop, for adjusting tightness." ]
, unit = Just "mm"
, disabled = not (Dict.get (Key.toString HasFixedLoop) model.fields == Just "true")
, attrs =
step "0.1" :: lengthFieldAttrs constraints.shortPiece.loops.fixed.play
}
, choiceField model
{ key = HasFreeLoop
, title = [ text "Free Loop" ]
, description = [ text "Whether to draw free loop. Certain kind of deployant buckles don't require a free loop." ]
, attrs = []
, choices =
[ { label = [ text "Enabled" ]
, description = [ text "Output free loop." ]
, value = "true"
}
, { label = [ text "Disabled" ]
, description = [ text "Do not output free loop." ]
, value = "false"
}
]
}
, numberField model
{ key = FreeLoopWidth
, title = [ text "Free Loop Width" ]
, description = [ text "Width of the free loop." ]
, unit = Just "mm"
, disabled = not (Dict.get (Key.toString HasFreeLoop) model.fields == Just "true")
, attrs =
step "0.1" :: lengthFieldAttrs constraints.shortPiece.loops.free.width
}
, numberField model
{ key = FreeLoopPlay
, title = [ text "Free Loop Play" ]
, description = [ text "Extra length to add to the free loop, for adjusting tightness." ]
, unit = Just "mm"
, disabled = not (Dict.get (Key.toString HasFreeLoop) model.fields == Just "true")
, attrs =
step "0.1" :: lengthFieldAttrs constraints.shortPiece.loops.free.play
}
, numberField model
{ key = FreeLoopOverlap
, title = [ text "Free Loop Overlap" ]
, description = [ text "Length of overlapping section of the free loop for gluing and/or stitching." ]
, unit = Just "mm"
, disabled = not (Dict.get (Key.toString HasFreeLoop) model.fields == Just "true")
, attrs =
step "0.1" :: lengthFieldAttrs constraints.shortPiece.loops.free.overlap
}
]
, hr [] []
, group
{ title = [ text "Rendering" ], description = Nothing }
[ numberField model
{ key = CanvasMargin
, title = [ text "Print Margin" ]
, description =
[ text "Lower values can cause printing problems depending on your printer." ]
, unit = Just "mm"
, disabled = False
, attrs = step "1.0" :: lengthFieldAttrs constraints.rendering.margin
}
, numberField model
{ key = LineWidth
, title = [ text "Line Width" ]
, description =
[ text "Stroke width (thickness) of the cutting lines and seam lines." ]
, unit = Just "mm"
, disabled = False
, attrs = step "0.1" :: lengthFieldAttrs constraints.rendering.lineWidth
}
, choiceField model
{ key = ColorSchema
, title = [ text "Color Schema" ]
, description = [ text "Color schema of the template." ]
, attrs = []
, choices =
[ { label = [ text "Black on White" ]
, description = [ text "Select this if you print the template with a regular printer." ]
, value = colorSchemaToString BlackOnWhite
}
, { label = [ text "White on Black" ]
, description = [ text "For digital output. Printing this with an inkjet printer is not a good idea." ]
, value = colorSchemaToString WhiteOnBlack
}
]
}
]
]