diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b9661ce --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +--- +BasedOnStyle: Chromium +AlignTrailingComments: true +BreakBeforeBraces: Linux +ColumnLimit: 120 +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 2 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerBindsToType: false +SpacesBeforeTrailingComments: 1 +TabWidth: 8 +UseTab: Never +... diff --git a/.gitignore b/.gitignore index a74cfed..96c6c72 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,7 @@ Carthage/Build fastlane/report.xml fastlane/screenshots + +# Swift Package Manager +.swiftpm/ +.build/ diff --git a/MathEditor.xcodeproj/project.pbxproj b/MathEditor.xcodeproj/project.pbxproj index e601607..f4e4328 100644 --- a/MathEditor.xcodeproj/project.pbxproj +++ b/MathEditor.xcodeproj/project.pbxproj @@ -3,13 +3,10 @@ archiveVersion = 1; classes = { }; - objectVersion = 47; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ - 1385A98064E559BF2F757304 /* libPods-MathEditor_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FF6761D7D7AC0EC9F0679C9 /* libPods-MathEditor_Example.a */; }; - 2040F7C6A902C4AEF598B076 /* libPods-MathEditor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 473720D7C87DFE272F052861 /* libPods-MathEditor.a */; }; - 243FB02EE755A1D41861623B /* libPods-MathEditor_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 023479B03FDA39233191C9B7 /* libPods-MathEditor_Tests.a */; }; 490BE5781CE6A08100AE31A0 /* MTDisplayEditingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 490BE5771CE6A08100AE31A0 /* MTDisplayEditingTest.m */; }; 49DEC8241CF5523E000053CD /* MTCaretView.m in Sources */ = {isa = PBXBuildFile; fileRef = 49DEC81F1CF5523E000053CD /* MTCaretView.m */; }; 49DEC8251CF5523E000053CD /* MTDisplay+Editing.m in Sources */ = {isa = PBXBuildFile; fileRef = 49DEC8211CF5523E000053CD /* MTDisplay+Editing.m */; }; @@ -29,6 +26,8 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 862D28DD2E2D4C4400F9B6FE /* MathEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 862D28DC2E2D4C4400F9B6FE /* MathEditor */; }; + 86B937A02F75C33F00FFC54B /* MathKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 86B9379F2F75C33F00FFC54B /* MathKeyboard */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; /* End PBXBuildFile section */ @@ -45,10 +44,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 023479B03FDA39233191C9B7 /* libPods-MathEditor_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MathEditor_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 09D14643E1EDE3F6786E897E /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 1377C04C01CE56EB11AB5A1B /* Pods-MathEditor.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor/Pods-MathEditor.debug.xcconfig"; sourceTree = ""; }; - 473720D7C87DFE272F052861 /* libPods-MathEditor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MathEditor.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 490BE5561CE695CC00AE31A0 /* libMathEditor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMathEditor.a; sourceTree = BUILT_PRODUCTS_DIR; }; 490BE5771CE6A08100AE31A0 /* MTDisplayEditingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTDisplayEditingTest.m; sourceTree = ""; }; 497F92211CF8CBFF00022162 /* MathEditor-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MathEditor-Prefix.pch"; sourceTree = ""; }; @@ -74,7 +70,6 @@ 49DEC83D1CF5591D000053CD /* WhiteBGKeyboardTab.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = WhiteBGKeyboardTab.xcassets; sourceTree = ""; }; 49DEC83E1CF57D75000053CD /* lmroman10-bolditalic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "lmroman10-bolditalic.otf"; sourceTree = ""; }; 49DEC83F1CF59F82000053CD /* MathEditor.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = MathEditor.podspec; sourceTree = ""; }; - 5628D2459CDCF8D3064ABC73 /* libPods-iosMathEditor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iosMathEditor.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58A195388D20070C39A /* MathEditor_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathEditor_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; @@ -92,16 +87,8 @@ 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; - 733BAF45E72F86EDFEAAAD61 /* libPods-iosMathEditor_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iosMathEditor_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 753F7D8158AE6B036B248A96 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 7A378091F23FA26A8EEA2F82 /* Pods-MathEditor_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor_Example/Pods-MathEditor_Example.release.xcconfig"; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; - 8A93C35286D2F42BBFFF6281 /* Pods-MathEditor.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor.release.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor/Pods-MathEditor.release.xcconfig"; sourceTree = ""; }; - 8FF6761D7D7AC0EC9F0679C9 /* libPods-MathEditor_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MathEditor_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - A4EC35D984BA58883006D2C3 /* Pods-MathEditor_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor_Tests/Pods-MathEditor_Tests.release.xcconfig"; sourceTree = ""; }; - AA22ED6294D4E5576093F8A8 /* Pods-MathEditor_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor_Tests/Pods-MathEditor_Tests.debug.xcconfig"; sourceTree = ""; }; - AEA5AA8AF2C91D3A7B9B05F9 /* libPods-iosMathEditor_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iosMathEditor_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - C22335FDD0B9F56B94B8187A /* Pods-MathEditor_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MathEditor_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MathEditor_Example/Pods-MathEditor_Example.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -109,7 +96,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2040F7C6A902C4AEF598B076 /* libPods-MathEditor.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -120,7 +106,8 @@ 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, - 1385A98064E559BF2F757304 /* libPods-MathEditor_Example.a in Frameworks */, + 86B937A02F75C33F00FFC54B /* MathKeyboard in Frameworks */, + 862D28DD2E2D4C4400F9B6FE /* MathEditor in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -131,7 +118,6 @@ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, - 243FB02EE755A1D41861623B /* libPods-MathEditor_Tests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -199,19 +185,6 @@ path = ../MathKeyboardResources; sourceTree = ""; }; - 4D5F9F41E950A502FB2B3BEA /* Pods */ = { - isa = PBXGroup; - children = ( - 1377C04C01CE56EB11AB5A1B /* Pods-MathEditor.debug.xcconfig */, - 8A93C35286D2F42BBFFF6281 /* Pods-MathEditor.release.xcconfig */, - C22335FDD0B9F56B94B8187A /* Pods-MathEditor_Example.debug.xcconfig */, - 7A378091F23FA26A8EEA2F82 /* Pods-MathEditor_Example.release.xcconfig */, - AA22ED6294D4E5576093F8A8 /* Pods-MathEditor_Tests.debug.xcconfig */, - A4EC35D984BA58883006D2C3 /* Pods-MathEditor_Tests.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; 6003F581195388D10070C39A = { isa = PBXGroup; children = ( @@ -221,7 +194,6 @@ 490BE5571CE695CC00AE31A0 /* mathEditor */, 6003F58C195388D20070C39A /* Frameworks */, 6003F58B195388D20070C39A /* Products */, - 4D5F9F41E950A502FB2B3BEA /* Pods */, ); sourceTree = ""; }; @@ -242,12 +214,6 @@ 6003F58F195388D20070C39A /* CoreGraphics.framework */, 6003F591195388D20070C39A /* UIKit.framework */, 6003F5AF195388D20070C39A /* XCTest.framework */, - 5628D2459CDCF8D3064ABC73 /* libPods-iosMathEditor.a */, - 733BAF45E72F86EDFEAAAD61 /* libPods-iosMathEditor_Example.a */, - AEA5AA8AF2C91D3A7B9B05F9 /* libPods-iosMathEditor_Tests.a */, - 473720D7C87DFE272F052861 /* libPods-MathEditor.a */, - 8FF6761D7D7AC0EC9F0679C9 /* libPods-MathEditor_Example.a */, - 023479B03FDA39233191C9B7 /* libPods-MathEditor_Tests.a */, ); name = Frameworks; sourceTree = ""; @@ -313,11 +279,9 @@ isa = PBXNativeTarget; buildConfigurationList = 490BE55E1CE695CC00AE31A0 /* Build configuration list for PBXNativeTarget "MathEditor" */; buildPhases = ( - 48A9CBFCAB5894F9BFC552E4 /* [CP] Check Pods Manifest.lock */, 490BE5521CE695CC00AE31A0 /* Sources */, 490BE5531CE695CC00AE31A0 /* Frameworks */, 490BE5541CE695CC00AE31A0 /* CopyFiles */, - 4C3A2645A6B5FE31FA662BF1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -332,12 +296,9 @@ isa = PBXNativeTarget; buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "MathEditor_Example" */; buildPhases = ( - A3E2D0D1A985C3FEA5864005 /* [CP] Check Pods Manifest.lock */, 6003F586195388D20070C39A /* Sources */, 6003F587195388D20070C39A /* Frameworks */, 6003F588195388D20070C39A /* Resources */, - D8A5CFAD5F09AAA428AB4D10 /* [CP] Embed Pods Frameworks */, - E202BD0EA2A01E97CAF04F2E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -352,12 +313,9 @@ isa = PBXNativeTarget; buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "MathEditor_Tests" */; buildPhases = ( - 4B44B263AD3BCE039EE8DD71 /* [CP] Check Pods Manifest.lock */, 6003F5AA195388D20070C39A /* Sources */, 6003F5AB195388D20070C39A /* Frameworks */, 6003F5AC195388D20070C39A /* Resources */, - E5084554A0ECA0F3755B8CC7 /* [CP] Embed Pods Frameworks */, - 02495CB9EE6CCCBF5814B5C0 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -374,8 +332,9 @@ 6003F582195388D10070C39A /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; CLASSPREFIX = MT; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 2630; ORGANIZATIONNAME = "Kostub Deshmukh"; TargetAttributes = { 490BE5551CE695CC00AE31A0 = { @@ -385,13 +344,16 @@ }; buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "MathEditor" */; compatibilityVersion = "Xcode 6.3"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 6003F581195388D10070C39A; + packageReferences = ( + 862D28DB2E2D4C4400F9B6FE /* XCLocalSwiftPackageReference "../MathEditor" */, + ); productRefGroup = 6003F58B195388D20070C39A /* Products */; projectDirPath = ""; projectRoot = ""; @@ -424,129 +386,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 02495CB9EE6CCCBF5814B5C0 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MathEditor_Tests/Pods-MathEditor_Tests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 48A9CBFCAB5894F9BFC552E4 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - 4B44B263AD3BCE039EE8DD71 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - 4C3A2645A6B5FE31FA662BF1 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MathEditor/Pods-MathEditor-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - A3E2D0D1A985C3FEA5864005 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - D8A5CFAD5F09AAA428AB4D10 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MathEditor_Example/Pods-MathEditor_Example-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - E202BD0EA2A01E97CAF04F2E /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MathEditor_Example/Pods-MathEditor_Example-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - E5084554A0ECA0F3755B8CC7 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MathEditor_Tests/Pods-MathEditor_Tests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 490BE5521CE695CC00AE31A0 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -603,13 +442,12 @@ /* Begin XCBuildConfiguration section */ 490BE55C1CE695CC00AE31A0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1377C04C01CE56EB11AB5A1B /* Pods-MathEditor.debug.xcconfig */; buildSettings = { CLANG_WARN_UNREACHABLE_CODE = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; OTHER_LDFLAGS = ( "$(inherited)", @@ -622,14 +460,13 @@ }; 490BE55D1CE695CC00AE31A0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8A93C35286D2F42BBFFF6281 /* Pods-MathEditor.release.xcconfig */; buildSettings = { CLANG_WARN_UNREACHABLE_CODE = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ( "$(inherited)", @@ -644,23 +481,39 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -673,9 +526,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -684,30 +538,47 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -715,11 +586,11 @@ }; 6003F5C0195388D20070C39A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C22335FDD0B9F56B94B8187A /* Pods-MathEditor_Example.debug.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MathEditorExample/MathEditor-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/MathEditorExample/iosMathEditor-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12; PRODUCT_BUNDLE_IDENTIFIER = MathChat.MathEditorExample; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -728,11 +599,11 @@ }; 6003F5C1195388D20070C39A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7A378091F23FA26A8EEA2F82 /* Pods-MathEditor_Example.release.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MathEditorExample/MathEditor-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/MathEditorExample/iosMathEditor-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12; PRODUCT_BUNDLE_IDENTIFIER = MathChat.MathEditorExample; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -741,7 +612,6 @@ }; 6003F5C3195388D20070C39A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AA22ED6294D4E5576093F8A8 /* Pods-MathEditor_Tests.debug.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; @@ -765,7 +635,6 @@ }; 6003F5C4195388D20070C39A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A4EC35D984BA58883006D2C3 /* Pods-MathEditor_Tests.release.xcconfig */; buildSettings = { GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; @@ -823,6 +692,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 862D28DB2E2D4C4400F9B6FE /* XCLocalSwiftPackageReference "../MathEditor" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../MathEditor; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 862D28DC2E2D4C4400F9B6FE /* MathEditor */ = { + isa = XCSwiftPackageProductDependency; + productName = MathEditor; + }; + 86B9379F2F75C33F00FFC54B /* MathKeyboard */ = { + isa = XCSwiftPackageProductDependency; + package = 862D28DB2E2D4C4400F9B6FE /* XCLocalSwiftPackageReference "../MathEditor" */; + productName = MathKeyboard; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 6003F582195388D10070C39A /* Project object */; } diff --git a/MathEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MathEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 6228eeb..919434a 100644 --- a/MathEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/MathEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/MathEditor.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/MathEditor.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..2efe63d --- /dev/null +++ b/MathEditor.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "3845228a2da848133f1c5b0fe02201c5631261188fc4a1a940a392278f808293", + "pins" : [ + { + "identity" : "iosmath", + "kind" : "remoteSourceControl", + "location" : "https://github.com/maitbayev/iosMath.git", + "state" : { + "branch" : "master", + "revision" : "066ba2f8353782a644889efe9ceb884ea844180b" + } + } + ], + "version" : 3 +} diff --git a/MathEditor.xcodeproj/xcshareddata/xcschemes/iosMathEditor-Example.xcscheme b/MathEditor.xcodeproj/xcshareddata/xcschemes/iosMathEditor-Example.xcscheme index 6ca118a..f7dfae5 100644 --- a/MathEditor.xcodeproj/xcshareddata/xcschemes/iosMathEditor-Example.xcscheme +++ b/MathEditor.xcodeproj/xcshareddata/xcschemes/iosMathEditor-Example.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - - - diff --git a/MathEditorExample/MTViewController.m b/MathEditorExample/MTViewController.m index 0ed30c7..e782a79 100644 --- a/MathEditorExample/MTViewController.m +++ b/MathEditorExample/MTViewController.m @@ -10,7 +10,8 @@ // #import "MTViewController.h" -#import "MTMathKeyboardRootView.h" + +@import MathKeyboard; @interface MTViewController () diff --git a/MathEditorSwift/Package.resolved b/MathEditorSwift/Package.resolved new file mode 100644 index 0000000..24b9309 --- /dev/null +++ b/MathEditorSwift/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "iosmath", + "kind" : "remoteSourceControl", + "location" : "https://github.com/maitbayev/iosMath.git", + "state" : { + "branch" : "master", + "revision" : "bf4a5466fc405031977f2edcf806ccb119c23836" + } + } + ], + "version" : 2 +} diff --git a/MathEditorSwift/Package.swift b/MathEditorSwift/Package.swift new file mode 100644 index 0000000..16e6890 --- /dev/null +++ b/MathEditorSwift/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.6 + +import PackageDescription + +let package = Package( + name: "MathEditorSwift", + platforms: [.iOS(.v13), .macOS(.v11)], + products: [ + .library( + name: "MathEditorSwift", + targets: ["MathEditorSwift"] + ) + ], + dependencies: [ + .package(url: "https://github.com/maitbayev/iosMath.git", branch: "master") + ], + targets: [ + .target( + name: "MathEditorSwift", + dependencies: ["iosMath"] + ), + .testTarget( + name: "MathEditorSwiftTests", + dependencies: ["MathEditorSwift"] + ), + ] +) diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/DummyTextInputHandler.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/DummyTextInputHandler.swift new file mode 100644 index 0000000..f232884 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/DummyTextInputHandler.swift @@ -0,0 +1,27 @@ +// +// MTTextInputHandler.swift +// MathEditorSwift +// +// Created by Madiyar Aitbayev on 26/03/2026. +// + +#if canImport(UIKit) + + import UIKit + + struct DummyTextInputHandler { + var selectedTextRange: UITextRange? + var markedTextRange: UITextRange? + var markedTextStyle: [NSAttributedString.Key: Any]? + var beginningOfDocument = UITextPosition() + var endOfDocument = UITextPosition() + var inputDelegate: (any UITextInputDelegate)? + var tokenizer: UITextInputTokenizer = UITextInputStringTokenizer() + } + +#else // canImport(UIKit) + + struct DummyTextInputHandler { + } + +#endif // canImport(UIKit) diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCancelView.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCancelView.swift new file mode 100644 index 0000000..6e01399 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCancelView.swift @@ -0,0 +1,34 @@ +import Foundation + +public final class MTCancelView: MTView { + private let imageView: MTImageView + + @objc + public init(target: AnyObject, action: Selector) { + #if canImport(UIKit) + let image = MTImage(systemName: "xmark.circle")?.withRenderingMode(.alwaysTemplate) + imageView = MTImageView(image: image) + imageView.contentMode = .scaleAspectFit + imageView.tintColor = .secondaryLabel + #else + imageView = MTImageView(frame: .zero) + imageView.image = MTImage(systemSymbolName: "xmark.circle", accessibilityDescription: nil) + imageView.imageScaling = .scaleProportionallyUpOrDown + imageView.contentTintColor = .secondaryLabelColor + #endif + + super.init(frame: .zero) + + addSubview(imageView) + imageView.pinToSuperview() + + addGestureRecognizer(MTTapGestureRecognizer(target: target, action: action)) + + isHidden = true + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCaretView.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCaretView.swift new file mode 100644 index 0000000..06a5b37 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTCaretView.swift @@ -0,0 +1,299 @@ +import Foundation +import iosMath + +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + +private let initialBlinkDelay: TimeInterval = 0.7 +private let blinkRate: TimeInterval = 0.5 +private let caretFontSize: CGFloat = 30 +private let caretAscent: CGFloat = 25 +private let caretWidth: CGFloat = 3 +private let caretDescent: CGFloat = 7 +private let caretHandleWidth: CGFloat = 15 +private let caretHandleDescent: CGFloat = 8 +private let caretHandleHeight: CGFloat = 20 +private let caretHandleHitAreaSize: CGFloat = 44 + +// The settings below make sense for the given font size. They are scaled appropriately when the fontsize changes. +private func caretHeight() -> CGFloat { + caretAscent + caretDescent +} + +private final class CaretHandle: MTView { + weak var label: MTEditableMathLabelSwift? + + var color: MTColor = MTColor.label { + didSet { setNeedsDisplay() } + } + + private var path = MTBezierPath() + private var isInteracting = false + + private var hitArea: CGRect { + // Create a hit area around the center. + let size = bounds.size + return CGRect( + x: (size.width - caretHandleHitAreaSize) / 2, + y: (size.height - caretHandleHitAreaSize) / 2, + width: caretHandleHitAreaSize, + height: caretHandleHitAreaSize + ) + } + + override init(frame: CGRect) { + super.init(frame: frame) + path = createHandlePath() + backgroundColor = .clear + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func createHandlePath() -> MTBezierPath { + let path = MTBezierPath() + let size = bounds.size + path.move(to: CGPoint(x: size.width / 2, y: 0)) + path.addLine(to: CGPoint(x: size.width, y: size.height / 4)) + path.addLine(to: CGPoint(x: size.width, y: size.height)) + path.addLine(to: CGPoint(x: 0, y: size.height)) + path.addLine(to: CGPoint(x: 0, y: size.height / 4)) + path.close() + return path + } + + private func interactionBegan() { + isInteracting = true + setNeedsDisplay() + } + + private func interactionEnded() { + isInteracting = false + setNeedsDisplay() + } + + private func handleDrag(localPoint: CGPoint) { + guard let label else { return } + let caretPoint = CGPoint(x: localPoint.x, y: localPoint.y - frame.origin.y) + let labelPoint = label.convert(caretPoint, from: self) + // puts the point at the top to the top of the current caret + label.moveCaret(to: labelPoint) + } + + public override func draw(_ rect: CGRect) { + let drawColor = color.withAlphaComponent(isInteracting ? 1.0 : 0.6) + drawColor.setFill() + path.fill() + } +} + +#if canImport(UIKit) + extension CaretHandle { + public override func layoutSubviews() { + super.layoutSubviews() + path = createHandlePath() + } + + public override func touchesBegan(_ touches: Set, with event: UIEvent?) { + interactionBegan() + } + + public override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + interactionEnded() + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first else { return } + handleDrag(localPoint: touch.location(in: self)) + } + + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { + interactionEnded() + } + + public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + self.hitArea.contains(point) + } + } +#endif // canImport(UIKit) + +#if canImport(AppKit) + extension CaretHandle { + public override var isFlipped: Bool { true } + + public override func layout() { + super.layout() + path = createHandlePath() + } + + public override func acceptsFirstMouse(for event: NSEvent?) -> Bool { + true + } + + public override func mouseDown(with event: NSEvent) { + interactionBegan() + } + + public override func mouseDragged(with event: NSEvent) { + handleDrag(localPoint: convert(event.locationInWindow, from: nil)) + } + + public override func mouseUp(with event: NSEvent) { + interactionEnded() + } + + public override func mouseCancelled(with event: NSEvent) { + interactionEnded() + } + + public override func hitTest(_ point: NSPoint) -> NSView? { + guard !isHidden else { return nil } + let localPoint = convert(point, from: superview) + return hitArea.contains(localPoint) ? self : nil + } + } +#endif // canImport(AppKit) + +final class MTCaretView: MTView { + public var caretColor: MTColor = MTColor.label { + didSet { + handle.color = caretColor + blinker.backgroundColor = caretColor + } + } + + private var blinkTimer: Timer? + private let blinker = MTView(frame: .zero) + private let handle: CaretHandle + private var scale: Double + + init(editor: MTEditableMathLabelSwift) { + scale = editor.fontSize / caretFontSize + handle = CaretHandle( + frame: CGRect( + x: 0, + y: 0, + width: caretHandleWidth * scale, + height: caretHandleHeight * scale + )) + super.init(frame: .zero) + + blinker.backgroundColor = caretColor + addSubview(blinker) + + handle.color = caretColor + handle.isHidden = true + handle.label = editor + addSubview(handle) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setPosition(_ position: CGPoint) { + // position is in the parent's coordinate system and it is the bottom left corner of the view. + frame = CGRect(x: position.x, y: position.y - caretAscent * scale, width: 0, height: 0) + } + + func setFontSize(_ fontSize: CGFloat) { + scale = fontSize / caretFontSize + setNeedsLayout() + } + + func showHandle(_ show: Bool) { + handle.isHidden = !show + } + + // Helper method to set an initial blink delay + func delayBlink() { + isHidden = false + blinker.isHidden = false + blinkTimer?.fireDate = Date(timeIntervalSinceNow: initialBlinkDelay) + } + + // Helper method to toggle hidden state of caret view. + private func blink() { + blinker.isHidden.toggle() + } + + private func doLayout() { + blinker.frame = CGRect(x: 0, y: 0, width: caretWidth * scale, height: caretHeight() * scale) + handle.frame = CGRect( + x: -((caretHandleWidth - caretWidth) * scale / 2), + y: (caretHeight() + caretHandleDescent) * scale, + width: caretHandleWidth * scale, + height: caretHandleHeight * scale + ) + } + + private func startBlinkingIfNeeded() { + guard superview != nil else { + blinkTimer?.invalidate() + blinkTimer = nil + return + } + if blinkTimer == nil { + blinkTimer = Timer.scheduledTimer(withTimeInterval: blinkRate, repeats: true) { + [weak self] _ in + self?.blink() + } + } + delayBlink() + } + + deinit { + blinkTimer?.invalidate() + blinkTimer = nil + } +} + +#if canImport(UIKit) + extension MTCaretView { + public override func didMoveToSuperview() { + super.didMoveToSuperview() + // UIView didMoveToSuperview override to set up blink timers after caret view created in superview. + isHidden = false + startBlinkingIfNeeded() + } + + public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + if !handle.isHidden { + return handle.point(inside: convert(point, to: handle), with: event) + } + return super.point(inside: point, with: event) + } + + public override func layoutSubviews() { + super.layoutSubviews() + doLayout() + } + } +#endif // canImport(UIKit) + +#if canImport(AppKit) + extension MTCaretView { + public override var isFlipped: Bool { true } + + public override func viewDidMoveToSuperview() { + super.viewDidMoveToSuperview() + isHidden = false + startBlinkingIfNeeded() + } + + public override func layout() { + super.layout() + doLayout() + } + + public override func hitTest(_ point: NSPoint) -> NSView? { + hitTestOutsideBounds(point) + } + } +#endif // canImport(AppKit) diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTDisplay+Editing.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTDisplay+Editing.swift new file mode 100644 index 0000000..a1f8779 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTDisplay+Editing.swift @@ -0,0 +1,462 @@ +// +// MTDisplay+Editing.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +import CoreText +import Foundation +import iosMath + +private let invalidPosition = CGPoint(x: -1, y: -1) +// Number of pixels outside the bounds to still consider a point as part of that bounds. +private let pixelDelta: CGFloat = 2 + +private func codePointIndexToStringIndex(_ str: String, _ codePointIndex: UInt) -> Int { + let utf16 = Array(str.utf16) + var codePointCount: UInt = 0 + var i = 0 + + while i < utf16.count { + if codePointCount == codePointIndex { + return i + } + codePointCount += 1 + + let c = utf16[i] + if CFStringIsSurrogateHighCharacter(c) || CFStringIsSurrogateLowCharacter(c) { + i += 1 + } + i += 1 + } + + return NSNotFound +} + +private func distanceFromPointToRect(_ point: CGPoint, _ rect: CGRect) -> CGFloat { + // Manhattan distance from the point to the nearest rectangle boundary. + var distance: CGFloat = 0 + + if point.x < rect.origin.x { + distance += rect.origin.x - point.x + } else if point.x > rect.maxX { + distance += point.x - rect.maxX + } + + if point.y < rect.origin.y { + distance += rect.origin.y - point.y + } else if point.y > rect.maxY { + distance += point.y - rect.maxY + } + + return distance +} + +extension MTDisplay { + // Empty implementations for the base class. + @objc(closestIndexToPoint:) + public func closestIndex(to point: CGPoint) -> MTMathListIndex? { + nil + } + + @objc(caretPositionForIndex:) + public func caretPosition(for index: MTMathListIndex) -> CGPoint { + invalidPosition + } + + @objc(highlightCharacterAtIndex:color:) + public func highlightCharacter(at index: MTMathListIndex, color: MTColor) {} + + @objc(highlightWithColor:) + public func highlight(with color: MTColor) {} +} + +extension MTCTLineDisplay { + @objc(closestIndexToPoint:) + public override func closestIndex(to point: CGPoint) -> MTMathListIndex? { + // Convert the point to the reference of the CTLine. + let relativePoint = CGPoint(x: point.x - position.x, y: point.y - position.y) + let idx = CTLineGetStringIndexForPosition(line, relativePoint) + if idx == kCFNotFound { + return nil + } + + // The CoreText index is UTF-16 based. Convert to a math-list atom index. + let mlIndex = convertToMathListIndex(UInt(idx)) + assert(mlIndex <= UInt(range.length), "Returned index out of range: \(idx)") + return MTMathListIndex.level0Index(UInt(range.location) + mlIndex) + } + + @objc(caretPositionForIndex:) + public override func caretPosition(for index: MTMathListIndex) -> CGPoint { + assert( + index.subIndexType == .subIndexTypeNone, "Index in a CTLineDisplay cannot have sub indexes.") + + let offset: CGFloat + if Int(index.atomIndex) == NSMaxRange(range) { + offset = width + } else { + assert(NSLocationInRange(Int(index.atomIndex), range), "Index not in range") + let strIndex = mathListIndexToStringIndex(index.atomIndex - UInt(range.location)) + offset = CTLineGetOffsetForStringIndex(line, strIndex, nil) + } + + return CGPoint(x: position.x + offset, y: position.y) + } + + @objc(highlightCharacterAtIndex:color:) + public override func highlightCharacter(at index: MTMathListIndex, color: MTColor) { + assert(NSLocationInRange(Int(index.atomIndex), range)) + assert(index.subIndexType == .subIndexTypeNone || index.subIndexType == .subIndexTypeNucleus) + + if index.subIndexType == .subIndexTypeNucleus { + assertionFailure("Nucleus highlighting not supported yet") + } + + let charIndex = codePointIndexToStringIndex( + attributedString.string, index.atomIndex - UInt(range.location)) + assert(charIndex != NSNotFound) + + let attrStr = NSMutableAttributedString(attributedString: attributedString) + let seqRange = (attrStr.string as NSString).rangeOfComposedCharacterSequence(at: charIndex) + attrStr.addAttribute( + kCTForegroundColorAttributeName as NSAttributedString.Key, + value: color.cgColor, + range: seqRange) + attributedString = attrStr + } + + @objc(highlightWithColor:) + public override func highlight(with color: MTColor) { + let attrStr = NSMutableAttributedString(attributedString: attributedString) + attrStr.addAttribute( + kCTForegroundColorAttributeName as NSAttributedString.Key, + value: color.cgColor, + range: NSRange(location: 0, length: attrStr.length)) + attributedString = attrStr + } + + @objc(convertToMathListIndex:) + public func convertToMathListIndex(_ strIndex: UInt) -> UInt { + // A single math atom may map to multiple UTF-16 code units. + var strLenCovered: UInt = 0 + for mlIndex in 0..= strIndex { + return UInt(mlIndex) + } + let atom = atoms[mlIndex] + strLenCovered += UInt(atom.nucleus.count) + } + // By the end we should have covered all characters that can be addressed. + assert(strLenCovered >= strIndex, "StrIndex should not be more than the len covered") + return UInt(atoms.count) + } + + @objc(mathListIndexToStringIndex:) + public func mathListIndexToStringIndex(_ mlIndex: UInt) -> Int { + assert(mlIndex < UInt(atoms.count), "Index not in range") + + var strIndex = 0 + for i in 0.. MTMathListIndex? { + // We can be before the fraction, inside the fraction, or after it. + if point.x < position.x - pixelDelta { + return MTMathListIndex.level0Index(UInt(range.location)) + } else if point.x > position.x + width + pixelDelta { + return MTMathListIndex.level0Index(UInt(NSMaxRange(range))) + } else { + let numeratorDistance = distanceFromPointToRect(point, numerator.displayBounds()) + let denominatorDistance = distanceFromPointToRect(point, denominator.displayBounds()) + if numeratorDistance < denominatorDistance { + return MTMathListIndex( + atLocation: UInt(range.location), + withSubIndex: numerator.closestIndex(to: point), + type: .subIndexTypeNumerator) + } else { + return MTMathListIndex( + atLocation: UInt(range.location), + withSubIndex: denominator.closestIndex(to: point), + type: .subIndexTypeDenominator) + } + } + } + + @objc(caretPositionForIndex:) + public override func caretPosition(for index: MTMathListIndex) -> CGPoint { + // Draw a caret before the fraction. + assert(index.subIndexType == .subIndexTypeNone) + return CGPoint(x: position.x, y: position.y) + } + + @objc(highlightCharacterAtIndex:color:) + public override func highlightCharacter(at index: MTMathListIndex, color: MTColor) { + assert(index.subIndexType == .subIndexTypeNone) + highlight(with: color) + } + + @objc(highlightWithColor:) + public override func highlight(with color: MTColor) { + numerator.highlight(with: color) + denominator.highlight(with: color) + } + + @objc(subAtomForIndexType:) + public func subAtom(forIndexType type: MTMathListSubIndexType) -> MTMathListDisplay? { + switch type { + case .subIndexTypeNumerator: + return numerator + case .subIndexTypeDenominator: + return denominator + default: + assertionFailure("Not a fraction subtype \(type.rawValue)") + return nil + } + } +} + +extension MTRadicalDisplay { + @objc(closestIndexToPoint:) + public override func closestIndex(to point: CGPoint) -> MTMathListIndex? { + // We can be before the radical, inside the radical, or after it. + if point.x < position.x - pixelDelta { + return MTMathListIndex.level0Index(UInt(range.location)) + } else if point.x > position.x + width + pixelDelta { + return MTMathListIndex.level0Index(UInt(NSMaxRange(range))) + } else { + let degreeDistance = distanceFromPointToRect(point, degree!.displayBounds()) + let radicandDistance = distanceFromPointToRect(point, radicand.displayBounds()) + + if degreeDistance < radicandDistance { + return MTMathListIndex( + atLocation: UInt(range.location), + withSubIndex: degree?.closestIndex(to: point), + type: .subIndexTypeDegree) + } else { + return MTMathListIndex( + atLocation: UInt(range.location), + withSubIndex: radicand.closestIndex(to: point), + type: .subIndexTypeRadicand) + } + } + } + + @objc(caretPositionForIndex:) + public override func caretPosition(for index: MTMathListIndex) -> CGPoint { + // Draw a caret before the radical. + assert(index.subIndexType == .subIndexTypeNone) + return CGPoint(x: position.x, y: position.y) + } + + @objc(highlightCharacterAtIndex:color:) + public override func highlightCharacter(at index: MTMathListIndex, color: MTColor) { + assert(index.subIndexType == .subIndexTypeNone) + highlight(with: color) + } + + @objc(highlightWithColor:) + public override func highlight(with color: MTColor) { + radicand.highlight(with: color) + } + + @objc(subAtomForIndexType:) + public func subAtom(forIndexType type: MTMathListSubIndexType) -> MTMathListDisplay? { + switch type { + case .subIndexTypeRadicand: + return radicand + case .subIndexTypeDegree: + return degree + default: + assertionFailure("Not a radical subtype \(type.rawValue)") + return nil + } + } +} + +extension MTMathListDisplay { + @objc(closestIndexToPoint:) + public override func closestIndex(to point: CGPoint) -> MTMathListIndex? { + // Subdisplay origins are relative to this display's position. + let translatedPoint = CGPoint(x: point.x - position.x, y: point.y - position.y) + + var closest: MTDisplay? + var xbounds = [MTDisplay]() + var minDistance = CGFloat.greatestFiniteMagnitude + + for atom in subDisplays { + let bounds = atom.displayBounds() + if bounds.origin.x - pixelDelta <= translatedPoint.x + && translatedPoint.x <= bounds.maxX + pixelDelta + { + xbounds.append(atom) + } + + let distance = distanceFromPointToRect(translatedPoint, bounds) + if distance < minDistance { + closest = atom + minDistance = distance + } + } + + let atomWithPoint: MTDisplay? + if xbounds.isEmpty { + if translatedPoint.x <= -pixelDelta { + // Far to the left. + return MTMathListIndex.level0Index(UInt(range.location)) + } else if translatedPoint.x >= width + pixelDelta { + // Far to the right. + return MTMathListIndex.level0Index(UInt(NSMaxRange(range))) + } else { + // Within mathlist bounds but not in any x-range; use nearest subdisplay. + atomWithPoint = closest + } + } else if xbounds.count == 1 { + atomWithPoint = xbounds[0] + if translatedPoint.x >= width - pixelDelta, + translatedPoint.y <= atomWithPoint!.displayBounds().minY - pixelDelta + { + // Near the end but too high for this atom; place caret at end of list. + return MTMathListIndex.level0Index(UInt(NSMaxRange(range))) + } + } else { + atomWithPoint = closest + } + + guard let atomWithPoint else { return nil } + + let index = atomWithPoint.closestIndex(to: translatedPoint) + + if let closestLine = atomWithPoint as? MTMathListDisplay { + assert( + closestLine.type == .subscript || closestLine.type == .superscript, + "MTLine type regular inside an MTLine - shouldn't happen") + // Subscript/superscript line: wrap the returned index as a nested sub-index. + let type: MTMathListSubIndexType = + (closestLine.type == .subscript) ? .subIndexTypeSubscript : .subIndexTypeSuperscript + let lineIndex = Int(closestLine.index) + guard lineIndex != NSNotFound else { return nil } + return MTMathListIndex(atLocation: UInt(lineIndex), withSubIndex: index, type: type) + } else if atomWithPoint.hasScript, let index { + // If we landed at atom end, caret should be before scripts, not after them. + if Int(index.atomIndex) == NSMaxRange(atomWithPoint.range) { + return MTMathListIndex( + atLocation: index.atomIndex - 1, + withSubIndex: MTMathListIndex.level0Index(1), + type: .subIndexTypeNucleus) + } + } + + return index + } + + @objc(subAtomForIndex:) + public func subAtom(for index: MTMathListIndex) -> MTDisplay? { + // Sub/superscripts are represented as MTMathListDisplay entries in subDisplays. + if index.subIndexType == .subIndexTypeSuperscript + || index.subIndexType == .subIndexTypeSubscript + { + for atom in subDisplays { + if let lineAtom = atom as? MTMathListDisplay, + Int(index.atomIndex) == Int(lineAtom.index) + { + if (lineAtom.type == .subscript && index.subIndexType == .subIndexTypeSubscript) + || (lineAtom.type == .superscript && index.subIndexType == .subIndexTypeSuperscript) + { + return lineAtom + } + } + } + } else { + for atom in subDisplays { + if !(atom is MTMathListDisplay) && NSLocationInRange(Int(index.atomIndex), atom.range) { + // Found the display that covers the requested index. + switch index.subIndexType { + case .subIndexTypeNone, .subIndexTypeNucleus: + return atom + + case .subIndexTypeDegree, .subIndexTypeRadicand: + if let radical = atom as? MTRadicalDisplay { + return radical.subAtom(forIndexType: index.subIndexType) + } + return nil + + case .subIndexTypeNumerator, .subIndexTypeDenominator: + if let frac = atom as? MTFractionDisplay { + return frac.subAtom(forIndexType: index.subIndexType) + } + return nil + + case .subIndexTypeSubscript, .subIndexTypeSuperscript, .subIndexTypeInner: + assertionFailure("Unexpected index type for this path") + return nil + + @unknown default: + return nil + } + } + } + } + return nil + } + + @objc(caretPositionForIndex:) + public override func caretPosition(for index: MTMathListIndex) -> CGPoint { + var pos = invalidPosition + + if Int(index.atomIndex) == NSMaxRange(range) { + // Special-case right edge of the range. + pos = CGPoint(x: width, y: 0) + } else if NSLocationInRange(Int(index.atomIndex), range) { + guard let atom = subAtom(for: index) else { return invalidPosition } + if index.subIndexType == .subIndexTypeNucleus { + guard let subIndex = index.sub else { return invalidPosition } + let nucleusPosition = index.atomIndex + subIndex.atomIndex + pos = atom.caretPosition(for: MTMathListIndex.level0Index(nucleusPosition)) + } else if index.subIndexType == .subIndexTypeNone { + pos = atom.caretPosition(for: index) + } else { + // Recurse into nested substructures. + guard let subIndex = index.sub else { return invalidPosition } + pos = atom.caretPosition(for: subIndex) + } + } else { + return invalidPosition + } + + if pos == invalidPosition { + // Position could not be resolved by subdisplays. + return pos + } + + // Convert from local coordinates before returning. + return CGPoint(x: pos.x + position.x, y: pos.y + position.y) + } + + @objc(highlightCharacterAtIndex:color:) + public override func highlightCharacter(at index: MTMathListIndex, color: MTColor) { + if NSLocationInRange(Int(index.atomIndex), range), let atom = subAtom(for: index) { + if index.subIndexType == .subIndexTypeNucleus || index.subIndexType == .subIndexTypeNone { + atom.highlightCharacter(at: index, color: color) + } else if let subIndex = index.sub { + // Recurse into nested substructures. + atom.highlightCharacter(at: subIndex, color: color) + } + } + } + + @objc(highlightWithColor:) + public override func highlight(with color: MTColor) { + for atom in subDisplays { + atom.highlight(with: color) + } + } +} diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTMathList+Editing.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTMathList+Editing.swift new file mode 100644 index 0000000..a5c67e6 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTMathList+Editing.swift @@ -0,0 +1,293 @@ +// +// MTMathList+Editing.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +import Foundation +import iosMath + +extension MTMathList { + @objc(insertAtom:atListIndex:) + public func insert(_ atom: MTMathAtom, atListIndex index: MTMathListIndex) { + if index.atomIndex > UInt(atoms.count) { + let exception = NSException( + name: .rangeException, + reason: "Index \(index.atomIndex) is out of bounds for list of size \(atoms.count)", + userInfo: nil + ) + exception.raise() + return + } + + switch index.subIndexType { + case .subIndexTypeNone: + insertAtom(atom, at: index.atomIndex) + + case .subIndexTypeNucleus: + let atomIndex = Int(index.atomIndex) + let currentAtom = atoms[atomIndex] + assert( + currentAtom.subScript != nil || currentAtom.superScript != nil, + "Nuclear fusion is not supported if there are no subscripts or superscripts.") + assert( + atom.subScript == nil && atom.superScript == nil, + "Cannot fuse with an atom that already has a subscript or a superscript") + guard let subIndex = index.sub else { return } + + atom.subScript = currentAtom.subScript + atom.superScript = currentAtom.superScript + currentAtom.subScript = nil + currentAtom.superScript = nil + insertAtom(atom, at: index.atomIndex + subIndex.atomIndex) + + case .subIndexTypeDegree, .subIndexTypeRadicand: + let atomIndex = Int(index.atomIndex) + guard let radical = atoms[atomIndex] as? MTRadical, radical.type == .radical else { + // Not radical, quit. + assertionFailure("No radical found at index \(index.atomIndex)") + return + } + guard let subIndex = index.sub else { return } + if index.subIndexType == .subIndexTypeDegree { + radical.degree?.insert(atom, atListIndex: subIndex) + } else { + radical.radicand?.insert(atom, atListIndex: subIndex) + } + + case .subIndexTypeDenominator, .subIndexTypeNumerator: + let atomIndex = Int(index.atomIndex) + guard let frac = atoms[atomIndex] as? MTFraction, frac.type == .fraction else { + // Not a fraction, quit. + assertionFailure("No fraction found at index \(index.atomIndex)") + return + } + guard let subIndex = index.sub else { return } + if index.subIndexType == .subIndexTypeNumerator { + frac.numerator.insert(atom, atListIndex: subIndex) + } else { + frac.denominator.insert(atom, atListIndex: subIndex) + } + + case .subIndexTypeSubscript: + let atomIndex = Int(index.atomIndex) + let current = atoms[atomIndex] + assert(current.subScript != nil, "No subscript for atom at index \(index.atomIndex)") + guard let subIndex = index.sub else { return } + current.subScript?.insert(atom, atListIndex: subIndex) + + case .subIndexTypeSuperscript: + let atomIndex = Int(index.atomIndex) + let current = atoms[atomIndex] + assert(current.superScript != nil, "No superscript for atom at index \(index.atomIndex)") + guard let subIndex = index.sub else { return } + current.superScript?.insert(atom, atListIndex: subIndex) + + case .subIndexTypeInner: + break + + @unknown default: + break + } + } + + @objc(removeAtomAtListIndex:) + public func removeAtom(atListIndex index: MTMathListIndex) { + if index.atomIndex >= UInt(atoms.count) { + let exception = NSException( + name: .rangeException, + reason: "Index \(index.atomIndex) is out of bounds for list of size \(atoms.count)", + userInfo: nil + ) + exception.raise() + return + } + + switch index.subIndexType { + case .subIndexTypeNone: + removeAtom(at: index.atomIndex) + + case .subIndexTypeNucleus: + let atomIndex = Int(index.atomIndex) + let currentAtom = atoms[atomIndex] + assert( + currentAtom.subScript != nil || currentAtom.superScript != nil, + "Nuclear fission is not supported if there are no subscripts or superscripts.") + var previous: MTMathAtom? + if index.atomIndex > 0 { + previous = atoms[Int(index.atomIndex - 1)] + } + if let previous, + previous.subScript == nil, + previous.superScript == nil + { + previous.superScript = currentAtom.superScript + previous.subScript = currentAtom.subScript + removeAtom(at: index.atomIndex) + } else { + // No previous atom, or the previous atom already has a sub/superscript. + currentAtom.nucleus = "" + } + + case .subIndexTypeRadicand, .subIndexTypeDegree: + let atomIndex = Int(index.atomIndex) + guard let radical = atoms[atomIndex] as? MTRadical, radical.type == .radical else { + // Not radical, quit. + assertionFailure("No radical found at index \(index.atomIndex)") + return + } + guard let subIndex = index.sub else { return } + if index.subIndexType == .subIndexTypeDegree { + radical.degree?.removeAtom(atListIndex: subIndex) + } else { + radical.radicand?.removeAtom(atListIndex: subIndex) + } + + case .subIndexTypeDenominator, .subIndexTypeNumerator: + let atomIndex = Int(index.atomIndex) + guard let frac = atoms[atomIndex] as? MTFraction, frac.type == .fraction else { + // Not a fraction, quit. + assertionFailure("No fraction found at index \(index.atomIndex)") + return + } + guard let subIndex = index.sub else { return } + if index.subIndexType == .subIndexTypeNumerator { + frac.numerator.removeAtom(atListIndex: subIndex) + } else { + frac.denominator.removeAtom(atListIndex: subIndex) + } + + case .subIndexTypeSubscript: + let atomIndex = Int(index.atomIndex) + let current = atoms[atomIndex] + assert(current.subScript != nil, "No subscript for atom at index \(index.atomIndex)") + guard let subIndex = index.sub else { return } + current.subScript?.removeAtom(atListIndex: subIndex) + + case .subIndexTypeSuperscript: + let atomIndex = Int(index.atomIndex) + let current = atoms[atomIndex] + assert(current.superScript != nil, "No superscript for atom at index \(index.atomIndex)") + guard let subIndex = index.sub else { return } + current.superScript?.removeAtom(atListIndex: subIndex) + + case .subIndexTypeInner: + break + + @unknown default: + break + } + } + + @objc(removeAtomsInListIndexRange:) + public func removeAtoms(inListIndexRange range: MTMathListRange) { + let start = range.start + + switch start.subIndexType { + case .subIndexTypeNone: + removeAtoms(in: NSRange(location: Int(start.atomIndex), length: Int(range.length))) + + case .subIndexTypeNucleus: + assertionFailure("Nuclear fission is not supported") + + case .subIndexTypeRadicand, .subIndexTypeDegree: + let atomIndex = Int(start.atomIndex) + guard let radical = atoms[atomIndex] as? MTRadical, radical.type == .radical else { + // Not radical, quit. + assertionFailure("No radical found at index \(start.atomIndex)") + return + } + guard let subIndexRange = range.subIndex() else { return } + if start.subIndexType == .subIndexTypeDegree { + radical.degree?.removeAtoms(inListIndexRange: subIndexRange) + } else { + radical.radicand?.removeAtoms(inListIndexRange: subIndexRange) + } + + case .subIndexTypeDenominator, .subIndexTypeNumerator: + let atomIndex = Int(start.atomIndex) + guard let frac = atoms[atomIndex] as? MTFraction, frac.type == .fraction else { + // Not a fraction, quit. + assertionFailure("No fraction found at index \(start.atomIndex)") + return + } + guard let subIndexRange = range.subIndex() else { return } + if start.subIndexType == .subIndexTypeNumerator { + frac.numerator.removeAtoms(inListIndexRange: subIndexRange) + } else { + frac.denominator.removeAtoms(inListIndexRange: subIndexRange) + } + + case .subIndexTypeSubscript: + let atomIndex = Int(start.atomIndex) + let current = atoms[atomIndex] + assert(current.subScript != nil, "No subscript for atom at index \(start.atomIndex)") + guard let subIndexRange = range.subIndex() else { return } + current.subScript?.removeAtoms(inListIndexRange: subIndexRange) + + case .subIndexTypeSuperscript: + let atomIndex = Int(start.atomIndex) + let current = atoms[atomIndex] + assert(current.superScript != nil, "No superscript for atom at index \(start.atomIndex)") + guard let subIndexRange = range.subIndex() else { return } + current.superScript?.removeAtoms(inListIndexRange: subIndexRange) + + case .subIndexTypeInner: + break + + @unknown default: + break + } + } + + @objc(atomAtListIndex:) + public func atom(atListIndex index: MTMathListIndex?) -> MTMathAtom? { + guard let index else { return nil } + guard index.atomIndex < UInt(atoms.count) else { return nil } + let atom = atoms[Int(index.atomIndex)] + + switch index.subIndexType { + case .subIndexTypeNone, .subIndexTypeNucleus: + return atom + + case .subIndexTypeSubscript: + guard let subIndex = index.sub else { return nil } + return atom.subScript?.atom(atListIndex: subIndex) + + case .subIndexTypeSuperscript: + guard let subIndex = index.sub else { return nil } + return atom.superScript?.atom(atListIndex: subIndex) + + case .subIndexTypeRadicand, .subIndexTypeDegree: + guard let radical = atom as? MTRadical, atom.type == .radical else { + // No radical at this index. + return nil + } + guard let subIndex = index.sub else { return nil } + if index.subIndexType == .subIndexTypeDegree { + return radical.degree?.atom(atListIndex: subIndex) + } else { + return radical.radicand?.atom(atListIndex: subIndex) + } + + case .subIndexTypeNumerator, .subIndexTypeDenominator: + guard let frac = atom as? MTFraction, atom.type == .fraction else { + // No fraction at this index. + return nil + } + guard let subIndex = index.sub else { return nil } + if index.subIndexType == .subIndexTypeDenominator { + return frac.denominator.atom(atListIndex: subIndex) + } else { + return frac.numerator.atom(atListIndex: subIndex) + } + + case .subIndexTypeInner: + return nil + + @unknown default: + return nil + } + } +} diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTTapGestureRecognizer.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTTapGestureRecognizer.swift new file mode 100644 index 0000000..68b65fe --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTTapGestureRecognizer.swift @@ -0,0 +1,9 @@ +import Foundation + +#if canImport(UIKit) + import UIKit + public typealias MTTapGestureRecognizer = UITapGestureRecognizer +#elseif canImport(AppKit) + import AppKit + public typealias MTTapGestureRecognizer = NSClickGestureRecognizer +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView+AutoLayout.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView+AutoLayout.swift new file mode 100644 index 0000000..c38bef3 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView+AutoLayout.swift @@ -0,0 +1,34 @@ +// +// MTView+AutoLayout.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +import Foundation + +#if canImport(AppKit) + import AppKit +#elseif canImport(UIKit) + import UIKit +#endif + +extension MTView { + public func pinToSuperview() { + pinToSuperview(withTop: 0, leading: 0, bottom: 0, trailing: 0) + } + + public func pinToSuperview( + withTop top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat + ) { + guard let superview else { return } + translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + topAnchor.constraint(equalTo: superview.topAnchor, constant: top), + leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: leading), + trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: -trailing), + bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -bottom), + ]) + } +} diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView.swift new file mode 100644 index 0000000..690469a --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/MTView.swift @@ -0,0 +1,26 @@ +// +// MTView.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +import Foundation + +#if canImport(UIKit) + import UIKit + public typealias MTView = UIView + public typealias MTColor = UIColor + public typealias MTBezierPath = UIBezierPath + public typealias MTImage = UIImage + public typealias MTImageView = UIImageView + public typealias MTEdgeInsets = UIEdgeInsets +#elseif canImport(AppKit) + import AppKit + public typealias MTView = NSView + public typealias MTColor = NSColor + public typealias MTBezierPath = NSBezierPath + public typealias MTImage = NSImage + public typealias MTImageView = NSImageView + public typealias MTEdgeInsets = NSEdgeInsets +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSColor+label.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSColor+label.swift new file mode 100644 index 0000000..bc7cf97 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSColor+label.swift @@ -0,0 +1,17 @@ +// +// NSColor+labelColor.swift +// MathEditorSwift +// +// Created by Madiyar Aitbayev on 26/03/2026. +// + +#if canImport(AppKit) + import AppKit + + extension NSColor { + static var label: NSColor { + NSColor.labelColor + } + } + +#endif // canImport(AppKit) diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+FirstResponder.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+FirstResponder.swift new file mode 100644 index 0000000..c7f15d5 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+FirstResponder.swift @@ -0,0 +1,18 @@ +// +// MTView+FirstResponder.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +#if os(macOS) + + import AppKit + + extension NSView { + @objc public var isFirstResponder: Bool { + window?.firstResponder == self + } + } + +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+HitTest.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+HitTest.swift new file mode 100644 index 0000000..32ffa20 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+HitTest.swift @@ -0,0 +1,38 @@ +// +// MTView+HitTest.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +#if canImport(AppKit) + import AppKit + extension NSView { + @objc public func hitTestOutsideBounds(_ point: NSPoint) -> NSView? { + hitTestOutsideBounds(point, ignoringSubviews: []) + } + + @objc public func hitTestOutsideBounds(_ point: NSPoint, ignoringSubviews: [NSView]) -> NSView? + { + if isHidden { + return nil + } + + let localPoint = convert(point, from: superview) + for child in subviews.reversed() { + if ignoringSubviews.contains(child) { + continue + } + if let hitView = child.hitTest(localPoint) { + return hitView + } + } + + if bounds.contains(localPoint) { + return self + } + + return nil + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+Layout.swift b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+Layout.swift new file mode 100644 index 0000000..848d492 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/Internal/MTView/NSView+Layout.swift @@ -0,0 +1,34 @@ +// +// NSView+Layout.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 24/03/2026. +// + +#if canImport(AppKit) + import AppKit + + extension NSView { + @objc(setNeedsLayout) + public func setNeedsLayout() { + self.needsDisplay = true + } + + @objc(setNeedsDisplay) + public func setNeedsDisplay() { + self.needsDisplay = true + } + + @objc(layoutIfNeeded) + public func layoutIfNeeded() { + layoutSubtreeIfNeeded() + } + + @objc(bringSubviewToFront:) + public func bringSubviewToFront(_ child: NSView) { + guard child.superview == self else { return } + child.removeFromSuperview() + addSubview(child) + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+NSView.swift b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+NSView.swift new file mode 100644 index 0000000..ddf00c3 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+NSView.swift @@ -0,0 +1,18 @@ +import Foundation + +#if canImport(AppKit) + import AppKit + + extension MTEditableMathLabelSwift { + public override func layout() { + super.layout() + doLayout() + } + + public override var isFlipped: Bool { true } + + public override func hitTest(_ point: NSPoint) -> NSView? { + hitTestOutsideBounds(point, ignoringSubviews: [label]) + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+Responder.swift b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+Responder.swift new file mode 100644 index 0000000..f133f10 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+Responder.swift @@ -0,0 +1,51 @@ +import Foundation + +#if canImport(UIKit) + import UIKit +#endif +#if canImport(AppKit) + import AppKit +#endif + +extension MTEditableMathLabelSwift { + public override func becomeFirstResponder() -> Bool { + let didBecome = super.becomeFirstResponder() + if didBecome { + doBecomeFirstResponder() + } + return didBecome + } + + public override func resignFirstResponder() -> Bool { + guard isFirstResponder else { return true } + let didResign = super.resignFirstResponder() + doResignFirstResponder() + return didResign + } +} + +#if canImport(UIKit) + extension MTEditableMathLabelSwift { + public override var inputView: UIView? { + keyboard as? UIView + } + + public override var canBecomeFirstResponder: Bool { true } + } +#endif + +#if canImport(AppKit) + extension MTEditableMathLabelSwift { + public override var acceptsFirstResponder: Bool { true } + + public override func keyDown(with event: NSEvent) { + // interpretKeyEvents feeds the event into the input system, + // which calls insertText: or deleteBackward: as appropriate. + interpretKeyEvents([event]) + } + + public override func deleteBackward(_ sender: Any?) { + deleteBackward() + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UITextInput.swift b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UITextInput.swift new file mode 100644 index 0000000..d1dc6c2 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UITextInput.swift @@ -0,0 +1,179 @@ +import Foundation + +#if canImport(UIKit) + import ObjectiveC + import UIKit + + // These are blank just to get a UITextInput implementation, to fix the dictation button bug. + // Proposed fix from: http://stackoverflow.com/questions/20980898/work-around-for-dictation-custom-text-view-bug + + extension MTEditableMathLabelSwift: UITextInput { + public var selectedTextRange: UITextRange? { + get { + textInputHandler.selectedTextRange + } + set { + textInputHandler.selectedTextRange = newValue + } + } + + public var inputDelegate: UITextInputDelegate? { + get { + textInputHandler.inputDelegate + } + set { + textInputHandler.inputDelegate = newValue + } + } + + public var markedTextRange: UITextRange? { + textInputHandler.markedTextRange + } + + public var markedTextStyle: [NSAttributedString.Key: Any]? { + get { + textInputHandler.markedTextStyle + } + set { + textInputHandler.markedTextStyle = newValue + } + } + + public var beginningOfDocument: UITextPosition { + textInputHandler.beginningOfDocument + } + + public var endOfDocument: UITextPosition { + textInputHandler.endOfDocument + } + + public var tokenizer: UITextInputTokenizer { + textInputHandler.tokenizer + } + + public func baseWritingDirection( + for position: UITextPosition, + in direction: UITextStorageDirection + ) -> NSWritingDirection { + .leftToRight + } + + public func caretRect(for position: UITextPosition) -> CGRect { + .zero + } + + public func unmarkText() {} + + public func characterRange(at point: CGPoint) -> UITextRange? { + nil + } + + public func characterRange( + byExtending position: UITextPosition, + in direction: UITextLayoutDirection + ) -> UITextRange? { + nil + } + + public func closestPosition(to point: CGPoint) -> UITextPosition? { + nil + } + + public func closestPosition(to point: CGPoint, within range: UITextRange) -> UITextPosition? { + nil + } + + public func compare(_ position: UITextPosition, to other: UITextPosition) -> ComparisonResult { + .orderedSame + } + + public func dictationRecognitionFailed() {} + + public func dictationRecordingDidEnd() {} + + public func firstRect(for range: UITextRange) -> CGRect { + .zero + } + + public func frame( + forDictationResultPlaceholder placeholder: Any + ) -> CGRect { + .zero + } + + public func insertDictationResult(_ dictationResult: [UIDictationPhrase]) {} + + public var insertDictationResultPlaceholder: Any { 0 } + + public func offset(from: UITextPosition, to toPosition: UITextPosition) -> Int { + 0 + } + + public func position( + from position: UITextPosition, + in direction: UITextLayoutDirection, + offset: Int + ) -> UITextPosition? { + nil + } + + public func position(from position: UITextPosition, offset: Int) -> UITextPosition? { + nil + } + + public func position( + within range: UITextRange, + farthestIn direction: UITextLayoutDirection + ) -> UITextPosition? { + nil + } + + public func removeDictationResultPlaceholder( + _ placeholder: Any, + willInsertResult: Bool + ) {} + + public func replace(_ range: UITextRange, withText text: String) {} + + public func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { + [] + } + + public func setBaseWritingDirection( + _ writingDirection: NSWritingDirection, + for range: UITextRange + ) {} + + public func setMarkedText(_ markedText: String?, selectedRange: NSRange) {} + + public func text(in range: UITextRange) -> String? { + nil + } + + public func textRange(from fromPosition: UITextPosition, to toPosition: UITextPosition) + -> UITextRange? + { + nil + } + + public var autocapitalizationType: UITextAutocapitalizationType { + .none + } + + public var autocorrectionType: UITextAutocorrectionType { + .no + } + + public var returnKeyType: UIReturnKeyType { + .default + } + + public var spellCheckingType: UITextSpellCheckingType { + .no + } + + public var keyboardType: UIKeyboardType { + .asciiCapable + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UIView.swift b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UIView.swift new file mode 100644 index 0000000..961660c --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift+UIView.swift @@ -0,0 +1,19 @@ +import Foundation + +#if canImport(UIKit) + import UIKit + + extension MTEditableMathLabelSwift { + public override func layoutSubviews() { + super.layoutSubviews() + doLayout() + } + + public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + if super.point(inside: point, with: event) { + return true + } + return caretView.point(inside: convert(point, to: caretView), with: event) + } + } +#endif diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift.swift b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift.swift new file mode 100644 index 0000000..f7a1608 --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTEditableMathLabelSwift.swift @@ -0,0 +1,771 @@ +import Foundation +import iosMath + +private let greekLowerStart: UInt32 = 0x03B1 +private let greekLowerEnd: UInt32 = 0x03C9 +private let greekCapitalStart: UInt32 = 0x0391 +private let greekCapitalEnd: UInt32 = 0x03A9 + +/// Delegate for the `MTEditableMathLabel`. All methods are optional. +public protocol MTEditableMathLabelDelegate: AnyObject { + func returnPressed(_ label: MTEditableMathLabelSwift) + func textModified(_ label: MTEditableMathLabelSwift) + func didBeginEditing(_ label: MTEditableMathLabelSwift) + func didEndEditing(_ label: MTEditableMathLabelSwift) +} + +extension MTEditableMathLabelDelegate { + public func returnPressed(_ label: MTEditableMathLabelSwift) {} + public func textModified(_ label: MTEditableMathLabelSwift) {} + public func didBeginEditing(_ label: MTEditableMathLabelSwift) {} + public func didEndEditing(_ label: MTEditableMathLabelSwift) {} +} + +/// This protocol provides information on the context of the current insertion point. +/// The keyboard may choose to enable/disable/highlight certain parts of the UI depending on the context. +/// e.g. you cannot enter the = sign when you are in a fraction so the keyboard could disable that. +public protocol MTMathKeyboardTraits { + var equalsAllowed: Bool { get set } + var fractionsAllowed: Bool { get set } + var variablesAllowed: Bool { get set } + var numbersAllowed: Bool { get set } + var operatorsAllowed: Bool { get set } + var exponentHighlighted: Bool { get set } + var squareRootHighlighted: Bool { get set } + var radicalHighlighted: Bool { get set } +} + +/// Any keyboard that provides input to the `MTEditableMathUILabel` must implement +/// this protocol. +/// +/// This protocol informs the keyboard when a particular `MTEditableMathUILabel` is being edited. +/// The keyboard should use this information to send `MTKeyInput` messages to the label. +/// +/// This protocol inherits from `MTMathKeyboardTraits`. +public protocol MTMathKeyboard: AnyObject, MTMathKeyboardTraits { + func startedEditing(_ label: MTView & MTKeyInput) + func finishedEditing(_ label: MTView & MTKeyInput) +} + +@objc(MTEditableMathLabelSwift) +public final class MTEditableMathLabelSwift: MTView, MTKeyInput { + @objc public var mathList: MTMathList = MTMathList() { + didSet { + label.mathList = mathList + insertionIndex = MTMathListIndex.level0Index(UInt(mathList.atoms.count)) + insertionPointChanged() + } + } + + @objc public var highlightColor: MTColor = .systemRed + + @objc public var textColor: MTColor? { + get { label.textColor } + set { label.textColor = newValue ?? label.textColor } + } + + @objc public var caretColor: MTColor { + get { caretView.caretColor } + set { caretView.caretColor = newValue } + } + + @objc public private(set) var cancelImage: MTCancelView? + @objc private(set) var caretView: MTCaretView! + public weak var delegate: MTEditableMathLabelDelegate? + public weak var keyboard: MTMathKeyboard? + + @objc public var fontSize: CGFloat { + get { label.fontSize } + set { + label.fontSize = newValue + caretView.setFontSize(newValue) + insertionPointChanged() + } + } + + @objc public var contentInsets: MTEdgeInsets { + get { label.contentInsets } + set { label.contentInsets = newValue } + } + + let label = MTMathUILabel(frame: .zero) + private var tapGestureRecognizer: MTTapGestureRecognizer? + private var insertionIndex: MTMathListIndex? + private var flipTransform = CGAffineTransform.identity + + var textInputHandler = DummyTextInputHandler() + + public override init(frame: CGRect) { + super.init(frame: frame) + initialize() + } + + @available(*, unavailable) + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override var backgroundColor: MTColor? { + didSet { + label.backgroundColor = backgroundColor + } + } + + @objc public func clear() { + mathList = MTMathList() + caretView.showHandle(false) + } + + @objc(highlightCharacterAtIndex:) + public func highlightCharacter(at index: MTMathListIndex) { + label.layoutIfNeeded() + guard let displayList = label.displayList else { return } + displayList.highlightCharacter(at: index, color: highlightColor) + setNeedsDisplay() + } + + @objc public func clearHighlights() { + setNeedsLayout() + } + + @objc(moveCaretToPoint:) + public func moveCaret(to point: CGPoint) { + insertionIndex = closestIndex(to: point) + insertionPointChanged() + } + + @objc public func startEditing() { + guard !isFirstResponder else { return } + #if canImport(AppKit) + window?.makeFirstResponder(self) + #else + becomeFirstResponder() + #endif + } + + @objc(enableTap:) + public func enableTap(_ enabled: Bool) { + tapGestureRecognizer?.isEnabled = enabled + } + + @objc(insertMathList:atPoint:) + public func insertMathList(_ list: MTMathList, at point: CGPoint) { + guard let detailedIndex = closestIndex(to: point) else { return } + // insert at the given index - but don't consider sublevels at this point + var index = MTMathListIndex.level0Index(detailedIndex.atomIndex) + for atom in list.atoms { + mathList.insert(atom, atListIndex: index) + index = index.next() + } + label.mathList = mathList + insertionIndex = index + insertionPointChanged() + } + + @objc public func mathDisplaySize() -> CGSize { + label.sizeThatFits(label.bounds.size) + } + + @objc public func doLayout() { + cancelImage?.frame = CGRect( + x: frame.size.width - 55, y: (frame.size.height - 45) / 2, width: 45, height: 45) + // update the flip transform + let transform = CGAffineTransform(translationX: 0, y: bounds.size.height) + flipTransform = CGAffineTransform(scaleX: 1, y: -1).concatenating(transform) + label.layoutIfNeeded() + insertionPointChanged() + } + + @objc public func doBecomeFirstResponder() { + if insertionIndex == nil { + insertionIndex = MTMathListIndex.level0Index(UInt(mathList.atoms.count)) + } + keyboard?.startedEditing(self) + insertionPointChanged() + delegate?.didBeginEditing(self) + } + + @objc public func doResignFirstResponder() { + keyboard?.finishedEditing(self) + insertionPointChanged() + delegate?.didEndEditing(self) + } + + @objc(insertText:) + public func insertText(_ string: String) { + if string == "\n" { + delegate?.returnPressed(self) + return + } + + guard !string.isEmpty else { return } + let scalar = string.unicodeScalars.first! + var insertedAtom: MTMathAtom? + + if string.count > 1 { + // Check if this is a supported command + insertedAtom = MTMathAtomFactory.atom(forLatexSymbolName: string) + } else { + insertedAtom = atom(forCharacter: scalar) + } + + if insertionIndex?.subIndexType == .subIndexTypeDenominator, insertedAtom?.type == .relation { + insertionIndex = insertionIndex?.levelDown()?.next() + } + + switch string { + case String(Character("^")): + handleExponentButton() + case MTSymbolSquareRoot: + handleRadical(withDegreeButtonPressed: false) + case MTSymbolCubeRoot: + handleRadical(withDegreeButtonPressed: true) + case String(Character("_")): + handleSubscriptButton() + case String(Character("/")): + handleSlashButton() + case "()": + removePlaceholderIfPresent() + insertParens() + case "||": + removePlaceholderIfPresent() + insertAbsValue() + default: + if let insertedAtom, let insertionIndex { + if !updatePlaceholderIfPresent(insertedAtom) { + // If a placeholder wasn't updated then insert the new element. + mathList.insert(insertedAtom, atListIndex: insertionIndex) + } + if insertedAtom.type == .fraction { + // go to the numerator + self.insertionIndex = insertionIndex.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: .subIndexTypeNumerator) + } else { + self.insertionIndex = insertionIndex.next() + } + } + } + + label.mathList = mathList + insertionPointChanged() + + // If trig function, insert parens after + if isTrigFunction(string) { + insertParens() + } + + delegate?.textModified(self) + } + + @objc public func deleteBackward() { + guard hasText, var previousIndex = insertionIndex?.previous() else { return } + + // delete the last atom from the list + mathList.removeAtom(atListIndex: previousIndex) + if previousIndex.finalSubIndexType() == MTMathListSubIndexType.subIndexTypeNucleus, + let downIndex = previousIndex.levelDown() + { + if let previous = downIndex.previous() { + previousIndex = previous.levelUp( + withSubIndex: MTMathListIndex.level0Index(1), + type: MTMathListSubIndexType.subIndexTypeNucleus) + } else { + previousIndex = downIndex + } + } + insertionIndex = previousIndex + + if insertionIndex?.isAtBeginningOfLine() == true, + insertionIndex?.subIndexType != .subIndexTypeNone + { + // We have deleted to the beginning of the line and it is not the outermost line + if mathList.atom(atListIndex: insertionIndex) == nil, let insertionIndex { + // add a placeholder if we deleted everything in the list + let atom = MTMathAtomFactory.placeholder() + // mark the placeholder as selected since that is the current insertion point. + atom.nucleus = MTSymbolBlackSquare + mathList.insert(atom, atListIndex: insertionIndex) + } + } + + label.mathList = mathList + insertionPointChanged() + delegate?.textModified(self) + } + + @objc public var hasText: Bool { + !mathList.atoms.isEmpty + } + + @objc(closestIndexToPoint:) + public func closestIndex(to point: CGPoint) -> MTMathListIndex? { + label.layoutIfNeeded() + // no mathlist, so can't figure it out. + guard let displayList = label.displayList else { return nil } + return displayList.closestIndex(to: convert(point, to: label)) + } + + @objc(caretRectForIndex:) + public func caretRect(for index: MTMathListIndex) -> CGPoint { + label.layoutIfNeeded() + // no mathlist so we can't figure it out. + guard let displayList = label.displayList else { return .zero } + return displayList.caretPosition(for: index) + } +} + +extension MTEditableMathLabelSwift { + fileprivate func initialize() { + // Add tap gesture recognizer to let the user enter editing mode. + let tap = MTTapGestureRecognizer(target: self, action: #selector(tap(_:))) + addGestureRecognizer(tap) + tapGestureRecognizer = tap + + // Create and set up the APLSimpleCoreTextView that will do the drawing. + addSubview(label) + label.pinToSuperview() + label.fontSize = 30 + label.backgroundColor = backgroundColor + #if canImport(UIKit) + label.isUserInteractionEnabled = false + #endif + label.textAlignment = .center + + createCancelImage() + + let transform = CGAffineTransform(translationX: 0, y: bounds.size.height) + flipTransform = CGAffineTransform(scaleX: 1, y: -1).concatenating(transform) + + caretView = MTCaretView(editor: self) + caretView.caretColor = MTColor(white: 0.1, alpha: 1.0) + + highlightColor = MTColor.systemRed + bringSubviewToFront(cancelImage!) + // start with an empty math list + mathList = MTMathList() + } + + fileprivate func createCancelImage() { + guard cancelImage == nil else { return } + let cancelImage = MTCancelView(target: self, action: #selector(clear)) + cancelImage.frame = CGRect( + x: frame.size.width - 55, y: (frame.size.height - 45) / 2, width: 45, height: 45) + addSubview(cancelImage) + self.cancelImage = cancelImage + } + + @objc fileprivate func tap(_ tap: MTTapGestureRecognizer) { + handleTap(at: tap.location(in: self)) + } + + fileprivate func handleTap(at point: CGPoint) { + if !isFirstResponder { + insertionIndex = nil + caretView.showHandle(false) + startEditing() + return + } + + // If already editing move the cursor and show handle + insertionIndex = + closestIndex(to: point) ?? MTMathListIndex.level0Index(UInt(mathList.atoms.count)) + caretView.showHandle(true) + insertionPointChanged() + } + + fileprivate static func clearPlaceholders(in mathList: MTMathList?) { + guard let mathList else { return } + for atom in mathList.atoms { + if atom.type == .placeholder { + atom.nucleus = MTSymbolWhiteSquare + } + if atom.superScript != nil { + clearPlaceholders(in: atom.superScript) + } + if atom.subScript != nil { + clearPlaceholders(in: atom.subScript) + } + if atom.type == .radical, let radical = atom as? MTRadical { + clearPlaceholders(in: radical.degree) + clearPlaceholders(in: radical.radicand) + } + if atom.type == .fraction, let fraction = atom as? MTFraction { + clearPlaceholders(in: fraction.numerator) + clearPlaceholders(in: fraction.denominator) + } + } + } + + // Helper method to update caretView when insertion point/selection changes. + fileprivate func insertionPointChanged() { + // If not in editing mode, we don't show the caret. + guard isFirstResponder else { + caretView.removeFromSuperview() + cancelImage?.isHidden = true + return + } + + Self.clearPlaceholders(in: mathList) + + if let index = insertionIndex, let atom = mathList.atom(atListIndex: index), + atom.type == .placeholder + { + atom.nucleus = MTSymbolBlackSquare + if index.finalSubIndexType() == .subIndexTypeNucleus { + // If the insertion index is inside a placeholder, move it out. + insertionIndex = index.levelDown() + } + } else if let previousIndex = insertionIndex?.previous(), + let atom = mathList.atom(atListIndex: previousIndex), + atom.type == .placeholder, + atom.superScript == nil, + atom.subScript == nil + { + insertionIndex = previousIndex + atom.nucleus = MTSymbolBlackSquare + } + + setKeyboardMode() + + // Find the insert point rect and create a caretView to draw the caret at this position. + guard let insertionIndex else { return } + let caretPosition = caretRect(for: insertionIndex) + // Check tht we were returned a valid position before displaying a caret there. + guard caretPosition != CGPoint(x: -1, y: -1) else { return } + + // caretFrame is in the flipped coordinate system, flip it back + caretView.setPosition(caretPosition.applying(flipTransform)) + if caretView.superview == nil { + addSubview(caretView) + setNeedsDisplay() + } + + // when a caret is displayed, the X symbol should be as well + cancelImage?.isHidden = false + // Set up a timer to "blink" the caret. + caretView.delayBlink() + label.setNeedsLayout() + } + + fileprivate func setKeyboardMode() { + keyboard?.exponentHighlighted = false + keyboard?.radicalHighlighted = false + keyboard?.squareRootHighlighted = false + keyboard?.equalsAllowed = true + + if insertionIndex?.hasSubIndex(of: .subIndexTypeSuperscript) == true { + keyboard?.exponentHighlighted = true + keyboard?.equalsAllowed = false + } + if insertionIndex?.subIndexType == .subIndexTypeNumerator { + keyboard?.equalsAllowed = false + } else if insertionIndex?.subIndexType == .subIndexTypeDenominator { + keyboard?.equalsAllowed = false + } + + if insertionIndex?.subIndexType == .subIndexTypeDegree { + keyboard?.radicalHighlighted = true + } else if insertionIndex?.subIndexType == .subIndexTypeRadicand { + keyboard?.squareRootHighlighted = true + } + } + + fileprivate func atom(forCharacter scalar: UnicodeScalar) -> MTMathAtom? { + let string = String(scalar) + // Get the basic conversion from MTMathAtomFactory, and then special case + // unicode characters and latex special characters. + if let atom = MTMathAtomFactory.atom(forCharacter: UInt16(scalar.value)) { + return atom + } + switch string { + case MTSymbolMultiplication: + return MTMathAtomFactory.times() + case MTSymbolSquareRoot: + return MTMathAtomFactory.placeholderSquareRoot() + case MTSymbolInfinity, MTSymbolDegree, MTSymbolAngle: + return MTMathAtom(type: .ordinary, value: string) + case MTSymbolDivision: + return MTMathAtomFactory.divide() + case MTSymbolFractionSlash: + return MTMathAtomFactory.placeholderFraction() + case "{": + return MTMathAtom(type: .open, value: string) + case "}": + return MTMathAtom(type: .close, value: string) + case MTSymbolGreaterEqual, MTSymbolLessEqual: + return MTMathAtom(type: .relation, value: string) + case "*": + return MTMathAtomFactory.times() + case "/": + return MTMathAtomFactory.divide() + default: + break + } + + let value = scalar.value + if (greekLowerStart...greekLowerEnd).contains(value) + || (greekCapitalStart...greekCapitalEnd).contains(value) + { + // All greek chars are rendered as variables. + return MTMathAtom(type: .variable, value: string) + } + if value < 0x21 || value > 0x7E || string == "'" || string == "~" { + // not ascii + return nil + } + // just an ordinary character + return MTMathAtom(type: .ordinary, value: string) + } + + fileprivate func handleExponentButton() { + handleScriptButton(.subIndexTypeSuperscript) + } + + fileprivate func handleSubscriptButton() { + handleScriptButton(.subIndexTypeSubscript) + } + + fileprivate func handleScriptButton(_ type: MTMathListSubIndexType) { + guard let insertionIndex else { return } + if insertionIndex.hasSubIndex(of: type) { + // The index is currently inside a script. The button gets it out of the script and move forward. + self.insertionIndex = getIndexAfterSpecialStructure(insertionIndex, type: type) + return + } + + if !insertionIndex.isAtBeginningOfLine(), + let atom = mathList.atom(atListIndex: insertionIndex.previous()) + { + let hadScript = scriptList(for: atom, type: type) != nil + let count = ensureScriptList(for: atom, type: type).atoms.count + if !hadScript { + self.insertionIndex = insertionIndex.previous()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: type) + } else if insertionIndex.finalSubIndexType() == .subIndexTypeNucleus { + // If we are already inside the nucleus, then we come out and go up to the script + self.insertionIndex = insertionIndex.levelDown()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(UInt(count)), type: type) + } else { + self.insertionIndex = insertionIndex.previous()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(UInt(count)), type: type) + } + return + } + + let emptyAtom = MTMathAtomFactory.placeholder() + setScriptList(makePlaceholderMathList(), on: emptyAtom, type: type) + if !updatePlaceholderIfPresent(emptyAtom) { + // If the placeholder hasn't been updated then insert it. + mathList.insert(emptyAtom, atListIndex: insertionIndex) + } + self.insertionIndex = insertionIndex.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: type) + } + + fileprivate func getIndexAfterSpecialStructure( + _ index: MTMathListIndex, type: MTMathListSubIndexType + ) -> MTMathListIndex { + var nextIndex = index + while nextIndex.hasSubIndex(of: type) { + nextIndex = nextIndex.levelDown() ?? nextIndex + } + //Point to just after this node. + return nextIndex.next() + } + + fileprivate func handleSlashButton() { + guard let insertionIndex else { return } + // special / handling - makes the thing a fraction + let numerator = MTMathList() + var current = insertionIndex + while !current.isAtBeginningOfLine() { + guard let atom = mathList.atom(atListIndex: current.previous()) else { break } + if atom.type != .number && atom.type != .variable { + // we don't put this atom on the fraction + break + } + // add the number to the beginning of the list + numerator.insert(atom, atListIndex: MTMathListIndex.level0Index(0)) + current = current.previous()! + } + + if current.atomIndex == insertionIndex.atomIndex { + // so we didn't really find any numbers before this, so make the numerator 1 + if let atom = atom(forCharacter: "1".unicodeScalars.first!) { + numerator.addAtom(atom) + } + if !current.isAtBeginningOfLine(), + let previousAtom = mathList.atom(atListIndex: current.previous()), + previousAtom.type == .fraction + { + let times = MTMathAtomFactory.times() + // add a times symbol + mathList.insert(times, atListIndex: current) + current = current.next() + } + } else { + // delete stuff in the mathlist from current to _insertionIndex + mathList.removeAtoms( + inListIndexRange: MTMathListRange.make( + current, length: insertionIndex.atomIndex - current.atomIndex)) + } + + let fraction = MTFraction() + fraction.denominator = MTMathList() + fraction.denominator.addAtom(MTMathAtomFactory.placeholder()) + fraction.numerator = numerator + // insert it + mathList.insert(fraction, atListIndex: current) + // update the insertion index to go the denominator + self.insertionIndex = current.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: .subIndexTypeDenominator) + } + + fileprivate func getOutOfRadical(_ index: MTMathListIndex) -> MTMathListIndex { + var index = index + if index.hasSubIndex(of: .subIndexTypeDegree) { + index = getIndexAfterSpecialStructure(index, type: .subIndexTypeDegree) + } + if index.hasSubIndex(of: .subIndexTypeRadicand) { + index = getIndexAfterSpecialStructure(index, type: .subIndexTypeRadicand) + } + return index + } + + fileprivate func handleRadical(withDegreeButtonPressed: Bool) { + guard let current = insertionIndex else { return } + + if current.hasSubIndex(of: .subIndexTypeDegree) + || current.hasSubIndex(of: .subIndexTypeRadicand), + let radical = mathList.atoms[Int(current.atomIndex)] as? MTRadical + { + if withDegreeButtonPressed { + if radical.degree == nil { + radical.degree = MTMathList() + radical.degree?.addAtom(MTMathAtomFactory.placeholder()) + insertionIndex = current.levelDown()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: .subIndexTypeDegree) + } else if current.hasSubIndex(of: .subIndexTypeRadicand) { + // If the cursor is at the radicand, switch it to the degree + insertionIndex = current.levelDown()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: .subIndexTypeDegree) + } else { + // If the cursor is at the degree, get out of the radical + insertionIndex = getOutOfRadical(current) + } + } else if current.hasSubIndex(of: .subIndexTypeDegree) { + // If the radical the cursor at has a degree, and the cursor is at the degree, move the cursor to the radicand. + insertionIndex = current.levelDown()?.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), type: .subIndexTypeRadicand) + } else { + // If the cursor is at the radicand, get out of the radical. + insertionIndex = getOutOfRadical(current) + } + return + } + + let radical = + withDegreeButtonPressed + ? MTMathAtomFactory.placeholderRadical() : MTMathAtomFactory.placeholderSquareRoot() + mathList.insert(radical, atListIndex: current) + insertionIndex = current.levelUp( + withSubIndex: MTMathListIndex.level0Index(0), + type: withDegreeButtonPressed ? .subIndexTypeDegree : .subIndexTypeRadicand + ) + } + + fileprivate func removePlaceholderIfPresent() { + guard let insertionIndex, let current = mathList.atom(atListIndex: insertionIndex), + current.type == .placeholder + else { return } + // remove this element - the inserted text replaces the placeholder + mathList.removeAtom(atListIndex: insertionIndex) + } + + // Returns true if updated + fileprivate func updatePlaceholderIfPresent(_ atom: MTMathAtom) -> Bool { + guard let insertionIndex, + let current = mathList.atom(atListIndex: insertionIndex), + current.type == .placeholder + else { return false } + if let superScript = current.superScript { + atom.superScript = superScript + } + if let subScript = current.subScript { + atom.subScript = subScript + } + // remove the placeholder and replace with atom. + mathList.removeAtom(atListIndex: insertionIndex) + mathList.insert(atom, atListIndex: insertionIndex) + return true + } + + // Return YES if string is a trig function, otherwise return NO + fileprivate func isTrigFunction(_ string: String) -> Bool { + ["sin", "cos", "tan", "sec", "csc", "cot"].contains(string) + } + + fileprivate func insertParens() { + insertPairedAtoms(open: "(", close: ")") + } + + fileprivate func insertAbsValue() { + insertPairedAtoms(open: "|", close: "|") + } + + fileprivate func insertPairedAtoms(open: Character, close: Character) { + guard let openAtom = atom(forCharacter: open.unicodeScalars.first!), + let closeAtom = atom(forCharacter: close.unicodeScalars.first!), + let insertionIndex + else { return } + mathList.insert(openAtom, atListIndex: insertionIndex) + self.insertionIndex = insertionIndex.next() + if let insertionIndex = self.insertionIndex { + mathList.insert(closeAtom, atListIndex: insertionIndex) + } + // Don't go to the next insertion index, to start inserting before the close parens. + } + + fileprivate func makePlaceholderMathList() -> MTMathList { + let list = MTMathList() + list.addAtom(MTMathAtomFactory.placeholder()) + return list + } + + fileprivate func scriptList(for atom: MTMathAtom, type: MTMathListSubIndexType) -> MTMathList? { + switch type { + case .subIndexTypeSuperscript: + atom.superScript + case .subIndexTypeSubscript: + atom.subScript + default: + nil + } + } + + fileprivate func setScriptList( + _ list: MTMathList?, on atom: MTMathAtom, type: MTMathListSubIndexType + ) { + switch type { + case .subIndexTypeSuperscript: + atom.superScript = list + case .subIndexTypeSubscript: + atom.subScript = list + default: + break + } + } + + @discardableResult + fileprivate func ensureScriptList(for atom: MTMathAtom, type: MTMathListSubIndexType) + -> MTMathList + { + if let list = scriptList(for: atom, type: type) { + return list + } + let list = makePlaceholderMathList() + setScriptList(list, on: atom, type: type) + return list + } + +} diff --git a/MathEditorSwift/Sources/MathEditorSwift/MTKeyInput.swift b/MathEditorSwift/Sources/MathEditorSwift/MTKeyInput.swift new file mode 100644 index 0000000..dca95ad --- /dev/null +++ b/MathEditorSwift/Sources/MathEditorSwift/MTKeyInput.swift @@ -0,0 +1,17 @@ +// +// MTKeyInput.swift +// MathEditorSwift +// +// Created by Madiyar Aitbayev on 26/03/2026. +// + +#if os(iOS) + import UIKit + public typealias MTKeyInput = UIKeyInput +#else + public protocol MTKeyInput { + var hasText: Bool { get } + func insertText(_ text: String) + func deleteBackward() + } +#endif diff --git a/MathEditorSwift/Tests/MathEditorSwiftTests/MTDisplayEditingTests.swift b/MathEditorSwift/Tests/MathEditorSwiftTests/MTDisplayEditingTests.swift new file mode 100644 index 0000000..c4b441a --- /dev/null +++ b/MathEditorSwift/Tests/MathEditorSwiftTests/MTDisplayEditingTests.swift @@ -0,0 +1,330 @@ +import CoreGraphics +import Testing +import iosMath + +@testable import MathEditorSwift + +private struct ClosestIndexCase { + let point: CGPoint + let expected: MTMathListIndex +} + +@Suite(.serialized) +struct MTDisplayEditingTests { + private let font: MTFont = MTFontManager().latinModernFont(withSize: 20) + + private func assertClosestIndex( + expression: String, + cases: [ClosestIndexCase] + ) { + let mathList = MTMathListBuilder.build(from: expression)! + let displayList = MTTypesetter.createLine(for: mathList, font: font, style: .display) + + for testCase in cases { + let actual = displayList.closestIndex(to: testCase.point) + #expect( + actual?.isEqual(testCase.expected) == true, + "Index \(String(describing: actual)) does not match \(testCase.expected) for point \(testCase.point)" + ) + } + } + + @Test("closest index for fraction") + func closestPointFraction() { + assertClosestIndex(expression: "\\frac{3}{2}", cases: fractionCases) + } + + @Test("closest index for regular expression") + func closestPointRegular() { + assertClosestIndex(expression: "4+2", cases: regularCases) + } + + @Test("closest index for regular plus fraction") + func closestPointRegularPlusFraction() { + assertClosestIndex(expression: "1+\\frac{3}{2}", cases: regularPlusFractionCases) + } + + @Test("closest index for fraction plus regular") + func closestPointFractionPlusRegular() { + assertClosestIndex(expression: "\\frac{3}{2}+1", cases: fractionPlusRegularCases) + } + + @Test("closest index for exponent") + func closestPointExponent() { + assertClosestIndex(expression: "2^3", cases: exponentCases) + } +} + +private let fractionCases: [ClosestIndexCase] = [ + .init(point: CGPoint(x: -10, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: -20), expected: .level0Index(0)), + .init(point: CGPoint(x: -2.5, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: -2.5, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: -2.5, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: -2.5, y: -20), expected: .level0Index(0)), + .init( + point: CGPoint(x: -1, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: -1, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: -1, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: -1, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 3, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 3, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 3, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 3, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 7, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 7, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 7, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 7, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 11, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 11, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 11, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init(point: CGPoint(x: 11, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 12.5, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 12.5, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 12.5, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 12.5, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 20, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 20, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 20, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 20, y: -20), expected: .level0Index(1)), +] + +private let regularCases: [ClosestIndexCase] = [ + .init(point: CGPoint(x: -10, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: -20), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: -20), expected: .level0Index(0)), + .init(point: CGPoint(x: 10, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 10, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 10, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 10, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 25, y: 0), expected: .level0Index(2)), + .init(point: CGPoint(x: 25, y: 8), expected: .level0Index(2)), + .init(point: CGPoint(x: 25, y: 40), expected: .level0Index(2)), + .init(point: CGPoint(x: 25, y: -20), expected: .level0Index(2)), + .init(point: CGPoint(x: 35, y: 0), expected: .level0Index(2)), + .init(point: CGPoint(x: 35, y: 8), expected: .level0Index(2)), + .init(point: CGPoint(x: 35, y: 40), expected: .level0Index(2)), + .init(point: CGPoint(x: 35, y: -20), expected: .level0Index(2)), + .init(point: CGPoint(x: 45, y: 0), expected: .level0Index(3)), + .init(point: CGPoint(x: 45, y: 8), expected: .level0Index(3)), + .init(point: CGPoint(x: 45, y: 40), expected: .level0Index(3)), + .init(point: CGPoint(x: 45, y: -20), expected: .level0Index(3)), + .init(point: CGPoint(x: 55, y: 0), expected: .level0Index(3)), + .init(point: CGPoint(x: 55, y: 8), expected: .level0Index(3)), + .init(point: CGPoint(x: 55, y: 40), expected: .level0Index(3)), + .init(point: CGPoint(x: 55, y: -20), expected: .level0Index(3)), +] + +private let regularPlusFractionCases: [ClosestIndexCase] = [ + .init(point: CGPoint(x: 30, y: 0), expected: .level0Index(2)), + .init(point: CGPoint(x: 30, y: 8), expected: .level0Index(2)), + .init(point: CGPoint(x: 30, y: 40), expected: .level0Index(2)), + .init(point: CGPoint(x: 30, y: -20), expected: .level0Index(2)), + .init(point: CGPoint(x: 32, y: 0), expected: .level0Index(2)), + .init(point: CGPoint(x: 32, y: 8), expected: .level0Index(2)), + .init(point: CGPoint(x: 32, y: 40), expected: .level0Index(2)), + .init(point: CGPoint(x: 32, y: -20), expected: .level0Index(2)), + .init( + point: CGPoint(x: 33, y: 0), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 33, y: 8), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 33, y: 40), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 33, y: -20), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 35, y: 0), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 35, y: 8), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 35, y: 40), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 35, y: -20), + expected: MTMathListIndex( + atLocation: 2, withSubIndex: .level0Index(0), type: .subIndexTypeDenominator)), +] + +private let fractionPlusRegularCases: [ClosestIndexCase] = [ + .init(point: CGPoint(x: 15, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 15, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 13, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 13, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 13, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 13, y: -20), expected: .level0Index(1)), + .init( + point: CGPoint(x: 11, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 11, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 11, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 11, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 9, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), + .init( + point: CGPoint(x: 9, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 9, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNumerator)), + .init( + point: CGPoint(x: 9, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeDenominator)), +] + +private let exponentCases: [ClosestIndexCase] = [ + .init(point: CGPoint(x: -10, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: -10, y: -20), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 0), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 8), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: 40), expected: .level0Index(0)), + .init(point: CGPoint(x: 0, y: -20), expected: .level0Index(0)), + .init( + point: CGPoint(x: 9, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 9, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 9, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeSuperscript)), + .init( + point: CGPoint(x: 9, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 10, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 10, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 10, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeSuperscript)), + .init( + point: CGPoint(x: 10, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 11, y: 0), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init( + point: CGPoint(x: 11, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeSuperscript)), + .init( + point: CGPoint(x: 11, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(0), type: .subIndexTypeSuperscript)), + .init( + point: CGPoint(x: 11, y: -20), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeNucleus)), + .init(point: CGPoint(x: 17, y: 0), expected: .level0Index(1)), + .init( + point: CGPoint(x: 17, y: 8), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeSuperscript)), + .init( + point: CGPoint(x: 17, y: 40), + expected: MTMathListIndex( + atLocation: 0, withSubIndex: .level0Index(1), type: .subIndexTypeSuperscript)), + .init(point: CGPoint(x: 17, y: -20), expected: .level0Index(1)), + .init(point: CGPoint(x: 30, y: 0), expected: .level0Index(1)), + .init(point: CGPoint(x: 30, y: 8), expected: .level0Index(1)), + .init(point: CGPoint(x: 30, y: 40), expected: .level0Index(1)), + .init(point: CGPoint(x: 30, y: -20), expected: .level0Index(1)), +] diff --git a/MathEditorSwift/regressions.md b/MathEditorSwift/regressions.md new file mode 100644 index 0000000..2620783 --- /dev/null +++ b/MathEditorSwift/regressions.md @@ -0,0 +1,106 @@ +# Swift vs Objective-C regression review + +## Scope +Compared `mathEditorSwift` against legacy `mathEditor`, with emphasis on Swift-only `guard` and early-return behavior. + +## Regressions + +### 1) `insertMathList(_:at:)` silently drops inserts when no closest index is available +- **Swift behavior**: Returns early when `closestIndex(to:)` is `nil`. + - `guard let detailedIndex = closestIndex(to: point) else { return }` +- **Legacy Objective-C behavior**: No early return; code proceeds using `detailedIndex.atomIndex` even when `detailedIndex` is `nil` (Objective-C nil messaging effectively falls back to index `0`), so list content still gets inserted. +- **Impact**: Programmatic inserts can be lost in Swift in edge cases where the display list is not yet ready. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 2) Swift drops normal typed input when `insertionIndex` is `nil` +- **Swift behavior**: In `insertText(_:)`, normal atom insertion is gated by `if let insertedAtom, let insertionIndex { ... }`; when `insertionIndex` is `nil`, nothing is inserted and no fallback index is chosen. +- **Legacy Objective-C behavior**: Uses `_insertionIndex` directly; with Objective-C nil messaging, insertion APIs are still invoked rather than skipped. +- **Impact**: Input can be ignored in transient states (e.g., before insertion point is initialized) instead of being inserted at a default position. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 3) Swift special insert operations no-op when `insertionIndex` is `nil` +- **Swift behavior**: Multiple operations immediately return on missing `insertionIndex`: + - `handleScriptButton(_:)` + - `handleSlashButton()` + - `handleRadical(withDegreeButtonPressed:)` + - `insertPairedAtoms(open:close:)` +- **Legacy Objective-C behavior**: Same operations execute without explicit index guards; nil messaging allows the methods to proceed instead of immediate no-op. +- **Impact**: `^`, `_`, `/`, radicals, and paired insertions (`()`, `||`) can be dropped in Swift under the same transient nil-index state. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +## Guard-specific note requested +The Swift pattern `guard let insertionIndex else { return }` (and similar early returns) is the main source of these regressions. In Objective-C, equivalent paths commonly continued because messaging `nil` did not short-circuit the entire operation. + +## Additional regressions (added) + +### 4) Paired insert shortcuts (`"()"`, `"||"`) can be dropped entirely by Swift early return +- **Swift behavior**: `insertText(_:)` routes to `insertParens()` / `insertAbsValue()`, which both call `insertPairedAtoms(open:close:)`. That helper has `guard let insertionIndex ... else { return }`, so the whole shortcut no-ops when the insertion index is temporarily nil. +- **Legacy Objective-C behavior**: `insertParens`/`insertAbsValue` directly execute insertions with `_insertionIndex` and do not short-circuit on a nil guard. +- **Impact**: User-visible shortcuts can be ignored in transient index-init windows where legacy behavior still inserted delimiters. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 5) First-key race after tap-to-edit: Swift can drop operators due guard-based nil-index exits +- **Swift behavior**: `handleTap(at:)` sets `insertionIndex = nil` before `startEditing()`. If a key arrives before `doBecomeFirstResponder()` reinitializes the index, operations like `^`, `_`, `/`, radicals, and paired insertions can return early through Swift guards. +- **Legacy Objective-C behavior**: The same tap path nils `_insertionIndex`, but operation handlers do not use `guard` exits and continue through nil-messaging paths. +- **Impact**: The first key after entering edit mode can be intermittently dropped in Swift under timing-sensitive input sequences. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +## Additional regressions (highlighting / API parity) + +### 6) Highlight redraw targets wrapper view instead of `MTMathUILabel` +- **Swift behavior**: `highlightCharacter(at:)` updates `displayList` and calls `setNeedsDisplayCompat()` on the wrapper view. +- **Legacy Objective-C behavior**: `highlightCharacterAtIndex:` calls `[self.label setNeedsDisplay]` on the embedded math label. +- **Impact**: Highlight changes can be visually delayed/missed until some other update repaints `MTMathUILabel`. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 7) Clearing highlights relayouts wrapper instead of the inner math label +- **Swift behavior**: `clearHighlights()` calls `setNeedsLayout()` on the wrapper view. +- **Legacy Objective-C behavior**: `clearHighlights` calls `[self.label setNeedsLayout]`, directly invalidating the display-list owner. +- **Impact**: Stale highlight state may persist because the label display list is not explicitly rebuilt. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 8) Swift class cannot be initialized from nib/storyboard (`init(coder:)` unavailable) +- **Swift behavior**: `init(coder:)` is marked unavailable and traps. +- **Legacy Objective-C behavior**: Supports archive-based initialization via `awakeFromNib`. +- **Impact**: Existing nib/storyboard integrations that worked with `MTEditableMathLabel` are not drop-in compatible with `MTEditableMathLabelSwift`. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 9) Tap-while-editing now shows the caret handle (legacy hid it) +- **Swift behavior**: In `handleTap(at:)`, already-editing taps call `caretView.showHandle(true)`. +- **Legacy Objective-C behavior**: `handleTapAtPoint:` uses `[_caretView showHandle:NO]` in the same branch. +- **Impact**: Visible interaction behavior changes and touch-hit area around caret becomes active after each editing tap. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 10) `textColor = nil` no longer clears custom text color +- **Swift behavior**: Setter coalesces nil with `newValue ?? label.textColor`, turning nil assignment into a no-op. +- **Legacy Objective-C behavior**: Setter forwards `textColor` directly to `self.label.textColor`, allowing nil reset semantics. +- **Impact**: Hosts that clear a previously set color by assigning nil no longer get legacy behavior. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.m` + +### 11) Objective-C integration points for `delegate`/`keyboard` are not API-compatible +- **Swift behavior**: `delegate` and `keyboard` are Swift-only protocol-typed properties (not exposed with Objective-C protocol-compatible types). +- **Legacy Objective-C behavior**: Public ObjC properties use ObjC protocols (`id`, `MTView*`). +- **Impact**: Legacy ObjC hosts cannot wire the same delegate/keyboard contracts to `MTEditableMathLabelSwift` as a drop-in migration. +- **Relevant files**: + - `mathEditorSwift/MTEditableMathLabelSwift.swift` + - `mathEditor/editor/MTEditableMathLabel.h` diff --git a/MathEditorSwift/todo.md b/MathEditorSwift/todo.md new file mode 100644 index 0000000..756585c --- /dev/null +++ b/MathEditorSwift/todo.md @@ -0,0 +1,27 @@ + +## MTCaretView + +1. - [x] No need for baseColor vs color +2. - [x] setNeedsDisplayCompat +3. - [x] handleDrag convert is different implementation +4. - [x] no mouse cancelled +5. - [x] can caretColor be non-optional +6. - [x] Can caret handle initialized with label +7. - [x] is startBlinkingIfNeeded correct? +8. - [x] Rename MTCaretHandleSwift to CaretHandle + +## MTEditableMathlabelSwift + +1. - [x] MTKeyInputSwift should be alias to UIKeyInput +2. - [x] Bring comments back +3. - [ ] highlightColor is different in `initialize` +4. - [x] layoutLabelIfNeeded should be the same as objc +5. - [x] setNeedsDisplayCompat should be changed +6. - [x] setLabelNeedsDisplayCompat should be changed +7. - [ ] getIndexAfterSpecialStructure: is next should unwrapped? + +### Responder + +1. - [x] Should not extend UIKeyInput +2. - [x] reuse becomeFirstResponder on both +3. - [x] Reuse resignFirstResponder on both diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.pbxproj b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7ae018e --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.pbxproj @@ -0,0 +1,400 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 86E17B822F7569EF0079CEC2 /* MathEditorSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 86E17B812F7569EF0079CEC2 /* MathEditorSwift */; }; + C1DAAE012F70231100E4E983 /* MathKeyboardSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = C1DAAE002F70231100E4E983 /* MathKeyboardSwiftUI */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 862D28E72E2D51E100F9B6FE /* MathEditorSwiftUIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathEditorSwiftUIExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 862D28E92E2D51E100F9B6FE /* MathEditorSwiftUIExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = MathEditorSwiftUIExample; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 862D28E42E2D51E100F9B6FE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1DAAE012F70231100E4E983 /* MathKeyboardSwiftUI in Frameworks */, + 86E17B822F7569EF0079CEC2 /* MathEditorSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 862D28DE2E2D51E100F9B6FE = { + isa = PBXGroup; + children = ( + 862D28E92E2D51E100F9B6FE /* MathEditorSwiftUIExample */, + C14D8B922F6CB11E003F79FF /* Frameworks */, + 862D28E82E2D51E100F9B6FE /* Products */, + ); + sourceTree = ""; + }; + 862D28E82E2D51E100F9B6FE /* Products */ = { + isa = PBXGroup; + children = ( + 862D28E72E2D51E100F9B6FE /* MathEditorSwiftUIExample.app */, + ); + name = Products; + sourceTree = ""; + }; + C14D8B922F6CB11E003F79FF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 862D28E62E2D51E100F9B6FE /* MathEditorSwiftUIExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 862D28F32E2D51E200F9B6FE /* Build configuration list for PBXNativeTarget "MathEditorSwiftUIExample" */; + buildPhases = ( + 862D28E32E2D51E100F9B6FE /* Sources */, + 862D28E42E2D51E100F9B6FE /* Frameworks */, + 862D28E52E2D51E100F9B6FE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 862D28E92E2D51E100F9B6FE /* MathEditorSwiftUIExample */, + ); + name = MathEditorSwiftUIExample; + packageProductDependencies = ( + C1DAAE002F70231100E4E983 /* MathKeyboardSwiftUI */, + 86E17B812F7569EF0079CEC2 /* MathEditorSwift */, + ); + productName = MathEditorSwiftUIExample; + productReference = 862D28E72E2D51E100F9B6FE /* MathEditorSwiftUIExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 862D28DF2E2D51E100F9B6FE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1640; + LastUpgradeCheck = 2630; + TargetAttributes = { + 862D28E62E2D51E100F9B6FE = { + CreatedOnToolsVersion = 16.4; + }; + }; + }; + buildConfigurationList = 862D28E22E2D51E100F9B6FE /* Build configuration list for PBXProject "MathEditorSwiftUIExample" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 862D28DE2E2D51E100F9B6FE; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + C1C5F2D42FF0000000000001 /* XCLocalSwiftPackageReference "../../MathEditor/MathKeyboardSwiftUI" */, + 86E17B802F7569EF0079CEC2 /* XCLocalSwiftPackageReference "../MathEditorSwift" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 862D28E82E2D51E100F9B6FE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 862D28E62E2D51E100F9B6FE /* MathEditorSwiftUIExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 862D28E52E2D51E100F9B6FE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 862D28E32E2D51E100F9B6FE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 862D28F12E2D51E200F9B6FE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = BT9948YGAL; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 862D28F22E2D51E200F9B6FE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = BT9948YGAL; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 862D28F42E2D51E200F9B6FE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = MathEditorSwiftUIExample/MathEditorSwiftUIExample.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = BT9948YGAL; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = matheditor.MathEditorSwiftUIExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.5; + }; + name = Debug; + }; + 862D28F52E2D51E200F9B6FE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = MathEditorSwiftUIExample/MathEditorSwiftUIExample.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = BT9948YGAL; + ENABLE_APP_SANDBOX = YES; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = matheditor.MathEditorSwiftUIExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 2.5; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 862D28E22E2D51E100F9B6FE /* Build configuration list for PBXProject "MathEditorSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 862D28F12E2D51E200F9B6FE /* Debug */, + 862D28F22E2D51E200F9B6FE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 862D28F32E2D51E200F9B6FE /* Build configuration list for PBXNativeTarget "MathEditorSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 862D28F42E2D51E200F9B6FE /* Debug */, + 862D28F52E2D51E200F9B6FE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 86E17B802F7569EF0079CEC2 /* XCLocalSwiftPackageReference "../MathEditorSwift" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../MathEditorSwift; + }; + C1C5F2D42FF0000000000001 /* XCLocalSwiftPackageReference "../../MathEditor/MathKeyboardSwiftUI" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../../MathEditor/MathKeyboardSwiftUI; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 86E17B812F7569EF0079CEC2 /* MathEditorSwift */ = { + isa = XCSwiftPackageProductDependency; + productName = MathEditorSwift; + }; + C1DAAE002F70231100E4E983 /* MathKeyboardSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = C1C5F2D42FF0000000000001 /* XCLocalSwiftPackageReference "../../MathEditor/MathKeyboardSwiftUI" */; + productName = MathKeyboardSwiftUI; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 862D28DF2E2D51E100F9B6FE /* Project object */; +} diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..d594318 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "df491f6bdc78add6bce6eb3401a45b660dc8096d349ea94028db165e9a15431e", + "pins" : [ + { + "identity" : "iosmath", + "kind" : "remoteSourceControl", + "location" : "https://github.com/maitbayev/iosMath.git", + "state" : { + "branch" : "master", + "revision" : "bf4a5466fc405031977f2edcf806ccb119c23836" + } + } + ], + "version" : 3 +} diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/xcshareddata/xcschemes/MathEditorSwiftUIExample.xcscheme b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/xcshareddata/xcschemes/MathEditorSwiftUIExample.xcscheme new file mode 100644 index 0000000..f7ab85f --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample.xcodeproj/xcshareddata/xcschemes/MathEditorSwiftUIExample.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..ffdfe15 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,85 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/Contents.json b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/ContentView.swift b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/ContentView.swift new file mode 100644 index 0000000..924c5d3 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/ContentView.swift @@ -0,0 +1,70 @@ +// Copyright © 2025 Snap, Inc. All rights reserved. + +import MathEditorSwift +import SwiftUI + +struct ContentView: View { + var body: some View { + MathEditorView() + .frame(maxHeight: 100) + .padding() + } +} + +#Preview { + ContentView() +} + +#if os(iOS) + import MathKeyboardSwiftUI + + struct MathEditorView: UIViewRepresentable { + typealias UIViewType = MTEditableMathLabelSwift + + func makeUIView(context: Context) -> MTEditableMathLabelSwift { + let mathLabel = MTEditableMathLabelSwift() + mathLabel.backgroundColor = .clear + mathLabel.keyboard = MTMathKeyboardSwiftUIRootView.sharedInstance() + mathLabel.textColor = .label + mathLabel.caretColor = .label + mathLabel.startEditing() + return mathLabel + } + + func updateUIView(_ uiView: MTEditableMathLabelSwift, context: Context) { + + } + } +#endif // os(iOS) + +#if os(macOS) + + struct MathEditorView: NSViewRepresentable { + typealias UIViewType = NSView + + func makeNSView(context: Context) -> NSView { + let mathLabel = MTEditableMathLabelSwift() + mathLabel.backgroundColor = .clear + mathLabel.caretColor = NSColor.labelColor + mathLabel.textColor = NSColor.labelColor + // mathLabel.keyboard = MTMathKeyboardRootView.sharedInstance(); + + let wrapper = NSView() + wrapper.addSubview(mathLabel) + mathLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + mathLabel.topAnchor.constraint(equalTo: wrapper.topAnchor), + mathLabel.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor), + mathLabel.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor), + mathLabel.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor, constant: -44), + ]) + wrapper.backgroundColor = .clear + return wrapper + } + + func updateNSView(_ nsView: NSView, context: Context) { + + } + } + +#endif diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExample.entitlements b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExample.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExample.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExampleApp.swift b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExampleApp.swift new file mode 100644 index 0000000..d70240a --- /dev/null +++ b/MathEditorSwiftUIExample/MathEditorSwiftUIExample/MathEditorSwiftUIExampleApp.swift @@ -0,0 +1,12 @@ +// Copyright © 2025 Snap, Inc. All rights reserved. + +import SwiftUI + +@main +struct MathEditorSwiftUIExampleApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/MathKeyboardSwiftUI/Package.resolved b/MathKeyboardSwiftUI/Package.resolved new file mode 100644 index 0000000..24b9309 --- /dev/null +++ b/MathKeyboardSwiftUI/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "iosmath", + "kind" : "remoteSourceControl", + "location" : "https://github.com/maitbayev/iosMath.git", + "state" : { + "branch" : "master", + "revision" : "bf4a5466fc405031977f2edcf806ccb119c23836" + } + } + ], + "version" : 2 +} diff --git a/MathKeyboardSwiftUI/Package.swift b/MathKeyboardSwiftUI/Package.swift new file mode 100644 index 0000000..2b4f07b --- /dev/null +++ b/MathKeyboardSwiftUI/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "MathKeyboardSwiftUI", + defaultLocalization: "en", + platforms: [.iOS(.v17), .macOS(.v13)], + products: [ + .library( + name: "MathKeyboardSwiftUI", + targets: ["MathKeyboardSwiftUI"] + ) + ], + dependencies: [ + .package(path: "../MathEditorSwift") + ], + targets: [ + .target( + name: "MathKeyboardSwiftUI", + dependencies: [ + "MathEditorSwift" + ], + path: "Sources/MathKeyboardSwiftUI", + resources: [.process("Resources")], + ) + ] +) diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardContainerView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardContainerView.swift new file mode 100644 index 0000000..e8df39a --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardContainerView.swift @@ -0,0 +1,36 @@ +// +// KeyboardContainerView.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 22/03/2026. +// + +import SwiftUI + +struct KeyboardContainerView: View { + let state: KeyboardState + let onAction: (KeyboardAction) -> Void + + var body: some View { + keyboardView(for: state.currentTab) + } + + @ViewBuilder + private func keyboardView(for tab: KeyboardTab) -> some View { + switch tab { + case .numbers: + NumbersKeyboardView(state: state, onAction: onAction) + case .operations: + OperationsKeyboardView(state: state, onAction: onAction) + case .functions: + FunctionsKeyboardView(state: state, onAction: onAction) + case .letters: + LettersKeyboardView( + state: state, + isLowercase: state.isLowercase, + onShift: { onAction(.toggleShift) }, + onAction: onAction + ) + } + } +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardState.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardState.swift new file mode 100644 index 0000000..c65492a --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardState.swift @@ -0,0 +1,19 @@ +// +// KeyboardState.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 22/03/2026. +// + +struct KeyboardState: Equatable { + var currentTab: KeyboardTab = .numbers + var isLowercase = true + var equalsAllowed = true + var fractionsAllowed = true + var variablesAllowed = true + var numbersAllowed = true + var operatorsAllowed = true + var exponentHighlighted = false + var squareRootHighlighted = false + var radicalHighlighted = false +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardTab.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardTab.swift new file mode 100644 index 0000000..f197cdf --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/KeyboardTab.swift @@ -0,0 +1,26 @@ +// +// KeyboardTab.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 22/03/2026. +// + +enum KeyboardTab: CaseIterable, Hashable, Equatable, Identifiable { + case numbers + case operations + case functions + case letters + + var id: Self { self } +} + +extension KeyboardTab { + var imageNames: (normal: String, selected: String) { + switch self { + case .numbers: ("Numbers Symbol wbg", "Number Symbol") + case .operations: ("Operations Symbol wbg", "Operations Symbol") + case .functions: ("Functions Symbol wbg", "Functions Symbol") + case .letters: ("Letter Symbol wbg", "Letter Symbol") + } + } +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/FunctionsKeyboardView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/FunctionsKeyboardView.swift new file mode 100644 index 0000000..db3b18c --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/FunctionsKeyboardView.swift @@ -0,0 +1,106 @@ +import Foundation +import MathEditorSwift +import SwiftUI +import iosMath + +struct FunctionsKeyboardView: View { + let state: KeyboardState + let onAction: (KeyboardAction) -> Void + + var body: some View { + MainKeyboardView( + backgroundImageName: "Functions Keyboard", + middleColumns: makeFunctionsGrid(state: state, onAction: onAction), + state: state, + onAction: onAction + ) + } +} + +private func makeFunctionsGrid( + state: KeyboardState, + onAction: @escaping (KeyboardAction) -> Void +) -> [[KeyboardCell]] { + let trigLeftItems: [KeyboardCell] = [ + .text( + label: "sin", tone: .dark, action: { onAction(.insertText("sin")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "sec", tone: .dark, action: { onAction(.insertText("sec")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "log", tone: .dark, action: { onAction(.insertText("log")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .image( + imageName: "Subscript", + action: { onAction(.insertText("_")) }, + enabled: true, + accessibilityLabel: "Subscript", + pressedAsset: "Keyboard-green-pressed"), + ] + + let trigMiddleItems: [KeyboardCell] = [ + .text( + label: "cos", tone: .dark, action: { onAction(.insertText("cos")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "csc", tone: .dark, action: { onAction(.insertText("csc")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "ln", tone: .dark, action: { onAction(.insertText("ln")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .image( + imageName: "Sqrt", + action: { onAction(.insertText(MTSymbolSquareRoot)) }, + enabled: true, + accessibilityLabel: "Square root", + pressedAsset: "Keyboard-green-pressed", + overlayAsset: state.squareRootHighlighted ? "Keyboard-green-pressed" : nil), + ] + + let trigRightItems: [KeyboardCell] = [ + .text( + label: "tan", tone: .dark, action: { onAction(.insertText("tan")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "cot", tone: .dark, action: { onAction(.insertText("cot")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .image( + imageName: "Log with base", + action: { + onAction(.insertText("log")) + onAction(.insertText("_")) + }, + enabled: true, + accessibilityLabel: "Log with base", + pressedAsset: "Keyboard-green-pressed"), + .image( + imageName: "Sqrt with Power", + action: { onAction(.insertText(MTSymbolCubeRoot)) }, + enabled: true, + accessibilityLabel: "Root with power", + pressedAsset: "Keyboard-green-pressed", + overlayAsset: state.radicalHighlighted ? "Keyboard-green-pressed" : nil), + ] + + let constantsItems: [KeyboardCell] = [ + .text( + label: "θ", tone: .dark, fontName: "TimesNewRomanPSMT", + action: { onAction(.insertText("θ")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "π", tone: .dark, fontName: "TimesNewRomanPSMT", + action: { onAction(.insertText("π")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "∠", tone: .dark, fontName: "Apple SD Gothic Neo", + action: { onAction(.insertText("∠")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + .text( + label: "°", tone: .dark, fontName: "HelveticaNeue-ThinItalic", + action: { onAction(.insertText("°")) }, enabled: true, + pressedAsset: "Keyboard-green-pressed"), + ] + + return [trigLeftItems, trigMiddleItems, trigRightItems, constantsItems] +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyButton.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyButton.swift new file mode 100644 index 0000000..775094e --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyButton.swift @@ -0,0 +1,73 @@ +// +// KeyButton.swift +// MathKeyboardSwiftUI +// +// Created by Madiyar Aitbayev on 23/03/2026. +// + +import SwiftUI + +struct KeyButton: View { + var cell: KeyboardCell + + init(_ cell: KeyboardCell) { + self.cell = cell + } + + var body: some View { + Button(action: cell.action) { + ZStack { + Rectangle().fill(Color.white.opacity(0.001)) + if let overlayAsset = cell.overlayAsset { + Image(overlayAsset, bundle: .module) + .resizable() + } + switch cell.content { + case .text(let text): + Text(text.value) + .font(.custom(text.fontName, size: text.fontSize)) + .foregroundColor(textColor(for: text.tone)) + .padding(cell.padding) + case .image(let image): + Image(image.name, bundle: .module) + .renderingMode(.original) + .scaledToFill() + .padding(cell.padding) + } + } + } + .buttonStyle( + KeyboardPressStyle(pressedAsset: cell.pressedAsset) + ) + .disabled(!cell.enabled) + .opacity(cell.enabled ? 1 : 0.75) + .accessibilityLabel(cell.accessibilityLabel) + } +} + +private func textColor(for tone: KeyboardCell.TextTone) -> Color { + switch tone { + case .light: .white + case .dark: .black + case .disabled: Color(white: 0.67) + } +} + +private struct KeyboardPressStyle: ButtonStyle { + let pressedAsset: String? + + func makeBody(configuration: Configuration) -> some View { + ZStack { + if configuration.isPressed { + if let pressedAsset { + Image(pressedAsset, bundle: .module) + .resizable() + .opacity(1.0) + } else { + Color.clear + } + } + configuration.label + } + } +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardAction.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardAction.swift new file mode 100644 index 0000000..7172a21 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardAction.swift @@ -0,0 +1,6 @@ +enum KeyboardAction { + case insertText(String) + case backspace + case dismiss + case toggleShift +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardCell.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardCell.swift new file mode 100644 index 0000000..0802474 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardCell.swift @@ -0,0 +1,106 @@ +// +// KeyboardCell.swift +// MathKeyboardSwiftUI +// +// Created by Madiyar Aitbayev on 23/03/2026. +// + +import Foundation +import SwiftUI + +struct KeyboardCell: Identifiable { + enum Content { + case text(TextContent) + case image(ImageContent) + } + + struct TextContent { + let value: String + let fontName: String + let fontSize: Double + let tone: TextTone + } + + struct ImageContent { + let name: String + } + + enum TextTone { + case light + case dark + case disabled + } + + let id = UUID() + let content: Content + let action: () -> Void + let enabled: Bool + let accessibilityLabel: String + let pressedAsset: String? + let overlayAsset: String? + var padding: EdgeInsets + + static func text( + label: String, + tone: TextTone, + fontName: String = "HelveticaNeue-Thin", + fontSize: CGFloat = 20, + action: @escaping () -> Void, + enabled: Bool, + accessibilityLabel: String? = nil, + pressedAsset: String? = nil, + overlayAsset: String? = nil, + padding: EdgeInsets = .zero, + ) -> KeyboardCell { + KeyboardCell( + content: .text( + TextContent( + value: label, + fontName: fontName, + fontSize: fontSize, + tone: tone + ) + ), + action: action, + enabled: enabled, + accessibilityLabel: accessibilityLabel ?? label, + pressedAsset: pressedAsset, + overlayAsset: overlayAsset, + padding: padding + ) + } + + static func image( + imageName: String, + action: @escaping () -> Void, + enabled: Bool, + accessibilityLabel: String, + pressedAsset: String? = nil, + overlayAsset: String? = nil, + padding: EdgeInsets = .zero, + ) -> KeyboardCell { + KeyboardCell( + content: .image(ImageContent(name: imageName)), + action: action, + enabled: enabled, + accessibilityLabel: accessibilityLabel, + pressedAsset: pressedAsset, + overlayAsset: overlayAsset, + padding: padding + ) + } +} + +extension EdgeInsets { + static var zero: EdgeInsets { + EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0) + } + + static func bottom(_ length: CGFloat) -> EdgeInsets { + EdgeInsets(top: 0, leading: 0, bottom: length, trailing: 0) + } + + static func top(_ length: CGFloat) -> EdgeInsets { + EdgeInsets(top: length, leading: 0, bottom: 0, trailing: 0) + } +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardFontRegistry.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardFontRegistry.swift new file mode 100644 index 0000000..9e89103 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/KeyboardFontRegistry.swift @@ -0,0 +1,25 @@ +// +// MTMathKeyboard.swift +// MathKeyboardSwiftUI +// +// Created by Madiyar Aitbayev on 23/03/2026. +// + +import SwiftUI + +enum KeyboardFontRegistry { + static let variableFontName: String = { + guard + let fontURL = Bundle.module.url(forResource: "lmroman10-bolditalic", withExtension: "otf"), + let provider = CGDataProvider(url: fontURL as CFURL), + let font = CGFont(provider) + else { + return "HelveticaNeue" + } + + let postScriptName = font.postScriptName as String? ?? "HelveticaNeue" + var error: Unmanaged? + CTFontManagerRegisterGraphicsFont(font, &error) + return postScriptName + }() +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/LettersKeyboardView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/LettersKeyboardView.swift new file mode 100644 index 0000000..e5ddaa2 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/LettersKeyboardView.swift @@ -0,0 +1,189 @@ +import Foundation +import SwiftUI + +struct LettersKeyboardView: View { + let state: KeyboardState + let isLowercase: Bool + let onShift: () -> Void + let onAction: (KeyboardAction) -> Void + + private var topRow: [String] { + makeLetterRow(["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]) + } + + private var middleRow: [String] { + makeLetterRow(["a", "s", "d", "f", "g", "h", "j", "k", "l"]) + } + + private var bottomRow: [String] { + makeLetterRow(["z", "x", "c", "v", "b", "n", "m"]) + } + + private var greekRow: [GreekKey] { + if isLowercase { + return [ + GreekKey(label: "α", accessibilityLabel: "alpha"), + GreekKey(label: "Δ", accessibilityLabel: "capital delta"), + GreekKey(label: "σ", accessibilityLabel: "sigma"), + GreekKey(label: "μ", accessibilityLabel: "mu"), + GreekKey(label: "λ", accessibilityLabel: "lambda"), + ] + } + + return [ + GreekKey(label: "ρ", accessibilityLabel: "rho"), + GreekKey(label: "ω", accessibilityLabel: "omega"), + GreekKey(label: "Φ", accessibilityLabel: "capital phi"), + GreekKey(label: "ν", accessibilityLabel: "nu"), + GreekKey(label: "β", accessibilityLabel: "beta"), + ] + } + + var body: some View { + GeometryReader { proxy in + let unitWidth = proxy.size.width / 10 + let rowHeight = proxy.size.height / 4 + + ZStack { + Image("Letters Keyboard", bundle: .module) + .resizable() + .frame(width: proxy.size.width, height: proxy.size.height) + + VStack(spacing: 0) { + letterRow(topRow, horizontalInset: 0, unitWidth: unitWidth, rowHeight: rowHeight) + letterRow( + middleRow, horizontalInset: unitWidth / 2, unitWidth: unitWidth, rowHeight: rowHeight) + bottomLetterRow(unitWidth: unitWidth, rowHeight: rowHeight) + greekRowView(unitWidth: unitWidth, rowHeight: rowHeight) + } + } + } + } + + @ViewBuilder + private func letterRow( + _ letters: [String], + horizontalInset: CGFloat, + unitWidth: CGFloat, + rowHeight: CGFloat + ) -> some View { + HStack(spacing: 0) { + Color.clear.frame(width: horizontalInset) + ForEach(letters, id: \.self) { letter in + letterCell(letter) + .frame(width: unitWidth, height: rowHeight) + } + Color.clear.frame(width: horizontalInset) + } + } + + @ViewBuilder + private func bottomLetterRow(unitWidth: CGFloat, rowHeight: CGFloat) -> some View { + HStack(spacing: 0) { + KeyButton(shiftCell) + .frame(width: unitWidth * 1.5, height: rowHeight) + + ForEach(bottomRow, id: \.self) { letter in + letterCell(letter) + .frame(width: unitWidth, height: rowHeight) + } + + KeyButton(backspaceCell) + .frame(width: unitWidth * 1.5, height: rowHeight) + } + } + + @ViewBuilder + private func greekRowView(unitWidth: CGFloat, rowHeight: CGFloat) -> some View { + HStack(spacing: 0) { + KeyButton(dismissCell) + .frame(width: unitWidth * 2.5, height: rowHeight) + + ForEach(greekRow) { key in + KeyButton(greekCell(key)) + .frame(width: unitWidth, height: rowHeight) + } + + KeyButton(enterCell) + .frame(width: unitWidth * 2.5, height: rowHeight) + } + } + + private func makeLetterRow(_ letters: [String]) -> [String] { + if isLowercase { + return letters + } + return letters.map { $0.uppercased() } + } + + private func letterCell(_ label: String) -> KeyButton { + KeyButton( + .text( + label: label, + tone: .dark, + action: { onAction(.insertText(label)) }, + enabled: true, + pressedAsset: "Keyboard-azure-pressed" + ) + ) + } + + private func greekCell(_ key: GreekKey) -> KeyboardCell { + .text( + label: key.label, + tone: .dark, + fontName: "CourierNewPS-ItalicMT", + action: { onAction(.insertText(key.label)) }, + enabled: true, + accessibilityLabel: key.accessibilityLabel, + pressedAsset: "Keyboard-azure-pressed" + ) + } + + private var shiftCell: KeyboardCell { + .image( + imageName: "Shift", + action: onShift, + enabled: true, + accessibilityLabel: "Shift", + pressedAsset: "Keyboard-grey-pressed" + ) + } + + private var backspaceCell: KeyboardCell { + .image( + imageName: "Backspace Small", + action: { onAction(.backspace) }, + enabled: true, + accessibilityLabel: "Backspace", + pressedAsset: "Keyboard-grey-pressed" + ) + } + + private var dismissCell: KeyboardCell { + .image( + imageName: "Keyboard Down", + action: { onAction(.dismiss) }, + enabled: true, + accessibilityLabel: "Dismiss keyboard", + pressedAsset: "Keyboard-grey-pressed" + ) + } + + private var enterCell: KeyboardCell { + .text( + label: "Enter", + tone: .light, + fontName: "HelveticaNeue-Light", + action: { onAction(.insertText("\n")) }, + enabled: true, + pressedAsset: "Keyboard-grey-pressed" + ) + } +} + +private struct GreekKey: Identifiable { + let id = UUID() + let label: String + let accessibilityLabel: String +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/MainKeyboardView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/MainKeyboardView.swift new file mode 100644 index 0000000..b1c2430 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/MainKeyboardView.swift @@ -0,0 +1,101 @@ +import Foundation +import MathEditorSwift +import SwiftUI +import iosMath + +struct MainKeyboardView: View { + let backgroundImageName: String + let middleColumns: [[KeyboardCell]] + let state: KeyboardState + let onAction: (KeyboardAction) -> Void + + var body: some View { + GeometryReader { proxy in + let totalWidth = proxy.size.width + let totalHeight = proxy.size.height + let utilityWidth = totalWidth * 0.225 + let standardColumnWidth = (totalWidth - utilityWidth) / 5 + let rowHeight = totalHeight / 4 + + ZStack { + Image(backgroundImageName, bundle: .module) + .resizable() + .frame(width: totalWidth, height: totalHeight) + + HStack(spacing: 0) { + VStack(spacing: 0) { + ForEach(featuresColumn) { item in + KeyButton(item).frame(width: standardColumnWidth, height: rowHeight) + } + } + + Grid(horizontalSpacing: 0, verticalSpacing: 0) { + ForEach(0..<4, id: \.self) { row in + GridRow { + ForEach(0..<4, id: \.self) { column in + KeyButton(middleColumns[column][row]) + .frame(width: standardColumnWidth, height: rowHeight) + } + } + } + } + + VStack(spacing: 0) { + KeyButton(backspaceCell).frame(width: utilityWidth, height: rowHeight) + KeyButton(enterCell).frame(width: utilityWidth, height: rowHeight * 2) + KeyButton(dismissCell).frame(width: utilityWidth, height: rowHeight) + } + } + } + .frame(width: totalWidth, height: totalHeight) + } + } +} + +extension MainKeyboardView { + private var featuresColumn: [KeyboardCell] { + [ + .text( + label: "x", tone: .light, fontName: KeyboardFontRegistry.variableFontName, + action: { onAction(.insertText("x")) }, enabled: state.variablesAllowed, + pressedAsset: "Keyboard-marine-pressed", padding: .bottom(10)), + .text( + label: "y", tone: .light, fontName: KeyboardFontRegistry.variableFontName, + action: { onAction(.insertText("y")) }, enabled: state.variablesAllowed, + pressedAsset: "Keyboard-marine-pressed", padding: .bottom(10)), + .image( + imageName: "Fraction", + action: { onAction(.insertText(MTSymbolFractionSlash)) }, + enabled: state.fractionsAllowed, + accessibilityLabel: "Fraction", + pressedAsset: "Keyboard-marine-pressed"), + .image( + imageName: "Exponent", + action: { onAction(.insertText("^")) }, enabled: true, accessibilityLabel: "Exponent", + pressedAsset: "Keyboard-marine-pressed", + overlayAsset: state.exponentHighlighted ? "blue-button-highlighted" : nil), + ] + } + private var backspaceCell: KeyboardCell { + KeyboardCell.image( + imageName: "Backspace", + action: { onAction(.backspace) }, enabled: true, accessibilityLabel: "Backspace", + pressedAsset: "Keyboard-grey-pressed") + } + private var enterCell: KeyboardCell { + KeyboardCell.text( + label: "Enter", tone: .light, + fontName: "Helvetica Neue Light", + action: { onAction(.insertText("\n")) }, enabled: true, + pressedAsset: "Keyboard-grey-pressed") + } + private var dismissCell: KeyboardCell { + KeyboardCell.image( + imageName: "Keyboard Down", + action: { onAction(.dismiss) }, enabled: true, accessibilityLabel: "Dismiss keyboard", + pressedAsset: "Keyboard-grey-pressed", + padding: .bottom(5) + ) + } + +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/NumbersKeyboardView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/NumbersKeyboardView.swift new file mode 100644 index 0000000..5f9bbaf --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/NumbersKeyboardView.swift @@ -0,0 +1,89 @@ +import Foundation +import SwiftUI + +struct NumbersKeyboardView: View { + let state: KeyboardState + let onAction: (KeyboardAction) -> Void + + var body: some View { + MainKeyboardView( + backgroundImageName: "Numbers Keyboard", + middleColumns: makeNumbersGrid(state: state, onAction: onAction), + state: state, + onAction: onAction + ) + } +} + +private func makeNumbersGrid( + state: KeyboardState, + onAction: @escaping (KeyboardAction) -> Void +) -> [[KeyboardCell]] { + let numbersLeftItems: [KeyboardCell] = [ + .text( + label: "7", tone: .dark, action: { onAction(.insertText("7")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "4", tone: .dark, action: { onAction(.insertText("4")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "1", tone: .dark, action: { onAction(.insertText("1")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "0", tone: .dark, action: { onAction(.insertText("0")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + ] + let numbersMiddleItems: [KeyboardCell] = [ + .text( + label: "8", tone: .dark, action: { onAction(.insertText("8")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "5", tone: .dark, action: { onAction(.insertText("5")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "2", tone: .dark, action: { onAction(.insertText("2")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: ".", tone: .dark, action: { onAction(.insertText(".")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + ] + let numbersRightItems: [KeyboardCell] = [ + .text( + label: "9", tone: .dark, action: { onAction(.insertText("9")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "6", tone: .dark, action: { onAction(.insertText("6")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "3", tone: .dark, action: { onAction(.insertText("3")) }, + enabled: state.numbersAllowed, pressedAsset: "Keyboard-grey-pressed"), + .text( + label: "=", tone: state.equalsAllowed ? .dark : .disabled, + action: { onAction(.insertText("=")) }, enabled: state.equalsAllowed, + pressedAsset: "Keyboard-grey-pressed", + overlayAsset: state.equalsAllowed ? nil : "num-button-disabled"), + ] + let operatorItems: [KeyboardCell] = [ + .text( + label: "÷", tone: .dark, action: { onAction(.insertText("÷")) }, + enabled: state.operatorsAllowed, pressedAsset: "Keyboard-orange-pressed", + padding: .bottom(5) + ), + .text( + label: "×", tone: .dark, action: { onAction(.insertText("×")) }, + enabled: state.operatorsAllowed, pressedAsset: "Keyboard-orange-pressed", + padding: .bottom(7) + ), + .text( + label: "-", tone: .dark, fontSize: 25, action: { onAction(.insertText("-")) }, + enabled: state.operatorsAllowed, pressedAsset: "Keyboard-orange-pressed", + padding: .bottom(9) + ), + .text( + label: "+", tone: .dark, action: { onAction(.insertText("+")) }, + enabled: state.operatorsAllowed, pressedAsset: "Keyboard-orange-pressed", + padding: .bottom(5) + ), + ] + return [numbersLeftItems, numbersMiddleItems, numbersRightItems, operatorItems] +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/OperationsKeyboardView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/OperationsKeyboardView.swift new file mode 100644 index 0000000..5aa3c1f --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Keyboards/OperationsKeyboardView.swift @@ -0,0 +1,81 @@ +import Foundation +import SwiftUI + +struct OperationsKeyboardView: View { + let state: KeyboardState + let onAction: (KeyboardAction) -> Void + + var body: some View { + MainKeyboardView( + backgroundImageName: "Operations Keyboard", + middleColumns: makeOperationsGrid(state: state, onAction: onAction), + state: state, + onAction: onAction + ) + } +} + +private func makeOperationsGrid( + state: KeyboardState, + onAction: @escaping (KeyboardAction) -> Void +) -> [[KeyboardCell]] { + let groupingLeftItems: [KeyboardCell] = [ + .text( + label: "(", tone: .dark, action: { onAction(.insertText("(")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "[", tone: .dark, action: { onAction(.insertText("[")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "{", tone: .dark, action: { onAction(.insertText("{")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "!", tone: .dark, action: { onAction(.insertText("!")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + ] + let groupingRightItems: [KeyboardCell] = [ + .text( + label: ")", tone: .dark, action: { onAction(.insertText(")")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "]", tone: .dark, action: { onAction(.insertText("]")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "}", tone: .dark, action: { onAction(.insertText("}")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "∞", tone: .dark, action: { onAction(.insertText("∞")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + ] + let relationItems: [KeyboardCell] = [ + .text( + label: "<", tone: .dark, action: { onAction(.insertText("<")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "≤", tone: .dark, action: { onAction(.insertText("≤")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "|□|", tone: .dark, fontSize: 24, action: { onAction(.insertText("||")) }, + enabled: true, accessibilityLabel: "Absolute value", + pressedAsset: "Keyboard-orange-pressed" + ), + .text( + label: ":", tone: .dark, action: { onAction(.insertText(":")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + ] + let punctuationItems: [KeyboardCell] = [ + .text( + label: ">", tone: .dark, action: { onAction(.insertText(">")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "≥", tone: .dark, action: { onAction(.insertText("≥")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: "%", tone: .dark, action: { onAction(.insertText("%")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + .text( + label: ",", tone: .dark, action: { onAction(.insertText(",")) }, enabled: true, + pressedAsset: "Keyboard-orange-pressed"), + ] + return [groupingLeftItems, groupingRightItems, relationItems, punctuationItems] +} diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MTMathKeyboardSwiftUIRootView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MTMathKeyboardSwiftUIRootView.swift new file mode 100644 index 0000000..c662f75 --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MTMathKeyboardSwiftUIRootView.swift @@ -0,0 +1,145 @@ +#if os(iOS) + + import MathEditorSwift + import SwiftUI + import UIKit + + public final class MTMathKeyboardSwiftUIRootView: MTView, MTMathKeyboard, + UIInputViewAudioFeedback + { + private static let defaultTab: KeyboardTab = .numbers + private static let shared = MTMathKeyboardSwiftUIRootView() + + private var state = KeyboardState() + private weak var textInput: (any MTView & MTKeyInput)? + private lazy var hostingController = UIHostingController( + rootView: makeRootView() + ) + + public var enableInputClicksWhenVisible: Bool { + true + } + + public override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + public static func sharedInstance() -> MTMathKeyboardSwiftUIRootView { + shared + } + + public func switchToDefaultTab() { + updateState { $0.currentTab = Self.defaultTab } + } + + public var equalsAllowed: Bool { + get { state.equalsAllowed } + set { updateState { $0.equalsAllowed = newValue } } + } + + public var fractionsAllowed: Bool { + get { state.fractionsAllowed } + set { updateState { $0.fractionsAllowed = newValue } } + } + + public var variablesAllowed: Bool { + get { state.variablesAllowed } + set { updateState { $0.variablesAllowed = newValue } } + } + + public var numbersAllowed: Bool { + get { state.numbersAllowed } + set { updateState { $0.numbersAllowed = newValue } } + } + + public var operatorsAllowed: Bool { + get { state.operatorsAllowed } + set { updateState { $0.operatorsAllowed = newValue } } + } + + public var exponentHighlighted: Bool { + get { state.exponentHighlighted } + set { updateState { $0.exponentHighlighted = newValue } } + } + + public var squareRootHighlighted: Bool { + get { state.squareRootHighlighted } + set { updateState { $0.squareRootHighlighted = newValue } } + } + + public var radicalHighlighted: Bool { + get { state.radicalHighlighted } + set { updateState { $0.radicalHighlighted = newValue } } + } + + public func startedEditing(_ label: any MTView & MTKeyInput) { + textInput = label + updateRootView() + } + + public func finishedEditing(_ label: any MTView & MTKeyInput) { + if textInput === label { + textInput = nil + updateRootView() + } + } + + private func updateState(_ update: (inout KeyboardState) -> Void) { + update(&state) + updateRootView() + } + + private func makeRootView() -> MathKeyboardRootView { + MathKeyboardRootView( + state: state, + onTabSelected: { [weak self] tab in + self?.updateState { $0.currentTab = tab } + }, + onAction: { [weak self] action in + self?.handleKeyboardAction(action) + } + ) + } + + private func commonInit() { + backgroundColor = .white + autoresizingMask = [.flexibleWidth, .flexibleHeight] + + let hostedView = hostingController.view! + if #available(iOS 16.4, *) { + hostingController.safeAreaRegions = [] + } + hostedView.backgroundColor = .clear + addSubview(hostedView) + hostedView.pinToSuperview() + + updateRootView() + } + + private func updateRootView() { + hostingController.rootView = makeRootView() + } + + private func handleKeyboardAction(_ action: KeyboardAction) { + UIDevice.current.playInputClick() + + switch action { + case .insertText(let text): + textInput?.insertText(text) + case .backspace: + textInput?.deleteBackward() + case .dismiss: + textInput?.resignFirstResponder() + case .toggleShift: + updateState { $0.isLowercase.toggle() } + } + } + } + +#endif diff --git a/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MathKeyboardRootView.swift b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MathKeyboardRootView.swift new file mode 100644 index 0000000..f8f717f --- /dev/null +++ b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/MathKeyboardRootView.swift @@ -0,0 +1,57 @@ +// +// MathKeyboardRootView.swift +// MathEditor +// +// Created by Madiyar Aitbayev on 22/03/2026. +// + +import SwiftUI + +public struct MathKeyboardRootView: View { + let state: KeyboardState + let onTabSelected: (KeyboardTab) -> Void + let onAction: (KeyboardAction) -> Void + + public var body: some View { + GeometryReader { proxy in + let totalHeight = proxy.size.height + let tabHeight = totalHeight / 5.0 + let keyboardHeight = totalHeight - tabHeight + + VStack(spacing: 0) { + HStack(spacing: 0) { + ForEach(KeyboardTab.allCases) { tab in + Button { + onTabSelected(tab) + } label: { + Image(tab.imageName(for: state), bundle: .module) + .renderingMode(.original) + .resizable() + .scaledToFit() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding(.horizontal, 8) + .padding(.vertical, 6) + } + .buttonStyle(.plain) + .background(Color(white: 0.768627451)) + } + } + .frame(height: tabHeight) + + KeyboardContainerView( + state: state, + onAction: onAction + ) + .frame(height: keyboardHeight) + } + .background(Color.white) + .ignoresSafeArea() + } + } +} + +extension KeyboardTab { + fileprivate func imageName(for state: KeyboardState) -> String { + state.currentTab == self ? imageNames.selected : imageNames.normal + } +} diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json diff --git a/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png similarity index 100% rename from MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json diff --git a/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf similarity index 100% rename from MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json diff --git a/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf similarity index 100% rename from MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf diff --git a/MathKeyboardResources/lmroman10-bolditalic.otf b/MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/lmroman10-bolditalic.otf similarity index 100% rename from MathKeyboardResources/lmroman10-bolditalic.otf rename to MathKeyboardSwiftUI/Sources/MathKeyboardSwiftUI/Resources/lmroman10-bolditalic.otf diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..24b9309 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "iosmath", + "kind" : "remoteSourceControl", + "location" : "https://github.com/maitbayev/iosMath.git", + "state" : { + "branch" : "master", + "revision" : "bf4a5466fc405031977f2edcf806ccb119c23836" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..9f1716e --- /dev/null +++ b/Package.swift @@ -0,0 +1,50 @@ +// swift-tools-version: 5.6 + +import PackageDescription + +let package = Package( + name: "MathEditor", + defaultLocalization: "en", + platforms: [.iOS(.v13), .macOS(.v11)], + products: [ + .library( + name: "MathEditor", + targets: ["MathEditor"]), + .library( + name: "MathKeyboard", + targets: ["MathKeyboard"]), + ], + dependencies: [ + .package(url: "https://github.com/maitbayev/iosMath.git", branch: "master") + ], + targets: [ + .target( + name: "MathEditor", + dependencies: [.product(name: "iosMath", package: "iosMath")], + path: "./mathEditor", + cSettings: [ + .headerSearchPath("./editor"), + .headerSearchPath("./internal"), + ] + ), + .target( + name: "MathKeyboard", + dependencies: [.product(name: "iosMath", package: "iosMath"), "MathEditor"], + path: "./mathKeyboard", + resources: [.process("MathKeyboardResources")], + cSettings: [ + .headerSearchPath("./keyboard") + ] + ), + .testTarget( + name: "MathEditorTests", + dependencies: ["MathEditor"], + path: "Tests", + cSettings: [ + .headerSearchPath("../mathEditor/editor"), + .headerSearchPath("../mathEditor/keyboard"), + .headerSearchPath("../mathEditor/internal"), + ] + ), + ] +) diff --git a/Tests/MTDisplayEditingTest.m b/Tests/MTDisplayEditingTest.m index 23adc74..f828b4f 100644 --- a/Tests/MTDisplayEditingTest.m +++ b/Tests/MTDisplayEditingTest.m @@ -17,7 +17,6 @@ #import "MTMathListBuilder.h" #import "MTFontManager.h" #import "MTTypesetter.h" - @interface MTDisplayEditingTest : XCTestCase @property (nonatomic) MTFont* font; diff --git a/format.sh b/format.sh new file mode 100755 index 0000000..0de1175 --- /dev/null +++ b/format.sh @@ -0,0 +1,26 @@ +set -euo pipefail + +clang_format() { + local dir=$1 + echo "Formatting $dir\n\n" + for file in $(find $dir -name '*.h' -or -name '*.m' -or -name '*.mm'); do + echo "Formatting $file" + clang-format $file -i + done +} + +format() { + local dir=$1 + echo "Formatting $dir" + swift format --in-place --parallel -r $dir +} + +lint() { + local dir=$1 + echo "Linting $dir" + swift format lint --strict --parallel -r $dir +} + +format . +lint . + diff --git a/mathEditor/editor/MTEditableMathLabel+NSView.m b/mathEditor/editor/MTEditableMathLabel+NSView.m new file mode 100644 index 0000000..9bfa91e --- /dev/null +++ b/mathEditor/editor/MTEditableMathLabel+NSView.m @@ -0,0 +1,42 @@ +// +// MTEditableMathLabel+NSView.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#if TARGET_OS_OSX + +#import "MTEditableMathLabel.h" +#import "MTMathUILabel.h" +#import "MTView/MTView+HitTest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MTEditableMathLabel (NSView) +@end + +@interface MTEditableMathLabel () +@property (nonatomic) MTMathUILabel *label; +@end + +@implementation MTEditableMathLabel(NSView) + +- (void)layout { + [super layout]; + [self doLayout]; +} + +- (BOOL)isFlipped { + return YES; +} + +- (nullable NSView *)hitTest:(NSPoint)point { + return [self hitTestOutsideBounds:point ignoringSubviews:@[self.label]]; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/mathEditor/editor/MTEditableMathLabel+Responder.m b/mathEditor/editor/MTEditableMathLabel+Responder.m new file mode 100644 index 0000000..0e3e4d7 --- /dev/null +++ b/mathEditor/editor/MTEditableMathLabel+Responder.m @@ -0,0 +1,90 @@ +// +// MTEditableMathLabel+UIResponder.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import +#import "MTConfig.h" +#import "MTEditableMathLabel.h" +#import "MTView/MTView+FirstResponder.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MTEditableMathLabel (Responder) +@end + +@implementation MTEditableMathLabel (Responder) + +#if TARGET_OS_IPHONE + +- (nullable UIView *)inputView +{ + return self.keyboard; +} + +#endif // TARGET_OS_IPHONE + +/** + NSResponder protocol override. + Our view can become first responder to receive user text input. + */ +- (BOOL)acceptsFirstResponder { + return [self canBecomeFirstResponder]; +} + +/** + UIResponder protocol override. + Our view can become first responder to receive user text input. + */ +- (BOOL)canBecomeFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + BOOL canBecome = [super becomeFirstResponder]; + if (canBecome) { + [self doBecomeFirstResponder]; + } else { + // Sometimes it takes some time + // [self performSelector:@selector(startEditing) withObject:nil afterDelay:0.0]; + } + return canBecome; +} + +/** + UIResponder protocol override. + Called when our view is being asked to resign first responder state. + */ +- (BOOL)resignFirstResponder +{ + BOOL val = YES; + if ([self isFirstResponder]) { + val = [super resignFirstResponder]; + [self doResignFirstResponder]; + } + return val; +} + + +#if TARGET_OS_OSX + +- (void)keyDown:(NSEvent *)event { + // interpretKeyEvents feeds the event into the input system, + // which calls insertText: or deleteBackward: as appropriate. + [self interpretKeyEvents:@[event]]; +} + +- (void)deleteBackward:(nullable id)sender { + [self deleteBackward]; +} + +#endif // TARGET_OS_OSX + +@end + +NS_ASSUME_NONNULL_END + diff --git a/mathEditor/editor/MTEditableMathLabel+UITextInput.m b/mathEditor/editor/MTEditableMathLabel+UITextInput.m new file mode 100644 index 0000000..e9f2dfc --- /dev/null +++ b/mathEditor/editor/MTEditableMathLabel+UITextInput.m @@ -0,0 +1,226 @@ +// +// NSObject+MTEditableMathLabel_UITextInput.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#if TARGET_OS_IPHONE + +#import +#import +#import "MTEditableMathLabel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MTEditableMathLabel (UITextInput) +@end + +@implementation MTEditableMathLabel (UITextInput) + +// These are blank just to get a UITextInput implementation, to fix the dictation button bug. +// Proposed fix from: http://stackoverflow.com/questions/20980898/work-around-for-dictation-custom-text-view-bug + +//@synthesize beginningOfDocument;@ +//@synthesize endOfDocument; +//@synthesize inputDelegate; +//@synthesize markedTextRange; +//@synthesize markedTextStyle; +//@synthesize selectedTextRange; +//@synthesize tokenizer; + +- (nullable UITextRange *)selectedTextRange +{ + return objc_getAssociatedObject(self, @selector(selectedTextRange)); +} +- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange +{ + objc_setAssociatedObject(self, @selector(selectedTextRange), selectedTextRange, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (nullable id)inputDelegate +{ + id (^block)(void) = objc_getAssociatedObject(self, @selector(inputDelegate)); + return block ? block() : nil; +} + +- (void)setInputDelegate:(nullable id)inputDelegate +{ + __weak id weakDelegate = inputDelegate; + id (^block)(void) = ^{ + return weakDelegate; + }; + objc_setAssociatedObject(self, @selector(inputDelegate), block, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (nullable UITextRange *)markedTextRange +{ + return objc_getAssociatedObject(self, @selector(markedTextRange)); +} + +- (nullable NSDictionary *)markedTextStyle +{ + return objc_getAssociatedObject(self, @selector(markedTextStyle)); +} +- (void)setMarkedTextStyle:(nullable NSDictionary *)markedTextStyle +{ + objc_setAssociatedObject(self, @selector(markedTextStyle), markedTextStyle, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (UITextPosition *)beginningOfDocument +{ + return objc_getAssociatedObject(self, @selector(beginningOfDocument)); +} + +- (UITextPosition *)endOfDocument +{ + return objc_getAssociatedObject(self, @selector(endOfDocument)); +} + +- (id)tokenizer +{ + id tokenizer = objc_getAssociatedObject(self, @selector(tokenizer)); + if (!tokenizer) { + tokenizer = [[UITextInputStringTokenizer alloc] initWithTextInput:self]; + objc_setAssociatedObject(self, @selector(tokenizer), tokenizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return tokenizer; +} + +- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position + inDirection:(UITextStorageDirection)direction +{ + return UITextWritingDirectionLeftToRight; +} + +- (CGRect)caretRectForPosition:(UITextPosition *)position +{ + return CGRectZero; +} + +- (void)unmarkText +{ +} + +- (nullable UITextRange *)characterRangeAtPoint:(CGPoint)point +{ + return nil; +} +- (nullable UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position + inDirection:(UITextLayoutDirection)direction +{ + return nil; +} +- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point +{ + return nil; +} +- (nullable UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range +{ + return nil; +} +- (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other +{ + return NSOrderedSame; +} +- (void)dictationRecognitionFailed +{ +} +- (void)dictationRecordingDidEnd +{ +} +- (CGRect)firstRectForRange:(UITextRange *)range +{ + return CGRectZero; +} + +- (CGRect)frameForDictationResultPlaceholder:(id)placeholder +{ + return CGRectZero; +} +- (void)insertDictationResult:(NSArray *)dictationResult +{ +} +- (id)insertDictationResultPlaceholder +{ + return nil; +} + +- (NSInteger)offsetFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition +{ + return 0; +} +- (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position + inDirection:(UITextLayoutDirection)direction + offset:(NSInteger)offset +{ + return nil; +} +- (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset +{ + return nil; +} + +- (nullable UITextPosition *)positionWithinRange:(UITextRange *)range + farthestInDirection:(UITextLayoutDirection)direction +{ + return nil; +} +- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult +{ +} +- (void)replaceRange:(UITextRange *)range withText:(NSString *)text +{ +} +- (NSArray *)selectionRectsForRange:(UITextRange *)range +{ + return nil; +} +- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range +{ +} +- (void)setMarkedText:(nullable NSString *)markedText selectedRange:(NSRange)selectedRange +{ +} + +- (nullable NSString *)textInRange:(UITextRange *)range +{ + return nil; +} +- (nullable UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition +{ + return nil; +} + +#pragma mark - UITextInputTraits + +- (UITextAutocapitalizationType)autocapitalizationType +{ + return UITextAutocapitalizationTypeNone; +} + +- (UITextAutocorrectionType)autocorrectionType +{ + return UITextAutocorrectionTypeNo; +} + +- (UIReturnKeyType)returnKeyType +{ + return UIReturnKeyDefault; +} + +- (UITextSpellCheckingType)spellCheckingType +{ + return UITextSpellCheckingTypeNo; +} + +- (UIKeyboardType)keyboardType +{ + return UIKeyboardTypeASCIICapable; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/mathEditor/editor/MTEditableMathLabel+UIView.m b/mathEditor/editor/MTEditableMathLabel+UIView.m new file mode 100644 index 0000000..7b539e1 --- /dev/null +++ b/mathEditor/editor/MTEditableMathLabel+UIView.m @@ -0,0 +1,40 @@ +// +// MTEditableMathLabel+NSView.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#if TARGET_OS_IPHONE + +#import "MTEditableMathLabel.h" +#import "MTCaretView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MTEditableMathLabel (UIView) +@end + +@implementation MTEditableMathLabel(UIView) + +-(void)layoutSubviews +{ + [super layoutSubviews]; + [self doLayout]; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event +{ + BOOL inside = [super pointInside:point withEvent:event]; + if (inside) { + return YES; + } + // check if a point is in the caret view. + return [self.caretView pointInside:[self convertPoint:point toView:self.caretView] withEvent:event]; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_IPHONE diff --git a/mathEditor/editor/MTEditableMathLabel.h b/mathEditor/editor/MTEditableMathLabel.h index 318a7fb..35bb11d 100644 --- a/mathEditor/editor/MTEditableMathLabel.h +++ b/mathEditor/editor/MTEditableMathLabel.h @@ -8,11 +8,14 @@ // MIT license. See the LICENSE file for details. // -#import -#import +@import iosMath; +#import "MTKeyInput.h" +#include "MTMathList.h" @class MTEditableMathLabel; @class MTMathListIndex; +@class MTCancelView; +@class MTCaretView; /** Delegate for the `MTEditableMathLabel`. All methods are optional. */ @protocol MTEditableMathLabelDelegate @@ -50,28 +53,31 @@ this protocol. This protocol informs the keyboard when a particular `MTEditableMathUILabel` is being edited. - The keyboard should use this information to send `UIKeyInput` messages to the label. + The keyboard should use this information to send `MTKeyInput` messages to the label. This protocol inherits from `MTMathKeyboardTraits`. */ @protocol MTMathKeyboard -- (void) startedEditing:(UIView*) label; -- (void) finishedEditing:(UIView*) label; +- (void) startedEditing:(MTView*) label; +- (void) finishedEditing:(MTView*) label; @end -@interface MTEditableMathLabel : UIView +@interface MTEditableMathLabel : MTView @property (nonatomic) MTMathList* mathList; -@property (nonatomic) UIColor* highlightColor; +@property (nonatomic) MTColor* highlightColor; +@property (nonatomic) MTColor* textColor; +@property (nonatomic) MTColor* caretColor; -@property (nonatomic) UIImageView* cancelImage; +@property (nonatomic) MTCancelView* cancelImage; +@property (nonatomic) MTCaretView* caretView; @property (nonatomic, weak) id delegate; -@property (nonatomic, weak) UIView* keyboard; +@property (nonatomic, weak) MTView* keyboard; @property (nonatomic) CGFloat fontSize; -@property (nonatomic) IBInspectable UIEdgeInsets contentInsets; +@property (nonatomic) MTEdgeInsets contentInsets; - (void) clear; @@ -86,4 +92,8 @@ - (CGSize) mathDisplaySize; +// Compatibility? +- (void)doLayout; +- (void)doBecomeFirstResponder; +- (void)doResignFirstResponder; @end diff --git a/mathEditor/editor/MTEditableMathLabel.m b/mathEditor/editor/MTEditableMathLabel.m index 05f528f..c0c757b 100644 --- a/mathEditor/editor/MTEditableMathLabel.m +++ b/mathEditor/editor/MTEditableMathLabel.m @@ -14,22 +14,25 @@ #import "MTMathList.h" #import "MTMathUILabel.h" #import "MTMathAtomFactory.h" +#import "MTCancelView.h" #import "MTCaretView.h" +#import "MTTapGestureRecognizer.h" #import "MTMathList+Editing.h" #import "MTDisplay+Editing.h" +#import "MTView/MTView+AutoLayout.h" +#import "MTView/MTView+Layout.h" +#import "MTView/MTView+FirstResponder.h" #import "MTUnicode.h" #import "MTMathListBuilder.h" -@interface MTEditableMathLabel() +@interface MTEditableMathLabel() @property (nonatomic) MTMathUILabel* label; -@property (nonatomic) UITapGestureRecognizer* tapGestureRecognizer; +@property (nonatomic) MTTapGestureRecognizer* tapGestureRecognizer; @end - @implementation MTEditableMathLabel { - MTCaretView* _caretView; MTMathListIndex* _insertionIndex; CGAffineTransform _flipTransform; NSMutableArray* _indicesToHighlight; @@ -53,59 +56,53 @@ - (void)awakeFromNib - (void) createCancelImage { - self.cancelImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cross"]]; + if (self.cancelImage != nil) { + return; + } + self.cancelImage = [[MTCancelView alloc] initWithTarget:self action:@selector(clear)]; CGRect frame = CGRectMake(self.frame.size.width - 55, (self.frame.size.height - 45)/2, 45, 45); self.cancelImage.frame = frame; [self addSubview:self.cancelImage]; - - self.cancelImage.userInteractionEnabled = YES; - UITapGestureRecognizer *cancelRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clearTapped:)]; - [self.cancelImage addGestureRecognizer:cancelRecognizer]; - cancelRecognizer.delegate = nil; - self.cancelImage.hidden = YES; } - (void) initialize { // Add tap gesture recognizer to let the user enter editing mode. - self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]; + self.tapGestureRecognizer = [[MTTapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]; [self addGestureRecognizer:self.tapGestureRecognizer]; - self.tapGestureRecognizer.delegate = self; // Create our text storage. self.mathList = [MTMathList new]; - - self.userInteractionEnabled = YES; - self.autoresizesSubviews = YES; - + // Create and set up the APLSimpleCoreTextView that will do the drawing. MTMathUILabel *label = [[MTMathUILabel alloc] initWithFrame:self.bounds]; - label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self addSubview:label]; + [label pinToSuperview]; label.fontSize = 30; label.backgroundColor = self.backgroundColor; + + #if TARGET_OS_IPHONE label.userInteractionEnabled = NO; + #endif label.textAlignment = kMTTextAlignmentCenter; self.label = label; + // [self createCancelImage]; CGAffineTransform transform = CGAffineTransformMakeTranslation(0, self.bounds.size.height); _flipTransform = CGAffineTransformConcat(CGAffineTransformMakeScale(1.0, -1.0), transform); _caretView = [[MTCaretView alloc] initWithEditor:self]; - _caretView.caretColor = [UIColor colorWithWhite:0.1 alpha:1.0]; + _caretView.caretColor = [MTColor colorWithWhite:0.1 alpha:1.0]; _indicesToHighlight = [NSMutableArray array]; - _highlightColor = [UIColor colorWithRed:0.8 green:0 blue:0.0 alpha:1.0]; + _highlightColor = [MTColor colorWithRed:0.8 green:0 blue:0.0 alpha:1.0]; [self bringSubviewToFront:self.cancelImage]; // start with an empty math list self.mathList = [MTMathList new]; } --(void)layoutSubviews -{ - [super layoutSubviews]; - +-(void)doLayout { CGRect frame = CGRectMake(self.frame.size.width - 55, (self.frame.size.height - 45)/2, 45, 45); self.cancelImage.frame = frame; @@ -117,7 +114,27 @@ -(void)layoutSubviews [self insertionPointChanged]; } -- (void)setBackgroundColor:(UIColor *)backgroundColor +- (void)setTextColor:(MTColor *)textColor +{ + self.label.textColor = textColor; +} + +- (MTColor*)textColor +{ + return self.label.textColor; +} + +- (void)setCaretColor:(MTColor *)caretColor +{ + _caretView.caretColor = caretColor; +} + +- (MTColor *)caretColor +{ + return _caretView.caretColor; +} + +- (void)setBackgroundColor:(MTColor *)backgroundColor { [super setBackgroundColor:backgroundColor]; self.label.backgroundColor = backgroundColor; @@ -136,12 +153,12 @@ - (CGFloat)fontSize return self.label.fontSize; } -- (void)setContentInsets:(UIEdgeInsets)contentInsets +- (void)setContentInsets:(MTEdgeInsets)contentInsets { self.label.contentInsets = contentInsets; } -- (UIEdgeInsets)contentInsets +- (MTEdgeInsets)contentInsets { return self.label.contentInsets; } @@ -153,82 +170,55 @@ - (CGSize) mathDisplaySize #pragma mark - Custom user interaction -- (UIView *)inputView -{ - return self.keyboard; -} -/** - UIResponder protocol override. - Our view can become first responder to receive user text input. - */ -- (BOOL)canBecomeFirstResponder +- (void)doBecomeFirstResponder { - return YES; -} + if (_insertionIndex == nil) { + _insertionIndex = [MTMathListIndex level0Index:self.mathList.atoms.count]; + } -- (BOOL)becomeFirstResponder -{ - BOOL canBecome = [super becomeFirstResponder]; - if (canBecome) { - if (_insertionIndex == nil) { - _insertionIndex = [MTMathListIndex level0Index:self.mathList.atoms.count]; - } + [self.keyboard startedEditing:self]; - [self.keyboard startedEditing:self]; - - [self insertionPointChanged]; - if ([self.delegate respondsToSelector:@selector(didBeginEditing:)]) { - [self.delegate didBeginEditing:self]; - } - } else { - // Sometimes it takes some time - // [self performSelector:@selector(startEditing) withObject:nil afterDelay:0.0]; + [self insertionPointChanged]; + if ([self.delegate respondsToSelector:@selector(didBeginEditing:)]) { + [self.delegate didBeginEditing:self]; } - return canBecome; } /** UIResponder protocol override. Called when our view is being asked to resign first responder state. */ -- (BOOL)resignFirstResponder +- (void)doResignFirstResponder { - BOOL val = YES; - if ([self isFirstResponder]) { - [self.keyboard finishedEditing:self]; - val = [super resignFirstResponder]; - [self insertionPointChanged]; - if ([self.delegate respondsToSelector:@selector(didEndEditing:)]) { - [self.delegate didEndEditing:self]; - } + [self.keyboard finishedEditing:self]; + [self insertionPointChanged]; + if ([self.delegate respondsToSelector:@selector(didEndEditing:)]) { + [self.delegate didEndEditing:self]; } - return val; -} - -/** - UIGestureRecognizerDelegate method. - Called to determine if we want to handle a given gesture. - */ -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch -{ - // If gesture touch occurs in our view, we want to handle it - return YES; - //return (touch.view == self); } - (void) startEditing { if (![self isFirstResponder]) { // Become first responder state (which shows software keyboard, if applicable). + #if TARGET_OS_OSX + [self.window makeFirstResponder:self]; + #else [self becomeFirstResponder]; + #endif } } /** Our tap gesture recognizer selector that enters editing mode, or if already in editing mode, updates the text insertion point. */ -- (void)tap:(UITapGestureRecognizer *)tap +- (void)tap:(MTTapGestureRecognizer *)tap +{ + [self handleTapAtPoint:[tap locationInView:self]]; +} + +- (void)handleTapAtPoint:(CGPoint)tapPoint { if (![self isFirstResponder]) { _insertionIndex = nil; @@ -236,7 +226,7 @@ - (void)tap:(UITapGestureRecognizer *)tap [self startEditing]; } else { // If already editing move the cursor and show handle - _insertionIndex = [self closestIndexToPoint:[tap locationInView:self]]; + _insertionIndex = [self closestIndexToPoint:tapPoint]; if (_insertionIndex == nil) { _insertionIndex = [MTMathListIndex level0Index:self.mathList.atoms.count]; } @@ -245,21 +235,17 @@ - (void)tap:(UITapGestureRecognizer *)tap } } -- (void)clearTapped:(UITapGestureRecognizer *)tap -{ - [self clear]; -} - - (void)clear { self.mathList = [MTMathList new]; [self insertionPointChanged]; + [_caretView showHandle:NO]; } - (void)moveCaretToPoint:(CGPoint)point { _insertionIndex = [self closestIndexToPoint:point]; - [_caretView showHandle:NO]; +// [_caretView showHandle:NO]; [self insertionPointChanged]; } @@ -825,33 +811,6 @@ - (BOOL)hasText return NO; } -#pragma mark - UITextInputTraits - -- (UITextAutocapitalizationType)autocapitalizationType -{ - return UITextAutocapitalizationTypeNone; -} - -- (UITextAutocorrectionType)autocorrectionType -{ - return UITextAutocorrectionTypeNo; -} - -- (UIReturnKeyType)returnKeyType -{ - return UIReturnKeyDefault; -} - -- (UITextSpellCheckingType)spellCheckingType -{ - return UITextSpellCheckingTypeNo; -} - -- (UIKeyboardType)keyboardType -{ - return UIKeyboardTypeASCIICapable; -} - #pragma mark - Hit Testing @@ -876,16 +835,6 @@ - (CGPoint)caretRectForIndex:(MTMathListIndex *)index return [self.label.displayList caretPositionForIndex:index]; } -- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event -{ - BOOL inside = [super pointInside:point withEvent:event]; - if (inside) { - return YES; - } - // check if a point is in the caret view. - return [_caretView pointInside:[self convertPoint:point toView:_caretView] withEvent:event]; -} - #pragma mark - Highlighting - (void)highlightCharacterAtIndex:(MTMathListIndex *)index @@ -908,118 +857,4 @@ - (void) clearHighlights [self.label setNeedsLayout]; } -#pragma mark - UITextInput - -// These are blank just to get a UITextInput implementation, to fix the dictation button bug. -// Proposed fix from: http://stackoverflow.com/questions/20980898/work-around-for-dictation-custom-text-view-bug - -@synthesize beginningOfDocument; -@synthesize endOfDocument; -@synthesize inputDelegate; -@synthesize markedTextRange; -@synthesize markedTextStyle; -@synthesize selectedTextRange; -@synthesize tokenizer; - -- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction -{ - return UITextWritingDirectionLeftToRight; -} - -- (CGRect)caretRectForPosition:(UITextPosition *)position -{ - return CGRectZero; -} - -- (void)unmarkText -{ - -} - -- (UITextRange *)characterRangeAtPoint:(CGPoint)point -{ - return nil; -} -- (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction -{ - return nil; -} -- (UITextPosition *)closestPositionToPoint:(CGPoint)point -{ - return nil; -} -- (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range -{ - return nil; -} -- (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other -{ - return NSOrderedSame; -} -- (void)dictationRecognitionFailed -{ -} -- (void)dictationRecordingDidEnd -{ -} -- (CGRect)firstRectForRange:(UITextRange *)range -{ - return CGRectZero; -} - -- (CGRect)frameForDictationResultPlaceholder:(id)placeholder -{ - return CGRectZero; -} -- (void)insertDictationResult:(NSArray *)dictationResult -{ -} -- (id)insertDictationResultPlaceholder -{ - return nil; -} - -- (NSInteger)offsetFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition -{ - return 0; -} -- (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset -{ - return nil; -} -- (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset -{ - return nil; -} - -- (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction -{ - return nil; -} -- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult -{ -} -- (void)replaceRange:(UITextRange *)range withText:(NSString *)text -{ -} -- (NSArray *)selectionRectsForRange:(UITextRange *)range -{ - return nil; -} -- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range -{ -} -- (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange -{ -} - -- (NSString *)textInRange:(UITextRange *)range -{ - return nil; -} -- (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition -{ - return nil; -} - @end diff --git a/mathEditor/editor/MTKeyInput.h b/mathEditor/editor/MTKeyInput.h new file mode 100644 index 0000000..9eab026 --- /dev/null +++ b/mathEditor/editor/MTKeyInput.h @@ -0,0 +1,24 @@ +// +// MTKeyInput.h +// +// Created for cross-platform key input abstraction. +// + +#import "MTConfig.h" + +#if TARGET_OS_IPHONE + +#import +#define MTKeyInput UIKeyInput + +#else + +@protocol MTKeyInput + +- (void)insertText:(NSString *)text; +- (void)deleteBackward; +- (BOOL)hasText; + +@end + +#endif diff --git a/mathEditor/include/MTEditableMathLabel.h b/mathEditor/include/MTEditableMathLabel.h new file mode 120000 index 0000000..3482464 --- /dev/null +++ b/mathEditor/include/MTEditableMathLabel.h @@ -0,0 +1 @@ +../editor/MTEditableMathLabel.h \ No newline at end of file diff --git a/mathEditor/include/MTKeyInput.h b/mathEditor/include/MTKeyInput.h new file mode 120000 index 0000000..ae5a7f9 --- /dev/null +++ b/mathEditor/include/MTKeyInput.h @@ -0,0 +1 @@ +../editor/MTKeyInput.h \ No newline at end of file diff --git a/mathEditor/internal/MTCancelView.h b/mathEditor/internal/MTCancelView.h new file mode 100644 index 0000000..8d243c6 --- /dev/null +++ b/mathEditor/internal/MTCancelView.h @@ -0,0 +1,13 @@ +// +// MTCancelView.h +// +// Created for the editable label clear affordance. +// + +@import iosMath; + +@interface MTCancelView : MTView + +- (instancetype)initWithTarget:(id)target action:(SEL)action; + +@end diff --git a/mathEditor/internal/MTCancelView.m b/mathEditor/internal/MTCancelView.m new file mode 100644 index 0000000..9d40ec9 --- /dev/null +++ b/mathEditor/internal/MTCancelView.m @@ -0,0 +1,50 @@ +// +// MTCancelView.m +// +// Created for the editable label clear affordance. +// + +#import "MTCancelView.h" +#import "MTTapGestureRecognizer.h" +#import "MTView/MTView+AutoLayout.h" + +@interface MTCancelView () + +#if TARGET_OS_IPHONE +@property (nonatomic, strong) UIImageView *imageView; +#else +@property (nonatomic, strong) NSImageView *imageView; +#endif + +@end + +@implementation MTCancelView + +- (instancetype)initWithTarget:(id)target action:(SEL)action +{ + self = [super initWithFrame:CGRectZero]; + if (self) { +#if TARGET_OS_IPHONE + UIImage *image = [UIImage systemImageNamed:@"xmark.circle"]; + image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + _imageView = [[UIImageView alloc] initWithImage:image]; + _imageView.contentMode = UIViewContentModeScaleAspectFit; + _imageView.tintColor = [MTColor secondaryLabelColor]; +#else + NSImage *image = [NSImage imageWithSystemSymbolName:@"xmark.circle" accessibilityDescription:nil]; + _imageView = [[NSImageView alloc] initWithFrame:CGRectZero]; + _imageView.image = image; + _imageView.imageScaling = NSImageScaleProportionallyUpOrDown; + _imageView.contentTintColor = [MTColor secondaryLabelColor]; +#endif + [self addSubview:_imageView]; + [_imageView pinToSuperview]; + + MTTapGestureRecognizer *tapRecognizer = [[MTTapGestureRecognizer alloc] initWithTarget:target action:action]; + [self addGestureRecognizer:tapRecognizer]; + self.hidden = YES; + } + return self; +} + +@end diff --git a/mathEditor/internal/MTCaretView.h b/mathEditor/internal/MTCaretView.h index 1c6bab8..09beae2 100644 --- a/mathEditor/internal/MTCaretView.h +++ b/mathEditor/internal/MTCaretView.h @@ -8,13 +8,13 @@ // MIT license. See the LICENSE file for details. // -#import +#import "MTConfig.h" @class MTEditableMathLabel; -@interface MTCaretView : UIView +@interface MTCaretView : MTView -@property (nonatomic) UIColor* caretColor; +@property (nonatomic) MTColor* caretColor; - (id) initWithEditor:(MTEditableMathLabel*)label; diff --git a/mathEditor/internal/MTCaretView.m b/mathEditor/internal/MTCaretView.m index 04e9f89..1e7b041 100644 --- a/mathEditor/internal/MTCaretView.m +++ b/mathEditor/internal/MTCaretView.m @@ -10,6 +10,10 @@ #import "MTCaretView.h" #import "MTEditableMathLabel.h" +#import "MTConfig.h" +#import "MTView/MTView+Layout.h" +#import "MTView/MTView+HitTest.h" +#import "NSBezierPath+addLineToPoint.h" static const NSTimeInterval InitialBlinkDelay = 0.7; static const NSTimeInterval BlinkRate = 0.5; @@ -27,15 +31,15 @@ static NSInteger getCaretHeight() { return kCaretAscent + kCaretDescent; } -@interface MTCaretHandle : UIView +@interface MTCaretHandle : MTView @property (nonatomic, weak) MTEditableMathLabel* label; @end @implementation MTCaretHandle { - UIBezierPath* _path; - UIColor* _color; + MTBezierPath* _path; + MTColor* _color; } - (id) initWithFrame:(CGRect)frame @@ -47,9 +51,9 @@ - (id) initWithFrame:(CGRect)frame return self; } -- (UIBezierPath*) createHandlePath +- (MTBezierPath*) createHandlePath { - UIBezierPath* path = [UIBezierPath bezierPath]; + MTBezierPath* path = [MTBezierPath bezierPath]; CGSize size = self.bounds.size; [path moveToPoint:CGPointMake(size.width/2, 0)]; [path addLineToPoint:CGPointMake(size.width, size.height/4)]; @@ -60,57 +64,137 @@ - (UIBezierPath*) createHandlePath return path; } -- (void) layoutSubviews -{ - _path = [self createHandlePath]; -} - - (void)drawRect:(CGRect)rect { [_color setFill]; [_path fill]; } -- (void) setColor:(UIColor*) color +- (void) setColor:(MTColor*) color { - _color = [color colorWithAlphaComponent:0.6]; + _color = [color colorWithAlphaComponent:0.7]; +} + +- (void)interactionBegan +{ + _color = [_color colorWithAlphaComponent:1.0]; + [self setNeedsDisplay]; +} + +- (void)interactionEnded +{ + _color = [_color colorWithAlphaComponent:0.6]; + [self setNeedsDisplay]; +} + +- (void)handleDragAtLocalPoint:(CGPoint)loc +{ + CGPoint caretPoint = CGPointMake(loc.x, loc.y - self.frame.origin.y); + CGPoint labelPoint = [_label convertPoint:caretPoint fromView:self]; + [_label moveCaretToPoint:labelPoint]; // puts the point at the top to the top of the current caret +} + +- (CGRect)hitArea +{ + // Create a hit area around the center. + CGSize size = self.bounds.size; + return CGRectMake((size.width - kCaretHandleHitAreaSize) / 2, + (size.height - kCaretHandleHitAreaSize) / 2, + kCaretHandleHitAreaSize, + kCaretHandleHitAreaSize); +} + +#if TARGET_OS_IPHONE + +- (void) layoutSubviews +{ + [super layoutSubviews]; + _path = [self createHandlePath]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self interactionBegan]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - + [self interactionEnded]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // From apple documentation UITouch *aTouch = [touches anyObject]; - CGPoint loc = [aTouch locationInView:self]; - CGRect frame = self.frame; - CGPoint caretPoint = CGPointMake(loc.x, loc.y - frame.origin.y); // puts the point at the top to the top of the current caret - CGPoint labelPoint = [_label convertPoint:caretPoint fromView:self]; - [_label moveCaretToPoint:labelPoint]; + [self handleDragAtLocalPoint:[aTouch locationInView:self]]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - + [self interactionEnded]; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { - // Create a hit area around the center. - CGSize size = self.bounds.size; - CGRect hitArea = CGRectMake((size.width - kCaretHandleHitAreaSize)/2, (size.height - kCaretHandleHitAreaSize)/2, kCaretHandleHitAreaSize, kCaretHandleHitAreaSize); - return CGRectContainsPoint(hitArea, point); + return CGRectContainsPoint([self hitArea], point); +} + +#endif + + +#if TARGET_OS_OSX +- (void) layout +{ + [super layout]; + _path = [self createHandlePath]; +} + +- (BOOL) isFlipped { + return YES; } +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + +- (void)mouseDown:(NSEvent *)event +{ + [self interactionBegan]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; + [self handleDragAtLocalPoint:point]; +} + +- (void)mouseUp:(NSEvent *)event +{ + [self interactionEnded]; +} + +- (void)mouseCancelled:(NSEvent *)event { + [self interactionEnded]; +} + +- (NSView *)hitTest:(NSPoint)point +{ + if (self.hidden) { + return nil; + } + CGPoint localPoint = [self convertPoint:point fromView:self.superview]; + if (CGRectContainsPoint([self hitArea], localPoint)) { + return self; + } + return nil; +} + +#endif // TARGET_OS_OSX + @end + @interface MTCaretView () @property (nonatomic) NSTimer *blinkTimer; @@ -119,7 +203,7 @@ @interface MTCaretView () @implementation MTCaretView { - UIView *_blinker; + MTView *_blinker; MTCaretHandle *_handle; CGFloat _scale; } @@ -129,11 +213,11 @@ - (id)initWithEditor:(MTEditableMathLabel*)label self = [super initWithFrame:CGRectZero]; if (self) { _scale = label.fontSize / kCaretFontSize; - _blinker = [[UIView alloc] initWithFrame:CGRectZero]; + _blinker = [[MTView alloc] initWithFrame:CGRectZero]; _blinker.backgroundColor = self.caretColor; [self addSubview:_blinker]; _handle = [[MTCaretHandle alloc] initWithFrame:CGRectMake(0, 0, kCaretHandleWidth * _scale, kCaretHandleHeight *_scale)]; - _handle.backgroundColor = [UIColor clearColor]; + _handle.backgroundColor = [MTColor clearColor]; _handle.hidden = YES; _handle.label = label; [self addSubview:_handle]; @@ -154,7 +238,7 @@ - (void) setFontSize:(CGFloat)fontSize [self setNeedsLayout]; } -- (void) layoutSubviews +- (void) doLayout { _blinker.frame = CGRectMake(0, 0, kCaretWidth * _scale, getCaretHeight() *_scale); _handle.frame = CGRectMake(-(kCaretHandleWidth - kCaretWidth) * _scale/2, (getCaretHeight() + kCaretHandleDescent) *_scale, kCaretHandleWidth *_scale, kCaretHandleHeight *_scale); @@ -171,7 +255,6 @@ - (void)blink _blinker.hidden = !_blinker.hidden; } - // UIView didMoveToSuperview override to set up blink timers after caret view created in superview. - (void)didMoveToSuperview { @@ -187,7 +270,6 @@ - (void)didMoveToSuperview } } - // Helper method to set an initial blink delay - (void)delayBlink { @@ -202,13 +284,14 @@ - (void)dealloc [_blinkTimer invalidate]; } -- (void)setCaretColor:(UIColor *)caretColor +- (void)setCaretColor:(MTColor *)caretColor { _caretColor = caretColor; _handle.color = caretColor; _blinker.backgroundColor = self.caretColor; } +#if TARGET_OS_IPHONE - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ if (!_handle.hidden) { return [_handle pointInside:[self convertPoint:point toView:_handle] withEvent:event]; @@ -217,6 +300,32 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ } } +- (void) layoutSubviews +{ + [super layoutSubviews]; + [self doLayout]; +} +#endif // TARGET_OS_IPHONE -@end +#if TARGET_OS_OSX +- (void)viewDidMoveToSuperview +{ + [super viewDidMoveToSuperview]; + [self didMoveToSuperview]; +} +- (void) layout { + [super layout]; + [self doLayout]; +} + +- (BOOL)isFlipped { + return YES; +} + +- (NSView *)hitTest:(NSPoint)point { + return [self hitTestOutsideBounds:point]; +} +#endif // TARGET_OS_OSX + +@end diff --git a/mathEditor/internal/MTDisplay+Editing.h b/mathEditor/internal/MTDisplay+Editing.h index 437232a..7af2631 100644 --- a/mathEditor/internal/MTDisplay+Editing.h +++ b/mathEditor/internal/MTDisplay+Editing.h @@ -20,10 +20,10 @@ - (CGPoint) caretPositionForIndex:(MTMathListIndex*) index; // Highlight the character(s) at the given index. -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color; +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color; // Highlight the entire display with the given color -- (void) highlightWithColor:(UIColor*) color; +- (void) highlightWithColor:(MTColor*) color; @end diff --git a/mathEditor/internal/MTDisplay+Editing.m b/mathEditor/internal/MTDisplay+Editing.m index bc719e4..a60ef5b 100644 --- a/mathEditor/internal/MTDisplay+Editing.m +++ b/mathEditor/internal/MTDisplay+Editing.m @@ -93,11 +93,11 @@ - (CGPoint)caretPositionForIndex:(MTMathListIndex *)index return kInvalidPosition; } -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color { } -- (void)highlightWithColor:(UIColor *)color +- (void)highlightWithColor:(MTColor *)color { } @end @@ -113,9 +113,9 @@ - (MTMathListIndex*) closestIndexToPoint:(CGPoint) point; - (CGPoint) caretPositionForIndex:(MTMathListIndex*) index; // Highlight the character(s) at the given index. -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color; +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color; -- (void)highlightWithColor:(UIColor *)color; +- (void)highlightWithColor:(MTColor *)color; @end @@ -156,7 +156,7 @@ - (CGPoint)caretPositionForIndex:(MTMathListIndex *)index } -- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)color +- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(MTColor *)color { assert(NSLocationInRange(index.atomIndex, self.range)); assert(index.subIndexType == kMTSubIndexTypeNone || index.subIndexType == kMTSubIndexTypeNucleus); @@ -173,7 +173,7 @@ - (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)colo self.attributedString = attrStr; } -- (void)highlightWithColor:(UIColor *)color +- (void)highlightWithColor:(MTColor *)color { NSMutableAttributedString* attrStr = self.attributedString.mutableCopy; [attrStr addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)[color CGColor] @@ -222,9 +222,9 @@ - (MTMathListIndex*) closestIndexToPoint:(CGPoint) point; - (CGPoint) caretPositionForIndex:(MTMathListIndex*) index; // Highlight the character(s) at the given index. -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color; +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color; -- (void)highlightWithColor:(UIColor *)color; +- (void)highlightWithColor:(MTColor *)color; - (MTMathListDisplay*) subAtomForIndexType:(MTMathListSubIndexType) type; @@ -261,13 +261,13 @@ - (CGPoint)caretPositionForIndex:(MTMathListIndex *)index return CGPointMake(self.position.x, self.position.y); } -- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)color +- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(MTColor *)color { assert(index.subIndexType == kMTSubIndexTypeNone); [self highlightWithColor:color]; } -- (void)highlightWithColor:(UIColor *)color +- (void)highlightWithColor:(MTColor *)color { [self.numerator highlightWithColor:color]; [self.denominator highlightWithColor:color]; @@ -306,9 +306,9 @@ - (MTMathListIndex*) closestIndexToPoint:(CGPoint) point; - (CGPoint) caretPositionForIndex:(MTMathListIndex*) index; // Highlight the character(s) at the given index. -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color; +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color; -- (void)highlightWithColor:(UIColor *)color; +- (void)highlightWithColor:(MTColor *)color; - (MTMathListDisplay*) subAtomForIndexType:(MTMathListSubIndexType) type; @@ -348,13 +348,13 @@ - (CGPoint)caretPositionForIndex:(MTMathListIndex *)index return CGPointMake(self.position.x, self.position.y); } -- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)color +- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(MTColor *)color { assert(index.subIndexType == kMTSubIndexTypeNone); [self highlightWithColor:color]; } -- (void)highlightWithColor:(UIColor *)color +- (void)highlightWithColor:(MTColor *)color { [self.radicand highlightWithColor:color]; } @@ -393,9 +393,9 @@ - (MTMathListIndex*) closestIndexToPoint:(CGPoint) point; - (CGPoint) caretPositionForIndex:(MTMathListIndex*) index; // Highlight the character(s) at the given index. -- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(UIColor*) color; +- (void) highlightCharacterAtIndex:(MTMathListIndex*) index color:(MTColor*) color; -- (void)highlightWithColor:(UIColor *)color; +- (void)highlightWithColor:(MTColor *)color; @end @@ -570,7 +570,7 @@ - (CGPoint)caretPositionForIndex:(MTMathListIndex *)index } -- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)color +- (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(MTColor *)color { if (!index) { return; @@ -586,7 +586,7 @@ - (void)highlightCharacterAtIndex:(MTMathListIndex *)index color:(UIColor *)colo } } -- (void)highlightWithColor:(UIColor *)color +- (void)highlightWithColor:(MTColor *)color { for (MTDisplay* atom in self.subDisplays) { [atom highlightWithColor:color]; diff --git a/mathEditor/internal/MTTapGestureRecognizer.h b/mathEditor/internal/MTTapGestureRecognizer.h new file mode 100644 index 0000000..ff47ffb --- /dev/null +++ b/mathEditor/internal/MTTapGestureRecognizer.h @@ -0,0 +1,19 @@ +// +// MTTapGestureRecognizer.h +// +// Small cross-platform tap gesture abstraction. +// + +@import iosMath; + +#if TARGET_OS_IPHONE + +#import +#define MTTapGestureRecognizer UITapGestureRecognizer + +#else + +#import +#define MTTapGestureRecognizer NSClickGestureRecognizer + +#endif diff --git a/mathEditor/internal/MTView/MTView+AutoLayout.h b/mathEditor/internal/MTView/MTView+AutoLayout.h new file mode 100644 index 0000000..62cf81b --- /dev/null +++ b/mathEditor/internal/MTView/MTView+AutoLayout.h @@ -0,0 +1,20 @@ +// +// MTView+AutoLayout.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MXView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXView (AutoLayout) + +- (void)pinToSuperview; + +- (void)pinToSuperviewWithTop:(CGFloat)top leading:(CGFloat)leading bottom:(CGFloat)bottom trailing:(CGFloat)trailing; + +@end + +NS_ASSUME_NONNULL_END diff --git a/mathEditor/internal/MTView/MTView+AutoLayout.m b/mathEditor/internal/MTView/MTView+AutoLayout.m new file mode 100644 index 0000000..be85c28 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+AutoLayout.m @@ -0,0 +1,32 @@ +// +// MTView+AutoLayout.m +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MTConfig.h" +#import "MTView+AutoLayout.h" + +@implementation MXView (AutoLayout) + +- (void)pinToSuperview +{ + [self pinToSuperviewWithTop:0 leading:0 bottom:0 trailing:0]; +} + +- (void)pinToSuperviewWithTop:(CGFloat)top leading:(CGFloat)leading bottom:(CGFloat)bottom trailing:(CGFloat)trailing +{ + MTView *superview = self.superview; + if (!superview) + return; + self.translatesAutoresizingMaskIntoConstraints = NO; + [NSLayoutConstraint activateConstraints:@[ + [self.topAnchor constraintEqualToAnchor:superview.topAnchor constant:top], + [self.leadingAnchor constraintEqualToAnchor:superview.leadingAnchor constant:leading], + [self.trailingAnchor constraintEqualToAnchor:superview.trailingAnchor constant:-trailing], + [self.bottomAnchor constraintEqualToAnchor:superview.bottomAnchor constant:-bottom] + ]]; +} + +@end diff --git a/mathEditor/internal/MTView/MTView+FirstResponder.h b/mathEditor/internal/MTView/MTView+FirstResponder.h new file mode 100644 index 0000000..67a2b94 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+FirstResponder.h @@ -0,0 +1,22 @@ +// +// MTView+FirstResponder.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MTConfig.h" + +NS_ASSUME_NONNULL_BEGIN + +#if TARGET_OS_OSX + +@interface NSView (FirstResponder) + +@property (nonatomic, readonly) BOOL isFirstResponder; + +@end + +#endif // TARGET_OS_OSX + +NS_ASSUME_NONNULL_END diff --git a/mathEditor/internal/MTView/MTView+FirstResponder.m b/mathEditor/internal/MTView/MTView+FirstResponder.m new file mode 100644 index 0000000..df4cda9 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+FirstResponder.m @@ -0,0 +1,26 @@ +// +// MTView+FirstResponder.m +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MTConfig.h" +#import "MTView+FirstResponder.h" + +NS_ASSUME_NONNULL_BEGIN + +#if TARGET_OS_OSX + +@implementation NSView (FirstResponder) + +- (BOOL)isFirstResponder +{ + return self.window.firstResponder == self; +} + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/mathEditor/internal/MTView/MTView+HitTest.h b/mathEditor/internal/MTView/MTView+HitTest.h new file mode 100644 index 0000000..0c87d63 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+HitTest.h @@ -0,0 +1,23 @@ +// +// MTView+HitTest.h +// MathEditor +// +// Created by Madiyar Aitbayev on 21/03/2026. +// + +#if TARGET_OS_OSX + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSView (HitTest) + +- (NSView *)hitTestOutsideBounds:(NSPoint)point; +- (NSView *)hitTestOutsideBounds:(NSPoint)point ignoringSubviews:(NSArray *)ignoredSubviews; + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/mathEditor/internal/MTView/MTView+HitTest.m b/mathEditor/internal/MTView/MTView+HitTest.m new file mode 100644 index 0000000..59822b5 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+HitTest.m @@ -0,0 +1,46 @@ +// +// MTView+HitTest.m +// MathEditor +// +// Created by Madiyar Aitbayev on 21/03/2026. +// + +#import "MTView+HitTest.h" + +#if TARGET_OS_OSX + +NS_ASSUME_NONNULL_BEGIN + +@implementation NSView (HitTest) + +- (NSView *)hitTestOutsideBounds:(NSPoint)point +{ + return [self hitTestOutsideBounds:point ignoringSubviews:@[]]; +} + +- (NSView *)hitTestOutsideBounds:(NSPoint)point ignoringSubviews:(NSArray *)ignoredSubviews +{ + if (self.hidden) { + return nil; + } + NSPoint localPoint = [self convertPoint:point fromView:self.superview]; + for (NSView *child in [self.subviews reverseObjectEnumerator]) { + if ([ignoredSubviews containsObject:child]) { + continue; + } + NSView *hitView = [child hitTest:localPoint]; + if (hitView) { + return hitView; + } + } + if (NSPointInRect(localPoint, self.bounds)) { + return self; + } + return nil; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // TARGET_OS_OSX diff --git a/mathEditor/internal/MTView/MTView+Layout.h b/mathEditor/internal/MTView/MTView+Layout.h new file mode 100644 index 0000000..fdeb22b --- /dev/null +++ b/mathEditor/internal/MTView/MTView+Layout.h @@ -0,0 +1,28 @@ +// +// MTView+Layout.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MXView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXView (Layout) + +#if TARGET_OS_OSX + +- (void)setNeedsLayout; + +- (void)setNeedsDisplay; + +- (void)layoutIfNeeded; + +- (void)bringSubviewToFront:(NSView *)view; + +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/mathEditor/internal/MTView/MTView+Layout.m b/mathEditor/internal/MTView/MTView+Layout.m new file mode 100644 index 0000000..0d43c50 --- /dev/null +++ b/mathEditor/internal/MTView/MTView+Layout.m @@ -0,0 +1,40 @@ +// +// MTView+Layout.m +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#import "MTView+Layout.h" + +@implementation MXView (Layout) + +#if TARGET_OS_OSX + +- (void)setNeedsLayout +{ + [self setNeedsLayout:YES]; +} + +- (void)setNeedsDisplay +{ + [self setNeedsDisplay:YES]; +} + +- (void)layoutIfNeeded +{ + [self layoutSubtreeIfNeeded]; +} + +- (void)bringSubviewToFront:(NSView *)view +{ + if (view.superview != self) { + return; + } + [view removeFromSuperview]; + [self addSubview:view]; +} + +#endif + +@end diff --git a/mathEditor/internal/MTView/MXView.h b/mathEditor/internal/MTView/MXView.h new file mode 100644 index 0000000..d781e72 --- /dev/null +++ b/mathEditor/internal/MTView/MXView.h @@ -0,0 +1,14 @@ +// +// MXView.h +// MathEditor +// +// Created by Madiyar Aitbayev on 20/03/2026. +// + +#if TARGET_OS_OSX +#import +#define MXView NSView +#else +#import +#define MXView UIView +#endif diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json new file mode 100644 index 0000000..067e4c8 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "left arrow disabled 1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "left arrow disabled 2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "left arrow disabled 3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png new file mode 100644 index 0000000..d24a0ee Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png new file mode 100644 index 0000000..33a4d0c Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png new file mode 100644 index 0000000..fd77cfe Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow-disabled.imageset/left arrow disabled 3x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json new file mode 100644 index 0000000..31fc527 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "left arrow 1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "left arrow 2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "left arrow 3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png new file mode 100644 index 0000000..5784e5f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png new file mode 100644 index 0000000..b77d261 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png new file mode 100644 index 0000000..aeaa03d Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/back-arrow.imageset/left arrow 3x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json new file mode 100644 index 0000000..56db975 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "blue-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png new file mode 100644 index 0000000..57f709f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/blue-button-highlighted.imageset/blue-pressed.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json new file mode 100644 index 0000000..a6edac4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "right arrow disabled 1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "right arrow disabled 2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "right arrow disabled 3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png new file mode 100644 index 0000000..1d982c9 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png new file mode 100644 index 0000000..62d40e9 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png new file mode 100644 index 0000000..20f9466 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow-disabled.imageset/right arrow disabled 3x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json new file mode 100644 index 0000000..b8aabfb --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "right arrow 1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "right arrow 2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "right arrow 3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png new file mode 100644 index 0000000..feb11ea Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png new file mode 100644 index 0000000..e4789ee Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png new file mode 100644 index 0000000..c856f9b Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/front-arrow.imageset/right arrow 3x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json new file mode 100644 index 0000000..808cdd4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "grey-button-disabled.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png new file mode 100644 index 0000000..c0c12ff Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-disabled.imageset/grey-button-disabled.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json new file mode 100644 index 0000000..03197d3 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-grey-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png new file mode 100644 index 0000000..353fbd4 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/grey-button-highlighted.imageset/keyboard-grey-pressed.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json new file mode 100644 index 0000000..9405a66 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "ipad-keyboard1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "ipad-keyboard2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png new file mode 100644 index 0000000..80689a1 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png new file mode 100644 index 0000000..3cc9208 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/ipad-background.imageset/ipad-keyboard2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json new file mode 100644 index 0000000..75260ac --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-background1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "keyboard-background2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png new file mode 100644 index 0000000..399996f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png new file mode 100644 index 0000000..3a411c2 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/iphone-background.imageset/keyboard-background2x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json new file mode 100644 index 0000000..228a6a7 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "kb-dark-blue-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png new file mode 100644 index 0000000..37951ab Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kb-dark-blue-pressed.imageset/kb-dark-blue-pressed.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json new file mode 100644 index 0000000..6b2910c --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "kb-slide-button-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png new file mode 100644 index 0000000..6978659 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/kbslide-button-highlighted.imageset/kb-slide-button-pressed.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json new file mode 100644 index 0000000..9ae1d29 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-slide11x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "keyboard-slide12x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png new file mode 100644 index 0000000..4c4d106 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide11x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png new file mode 100644 index 0000000..7459821 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg1.imageset/keyboard-slide12x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json new file mode 100644 index 0000000..c9fa654 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-slide21x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "keyboard-slide22x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png new file mode 100644 index 0000000..49a60d2 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide21x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png new file mode 100644 index 0000000..5076081 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg2.imageset/keyboard-slide22x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json new file mode 100644 index 0000000..5e86f7a --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-slide31x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "keyboard-slide32x.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png new file mode 100644 index 0000000..82b639c Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide31x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png new file mode 100644 index 0000000..77d9cb5 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/keyboard-slide-bg3.imageset/keyboard-slide32x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json new file mode 100644 index 0000000..14852e1 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "multiplication1x.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "multiplication2x-ipad.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png new file mode 100644 index 0000000..8c8876d Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication1x.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png new file mode 100644 index 0000000..2faa5bf Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-ipad.imageset/multiplication2x-ipad.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json new file mode 100644 index 0000000..ebe6ab3 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "multiplication1x-iphone.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "multiplication2x-iphone.png" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png new file mode 100644 index 0000000..12c5458 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication1x-iphone.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png new file mode 100644 index 0000000..e03c22f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/multiplication-icon-iphone.imageset/multiplication2x-iphone.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json new file mode 100644 index 0000000..808cdd4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "grey-button-disabled.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png new file mode 100644 index 0000000..b9ad3be Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-disabled.imageset/grey-button-disabled.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json new file mode 100644 index 0000000..611ff31 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-num-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png new file mode 100644 index 0000000..1bc84a4 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/num-button-highlighted.imageset/keyboard-num-pressed.png differ diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json new file mode 100644 index 0000000..9f29cb3 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "keyboard-orange-pressed.png" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png new file mode 100644 index 0000000..7021eec Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/KeyboardAssests.xcassets/orange-button-highlighted.imageset/keyboard-orange-pressed.png differ diff --git a/MathKeyboardResources/MTKeyboard.xib b/mathKeyboard/MathKeyboardResources/MTKeyboard.xib similarity index 100% rename from MathKeyboardResources/MTKeyboard.xib rename to mathKeyboard/MathKeyboardResources/MTKeyboard.xib diff --git a/MathKeyboardResources/MTKeyboardTab2.xib b/mathKeyboard/MathKeyboardResources/MTKeyboardTab2.xib similarity index 100% rename from MathKeyboardResources/MTKeyboardTab2.xib rename to mathKeyboard/MathKeyboardResources/MTKeyboardTab2.xib diff --git a/MathKeyboardResources/MTKeyboardTab3.xib b/mathKeyboard/MathKeyboardResources/MTKeyboardTab3.xib similarity index 100% rename from MathKeyboardResources/MTKeyboardTab3.xib rename to mathKeyboard/MathKeyboardResources/MTKeyboardTab3.xib diff --git a/MathKeyboardResources/MTKeyboardTab4.xib b/mathKeyboard/MathKeyboardResources/MTKeyboardTab4.xib similarity index 100% rename from MathKeyboardResources/MTKeyboardTab4.xib rename to mathKeyboard/MathKeyboardResources/MTKeyboardTab4.xib diff --git a/MathKeyboardResources/MTMathKeyboardRootView.xib b/mathKeyboard/MathKeyboardResources/MTMathKeyboardRootView.xib similarity index 100% rename from MathKeyboardResources/MTMathKeyboardRootView.xib rename to mathKeyboard/MathKeyboardResources/MTMathKeyboardRootView.xib diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf new file mode 100644 index 0000000..40e6cd4 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Backspace Small.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json new file mode 100644 index 0000000..f09a029 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace Small.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Backspace Small.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf new file mode 100644 index 0000000..cfcd807 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Backspace.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json new file mode 100644 index 0000000..28439e2 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Backspace.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Backspace.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json new file mode 100644 index 0000000..523c00a --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Exponent.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf new file mode 100644 index 0000000..dd5bfeb Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Exponent.imageset/Exponent.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json new file mode 100644 index 0000000..9c07ead --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Fraction.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf new file mode 100644 index 0000000..a5a3f96 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Fraction.imageset/Fraction.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json new file mode 100644 index 0000000..c4cbd27 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Functions Keyboard.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf new file mode 100644 index 0000000..292ed6b Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Keyboard.imageset/Functions Keyboard.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json new file mode 100644 index 0000000..3fb6c37 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Functions Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf new file mode 100644 index 0000000..fd14e50 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Functions Symbol.imageset/Functions Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json new file mode 100644 index 0000000..2335dad --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard Down.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf new file mode 100644 index 0000000..de758f5 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard Down.imageset/Keyboard Down.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json new file mode 100644 index 0000000..186ba84 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard-azure-pressed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf new file mode 100644 index 0000000..67007f0 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-azure-pressed.imageset/Keyboard-azure-pressed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json new file mode 100644 index 0000000..02c527f --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard-green-pressed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf new file mode 100644 index 0000000..c7ee3b2 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-green-pressed.imageset/Keyboard-green-pressed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json new file mode 100644 index 0000000..02f937b --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard-grey-pressed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf new file mode 100644 index 0000000..3328558 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-grey-pressed.imageset/Keyboard-grey-pressed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json new file mode 100644 index 0000000..058ab02 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard-marine-pressed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf new file mode 100644 index 0000000..1dd756f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-marine-pressed.imageset/Keyboard-marine-pressed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json new file mode 100644 index 0000000..567019b --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Keyboard-orange-pressed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf new file mode 100644 index 0000000..6d3c6b3 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Keyboard-orange-pressed.imageset/Keyboard-orange-pressed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json new file mode 100644 index 0000000..9ad12a1 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Letter Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf new file mode 100644 index 0000000..37e03d3 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letter Symbol.imageset/Letter Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json new file mode 100644 index 0000000..53d8353 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Letters Keyboard.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf new file mode 100644 index 0000000..0bbcaea Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Letters Keyboard.imageset/Letters Keyboard.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json new file mode 100644 index 0000000..64b7b1b --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Log Inverted.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf new file mode 100644 index 0000000..3a5c785 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log Inverted.imageset/Log Inverted.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json new file mode 100644 index 0000000..084900d --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Log with base.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf new file mode 100644 index 0000000..2809297 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Log with base.imageset/Log with base.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json new file mode 100644 index 0000000..3035c06 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Number Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf new file mode 100644 index 0000000..e16e728 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Number Symbol.imageset/Number Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json new file mode 100644 index 0000000..4c1745d --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Numbers Keyboard.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf new file mode 100644 index 0000000..5223f19 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Numbers Keyboard.imageset/Numbers Keyboard.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json new file mode 100644 index 0000000..def13fd --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Operations Keyboard.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf new file mode 100644 index 0000000..388605a Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Keyboard.imageset/Operations Keyboard.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json new file mode 100644 index 0000000..68827d4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Operations Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf new file mode 100644 index 0000000..312ca49 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Operations Symbol.imageset/Operations Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json new file mode 100644 index 0000000..451c244 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Shift.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf new file mode 100644 index 0000000..62f0226 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Shift.imageset/Shift.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json new file mode 100644 index 0000000..4ebfa5a --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Sqrt White Fixed.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf new file mode 100644 index 0000000..f41533f Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Inverted.imageset/Sqrt White Fixed.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json new file mode 100644 index 0000000..8be156b --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Sqrt Power Inverted.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf new file mode 100644 index 0000000..fa6a74b Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt Power Inverted.imageset/Sqrt Power Inverted.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json new file mode 100644 index 0000000..20a091b --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Sqrt with Power.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf new file mode 100644 index 0000000..d7c2166 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt with Power.imageset/Sqrt with Power.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json new file mode 100644 index 0000000..77856d4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Sqrt.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf new file mode 100644 index 0000000..32e996a Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Sqrt.imageset/Sqrt.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json new file mode 100644 index 0000000..b9f4407 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Subscript Inverted.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf new file mode 100644 index 0000000..09e5f0d Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript Inverted.imageset/Subscript Inverted.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json new file mode 100644 index 0000000..465f18e --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Subscript.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf new file mode 100644 index 0000000..37bbc58 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/NewKeyboardAssets.xcassets/Subscript.imageset/Subscript.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json new file mode 100644 index 0000000..3fb6c37 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Functions Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf new file mode 100644 index 0000000..94dd9ef Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Functions Symbol wbg.imageset/Functions Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json new file mode 100644 index 0000000..9ad12a1 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Letter Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf new file mode 100644 index 0000000..d51fa45 Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Letter Symbol wbg.imageset/Letter Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json new file mode 100644 index 0000000..84ff917 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Numbers Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf new file mode 100644 index 0000000..a79d54e Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Numbers Symbol wbg.imageset/Numbers Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json new file mode 100644 index 0000000..68827d4 --- /dev/null +++ b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Operations Symbol.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf new file mode 100644 index 0000000..6a2a12d Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/WhiteBGKeyboardTab.xcassets/Operations Symbol wbg.imageset/Operations Symbol.pdf differ diff --git a/mathKeyboard/MathKeyboardResources/lmroman10-bolditalic.otf b/mathKeyboard/MathKeyboardResources/lmroman10-bolditalic.otf new file mode 100644 index 0000000..98297be Binary files /dev/null and b/mathKeyboard/MathKeyboardResources/lmroman10-bolditalic.otf differ diff --git a/mathKeyboard/include/module.modulemap b/mathKeyboard/include/module.modulemap new file mode 100644 index 0000000..ca67926 --- /dev/null +++ b/mathKeyboard/include/module.modulemap @@ -0,0 +1,6 @@ +module MathKeyboard { + header "../keyboard/MTMathKeyboardRootView.h" + header "../keyboard/MTKeyboard.h" + + export * +} diff --git a/mathEditor/keyboard/MTKeyboard.h b/mathKeyboard/keyboard/MTKeyboard.h similarity index 96% rename from mathEditor/keyboard/MTKeyboard.h rename to mathKeyboard/keyboard/MTKeyboard.h index f96609d..626e63e 100644 --- a/mathEditor/keyboard/MTKeyboard.h +++ b/mathKeyboard/keyboard/MTKeyboard.h @@ -8,6 +8,8 @@ // MIT license. See the LICENSE file for details. // +#if TARGET_OS_IPHONE + #import #import "MTEditableMathLabel.h" @@ -25,7 +27,7 @@ @property (weak, nonatomic) IBOutlet UIButton *squareRootButton; @property (weak, nonatomic) IBOutlet UIButton *radicalButton; -@property (nonatomic, weak) UIView* textView; +@property (nonatomic, weak) MTView* textView; @property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *numbers; @property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *variables; @property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *operators; @@ -63,3 +65,5 @@ - (void) setRadicalState:(BOOL) highlighted; @end + +#endif diff --git a/mathEditor/keyboard/MTKeyboard.m b/mathKeyboard/keyboard/MTKeyboard.m similarity index 98% rename from mathEditor/keyboard/MTKeyboard.m rename to mathKeyboard/keyboard/MTKeyboard.m index 7a4b5fa..6c765d3 100644 --- a/mathEditor/keyboard/MTKeyboard.m +++ b/mathKeyboard/keyboard/MTKeyboard.m @@ -8,8 +8,9 @@ // MIT license. See the LICENSE file for details. // +#if TARGET_OS_IPHONE + #import "MTKeyboard.h" -#import "MTMathKeyboardRootView.h" #import "MTFontManager.h" #import "MTMathAtomFactory.h" @@ -37,7 +38,7 @@ - (NSString*) registerAndGetFontName static dispatch_once_t once_token; dispatch_once(&once_token, ^{ - NSBundle *bundle = [MTMathKeyboardRootView getMathKeyboardResourcesBundle]; + NSBundle *bundle = SWIFTPM_MODULE_BUNDLE; NSString* fontPath = [bundle pathForResource:@"lmroman10-bolditalic" ofType:@"otf"]; CGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]); CGFontRef myFont = CGFontCreateWithDataProvider(fontDataProvider); @@ -273,3 +274,5 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event } @end + +#endif diff --git a/mathEditor/keyboard/MTMathKeyboardRootView.h b/mathKeyboard/keyboard/MTMathKeyboardRootView.h similarity index 97% rename from mathEditor/keyboard/MTMathKeyboardRootView.h rename to mathKeyboard/keyboard/MTMathKeyboardRootView.h index e3cae99..3c3dc62 100644 --- a/mathEditor/keyboard/MTMathKeyboardRootView.h +++ b/mathKeyboard/keyboard/MTMathKeyboardRootView.h @@ -9,8 +9,9 @@ // MIT license. See the LICENSE file for details. // +#if TARGET_OS_IPHONE + #import -#import #import "MTKeyboard.h" #import "MTEditableMathLabel.h" @@ -42,3 +43,5 @@ @property (nonatomic) BOOL radicalHighlighted; @end + +#endif diff --git a/mathEditor/keyboard/MTMathKeyboardRootView.m b/mathKeyboard/keyboard/MTMathKeyboardRootView.m similarity index 96% rename from mathEditor/keyboard/MTMathKeyboardRootView.m rename to mathKeyboard/keyboard/MTMathKeyboardRootView.m index cdbb35f..d542560 100644 --- a/mathEditor/keyboard/MTMathKeyboardRootView.m +++ b/mathKeyboard/keyboard/MTMathKeyboardRootView.m @@ -9,6 +9,8 @@ // MIT license. See the LICENSE file for details. // +#if TARGET_OS_IPHONE + #import "MTMathKeyboardRootView.h" static NSInteger const DEFAULT_KEYBOARD = 0; @@ -46,7 +48,7 @@ +(instancetype)sharedInstance { // Gets the math keyboard resources bundle +(NSBundle *)getMathKeyboardResourcesBundle { - return [NSBundle bundleWithURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"MTKeyboardResources" withExtension:@"bundle"]]; + return SWIFTPM_MODULE_BUNDLE; } -(void)awakeFromNib @@ -212,14 +214,14 @@ - (void)setRadicalHighlighted:(BOOL)radicalHighlighted } } -- (void)startedEditing:(UIView *)label +- (void)startedEditing:(MTView *)label { for (MTKeyboard *keyboard in _keyboards) { keyboard.textView = label; } } -- (void)finishedEditing:(UIView *)label +- (void)finishedEditing:(MTView *)label { for (MTKeyboard *keyboard in _keyboards) { keyboard.textView = nil; @@ -227,3 +229,5 @@ - (void)finishedEditing:(UIView *)label } @end + +#endif