9. Layout templates

Chapters 1 to 8 built every line from keywords: the elements come in a fixed order, and each one carries its own spaces and words (ETA, %/s). A template lays the line out instead: literal text, and fields in braces where the values go. march writes its line as it wants it, with the residual of the solution as a field of its own:
call bar%initialize(template='march {bar:30} {percent:yellow} step {count:cyan} ETA {eta:blue} res {residual:magenta}', &
partial_blocks=.true., filled_char_color_fg='cyan', empty_char_color_bg='black_intense', &
max_value=real(steps, R8P))
field%value => residual
call bar%add_field('residual', field) ! after initialize, before startWhile running:
$ march
march ███████████████ 50% step 25/50 ETA hh:mm:ss res 3.0E-08At the end:
$ march
march ██████████████████████████████ 100% step 50/50 ETA hh:mm:ss res 8.9E-16- Everything outside the braces is written as it is:
march,step,ETA,res. The fields write their value only:{percent}isnnn%,{count}is25/50,{eta}ishh:mm:ss; the spacing is the template's. - After a colon, a field takes colours and a style:
{percent:yellow},{eta:white,on_blue,bold_on}. The bar takes its width instead,{bar:30}; its colours stay the keywords of the filled and empty parts. - A mistake stops the program with a message that names it: an unknown colour or style, an unclosed brace, in
initialize; a field that is neither forbear's nor added by the program, instart, so thatadd_fieldcan come in between.
A field of your own
{residual} is not a field of forbear: the program adds it. A field is a type that extends field_object and returns its text from render:
module residual_fields
!< A field of the program: the residual of the solution, read at every drawing.
use, intrinsic :: iso_fortran_env, only : R8P=>real64
use forbear, only : field_object, progress_object
implicit none
private
public :: residual_field
type, extends(field_object) :: residual_field
real(R8P), pointer :: value => null() ! the residual, a variable of the program
contains
procedure, pass(self) :: render
endtype residual_field
contains
function render(self, progress) result(text)
!< Return the residual, always 7 characters wide.
class(residual_field), intent(in) :: self
type(progress_object), intent(in) :: progress
character(len=:), allocatable :: text
character(len=7) :: buffer
write(buffer, '(ES7.1)') self%value
text = buffer
endfunction render
endmodule residual_fieldsrendergets the progress of the bar (progress_object: current value, fraction, percent, speed, elapsed time, ETA), and may read anything else through its components: here, a pointer to the residual of the program.bar%add_field('residual', field), afterinitialize(which removes the fields) and beforestart, names it for the template. The bar keeps a copy of the field; the pointer in the copy still points to the variable of the program.- Keep the text of a field the same width at every drawing, as the fields of forbear do: a line that shrinks is erased to its end, but a line whose fields move around is hard to read.
The keywords of chapters 1 to 8 still work, and draw exactly what they drew: a template is another way to describe the same line, for when the order, the words or the spacing are yours.
What you learned
template=, its fields and their colours; fields of the program with field_object and add_field. Reference: Layout templates.
Next: 10. Unknown ends.