diff --git a/ivette/src/dome/renderer/colors.tsx b/ivette/src/dome/renderer/colors.tsx new file mode 100644 index 0000000000000000000000000000000000000000..09046e30ff60b7c0fc49d0b48eeef64ec9f393e2 --- /dev/null +++ b/ivette/src/dome/renderer/colors.tsx @@ -0,0 +1,96 @@ +/* ************************************************************************ */ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2024 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/* ************************************************************************ */ + +/** + @packageDocumentation + @module dome/colors + */ + +import React from 'react'; +import { useStyle, useColorTheme } from 'dome/themes'; + +export enum EColor { + DEFAULT = "default", + WHITE = 'white', + GREY = 'grey', + DARK = 'dark', + PRIMARY = 'primary', + SELECTED = 'selected', + GREEN = 'green', + ORANGE = 'orange', + RED = 'red', + YELLOW = 'yellow', + BLUE = 'blue', + PINK = 'pink' +} + +export type TColor = `${EColor}` + +type TColorVal = { + [key in EColor]: string +} + +export interface IHookColors { + BGCOLOR: TColorVal; + SGCOLOR: TColorVal; + FGCOLOR: TColorVal; + EDCOLOR: TColorVal; +} + +type TColorCategory = 'bg'|'fg'|'sg'|'ed'; + +export function useColor(): IHookColors { + const style = useStyle(); + const [theme, ] = useColorTheme(); + + const enum2Tcolor = (callback: (elt: EColor) => string): TColorVal => { + return { + ...(Object.fromEntries( + Object.values(EColor).map((val: EColor) => [ val, callback(val)]) + )), + } as TColorVal; + }; + + function getColorType(type: TColorCategory): TColorVal { + return enum2Tcolor( + (elt) => style.getPropertyValue('--graph-'+type+'-color-'+elt) + ); + } + + const colors = React.useMemo(() => { + return { + // node background colors + BGCOLOR: getColorType('bg'), + // // cluster background colors + SGCOLOR: getColorType('sg'), + // foreground colors + FGCOLOR: getColorType('fg'), + // // edge colors + EDCOLOR: getColorType('ed'), + }; + }, + /** style is dependent on theme but it is not used directly */ + // eslint-disable-next-line react-hooks/exhaustive-deps + [theme] + ); + return colors; +} diff --git a/ivette/src/dome/renderer/dark.css b/ivette/src/dome/renderer/dark.css index 31e009d27d3c4418244c47c03fa1ebfd8a0f24e0..8282b2569fecae7b711f543a3d42a24f840de2e0 100644 --- a/ivette/src/dome/renderer/dark.css +++ b/ivette/src/dome/renderer/dark.css @@ -124,5 +124,59 @@ --status-valid-under-hyp: linear-gradient(to right, var(--status-valid) 49%, var(--status-unknown) 51%); --status-considered-valid: linear-gradient(to right, var(--status-valid) 49%, #73BBBB 51%); --status-invalid-under-hyp: linear-gradient(to right, var(--status-invalid) 49%, var(--status-unknown) 51%); + + /* node background colors */ + --graph-bg-color-default: #4c596b; + --graph-bg-color-white: #fff; + --graph-bg-color-grey: #ccc; + --graph-bg-color-dark: #666; + --graph-bg-color-primary: dodgerblue; + --graph-bg-color-selected: deepskyblue; + --graph-bg-color-green: lime; + --graph-bg-color-orange: #ffa700; + --graph-bg-color-red: red; + --graph-bg-color-yellow: yellow; + --graph-bg-color-blue: cyan; + --graph-bg-color-pink: hotpink; + /* cluster background colors */ + --graph-sg-color-default: #4c596b; + --graph-sg-color-white: #ccc; + --graph-sg-color-grey: #bbb; + --graph-sg-color-dark: #aaa; + --graph-sg-color-primary: #4fc3f7; + --graph-sg-color-selected: #4c596b; + --graph-sg-color-green: #AED581; + --graph-sg-color-orange: #FFCC80; + --graph-sg-color-red: #ff6e6e; + --graph-sg-color-yellow: #d4cc82; + --graph-sg-color-blue: #bbdefb; + --graph-sg-color-pink: #f8bbd0; + /** foreground colors */ + --graph-fg-color-default: white; + --graph-fg-color-white: black; + --graph-fg-color-grey: black; + --graph-fg-color-dark: white; + --graph-fg-color-primary: white; + --graph-fg-color-selected: black; + --graph-fg-color-green: black; + --graph-fg-color-orange: black; + --graph-fg-color-red: white; + --graph-fg-color-yellow: black; + --graph-fg-color-blue: black; + --graph-fg-color-pink: white; + /** edge colors */ + --graph-ed-color-default: white; + --graph-ed-color-white: #fff; + --graph-ed-color-grey: #ddd; + --graph-ed-color-dark: black; + --graph-ed-color-primary: dodgerblue; + --graph-ed-color-selected: deepskyblue; + --graph-ed-color-green: green; + --graph-ed-color-orange: orange; + --graph-ed-color-red: red; + --graph-ed-color-yellow: #e5e100; + --graph-ed-color-blue: deepskyblue; + --graph-ed-color-pink: palevioletred1; + } } diff --git a/ivette/src/dome/renderer/graph/diagram.tsx b/ivette/src/dome/renderer/graph/diagram.tsx index 291f0a79f4f7bedce638fdcdd7fc7c85ae838d08..d594f647ca21f1aa19bdea0b29be528c7f48262b 100644 --- a/ivette/src/dome/renderer/graph/diagram.tsx +++ b/ivette/src/dome/renderer/graph/diagram.tsx @@ -23,6 +23,7 @@ import React from 'react'; import { Catch } from 'dome/errors'; import { classes } from 'dome/misc/utils'; +import { useColor, IHookColors, TColor, EColor } from '../colors'; import { Size } from 'react-virtualized'; import { select, selectAll } from 'd3-selection'; import { graphviz } from 'd3-graphviz'; @@ -37,12 +38,6 @@ export type Direction = 'LR' | 'TD'; export type Font = 'serif' | 'sans' | 'mono'; -export type Color = - | 'white' | 'grey' | 'dark' - | 'primary' | 'selected' - | 'green' | 'orange' | 'red' - | 'yellow' | 'blue' | 'pink'; - export type Shape = | 'point' | 'box' | 'diamond' | 'hexagon' @@ -72,7 +67,7 @@ export interface Node { /** Node font (label) */ font?: Font; /** Node color (filled background) */ - color?: Color; + color?: TColor; /** * Shape. Nested boxes alternate LR and TD directions. Initial direction is * orthogonal to the graph direction. Node label is ignored for box layout. @@ -96,7 +91,7 @@ export interface Edge { /** Default is `solid` */ line?: Line; /** Default is `dark` */ - color?: Color; + color?: TColor; head?: Arrow; headLabel?: string, headAnchor?: Anchor; @@ -120,7 +115,7 @@ export interface Cluster { /** Title (default is none) */ title?: string; /** Background color (default is grey) */ - color?: Color; + color?: TColor; } /* -------------------------------------------------------------------------- */ @@ -152,7 +147,6 @@ export interface DiagramProps { /** Debug the generated DotModel */ onModelChanged?: (model: string) => void; - } /* -------------------------------------------------------------------------- */ @@ -165,66 +159,6 @@ const FONTNAME = { 'mono': 'Courier', }; -// node background colors -const BGCOLOR = { - 'white': '#fff', - 'grey': '#ccc', - 'dark': '#666', - 'primary': 'dodgerblue', - 'selected': 'deepskyblue', - 'green': 'lime', - 'orange': '#ffa700', - 'red': 'red', - 'yellow': 'yellow', - 'blue': 'cyan', - 'pink': 'hotpink', -}; - -// cluster background colors -const SGCOLOR = { - 'white': '#eee', - 'grey': '#ccc', - 'dark': '#aaa', - 'primary': '#4fc3f7', - 'selected': '#90caf9', - 'green': '#AED581', - 'orange': '#FFCC80', - 'red': '#ff6e6e', - 'yellow': '#fff59d', - 'blue': '#bbdefb', - 'pink': '#f8bbd0', -}; - -// foreground colors -const FGCOLOR = { - 'white': 'black', - 'grey': 'black', - 'dark': 'white', - 'primary': 'white', - 'selected': 'black', - 'green': 'black', - 'orange': 'black', - 'red': 'white', - 'yellow': 'black', - 'blue': 'black', - 'pink': 'white', -}; - -// edge colors -const EDCOLOR = { - 'white': '#ccc', - 'grey': '#888', - 'dark': 'black', - 'primary': 'dodgerblue', - 'selected': 'deepskyblue', - 'green': 'green', - 'orange': 'orange', - 'red': 'red', - 'yellow': '#e5e100', - 'blue': 'deepskyblue', - 'pink': 'palevioletred1', -}; - /* -------------------------------------------------------------------------- */ /* --- Edge Model --- */ /* -------------------------------------------------------------------------- */ @@ -242,6 +176,7 @@ type cluster = { props: Cluster; nodes: Node[]; } class Builder { + private colors: IHookColors; private selected: string | undefined; private spec = ''; @@ -250,6 +185,10 @@ class Builder { private rmap = new Map<string, string>(); private cmap = new Map<string, cluster>(); + constructor(colors: IHookColors) { + this.colors = colors; + } + index(id: string): string { const n = this.imap.get(id); if (n !== undefined) return n; @@ -372,23 +311,24 @@ class Builder { .attr('shape', n.shape) .attr('tooltip', n.title ?? n.label ?? n.id); } - const color = n.color ?? (n.id === this.selected ? 'selected' : 'white'); + const color = n.color ?? + ( n.id === this.selected ? EColor.SELECTED : EColor.DEFAULT ); this - .attr('fontcolor', FGCOLOR[color]) - .attr('fillcolor', BGCOLOR[color]) + .attr('fontcolor', this.colors.FGCOLOR[color]) + .attr('fillcolor', this.colors.BGCOLOR[color]) .println(' ];'); } cluster(c: cluster): void { const { props: s, nodes } = c; - const { color = 'grey' } = s; + const { color = EColor.GREY } = s; this .print(' subgraph cluster_', this.index(s.id), ' {\n ') .attr('style', 'filled') .attr('label', s.label) .attr('tooltip', s.title ?? s.id) - .attr('fontcolor', FGCOLOR[color]) - .attr('fillcolor', SGCOLOR[color]) + .attr('fontcolor', this.colors.FGCOLOR[color]) + .attr('fillcolor', this.colors.SGCOLOR[color]) .print('\n '); nodes.forEach(n => this.print(' ', this.index(n.id), ';')); this.println('\n }'); @@ -433,7 +373,12 @@ class Builder { .attr('tailtooltip', e.tailLabel ? tooltip : undefined) .attr('tooltip', tooltip) .attr('dir', DIR(head, tail)) - .attr('color', e.color ? EDCOLOR[e.color] : undefined) + .attr('color', e.color ? + this.colors.EDCOLOR[e.color] : + this.colors.EDCOLOR[EColor.DEFAULT]) + .attr('fontcolor', e.color ? + this.colors.EDCOLOR[e.color] : + this.colors.EDCOLOR[EColor.DEFAULT]) .attr('style', line === 'solid' ? undefined : line) .attr('arrowhead', head === 'arrow' ? undefined : head) .attr('arrowtail', tail === 'arrow' ? undefined : tail) @@ -457,9 +402,11 @@ const newDivId = (): string => `dome_xDiagram_g${++divId}`; interface GraphvizProps extends DiagramProps { size: Size } function GraphvizView(props: GraphvizProps): JSX.Element { + // Colors + const colors = useColor(); // --- Builder Instance (unique) - const builder = React.useMemo(() => new Builder, []); + const builder = React.useMemo(() => new Builder(colors), [colors]); // --- Model Generation const { diff --git a/ivette/src/dome/renderer/graph/graph.tsx b/ivette/src/dome/renderer/graph/graph.tsx index 535d0dd9bb118e6f0e3d3b272a70ff37dcdb31ef..751e815ca44c8c9671492bbd3c03a4988cae1a30 100644 --- a/ivette/src/dome/renderer/graph/graph.tsx +++ b/ivette/src/dome/renderer/graph/graph.tsx @@ -21,6 +21,7 @@ /* ************************************************************************ */ import React from 'react'; +import { createRoot } from 'react-dom/client'; import ForceGraph2D, { ForceGraphMethods as ForceGraphMethods2D, @@ -32,17 +33,25 @@ import ForceGraph3D, { ForceGraphMethods as ForceGraphMethods3D, LinkObject as LinkObject3D, NodeObject as NodeObject3D, + ForceGraphProps as ForceGraphProps3D, } from 'react-force-graph-3d'; +/** Three is a dependency of 3d-force-graph */ +import { + CSS2DRenderer, CSS2DObject +} from 'three/examples/jsm/renderers/CSS2DRenderer'; + import { Size } from 'react-virtualized'; import AutoSizer from 'react-virtualized-auto-sizer'; -// ForceGraphMethods as ForceGraphMethods3D, +import * as Dome from 'dome'; +import * as Themes from 'dome/themes'; /* -------------------------------------------------------------------------- */ /* --- Graph Specifications --- */ /* -------------------------------------------------------------------------- */ +/** Type layout */ export type Layout = '2D' | '3D'; export interface Node { @@ -57,6 +66,131 @@ export interface Edge { target: string /** Target node identifier */; } +/* -------------------------------------------------------------------------- */ +/* --- Force Graph Components --- */ +/* -------------------------------------------------------------------------- */ + +interface GNode { + id: string; + label?: string; +} +interface GLink { + source: string; + target: string; +} +interface GData { + nodes: GNode[]; + links: GLink[]; +} + +interface IGProps { + data: GData; + onSelection?: SelectionCallback; + selected: string | undefined; + size: Size; +} + +interface IGProps2D extends IGProps { + options?: IGraphOptions; +} + +interface IGProps3D extends IGProps { + options?: IGraphOptions3D; +} + +/* -------------------------------------------------------------------------- */ +/* --- Graph options --- */ +/* -------------------------------------------------------------------------- */ +/** Nodes options */ +export interface INodesOptions { + /** visibility of the nodes */ + visibility?: boolean | ((node: GNode) => boolean); +} + +/** Links options */ +export interface ILinksOptions { + /** Width of the links */ + width?: number | ((node: GLink) => number); + /** Color of the links */ + color?: string | ((node: GLink) => string); + /** visibility of the links */ + visibility?: boolean | ((node: GLink) => boolean); + /** Size of the arrows */ + directionalArrow?: number; + /** Number of directional particles */ + directionalParticle?: number; + /** Width of directional particles */ + particleWidth?: number; + /** Color of directional particles */ + particleColor?: string | ((node: GLink) => string) +} + +/** Common options for Graph2D and Graph3D */ +export interface IGraphOptions { + /** Background color */ + backgroundColor?: string; + /** Moves the camera to see all of the nodes if active */ + autoCenter?: boolean; + /** + * If displayMode = td, the graph will be display like a tree from top to down + * Work only if the graph has no cycle + */ + displayMode?: 'td'; + /** Spacing between depths level */ + depthSpacing?: number; + /** A string[][] ref to save the cycles */ + cycles?: React.MutableRefObject<string[][]> + /** Nodes options */ + nodesOptions?: INodesOptions; + /** Links options */ + linkOptions?: ILinksOptions; +} + +/** Specific options for graph3D */ +export interface IGraphOptions3D extends IGraphOptions { + /** function to create the HTML for 2D object */ + htmlNode?: (node: NodeObject3D<GNode>) => JSX.Element; + /** Node repulsion */ + horizontalSpacing?: number; +} + +function getOnEngineStop( + fgRef: React.MutableRefObject<ForceGraphMethods3D<NodeObject3D<GNode>, + LinkObject3D<GNode, GLink>> | undefined>, + options: { + autoCenter?: boolean + } +):() => void { + return () => { + if (options.autoCenter && fgRef.current) { + fgRef.current.zoomToFit(400, 20); + } + }; +} + +function getOnDagError( + cycles: React.MutableRefObject<string[][]> +):(val: (string | number)[]) => void { + return (val: (string | number)[]) => cycles.current.push(val as string[]); +} + +/** Tranform JSX.Element to HtmlObject */ +const jsxToHtmlObject = (jsxElement: JSX.Element): HTMLDivElement => { + const container = document.createElement('div'); + createRoot(container).render(jsxElement); + return container; +}; + +/** Return a function who make an object2D */ +function getObject2DFunction( + getNode: (node: NodeObject3D<GNode>) => JSX.Element +) { + return (node: NodeObject3D<GNode>) => { + const nodeEl = jsxToHtmlObject(getNode(node)); + return new CSS2DObject(nodeEl || document.createElement('div')); + }; +} + /* -------------------------------------------------------------------------- */ /* --- Graph Component Properties --- */ /* -------------------------------------------------------------------------- */ @@ -88,37 +222,19 @@ export interface GraphProps { /** Styling the Graph main div element. */ className?: string; -} - -/* -------------------------------------------------------------------------- */ -/* --- Force Graph Components --- */ -/* -------------------------------------------------------------------------- */ -interface GNode { - id: string; - label?: string; -} -interface GLink { - source: string; - target: string; -} -interface GData { - nodes: GNode[]; - links: GLink[]; -} + /** Options graph 2D */ + options2D?: IGraphOptions; -interface GProps { - data: GData; - onSelection?: SelectionCallback; - selected: string | undefined; - size: Size; + /** Options graph 3D */ + options3D?: IGraphOptions3D; } /* -------------------------------------------------------------------------- */ /* --- 2D Force Graph Component --- */ /* -------------------------------------------------------------------------- */ -function Graph2D(props: GProps): JSX.Element { +function Graph2D(props: IGProps2D): JSX.Element { const { data, onSelection, selected, size } = props; const { width, height } = size; @@ -127,6 +243,8 @@ function Graph2D(props: GProps): JSX.Element { | undefined >(undefined); + const style = Themes.useStyle(); + React.useEffect(() => { if (fgRef2D.current && selected) { const selectedNode: NodeObject2D | undefined = data.nodes.find( @@ -160,7 +278,10 @@ function Graph2D(props: GProps): JSX.Element { node.fy = node.y; }} cooldownTime={50} - nodeColor={(node) => (node.id === selected ? '#F4D03F' : '#5DADE2')} + nodeColor={(node) => (node.id === selected ? + style.getPropertyValue('--graph-bg-color-selected') : + style.getPropertyValue('--graph-bg-color-orange')) + } /> ); } @@ -168,9 +289,56 @@ function Graph2D(props: GProps): JSX.Element { /* -------------------------------------------------------------------------- */ /* --- 3D Force Graph Component --- */ /* -------------------------------------------------------------------------- */ +function getForceGraphOptions( + fgRef: React.MutableRefObject<ForceGraphMethods3D<NodeObject3D<GNode>, + LinkObject3D<GNode, GLink>> | undefined>, + options: IGraphOptions3D, +): ForceGraphProps3D<GNode, GLink> { + const ret: ForceGraphProps3D<GNode, GLink> = {}; + + const { linkOptions, nodesOptions } = options; + + if (linkOptions) { + const { width, color, visibility, directionalArrow, + directionalParticle, particleWidth, particleColor + } = linkOptions; + if (width) ret.linkWidth = width; + if (color) ret.linkColor = color; + if (visibility) ret.linkVisibility = visibility; + if (directionalArrow) ret.linkDirectionalArrowLength = directionalArrow; + if (directionalParticle) ret.linkDirectionalParticles = directionalParticle; + if (particleWidth) ret.linkDirectionalParticleWidth = particleWidth; + if (particleColor) ret.linkDirectionalParticleColor = particleColor; + } + + if (nodesOptions?.visibility) ret.nodeVisibility = nodesOptions?.visibility; + + if(options) { + const { + backgroundColor, displayMode, depthSpacing, cycles, htmlNode, autoCenter + } = options; + + if (displayMode) ret.dagMode = displayMode; + if (depthSpacing) ret.dagLevelDistance = depthSpacing; + if (backgroundColor) ret.backgroundColor = backgroundColor; + + ret.onEngineStop = getOnEngineStop(fgRef, { autoCenter }); + if(cycles) ret.onDagError = getOnDagError(cycles); + + if(htmlNode) { + ret.extraRenderers = [new CSS2DRenderer()]; + ret.nodeThreeObjectExtend = false; + ret.nodeThreeObject = getObject2DFunction(htmlNode); + } + } + + return ret; +} -function Graph3D(props: GProps): JSX.Element { - const { data, onSelection, selected, size } = props; +function Graph3D(props: IGProps3D): JSX.Element { + const { data, onSelection, selected, size, + options = {} + } = props; const { width, height } = size; const fgRef3D = React.useRef< @@ -178,6 +346,12 @@ function Graph3D(props: GProps): JSX.Element { | undefined >(undefined); + const graphOptions = getForceGraphOptions(fgRef3D, options || {}); + const style = Themes.useStyle(); + + const [ , flipHorizontalSpacingIsSet ] = + Dome.useFlipSettings('ivette.callgraph.horizontalSpacingIsSet', true); + React.useEffect(() => { if (fgRef3D.current && selected) { // distance to set between camera and node @@ -198,7 +372,23 @@ function Graph3D(props: GProps): JSX.Element { } } } - }, [selected, data]); + }, [fgRef3D, selected, data]); + + React.useEffect(() => { + /** Waiting for fgRef3D.current initialization */ + if (!fgRef3D.current) { + const timer = setTimeout(() => { + flipHorizontalSpacingIsSet(); + }, 100); + return () => clearTimeout(timer); + } + + if (options.horizontalSpacing) { + // Adjust node repulsion + fgRef3D.current.d3Force('charge')?.strength(-options.horizontalSpacing); + } + return; + }, [options.horizontalSpacing, flipHorizontalSpacingIsSet]); return ( <ForceGraph3D<GNode, GLink> @@ -223,7 +413,11 @@ function Graph3D(props: GProps): JSX.Element { node.fy = node.y; node.fz = node.z; }} - nodeColor={(node) => (node.id === selected ? '#F4D03F' : '#5DADE2')} + nodeColor={(node) => (node.id === selected ? + style.getPropertyValue('--graph-bg-color-selected') : + style.getPropertyValue('--graph-bg-color-orange')) + } + {...graphOptions} /> ); } @@ -233,7 +427,8 @@ function Graph3D(props: GProps): JSX.Element { /* -------------------------------------------------------------------------- */ export function Graph(props: GraphProps): JSX.Element { - const { nodes, edges, onSelection, display = true, selected } = props; + const { nodes, edges, onSelection, + display = true, selected, options3D } = props; const data: GData = React.useMemo( () => ({ nodes: nodes.slice(), @@ -268,6 +463,7 @@ export function Graph(props: GraphProps): JSX.Element { data={data} onSelection={onSelection} selected={selected} + options={options3D} size={size} /> </div> diff --git a/ivette/src/dome/renderer/graph/style.css b/ivette/src/dome/renderer/graph/style.css index c8818cb37fc1b12a09f3fc820acec6e7e6a256a0..3f7a38c09b5c5fd3ab43b766765e161b004e28d8 100644 --- a/ivette/src/dome/renderer/graph/style.css +++ b/ivette/src/dome/renderer/graph/style.css @@ -3,5 +3,5 @@ /* -------------------------------------------------------------------------- */ .dome-xDiagram { - background: #eee; + background: var(--background); } diff --git a/ivette/src/dome/renderer/light.css b/ivette/src/dome/renderer/light.css index 732c57a2427ad095520fba78a333b5638bca3ba1..0975353f91073cfaea9e7b57b000e09502782c89 100644 --- a/ivette/src/dome/renderer/light.css +++ b/ivette/src/dome/renderer/light.css @@ -124,5 +124,58 @@ --status-valid-under-hyp: linear-gradient(to right, var(--status-valid) 49%, var(--status-unknown) 51%); --status-considered-valid: linear-gradient(to right, var(--status-valid) 49%, #73BBBB 51%); --status-invalid-under-hyp: linear-gradient(to right, var(--status-invalid) 49%, var(--status-unknown) 51%); + + /* node background colors */ + --graph-bg-color-default: #fff; + --graph-bg-color-white: #fff; + --graph-bg-color-grey: #ccc; + --graph-bg-color-dark: #666; + --graph-bg-color-primary: dodgerblue; + --graph-bg-color-selected: deepskyblue; + --graph-bg-color-green: lime; + --graph-bg-color-orange: #ffa700; + --graph-bg-color-red: red; + --graph-bg-color-yellow: yellow; + --graph-bg-color-blue: cyan; + --graph-bg-color-pink: hotpink; + /* cluster background colors */ + --graph-sg-color-default: #eee; + --graph-sg-color-white: #eee; + --graph-sg-color-grey: #ccc; + --graph-sg-color-dark: #aaa; + --graph-sg-color-primary: #4fc3f7; + --graph-sg-color-selected: #90caf9; + --graph-sg-color-green: #AED581; + --graph-sg-color-orange: #FFCC80; + --graph-sg-color-red: #ff6e6e; + --graph-sg-color-yellow: #fff59d; + --graph-sg-color-blue: #bbdefb; + --graph-sg-color-pink: #f8bbd0; + /** foreground colors */ + --graph-fg-color-default: black; + --graph-fg-color-white: black; + --graph-fg-color-grey: black; + --graph-fg-color-dark: white; + --graph-fg-color-primary: white; + --graph-fg-color-selected: black; + --graph-fg-color-green: black; + --graph-fg-color-orange: black; + --graph-fg-color-red: white; + --graph-fg-color-yellow: black; + --graph-fg-color-blue: black; + --graph-fg-color-pink: white; + /** edge colors */ + --graph-ed-color-default: black; + --graph-ed-color-white: #ccc; + --graph-ed-color-grey: #888; + --graph-ed-color-dark: black; + --graph-ed-color-primary: dodgerblue; + --graph-ed-color-selected: deepskyblue; + --graph-ed-color-green: green; + --graph-ed-color-orange: orange; + --graph-ed-color-red: red; + --graph-ed-color-yellow: #e5e100; + --graph-ed-color-blue: deepskyblue; + --graph-ed-color-pink: palevioletred1; } } diff --git a/ivette/src/dome/renderer/themes.tsx b/ivette/src/dome/renderer/themes.tsx index 655d4639010a5e7bcba1f07f674f2908e15ef6b3..f878aab3500fda3fbee0ffa8bb34b005f15e9478 100644 --- a/ivette/src/dome/renderer/themes.tsx +++ b/ivette/src/dome/renderer/themes.tsx @@ -29,6 +29,7 @@ @module dome/themes */ +import React from 'react'; import * as Dome from 'dome'; import * as Settings from 'dome/data/settings'; import { State } from 'dome/data/states'; @@ -80,4 +81,13 @@ export function useColorThemeSettings(): State<ColorSettings> { return [jColorSettings(pref), setTheme]; } +export function useStyle(): CSSStyleDeclaration { + const [theme, ] = useColorTheme(); + const style = React.useMemo(() => getComputedStyle(document.body), + /** style is dependent on theme but it is not used directly */ + // eslint-disable-next-line react-hooks/exhaustive-deps + [theme] + ); + return style; +} /* -------------------------------------------------------------------------- */ diff --git a/ivette/src/frama-c/react-three-css2drenderer.d.ts b/ivette/src/frama-c/react-three-css2drenderer.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..ea2d9ce6f51a0787586b80cd0ce73e8db8b44300 --- /dev/null +++ b/ivette/src/frama-c/react-three-css2drenderer.d.ts @@ -0,0 +1,23 @@ +/* ************************************************************************ */ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2024 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/* ************************************************************************ */ + +declare module 'three/examples/jsm/renderers/CSS2DRenderer'; diff --git a/ivette/src/sandbox/dotdiagram.tsx b/ivette/src/sandbox/dotdiagram.tsx index 191617f28bc1a6299abcf24c7ad53caf922ebd39..93cdf010d7325b61e51584d0d0688c2172ce4d21 100644 --- a/ivette/src/sandbox/dotdiagram.tsx +++ b/ivette/src/sandbox/dotdiagram.tsx @@ -89,7 +89,10 @@ const edges: Edge[] = [ function makeCluster(s: string | undefined): Cluster { const color = nodes.find(n => n.id === s)?.color; - return { id: 'BG', title: 'Background Cluster', color }; + return { + id: 'BG', + title: 'Background Cluster', + color: color ?? "default" }; } function DiagramSample(): JSX.Element {