public static float XOffsetForGlyphIndex <TFont, TGlyph> (this AttributedGlyphRun <TFont, TGlyph> line, TypesettingContext <TFont, TGlyph> context, int index) where TFont : IFont <TGlyph> { if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), index, "The index is negative."); } int i = 0; float x = 0; var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs, line.Length).Advances; foreach (var(advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create)) { if (i++ >= index) { return(x); } else { x += advance + kernAfter; } } throw new ArgumentOutOfRangeException(nameof(index), index, "The index is beyond the end of the string."); }
public static MathListIndex?IndexForPoint <TFont, TGlyph> (this TextLineDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // Convert the point to the reference of the CTLine var relativePoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y); var runsAndIndicies = self.Runs .Select(run => (run, run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X))) .Where(x => x.Item2.HasValue) .ToArray(); if (runsAndIndicies.Length == 0) { return(null); } var(r, nindex) = runsAndIndicies.Single(); var index = nindex.GetValueOrDefault(); var diffLng = r.Run.Length != r.Range.Length; if (index < 0 || (!diffLng && index > self.Range.Length) || (diffLng && index > r.Run.Length)) { throw new InvalidCodePathException ($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})"); } return(diffLng ? index > r.Run.Length / 2 ? MathListIndex.Level0Index(self.Range.End) : MathListIndex.Level0Index(self.Range.Location) : MathListIndex.Level0Index(self.Range.Location + index)); }
public static MathListIndex IndexForPoint <TFont, TGlyph>( this IGlyphDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> _, PointF point) where TFont : IFont <TGlyph> => point.X > self.Position.X + self.Width / 2 ? MathListIndex.Level0Index(self.Range.End) : MathListIndex.Level0Index(self.Range.Location);
public AppleMathView(TypesettingContext <TFont, TGlyph> typesettingContext, float fontSize) { Layer.GeometryFlipped = true; BackgroundColor = NColor.FromRGB(0.9f, 0.9f, 0.9f); TextColor = NColor.Black; FontSize = fontSize; _typesettingContext = typesettingContext; }
public TextLineDisplay( AttributedString <TFont, TGlyph> text, Range range, TypesettingContext <TFont, TGlyph> context, IReadOnlyList <MathAtom> atoms, PointF position) : this( text.Runs.Select(run => new TextRunDisplay <TFont, TGlyph>(run, new Range(range.Location, run.Length), context) ).ToList(), atoms, position) { }
public static PointF?PointForIndex <TFont, TGlyph>( this IGlyphDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> _, MathListIndex index) where TFont : IFont <TGlyph> => index.SubIndexType != MathListSubIndexType.None ? throw new ArgumentException ("The subindex must be none to get the closest point for it.", nameof(index)) : index.AtomIndex == self.Range.End // draw a caret after the glyph ? self.Position.Plus(new PointF(self.DisplayBounds().Right, 0)) // draw a caret before the glyph : self.Position;
public MathKeyboard(TypesettingContext <TFont, TGlyph> context, TFont font, double blinkMilliseconds = DefaultBlinkMilliseconds) { Context = context; Font = font; blinkTimer = new Timer(blinkMilliseconds); blinkTimer.Elapsed += (sender, e) => { if (!(MathList.AtomAt(_insertionIndex) is Atoms.Placeholder) || LaTeXSettings.PlaceholderBlinks) { InsertionPositionHighlighted = !InsertionPositionHighlighted; } }; blinkTimer.Start(); }
public static MathListIndex IndexForPoint <TFont, TGlyph>( this LargeOpLimitsDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> => // We can be before or after the large operator point.X <self.Position.X - PixelDelta // We are before the large operator, so ?MathListIndex.Level0Index(self.Range.Location) : point.X> self.Position.X + self.Width + PixelDelta // We are after the large operator ? MathListIndex.Level0Index(self.Range.End) : self.UpperLimit is {
public TextRunDisplay( AttributedGlyphRun <TFont, TGlyph> run, Range range, TypesettingContext <TFont, TGlyph> context) { var font = run.Font; Run = run; Range = range; Width = context.GlyphBoundsProvider.GetTypographicWidth(font, run); _ComputeAscentDescent(context, font); }
public static MathListIndex IndexForPoint <TFont, TGlyph>( this InnerDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> => // We can be before or after the inner point.X <self.Position.X + (self.Left?.Width / 2 ?? 0) //We are before the inner, so ? MathListIndex.Level0Index(self.Range.Location) : point.X> self.Position.X + self.Width - (self.Right?.Width / 2 ?? 0) //We are after the inner ? MathListIndex.Level0Index(self.Range.End) : MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Inner, self.Inner.IndexForPoint(context, point));
public TextRunDisplay( AttributedGlyphRun <TFont, TGlyph> run, Range range, TypesettingContext <TFont, TGlyph> context) { var font = run.Font; Run = run; Range = range; Width = context.GlyphBoundsProvider.GetTypographicWidth(font, run); // Compute ascent and descent var rects = context.GlyphBoundsProvider.GetBoundingRectsForGlyphs(font, Run.Glyphs, Run.GlyphInfos.Count); Ascent = rects.IsEmpty() ? 0 : rects.Max(rect => rect.Bottom); // Convert to non-flipped naming here, Descent = rects.IsEmpty() ? 0 : rects.Max(rect => - rect.Y); }
public static MathListIndex IndexForPoint <TFont, TGlyph>( this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> => // We can be before or after the radical point.X <self.Position.X - PixelDelta //We are before the radical, so ?MathListIndex.Level0Index(self.Range.Location) : point.X> self.Position.X + self.Width + PixelDelta //We are after the radical ? MathListIndex.Level0Index(self.Range.End) //We can be either near the degree or the radicand : DistanceFromPointToRect(point, self.Degree != null ? new RectangleF(self.Degree.Position, self.Degree.DisplayBounds().Size) : default) < DistanceFromPointToRect(point, new RectangleF(self.Radicand.Position, self.Radicand.DisplayBounds().Size)) ? self.Degree != null ? MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Degree, self.Degree.IndexForPoint(context, point)) : MathListIndex.Level0Index(self.Range.Location) : MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Radicand, self.Radicand.IndexForPoint(context, point));
private void _ComputeAscentDescent(TypesettingContext <TFont, TGlyph> context, TFont font) { var rects = context.GlyphBoundsProvider.GetBoundingRectsForGlyphs(font, Run.Glyphs.AsForEach(), Run.GlyphInfos.Count); var tops = rects.Select(rect => rect.Bottom); // Convert to non-flipped naming here, var bottoms = rects.Select(rect => rect.Y); float ascent = 0; float descent = 0; foreach (var top in tops) { ascent = Math.Max(ascent, top); } foreach (var bottom in bottoms) { descent = Math.Max(descent, -bottom); } Ascent = ascent; Descent = descent; }
public static MathListIndex IndexForPoint <TFont, TGlyph>( this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> => // We can be before or after the fraction point.X <self.Position.X - PixelDelta //We are before the fraction, so ?MathListIndex.Level0Index(self.Range.Location) : point.X> self.Position.X + self.Width + PixelDelta //We are after the fraction ? MathListIndex.Level0Index(self.Range.End) : point.Y > self.LinePosition + PixelDelta ? MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Numerator, self.Numerator.IndexForPoint(context, point)) : point.Y <self.LinePosition - PixelDelta ?MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Denominator, self.Denominator.IndexForPoint(context, point)) : point.X> self.Position.X + self.Width / 2 ? MathListIndex.Level0Index(self.Range.End) : MathListIndex.Level0Index(self.Range.Location);
public MathKeyboard(TypesettingContext <TFont, TGlyph> context, TFont font, double blinkMilliseconds = DefaultBlinkMilliseconds) { Context = context; Font = font; blinkTimer = new Timer(blinkMilliseconds); blinkTimer.Elapsed += (sender, e) => { switch (CaretState) { case MathKeyboardCaretState.Shown: case MathKeyboardCaretState.ShownThroughPlaceholder: CaretState = MathKeyboardCaretState.TemporarilyHidden; break; case MathKeyboardCaretState.TemporarilyHidden: CaretState = MathKeyboardCaretState.Shown; break; } }; blinkTimer.Start(); }
public static TextLineDisplay <TFont, TGlyph> Create <TFont, TGlyph>( AttributedString <TFont, TGlyph> text, Range range, TypesettingContext <TFont, TGlyph> context, IEnumerable <IMathAtom> atoms ) where TFont : MathFont <TGlyph> { int index = range.Location; List <TextRunDisplay <TFont, TGlyph> > textRuns = new List <TextRunDisplay <TFont, TGlyph> >(); foreach (var run in text.Runs) { var innerRange = new Range(index, run.Length); var textRun = new TextRunDisplay <TFont, TGlyph>( run, innerRange, context ); textRuns.Add(textRun); } return(new TextLineDisplay <TFont, TGlyph>(textRuns, atoms)); }
public static int?GlyphIndexForXOffset <TFont, TGlyph> (this AttributedGlyphRun <TFont, TGlyph> line, TypesettingContext <TFont, TGlyph> context, float offset) where TFont : IFont <TGlyph> { if (offset < 0) { return(0); // Move cursor to index 0 } if (line.Placeholder) { return(0); } int i = 0; float x = 0; var advances = context.GlyphBoundsProvider.GetAdvancesForGlyphs(line.Font, line.Glyphs, line.Length).Advances; foreach (var(advance, kernAfter) in advances.Zip(line.GlyphInfos.Select(g => g.KernAfterGlyph), ValueTuple.Create)) { if (x <= offset && offset < advance + x) { return(Math.Abs(offset - x) < Math.Abs(advance + x - offset) ? i : i + 1); } else { x += advance + kernAfter; i++; if (offset < x) // If the point is in the kern after this, then the index is the one after this { return(i); } } } return(i); }
public static PointF?PointForIndex <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph> { if (index is null) { return(null); } PointF?position; if (index.AtomIndex == self.Range.End) { // Special case the edge of the range position = new PointF(self.Width, 0); } else if (self.Range.Contains(index.AtomIndex) && self.SubDisplayForIndex(index) is IDisplay <TFont, TGlyph> display) { switch (index.SubIndexType) { case MathListSubIndexType.BetweenBaseAndScripts: var nucleusPosition = index.AtomIndex + index.SubIndex.AtomIndex; position = display.PointForIndex(context, MathListIndex.Level0Index(nucleusPosition)); break; case MathListSubIndexType.None: position = display.PointForIndex(context, index); break; default: // Recurse position = display.PointForIndex(context, index.SubIndex); break; } } else { // Outside the range return(null); } if (position is PointF found) { // Convert bounds from our coordinate system before returning found.X += self.Position.X; found.Y += self.Position.Y; return(found); } else { // We didn't find the position return(null); } }
public MathKeyboard(TypesettingContext <TFont, TGlyph> context) => Context = context;
public static PointF?PointForIndex <TFont, TGlyph>(this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph> { if (index.SubIndexType != MathListSubIndexType.None) { throw Arg("The subindex must be none to get the closest point for it.", nameof(index)); } // draw a caret after the fraction return(new PointF(self.DisplayBounds.Right, self.Position.Y)); }
public static PointF?PointForIndex <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph> { if (index.SubIndexType != MathListSubIndexType.None) { throw Arg("The subindex must be none to get the closest point for it.", nameof(index)); } if (index.AtomIndex == self.Range.End) { // draw a caret after the radical return(self.Position.Plus(new PointF(self.DisplayBounds.Right, 0))); } // draw a caret before the radical return(self.Position); }
public static MathListIndex?IndexForPoint <TFont, TGlyph> (this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // The origin of for the subelements of a MathList is the current position, // so translate the current point to our origin. var translatedPoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y); IDisplay <TFont, TGlyph>?closest = null; var xbounds = new List <IDisplay <TFont, TGlyph> >(); float minDistance = float.MaxValue; foreach (var display in self.Displays) { var bounds = display.DisplayBounds(); var rect = new RectangleF(display.Position, bounds.Size); var maxBoundsX = rect.Right; if (rect.X - PixelDelta <= translatedPoint.X && translatedPoint.X <= maxBoundsX + PixelDelta) { xbounds.Add(display); } var distance = DistanceFromPointToRect(translatedPoint, rect); if (distance < minDistance) { closest = display; minDistance = distance; } } IDisplay <TFont, TGlyph>?displayWithPoint; switch (xbounds.Count) { case 0: if (translatedPoint.X <= -PixelDelta) { // All the way to the left return(self.Range.Location < 0 ? null : MathListIndex.Level0Index(self.Range.Location)); } else if (translatedPoint.X >= self.Width + PixelDelta) { // if closest is a script if (closest is ListDisplay <TFont, TGlyph> ld && ld.LinePosition != LinePosition.Regular) { // then we try to find its parent var parent = self.Displays.FirstOrDefault(d => d.HasScript && d.Range.Contains(ld.IndexInParent)); if (parent != null) { return(MathListIndex.Level0Index(parent.Range.End)); } } // All the way to the right return (self.Range.End < 0 ? null : self.Displays.Count == 1 && self.Displays[0] is TextLineDisplay <TFont, TGlyph> { Atoms : var atoms } && atoms.Count == 1 && atoms[0] is Atom.Atoms.Placeholder ? MathListIndex.Level0Index(self.Range.Location) : MathListIndex.Level0Index(self.Range.End)); }
public static MathListIndex IndexForPoint <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // We can be before or after the radical if (point.X < self.Position.X - PixelDelta) { //We are before the radical, so return(MathListIndex.Level0Index(self.Range.Location)); } else if (point.X > self.Position.X + self.Width + PixelDelta) { //We are after the radical return(MathListIndex.Level0Index(self.Range.End)); } //We can be either near the degree or the radicand var degreeDistance = DistanceFromPointToRect(point, self.Degree?.DisplayBounds ?? default); var radicandDistance = DistanceFromPointToRect(point, self.Radicand.DisplayBounds); if (degreeDistance < radicandDistance) { return(MathListIndex.IndexAtLocation(self.Range.Location, self.Degree.IndexForPoint(context, point), MathListSubIndexType.Numerator)); } else { return(MathListIndex.IndexAtLocation(self.Range.Location, self.Radicand.IndexForPoint(context, point), MathListSubIndexType.Denominator)); } }
public static MathListIndex IndexForPoint <TFont, TGlyph>(this FractionDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // We can be before or after the fraction if (point.X < self.Position.X - PixelDelta) { //We are before the fraction, so return(MathListIndex.Level0Index(self.Range.Location)); } else if (point.X > self.Position.X + self.Width + PixelDelta) { //We are after the fraction return(MathListIndex.Level0Index(self.Range.End)); } //We can be either near the numerator or denominator var numeratorDistance = DistanceFromPointToRect(point, self.Numerator.DisplayBounds); var denominatorDistance = DistanceFromPointToRect(point, self.Denominator.DisplayBounds); if (numeratorDistance < denominatorDistance) { return(MathListIndex.IndexAtLocation(self.Range.Location, self.Numerator.IndexForPoint(context, point), MathListSubIndexType.Numerator)); } else { return(MathListIndex.IndexAtLocation(self.Range.Location, self.Denominator.IndexForPoint(context, point), MathListSubIndexType.Denominator)); } }
public static MathListIndex IndexForPoint <TFont, TGlyph>(this RadicalDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // We can be before or after the radical if (point.X < self.Position.X - PixelDelta) { //We are before the radical, so return(MathListIndex.Level0Index(self.Range.Location)); } else if (point.X > self.Position.X + self.Width + PixelDelta) { //We are after the radical return(MathListIndex.Level0Index(self.Range.End)); } //We can be either near the degree or the radicand var degreeRect = self.Degree != null ? new RectangleF(self.Degree.Position, self.Degree.DisplayBounds.Size) : default; var radicandRect = new RectangleF(self.Radicand.Position, self.Radicand.DisplayBounds.Size); var degreeDistance = DistanceFromPointToRect(point, degreeRect); var radicandDistance = DistanceFromPointToRect(point, radicandRect); if (degreeDistance < radicandDistance) { if (self.Degree != null) { return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Degree, self.Degree.IndexForPoint(context, point))); } return(MathListIndex.Level0Index(self.Range.Location)); } else { return(MathListIndex.IndexAtLocation(self.Range.Location, MathListSubIndexType.Radicand, self.Radicand.IndexForPoint(context, point))); } }
public static MathListIndex IndexForPoint <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // The origin of for the subelements of a MathList is the current position, so translate the current point to our origin. var translatedPoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y); IDisplay <TFont, TGlyph> closest = null; var xbounds = new List <IDisplay <TFont, TGlyph> >(); float minDistance = float.MaxValue; foreach (var display in self.Displays) { var bounds = display.DisplayBounds; var rect = new RectangleF(display.Position, bounds.Size); var maxBoundsX = rect.Right; if (rect.X - PixelDelta <= translatedPoint.X && translatedPoint.X <= maxBoundsX + PixelDelta) { xbounds.Add(display); } var distance = DistanceFromPointToRect(translatedPoint, rect); if (distance < minDistance) { closest = display; minDistance = distance; } } IDisplay <TFont, TGlyph> displayWithPoint; switch (xbounds.Count) { case 0: if (translatedPoint.X <= -PixelDelta) { // All the way to the left return(self.Range.Location < 0 ? null : MathListIndex.Level0Index(self.Range.Location)); } else if (translatedPoint.X >= self.Width + PixelDelta) { // if closest is a script if (closest != null && closest is ListDisplay <TFont, TGlyph> ld && ld.LinePosition != Enumerations.LinePosition.Regular) { // then we try to find its parent var parent = self.Displays.FirstOrDefault(d => d.HasScript && d.Range.Contains(ld.IndexInParent)); if (parent != null) { return(MathListIndex.Level0Index(parent.Range.End)); } } // All the way to the right return(self.Range.End < 0 ? null : MathListIndex.Level0Index(self.Range.End)); } else { // It is within the ListDisplay but not within the X bounds of any sublist. Use the closest in that case. displayWithPoint = closest; } break; case 1: displayWithPoint = xbounds[0]; var rect = new RectangleF(displayWithPoint.Position, displayWithPoint.DisplayBounds.Size); if (translatedPoint.X >= self.Width - PixelDelta) { //The point is close to the end. Only use the selected X bounds if the Y is within range. if (translatedPoint.Y <= rect.YMin() - PixelDelta) { //The point is less than the Y including the delta. Move the cursor to the end rather than in this atom. return(MathListIndex.Level0Index(self.Range.End)); } } break; default: //Use the closest since there are more than 2 sublists which have this X position. displayWithPoint = closest; break; } if (displayWithPoint is null) { return(null); } var index = displayWithPoint.IndexForPoint(context, translatedPoint); if (displayWithPoint is ListDisplay <TFont, TGlyph> closestLine) { if (closestLine.LinePosition is Enumerations.LinePosition.Regular) { throw Arg($"{nameof(ListDisplay<TFont, TGlyph>)} {nameof(ListDisplay<TFont, TGlyph>.LinePosition)} {nameof(Enumerations.LinePosition.Regular)} " + $"inside an {nameof(ListDisplay<TFont, TGlyph>)} - shouldn't happen", nameof(self)); } // This is a subscript or a superscript, return the right type of subindex var indexType = closestLine.LinePosition is Enumerations.LinePosition.Subscript ? MathListSubIndexType.Subscript : MathListSubIndexType.Superscript; // The index of the atom this denotes. if (closestLine.IndexInParent is int.MinValue) { throw Arg($"Index was not set for a {indexType} in the {nameof(ListDisplay<TFont, TGlyph>)}.", nameof(self)); } return(MathListIndex.IndexAtLocation(closestLine.IndexInParent, indexType, index)); } else if (displayWithPoint.HasScript) { //The display list has a subscript or a superscript. If the index is at the end of the atom, then we need to put it before the sub/super script rather than after. if (index?.AtomIndex == displayWithPoint.Range.End) { return(MathListIndex.IndexAtLocation(index.AtomIndex - 1, MathListSubIndexType.BetweenBaseAndScripts, MathListIndex.Level0Index(1))); } } return(index); }
public static MathListIndex IndexForPoint <TFont, TGlyph>(this TextLineDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> { // Convert the point to the reference of the CTLine var relativePoint = new PointF(point.X - self.Position.X, point.Y - self.Position.Y); var indices = self.Runs.Select(run => run.Run.GlyphIndexForXOffset(context, relativePoint.Plus(run.Position).X)).Where(x => x.HasValue); if (indices.IsEmpty()) { return(null); } var index = indices.Single().GetValueOrDefault(); // The index returned is in UTF-16, translate to codepoint index. // NSUInteger codePointIndex = stringIndexToCodePointIndex(self.attributedString.string, index); // Convert the code point index to an index into the mathlist var mlIndex = self.StringIndexToMathListIndex(index); // index will be between 0 and _range.length inclusive if (mlIndex < 0 || mlIndex > self.Range.Length) { throw new InvalidCodePathException($"Returned index out of range: {index}, range ({self.Range.Location}, {self.Range.Length})"); } // translate to the current index return(MathListIndex.Level0Index(self.Range.Location + mlIndex)); }
public static PointF?PointForIndex <TFont, TGlyph>(this ListDisplay <TFont, TGlyph> self, TypesettingContext <TFont, TGlyph> context, MathListIndex index) where TFont : IFont <TGlyph> { if (index is null) { return(null); } PointF?position = null; var nonScripted = self.Displays .Where(d => !(d is ListDisplay <TFont, TGlyph> ld && ld.LinePosition != Enumerations.LinePosition.Regular)) .ToArray(); if (index.SubIndexType == MathListSubIndexType.None && nonScripted.Length > 0 && nonScripted.All(d => d.Range.End <= index.AtomIndex)) { position = new PointF(self.Width, 0); } else { if (index.AtomIndex == self.Range.End) { // Special case the edge of the range position = new PointF(self.Width, 0); } else if (self.Range.Contains(index.AtomIndex) && self.SubDisplayForIndex(index) is IDisplay <TFont, TGlyph> display) { switch (index.SubIndexType) { case MathListSubIndexType.BetweenBaseAndScripts: var nucleusPosition = index.AtomIndex + index.SubIndex.AtomIndex; position = display.PointForIndex(context, MathListIndex.Level0Index(nucleusPosition)); break; case MathListSubIndexType.None: if (!display.HasScript) { position = display.PointForIndex(context, index); } else { var mainPosition = display.PointForIndex(context, index); position = self.Displays.SingleOrDefault(d => d is ListDisplay <TFont, TGlyph> ld && ld.IndexInParent == index.AtomIndex - 1) is IDisplay <TFont, TGlyph> scripted && mainPosition != null ? new PointF(mainPosition.Value.X + scripted.Width, 0) : mainPosition; } break; default: // Recurse position = display.PointForIndex(context, index.SubIndex); break; } } else { // Outside the range return(null); } } if (position is PointF found) { // Convert bounds from our coordinate system before returning found.X += self.Position.X; found.Y += self.Position.Y; return(found); } else { // We didn't find the position return(null); } }
/// <summary> /// Finds the index in the mathlist before which a new character should be inserted. /// Returns null if it cannot find the index. /// </summary> public static MathListIndex?IndexForPoint <TFont, TGlyph>( this IDisplay <TFont, TGlyph> display, TypesettingContext <TFont, TGlyph> context, PointF point) where TFont : IFont <TGlyph> => display switch {
public MathKeyboard(TypesettingContext <TFont, TGlyph> context, TFont font) => (Context, Font) = (context, font);