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.
See also
A list for all styling nodes can be found here: Nodes List.
3 Color Formats¶
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 Color4 enum.
Below follows a chart with all colors available with this enum.
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.
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
int anywhere a color is needed. Which integer corresponds to which color
can be viewed in the figure below.
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
True Color¶
Lastly, there is true color (24 bit or rgb color). This color format can be
accessed via rgb(), hsl() or
hex() functions that create a
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.
See also
Different renderers are discussed in I/O Overview.
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 StyleRule objects to represent styles
rules. These objects contains a foreground and background color as well as a
bunch of StyleAttr flags to represent style attributes
like bold or italic. Color can be represented either by an int (for 4
and 8 bit colors) or a Color24 (for 24 bit colors). To
use a style rule on a layout you can use push_rule.
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:
┌──────────────────┐
│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
bg_fill.
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:
┌──────────────────┐ │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.
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
fg and 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
bold and italic.
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:
┌────────────────────────────┐
│This text is red and bold │
└────────────────────────────┘
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 styled to wrap nodes you want to style.
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:
┌────────────────────────────┐ │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 rich_text node or
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 span and specify a
style_rule.
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:
┌──────────────────┐ │Some of this text │ │will be styled. │ │Also, it is │ │possible to nest │ │spans! │ │ │ │ │ │ │ └──────────────────┘