WIP: Equation editing #1
Loading…
Reference in New Issue
No description provided.
Delete Branch "Heath123/TeX:equedit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
So far it's just cursor movement, and the cursor has to be drawn by the calling application. It also has to handle keypresses, e.g.:
Note that this includes breaking API changes. Also, there are many things in here that aren't really done, and the cursor navigates through some things in an odd order and it doesn't work with matrices (it just skips over them as if they were a single node). Also some of my code might not fit the code style but I made it mostly fit.
@ -18,0 +40,4 @@
else if (action == CURSOR_MOVE_RIGHT) {
// We take away 2 because offset 0 is actually one character into the text,
// and the last possible offset is one character before the end of the text,
// because if the cursor is at the start or end it goes in the parent flow.
Note that if you don't allow cursor positions at the start and end of text nodes, you will need extra logic to decide where to insert text when typing. Specifically, if the cursor is in a flow, you won't be able to just insert a new text node, you'll have to check whether there is a text node nearby.
I'd personally rather do the opposite, ie. move the cursor at the extreme positions of text nodes while skipping the corresponding inter-node positions in the flow. I can also see it helping when inserting other nodes (eg. inserting a fraction and grabbing parts of a text node with it), though that can surely be debated.
So I guess I could add another return type which is "not only did the cursor leave this node, but it needs to skip a whole other space too" and have the parent flow do that
Hmm I'd say it's better to not have the child node make decisions about its parent, which might not even exist. Instead, I'd add code in the flow function saying that if we just exited a text node, we skip the adjacent in-between-nodes positions in the flow.
Well if the recursive function is being called, something is calling it, right? So it can choose to ignore the value, but it wants to observe it it can. At least, that's how my current code works with the CURSOR_PAST_END logic
It could be called by the user directly because the node is top-level. What I mean is each node should behave as if it was isolated, because such design (when applicable) makes it easier to keep the information flow clean.
Ultimately it's just a subtle difference of looking at the chain of tests and decisions that the program will make, and deciding where recursive calls and returns should occur, ie. which node/flow is responsible for making each decision.
In this case I believe the behavior at the end of text nodes is a quirk of flows, not a fundamental property of the model. It wouldn't make sense for a fraction to return "right and skip one place". Hence, I believe it's cleaner to change the flow function than then API for these functions.
(These are fairly small details and neither option would completely break the code structure. I just think it's worth exploring the details once in a while, as these are rarely discussed but still important when building clean interfaces. Choosing the best option every times adds up eventually!)
I was thinking that if user code is calling this function then it's acting as a parent, and may be managing the cursor itself in the same way (e.g. navigating between multiple equations) so this information could be valuable to it.
It does feel a little hacky to have a separate return value like that, but then it feels even more hacky to have the parent have a special case in the code for a certain node type. For example, what if a new type of text node is added that can handle e.g. taller characters, and needs to behave the same way? It seems like it would be cleaner to have a way to specify that behaviour for any node that behaves that way than it would to check this in the parent...
My mental model of this is that any information required for the parent to manage the cursor movement when it moves out of a node flows up the tree, which isn't so much the node no longer being isolated as the parent and child working together to set the node to the correct position, which is necessary anyway as the child doesn't have enough information to do it on its own. If there is no parent then that's fine as the position the child leaves the cursor in before it returns is the correct and expected position if there is no parent to move it to, which in this case is the start or end of the text node.
If you're sure that the special case way is cleaner then I will do it but it feels less clean to me. I thought the cleanest way would be the original thing where it exits to the flow which is why I did that originally
That's a fair analysis. The big question then is: does that behavior generalizes to enough nodes to be considered part of the model?
I believe it's specific to text nodes because skipping a position in the flow only makes sense when two conditions are met: (1) visual text alignment, and (2) semantic equivalence.
(2) refers to the fact that the formula itself still means the same thing regardless of whether we insert at the end of a text node or just after it. This is a pretty rare thing, for instance if I have a formula
2^3
and the cursor before the 2 I really care about whether I'm inserting inside the base or outside of it. Visually it is similar (and confusing), but fundamentally the insertions are quite different, so the cursor should allow both to happen. I think text nodes are the only case where this will happen because they are the only nodes to not have a mathematical interpretation.(1) refers to the fact that if the two positions being considered (end of text and inside the flow) are not visually identical (up to a couple pixels), the mechanic will be confusing/wrong. Inserting at two different places cannot reasonably be understood to be the same thing (and math notation usually follows that principle). That means the decision to skip a cursor position must depend on some layout, which is node- and flow-specific, so it makes little sense to me to have it as a general response.
One last thought: an alternative to "move right and skip a space" might be "move right" along with "btw, the rightmost cursor position of this node type can be shared with the parent".
Anyway, no bikeshedding there. Feel free to choose whatever solution you think is the cleanest. I just wanted to dive in this question as a design exercise, which I'm sure we've achieved now. x3
I've realised that my solution isn't really any cleaner because there still has to be special logic in the parent for entering the nodes, so I'll just do it your way (when I get back to working on this)
What special logic is that? I'd expect that entering a new node is a simple call to
TeX_node_cursor_enter()
, which would not be specific to any type of node (unlike my proposition which needs to add logic specific to text nodes).Well the special logic is because it needs to enter the node earlier than it normally would. With a normal node like a fraction, if it starts like this:
Then you would move the cursor one to the right, between the two nodes:
Then once again to enter the node:
For text nodes you start like this (pretend that the 1 is its own node for some reason):
And then when you go right again you want to immediately capture the cursor:
Without special logic, the cursor would instead be in between the 1 and the text node.
The child can't handle this capturing with the current structure, because the cursor is in the parent at this point so the child isn't even recursed into. So there has to be special logic in the parent anyway. Though I haven't worked on this for a while so I may be forgetting something here
(these screenshots aren't from the calculator, I haven't gotten antialiased fonts working yet)
I made it work like you said, but now the program has to do this check after each action:
I'll make the library do this for you
Is that really a good idea? I'd rather go into text nodes when text gets input instead of at every action. If you edit in a way that adds a node, like inserting a fraction, sum, etc, I think it's better to have the cursor be in the flow.
When you've navigating through the nodes there is no input, so it needs to either do this, or go back to way of doing it I had before where the cursor wasn't allowed to be at the start and end, don't I?
I could make it always follow this rule except when a node has just been added but that seems like like it would cause problems with the only advantage being having to do one less check sometimes, so I think it's better if it's consistent with always being in a text node if it's at the start or end. Or am I misunderstanding what you're saying?
Consistency is definitely better, that's for sure. I think I'm just not clear on the fine details of your model. Let's forget my remark, as long as you have a clear set of valid cursor positions with no "duplicates" (ie. you don't have both offset 0 in a text node and in a flow before that node) and the cursor position is always one of them, you're definitely fine.
That's really nice! This is exactly how to use the recursive method. Other than the search which is a bit unnatural (but not easy to avoid as we discussed), there isn't much to improve I think. Maybe naming/ordering/etc with the API later when we merge the whole thing. One question: why did you not draw the cursor directly in the rendering functions?
Currently, the left brackets overlap the cursor, so I needed the cursor to always be drawn at the end on top of them and in a different colour. This could be fixed by adjusting the cursor to not overlap things but this seemed like the easiest way to fix it and maybe it could allow for more customisation
74d94a3979
to12b8599370
Step 1:
From your project repository, check out a new branch and test the changes.Step 2:
Merge the changes and update on Forgejo.