Open charlesroddie opened 6 years ago
I was thinking about an alternate route we could discuss: maybe we could store the cursor position in the original text, and Atom could have some way of mapping between original text and its' internal contents?
Consider that:
\frac{2+2}{3+|3}
(where |
denotes a cursor)
Our hypothetical FracAtom
could store source info (infamous SourceSpans
from WPF-Math or something else) and provide a mapping: 3+3
is mapped to RowAtom
that consists of Char(3)
, Symbol(+)
and Char(3)
. That way, our Atoms shouldn't store any selection/cursor information, but the editor (even the editor that doesn't allow the user to edit LaTeX markup) could always properly calculate the position.
Also this would solve the navigation problem: having a complex Atom
, it's usually nontrivial to calculate what should happen if the user press →
button on the keyboard. But with text-based representation, it will be easier I think.
Having said all of this, I don't think that initial idea from the OP is bad.
But I'm not sure if we want to add all the editor stuff to this library in such a way. Consider that every math rendering library will now need to support this InputSquare
. And what if math editor library will need some additional atoms aside from that?
I'd recommend to add an extendable mechanism that will be useful for everyone, no matter if they're editing math or rendering it or whatever. Probably let's add some "tags" to our atoms? E.g.
type TaggedAtom<'a> = { atom : Atom; tag : 'a }
Allow the user to add their own "tags" to atoms and add extension points to the renderer, so the user code is able to analyze the tags and instruct the renderer accordingly. Something like this:
type MyAtomState = Rendered | InputSquare | Selected
type MyMathEditorAtom = TaggedAtom<MyState>
let myRenderer =
let baseRenderer = MathAtom.createRenderer<MyAtomState>()
{ baseRenderer with
getBackground = fun (a : TaggedAtom) -> match a.tag with
| Rendered -> baseRenderer.getBackground a
| InputSquare -> Color.Red
| Selected -> Color.Blue }
(that's just a quick sample, I'm not sure what actual extension points would be)
@ForNeVeR maybe we could store the cursor position in the original text, and Atom could have some way of mapping between original text and its' internal contents?
having a complex Atom, it's usually nontrivial to calculate what should happen if the user press → button on the keyboard. But with text-based representation, it will be easier I think.
I don't think we should retain LaTeX because:
Consider that every math rendering library will now need to support this InputSquare. And what if math editor library will need some additional atoms aside from that?
We are supporting 2 rendering libraries, which may become 1 in future.
Options for differing feature support between wpf-math and csharpmath:
Support the union of features, and consuming libraries can ignore cases that are not supported (with an explicit match and error). We could if needed package up CSharpMath-only features in one place so this is easier and the same for wpf-math.
Support the intersection of features and add an extensibility solution. But this needs to add new Atom cases and add the processing for new atoms in various places, so it will be complex to use and manage. We'd need to spec it out and then make sure there is one person in each rendering project who is able to use it, and that ordinary contributors can code without understanding this mechanism.
Do the extensions or subtractions in forks of MathAtom.
@charlesroddie Support 3. The main work of locating an atom in MathEditor is MathListIndex. (Correct the syntax if necessary)
type MathListIndex =
member val AtomIndex = 0 with get, set
member val SubIndex : MathListIndex option with get, set
member val SubIndexType : MathListSubIndexType with get, set
type MathListSubIndexType =
| None
/// The position in the subindex is an index into the nucleus
| Nucleus
/// The subindex indexes into the superscript.
| Superscript
/// The subindex indexes into the subscript
| Subscript
/// The subindex indexes into the numerator (only valid for fractions)
| Numerator
/// The subindex indexes into the denominator (only valid for fractions)
| Denominator
/// The subindex indexes into the radicand (only valid for radicals)
| Radicand
/// The subindex indexes into the degree (only valid for radicals)
| Degree
We could put extra cases inside the Atom type:
Alternatively we could make separate Atom from cursor position / selection:
@Happypig375 thoughts appreciated as you work on the MathEditor port.
I think the first approach will be much cleaner. In the simplest implementation it may not be the most performant as l/r/u/d moves would lead to laying out everything again, but as long as it takes << 1/60 s to process a change we don't need to worry.