diff --git a/ivette/src/dome/renderer/frame/sidebars.tsx b/ivette/src/dome/renderer/frame/sidebars.tsx index 0790d5e0b57b62edc04c15cba9f2003ebb32cc13..db78df2556b4bae27ca17c1ac9bc737212e81373 100644 --- a/ivette/src/dome/renderer/frame/sidebars.tsx +++ b/ivette/src/dome/renderer/frame/sidebars.tsx @@ -35,7 +35,7 @@ import { Badge } from 'dome/controls/icons'; import { Label } from 'dome/controls/labels'; import { classes } from 'dome/misc/utils'; import { Hbox, Hfill } from 'dome/layout/boxes'; -import { IconButton, Button } from 'dome/controls/buttons'; +import { IconButton, IconButtonProps } from 'dome/controls/buttons'; import './style.css'; @@ -72,12 +72,12 @@ export function SideBar(props: SideBarProps): JSX.Element { export type BadgeElt = undefined | null | string | number | React.ReactNode; export type Badges = BadgeElt | BadgeElt[]; -const makeBadgeElt = (elt: BadgeElt): React.ReactNode => { +const makeBadgeElt = (elt: BadgeElt, index: number): React.ReactNode => { if (elt === undefined || elt === null) return null; switch (typeof (elt)) { case 'number': case 'string': - return <Badge value={elt} />; + return <Badge value={elt} key={`item#${index}`} />; default: return elt; } @@ -86,7 +86,7 @@ const makeBadgeElt = (elt: BadgeElt): React.ReactNode => { const makeBadge = (elt: Badges): React.ReactNode => { if (Array.isArray(elt)) return elt.map(makeBadgeElt); - return makeBadgeElt(elt); + return makeBadgeElt(elt, 0); }; // -------------------------------------------------------------------------- @@ -110,8 +110,8 @@ export interface SectionProps { disabled?: boolean; /** Badge summary (only visible when folded). */ summary?: Badges; - /** Right-click callback. */ - onContextMenu?: () => void; + /** Additional controls, (only visible when unfolded). */ + rightButtonProps?: IconButtonProps; /** Section contents. */ children?: React.ReactNode; } @@ -134,22 +134,10 @@ export function Section(props: SectionProps): JSX.Element | null { const { enabled = true, disabled = false, children } = props; if (disabled || !enabled) return null; - const noChildContent = - <div className='dome-xSideBarSection-content'> - <label className='dome-xSideBarSection-info'> - {'There is no function to display. Maybe you can change ' + - 'the filtering options.'} - </label> - <Button - icon='TUNINGS' - label='Functions filtering options' - onClick={props.onContextMenu} - visible={!(props.onContextMenu === undefined)} - /> - </div>; - const visible = unfold ?? state; const maxHeight = visible ? 'max-content' : 0; + const { rightButtonProps: iconProps } = props; + const rightButton = iconProps ? <IconButton {...iconProps}/> : undefined; return ( <div className='dome-xSideBarSection'> @@ -161,17 +149,11 @@ export function Section(props: SectionProps): JSX.Element | null { icon={icon} onClick={flipState} /> - {!visible && makeBadge(props.summary)} <Hfill /> - <IconButton - icon='TUNINGS' - title={'Functions filtering options'} - onClick={props.onContextMenu} - visible={!(props.onContextMenu === undefined)} - /> + {visible ? rightButton : makeBadge(props.summary)} </Hbox> <div className='dome-xSideBarSection-content' style={{ maxHeight }}> - {React.Children.count(children) === 0 ? noChildContent : children} + {children} </div> </div> ); diff --git a/ivette/src/dome/renderer/frame/style.css b/ivette/src/dome/renderer/frame/style.css index 48ce60ce8417f0e358b215753fd663c34b9707db..266bed0e505b00875e050e9834c43bf5ada4b9d7 100644 --- a/ivette/src/dome/renderer/frame/style.css +++ b/ivette/src/dome/renderer/frame/style.css @@ -120,10 +120,6 @@ align-items: center ; } -.dome-xSideBarSection-title > .dome-xIcon { - padding: 0px; -} - .dome-xSideBarSection-title > .dome-xLabel { flex: 1 1 ; overflow: hidden ; @@ -135,6 +131,11 @@ flex: 0 0 ; } +.dome-xSideBarSection-filterButton { + margin: 0px; + padding: 0px; +} + .dome-xSideBarSection-hideshow { flex: 0 0 ; margin: 1px ; diff --git a/ivette/src/frama-c/kernel/Globals.tsx b/ivette/src/frama-c/kernel/Globals.tsx index b7d52553bbf11704a5f30e901527b8b1a72f309f..701e001b43167db5b5d8755f3fecccb9df6eff51 100644 --- a/ivette/src/frama-c/kernel/Globals.tsx +++ b/ivette/src/frama-c/kernel/Globals.tsx @@ -29,6 +29,7 @@ import * as Dome from 'dome'; import { classes } from 'dome/misc/utils'; import { alpha } from 'dome/data/compare'; import { Section, Item } from 'dome/frame/sidebars'; +import { Button } from 'dome/controls/buttons'; import * as Ivette from 'ivette'; import * as States from 'frama-c/states'; @@ -180,22 +181,55 @@ export default function Globals(): JSX.Element { const nTotal = fcts.length; const nFilter = filtered.length; const title = `Functions ${nFilter} / ${nTotal}`; + + const filterButtonProps = { + icon: 'TUNINGS', + title: 'Functions filtering options', + onClick: onContextMenu, + visible: !(onContextMenu === undefined), + className: 'dome-xSideBarSection-filterButton' + }; + + const filteredFunctions = + filtered.map((fct) => ( + <FctItem + key={fct.name} + fct={fct} + current={current} + onSelection={onSelection} + /> + )); + + const noFunction = + <div className='dome-xSideBarSection-content'> + <label className='dome-xSideBarSection-info'> + {'There is no function to display.'} + </label> + </div>; + + const allFiltered = + <div className='dome-xSideBarSection-content'> + <label className='dome-xSideBarSection-info'> + {'There is no function to display. Maybe you can change ' + + 'the filtering options.'} + </label> + <Button + icon='TUNINGS' + label='Functions filtering options' + onClick={onContextMenu} + visible={!(onContextMenu === undefined)} + /> + </div>; + return ( <Section label="Functions" title={title} - onContextMenu={onContextMenu} defaultUnfold - disabled={nTotal === 0} + rightButtonProps={filterButtonProps} + summary={[nFilter]} > - {filtered.map((fct) => ( - <FctItem - key={fct.name} - fct={fct} - current={current} - onSelection={onSelection} - /> - ))} + {nFilter > 0 ? filteredFunctions : nTotal > 0 ? allFiltered : noFunction} </Section> );