Lesson 15 editors

Colour and gradient

A colour is six hex digits and a gradient is a list of stops. :set editor color and :set editor gradient draw them beside the text and let the keys move through colour space, rewriting the digits in place.

The picker

bi already paints a hex literal in the colour it names. Picking a different one still meant typing digits, which is what this tool is for: put the cursor on #fb4934 and run :set editor color.

The curve’s arrangement comes up — source, picker split to its right, form down the edge — with focus on the picker.

┌──────────────────────────────────┐
│            ◯                     │   the square: saturation left to right,
│                                  │   value bottom to top, painted at the
└──────────────────────────────────┘   current hue; the ring is the colour
 ▌ red · yellow · green · cyan · blue · magenta · red ▐   the hue strip
 ▌ ░░░░▒▒▒▒▓▓▓▓████████████████████ ▐                    alpha, for eight digits
 ████████████████│███████████████████                    before │ now

The square is 512 pixels a side, the strips sit under it, and the swatch at the bottom shows the colour you opened on to the left of the bar and the colour now to the right, so a change is judged against what it replaces. The component the keys act on carries a bright border.

tuning.c In and out of the colour picker 1 / 5
In and out of the colour picker: step 0 In and out of the colour picker: step 1 In and out of the colour picker: step 2 In and out of the colour picker: step 3 In and out of the colour picker: step 4

The way in and the way out are the curve editor’s: :set editor color on the literal, Esc back to the text. What the square rewrites is the six digits under the cursor — there is no palette file, no swatch library and nothing to sync.

Keys

KeyDoes
Tab Shift-Tabthe next, previous component: square, hue, alpha
h lsquare: saturation down, up; on a strip: hue or alpha
j ksquare: value (or lightness) down, up
0 $the component to its start, its end — grey or full colour, red, clear or opaque
gg Gvalue to full, to none
mthe square’s vertical axis: value or lightness
yh yf yryank the colour as hex, floats, or bytes
Enterthe colour on the ex line: :tool color hex #fb4934
u Ctrl-Rundo, redo — the source buffer’s
Escclose the picker and go back where you came from

A step is a fiftieth of each range — 0.02 of saturation, 7.2° of hue, 5 of the 255 alpha steps — and counts multiply, so 5l is a tenth of the way across. :tool color step 0.1 makes them coarser.

The three yanks go into the register ring exactly as y does in text, so p pastes them into any buffer: #fb4934 for a stylesheet, 0.984, 0.286, 0.204 for a shader, 251, 73, 52 for a byte-oriented API.

tip
The square is HSV by default — saturation across, value up — because that is how a palette is usually described. m switches the vertical axis to lightness for an HSL palette. There is no RGB square (three axes do not fit in two) and no wheel: the hue strip is a wheel unrolled, and it takes h and l better.

A grey remembers what it was. Black has no saturation and grey no hue, and six digits cannot say what they were, so the picker keeps hue and saturation beside the colour: j down to black and k back up returns the same red.

As commands

CommandDoes
:tool color hex #fb4934the colour; alpha too when the literal has it
:tool color rgb 251 73 52bytes, with an optional alpha fourth
:tool color floats 0.98 0.29 0.2the same as floats
:tool color hsv 6 71 98degrees, percent, percent; hsl likewise
:tool color alpha 128a byte — refused on a six-digit literal
:tool color mode hsvor hsl
:tool color focus squarehue, alpha
:tool color yank hexfloats, rgb
:tool color step 0.02how far a key moves

The form carries R G B A, H S V (or L), Mode and Step, and every one of them turns — move R and the state follows, move H and the digits do. The status row reads #fb4934 h 6 s 71 v 98.

What counts as a colour

# and six or eight hex digits, with the cursor anywhere on them, on the #, or just after the last digit. Quotes around it belong to the language and are left alone. Three-digit CSS shorthand is not picked up, because writing six digits back would change the shape of the literal. Anywhere else, the answer is no colour under the cursor.

Writing back replaces the digits and nothing else: the case is kept, so #FB4934 stays upper, and a six-digit literal stays six — no alpha strip is drawn and no alpha is written.

The gradient bar

A gradient is a list of stops, each a position along 0..1 and a colour. :set editor gradient with the cursor in the list draws the ramp beside the text with a triangle under each stop, the selected one white.

[[0, "#000000ff"], [0.5, "#ff8800ff"], [1, "#ffffffff"]]

The ramp is linear in sRGB between neighbouring stops and flat outside them, which is what CSS and every engine’s default do. The literal is found the way the curve’s is — a bracket scan out to the innermost group whose children each hold one number and one colour — so no layout is needed: a number is a position and a colour is a colour.

tuning.c In and out of the gradient editor 1 / 6
In and out of the gradient editor: step 0 In and out of the gradient editor: step 1 In and out of the gradient editor: step 2 In and out of the gradient editor: step 3 In and out of the gradient editor: step 4 In and out of the gradient editor: step 5

The way in and the way out are the curve editor’s: :set editor gradient on the list, Esc back to the text with the edits already in the buffer. What you slide is the number in the file.

KeyDoes
Tab Shift-Tabnext, previous stop, wrapping; gg G first, last
h lthe stop earlier, later by one step (0.05)
0 $the stop to 0, to 1
aa stop halfway to the next one, in the ramp’s colour there
xdelete the stop — a gradient keeps two
copen the colour picker over this stop’s colour
yh yf yryank this stop’s colour
Enterthe stop on the ex line: :tool gradient t 0.5
Escclose the tool, back to the source

Stops sort themselves. t is clamped to 0..1 and nothing else: slide a stop past its neighbour and the two change places in the list, in the text, and the moved one stays selected. The list an engine reads is always sorted, and the editor never asks you to keep it so.

:tool gradient also takes stop, t, color, step, add [t], delete, pick and yank, and its form shows the stop and its t as readouts with the step underneath. The status row reads stop 2 of 3 t 0.500 #ff8800ff.

Both at once

c on a stop does not replace the bar with the picker — it joins them. The picker’s picture splits to the right of the bar, and its form goes under the gradient’s form, so the two pictures sit side by side and the two forms stack at the edge. Every step the picker takes redraws the ramp, because both are views of one buffer, and Esc or :q in the picker lands you back on the bar.

tip
Both editors also open from the property view: Enter on an rgb or rgba row opens the picker over the value, and Enter on a gradient row — drawn there as sixteen bricks and a stop count — opens the bar. An inherited value is written into the instance first, and dd puts the default back.

Closing them

:w and :q are the source’s, as for the curve: :w from any of the three windows writes the buffer, :q closes the tool and puts the cursor back. Closing any one window by hand closes the rest — a form with no picture, or a picture with no form, is half a tool.

The anchor is the byte of the #, or of the gradient’s open bracket. After every change the tool re-reads from there; when the literal is gone it looks again from the source’s cursor, or by its path when the property view opened it, and otherwise says colour lost or gradient lost and blanks the picture until you open it again.