Skip to content

9. Layout templates ​

march with a layout of its own: words between the fields, and the residual as a field

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:

f90
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 start

While running:

$ march
march ███████████████                 50% step 25/50 ETA hh:mm:ss res 3.0E-08

At 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} is nnn%, {count} is 25/50, {eta} is hh: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, in start, so that add_field can 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:

f90
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_fields
  • render gets 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), after initialize (which removes the fields) and before start, 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.

Released under the GPL v3, BSD 2-Clause, BSD 3-Clause or MIT License.