Adding Keyboard Navigation
Although the basic engine has mouse controls, and the tab key moves between cells, it's worth adding some extra keyboard navigation; the up and down arrow keys are very helpful if you expect to do much editing. To avoid adding code to every cell to handle keyboard events, you can use a nice feature in REBOL/View: insert-event-func
word lets you specify a callback function that the main parent calls whenever it "sees" an event. Using this feature, you can filter and act on events in one central location. First, define a callback function:
event-func: func [face event /local f] [
; The face we get passed is *not* the cell, it's
; the main layout.
; We use system/view/focal-face to get the cell
; being edited.
; The ALL function is a shortcut for ANDing
; clauses together, e.g.
; if (('key = event/type) and (in-cell?))
if all ['key = event/type in-cell?] [
switch event/key [
F2 [if in-cell? [show-formula
up [move up]
down [move down]
; left/right conflict with edit
' navigation, need a more
; complex system to track edit mode if we
' want to do this. Use Tab/Shift+Tab to
; move L/R in the meantime.
;left [move left]
;right [move right]
Then you install the completed callback function in the event chain using insert-event-func
Note that the code also handles an F2 key press, so you can edit the formula for a cell as you would in a traditional spreadsheet. The function that actually moves the focus is called move
. If you're new to REBOL, it may not be clear how move
works, so here's some commented code that should help explain it.
move: func ['way /local pos] [
; Find the location of the current cell in the
; list of cells
pos: find cells cur-cell
; "enter cur-cell" triggers the recalc; the tab
; key does that automatically, arrow keys don't
if find [up down] way
[enter cur-cell] ; left right
; Now we shift the position in the list of cells,
; relative to the current cell, based on the
; direction they're moving, and pick the cell at
; that location so we can move to it.
; Left and Right arrows conflict with in-cell
; editing, and need more code to support changing
; between navigation and edit modes.
cell: pick switch way [
up [skip pos negate sheet-size/x * 2]
down [skip pos sheet-size/x * 2]
;left [back pos]
;right [next pos]
; Keep from falling off the top of the list due to
; a negative skip.
if not object? cell [cell: none]
; Set the current cell
if cell [focus cell]
code illustrates one other advanced trickthe use of a lit-word!
(notice the tick-mark/apostrophe in the word way
in the first line of preceding code) as a parameter. That tells REBOL not
to evaluate the argument it gets. In this case way
is going to be a word!
value, something like up
. Normally you would have to use a lit-word!
when making the call, for example:
That's not terrible, but it's not quite as nice as this.
The goal here is to make the higher-level logic look less like "code," and is an important concept when you think in terms of REBOL dialects. The use of a lit-word!
as a parameter shows how much control REBOL gives you over when evaluation occurs. That's important because, in REBOL, there is no code, there is only data that gets evaluated
(it takes a while for that concept to sink in, especially if you're an experienced programmer used to other languages).