From f826c0e3cc62b6faeb73d9fc462be084559f736f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Thu, 17 Dec 2020 16:56:08 +0100 Subject: [PATCH] [ivette/eva] zoom cell --- ivette/src/frama-c/eva/Values.tsx | 60 +++++++++++++++++++++++++------ ivette/src/frama-c/eva/layout.ts | 13 ++++--- ivette/src/frama-c/eva/probes.ts | 9 +++++ ivette/src/frama-c/eva/style.css | 1 + 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/ivette/src/frama-c/eva/Values.tsx b/ivette/src/frama-c/eva/Values.tsx index 9bcc4f76602..510d9ee8986 100644 --- a/ivette/src/frama-c/eva/Values.tsx +++ b/ivette/src/frama-c/eva/Values.tsx @@ -50,29 +50,39 @@ function ProbeEditor() { const label = probe?.label; const code = probe?.code; const rank = probe?.rank; - const byCS = probe?.byCallstacks; const stmt = rank ? `@S${rank}` : undefined; + const byCS = probe?.byCallstacks; const stacks = model.getStacks(probe).length; + const stackable = byCS || stacks > 1; const { cols, rows } = sizeof(code); const width = WSIZER.dimension(cols) + 4; const height = HSIZER.dimension(rows) + 3; const visible = probe ? !!code : model.getRowCount() > 0; + const zoomed = probe?.zoomed; + const zoomable = probe?.zoomable; const visibility = visible ? 'visible' : 'hidden'; return ( <Hpack style={{ visibility }} className="eva-probe"> <Label className="eva-probe-label">{label && `${label}:`}</Label> - <div style={{ width, height }} className="eva-probe-code"> + <div style={{ minWidth: width, height }} className="eva-probe-code"> <SizedArea cols={cols} rows={rows}>{code}</SizedArea> </div> <Code className="eva-probe-stmt">{stmt}</Code> <IconButton className="eva-probe-button" - visible={byCS || stacks > 0} + display={stackable} selected={byCS} icon="ITEMS.LIST" title={`Details by callstack (${stacks})`} onClick={() => { if (probe) probe.setByCallstacks(!byCS); }} /> + <IconButton + className="eva-probe-button" + display={zoomable} + selected={zoomed} + icon="SEARCH" + onClick={() => { if (probe) probe.setZoomed(!zoomed); }} + /> <IconButton className="eva-probe-button" kind={transient ? 'selected' : 'warning'} @@ -94,7 +104,7 @@ interface TableCellProps { row: Row; } -const CELLPADDING = 4; +const CELLPADDING = 12; function TableCell(props: TableCellProps) { const model = useModel(); @@ -118,8 +128,10 @@ function TableCell(props: TableCellProps) { const atpoint = rank && ( <span className="eva-probe-stmt">@S{rank}</span> ); + const text = + label ? <span className="dome-text-label">{label}</span> : code; contents = ( - <span className="dome-text-label">{label ?? code}{atpoint}</span> + <span className="dome-text-code">{text}{atpoint}</span> ); } break; @@ -158,11 +170,15 @@ function TableCell(props: TableCellProps) { setSelection({ location }); } }; + const onDoubleClick = () => { + if (probe && probe.zoomable) probe.setZoomed(!probe.zoomed); + }; return ( <div className={className} style={style} onClick={onClick} + onDoubleClick={onDoubleClick} > {contents} </div> @@ -190,6 +206,7 @@ function TableRow(props: TableRowProps) { {sk === undefined ? '#' : `${1 + sk}`} </div> ); + const style: React.CSSProperties = { left: row.stacks ? 0 : 12 }; const contents = probes.map((probe) => ( <TableCell key={probe.marker} @@ -199,7 +216,7 @@ function TableRow(props: TableRowProps) { )); return ( <Hpack className={className} style={props.style}> - <div className="eva-row"> + <div style={style} className="eva-row"> {header} {contents} </div> @@ -217,9 +234,13 @@ interface Dimension { height: number; } -function ValuesPanel(props: Dimension) { +interface ValuesPanelProps extends Dimension { + zoom: number; +} + +function ValuesPanel(props: ValuesPanelProps) { const model = useModel(); - const { width, height } = props; + const { zoom, width, height } = props; // --- reset line cache const listRef = React.useRef<VariableSizeList>(null); Dome.useEvent(model.laidout, () => { @@ -239,7 +260,7 @@ function ValuesPanel(props: Dimension) { const [selection] = States.useSelection(); React.useEffect(() => { const target = Ast.jMarker(selection?.current?.marker); - model.setLayout({ margin, target }); + model.setLayout({ zoom, margin, target }); }); // --- render list return ( @@ -263,14 +284,31 @@ function ValuesPanel(props: Dimension) { // -------------------------------------------------------------------------- function ValuesComponent() { + const [zoom, setZoom] = Dome.useNumberSettings('eva-zoom-factor', 0); return ( <> - <TitleBar /> + <TitleBar> + <IconButton + enabled={zoom > 0} + icon="ZOOM.OUT" + onClick={() => setZoom(zoom - 1)} + /> + <IconButton + enabled={zoom < 20} + icon="ZOOM.IN" + onClick={() => setZoom(zoom + 1)} + /> + </TitleBar> <Vfill> <ProbeEditor /> <Vfill> <AutoSizer> - {(dim: Dimension) => <ValuesPanel {...dim} />} + {(dim: Dimension) => ( + <ValuesPanel + zoom={zoom} + {...dim} + /> + )} </AutoSizer> </Vfill> </Vfill> diff --git a/ivette/src/frama-c/eva/layout.ts b/ivette/src/frama-c/eva/layout.ts index 92b6a71667e..d31e9c7f53a 100644 --- a/ivette/src/frama-c/eva/layout.ts +++ b/ivette/src/frama-c/eva/layout.ts @@ -5,7 +5,7 @@ import { callstack } from 'frama-c/api/plugins/eva/values'; import { Probe } from './probes'; import { StacksCache } from './stacks'; -import { Size, EMPTY, addH, ValueCache } from './cells'; +import { Size, EMPTY, leq, addH, ValueCache } from './cells'; export interface LayoutProps { zoom?: number; @@ -63,16 +63,19 @@ export class LayoutEngine { private buffer: Probe[] = []; private rows: Row[] = []; - crop(s: Size): Size { + crop(zoomed: boolean, s: Size): Size { + const cols = zoomed ? s.cols : Math.min(s.cols, this.hcrop); + const rows = zoomed ? s.rows : Math.min(s.rows, this.vcrop); return { - cols: Math.max(HCROP, Math.min(s.cols, this.hcrop)), - rows: Math.max(VCROP, Math.min(s.rows, this.vcrop)), + cols: Math.max(HCROP, cols), + rows: Math.max(VCROP, rows), }; } push(p: Probe) { const probeSize = this.values.getProbeSize(p.marker); - const s = this.crop(probeSize); + const s = this.crop(p.zoomed, probeSize); + p.zoomable = p.zoomed || !leq(probeSize, s); p.minCols = s.cols; p.maxCols = Math.max(p.minCols, probeSize.cols); const stmt = p.byCallstacks ? p.stmt : undefined; diff --git a/ivette/src/frama-c/eva/probes.ts b/ivette/src/frama-c/eva/probes.ts index 75f326d4bb5..bd8a561d090 100644 --- a/ivette/src/frama-c/eva/probes.ts +++ b/ivette/src/frama-c/eva/probes.ts @@ -54,6 +54,8 @@ export class Probe { minCols: number = LabelSize; maxCols: number = LabelSize; byCallstacks = false; + zoomed = false; + zoomable = false; constructor(state: StateCallbacks, marker: Ast.marker) { this.marker = marker; @@ -106,6 +108,13 @@ export class Probe { } } + setZoomed(zoomed: boolean) { + if (zoomed !== this.zoomed) { + this.zoomed = zoomed; + this.state.forceLayout(); + } + } + // -------------------------------------------------------------------------- // --- Ordering // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/eva/style.css b/ivette/src/frama-c/eva/style.css index dea6379e551..738cfe2dc9a 100644 --- a/ivette/src/frama-c/eva/style.css +++ b/ivette/src/frama-c/eva/style.css @@ -57,6 +57,7 @@ .eva-row { display: flex; + position: relative; flex: 0 1 auto; height: 100%; border-bottom: thin solid black; -- GitLab