What draws the border you see while tabbing or arrowing?
Follow the event. A Tab or arrow key press goes to the inner Swing table that the Power Table wraps, not to the outer Vision component. The inner table moves its lead selection, updates the selection model, and repaints the cells whose state changed. For each repainted cell, the table's renderer runs, and the renderer calls the configureCell extension function. The thin highlight you see on the lead cell is the look-and-feel's default focus-cell border. Nothing in the Property Editor controls it.
Editing is a different path. When a cell enters edit mode, the cell editor component draws on top of the renderer. That is why you can style the border during editing but not during navigation: navigation never reaches the editor. To change the navigation highlight, work in the renderer path, which means configureCell.
| Stage | Component | What you can change there |
|---|---|---|
| Key press (Tab, arrows) | Inner table key bindings | Nothing in script; this stage moves the selection |
| Selection update | Inner table selection model | Selection properties (selectedRow, selectedColumn) read in propertyChange
|
| Repaint of affected cells | Renderer |
configureCell return dict: background, border, and other style keys |
| Edit mode | Cell editor | Editor styling, which does not apply during navigation |
Which two approaches produce a custom navigation border?
Approach A: key off the selected argument. configureCell receives a selected boolean for every cell it renders. That flag follows arrow and Tab movement, because each key press changes the selection and triggers a repaint of the old and new cells. Return a border entry when selected is true, and the thick border moves with the cursor.
Approach B: anchor the border to the clicked cell with custom properties. If the border must stay on the cell the operator originally clicked while the arrow keys move the selection elsewhere, selected is the wrong signal. It stops being true for the clicked cell as soon as the selection moves. Store the clicked row and column in custom properties on the Power Table. Write them from the onMouseClick extension function, and compare against them in configureCell.
| Criterion | A: selected argument |
B: clicked-cell custom properties |
|---|---|---|
| Border location | Current selected cell; follows Tab and arrows | Cell last clicked; stays put while arrowing |
| Scripts touched |
configureCell only |
configureCell, onMouseClick, propertyChange handler |
| Custom properties | None | Two integers, e.g. clickedRow, clickedColumn
|
| Manual repaint required | No; selection change repaints automatically | Yes; self.repaint() after writing the properties |
| Clearing logic | Automatic when selection clears | Explicit reset to -1 when selection goes to -1
|
| Depends on selection mode | Yes. Row-only selection flags every cell in the row | No. Compares row and column indices directly |
Which approach should you use?
Use Approach A when the goal is a more visible cursor, meaning a thicker or colored version of the default focus highlight that tracks the keyboard. It needs one extension function and no state, and the table's own repaint logic keeps it correct.
Use Approach B only when you need an anchor, such as marking the cell an operator clicked as the reference point while they navigate to compare values. It adds state, and state needs explicit repaint and explicit clearing. Both approaches can run together. Approach B's final configureCell already combines the anchor border with the selected-cell background.
How do I make the border follow the arrow keys?
- Check the selection behavior first. If
selectedis true for every cell in a row, row selection is on without column selection, and the border will outline the entire row. Enable cell-level selection in the table's selection properties if you want a single-cell outline. - Open the Power Table's Component Scripting, go to Extension Functions, and enable
configureCell. - Replace the body with the logic below. Keep the generated function signature; only the arguments
selectedandrowVieware used here. - Save, switch the Designer to Preview Mode, click a cell, and press the arrow keys and Tab.
#def configureCell(self, [...], colView):
if selected:
from javax.swing import BorderFactory
borderColor = system.gui.color('red')
border = BorderFactory.createMatteBorder(4, 4, 4, 4, borderColor)
return {'background': self.selectionBackground, 'border': border}
elif rowView % 2 == 0:
return {'background': 'white'}
else:
return {'background': 'lightgrey'}
Notes on the mechanism:
-
BorderFactory.createMatteBorder(top, left, bottom, right, color)takes per-side thickness in pixels.4, 4, 4, 4gives a uniform 4 px frame. A matte border draws inside the cell bounds, so a thick border reduces the space left for text. Drop to 2 px if values start clipping in narrow columns. - Returning
self.selectionBackgroundkeeps the table's configured selection color. If you return a dict without a background for a selected cell, the cell can lose its selection shading. - The striping uses
rowView, the view index, so alternating colors stay alternating after the operator sorts the table. - Importing
BorderFactoryinside the branch runs on every render of a selected cell. That cost is negligible, but you can move the import and border construction to module level in a project script if the table is large.
How do I keep the border on the clicked cell while arrowing?
- Add two custom properties to the Power Table:
clickedRowandclickedColumn, both Integer. Set both to-1so no cell matches before the first click. - Enable the
onMouseClickextension function, not themouseClickedevent handler. The inner table consumes the click. The component-level mouse event handler sits on the outer wrapper and does not fire for clicks inside the table's cells. The extension function receives the clicked cell's indices directly.
#def onMouseClick(self, [...], event):
self.clickedRow = rowIndex
self.clickedColumn = colIndex
self.repaint() # forces configureCell to re-run for the whole table
- Keep the
self.repaint()call. Writing a custom property does not invalidate any cell, so without it the old anchor keeps its border and the new one does not get one until something else triggers a repaint. - Clear the anchor when the selection clears. In the Power Table's
propertyChangeevent handler:
if event.propertyName in ['selectedRow', 'selectedColumn'] and event.newValue == -1:
event.source.clickedRow = -1
event.source.clickedColumn = -1
event.source.repaint() # trigger configureCell
- Update
configureCellto compare each cell against the stored anchor before checkingselected:
#def configureCell(self, [...], colView):
if rowIndex == self.clickedRow and colIndex == self.clickedColumn:
from javax.swing import BorderFactory
borderColor = system.gui.color('red')
border = BorderFactory.createMatteBorder(4, 4, 4, 4, borderColor)
if selected:
return {'background': self.selectionBackground, 'border': border}
else:
return {'border': border}
elif selected:
return {'background': self.selectionBackground}
elif rowView % 2 == 0:
return {'background': 'white'}
else:
return {'background': 'lightgrey'}
Index discipline matters here. onMouseClick stores rowIndex and colIndex, and configureCell compares against rowIndex and colIndex, so both sides use the same index space. After a sort, the anchor stays on the same data row, even though that row now appears at a different screen position. If you mix rowIndex on one side with rowView on the other, the border lands on the wrong cell as soon as the operator sorts or reorders columns.
Why is the border missing, stuck, or on the wrong cells?
| Symptom | Cause | Correction |
|---|---|---|
| Custom border appears only while editing a cell | Styling applied to the cell editor, which only exists in edit mode | Return a border from configureCell keyed on selected
|
| Border outlines the whole row when arrowing | Row-only selection; selected is true for every cell in the row |
Enable cell-level selection, or use Approach B's row and column comparison |
| Click does nothing; custom properties never change | Logic placed in the mouseClicked event handler, which does not see clicks on the inner table |
Move it to the onMouseClick extension function |
| Anchor border lags one click behind or stays on the old cell | No repaint after writing clickedRow/clickedColumn
|
Call self.repaint() in onMouseClick
|
| Anchor border persists after selection is cleared | No reset logic | Reset both properties to -1 in propertyChange and repaint |
| Anchor jumps to a different row after sorting | Model index stored, view index compared (or the reverse) | Use rowIndex/colIndex on both sides |
| Selected cell loses its highlight color | Returned dict omits background for selected cells |
Include 'background': self.selectionBackground
|
| Cell text clipped inside the border | Matte border thickness consumes cell insets | Reduce thickness or widen the column |
How do I verify the border behavior?
- In Preview Mode (or a launched client), click any cell. For Approach A, that cell shows the red 4 px border and the selection background. For Approach B, it shows the same border and background.
- Press the right arrow, down arrow, and Tab several times. For Approach A, the border moves with each key press and the previous cell returns to its stripe color with no border. For Approach B, the clicked cell keeps its red border without the selection background, and the currently selected cell shows the selection background.
- Click a different cell (Approach B). The border moves to the new cell immediately. If it moves only after a later key press, the
repaint()call is missing. - Sort by a column header. The striping stays alternating because it uses
rowView. The Approach B anchor follows its data row because it usesrowIndex. - Clear the selection, for example by writing
-1toselectedRowfrom a test button. With Approach B, confirm thatclickedRowandclickedColumnread-1in the Property Editor and that no cell keeps the red border.
FAQ
How do I change the Ignition Power Table focus border color when using arrow keys?
Enable the configureCell extension function. When selected is true, return {'background': self.selectionBackground, 'border': border}, where border comes from BorderFactory.createMatteBorder(4, 4, 4, 4, system.gui.color('red')). The selected flag follows Tab and arrow navigation.
How do I keep a highlight on the clicked Power Table cell while arrowing to other cells?
Add Integer custom properties clickedRow and clickedColumn (default -1). Set them from the onMouseClick extension function, then call self.repaint(). In configureCell, return the border when rowIndex and colIndex match the stored values.
Why doesn't the Power Table mouseClicked event handler fire on cell clicks?
The click is consumed by the inner table, and the component-level event handler sits on the outer wrapper. Use the onMouseClick extension function, which receives the clicked row and column indices.
How do I force configureCell to re-run after changing a custom property?
Call repaint() on the Power Table: self.repaint() inside extension functions, or event.source.repaint() in event handlers. Changing a custom property does not invalidate any cell by itself.
How do I stop the selected-cell border from outlining the entire row?
The table is in row-only selection, so selected is true for every cell in the row. Enable cell-level selection in the table's selection properties, or compare explicit row and column indices stored in custom properties instead of relying on selected.