Resolving field resizing crashes.

This commit is contained in:
Michael Griebling
2023-01-18 09:02:28 -05:00
parent 2dd0fac5a8
commit 13a8a9097c
2 changed files with 76 additions and 58 deletions

View File

@@ -10,7 +10,7 @@ import SwiftUI
#if os(macOS) #if os(macOS)
class MTLabel : NSTextField { public class MTLabel : NSTextField {
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)

View File

@@ -48,27 +48,24 @@ public enum MTTextAlignment : UInt {
*/ */
@IBDesignable @IBDesignable
public class MTMathUILabel : MTView { public class MTMathUILabel : MTView {
private var _mathList:MTMathList?
/** The `MTMathList` to render. Setting this will remove any /** The `MTMathList` to render. Setting this will remove any
`latex` that has already been set. If `latex` has been set, this will `latex` that has already been set. If `latex` has been set, this will
return the parsed `MTMathList` if the `latex` parses successfully. Use this return the parsed `MTMathList` if the `latex` parses successfully. Use this
setting if the `MTMathList` has been programmatically constructed, otherwise it setting if the `MTMathList` has been programmatically constructed, otherwise it
is preferred to use `latex`. is preferred to use `latex`.
*/ */
var mathList:MTMathList? { public var mathList:MTMathList? {
set { set {
_mathList = newValue _mathList = newValue
error = nil _error = nil
_latex = MTMathListBuilder.mathListToString(newValue) _latex = MTMathListBuilder.mathListToString(newValue)
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
self.setNeedsLayout() self.setNeedsLayout()
} }
get { _mathList } get { _mathList }
} }
private var _mathList:MTMathList?
private var _latex:String=""
/** The latex string to be displayed. Setting this will remove any `mathList` that /** The latex string to be displayed. Setting this will remove any `mathList` that
has been set. If latex has not been set, this will return the latex output for the has been set. If latex has not been set, this will return the latex output for the
@@ -78,12 +75,12 @@ public class MTMathUILabel : MTView {
public var latex:String { public var latex:String {
set { set {
_latex = newValue _latex = newValue
self.error = nil _error = nil
var error : NSError? = nil var error : NSError? = nil
_mathList = MTMathListBuilder.build(fromString: newValue, error: &error) _mathList = MTMathListBuilder.build(fromString: newValue, error: &error)
if error != nil { if error != nil {
_mathList = nil _mathList = nil
self.error = error _error = error
self.errorLabel?.text = error!.localizedDescription self.errorLabel?.text = error!.localizedDescription
self.errorLabel?.frame = self.bounds self.errorLabel?.frame = self.bounds
self.errorLabel?.isHidden = !self.displayErrorInline self.errorLabel?.isHidden = !self.displayErrorInline
@@ -95,84 +92,108 @@ public class MTMathUILabel : MTView {
} }
get { _latex } get { _latex }
} }
private var _latex = ""
/** This contains any error that occurred when parsing the latex. */ /** This contains any error that occurred when parsing the latex. */
public var error:NSError? public var error:NSError? { _error }
private var _error:NSError?
/** If true, if there is an error it displays the error message inline. Default true. */ /** If true, if there is an error it displays the error message inline. Default true. */
public var displayErrorInline = true public var displayErrorInline = true
/** The MTFont to use for rendering. */ /** The MTFont to use for rendering. */
var font = MTFontManager.fontManager.defaultFont { public var font:MTFont? {
didSet { set {
guard newValue != nil else { return }
_font = newValue
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
self.setNeedsLayout() self.setNeedsLayout()
} }
get { _font }
} }
private var _font:MTFont?
/** Convenience method to just set the size of the font without changing the fontface. */ /** Convenience method to just set the size of the font without changing the fontface. */
@IBInspectable @IBInspectable
public var fontSize = MTFontManager.fontManager.kDefaultFontSize { public var fontSize:CGFloat {
didSet { set {
self.font = font?.copy(withSize: fontSize) _fontSize = newValue
let font = font?.copy(withSize: newValue)
self.font = font // also forces an update
} }
get { _fontSize }
} }
private var _fontSize:CGFloat=0
/** This sets the text color of the rendered math formula. The default color is black. */ /** This sets the text color of the rendered math formula. The default color is black. */
@IBInspectable @IBInspectable
public var textColor:MTColor? = MTColor.black { public var textColor:MTColor? {
didSet { set {
self.displayList?.textColor = textColor guard newValue != nil else { return }
_textColor = newValue
self.displayList?.textColor = newValue
self.setNeedsDisplay() self.setNeedsDisplay()
} }
get { _textColor }
} }
private var _textColor:MTColor?
/** The minimum distance from the margin of the view to the rendered math. This value is /** The minimum distance from the margin of the view to the rendered math. This value is
`UIEdgeInsetsZero` by default. This is useful if you need some padding between the math and `UIEdgeInsetsZero` by default. This is useful if you need some padding between the math and
the border/background color. sizeThatFits: will have its returned size increased by these insets. the border/background color. sizeThatFits: will have its returned size increased by these insets.
*/ */
@IBInspectable @IBInspectable
public var contentInsets = MTEdgeInsetsZero { public var contentInsets:MTEdgeInsets {
didSet { set {
_contentInsets = newValue
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
self.setNeedsLayout() self.setNeedsLayout()
} }
get { _contentInsets }
} }
private var _contentInsets = MTEdgeInsetsZero
/** The Label mode for the label. The default mode is Display */ /** The Label mode for the label. The default mode is Display */
public var labelMode = MTMathUILabelMode.display { public var labelMode:MTMathUILabelMode {
didSet { set {
_labelMode = newValue
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
self.setNeedsLayout() self.setNeedsLayout()
} }
get { _labelMode }
} }
private var _labelMode = MTMathUILabelMode.display
/** Horizontal alignment for the text. The default is align left. */ /** Horizontal alignment for the text. The default is align left. */
public var textAlignment = MTTextAlignment.left { public var textAlignment:MTTextAlignment {
didSet { set {
_textAlignment = newValue
self.invalidateIntrinsicContentSize() self.invalidateIntrinsicContentSize()
self.setNeedsLayout() self.setNeedsLayout()
} }
get { _textAlignment }
} }
private var _textAlignment = MTTextAlignment.left
/** The internal display of the MTMathUILabel. This is for advanced use only. */ /** The internal display of the MTMathUILabel. This is for advanced use only. */
var displayList: MTMathListDisplay? = nil public var displayList: MTMathListDisplay? { _displayList }
private var _displayList:MTMathListDisplay?
public var currentStyle:MTLineStyle { public var currentStyle:MTLineStyle {
switch labelMode { switch _labelMode {
case .display: return .display case .display: return .display
case .text: return .text case .text: return .text
} }
} }
var errorLabel: MTLabel? public var errorLabel: MTLabel?
override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
self.initCommon() self.initCommon()
} }
required init?(coder: NSCoder) { public required init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)
self.initCommon() self.initCommon()
} }
@@ -183,32 +204,33 @@ public class MTMathUILabel : MTView {
#else #else
self.layer.isGeometryFlipped = true self.layer.isGeometryFlipped = true
#endif #endif
fontSize = 20 _fontSize = 20
contentInsets = MTEdgeInsetsZero _contentInsets = MTEdgeInsetsZero
labelMode = .display _labelMode = .display
let font = MTFontManager.fontManager.defaultFont let font = MTFontManager.fontManager.defaultFont
self.font = font self.font = font
textAlignment = .left _textAlignment = .left
displayList = nil _displayList = nil
displayErrorInline = true displayErrorInline = true
self.backgroundColor = MTColor.clear self.backgroundColor = MTColor.clear
textColor = MTColor.black _textColor = MTColor.black
errorLabel = MTLabel() let label = MTLabel()
self.errorLabel = label
#if os(macOS) #if os(macOS)
errorLabel?.layer?.isGeometryFlipped = true label.layer?.isGeometryFlipped = true
#else #else
errorLabel?.layer.isGeometryFlipped = true label.layer.isGeometryFlipped = true
#endif #endif
errorLabel?.isHidden = true label.isHidden = true
errorLabel?.textColor = MTColor.red label.textColor = MTColor.red
self.addSubview(errorLabel!) self.addSubview(label)
} }
override public func draw(_ dirtyRect: MTRect) { override public func draw(_ dirtyRect: MTRect) {
super.draw(dirtyRect) super.draw(dirtyRect)
if self.mathList == nil { return } if self.mathList == nil { return }
// drawing code // drawing code
let context = MTGraphicsGetCurrentContext()! let context = MTGraphicsGetCurrentContext()!
context.saveGState() context.saveGState()
@@ -222,29 +244,25 @@ public class MTMathUILabel : MTView {
func _layoutSubviews() { func _layoutSubviews() {
if mathList != nil { if mathList != nil {
displayList = MTTypesetter.createLineForMathList(mathList, font: font, style: currentStyle) _displayList = MTTypesetter.createLineForMathList(mathList, font: font, style: currentStyle)
displayList?.textColor = textColor _displayList!.textColor = textColor
var textX = CGFloat(0) var textX = CGFloat(0)
switch self.textAlignment { switch self.textAlignment {
case .left: case .left: textX = contentInsets.left
textX = self.contentInsets.left case .center: textX = (bounds.size.width - contentInsets.left - contentInsets.right - _displayList!.width) / 2 + contentInsets.left
case .center: case .right: textX = bounds.size.width - _displayList!.width - contentInsets.right
textX = (bounds.size.width - contentInsets.left - contentInsets.right - displayList!.width) / 2 +
contentInsets.left
case .right:
textX = bounds.size.width - displayList!.width - contentInsets.right
} }
let availableHeight = bounds.size.height - contentInsets.bottom - contentInsets.top let availableHeight = bounds.size.height - contentInsets.bottom - contentInsets.top
// center things vertically // center things vertically
var height = displayList!.ascent + displayList!.descent var height = _displayList!.ascent + _displayList!.descent
if height < fontSize/2 { if height < fontSize/2 {
height = fontSize/2 // set height to half the font size height = fontSize/2 // set height to half the font size
} }
let textY = (availableHeight - height) / 2 + displayList!.descent + contentInsets.bottom let textY = (availableHeight - height) / 2 + _displayList!.descent + contentInsets.bottom
displayList?.position = CGPointMake(textX, textY) _displayList!.position = CGPointMake(textX, textY)
} else { } else {
displayList = nil _displayList = nil
} }
errorLabel?.frame = self.bounds errorLabel?.frame = self.bounds
self.setNeedsDisplay() self.setNeedsDisplay()
@@ -253,8 +271,8 @@ public class MTMathUILabel : MTView {
func _sizeThatFits(_ size:CGSize) -> CGSize { func _sizeThatFits(_ size:CGSize) -> CGSize {
var size = size var size = size
var displayList:MTMathListDisplay? = nil var displayList:MTMathListDisplay? = nil
if mathList != nil { if _mathList != nil {
displayList = MTTypesetter.createLineForMathList(mathList, font: font, style: currentStyle) displayList = MTTypesetter.createLineForMathList(_mathList, font: font, style: currentStyle)
} }
size.width = displayList!.width + contentInsets.left + contentInsets.right size.width = displayList!.width + contentInsets.left + contentInsets.right
size.height = displayList!.ascent + displayList!.descent + contentInsets.top + contentInsets.bottom size.height = displayList!.ascent + displayList!.descent + contentInsets.top + contentInsets.bottom