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.
The
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
system/view/focal-face]]
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]
]
]
event
]
Then you install the completed callback function in the event chain using
insert-event-func.
insert-event-func :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]
] 1
; 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]
]
The
move 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 or
down. Normally you would have to use a
lit-word! when making the call, for example:
move 'up
move 'down
That's not terrible, but it's not quite as nice as this.
move up
move down
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).