Skip to content

Commit

Permalink
Introduce group-level selection and navigation for table
Browse files Browse the repository at this point in the history
- Disable row and cell navigation and selection
- Introduce custom selection and navigation handling based on groups
- Select a group by hitting 'Enter'
- Edit a group by hitting 'Space' (only available for data groups)
- Support copying data from the groups using Ctrl+C and Ctrl+X
- Only allow single selection for now
  • Loading branch information
martin-fleck-at committed Jun 24, 2024
1 parent fa67c60 commit d345e2c
Show file tree
Hide file tree
Showing 10 changed files with 604 additions and 126 deletions.
67 changes: 56 additions & 11 deletions media/memory-table.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
white-space: nowrap;
}

.memory-inspector-table .column-data .byte-group.editable:hover {
border-bottom: 1px dotted var(--vscode-editorHoverWidget-border);
.p-datatable :focus-visible {
outline-style: dotted;
outline-offset: -1px;
}

/* == MoreMemorySelect == */
Expand Down Expand Up @@ -161,22 +162,60 @@
color: var(--vscode-debugTokenExpression-name);
}

/* == Data Edit == */
/* Cell Styles */

.byte-group {
font-family: var(--vscode-editor-font-family);
margin-right: 2px;
padding: 0 1px; /* we use this padding to balance out the 2px that are needed for the editing */
.p-datatable .p-datatable-tbody > tr > td[data-column="address"][role="cell"],
.p-datatable .p-datatable-tbody > tr > td[data-column="ascii"][role="cell"] {
padding: 0;
}

.p-datatable .p-datatable-tbody > tr > td[data-column="data"][role="cell"],
.p-datatable .p-datatable-tbody > tr > td[data-column="variables"][role="cell"] {
padding: 0 12px;
vertical-align: middle;
}

/* Group Styles */

[role='group']:hover {
border-bottom: 0px;
color: var(--vscode-list-activeSelectionForeground);
outline: 1px solid var(--vscode-list-focusOutline);
}

[role='group'][data-group-selected='true'] {
background: var(--vscode-list-activeSelectionBackground);
color: var(--vscode-list-activeSelectionForeground);
outline: 1px solid var(--vscode-list-focusOutline);
}

.byte-group:last-child {
[data-column="address"][role="group"],
[data-column="ascii"][role="group"] {
padding: 4px 12px;
display: flex;
outline-offset: -1px;
}

[data-column="data"][role="group"],
[data-column="variables"][role="group"] {
padding: 4px 1px;
line-height: 23.5px;
outline-offset: -1px;
}

/* Data Column */


/* == Data Edit == */

[data-column="data"][role="group"]:last-child {
margin-right: 0px;
}

.byte-group:has(> .data-edit) {
[data-column="data"][role="group"]:has(> .data-edit) {
outline: 1px solid var(--vscode-inputOption-activeBorder);
outline-offset: 1px;
padding: 0px; /* editing takes two more pixels cause the input field will cut off the characters otherwise. */
padding-left: 0px; /* editing takes two more pixels cause the input field will cut off the characters otherwise. */
padding-right: 0px; /* editing takes two more pixels cause the input field will cut off the characters otherwise. */
}

.data-edit {
Expand All @@ -193,5 +232,11 @@
.data-edit:enabled:focus {
outline: none;
border: none;
color: var(--vscode-list-activeSelectionForeground);
text-indent: 1px;
}

.p-datatable .p-datatable-tbody > tr > td.p-highlight:has(>.selected) {
background: transparent;
outline: none;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"@vscode/codicons": "^0.0.32",
"deepmerge": "^4.3.1",
"fast-deep-equal": "^3.1.3",
"formik": "^2.4.5",
"lodash": "^4.17.21",
Expand Down
21 changes: 14 additions & 7 deletions src/webview/columns/address-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
********************************************************************************/

import React, { ReactNode } from 'react';
import { Memory } from '../../common/memory';
import { BigIntMemoryRange, getAddressString, getRadixMarker } from '../../common/memory-range';
import { ColumnContribution, ColumnFittingType, TableRenderOptions } from './column-contribution-service';
import { getAddressString, getRadixMarker } from '../../common/memory-range';
import { MemoryRowData } from '../components/memory-table';
import { ColumnContribution, ColumnFittingType, ColumnRenderProps } from './column-contribution-service';
import { createDefaultSelection, groupAttributes, SelectionProps } from './table-group';

export class AddressColumn implements ColumnContribution {
static ID = 'address';
Expand All @@ -28,10 +29,16 @@ export class AddressColumn implements ColumnContribution {

fittingType: ColumnFittingType = 'content-width';

render(range: BigIntMemoryRange, _: Memory, options: TableRenderOptions): ReactNode {
return <span className='memory-start-address hoverable' data-column='address'>
{options.showRadixPrefix && <span className='radix-prefix'>{getRadixMarker(options.addressRadix)}</span>}
<span className='address'>{getAddressString(range.startAddress, options.addressRadix, options.effectiveAddressLength)}</span>
render(columnIndex: number, row: MemoryRowData, config: ColumnRenderProps): ReactNode {
const selectionProps: SelectionProps = {
createSelection: (event, position) => createDefaultSelection(event, position, AddressColumn.ID, row),
getSelection: () => config.selection,
setSelection: config.setSelection
};
const groupProps = groupAttributes({ columnIndex, rowIndex: row.rowIndex, groupIndex: 0, maxGroupIndex: 0 }, selectionProps);
return <span className='memory-start-address hoverable' data-column='address' {...groupProps}>
{config.tableConfig.showRadixPrefix && <span className='radix-prefix'>{getRadixMarker(config.tableConfig.addressRadix)}</span>}
<span className='address'>{getAddressString(row.startAddress, config.tableConfig.addressRadix, config.tableConfig.effectiveAddressLength)}</span>
</span>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ReactNode } from 'react';
import React, { ReactNode } from 'react';
import * as manifest from '../../common/manifest';
import { Memory } from '../../common/memory';
import { BigIntMemoryRange, toOffset } from '../../common/memory-range';
import { ColumnContribution, TableRenderOptions } from './column-contribution-service';
import { toOffset } from '../../common/memory-range';
import { MemoryRowData } from '../components/memory-table';
import { ColumnContribution, ColumnRenderProps } from './column-contribution-service';
import { createDefaultSelection, groupAttributes, SelectionProps } from './table-group';

function isPrintableAsAscii(input: number): boolean {
return input >= 32 && input < (128 - 1);
Expand All @@ -30,17 +31,25 @@ function getASCIIForSingleByte(byte: number | undefined): string {
}

export class AsciiColumn implements ColumnContribution {
readonly id = manifest.CONFIG_SHOW_ASCII_COLUMN;
static ID = manifest.CONFIG_SHOW_ASCII_COLUMN;
readonly id = AsciiColumn.ID;
readonly label = 'ASCII';
readonly priority = 3;
render(range: BigIntMemoryRange, memory: Memory, options: TableRenderOptions): ReactNode {
const mauSize = options.bytesPerMau * 8;
const startOffset = toOffset(memory.address, range.startAddress, mauSize);
const endOffset = toOffset(memory.address, range.endAddress, mauSize);

render(columnIndex: number, row: MemoryRowData, config: ColumnRenderProps): ReactNode {
const selectionProps: SelectionProps = {
createSelection: (event, position) => createDefaultSelection(event, position, AsciiColumn.ID, row),
getSelection: () => config.selection,
setSelection: config.setSelection
};
const groupProps = groupAttributes({ columnIndex, rowIndex: row.rowIndex, groupIndex: 0, maxGroupIndex: 0 }, selectionProps);
const mauSize = config.tableConfig.bytesPerMau * 8;
const startOffset = toOffset(config.memory.address, row.startAddress, mauSize);
const endOffset = toOffset(config.memory.address, row.endAddress, mauSize);
let result = '';
for (let i = startOffset; i < endOffset; i++) {
result += getASCIIForSingleByte(memory.bytes[i]);
result += getASCIIForSingleByte(config.memory.bytes[i]);
}
return result;
return <span data-column='ascii' className='ascii' {...groupProps}>{result}</span>;
}
}
14 changes: 12 additions & 2 deletions src/webview/columns/column-contribution-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,23 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ColumnPassThroughOptions } from 'primereact/column';
import type * as React from 'react';
import { Memory } from '../../common/memory';
import { BigIntMemoryRange } from '../../common/memory-range';
import { ReadMemoryArguments } from '../../common/messaging';
import { MemoryRowData, MemoryTableSelection, MemoryTableState } from '../components/memory-table';
import type { Disposable, MemoryState, SerializedTableRenderOptions, UpdateExecutor } from '../utils/view-types';

export type ColumnFittingType = 'content-width';

export interface ColumnRenderProps {
memory: Memory;
tableConfig: TableRenderOptions;
groupsPerRowToRender: number;
selection?: MemoryTableSelection;
setSelection: (selection?: MemoryTableSelection) => void;
}

export interface ColumnContribution {
readonly id: string;
readonly className?: string;
Expand All @@ -30,7 +39,8 @@ export interface ColumnContribution {
fittingType?: ColumnFittingType;
/** Sorted low to high. If omitted, sorted alphabetically by ID after all contributions with numbers. */
priority?: number;
render(range: BigIntMemoryRange, memory: Memory, options: TableRenderOptions): React.ReactNode
pt?(columnIndex: number, state: MemoryTableState): ColumnPassThroughOptions;
render(columnIdx: number, row: MemoryRowData, config: ColumnRenderProps): React.ReactNode
/** Called when fetching new memory or when activating the column. */
fetchData?(currentViewParameters: ReadMemoryArguments): Promise<void>;
/** Called when the user reveals the column */
Expand Down
Loading

0 comments on commit d345e2c

Please sign in to comment.