Styling ======= Styling in the terminal has historically been quite confusing. Due to terminals being very old, there have been added multiple ways of styling, without the ability to remove legacy styling options due to backwards compatibility. .. seealso:: A list for all styling nodes can be found here: :doc:`nodes`. 3 Color Formats --------------- .. _color4: 4 bit color ~~~~~~~~~~~ One consequence of terminal history is that there are three ways of doing colors. The oldest, and most widely supported way is the 4 bit colors. **Colors specified with this format render differently depending on terminal theme!** This color format is accessible with the :obj:`~functui.classes.Color4` enum. Below follows a chart with all colors available with this enum. .. raw:: html
    BLACK   BRIGHT_BLACK     
    RED     BRIGHT_RED       
    GREEN   BRIGHT_GREEN     
    YELLOW  BRIGHT_YELLOW    
    BLUE    BRIGHT_BLUE      
    MAGENTA BRIGHT_MAGENTA   
    CYAN    BRIGHT_CYAN      
    WHITE   BRIGHT_WHITE     
    RESET
    
.. important:: ``Color4`` has a ``RESET`` attribute. It represents your terminal's default foreground or backround color. .. _color8: XTERM-256 ~~~~~~~~~ Another widely supported color format is XTERM-256 (8 bit colors) This format is also very widely supported. To access this format you just simply use an :obj:`int` anywhere a color is needed. Which integer corresponds to which color can be viewed in the figure below. .. raw:: html
    Regular and bright colors are rendered differently depending on terminal theme
    Regular:     0    1    2    3    4    5    6    7                                    
    Bright:      8    9   10   11   12   13   14   15                                    

    Color Cube
      16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   33 
      52   53   54   55   56   57   58   59   60   61   62   63   64   65   66   67   68   69 
      88   89   90   91   92   93   94   95   96   97   98   99  100  101  102  103  104  105 
     124  125  126  127  128  129  130  131  132  133  134  135  136  137  138  139  140  141 
     160  161  162  163  164  165  166  167  168  169  170  171  172  173  174  175  176  177 
     196  197  198  199  200  201  202  203  204  205  206  207  208  209  210  211  212  213 
      34   35   36   37   38   39   40   41   42   43   44   45   46   47   48   49   50   51 
      70   71   72   73   74   75   76   77   78   79   80   81   82   83   84   85   86   87 
     106  107  108  109  110  111  112  113  114  115  116  117  118  119  120  121  122  123 
     142  143  144  145  146  147  148  149  150  151  152  153  154  155  156  157  158  159 
     178  179  180  181  182  183  184  185  186  187  188  189  190  191  192  193  194  195 
     214  215  216  217  218  219  220  221  222  223  224  225  226  227  228  229  230  231 

    Color ramps, black and white intentionally excluded
     232  233  234  235  236  237  238  239  240  241  242  243 
     244  245  246  247  248  249  250  251  252  253  254  255 
    
.. tip:: Colors represented by integers 0 through 15 are exactly the same colors as in :obj:`~functui.classes.Color4` enum (except ``RESET``). That enum is actually an :obj:`~enum.IntEnum` which means that it's members are treated as integers. (``RESET`` member is represented as -1) .. _color24: True Color ~~~~~~~~~~ Lastly, there is true color (24 bit or rgb color). This color format can be accessed via :func:`~functui.classes.rgb`, :func:`~functui.classes.hsl` or :func:`~functui.classes.hex` functions that create a :obj:`~functui.classes.Color24` object that stores the color. This is the color format that the support is somewhat lacking. Most notably, the curses renderer does not support it. .. seealso:: Different renderers are discussed in :doc:`io`. Color Downgrading ----------------- If you are using a color format that is not supported by the renderer (or terminal), it will be automatically downgraded to a supported format, with the new colors trying to match the original as closely as possible. Styling elements ---------------- Functui uses :obj:`~functui.classes.StyleRule` objects to represent styles rules. These objects contains a foreground and background color as well as a bunch of :obj:`~functui.classes.StyleAttr` flags to represent style attributes like bold or italic. Color can be represented either by an :obj:`int` (for 4 and 8 bit colors) or a :obj:`~functui.classes.Color24` (for 24 bit colors). To use a style rule on a layout you can use :obj:`~functui.common.push_rule`. .. code-block:: py from functui import * # for Rect, StyleRule, StyleAttr, Color4 and rgb from functui.common import * # for text, push_rule and border from functui.io.ansi import layout_to_str style_rule = StyleRule( fg=Color4.BRIGHT_YELLOW, bg=rgb(10, 134, 143), add_attrs=StyleAttr.ITALIC | StyleAttr.BOLD # StyleAttr is a flag which means you # can use | to combine muliple attributes. ) # use style on a layout layout = text("styled_text") | push_rule(style_rule) | border print(layout_to_str(layout, Rect(20, 3))) Expected Output: .. raw:: html
    ┌──────────────────┐
    │styled_text       │
    └──────────────────┘
    
.. important:: As you may notice, only the text was styled even though technically the text node is taking up all of the available space inside the border. This is due to styles being applied only to "printed" characters, rather than the whole area a node takes up. To apply style to the whole node, use :obj:`~functui.common.bg_fill`. .. code-block:: py from functui import * from functui.common import * from functui.io.ansi import layout_to_str style_rule = StyleRule( fg=Color4.BRIGHT_YELLOW, bg=rgb(10, 134, 143), add_attrs=StyleAttr.ITALIC | StyleAttr.BOLD # StyleAttr is a flag which means you # can use | to combine muliple attributes. ) layout = text("styled_text") | bg_fill | push_rule(style_rule) | border # bg_fill here ----------------^^^^^^^ print(layout_to_str(layout, Rect(20, 3))) Expected Output: .. raw:: html
        ┌──────────────────┐
        │styled_text       │
        └──────────────────┘
        
``rule_*`` ~~~~~~~~~~ In some cases though, this syntax of defining style rules can be quite tedious, especially if you only want to define a few style attributes. For those cases, there are numerous `rule_*` constants and functions. .. code-block:: py from functui import * from functui.common import * style_rule = rule_fg(Color4.RED) | rule_bold | rule_italic # much less visual noise when defining styles inline layout = text("foo") | styled(border, rule_fg(Color4.RED)) Convenient Styling Nodes ~~~~~~~~~~~~~~~~~~~~~~~~ Finally, it's not always that you want to create a new style rule. Especially when it comes to quick prototyping, there are cases when you just want to apply one style and don't need the ability to reuse it. For those cases use :obj:`~functui.common.fg` and :obj:`~functui.common.bg` wrapper nodes for adding color to foreground and background respectively. To apply styles to a layout there are wrapper nodes that are named as style attributes. For example :obj:`~functui.common.bold` and :obj:`~functui.common.italic`. .. code-block:: py from functui import * from functui.common import * layout = text("This text is red and bold") | fg(Color4.RED) | bold | border print(layout_to_str(layout, Rect(30, 3))) Expected output: .. raw:: html
    ┌────────────────────────────┐
    │This text is red and bold   │
    └────────────────────────────┘
    
:obj:`~functui.common.styled` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Notice how these wrapper nodes style all of their descendants with the specified style. If you want to style only certain wrapper nodes (for example a border), you can use :obj:`~functui.common.styled` to wrap nodes you want to style. .. code-block:: py from functui import * from functui.common import * style_rule = rule_fg(rgb(0, 255, 255)) layout = text("This text is not styled.\nBut the border around it is.")\ | styled(border, style_rule) print(layout_to_str(layout, Rect(30, 4))) Expected output: .. raw:: html
    ┌────────────────────────────┐
    │This text is not styled.    │
    │But the border around it is.│
    └────────────────────────────┘
    
Rich Text --------- Sometimes there may be a need for multiple styles in the same paragraph. This can be done with the :obj:`~functui.rich_text.rich_text` node or :obj:`~functui.rich_text.adaptive_text` if you also want that paragraph to be responsive to screen size changes. To style only a part of the paragraph, wrap that part in a :obj:`~functui.rich_text.span` and specify a :obj:`~functui.classes.style_rule`. .. code-block:: py from functui import * from functui.common import * from functui.rich_text import adaptive_text, rich_text, span from functui.io.ansi import layout_to_str layout = adaptive_text( "Some of this ", span("text", rule=rule_fg(Color4.BRIGHT_RED)), " will be ", span("styled. ", rule=rule_italic), span( "Also, it is possible to ", span("nest",rule=rule_fg(Color4.BRIGHT_BLACK) | rule_bold), " spans!", rule=rule_bg(Color4.BLUE) ) ) | border print(layout_to_str(layout, Rect(20, 10))) Expected Output: .. raw:: html
    ┌──────────────────┐
    │Some of this text │
    │will be styled.   │
    │Also, it is       │
    │possible to nest  │
    │spans!            │
    │                  │
    │                  │
    │                  │
    └──────────────────┘