Here's a version of the previous answer that doesn't move the selection and only works at the Box
level:
MakeBoxes[replacementMarker[a_, tag_], StandardForm] ^:=
TagBox[MakeBoxes[a],
tag,
BoxID -> tag]
replaceMark[obj_, function_, tag_] :=
With[{ref =
FE`BoxReference[
FE`Evaluate@obj,
{{tag}}
]
},
FrontEndExecute@
FrontEnd`BoxReferenceReplace[ref,
FrontEndExecute@
FrontEnd`BoxReferenceRead[ref] // function
]
]
Row[{"Not replaced",
replacementMarker[
Panel[
Column[{Style["This is a panel", Bold],
Button["Press me!",
replaceMark[
EvaluationNotebook[],
MakeBoxes[" Now I'm just text."] &,
"replacementTag"
]
]
}]],
"replacementTag"],
"Not replaced"}
]
It works in exactly the same way, but uses the FrontEnd`BoxReference*
functions as "documented" here
The nice thing about this is it makes preserving the TagBox
pretty easy. We just need to use a BoxOffset
to replace the inner contents:
replaceMark[obj_, function_, tag_] :=
With[{ref =
FE`BoxReference[
FE`Evaluate@obj,
{{tag}},
FE`BoxOffset -> {FE`BoxChild[1]}
]
},
FrontEndExecute@
FrontEnd`BoxReferenceReplace[ref,
FrontEndExecute@FrontEnd`BoxReferenceRead[ref] // function]
]
One thing to note when playing with this is that it replaces the next instance of the ID from the current selection, so if you don't delete any replaced cells after your cursor it will seem as if it isn't working. Alternatively provide a UUID to your box:
With[{uuid = CreateUUID[]},
Row[{"Not replaced",
replacementMarker[
Panel[
Column[{
Style["This is a panel", Bold],
Button["Press me!",
replaceMark[EvaluationNotebook[],
MakeBoxes[uuid] &,
uuid]
]
}]
],
uuid],
"Not replaced"}]
]
We can even stick this in the MakeBoxes
call:
MakeBoxes[replacementMarker[a_], StandardForm] ^:=
With[{uuid = CreateUUID[]},
TagBox[MakeBoxes[a] /. FE`$BoxUUID -> uuid,
uuid,
BoxID -> uuid
]
];
With[{uuid = CreateUUID[]},
Row[{"Not replaced",
replacementMarker[
Panel[
Column[{
Style["This is a panel", Bold],
Button["Press me!",
replaceMark[
EvaluationNotebook[],
MakeBoxes[FE`$BoxUUID] &,
FE`$BoxUUID
]
]
}]
]],
"Not replaced"}]
]
Now any raw replacementMarker
will make itself unique with the ID being propagated down via FE`$BoxUUID
with the UUID being preserved after replacement.