From 21ab978b0664d9fe4b20c03181a7bedaae90f7cf Mon Sep 17 00:00:00 2001 From: NatLeung96 Date: Wed, 15 Jul 2026 16:03:01 +0100 Subject: [PATCH 1/2] add editable to bob parser --- src/ui/widgets/EmbeddedDisplay/bobParser.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui/widgets/EmbeddedDisplay/bobParser.ts b/src/ui/widgets/EmbeddedDisplay/bobParser.ts index 9ebfd8a..3887c28 100644 --- a/src/ui/widgets/EmbeddedDisplay/bobParser.ts +++ b/src/ui/widgets/EmbeddedDisplay/bobParser.ts @@ -676,7 +676,8 @@ export const BOB_SIMPLE_PARSERS: ParserDict = { tabWidth: ["tab_width", bobParseNumber], tabHeight: ["tab_height", bobParseNumber], tabSpacing: ["tab_spacing", bobParseNumber], - activeTab: ["active_tab", bobParseNumber] + activeTab: ["active_tab", bobParseNumber], + editable: ["editable", opiParseBoolean] }; export const BOB_COMPLEX_PARSERS: ComplexParserDict = { From 64704ec6c44ac12f0e3a940742fb0c58d458e7af Mon Sep 17 00:00:00 2001 From: NatLeung96 Date: Thu, 16 Jul 2026 13:10:44 +0100 Subject: [PATCH 2/2] add autocomplete to menuButton --- src/ui/widgets/MenuButton/menuButton.tsx | 97 ++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/src/ui/widgets/MenuButton/menuButton.tsx b/src/ui/widgets/MenuButton/menuButton.tsx index 2ee5bf8..efddf14 100644 --- a/src/ui/widgets/MenuButton/menuButton.tsx +++ b/src/ui/widgets/MenuButton/menuButton.tsx @@ -18,7 +18,13 @@ import { WidgetActions } from "../widgetActions"; import { FileContext } from "../../../misc/fileContext"; -import { MenuItem, Select, SelectChangeEvent } from "@mui/material"; +import { + Autocomplete, + createFilterOptions, + MenuItem, + Select, + TextField +} from "@mui/material"; import { getPvValueAndName } from "../utils"; import log from "loglevel"; import { @@ -36,6 +42,7 @@ export const MenuButtonProps = { font: FontPropOpt, border: BorderPropOpt, enabled: BoolPropOpt, + editable: BoolPropOpt, // opi specific prop actionsFromPv: BoolPropOpt, actions: ActionsPropType, @@ -132,19 +139,35 @@ export const MenuButtonComponent = ( ); }); - function onChange(event: SelectChangeEvent): void { + function onChange(newValue: string | null): void { // Do nothing if we click on the first blank option - if (event.target.value) { + if (newValue) { // If no PV connected, reset to No PV if (!pvName) { setDisplayValue("No PV"); } else { // If PV connected, we allow the value to change and trigger index change try { - executeAction(actions[options.indexOf(event.target.value)], files); + if (options.includes(newValue)) { + executeAction(actions[options.indexOf(newValue)], files); + } else { + executeAction( + { + type: WRITE_PV, + writePvInfo: { + pvName: pvName, + value: + valueType === DtypeValues.NUMBER + ? Number(newValue) + : newValue + } + } as WritePv, + files + ); + } } catch (e: any) { // If action fails due to widget items not existing on PV - if (enumPv && !value?.display.choices?.includes(event.target.value)) { + if (enumPv && !value?.display.choices?.includes(newValue)) { // Add an option to display our error string setDisplayValue(e.toString()); } else { @@ -155,7 +178,67 @@ export const MenuButtonComponent = ( } } - return ( + const filter = createFilterOptions(); + + return props.editable ? ( + onChange(newValue)} + selectOnFocus + clearOnBlur + handleHomeEndKeys + freeSolo + options={options} + filterOptions={(options, params) => { + const filtered = filter(options, params); + const { inputValue } = params; + const isExisting = options.some(option => inputValue === option); + if (inputValue !== "" && !isExisting) { + filtered.push(inputValue); + } + return filtered; + }} + renderInput={params => } + renderOption={(props, option) => { + const { key, ...optionProps } = props; + return ( +
  • + {option} +
  • + ); + }} + slotProps={{ + paper: { + sx: { ...style?.colors } + } + }} + sx={{ + cursor: disabled ? "not-allowed" : "default", + height: "100%", + width: "100%", + "& .MuiFormControl-root": { + height: "100%", + width: "100%" + }, + "& .MuiInputBase-root": { + ...style?.colors, + height: "100%", + widht: "100%" + }, + "& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline": { + borderRadius: "4px", + inset: "0px", + "& .css-1nf2c5d-MuiNotchedOutlined-root": { + height: "0px" + } + }, + "& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline": + { + borderColor: "#000000" + } + }} + /> + ) : (