diff --git a/CSharpMath.Editor.Tests/KeyPressTests.cs b/CSharpMath.Editor.Tests/KeyPressTests.cs index e5d84b13..49ba66c1 100644 --- a/CSharpMath.Editor.Tests/KeyPressTests.cs +++ b/CSharpMath.Editor.Tests/KeyPressTests.cs @@ -11,12 +11,12 @@ namespace CSharpMath.Editor.Tests { public class KeyPressTests { private static readonly TypesettingContext context = TestTypesettingContexts.Instance; static void Test(string latex, K[] inputs) { - var keyboard = new MathKeyboard(context, new TestFont()); + var keyboard = new MathKeyboard(context, new TestFont(20)); keyboard.KeyPress(inputs); Assert.Equal(latex, keyboard.LaTeX); } static void TestEvent(EventInteractor attach, EventInteractor detach, K[] inputs) { - var keyboard = new MathKeyboard(context, new TestFont()); + var keyboard = new MathKeyboard(context, new TestFont(20)); Assert.Raises( h => attach(keyboard, new EventHandler(h)), h => detach(keyboard, new EventHandler(h)), @@ -153,12 +153,19 @@ public void NoDuplicateValues() { [ Theory, T(@"", K.Left, K.Left, K.Left, K.Right, K.Right, K.Right), + T(@"1", K.Left, K.D1), + T(@"1", K.Right, K.D1), T(@"\square ^■2", K.Power, K.Left, K.Left, K.Right, K.Right, K.Right, K.D2, K.Left, K.Left), + T(@"\square _■2", K.Subscript, K.Left, K.Left, K.Right, K.Right, K.Right, K.D2, K.Left, K.Left), T(@"+-\times \div ", K.Divide, K.Left, K.Multiply, K.Left, K.Minus, K.Left, K.Plus), T(@"\sin \cos \tan \arcsin \arccos \arctan ", K.ArcSine, K.ArcCosine, K.Left, K.Left, K.Sine, K.Cosine, K.Right, K.Right, K.ArcTangent, K.Left, K.Left, K.Left, K.Tangent), - T(@"e^{\square }", K.Power, K.Left, K.SmallE, K.Right), - T(@"e^■", K.Power, K.Left, K.SmallE, K.Left), + T(@"e^{\square }", K.Power, K.Left, K.SmallE), + T(@"ef^{\square }", K.Power, K.Left, K.SmallE, K.SmallF), + T(@"e^f", K.Power, K.Left, K.SmallE, K.Right, K.SmallF), + T(@"e_{\square }", K.Subscript, K.Left, K.SmallE), + T(@"ef_{\square }", K.Subscript, K.Left, K.SmallE, K.SmallF), + T(@"e_f", K.Subscript, K.Left, K.SmallE, K.Right, K.SmallF), T(@"\left| x\right| \vert y\vert ", K.Absolute, K.SmallX, K.Right, K.VerticalBar, K.SmallY, K.VerticalBar), T(@"\left( 1\right) (2)", K.BothRoundBrackets, K.D1, K.Right, K.LeftRoundBracket, K.D2, K.RightRoundBracket), T(@"1\left( 2\left[ 3\left\{ ■\right\} \right] \right) ", K.BothRoundBrackets, K.BothSquareBrackets, K.BothCurlyBrackets, K.Left, @@ -194,6 +201,92 @@ public void NoDuplicateValues() { ] public void LeftRightNavigation(string latex, params K[] inputs) => Test(latex, inputs); + [ + Theory, + T(@"", K.Up, K.Up, K.Up, K.Down, K.Down, K.Down), + T(@"1", K.Up, K.D1), + T(@"1", K.Down, K.D1), + T(@"\frac{\square }{■}", K.Fraction, K.Down), + T(@"\frac{\square }{1}", K.Fraction, K.Down, K.D1), + T(@"\frac{■}{\square }", K.Fraction, K.Down, K.Up), + T(@"\frac{1}{\square }", K.Fraction, K.Down, K.Up, K.D1), + T(@"\frac{\square }{■}", K.Fraction, K.Down, K.Up, K.Down), + T(@"\frac{\square }{1}", K.Fraction, K.Down, K.Up, K.Down, K.D1), + T(@"\frac{123x456}{y}", K.Fraction, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.Up, K.SmallX, K.Down, K.SmallY), + T(@"\frac{y}{123x456}", K.Fraction, K.Down, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Up, K.Down, K.SmallX, K.Up, K.SmallY), + T(@"\frac{1234z56}{xy}", K.Fraction, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.SmallX, K.SmallY, K.Up, K.SmallZ), + T(@"\frac{xy}{1234z56}", K.Fraction, K.Down, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Up, K.SmallX, K.SmallY, K.Down, K.SmallZ), + T(@"\frac{\sqrt{2}}{■}", K.Fraction, K.SquareRoot, K.D2, K.Down), + T(@"\frac{■}{\sqrt{2}}", K.Fraction, K.Down, K.SquareRoot, K.D2, K.Up), + T(@"\frac{\sqrt{789}}{\square }", K.Fraction, K.SquareRoot, K.D8, K.D9, K.Down, K.Up, K.D7), + T(@"\frac{\square }{\sqrt{789}}", K.Fraction, K.Down, K.SquareRoot, K.D8, K.D9, K.Up, K.Down, K.D7), + T(@"\frac{\left( a\right) }{\left( b\right) }", K.Fraction, K.BothRoundBrackets, K.Down, K.BothRoundBrackets, K.Up, K.SmallA, K.Down, K.SmallB), + T(@"\frac{\left( b\right) }{\left( a\right) }", K.Fraction, K.Down, K.BothRoundBrackets, K.Up, K.BothRoundBrackets, K.Down, K.SmallA, K.Up, K.SmallB), + T(@"2^{ab}c", K.D2, K.Power, K.SmallA, K.Up, K.SmallB, K.Down, K.SmallC), + T(@"2_{ab}c", K.D2, K.Subscript, K.SmallA, K.Down, K.SmallB, K.Up, K.SmallC), + T(@"2_{cab}^{\square }", K.D2, K.Subscript, K.SmallA, K.SmallB, K.Up, K.Power, K.Down, K.SmallC), + T(@"2c_{\square }^{ab}", K.D2, K.Power, K.SmallA, K.SmallB, K.Down, K.Subscript, K.Up, K.SmallC), + T(@"\square _1^3", K.Subscript, K.Up, K.Power, K.Down, K.D1, K.Up, K.D3), + T(@"\square ^{\square }2", K.Power, K.Down, K.D2), + T(@"\square _{\square }2", K.Subscript, K.Up, K.D2), + T(@"123^■", K.D1, K.Power, K.Down, K.D2, K.D3, K.Up), + T(@"123_■", K.D1, K.Subscript, K.Up, K.D2, K.D3, K.Down), + T(@"1^{\square ^{\square ^{4^{\square }}}}", K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"1_{\square _{\square _{4_{\square }}}}", K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"\frac{1^{\square ^{\square ^{4^{\square }}}}}{\square }", K.Fraction, K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"\frac{1_{\square _{\square _{4_{\square }}}}}{\square }", K.Fraction, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"\frac{\square }{1^{\square ^{\square ^{4^{\square }}}}}", K.Fraction, K.Down, K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"\frac{\square }{1_{\square _{\square _{4_{\square }}}}}", K.Fraction, K.Down, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"1^{\square ^{\square ^{\square ^5}}}", K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.Up, K.D5), + T(@"1_{\square _{\square _{\square _5}}}", K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.Down, K.D5), + T(@"\frac{1^{\square ^{\square ^{\square ^5}}}}{\square }", K.Fraction, K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.Up, K.D5), + T(@"\frac{1_{\square _{\square _{\square _5}}}}{\square }", K.Fraction, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.Down, K.D5), + T(@"\frac{\square }{1^{\square ^{\square ^{\square ^5}}}}", K.Fraction, K.Down, K.Power, K.Power, K.Power, K.Power, K.Down, K.Down, K.Down, K.Down, K.D1, K.Up, K.Up, K.Up, K.Up, K.D5), + T(@"\frac{\square }{1_{\square _{\square _{\square _5}}}}", K.Fraction, K.Down, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Up, K.Up, K.Up, K.Up, K.D1, K.Down, K.Down, K.Down, K.Down, K.D5), + ] + public void UpDownNavigation(string latex, params K[] inputs) => Test(latex, inputs); + + [ + Theory, + T(@"\square ^21", K.Power, K.D2, K.Left, K.Down, K.D1), + T(@"\square _21", K.Subscript, K.D2, K.Left, K.Up, K.D1), + T(@"01^2", K.D0, K.Power, K.D2, K.Left, K.Down, K.D1), + T(@"01_2", K.D0, K.Subscript, K.D2, K.Left, K.Up, K.D1), + T(@"1^{23}", K.Power, K.D2, K.D3, K.Left, K.Left, K.Down, K.D1), + T(@"1_{23}", K.Subscript, K.D2, K.D3, K.Left, K.Left, K.Up, K.D1), + T(@"x^{zy}", K.SmallX, K.Power, K.SmallY, K.Left, K.Left, K.Up, K.SmallZ), + T(@"x_{zy}", K.SmallX, K.Subscript, K.SmallY, K.Left, K.Left, K.Down, K.SmallZ), + T(@"1^23", K.D1, K.Power, K.Right, K.D3, K.Left, K.Left, K.Up, K.D2), + T(@"1_23", K.D1, K.Subscript, K.Right, K.D3, K.Left, K.Left, K.Down, K.D2), + T(@"1^{\left( 2\right) }3", K.D1, K.Power, K.BothRoundBrackets, K.Right, K.Right, K.D3, K.Left, K.Up, K.Left, K.D2), + T(@"1_{\left( 2\right) }3", K.D1, K.Subscript, K.BothRoundBrackets, K.Right, K.Right, K.D3, K.Left, K.Down, K.Left, K.D2), + T(@"\frac{a_{123x456}}{b^{6543y21}}", K.Fraction, K.SmallA, K.Subscript, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.Down, + K.SmallB, K.Power, K.D6, K.D5, K.D4, K.D3, K.D2, K.D1, K.Left, K.Left, K.Left, K.Up, K.SmallX, K.Down, K.SmallY), + T(@"\frac{a^{1e23456}b}{cd_{654321}}", K.Fraction, K.SmallA, K.Power, K.D1, K.D2, K.D3, K.D4, K.D5, K.D6, K.Down, K.SmallB, K.Down, + K.SmallC, K.Subscript, K.D6, K.D5, K.D4, K.D3, K.D2, K.D1, K.Left, K.Left, K.Left, K.Up, K.SmallD, K.Up, K.SmallE), + T(@"1^{\square ^{\square ^{4^{\square }}}}", K.Power, K.Power, K.Power, K.Power, K.Left, K.Left, K.Left, K.Left, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"1_{\square _{\square _{4_{\square }}}}", K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Left, K.Left, K.Left, K.Left, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"\frac{1^{\square ^{\square ^{4^{\square }}}}}{\square }", K.Fraction, K.Power, K.Power, K.Power, K.Power, K.Left, K.Left, K.Left, K.Left, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"\frac{1_{\square _{\square _{4_{\square }}}}}{\square }", K.Fraction, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Left, K.Left, K.Left, K.Left, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"\frac{\square }{1^{\square ^{\square ^{4^{\square }}}}}", K.Fraction, K.Down, K.Power, K.Power, K.Power, K.Power, K.Left, K.Left, K.Left, K.Left, K.D1, K.Up, K.Up, K.Up, K.D4), + T(@"\frac{\square }{1_{\square _{\square _{4_{\square }}}}}", K.Fraction, K.Down, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Left, K.Left, K.Left, K.Left, K.D1, K.Down, K.Down, K.Down, K.D4), + T(@"1^{\square ^{\square ^{\square ^{\square }}7}8}9", K.D1, K.Power, K.Power, K.Power, K.Power, K.Right, K.Down, K.Down, K.Down, K.D9, K.Left, K.Up, K.Up, K.D7, K.Down, K.D8), + T(@"1_{\square _{\square _{\square _{\square }}7}8}9", K.D1, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Right, K.Up, K.Up, K.Up, K.D9, K.Left, K.Down, K.Down, K.D7, K.Up, K.D8), + T(@"\frac{1^{\square ^{\square ^{\square ^{\square }}7}8}9}{\square }", K.Fraction, K.D1, K.Power, K.Power, K.Power, K.Power, K.Right, K.Down, K.Down, K.Down, K.D9, K.Left, K.Up, K.Up, K.D7, K.Down, K.D8), + T(@"\frac{1_{\square _{\square _{\square _{\square }}7}8}9}{\square }", K.Fraction, K.D1, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Right, K.Up, K.Up, K.Up, K.D9, K.Left, K.Down, K.Down, K.D7, K.Up, K.D8), + T(@"\frac{\square }{1^{\square ^{\square ^{\square ^{\square }}7}8}9}", K.Fraction, K.Down, K.D1, K.Power, K.Power, K.Power, K.Power, K.Right, K.Down, K.Down, K.Down, K.D9, K.Left, K.Up, K.Up, K.D7, K.Down, K.D8), + T(@"\frac{\square }{1_{\square _{\square _{\square _{\square }}7}8}9}", K.Fraction, K.Down, K.D1, K.Subscript, K.Subscript, K.Subscript, K.Subscript, K.Right, K.Up, K.Up, K.Up, K.D9, K.Left, K.Down, K.Down, K.D7, K.Up, K.D8), + T(@"\frac{ef^{gh}}{\square }", K.Fraction, K.SmallE, K.Power, K.SmallH, K.Left, K.Down, K.SmallF, K.Up, K.SmallG), + T(@"\frac{ef_{gh}}{\square }", K.Fraction, K.SmallE, K.Subscript, K.SmallH, K.Left, K.Up, K.SmallF, K.Down, K.SmallG), + T(@"\frac{\square }{ef^{gh}}", K.Fraction, K.Down, K.SmallE, K.Power, K.SmallH, K.Left, K.Down, K.SmallF, K.Up, K.SmallG), + T(@"\frac{\square }{ef_{gh}}", K.Fraction, K.Down, K.SmallE, K.Subscript, K.SmallH, K.Left, K.Up, K.SmallF, K.Down, K.SmallG), + T(@"\frac{a^b}{c}", K.Fraction, K.SmallA, K.Power, K.SmallB, K.Left, K.Down, K.Down, K.SmallC), + T(@"\frac{a_{tb}}{c}", K.Fraction, K.SmallA, K.Subscript, K.SmallB, K.Left, K.Up, K.Down, K.SmallT, K.Down, K.SmallC), + T(@"\frac{c}{a^{tb}}", K.Fraction, K.Down, K.SmallA, K.Power, K.SmallB, K.Left, K.Down, K.Up, K.SmallT, K.Up, K.SmallC), + T(@"\frac{c}{a_b}", K.Fraction, K.Down, K.SmallA, K.Subscript, K.SmallB, K.Left, K.Up, K.Up, K.SmallC), + ] + public void FourDirectionalNavigation(string latex, params K[] inputs) => Test(latex, inputs); + [ Theory, T(@"", K.Clear, K.Clear, K.Left, K.Left, K.X, K.Clear, K.Right, K.Right, K.Y, K.Clear), @@ -227,11 +320,27 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Backspace, K.Backspace, K.Backspace, K.Backspace, K.Backspace), + T(@"2", K.Backspace, K.Backspace, K.D1, K.Backspace, K.D2), T(@"1", K.D1, K.D2, K.Backspace), T(@"x^2", K.SmallX, K.Power, K.D2, K.D1, K.Backspace), - T(@"y_{3_4}", K.SmallY, K.Subscript, K.D3, K.Subscript, K.Backspace, K.Backspace, K.D4, K.D5, K.Backspace), - T(@"5^■", K.D5, K.Power, K.Iota, K.Kappa, K.SmallEta, K.Backspace, K.Backspace, K.Backspace, K.Backspace), - T(@"\frac{■}{\square }", K.Fraction, K.Backspace), + T(@"5", K.D5, K.Power, K.Iota, K.Kappa, K.SmallEta, K.Backspace, K.Backspace, K.Backspace, K.Backspace), + T(@"", K.Fraction, K.Backspace), + T(@"", K.Power, K.Backspace), + T(@"", K.Subscript, K.Backspace), + T(@"", K.SquareRoot, K.Backspace), + T(@"3", K.CubeRoot, K.Backspace), + T(@"", K.NthRoot, K.Backspace), + T(@"a", K.SmallA, K.Fraction, K.Backspace), + T(@"a", K.SmallA, K.SquareRoot, K.Backspace), + T(@"a3", K.SmallA, K.CubeRoot, K.Backspace), + T(@"a", K.SmallA, K.NthRoot, K.Backspace), + T(@"a", K.SmallA, K.Power, K.Backspace), + T(@"a", K.SmallA, K.Subscript, K.Backspace), + T(@"\square ^■", K.Power, K.Subscript, K.Backspace), + T(@"\square ^■", K.Power, K.Power, K.Backspace), + T(@"\square _■", K.Subscript, K.Power, K.Backspace), + T(@"\square _■", K.Subscript, K.Subscript, K.Backspace), + T(@"y_4", K.SmallY, K.Subscript, K.D3, K.Subscript, K.Backspace, K.Backspace, K.D4, K.D5, K.Backspace), T(@"", K.VerticalBar, K.VerticalBar, K.Backspace, K.Backspace, K.Backspace) ] public void Backspace(string latex, params K[] inputs) => Test(latex, inputs); @@ -239,15 +348,97 @@ public void Return(params K[] inputs) => [ Theory, T(@"", K.Left, K.Left, K.Backspace, K.Backspace, K.Right, K.Right, K.Backspace, K.Backspace, K.Left), + T(@"", K.Power, K.Left, K.Backspace), + T(@"", K.Subscript, K.Left, K.Backspace), + T(@".\frac{\square }{\square }", K.Fraction, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt{\square }", K.SquareRoot, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt[\square ]{\square }", K.NthRoot, K.Left, K.Backspace, K.Decimal), + T(@".\left( \square \right) ", K.BothRoundBrackets, K.Left, K.Backspace, K.Decimal), + T(@".", K.Power, K.Left, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Left, K.Backspace, K.Decimal), + T(@".\frac{\square }{\square }", K.SmallA, K.Fraction, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt{\square }", K.SmallA, K.SquareRoot, K.Left, K.Backspace, K.Decimal), + T(@".\sqrt[\square ]{\square }", K.SmallA, K.NthRoot, K.Left, K.Backspace, K.Decimal), + T(@".\left( \square \right) ", K.SmallA, K.BothRoundBrackets, K.Left, K.Backspace, K.Decimal), + T(@".^{\square }", K.SmallA, K.Power, K.Left, K.Backspace, K.Decimal), + T(@"._{\square }", K.SmallA, K.Subscript, K.Left, K.Backspace, K.Decimal), + T(@".", K.Fraction, K.Right, K.Backspace, K.Decimal), + T(@".", K.SquareRoot, K.Right, K.Backspace, K.Decimal), + T(@".", K.NthRoot, K.Right, K.Backspace, K.Decimal), + T(@".", K.BothRoundBrackets, K.Right, K.Backspace, K.Decimal), + T(@".", K.Power, K.Right, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.Fraction, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.SquareRoot, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.NthRoot, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.BothRoundBrackets, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Power, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Subscript, K.Right, K.Backspace, K.Decimal), + T(@".", K.Fraction, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SquareRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.NthRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.BothRoundBrackets, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.Power, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.Subscript, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.Fraction, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.SquareRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.NthRoot, K.Right, K.Right, K.Backspace, K.Decimal), + T(@"a.", K.SmallA, K.BothRoundBrackets, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Power, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".", K.SmallA, K.Subscript, K.Right, K.Right, K.Backspace, K.Decimal), + T(@".bcd", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.BothRoundBrackets, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".bcd", K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.SquareRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.BothRoundBrackets, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Power, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"a.bcd", K.SmallA, K.Subscript, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"bcd.", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.Backspace, K.Decimal), + T(@"bcd.", K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.", K.NthRoot, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.Fraction, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.", K.SmallA, K.NthRoot, K.Right, K.SmallB, K.SmallC, K.SmallD, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.efg", K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"bcd.efg", K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.efg", K.SmallA, K.Fraction, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"abcd.efg", K.SmallA, K.NthRoot, K.SmallB, K.SmallC, K.SmallD, K.Right, K.SmallE, K.SmallF, K.SmallG, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456", K.Subscript, K.D4, K.D5, K.D6, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".789", K.Power, K.D7, K.D8, K.D9, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456789", K.Subscript, K.D4, K.D5, K.D6, K.Right, K.Power, K.D7, K.D8, K.D9, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@".456789", K.Power, K.D7, K.D8, K.D9, K.Right, K.Subscript, K.D4, K.D5, K.D6, K.Left, K.Left, K.Left, K.Left, K.Backspace, K.Decimal), + T(@"\square ^{\square }", K.Power, K.Right, K.Subscript, K.Backspace), + T(@"\square _{\square }", K.Subscript, K.Right, K.Power, K.Backspace), + T(@"X^{\square }", K.X, K.Power, K.Right, K.Subscript, K.Backspace), + T(@"X_{\square }", K.X, K.Subscript, K.Right, K.Power, K.Backspace), + T(@"\square ^Z", K.Power, K.Z, K.Right, K.Subscript, K.Backspace), + T(@"\square _{\square }Z", K.Subscript, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"X^Z", K.X, K.Power, K.Z, K.Right, K.Subscript, K.Backspace), + T(@"X_{\square }Z", K.X, K.Subscript, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"\square ^{\square }Y", K.Power, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"\square _Y", K.Subscript, K.Y, K.Right, K.Power, K.Backspace), + T(@"X^{\square }Y", K.X, K.Power, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"X_Y", K.X, K.Subscript, K.Y, K.Right, K.Power, K.Backspace), + T(@"\square ^ZY", K.Power, K.Z, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"\square _YZ", K.Subscript, K.Y, K.Right, K.Power, K.Z, K.Left, K.Backspace), + T(@"X^ZY", K.X, K.Power, K.Z, K.Right, K.Subscript, K.Y, K.Left, K.Backspace), + T(@"X_YZ", K.X, K.Subscript, K.Y, K.Right, K.Power, K.Z, K.Left, K.Backspace), T(@"\frac{\square }{3}", K.Slash, K.D3, K.Left, K.Left, K.Backspace, K.Left), T(@"1_3", K.D1, K.D2, K.Subscript, K.D3, K.Left, K.Left, K.Backspace), T(@"1_3^2", K.D1, K.D4, K.Subscript, K.D3, K.Left, K.Left, K.Power, K.D2, K.Left, K.Left, K.Left, K.Left, K.Backspace), T(@"1_2^3", K.D1, K.D4, K.Power, K.D3, K.Left, K.Left, K.Subscript, K.D2, K.Left, K.Left, K.Backspace), - T(@"■^6", K.Power, K.D6, K.Left, K.Left, K.Left, K.X, K.Left, K.Left, K.Left, K.Backspace), + T(@"■^6", K.Power, K.D6, K.Left, K.Left, K.Left, K.X, K.Left, K.Left, K.Right, K.Backspace), + T(@"■_6", K.Subscript, K.D6, K.Left, K.Left, K.Left, K.X, K.Left, K.Left, K.Right, K.Backspace), T(@"\sqrt[■]{\square }", K.NthRoot, K.SmallA, K.Backspace), T(@"\sqrt{■}", K.SquareRoot, K.SmallA, K.Backspace), T(@"\frac{1}{■}", K.Slash, K.D6, K.Backspace), - T(@"■_5", K.Subscript, K.D5, K.Left, K.Left, K.Backspace, K.X, K.Left, K.Left, K.Left, K.Backspace), T(@"7+1^X", K.D7, K.Plus, K.D1, K.D2, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7.^X", K.D7, K.Decimal, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), T(@"7+■^X", K.D7, K.Plus, K.D1, K.Power, K.X, K.Left, K.Left, K.Backspace), diff --git a/CSharpMath.Editor/Extensions/MathList.cs b/CSharpMath.Editor/Extensions/MathList.cs index 050f0870..d9d009ba 100644 --- a/CSharpMath.Editor/Extensions/MathList.cs +++ b/CSharpMath.Editor/Extensions/MathList.cs @@ -4,27 +4,39 @@ namespace CSharpMath.Editor { using Atom; using Atoms = Atom.Atoms; using Structures; + using System.Linq; + partial class Extensions { static void InsertAtAtomIndexAndAdvance(this MathList self, int atomIndex, MathAtom atom, ref MathListIndex advance, MathListSubIndexType advanceType) { if (atomIndex < 0 || atomIndex > self.Count) - throw new IndexOutOfRangeException($"Index {atomIndex} is out of bounds for list of size {self.Atoms.Count}"); + throw new IndexOutOfRangeException($"Insertion index {atomIndex} is out of bounds for list of size {self.Atoms.Count}"); // Test for placeholder to the right of index, e.g. \sqrt{‸■} -> \sqrt{2‸} if (atomIndex < self.Count && self[atomIndex] is Atoms.Placeholder placeholder) { atom.Superscript.Append(placeholder.Superscript); atom.Subscript.Append(placeholder.Subscript); self[atomIndex] = atom; - } else self.Insert(atomIndex, atom); - advance = advanceType switch - { - MathListSubIndexType.None => advance.Next, - _ => advance.LevelUpWithSubIndex(advanceType, MathListIndex.Level0Index(0)), - }; + advance = advanceType switch + { + MathListSubIndexType.None => + atom.Superscript.IsEmpty() && atom.Subscript.IsEmpty() + ? advance.Next + : advance.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)), + _ => advance.LevelUpWithSubIndex(advanceType, MathListIndex.Level0Index(0)), + }; + } else { + self.Insert(atomIndex, atom); + advance = advanceType switch + { + MathListSubIndexType.None => advance.Next, + _ => advance.LevelUpWithSubIndex(advanceType, MathListIndex.Level0Index(0)), + }; + } } /// Inserts and modifies to advance to the next position. public static void InsertAndAdvance(this MathList self, ref MathListIndex index, MathAtom atom, MathListSubIndexType advanceType) { index ??= MathListIndex.Level0Index(0); - if (index.AtomIndex > self.Atoms.Count) - throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); + if (index.AtomIndex < 0 || index.AtomIndex > self.Atoms.Count) + throw new IndexOutOfRangeException($"Insertion index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); switch (index.SubIndexType) { case MathListSubIndexType.None: self.InsertAtAtomIndexAndAdvance(index.AtomIndex, atom, ref index, advanceType); @@ -81,12 +93,66 @@ public static void InsertAndAdvance(this MathList self, ref MathListIndex index, } public static void RemoveAt(this MathList self, ref MathListIndex index) { - index ??= MathListIndex.Level0Index(0); - if (index.AtomIndex > self.Atoms.Count) - throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); + void RemoveAtInnerList(ref MathListIndex index, TAtom atom, int innerListIndex) where TAtom : MathAtom, IMathListContainer { + if (index.SubIndex is null) throw new InvalidCodePathException($"{nameof(index.SubIndex)} should exist"); + if (index.IsBeforeSubList) { + index = index.LevelDown() + ?? throw new InvalidCodePathException($"{nameof(index.SubIndex)} is not null but {nameof(index.LevelDown)} is null"); + self.RemoveAt(ref index); + MathListIndex tempIndex = index; + int i = 0; + foreach (var innerList in atom.InnerLists) + if (!(innerList.Count == 1 && innerList[0] is Atoms.Placeholder)) + if (i++ < innerListIndex) { + foreach (var inner in innerList) + self.InsertAndAdvance(ref index, inner, MathListSubIndexType.None); + tempIndex = index; + } + else + foreach (var inner in innerList) + self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); + if(index.SubIndexType != MathListSubIndexType.None && tempIndex.AtomIndex == 0 // We deleted an atom only consisting of placeholders + || atom.Superscript.Count > 0 || atom.Subscript.Count > 0) + self.InsertAndAdvance(ref tempIndex, LaTeXSettings.Placeholder, MathListSubIndexType.None); + if(atom.Superscript.Count > 0) self[tempIndex.AtomIndex - 1].Superscript.Append(atom.Superscript); + if(atom.Subscript.Count > 0) self[tempIndex.AtomIndex - 1].Subscript.Append(atom.Subscript); + } else atom.InnerLists.ElementAt(innerListIndex).RemoveAt(ref index.SubIndex); + } + void RemoveAtInnerScript(ref MathListIndex index, MathAtom atom, bool superscript) { + if (index.SubIndex is null) throw new InvalidCodePathException($"{nameof(index.SubIndex)} should exist"); + var script = superscript ? atom.Superscript : atom.Subscript; + if (index.IsBeforeSubList) { + index = index.LevelDown() + ?? throw new InvalidCodePathException($"{nameof(index.SubIndex)} is not null but {nameof(index.LevelDown)} is null"); + if (atom is Atoms.Placeholder && (superscript ? atom.Subscript : atom.Superscript).Count == 0) + self.RemoveAt(index.AtomIndex); + else index = index.Next; + var tempIndex = index; + if (!(script.Count == 1 && script[0] is Atoms.Placeholder)) + foreach (var inner in script) + self.InsertAndAdvance(ref tempIndex, inner, MathListSubIndexType.None); + script.Clear(); + } else script.RemoveAt(ref index.SubIndex); + } + + if (index.AtomIndex < -1 || index.AtomIndex >= self.Atoms.Count) + throw new IndexOutOfRangeException($"Deletion index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}"); switch (index.SubIndexType) { case MathListSubIndexType.None: - self.RemoveAt(index.AtomIndex); + if (index.AtomIndex == -1) { + index = index.Next; + if (self.Atoms[index.AtomIndex] is Atoms.Placeholder { Superscript: var super, Subscript: var sub }) { + self.RemoveAt(index.AtomIndex); + var tempIndex = index; + if (!(sub.Count == 1 && sub[0] is Atoms.Placeholder)) + foreach (var s in sub) + self.InsertAndAdvance(ref tempIndex, s, MathListSubIndexType.None); + if (!(super.Count == 1 && super[0] is Atoms.Placeholder)) + foreach (var s in super) + self.InsertAndAdvance(ref tempIndex, s, MathListSubIndexType.None); + } + } else + self.RemoveAt(index.AtomIndex); break; case var _ when index.SubIndex is null: throw new InvalidCodePathException("index.SubIndex is null despite non-None subindex type"); @@ -132,33 +198,35 @@ public static void RemoveAt(this MathList self, ref MathListIndex index) { if (!(self.Atoms[index.AtomIndex] is Atoms.Radical radical)) throw new SubIndexTypeMismatchException(typeof(Atoms.Radical), index); if (index.SubIndexType == MathListSubIndexType.Degree) - radical.Degree.RemoveAt(ref index.SubIndex); - else radical.Radicand.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, radical, 0); + else + RemoveAtInnerList(ref index, radical, 1); break; case MathListSubIndexType.Numerator: case MathListSubIndexType.Denominator: if (!(self.Atoms[index.AtomIndex] is Atoms.Fraction frac)) throw new SubIndexTypeMismatchException(typeof(Atoms.Fraction), index); if (index.SubIndexType == MathListSubIndexType.Numerator) - frac.Numerator.RemoveAt(ref index.SubIndex); - else frac.Denominator.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, frac, 0); + else + RemoveAtInnerList(ref index, frac, 1); break; case MathListSubIndexType.Subscript: var current = self.Atoms[index.AtomIndex]; if (current.Subscript.IsEmpty()) throw new SubIndexTypeMismatchException(index); - current.Subscript.RemoveAt(ref index.SubIndex); + RemoveAtInnerScript(ref index, current, false); break; case MathListSubIndexType.Superscript: current = self.Atoms[index.AtomIndex]; if (current.Superscript.IsEmpty()) throw new SubIndexTypeMismatchException(index); - current.Superscript.RemoveAt(ref index.SubIndex); + RemoveAtInnerScript(ref index, current, true); break; case MathListSubIndexType.Inner: if (!(self.Atoms[index.AtomIndex] is Atoms.Inner inner)) throw new SubIndexTypeMismatchException(typeof(Atoms.Inner), index); - inner.InnerList.RemoveAt(ref index.SubIndex); + RemoveAtInnerList(ref index, inner, 0); break; default: throw new SubIndexTypeMismatchException(index); @@ -167,7 +235,7 @@ public static void RemoveAt(this MathList self, ref MathListIndex index) { // We have deleted to the beginning of the line and it is not the outermost line if (self.AtomAt(index) is null) { self.InsertAndAdvance(ref index, LaTeXSettings.Placeholder, MathListSubIndexType.None); - index = index.Previous ?? throw new InvalidCodePathException("Cannot go back after insertion?"); ; + index = index.Previous ?? throw new InvalidCodePathException("Cannot go back after insertion?"); } } } @@ -218,7 +286,7 @@ public static void RemoveAtoms(this MathList self, MathListRange? nullableRange) } public static MathAtom? AtomAt(this MathList self, MathListIndex? index) { - if (index is null || index.AtomIndex >= self.Atoms.Count) return null; + if (index is null || index.AtomIndex < 0 || index.AtomIndex >= self.Atoms.Count) return null; var atom = self.Atoms[index.AtomIndex]; switch (index.SubIndexType) { case MathListSubIndexType.None: diff --git a/CSharpMath.Editor/MathKeyboard.cs b/CSharpMath.Editor/MathKeyboard.cs index efb3a007..cd0cf430 100644 --- a/CSharpMath.Editor/MathKeyboard.cs +++ b/CSharpMath.Editor/MathKeyboard.cs @@ -61,7 +61,7 @@ public MathKeyboardCaretState CaretState { blinkTimer.Stop(); blinkTimer.Start(); if (value != MathKeyboardCaretState.Hidden && - MathList.AtomAt(_insertionIndex) is Atoms.Placeholder placeholder) + MathList.AtomAt(_insertionIndex) is Atoms.Placeholder placeholder) (placeholder.Nucleus, _caretState) = value == MathKeyboardCaretState.TemporarilyHidden ? ("\u25A1", MathKeyboardCaretState.TemporarilyHidden) @@ -87,7 +87,7 @@ public MathListIndex InsertionIndex { public LineStyle LineStyle { get; set; } public Structures.Color SelectColor { get; set; } public virtual RectangleF Measure => Display?.DisplayBounds() ?? RectangleF.Empty; - public bool HasText => MathList?.Atoms?.Count > 0; + public bool HasText => MathList.Atoms.Count > 0; public void RecreateDisplayFromMathList() { var position = Display?.Position ?? default; Display = Typesetter.CreateLine(MathList, Font, Context, LineStyle); @@ -279,8 +279,6 @@ void MoveCursorLeft() { _insertionIndex = prev; break; } - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); if (_insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts) { var prevInd = _insertionIndex.LevelDown(); if (prevInd != null && MathList.AtomAt(prevInd) is Atoms.Placeholder) @@ -292,8 +290,6 @@ void MoveCursorLeft() { } } void MoveCursorRight() { - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); switch (MathList.AtomAt(_insertionIndex)) { case null: //After Count var levelDown = _insertionIndex.LevelDown(); @@ -380,16 +376,159 @@ void MoveCursorRight() { _insertionIndex = _insertionIndex.Next; break; } - if (_insertionIndex is null) - throw new InvalidOperationException($"{nameof(_insertionIndex)} is null."); if (_insertionIndex.FinalSubIndexType is MathListSubIndexType.BetweenBaseAndScripts && MathList.AtomAt(_insertionIndex.LevelDown()) is Atoms.Placeholder) MoveCursorRight(); } - + void MoveCursorUp() { + if (Display is null) RecreateDisplayFromMathList(); + if (MathList.AtomAt(_insertionIndex) is Atoms.Placeholder { Superscript: { Count: var superCount } } && superCount > 0) { + _insertionIndex = _insertionIndex.LevelUpWithSubIndex(MathListSubIndexType.Superscript, MathListIndex.Level0Index(0)); + return; + } + if (_insertionIndex.Previous is { } prev && MathList.AtomAt(prev) is { Superscript: var super } && super.Count > 0) { + _insertionIndex = prev.LevelUpWithSubIndex(MathListSubIndexType.Superscript, MathListIndex.Level0Index(super.Count)); + if (_insertionIndex.Previous is { } prev2 && MathList.AtomAt(prev2) is Atoms.Placeholder p && p.Superscript.IsEmpty() && p.Subscript.IsEmpty()) + _insertionIndex = prev2; + return; + } + for (MathListIndex? verticalIndex = _insertionIndex; verticalIndex != null; verticalIndex = verticalIndex.LevelDown()) { + switch (verticalIndex.FinalSubIndexType) { + case MathListSubIndexType.Denominator: + var numerator = + verticalIndex.LevelDown()?.LevelUpWithSubIndex(MathListSubIndexType.Numerator, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + var x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var y = + ClosestPointToIndex(numerator)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + return; + case MathListSubIndexType.Subscript: + var levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + if (MathList.AtomAt(levelDown) is { Superscript: { Count: 0 } } atom) { + var left = + atom is Atoms.Placeholder + ? levelDown + : levelDown.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)); + var leftX = + ClosestPointToIndex(left)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var right = levelDown.Next; + var rightX = + ClosestPointToIndex(right)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = x - leftX <= rightX - x ? left : right; + } else { + var superscript = + levelDown?.LevelUpWithSubIndex(MathListSubIndexType.Superscript, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + y = + ClosestPointToIndex(superscript)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + } + return; + case MathListSubIndexType.BetweenBaseAndScripts: + levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + if (MathList.AtomAt(levelDown)?.Superscript.IsNonEmpty() + ?? throw new InvalidCodePathException(nameof(levelDown) + " is invalid for " + nameof(MathList))) { + _insertionIndex = levelDown.LevelUpWithSubIndex(MathListSubIndexType.Superscript, MathListIndex.Level0Index(0)); + return; + } + break; + } + } + } + void MoveCursorDown() { + if (Display is null) RecreateDisplayFromMathList(); + if (MathList.AtomAt(_insertionIndex) is Atoms.Placeholder { Subscript: { Count: var subCount } } && subCount > 0) { + _insertionIndex = _insertionIndex.LevelUpWithSubIndex(MathListSubIndexType.Subscript, MathListIndex.Level0Index(0)); + return; + } + if (_insertionIndex.Previous is { } prev && MathList.AtomAt(prev) is { Subscript: var sub } && sub.Count > 0) { + _insertionIndex = prev.LevelUpWithSubIndex(MathListSubIndexType.Subscript, MathListIndex.Level0Index(sub.Count)); + if (_insertionIndex.Previous is { } prev2 && MathList.AtomAt(prev2) is Atoms.Placeholder p && p.Superscript.IsEmpty() && p.Subscript.IsEmpty()) + _insertionIndex = prev2; + return; + } + for (MathListIndex? verticalIndex = _insertionIndex; verticalIndex != null; verticalIndex = verticalIndex.LevelDown()) { + switch (verticalIndex.FinalSubIndexType) { + case MathListSubIndexType.Numerator: + var denominator = + verticalIndex.LevelDown()?.LevelUpWithSubIndex(MathListSubIndexType.Denominator, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + var x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var y = + ClosestPointToIndex(denominator)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + return; + case MathListSubIndexType.Superscript: + var levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + x = + ClosestPointToIndex(_insertionIndex)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + if (MathList.AtomAt(levelDown) is { Subscript: { Count: 0 } } atom) { + var left = + atom is Atoms.Placeholder + ? levelDown + : levelDown.LevelUpWithSubIndex(MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1)); + var leftX = + ClosestPointToIndex(left)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + var right = levelDown.Next; + var rightX = + ClosestPointToIndex(right)?.X + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = x - leftX <= rightX - x ? left : right; + } else { + var subscript = + levelDown?.LevelUpWithSubIndex(MathListSubIndexType.Subscript, MathListIndex.Level0Index(0)) + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + y = + ClosestPointToIndex(subscript)?.Y + ?? throw new InvalidCodePathException("Null closest point despite valid " + nameof(MathListIndex)); + _insertionIndex = + ClosestIndexToPoint(new PointF(x, y)) + ?? throw new InvalidCodePathException("Null closest index despite valid point"); + } + return; + case MathListSubIndexType.BetweenBaseAndScripts: + levelDown = + verticalIndex.LevelDown() + ?? throw new InvalidCodePathException("Null levelDown despite non-None " + nameof(verticalIndex.FinalSubIndexType)); + if (MathList.AtomAt(levelDown)?.Subscript.IsNonEmpty() + ?? throw new InvalidCodePathException(nameof(levelDown) + " is invalid for " + nameof(MathList))) { + _insertionIndex = levelDown.LevelUpWithSubIndex(MathListSubIndexType.Subscript, MathListIndex.Level0Index(0)); + return; + } + break; + } + } + } void DeleteBackwards() { // delete the last atom from the list - if (HasText && _insertionIndex.Previous is MathListIndex previous) { + if (HasText && _insertionIndex.PreviousOrBeforeWholeList is MathListIndex previous) { _insertionIndex = previous; MathList.RemoveAt(ref _insertionIndex); } @@ -430,10 +569,11 @@ void InsertSymbolName(string name, bool subscript = false, bool superscript = fa } switch (input) { -#warning Unimplemented up/down buttons case MathKeyboardInput.Up: + MoveCursorUp(); break; case MathKeyboardInput.Down: + MoveCursorDown(); break; case MathKeyboardInput.Left: MoveCursorLeft(); @@ -491,7 +631,7 @@ void InsertSymbolName(string name, bool subscript = false, bool superscript = fa break; case MathKeyboardInput.BaseEPower: InsertAtom(LaTeXSettings.ForAscii((sbyte)'e') - ?? throw new InvalidCodePathException("LaTeXDefaults.ForAscii((byte)'e') is null")); + ?? throw new InvalidCodePathException($"{nameof(LaTeXSettings.ForAscii)}((sbyte)'e') is null")); HandleScriptButton(true); break; case MathKeyboardInput.Logarithm: diff --git a/CSharpMath.Editor/MathListIndex.cs b/CSharpMath.Editor/MathListIndex.cs index cb880cd3..4c6977b5 100644 --- a/CSharpMath.Editor/MathListIndex.cs +++ b/CSharpMath.Editor/MathListIndex.cs @@ -80,6 +80,14 @@ public MathListIndex LevelUpWithSubIndex(MathListSubIndexType type, MathListInde MathListSubIndexType.None => AtomIndex > 0 ? Level0Index(AtomIndex - 1) : null, _ => SubIndex?.Previous is MathListIndex prevSubIndex ? IndexAtLocation(AtomIndex, SubIndexType, prevSubIndex) : null, }; + /// Should be used inside only! + internal bool IsBeforeSubList => SubIndex is { AtomIndex: -1, SubIndexType: MathListSubIndexType.None }; + /// Valid for only! + internal MathListIndex? PreviousOrBeforeWholeList => SubIndexType switch + { + MathListSubIndexType.None => AtomIndex > -1 ? Level0Index(AtomIndex - 1) : null, + _ => SubIndex?.PreviousOrBeforeWholeList is MathListIndex prevSubIndex ? IndexAtLocation(AtomIndex, SubIndexType, prevSubIndex) : null, + }; ///Returns the next index. public MathListIndex Next => SubIndexType switch diff --git a/CSharpMath.Evaluation/Interpret.cs b/CSharpMath.Evaluation/Interpret.cs index 6846b38b..642c8ac4 100644 --- a/CSharpMath.Evaluation/Interpret.cs +++ b/CSharpMath.Evaluation/Interpret.cs @@ -29,7 +29,7 @@ public static string Interpret(Atom.MathList mathList, System.Func // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml b/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml index d7538da6..2389f7af 100644 --- a/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml +++ b/CSharpMath.Forms.Example/CSharpMath.Forms.Example/Controls/MathKeyboard.xaml @@ -21,7 +21,7 @@