├── .appcast.xml
├── .gitignore
├── README.md
├── assets
├── HelloSketch.framework
│ ├── Headers
│ ├── HelloSketch
│ ├── HelloSketch.js
│ ├── Modules
│ ├── Resources
│ └── Versions
│ │ ├── A
│ │ ├── Headers
│ │ │ └── HelloSketch.h
│ │ ├── HelloSketch
│ │ ├── Modules
│ │ │ └── module.modulemap
│ │ └── Resources
│ │ │ └── Info.plist
│ │ └── Current
└── icon.png
├── bettertypetool.sketchplugin
└── Contents
│ ├── Resources
│ ├── HelloSketch.framework
│ │ ├── Headers
│ │ │ └── HelloSketch.h
│ │ ├── HelloSketch
│ │ ├── HelloSketch.js
│ │ ├── Modules
│ │ │ └── module.modulemap
│ │ ├── Resources
│ │ │ └── Info.plist
│ │ └── Versions
│ │ │ ├── A
│ │ │ ├── Headers
│ │ │ │ └── HelloSketch.h
│ │ │ ├── HelloSketch
│ │ │ ├── Modules
│ │ │ │ └── module.modulemap
│ │ │ └── Resources
│ │ │ │ └── Info.plist
│ │ │ └── Current
│ │ │ ├── Headers
│ │ │ └── HelloSketch.h
│ │ │ ├── HelloSketch
│ │ │ ├── Modules
│ │ │ └── module.modulemap
│ │ │ └── Resources
│ │ │ └── Info.plist
│ └── icon.png
│ └── Sketch
│ ├── manifest.json
│ ├── my-command.js
│ └── my-command.js.map
├── images
├── Icon.sketch
├── Images.sketch
├── banner.png
└── banner.svg
├── package-lock.json
├── package.json
└── src
├── manifest.json
└── my-command.js
/.appcast.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 | -
8 |
9 |
10 | -
11 |
12 |
13 | -
14 |
15 |
16 | -
17 |
18 |
19 | -
20 |
21 |
22 | -
23 |
24 |
25 | -
26 |
27 |
28 | -
29 |
30 |
31 | -
32 |
33 |
34 | -
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # build artifacts
2 | plugin.sketchplugin
3 |
4 | # npm
5 | node_modules
6 | .npm
7 | npm-debug.log
8 |
9 | # mac
10 | .DS_Store
11 |
12 | # WebStorm
13 | .idea
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 
6 |
7 | ## Supported OpenType Features
8 | - Number Position
9 | - Default Position
10 | - Superscript
11 | - Subscript
12 | - Ordinals
13 | - Scientific Notation
14 | - Number Spacing
15 | - Proportional
16 | - Monospaced/Tabular
17 | - Number Case
18 | - Lining Figures
19 | - Old-style Figures
20 | - Small Caps
21 | - Lower Case
22 | - Upper Case
23 |
24 | ## Inital Release Features
25 | - [X] When setting up the UI, set the state of the buttons properly the first time (i.e. check if there is a text object selected or not and then set the state rather than setting the default state of the UI then running `updateUI()`)
26 | - [X] Dark mode support
27 | - [X] Properly style small caps buttons
28 | - [X] Basic Support for applying properties to multiple selected Text Layers
29 | - [X] Better communicate when fonts have unspported properties
30 | - [X] Basic substring support
31 |
32 | ## Later Goals
33 | - [ ] Support Fractional Forms
34 | - [ ] Better Support Multiple layer selection
35 | - [ ] Resolve applying small caps to layers with different fonts
36 | - [ ] Respect Pixelfitting settings
37 | - [ ] More OpenType Features! (Ligatures, Stylistic Sets, etc)
38 | - [ ] Text Style support (I hope Sketch will consider tweaking things on their end rather me hacking it together)
39 | - [X] Better substring support
40 | - [ ] Keyboard shortcuts!
41 |
42 | ## Building The Plugin
43 |
44 | _This plugin was created using `skpm`. For a detailed explanation on how things work, checkout the [skpm Readme](https://github.com/skpm/skpm/blob/master/README.md)._
45 |
46 | Install the dependencies
47 |
48 | ```bash
49 | npm install
50 | ```
51 |
52 | Once the installation is done, you can run some commands inside the project folder:
53 |
54 | ```bash
55 | npm run build
56 | ```
57 |
58 | To watch for changes:
59 |
60 | ```bash
61 | npm run watch
62 | ```
63 |
64 | Additionally, if you wish to run the plugin every time it is built:
65 |
66 | ```bash
67 | npm run start
68 | ```
69 |
70 | ## Custom Configuration
71 |
72 | ### Babel
73 |
74 | To customize Babel, you have two options:
75 |
76 | * You may create a [`.babelrc`](https://babeljs.io/docs/usage/babelrc) file in your project's root directory. Any settings you define here will overwrite matching config-keys within skpm preset. For example, if you pass a "presets" object, it will replace & reset all Babel presets that skpm defaults to.
77 |
78 | * If you'd like to modify or add to the existing Babel config, you must use a `webpack.skpm.config.js` file. Visit the [Webpack](#webpack) section for more info.
79 |
80 | ### Webpack
81 |
82 | To customize webpack create `webpack.skpm.config.js` file which exports function that will change webpack's config.
83 |
84 | ```js
85 | /**
86 | * Function that mutates original webpack config.
87 | * Supports asynchronous changes when promise is returned.
88 | *
89 | * @param {object} config - original webpack config.
90 | * @param {boolean} isPluginCommand - whether the config is for a plugin command or a resource
91 | **/
92 | module.exports = function(config, isPluginCommand) {
93 | /** you can change config here **/
94 | }
95 | ```
96 |
97 | ## Debugging
98 |
99 | To view the output of your `console.log`, you have a few different options:
100 |
101 | * Use the [`sketch-dev-tools`](https://github.com/skpm/sketch-dev-tools)
102 | * Open `Console.app` and look for the sketch logs
103 | * Look at the `~/Library/Logs/com.bohemiancoding.sketch3/Plugin Output.log` file
104 |
105 | Skpm provides a convenient way to do the latter:
106 |
107 | ```bash
108 | skpm log
109 | ```
110 |
111 | The `-f` option causes `skpm log` to not stop when the end of logs is reached, but rather to wait for additional data to be appended to the input
112 |
113 | ## Publishing your plugin
114 |
115 | ```bash
116 | skpm publish
117 | ```
118 |
119 | (where `bump` can be `patch`, `minor` or `major`)
120 |
121 | `skpm publish` will create a new release on your GitHub repository and create an appcast file in order for Sketch users to be notified of the update.
122 |
123 | You will need to specify a `repository` in the `package.json`:
124 |
125 | ```diff
126 | ...
127 | + "repository" : {
128 | + "type": "git",
129 | + "url": "git+https://github.com/ORG/NAME.git"
130 | + }
131 | ...
132 | ```
133 |
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Headers:
--------------------------------------------------------------------------------
1 | Versions/Current/Headers
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/HelloSketch:
--------------------------------------------------------------------------------
1 | Versions/Current/HelloSketch
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/HelloSketch.js:
--------------------------------------------------------------------------------
1 | var HelloSketch_FrameworkPath = HelloSketch_FrameworkPath || COScript.currentCOScript().env().scriptURL.path().stringByDeletingLastPathComponent();
2 | var HelloSketch_Log = HelloSketch_Log || log;
3 | (function() {
4 | var mocha = Mocha.sharedRuntime();
5 | var frameworkName = "HelloSketch";
6 | var directory = HelloSketch_FrameworkPath;
7 | if (mocha.valueForKey(frameworkName)) {
8 | HelloSketch_Log("😎 loadFramework: `" + frameworkName + "` already loaded.");
9 | return true;
10 | } else if ([mocha loadFrameworkWithName:frameworkName inDirectory:directory]) {
11 | HelloSketch_Log("✅ loadFramework: `" + frameworkName + "` success!");
12 | mocha.setValue_forKey_(true, frameworkName);
13 | return true;
14 | } else {
15 | HelloSketch_Log("❌ loadFramework: `" + frameworkName + "` failed!: " + directory + ". Please define HelloSketch_FrameworkPath if you're trying to @import in a custom plugin");
16 | return false;
17 | }
18 | })();
19 |
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Modules:
--------------------------------------------------------------------------------
1 | Versions/Current/Modules
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Resources:
--------------------------------------------------------------------------------
1 | Versions/Current/Resources
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Versions/A/Headers/HelloSketch.h:
--------------------------------------------------------------------------------
1 | //
2 | // HelloSketch.h
3 | // HelloSketch
4 | //
5 | // Created by Kevin Gutowski on 1/3/19.
6 | // Copyright © 2019 Kevin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for HelloSketch.
12 | FOUNDATION_EXPORT double HelloSketchVersionNumber;
13 |
14 | //! Project version string for HelloSketch.
15 | FOUNDATION_EXPORT const unsigned char HelloSketchVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Versions/A/HelloSketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/assets/HelloSketch.framework/Versions/A/HelloSketch
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module HelloSketch {
2 | umbrella header "HelloSketch.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 18E226
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | HelloSketch
11 | CFBundleIdentifier
12 | com.kevingutowski.HelloSketch
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | HelloSketch
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 10E1001
31 | DTPlatformVersion
32 | GM
33 | DTSDKBuild
34 | 18E219
35 | DTSDKName
36 | macosx10.14
37 | DTXcode
38 | 1020
39 | DTXcodeBuild
40 | 10E1001
41 | NSHumanReadableCopyright
42 | Copyright © 2019 Kevin. All rights reserved.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/assets/HelloSketch.framework/Versions/Current:
--------------------------------------------------------------------------------
1 | A
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/assets/icon.png
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Headers/HelloSketch.h:
--------------------------------------------------------------------------------
1 | //
2 | // HelloSketch.h
3 | // HelloSketch
4 | //
5 | // Created by Kevin Gutowski on 1/3/19.
6 | // Copyright © 2019 Kevin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for HelloSketch.
12 | FOUNDATION_EXPORT double HelloSketchVersionNumber;
13 |
14 | //! Project version string for HelloSketch.
15 | FOUNDATION_EXPORT const unsigned char HelloSketchVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/HelloSketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/HelloSketch
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/HelloSketch.js:
--------------------------------------------------------------------------------
1 | var HelloSketch_FrameworkPath = HelloSketch_FrameworkPath || COScript.currentCOScript().env().scriptURL.path().stringByDeletingLastPathComponent();
2 | var HelloSketch_Log = HelloSketch_Log || log;
3 | (function() {
4 | var mocha = Mocha.sharedRuntime();
5 | var frameworkName = "HelloSketch";
6 | var directory = HelloSketch_FrameworkPath;
7 | if (mocha.valueForKey(frameworkName)) {
8 | HelloSketch_Log("😎 loadFramework: `" + frameworkName + "` already loaded.");
9 | return true;
10 | } else if ([mocha loadFrameworkWithName:frameworkName inDirectory:directory]) {
11 | HelloSketch_Log("✅ loadFramework: `" + frameworkName + "` success!");
12 | mocha.setValue_forKey_(true, frameworkName);
13 | return true;
14 | } else {
15 | HelloSketch_Log("❌ loadFramework: `" + frameworkName + "` failed!: " + directory + ". Please define HelloSketch_FrameworkPath if you're trying to @import in a custom plugin");
16 | return false;
17 | }
18 | })();
19 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module HelloSketch {
2 | umbrella header "HelloSketch.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 18E226
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | HelloSketch
11 | CFBundleIdentifier
12 | com.kevingutowski.HelloSketch
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | HelloSketch
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 10E1001
31 | DTPlatformVersion
32 | GM
33 | DTSDKBuild
34 | 18E219
35 | DTSDKName
36 | macosx10.14
37 | DTXcode
38 | 1020
39 | DTXcodeBuild
40 | 10E1001
41 | NSHumanReadableCopyright
42 | Copyright © 2019 Kevin. All rights reserved.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/A/Headers/HelloSketch.h:
--------------------------------------------------------------------------------
1 | //
2 | // HelloSketch.h
3 | // HelloSketch
4 | //
5 | // Created by Kevin Gutowski on 1/3/19.
6 | // Copyright © 2019 Kevin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for HelloSketch.
12 | FOUNDATION_EXPORT double HelloSketchVersionNumber;
13 |
14 | //! Project version string for HelloSketch.
15 | FOUNDATION_EXPORT const unsigned char HelloSketchVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/A/HelloSketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/A/HelloSketch
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/A/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module HelloSketch {
2 | umbrella header "HelloSketch.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/A/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 18E226
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | HelloSketch
11 | CFBundleIdentifier
12 | com.kevingutowski.HelloSketch
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | HelloSketch
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 10E1001
31 | DTPlatformVersion
32 | GM
33 | DTSDKBuild
34 | 18E219
35 | DTSDKName
36 | macosx10.14
37 | DTXcode
38 | 1020
39 | DTXcodeBuild
40 | 10E1001
41 | NSHumanReadableCopyright
42 | Copyright © 2019 Kevin. All rights reserved.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/Current/Headers/HelloSketch.h:
--------------------------------------------------------------------------------
1 | //
2 | // HelloSketch.h
3 | // HelloSketch
4 | //
5 | // Created by Kevin Gutowski on 1/3/19.
6 | // Copyright © 2019 Kevin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for HelloSketch.
12 | FOUNDATION_EXPORT double HelloSketchVersionNumber;
13 |
14 | //! Project version string for HelloSketch.
15 | FOUNDATION_EXPORT const unsigned char HelloSketchVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/Current/HelloSketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/Current/HelloSketch
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/Current/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | framework module HelloSketch {
2 | umbrella header "HelloSketch.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/HelloSketch.framework/Versions/Current/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildMachineOSBuild
6 | 18E226
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | HelloSketch
11 | CFBundleIdentifier
12 | com.kevingutowski.HelloSketch
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | HelloSketch
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSupportedPlatforms
22 |
23 | MacOSX
24 |
25 | CFBundleVersion
26 | 1
27 | DTCompiler
28 | com.apple.compilers.llvm.clang.1_0
29 | DTPlatformBuild
30 | 10E1001
31 | DTPlatformVersion
32 | GM
33 | DTSDKBuild
34 | 18E219
35 | DTSDKName
36 | macosx10.14
37 | DTXcode
38 | 1020
39 | DTXcodeBuild
40 | 10E1001
41 | NSHumanReadableCopyright
42 | Copyright © 2019 Kevin. All rights reserved.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/bettertypetool.sketchplugin/Contents/Resources/icon.png
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Sketch/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "icon": "icon.png",
5 | "commands": [
6 | {
7 | "name": "Toggle BetterTypePanel",
8 | "identifier": "my-command-identifier",
9 | "script": "my-command.js",
10 | "shortcut": "control cmd t"
11 | },
12 | {
13 | "name": "selectionChanged",
14 | "identifier": "mySelectionChanged",
15 | "script": "my-command.js",
16 | "handlers": {
17 | "actions": {
18 | "SelectionChanged.begin": "selectionChanged",
19 | "TextChanged.finish": "textChanged"
20 | }
21 | }
22 | }
23 | ],
24 | "menu": {
25 | "title": "betterTypePanel",
26 | "items": [
27 | "my-command-identifier"
28 | ],
29 | "isRoot": true
30 | },
31 | "version": "0.3.3",
32 | "description": "A sketch plugin to help manage common OpenType properties",
33 | "name": "betterTypePanel",
34 | "identifier": "betterTypePanel",
35 | "disableCocoaScriptPreprocessor": true,
36 | "appcast": "https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/master/.appcast.xml",
37 | "author": "Kevin Gutowski",
38 | "authorEmail": "kskiviolin@gmail.com"
39 | }
--------------------------------------------------------------------------------
/bettertypetool.sketchplugin/Contents/Sketch/my-command.js:
--------------------------------------------------------------------------------
1 | var globalThis=this;function __skpm_run(t,e){globalThis.context=e;var i=function(t){var e={};function i(a){if(e[a])return e[a].exports;var n=e[a]={i:a,l:!1,exports:{}};return t[a].call(n.exports,n,n.exports,i),n.l=!0,n.exports}return i.m=t,i.c=e,i.d=function(t,e,a){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)i.d(a,n,function(e){return t[e]}.bind(null,n));return a},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=1)}([function(t,e){t.exports=require("sketch")},function(t,i,a){"use strict";a.r(i),a.d(i,"shutdown",function(){return g}),a.d(i,"selectionChanged",function(){return h}),a.d(i,"textChanged",function(){return C});var n=a(0),r=a.n(n);COScript.currentCOScript().setShouldKeepAround_(!0);var o="com.betterTypePanel",s="com.betterTypePanel.popupButton.verticalPosition",l="com.betterTypePanel.radioButton.proportional",S="com.betterTypePanel.radioButton.monospaced",u="com.betterTypePanel.button.lowerCase",c="com.betterTypePanel.button.upperCase",d="com.betterTypePanel.radioButton.liningFigures",f="com.betterTypePanel.radioButton.oldStyle",m="com.betterTypePanel.popupButton.sfSymbolSize",p="com.betterTypePanel.row.sfSymbolSize",N="com.betterTypePanel.vibrancy",b=312,y=210;function g(){try{T(),HSMain.alloc().init().stopObservingTextViewSelectionChanges()}catch(t){console.error(t)}}function h(t){framework("CoreText"),NSThread.mainThread().threadDictionary()[o]&&w()}function C(){if(framework("CoreText"),NSThread.mainThread().threadDictionary()[o]){w(!0)}}function T(){var t,i,a=e.scriptPath||COScript.currentCOScript().env().scriptURL.path(),n=a.stringByDeletingLastPathComponent().stringByDeletingLastPathComponent()+"/Resources",r=r||log;t=Mocha.sharedRuntime(),i=n,t.valueForKey("HelloSketch")||(t.loadFrameworkWithName_inDirectory("HelloSketch",i)?t.setValue_forKey_(!0,"HelloSketch"):r("❌ betterTypePanel loadFramework: `HelloSketch` failed!: "+i+". Please define HelloSketch_FrameworkPath if you're trying to @import in a custom plugin - scriptPath: "+a))}function k(t,e,i){t.setTranslatesAutoresizingMaskIntoConstraints(!1),v(NSLayoutAttributeTop,t,e,i[0]),v(NSLayoutAttributeTrailing,t,e,i[1]),v(NSLayoutAttributeBottom,t,e,i[2]),v(NSLayoutAttributeLeading,t,e,i[3])}function v(t,e,i,a){i.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(e,t,NSLayoutRelationEqual,i,t,1,a))}function O(t){var e=t.text,i=t.frame,a=t.alignment,n=t.fontSize,r=void 0===n?13:n,o=NSTextField.alloc().initWithFrame(i);return o.setStringValue(e),o.setAlignment(a),o.setFont(NSFont.systemFontOfSize(r)),o.setBezeled(!1),o.setDrawsBackground(!1),o.setEditable(!1),o.setSelectable(!1),o}function A(t){var e=r.a.getSelectedDocument();e.selectedLayers.layers.filter(function(t){return"Text"==t.type}).forEach(function(e){var i=e.sketchObject.font(),a=i.pointSize(),n=(i.fontDescriptor().fontAttributes()[NSFontFeatureSettingsAttribute],i.fontDescriptor().fontDescriptorByAddingAttributes(t)),r=NSFont.fontWithDescriptor_size(n,a);if(e.sketchObject.setFont(r),1==e.sketchObject.isEditingText()){var o=e.sketchObject.editingDelegate().textView(),s=o.textStorage();x(e).forEach(function(e){var i=e.font,a=e.range,n=i.pointSize(),r=i.fontDescriptor().fontDescriptorByAddingAttributes(t),o=NSFont.fontWithDescriptor_size(r,n),l=NSDictionary.dictionaryWithObject_forKey(o,NSFontAttributeName);s.addAttributes_range(l,a)}),o.didChangeText()}}),e.sketchObject.inspectorController().reload()}function _(t,e){var i=NSFont.boldSystemFontOfSize(13),a=L(t,e),n=(i.fontDescriptor().fontAttributes()[NSFontFeatureSettingsAttribute],i.fontDescriptor().fontDescriptorByAddingAttributes(a));return NSFont.fontWithDescriptor_size(n,13)}function L(t,e){return{[NSFontFeatureSettingsAttribute]:[{[NSFontFeatureTypeIdentifierKey]:t,[NSFontFeatureSelectorIdentifierKey]:e}]}}function w(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],e=r.a.getSelectedDocument().selectedLayers.layers,i=NSThread.mainThread().threadDictionary(),a=i[N];if(null==e)return W(i),void(a.isHidden()||(a.layer().setBackgroundFilters([]),a.setHidden(!0)));var n=e.filter(function(t){return"Text"==t.type});if(0==n.length)return W(i),void(a.isHidden()||(a.layer().setBackgroundFilters([]),a.setHidden(!0)));var b=[];n.forEach(function(e){x(e,t).forEach(function(t){var e,i,a,n,r,s,l,S,u;e=t.font,i=e.familyName(),a=!1,n=NSThread.mainThread().threadDictionary(),r=n[p],s=n[o],l=s.frame().origin.x,S=s.frame().origin.y,s.frame().size.height,u=s.frame().size.height,["SF Pro Text","SF Pro Rounded","SF Pro Display","SF Compact Text","SF Compact Rounded","SF Compact Display"].forEach(function(t){if(i==t)return a=!0,void(235!=u&&(s.setFrame_display_animate(NSMakeRect(l,S-25,312,235),!0,!0),r.setHidden(!1)))}),a||210!=u&&(r.setHidden(!0),s.setFrame_display_animate(NSMakeRect(l,S+25,312,210),!0,!0));var c=function(t){var e={verticalPosition:"default",numberSpacing:"proportional",numberCase:"lining",smallCapsLowerCase:!1,smallCapsUpperCase:!1,sfSymbolSize:"medium"};(function(t){framework("CoreText");var e=[];try{T();var i=HSMain.alloc().init(),a=CTFontCreateWithName(t.fontName(),t.pointSize(),null),n=CTFontCopyFeatures(a),r=i.bridgeArray(n);e=function(t){var e=[];if(t){var i=[];t.forEach(function(t){i.push(Number(t.CTFeatureTypeIdentifier))}),i.includes(10)||e.push("verticalPosition"),i.includes(6)||e.push("numberSpacing"),i.includes(21)||e.push("numberCase"),i.includes(37)||e.push("smallCapsLowerCase"),i.includes(38)||e.push("smallCapsUpperCase")}else e.push("verticalPosition","numberSpacing","numberCase","smallCapsLowerCase","smallCapsUpperCase");return e}(r)}catch(s){console.error(s)}var o=t.familyName().toLowerCase().trim();["sf pro text","sf pro rounded","sf pro display","sf compact text","sf compact rounded","sf compact display"].includes(o)||e.push("sfSymbolSize");return e})(t).forEach(function(t){switch(t){case"verticalPosition":e.verticalPosition="disabled";break;case"numberSpacing":e.numberSpacing="disabled";break;case"numberCase":e.numberCase="disabled";break;case"smallCapsLowerCase":e.smallCapsLowerCase="disabled";break;case"smallCapsUpperCase":e.smallCapsUpperCase="disabled";break;case"sfSymbolSize":e.sfSymbolSize="disabled"}});var i=t.fontDescriptor().fontAttributes()[NSFontFeatureSettingsAttribute];i&&i.forEach(function(t){var i=Number(t[NSFontFeatureTypeIdentifierKey]),a=Number(t[NSFontFeatureSelectorIdentifierKey]);switch(i){case 10:switch(a){case 0:e.verticalPosition="default";break;case 1:e.verticalPosition="superscript";break;case 2:e.verticalPosition="subscript";break;case 3:e.verticalPosition="ordinals";break;case 4:e.verticalPosition="scientific inferiors"}break;case 6:switch(a){case 0:e.numberSpacing="monospaced";break;case 1:e.numberSpacing="proportional"}break;case 21:switch(a){case 0:e.numberCase="oldStyle";break;case 1:e.numberCase="lining"}break;case 37:switch(a){case 0:e.smallCapsLowerCase=!1;break;case 1:e.smallCapsLowerCase=!0}break;case 38:switch(a){case 0:e.smallCapsUpperCase=!1;break;case 1:e.smallCapsUpperCase=!0}break;case 35:switch(a){case 30:e.sfSymbolSize="small";break;case 31:break;case 32:e.sfSymbolSize="large"}}});return e}(t.font);b.push(c)})});var y={smallCapsLowerCase:[],smallCapsUpperCase:[],sfSymbolSize:[],numberCase:[],verticalPosition:[],numberSpacing:[]};for(var g in b.forEach(function(t){Object.keys(t).forEach(function(e){y[e].push(t[e])})}),y)y[g]=y[g].filter(h);function h(t,e,i){return i.indexOf(t)===e}var C,k=!0;for(var v in y){if(y[v].length>1){k=!1;break}if("disabled"!==y[v][0]){k=!1;break}}for(var O in k&&a.isHidden()?(a.setHidden(!1),a.layer().setBackgroundFilters([(C=CIFilter.filterWithName("CIGaussianBlur"),C.setDefaults(),C.setValue_forKey(2,"inputRadius"),C.setName("blur"),C)])):k||a.isHidden()?k&&a.isHidden():(a.layer().setBackgroundFilters([]),a.setHidden(!0)),y)if("verticalPosition"==O)!function(){var t=i[s];t.setEnabled(!0),M(),y[O].length>1?(t.selectItemWithTitle("Multiple"),y[O].forEach(function(e){"default"==e?t.itemWithTitle("Default Position").setState(NSControlStateValueMixed):"superscript"==e?t.itemWithTitle("Superscript").setState(NSControlStateValueMixed):"subscript"==e?t.itemWithTitle("Subscript").setState(NSControlStateValueMixed):"ordinals"==e?t.itemWithTitle("Ordinals").setState(NSControlStateValueMixed):"scientific inferiors"==e&&t.itemWithTitle("Scientific Notation").setState(NSControlStateValueMixed)})):"default"==y[O][0]?(t.selectItemWithTitle("Default Position"),t.itemWithTitle("Default Position").setState(NSControlStateValueOn)):"superscript"==y[O][0]?(t.selectItemWithTitle("Superscript"),t.itemWithTitle("Superscript").setState(NSControlStateValueOn)):"subscript"==y[O][0]?(t.selectItemWithTitle("Subscript"),t.itemWithTitle("Subscript").setState(NSControlStateValueOn)):"ordinals"==y[O][0]?(t.selectItemWithTitle("Ordinals"),t.itemWithTitle("Ordinals").setState(NSControlStateValueOn)):"scientific inferiors"==y[O][0]?(t.selectItemWithTitle("Scientific Notation"),t.itemWithTitle("Scientific Notation").setState(NSControlStateValueOn)):"disabled"==y[O][0]&&t.setEnabled(!1)}();else if("numberSpacing"==O){var A=i[l],_=i[S];A.setEnabled(!0),_.setEnabled(!0),y[O].length>1?(A.setState(NSOffState),_.setState(NSOffState)):"proportional"==y[O][0]?(A.setState(NSOnState),_.setState(NSOffState)):"monospaced"==y[O][0]?(A.setState(NSOffState),_.setState(NSOnState)):"disabled"==y[O][0]&&(A.setState(NSOffState),_.setState(NSOffState),A.setEnabled(!1),_.setEnabled(!1))}else if("numberCase"==O){var L=i[d],w=i[f];L.setEnabled(!0),w.setEnabled(!0),y[O].length>1?(L.setState(NSOffState),w.setState(NSOffState)):"lining"==y[O][0]?(L.setState(NSOnState),w.setState(NSOffState)):"oldStyle"==y[O][0]?(L.setState(NSOffState),w.setState(NSOnState)):"disabled"==y[O][0]&&(L.setState(NSOffState),w.setState(NSOffState),L.setEnabled(!1),w.setEnabled(!1))}else if("smallCapsUpperCase"==O){var F=i[c];F.setEnabled(!0),y[O].length>1?F.setState(NSOffState):0==y[O][0]?F.setState(NSOffState):1==y[O][0]?F.setState(NSOnState):"disabled"==y[O][0]&&(F.setState(NSOffState),F.setEnabled(!1))}else if("smallCapsLowerCase"==O){var R=i[u];R.setEnabled(!0),y[O].length>1?R.setState(NSOffState):0==y[O][0]?R.setState(NSOffState):1==y[O][0]?R.setState(NSOnState):"disabled"==y[O][0]&&(R.setState(NSOffState),R.setEnabled(!1))}else"sfSymbolSize"==O?function(){var t=i[m];t.setEnabled(!0),V(),y[O].length>1?(t.selectItemWithTitle("Multiple"),y[O].forEach(function(e){"small"==e?t.itemWithTitle("Small").setState(NSControlStateValueMixed):"medium"==e?t.itemWithTitle("Medium").setState(NSControlStateValueMixed):"large"==e&&t.itemWithTitle("Large").setState(NSControlStateValueMixed)})):"small"==y[O][0]?(t.selectItemWithTitle("Small"),t.itemWithTitle("Small").setState(NSControlStateValueOn)):"medium"==y[O][0]?(t.selectItemWithTitle("Medium"),t.itemWithTitle("Medium").setState(NSControlStateValueOn)):"large"==y[O][0]?(t.selectItemWithTitle("Large"),t.itemWithTitle("Large").setState(NSControlStateValueOn)):"disabled"==y[O][0]&&t.setEnabled(!1)}():y[O]}function W(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:["all"];(e.includes("all")||e.includes("verticalPosition"))&&t[s].setEnabled(!1);if(e.includes("all")||e.includes("numberSpacing")){var i=t[l],a=t[S];i.setEnabled(!1),a.setEnabled(!1)}if(e.includes("all")||e.includes("numberCase")){var n=t[d],r=t[f];n.setEnabled(!1),r.setEnabled(!1)}(e.includes("all")||e.includes("smallCapsUpperCase"))&&t[c].setEnabled(!1);(e.includes("all")||e.includes("smallCapsLowerCase"))&&t[u].setEnabled(!1);(e.includes("all")||e.includes("sfSymbolSize"))&&t[m].setEnabled(!1)}function F(t,e,i){t.close();try{T(),HSMain.alloc().init().stopObservingTextViewSelectionChanges()}catch(a){console.error(a)}e.removeObjectForKey(i),e.panelOpen=!1,COScript.currentCOScript().setShouldKeepAround_(!1)}function M(){var t=NSThread.mainThread().threadDictionary()[s];t.itemWithTitle("Default Position").setState(NSControlStateValueOff),t.itemWithTitle("Superscript").setState(NSControlStateValueOff),t.itemWithTitle("Subscript").setState(NSControlStateValueOff),t.itemWithTitle("Ordinals").setState(NSControlStateValueOff),t.itemWithTitle("Scientific Notation").setState(NSControlStateValueOff)}function V(){var t=NSThread.mainThread().threadDictionary()[m];t.itemWithTitle("Small").setState(NSControlStateValueOff),t.itemWithTitle("Medium").setState(NSControlStateValueOff),t.itemWithTitle("Large").setState(NSControlStateValueOff)}function x(t){var e,i,a,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=t.sketchObject,o=MOPointer.alloc().init(),s=!0;try{a=r.editingDelegate().textView()}catch(f){s=!1}if(s){var l=a.textStorage();i=a.selectedRange(),e=l,n&&(i=NSMakeRange(0,t.text.length))}else{var S=r.attributedStringValue();i=NSMakeRange(0,t.text.length),e=S}var u=[];if(0==i.length){var c=e.attribute_atIndex_longestEffectiveRange_inRange(NSFontAttributeName,i.location,o,i);u.push({font:c,range:o.value()})}for(;i.length>0;){var d=e.attribute_atIndex_longestEffectiveRange_inRange(NSFontAttributeName,i.location,o,i);i=NSMakeRange(NSMaxRange(o.value()),NSMaxRange(i)-NSMaxRange(o.value())),u.push({font:d,range:o.value()})}return u}i.default=function(){var t;(t=NSThread.mainThread().threadDictionary())[o]?F(t[o],t,o):(t.panelOpen=!0,function(t,e){var i=NSPanel.alloc().init();i.setFrame_display(NSMakeRect(0,0,b,y),!0),i.setStyleMask(NSTexturedBackgroundWindowMask|NSTitledWindowMask|NSClosableWindowMask),i.title="BetterTypePanel",i.center(),i.makeKeyAndOrderFront(null),i.setLevel(NSFloatingWindowLevel),i.standardWindowButton(NSWindowMiniaturizeButton).setHidden(!0),i.standardWindowButton(NSWindowZoomButton).setHidden(!0),t[e]=i;var a=O({text:"Number Position:",frame:NSMakeRect(0,0,109,17),alignment:NSTextAlignmentRight});a.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(a,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,109));var n=NSPopUpButton.alloc().initWithFrame(NSMakeRect(0,0,150,25));n.addItemsWithTitles(["Default Position","Superscript","Subscript","Ordinals","Scientific Notation","Multiple"]),t[s]=n,n.itemWithTitle("Multiple").setHidden(!0),n.setCOSJSTargetFunction(function(t){return function(t){if("Superscript"==t.title()){var e=L(kVerticalPositionType,kSuperiorsSelector);A(e)}else if("Subscript"==t.title()){var i=L(kVerticalPositionType,kInferiorsSelector);A(i)}else if("Ordinals"==t.title()){var a=L(kVerticalPositionType,kOrdinalsSelector);A(a)}else if("Scientific Notation"==t.title()){var n=L(kVerticalPositionType,kScientificInferiorsSelector);A(n)}else{var r=L(kVerticalPositionType,kNormalPositionSelector);A(r)}}(t)});var r=NSStackView.alloc().initWithFrame(NSMakeRect(0,0,284,25));r.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),r.setAlignment(NSLayoutAttributeFirstBaseline),r.setSpacing(4),r.setViews_inGravity([a,n],NSStackViewGravityLeading);var o=O({text:"Number Spacing:",frame:NSMakeRect(0,0,109,17),alignment:NSTextAlignmentRight});o.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(o,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,109));var g=NSButton.alloc().initWithFrame(NSMakeRect(0,0,97,18));g.setButtonType(NSRadioButton),g.setTitle("Proportional"),g.setState(NSOnState),t[l]=g;var h=NSButton.alloc().initWithFrame(NSMakeRect(0,0,150,18));h.setButtonType(NSRadioButton),h.setTitle("Monospaced/Tabular"),h.setState(NSOffState),t[S]=h;var C=function(t){if("Proportional"==t.title()){var e=L(kNumberSpacingType,kProportionalNumbersSelector);A(e)}else{var i=L(kNumberSpacingType,kMonospacedNumbersSelector);A(i)}};g.setCOSJSTargetFunction(function(t){return C(t)}),h.setCOSJSTargetFunction(function(t){return C(t)});var T=NSStackView.stackViewWithViews([g,h]);T.setOrientation(NSUserInterfaceLayoutOrientationVertical),T.setAlignment(NSLayoutAttributeLeading),T.setSpacing(4),T.setTranslatesAutoresizingMaskIntoConstraints(!1);var w=NSStackView.alloc().initWithFrame(NSMakeRect(0,0,284,36));w.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),w.setAlignment(NSLayoutAttributeFirstBaseline),w.setSpacing(4),w.setViews_inGravity([o,T],NSStackViewGravityLeading);var W=O({text:"Number Case:",frame:NSMakeRect(0,0,109,17),alignment:NSTextAlignmentRight});W.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(W,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,109));var M=NSButton.alloc().initWithFrame(NSMakeRect(0,0,104,17));M.setButtonType(NSRadioButton),M.setTitle("Lining figures"),M.setState(NSOnState),t[d]=M;var V=NSButton.alloc().initWithFrame(NSMakeRect(0,0,124,18));V.setButtonType(NSRadioButton),V.setTitle("Old-style figures"),V.setState(NSOffState),t[f]=V;var x=function(t){if("Old-style figures"==t.title()){var e=L(kNumberCaseType,kLowerCaseNumbersSelector);A(e)}else{var i=L(kNumberCaseType,kUpperCaseNumbersSelector);A(i)}};M.setCOSJSTargetFunction(function(t){return x(t)}),V.setCOSJSTargetFunction(function(t){return x(t)});var R=NSStackView.stackViewWithViews([M,V]);R.setOrientation(NSUserInterfaceLayoutOrientationVertical),R.setAlignment(NSLayoutAttributeLeading),R.setSpacing(4),R.setTranslatesAutoresizingMaskIntoConstraints(!1);var B=NSStackView.alloc().initWithFrame(NSMakeRect(0,0,284,36));B.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),B.setAlignment(NSLayoutAttributeFirstBaseline),B.setSpacing(4),B.setViews_inGravity([W,R],NSStackViewGravityLeading);var P=O({text:"Small Caps:",frame:NSMakeRect(0,0,109,17),alignment:NSTextAlignmentRight});P.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(P,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,109));var I=O({text:"Tt →",frame:NSMakeRect(0,0,32,17),alignment:NSTextAlignmentLeft}),E=NSButton.alloc().initWithFrame(NSMakeRect(0,0,72,32));E.setButtonType(NSButtonTypeOnOff),E.setBezelStyle(NSRoundedBezelStyle);var z=NSMutableAttributedString.new().initWithString("Tt"),D=NSMakeRange(1,1),H=_(37,1);z.setAlignment_range(NSTextAlignmentCenter,NSMakeRange(0,2)),z.addAttribute_value_range(NSFontAttributeName,H,D),E.setAttributedTitle(z),E.setState(NSOffState),E.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(E,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,60)),t[u]=E,E.setCOSJSTargetFunction(function(t){return function(t){if(0==t.state()){var e=L(kLowerCaseType,kDefaultLowerCaseSelector);A(e)}else{var i=L(kLowerCaseType,kLowerCaseSmallCapsSelector);A(i)}}(t)});var U=O({text:"Lower Case",frame:NSMakeRect(0,0,65,14),alignment:NSTextAlignmentCenter,fontSize:11}),j=NSStackView.stackViewWithViews([E,U]);j.setOrientation(NSUserInterfaceLayoutOrientationVertical),j.setAlignment(NSLayoutAttributeCenterX),j.setSpacing(4),j.setTranslatesAutoresizingMaskIntoConstraints(!1);var K=NSButton.alloc().initWithFrame(NSMakeRect(0,0,72,32));K.setButtonType(NSButtonTypeOnOff),K.setBezelStyle(NSRoundedBezelStyle);var G=NSMutableAttributedString.new().initWithString("Tt"),q=NSMakeRange(0,1),J=_(38,1);G.setAlignment_range(NSTextAlignmentCenter,NSMakeRange(0,2)),NSMutableParagraphStyle.new().init().setAlignment(NSTextAlignmentCenter),G.addAttribute_value_range(NSFontAttributeName,J,q),K.setAttributedTitle(G),K.setState(NSOffState),K.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(K,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,60)),t[c]=K,K.setCOSJSTargetFunction(function(t){return function(t){if(0==t.state()){var e=L(kUpperCaseType,kDefaultUpperCaseSelector);A(e)}else{var i=L(kUpperCaseType,kUpperCaseSmallCapsSelector);A(i)}}(t)});var X=O({text:"Upper Case",frame:NSMakeRect(0,0,66,14),alignment:NSTextAlignmentCenter,fontSize:11}),Z=NSStackView.stackViewWithViews([K,X]);Z.setOrientation(NSUserInterfaceLayoutOrientationVertical),Z.setSpacing(4),Z.setTranslatesAutoresizingMaskIntoConstraints(!1);var Q=NSStackView.stackViewWithViews([I,j,Z]);Q.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),Q.setAlignment(NSLayoutAttributeFirstBaseline),Q.setSpacing(4),Q.setTranslatesAutoresizingMaskIntoConstraints(!1);var Y=NSStackView.alloc().initWithFrame(NSMakeRect(0,0,284,36));Y.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),Y.setAlignment(NSLayoutAttributeFirstBaseline),Y.setSpacing(4),Y.setViews_inGravity([P,Q],NSStackViewGravityLeading);var $=O({text:"SF Symbol Size:",frame:NSMakeRect(0,0,109,17),alignment:NSTextAlignmentRight});$.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant($,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,109));var tt=NSPopUpButton.alloc().initWithFrame(NSMakeRect(0,0,150,25));tt.addItemsWithTitles(["Small","Medium","Large","Multiple"]),t[m]=tt,tt.itemWithTitle("Multiple").setHidden(!0),tt.setCOSJSTargetFunction(function(t){return function(t){if("Small"==t.title()){var e=L(kStylisticAlternativesType,kStylisticAltFifteenOnSelector),i=L(kStylisticAlternativesType,kStylisticAltSixteenOffSelector),a=[e,i];a.forEach(function(t){A(t)})}else if("Medium"==t.title()){var n=L(kStylisticAlternativesType,kStylisticAltFifteenOffSelector),r=L(kStylisticAlternativesType,kStylisticAltSixteenOffSelector),o=[n,r];o.forEach(function(t){A(t)})}else if("Large"==t.title()){var s=L(kStylisticAlternativesType,kStylisticAltFifteenOffSelector),l=L(kStylisticAlternativesType,kStylisticAltSixteenOnSelector),S=[s,l];S.forEach(function(t){A(t)})}}(t)});var et=NSStackView.alloc().initWithFrame(NSMakeRect(0,0,284,25));et.setOrientation(NSUserInterfaceLayoutOrientationHorizontal),et.setAlignment(NSLayoutAttributeFirstBaseline),et.setSpacing(4),et.setViews_inGravity([$,tt],NSStackViewGravityLeading),t[p]=et,et.setHidden(!0);var it=NSStackView.stackViewWithViews([r,w,B,Y,et]);it.setOrientation(NSUserInterfaceLayoutOrientationVertical),it.setAlignment(NSLayoutAttributeLeading),it.setSpacing(8),it.setTranslatesAutoresizingMaskIntoConstraints(!1),i.contentView().addSubview(it),i.contentView().setFlipped(!0),k(it,i.contentView(),[16,16,8,16]),function(t){var e=NSView.alloc().initWithFrame(NSMakeRect(0,0,b,y));e.setBackgroundColor(NSColor.colorWithRed_green_blue_alpha(0,0,0,.6)),e.setWantsLayer(!0),t.addSubview(e),k(e,t,[0,0,0,0]);var i=O({text:"Selected text doesn't contain any supported font features. Please try a different typeface.",frame:NSMakeRect(0,0,b,17),alignment:NSTextAlignmentCenter,fontSize:12});i.setTextColor(NSColor.whiteColor()),e.addSubview(i),i.setTranslatesAutoresizingMaskIntoConstraints(!1),v(NSLayoutAttributeCenterX,i,e,0),v(NSLayoutAttributeTop,i,e,40),i.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(i,NSLayoutAttributeWidth,NSLayoutRelationEqual,nil,NSLayoutAttributeNotAnAttribute,1,260)),e.setHidden(!0),NSThread.mainThread().threadDictionary()[N]=e}(i.contentView()),t[e]=i,i.standardWindowButton(NSWindowCloseButton).setCOSJSTargetFunction(function(a){F(i,t,e)})}(t,o)),T(),framework("CoreText");var e=HSMain.alloc().init();e.beginObservingTextViewSelectionChanges(),e.setCallbackForTextViewSelectionChange(function(){w()}),w()}}]);"default"===t&&"function"==typeof i?i(e):i[t](e)}globalThis.onRun=__skpm_run.bind(this,"default"),globalThis.selectionChanged=__skpm_run.bind(this,"selectionChanged"),globalThis.textChanged=__skpm_run.bind(this,"textChanged");
--------------------------------------------------------------------------------
/images/Icon.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/images/Icon.sketch
--------------------------------------------------------------------------------
/images/Images.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/images/Images.sketch
--------------------------------------------------------------------------------
/images/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinGutowski/betterTypePanel/51024af7f07877e50a2cd4ebf8d51061351371e1/images/banner.png
--------------------------------------------------------------------------------
/images/banner.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bettertypepanel",
3 | "version": "0.3.3",
4 | "engines": {
5 | "sketch": ">=3.0"
6 | },
7 | "skpm": {
8 | "name": "betterTypePanel",
9 | "manifest": "src/manifest.json",
10 | "main": "bettertypetool.sketchplugin",
11 | "assets": [
12 | "assets/**/*"
13 | ]
14 | },
15 | "scripts": {
16 | "build": "skpm-build",
17 | "watch": "skpm-build --watch",
18 | "start": "skpm-build --watch --run",
19 | "postinstall": "npm run build && skpm-link"
20 | },
21 | "devDependencies": {
22 | "@skpm/builder": "^0.7.1"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/KevinGutowski/betterTypePanel"
27 | },
28 | "author": "Kevin Gutowski ",
29 | "description": "A sketch plugin to help manage common OpenType properties"
30 | }
31 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "icon": "icon.png",
5 | "commands": [
6 | {
7 | "name": "Toggle BetterTypePanel",
8 | "identifier": "my-command-identifier",
9 | "script": "./my-command.js",
10 | "shortcut": "control cmd t"
11 | },
12 | {
13 | "name": "selectionChanged",
14 | "identifier": "mySelectionChanged",
15 | "script": "./my-command.js",
16 | "handlers": {
17 | "actions": {
18 | "SelectionChanged.begin" : "selectionChanged",
19 | "TextChanged.finish": "textChanged"
20 | }
21 | }
22 | }
23 | ],
24 | "menu": {
25 | "title": "betterTypePanel",
26 | "items": [
27 | "my-command-identifier"
28 | ],
29 | "isRoot": true
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/my-command.js:
--------------------------------------------------------------------------------
1 | import sketch from 'sketch'
2 | // Documentation:
3 | // https://developer.sketchapp.com/reference/api/
4 | // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html
5 |
6 | COScript.currentCOScript().setShouldKeepAround_(true)
7 |
8 | let threadIdentifier = "com.betterTypePanel"
9 | let verticalPositionPopupButtonID = "com.betterTypePanel.popupButton.verticalPosition"
10 | let radioButtonProportionalID = "com.betterTypePanel.radioButton.proportional"
11 | let radioButtonMonospacedOrTabularID = "com.betterTypePanel.radioButton.monospaced"
12 | let pushOnOffButtonLowerCaseID = "com.betterTypePanel.button.lowerCase"
13 | let pushOnOffButtonUpperCaseID = "com.betterTypePanel.button.upperCase"
14 | let radioButtonLiningFiguresID = "com.betterTypePanel.radioButton.liningFigures"
15 | let radioButtonOldStyleFiguresID = "com.betterTypePanel.radioButton.oldStyle"
16 | let sfSymbolSizePopupButtonID = "com.betterTypePanel.popupButton.sfSymbolSize"
17 | let sfSymbolSizeRow = "com.betterTypePanel.row.sfSymbolSize"
18 | let vibrancyViewID = "com.betterTypePanel.vibrancy"
19 | var panelWidth = 312
20 | var panelHeight = 210
21 |
22 | export default function() {
23 | runPanel()
24 |
25 | setupFramework()
26 | framework("CoreText");
27 |
28 | let main = HSMain.alloc().init()
29 | main.beginObservingTextViewSelectionChanges()
30 | main.setCallbackForTextViewSelectionChange(() => {
31 | updateUI()
32 | })
33 |
34 | updateUI()
35 | }
36 |
37 | export function shutdown() {
38 | try {
39 | setupFramework()
40 | let main = HSMain.alloc().init()
41 | main.stopObservingTextViewSelectionChanges()
42 | } catch (error) {
43 | console.error(error)
44 | }
45 | }
46 |
47 | export function selectionChanged(context) {
48 | framework("CoreText");
49 | let threadDictionary = NSThread.mainThread().threadDictionary()
50 | // check if the panel is open, if open update UI, else just do nothing
51 | if (threadDictionary[threadIdentifier]) {
52 | updateUI()
53 | } else {
54 | return
55 | }
56 | }
57 |
58 | export function textChanged() {
59 | framework("CoreText");
60 | let threadDictionary = NSThread.mainThread().threadDictionary()
61 | // check if the panel is open, if open update UI, else just do nothing
62 | if (threadDictionary[threadIdentifier]) {
63 | let useFullSelection = true
64 | updateUI(useFullSelection)
65 | } else {
66 | return
67 | }
68 | }
69 |
70 | function setupFramework() {
71 | var scriptPath = context.scriptPath || COScript.currentCOScript().env().scriptURL.path()
72 | var HelloSketch_FrameworkPath = scriptPath.stringByDeletingLastPathComponent().stringByDeletingLastPathComponent() + "/Resources"
73 | var HelloSketch_Log = HelloSketch_Log || log;
74 | (function() {
75 | var mocha = Mocha.sharedRuntime();
76 | var frameworkName = "HelloSketch";
77 | var directory = HelloSketch_FrameworkPath;
78 | if (mocha.valueForKey(frameworkName)) {
79 | //HelloSketch_Log("😎 betterTypePanel loadFramework: `" + frameworkName + "` already loaded.");
80 | return true;
81 | } else if (mocha.loadFrameworkWithName_inDirectory(frameworkName, directory)) {
82 | //HelloSketch_Log("✅ betterTypePanel loadFramework: `" + frameworkName + "` success!");
83 | mocha.setValue_forKey_(true, frameworkName);
84 | return true;
85 | } else {
86 | HelloSketch_Log("❌ betterTypePanel loadFramework: `" + frameworkName + "` failed!: " + directory + ". Please define HelloSketch_FrameworkPath if you're trying to @import in a custom plugin - scriptPath: " + scriptPath);
87 | return false;
88 | }
89 | })();
90 | }
91 |
92 | function runPanel() {
93 | let threadDictionary = NSThread.mainThread().threadDictionary()
94 |
95 | // If there is already a panel, close it
96 | if (threadDictionary[threadIdentifier]) {
97 | closePanel(threadDictionary[threadIdentifier], threadDictionary, threadIdentifier)
98 | } else {
99 | threadDictionary.panelOpen = true
100 | setupPanel(threadDictionary, threadIdentifier)
101 | }
102 | }
103 |
104 | function setupPanel(threadDictionary, identifier) {
105 | let panel = NSPanel.alloc().init()
106 | panel.setFrame_display(NSMakeRect(0, 0, panelWidth, panelHeight), true)
107 | panel.setStyleMask(NSTexturedBackgroundWindowMask | NSTitledWindowMask | NSClosableWindowMask)
108 | panel.title = "BetterTypePanel"
109 |
110 | panel.center()
111 | panel.makeKeyAndOrderFront(null)
112 | panel.setLevel(NSFloatingWindowLevel)
113 |
114 | panel.standardWindowButton(NSWindowMiniaturizeButton).setHidden(true)
115 | panel.standardWindowButton(NSWindowZoomButton).setHidden(true)
116 |
117 | threadDictionary[identifier] = panel
118 |
119 | const column1width = 109
120 | const column2width = 171
121 | const columnSpacing = 4
122 | const rowSpacing = 8
123 | const mainViewWidth = column1width + column2width + columnSpacing
124 |
125 | // MARK: SETUP ROW 1
126 | var verticalPositionLabel = createTextField({
127 | text: "Number Position:",
128 | frame: NSMakeRect(0,0,column1width,17),
129 | alignment: NSTextAlignmentRight
130 | })
131 |
132 | verticalPositionLabel.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
133 | verticalPositionLabel,
134 | NSLayoutAttributeWidth,
135 | NSLayoutRelationEqual,
136 | nil,
137 | NSLayoutAttributeNotAnAttribute,
138 | 1.0,
139 | column1width
140 | ))
141 |
142 | var verticalPositionPopupButton = NSPopUpButton.alloc().initWithFrame(NSMakeRect(0,0,150,25))
143 | verticalPositionPopupButton.addItemsWithTitles([
144 | 'Default Position',
145 | 'Superscript',
146 | 'Subscript',
147 | 'Ordinals',
148 | 'Scientific Notation',
149 | 'Multiple'
150 | ])
151 |
152 | threadDictionary[verticalPositionPopupButtonID] = verticalPositionPopupButton
153 | verticalPositionPopupButton.itemWithTitle('Multiple').setHidden(true)
154 |
155 | let verticalPositionTargetFunction = (sender) => {
156 | // console.log(sender.title() + ' dropdown button was selected')
157 | // Vertical Position
158 | // ID: kVerticalPositionType
159 | //
160 | // Selectors
161 | //
162 | // kNormalPositionSelector
163 | // This is the default. It means to display the text with no vertical displacement.
164 | //
165 | // kSuperiorsSelector
166 | // Changes any characters having superior forms in the font into those forms.
167 | //
168 | // kInferiorsSelector
169 | // Changes any characters having inferior forms in the font into those forms.
170 | //
171 | // kOrdinalsSelector
172 | // Contextually changes certain letters into their superior forms, like in Spanish changing from 1a to 1ª.
173 | //
174 | // kScientificInferiorsSelector
175 | // Changes any characters having them into inferior forms designed for a technical context (as in H2O).
176 | //
177 | if (sender.title() == 'Superscript') {
178 | let settingsAttribute = getSettingsAttributeForKey_Value(kVerticalPositionType, kSuperiorsSelector)
179 | updateFontFeatureSettingsAttribute(settingsAttribute)
180 | } else if (sender.title() == 'Subscript') {
181 | let settingsAttribute = getSettingsAttributeForKey_Value(kVerticalPositionType, kInferiorsSelector)
182 | updateFontFeatureSettingsAttribute(settingsAttribute)
183 | } else if (sender.title() == 'Ordinals') {
184 | let settingsAttribute = getSettingsAttributeForKey_Value(kVerticalPositionType, kOrdinalsSelector)
185 | updateFontFeatureSettingsAttribute(settingsAttribute)
186 | } else if (sender.title() == 'Scientific Notation') {
187 | let settingsAttribute = getSettingsAttributeForKey_Value(kVerticalPositionType, kScientificInferiorsSelector)
188 | updateFontFeatureSettingsAttribute(settingsAttribute)
189 | } else {
190 | let settingsAttribute = getSettingsAttributeForKey_Value(kVerticalPositionType, kNormalPositionSelector)
191 | updateFontFeatureSettingsAttribute(settingsAttribute)
192 | }
193 | }
194 |
195 | verticalPositionPopupButton.setCOSJSTargetFunction(sender => verticalPositionTargetFunction(sender))
196 |
197 | var row1 = NSStackView.alloc().initWithFrame(NSMakeRect(0,0,mainViewWidth,25))
198 | row1.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
199 | row1.setAlignment(NSLayoutAttributeFirstBaseline)
200 | row1.setSpacing(columnSpacing)
201 | row1.setViews_inGravity([verticalPositionLabel,verticalPositionPopupButton],NSStackViewGravityLeading)
202 |
203 | // MARK: Setup Row 2
204 | var numberSpacingLabel = createTextField({
205 | text: "Number Spacing:",
206 | frame: NSMakeRect(0,0,column1width,17),
207 | alignment: NSTextAlignmentRight
208 | })
209 |
210 | numberSpacingLabel.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
211 | numberSpacingLabel,
212 | NSLayoutAttributeWidth,
213 | NSLayoutRelationEqual,
214 | nil,
215 | NSLayoutAttributeNotAnAttribute,
216 | 1.0,
217 | column1width
218 | ))
219 |
220 | let radioButtonProportional = NSButton.alloc().initWithFrame(NSMakeRect(0,0,97,18))
221 | radioButtonProportional.setButtonType(NSRadioButton)
222 | radioButtonProportional.setTitle('Proportional')
223 | radioButtonProportional.setState(NSOnState)
224 |
225 | threadDictionary[radioButtonProportionalID] = radioButtonProportional
226 |
227 | let radioButtonMonospacedOrTabular = NSButton.alloc().initWithFrame(NSMakeRect(0,0,150,18))
228 | radioButtonMonospacedOrTabular.setButtonType(NSRadioButton)
229 | radioButtonMonospacedOrTabular.setTitle('Monospaced/Tabular')
230 | radioButtonMonospacedOrTabular.setState(NSOffState)
231 |
232 | threadDictionary[radioButtonMonospacedOrTabularID] = radioButtonMonospacedOrTabular
233 |
234 | let numberSpacingTargetFunction = (sender) => {
235 | // console.log(sender.title() + ' radio button was clicked')
236 |
237 | if (sender.title() == 'Proportional') {
238 | let settingsAttribute = getSettingsAttributeForKey_Value(kNumberSpacingType, kProportionalNumbersSelector)
239 | updateFontFeatureSettingsAttribute(settingsAttribute)
240 | } else {
241 | let settingsAttribute = getSettingsAttributeForKey_Value(kNumberSpacingType, kMonospacedNumbersSelector)
242 | updateFontFeatureSettingsAttribute(settingsAttribute)
243 | }
244 | }
245 |
246 | radioButtonProportional.setCOSJSTargetFunction(sender => numberSpacingTargetFunction(sender))
247 | radioButtonMonospacedOrTabular.setCOSJSTargetFunction(sender => numberSpacingTargetFunction(sender))
248 |
249 | var numberSpacingRadioGroupStackView = NSStackView.stackViewWithViews([radioButtonProportional, radioButtonMonospacedOrTabular])
250 | numberSpacingRadioGroupStackView.setOrientation(NSUserInterfaceLayoutOrientationVertical)
251 | numberSpacingRadioGroupStackView.setAlignment(NSLayoutAttributeLeading)
252 | numberSpacingRadioGroupStackView.setSpacing(4)
253 | numberSpacingRadioGroupStackView.setTranslatesAutoresizingMaskIntoConstraints(false)
254 |
255 | var row2 = NSStackView.alloc().initWithFrame(NSMakeRect(0,0,mainViewWidth,36))
256 | row2.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
257 | row2.setAlignment(NSLayoutAttributeFirstBaseline)
258 | row2.setSpacing(columnSpacing)
259 | row2.setViews_inGravity([numberSpacingLabel, numberSpacingRadioGroupStackView], NSStackViewGravityLeading)
260 |
261 | // MARK: Setup Row 3
262 | var numberCaseLabel = createTextField({
263 | text: "Number Case:",
264 | frame: NSMakeRect(0,0,column1width,17),
265 | alignment: NSTextAlignmentRight
266 | })
267 |
268 | numberCaseLabel.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
269 | numberCaseLabel,
270 | NSLayoutAttributeWidth,
271 | NSLayoutRelationEqual,
272 | nil,
273 | NSLayoutAttributeNotAnAttribute,
274 | 1.0,
275 | column1width
276 | ))
277 |
278 | var radioButtonLiningFigures = NSButton.alloc().initWithFrame(NSMakeRect(0,0,104,17))
279 | radioButtonLiningFigures.setButtonType(NSRadioButton)
280 | radioButtonLiningFigures.setTitle('Lining figures')
281 | radioButtonLiningFigures.setState(NSOnState)
282 |
283 | threadDictionary[radioButtonLiningFiguresID] = radioButtonLiningFigures
284 |
285 | var radioButtonOldStyleFigures = NSButton.alloc().initWithFrame(NSMakeRect(0,0,124,18))
286 | radioButtonOldStyleFigures.setButtonType(NSRadioButton)
287 | radioButtonOldStyleFigures.setTitle('Old-style figures')
288 | radioButtonOldStyleFigures.setState(NSOffState)
289 |
290 | threadDictionary[radioButtonOldStyleFiguresID] = radioButtonOldStyleFigures
291 |
292 | let numberCaseTargetFunction = (sender) => {
293 | // console.log(sender.title() + ' radio button was clicked')
294 |
295 | if (sender.title() == "Old-style figures") {
296 | let settingsAttribute = getSettingsAttributeForKey_Value(kNumberCaseType, kLowerCaseNumbersSelector)
297 | updateFontFeatureSettingsAttribute(settingsAttribute)
298 | } else {
299 | let settingsAttribute = getSettingsAttributeForKey_Value(kNumberCaseType, kUpperCaseNumbersSelector)
300 | updateFontFeatureSettingsAttribute(settingsAttribute)
301 | }
302 | }
303 |
304 | radioButtonLiningFigures.setCOSJSTargetFunction(sender => numberCaseTargetFunction(sender))
305 | radioButtonOldStyleFigures.setCOSJSTargetFunction(sender => numberCaseTargetFunction(sender))
306 |
307 | var numberCaseRadioGroupStackView = NSStackView.stackViewWithViews([radioButtonLiningFigures,radioButtonOldStyleFigures])
308 | numberCaseRadioGroupStackView.setOrientation(NSUserInterfaceLayoutOrientationVertical)
309 | numberCaseRadioGroupStackView.setAlignment(NSLayoutAttributeLeading)
310 | numberCaseRadioGroupStackView.setSpacing(4)
311 | numberCaseRadioGroupStackView.setTranslatesAutoresizingMaskIntoConstraints(false)
312 |
313 | var row3 = NSStackView.alloc().initWithFrame(NSMakeRect(0,0,mainViewWidth,36))
314 | row3.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
315 | row3.setAlignment(NSLayoutAttributeFirstBaseline)
316 | row3.setSpacing(columnSpacing)
317 | row3.setViews_inGravity([numberCaseLabel, numberCaseRadioGroupStackView], NSStackViewGravityLeading)
318 |
319 | // MARK: Setup Row 4
320 | var smallCapsLabel = createTextField({
321 | text: "Small Caps:",
322 | frame: NSMakeRect(0,0,column1width,17),
323 | alignment: NSTextAlignmentRight
324 | })
325 |
326 | smallCapsLabel.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
327 | smallCapsLabel,
328 | NSLayoutAttributeWidth,
329 | NSLayoutRelationEqual,
330 | nil,
331 | NSLayoutAttributeNotAnAttribute,
332 | 1.0,
333 | column1width
334 | ))
335 |
336 | var smallCapsExampleLabel = createTextField({
337 | text: "Tt →",
338 | frame: NSMakeRect(0,0,32,17),
339 | alignment: NSTextAlignmentLeft,
340 | })
341 |
342 | var pushOnOffButtonLowerCase = NSButton.alloc().initWithFrame(NSMakeRect(0,0,72,32))
343 | pushOnOffButtonLowerCase.setButtonType(NSButtonTypeOnOff)
344 | pushOnOffButtonLowerCase.setBezelStyle(NSRoundedBezelStyle)
345 |
346 | let lowerCaseAttributedString = NSMutableAttributedString.new().initWithString("Tt")
347 | let lowerCaseRange = NSMakeRange(1,1)
348 | let lowerCaseFont = getFontForKey_Value(37,1)
349 | lowerCaseAttributedString.setAlignment_range(NSTextAlignmentCenter, NSMakeRange(0,2))
350 | lowerCaseAttributedString.addAttribute_value_range(NSFontAttributeName,lowerCaseFont,lowerCaseRange)
351 | pushOnOffButtonLowerCase.setAttributedTitle(lowerCaseAttributedString)
352 | pushOnOffButtonLowerCase.setState(NSOffState)
353 |
354 | pushOnOffButtonLowerCase.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
355 | pushOnOffButtonLowerCase,
356 | NSLayoutAttributeWidth,
357 | NSLayoutRelationEqual,
358 | nil,
359 | NSLayoutAttributeNotAnAttribute,
360 | 1.0,
361 | 60
362 | ))
363 |
364 | threadDictionary[pushOnOffButtonLowerCaseID] = pushOnOffButtonLowerCase
365 |
366 | let smallCapsLowerCaseTargetFunction = (sender) => {
367 | // console.log(sender.title() + ' toggle was clicked')
368 | // Small Caps Lower Case
369 | // ID: kLowerCaseType
370 | //
371 | // SELECTORS
372 | // kDefaultLowerCaseSelector = 0
373 | // Use standard lower-case glyphs
374 | //
375 | // kLowerCaseSmallCapsSelector = 1
376 | // Display lower-case glyphs as small caps. (This is the most common way of displaying small caps.)
377 | //
378 | // kLowerCasePetiteCapsSelector = 2
379 | // Display lower-case glyphs as petite caps
380 | //
381 | if (sender.state() == 0) {
382 | let settingsAttribute = getSettingsAttributeForKey_Value(kLowerCaseType, kDefaultLowerCaseSelector)
383 | updateFontFeatureSettingsAttribute(settingsAttribute)
384 | } else {
385 | let settingsAttribute = getSettingsAttributeForKey_Value(kLowerCaseType, kLowerCaseSmallCapsSelector)
386 | updateFontFeatureSettingsAttribute(settingsAttribute)
387 | }
388 | }
389 | pushOnOffButtonLowerCase.setCOSJSTargetFunction(sender => smallCapsLowerCaseTargetFunction(sender))
390 |
391 | var lowerCaseLabel = createTextField({
392 | text: "Lower Case",
393 | frame: NSMakeRect(0,0,65,14),
394 | alignment: NSTextAlignmentCenter,
395 | fontSize: 11
396 | })
397 |
398 | var lowerCaseStackView = NSStackView.stackViewWithViews([pushOnOffButtonLowerCase,lowerCaseLabel])
399 | lowerCaseStackView.setOrientation(NSUserInterfaceLayoutOrientationVertical)
400 | lowerCaseStackView.setAlignment(NSLayoutAttributeCenterX)
401 | lowerCaseStackView.setSpacing(4)
402 | lowerCaseStackView.setTranslatesAutoresizingMaskIntoConstraints(false)
403 |
404 | var pushOnOffButtonUpperCase = NSButton.alloc().initWithFrame(NSMakeRect(0,0,72,32))
405 | pushOnOffButtonUpperCase.setButtonType(NSButtonTypeOnOff)
406 | pushOnOffButtonUpperCase.setBezelStyle(NSRoundedBezelStyle)
407 |
408 | let upperCaseAttributedString = NSMutableAttributedString.new().initWithString("Tt")
409 | let upperCaseRange = NSMakeRange(0,1)
410 | let upperCaseFont = getFontForKey_Value(38,1)
411 | upperCaseAttributedString.setAlignment_range(NSTextAlignmentCenter, NSMakeRange(0,2))
412 | let centerAlignUpperCase = NSMutableParagraphStyle.new().init().setAlignment(NSTextAlignmentCenter)
413 | upperCaseAttributedString.addAttribute_value_range(NSFontAttributeName, upperCaseFont, upperCaseRange)
414 | pushOnOffButtonUpperCase.setAttributedTitle(upperCaseAttributedString)
415 | pushOnOffButtonUpperCase.setState(NSOffState)
416 |
417 | pushOnOffButtonUpperCase.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
418 | pushOnOffButtonUpperCase,
419 | NSLayoutAttributeWidth,
420 | NSLayoutRelationEqual,
421 | nil,
422 | NSLayoutAttributeNotAnAttribute,
423 | 1.0,
424 | 60
425 | ))
426 |
427 | threadDictionary[pushOnOffButtonUpperCaseID] = pushOnOffButtonUpperCase
428 |
429 | let smallCapsUpperCaseTargetFunction = (sender) => {
430 | // console.log(sender.title() + ' toggle was clicked')
431 | //Small Caps Upper Case
432 | // ID: kUpperCaseType
433 | //
434 | // SELECTORS
435 | //
436 | // kDefaultUpperCaseSelector = 0
437 | // Use standard upper-case glyphs
438 | //
439 | // kUpperCaseSmallCapsSelector = 1
440 | // Display upper-case glyphs as small caps (used commonly with acronyms).
441 | //
442 | // kUpperCasePetiteCapsSelector = 2
443 | // Display upper-case glyphs as petite caps
444 | //
445 | if (sender.state() == 0) {
446 | //Need to set to default setting
447 | let settingsAttribute = getSettingsAttributeForKey_Value(kUpperCaseType, kDefaultUpperCaseSelector)
448 | updateFontFeatureSettingsAttribute(settingsAttribute)
449 | } else {
450 | let settingsAttribute = getSettingsAttributeForKey_Value(kUpperCaseType, kUpperCaseSmallCapsSelector)
451 | updateFontFeatureSettingsAttribute(settingsAttribute)
452 | }
453 | }
454 | pushOnOffButtonUpperCase.setCOSJSTargetFunction(sender => smallCapsUpperCaseTargetFunction(sender))
455 |
456 | var upperCaseLabel = createTextField({
457 | text: "Upper Case",
458 | frame: NSMakeRect(0,0,66,14),
459 | alignment: NSTextAlignmentCenter,
460 | fontSize: 11
461 | })
462 |
463 | var upperCaseStackView = NSStackView.stackViewWithViews([pushOnOffButtonUpperCase,upperCaseLabel])
464 | upperCaseStackView.setOrientation(NSUserInterfaceLayoutOrientationVertical)
465 | upperCaseStackView.setSpacing(4)
466 | upperCaseStackView.setTranslatesAutoresizingMaskIntoConstraints(false)
467 |
468 | var smallCapsButtonGroupStackView = NSStackView.stackViewWithViews([smallCapsExampleLabel,lowerCaseStackView,upperCaseStackView])
469 | smallCapsButtonGroupStackView.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
470 | smallCapsButtonGroupStackView.setAlignment(NSLayoutAttributeFirstBaseline)
471 | smallCapsButtonGroupStackView.setSpacing(4)
472 | smallCapsButtonGroupStackView.setTranslatesAutoresizingMaskIntoConstraints(false)
473 |
474 | var row4 = NSStackView.alloc().initWithFrame(NSMakeRect(0,0,mainViewWidth,36))
475 | row4.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
476 | row4.setAlignment(NSLayoutAttributeFirstBaseline)
477 | row4.setSpacing(columnSpacing)
478 | row4.setViews_inGravity([smallCapsLabel, smallCapsButtonGroupStackView], NSStackViewGravityLeading)
479 |
480 | // MARK: SETUP ROW 5
481 | var sfSymbolSizeLabel = createTextField({
482 | text: "SF Symbol Size:",
483 | frame: NSMakeRect(0,0,column1width,17),
484 | alignment: NSTextAlignmentRight
485 | })
486 |
487 | sfSymbolSizeLabel.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
488 | sfSymbolSizeLabel,
489 | NSLayoutAttributeWidth,
490 | NSLayoutRelationEqual,
491 | nil,
492 | NSLayoutAttributeNotAnAttribute,
493 | 1.0,
494 | column1width
495 | ))
496 |
497 | var sfSymbolSizePopupButton = NSPopUpButton.alloc().initWithFrame(NSMakeRect(0,0,150,25))
498 | sfSymbolSizePopupButton.addItemsWithTitles([
499 | 'Small',
500 | 'Medium',
501 | 'Large',
502 | 'Multiple'
503 | ])
504 | threadDictionary[sfSymbolSizePopupButtonID] = sfSymbolSizePopupButton
505 | sfSymbolSizePopupButton.itemWithTitle('Multiple').setHidden(true)
506 |
507 | let sfSymbolSizeTargetFunction = (sender) => {
508 | //console.log(sender.title() + ' dropdown button was selected')
509 | // sfSymbolSizeLabel
510 | // ID: kStylisticAlternativesType 35
511 | //
512 | // Selectors are very brittle. Need to figure out how to read StylisticAlts
513 | // from font so I'm not guessing what number the selectors are (they could change).
514 | // I should get their selectors progrmatically.
515 | //
516 | // kStylisticAltFifteenOnSelector
517 | // This is referring to Small Symbols or Glyphs for the SF Pro Text Font
518 | //
519 | // kStylisticAltSixteenOnSelector
520 | // This is referring to Large Symbols or Glyphs for the SF Pro Text Font
521 | //
522 | // Note:
523 | // There is no Medium Symbols checkbox for SF Pro Text
524 | if (sender.title() == 'Small') {
525 | let settingsAttributeSFSmall = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltFifteenOnSelector)
526 | let settingsAttributeSFLarge = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltSixteenOffSelector)
527 | let settingsAttributes = [settingsAttributeSFSmall,settingsAttributeSFLarge]
528 | settingsAttributes.forEach(settingsAttribute => {
529 | //console.log(settingsAttribute)
530 | updateFontFeatureSettingsAttribute(settingsAttribute)
531 | })
532 | } else if (sender.title() == 'Medium') {
533 | let settingsAttributeSFSmall = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltFifteenOffSelector)
534 | let settingsAttributeSFLarge = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltSixteenOffSelector)
535 | let settingsAttributes = [settingsAttributeSFSmall,settingsAttributeSFLarge]
536 | settingsAttributes.forEach(settingsAttribute => {
537 | //console.log(settingsAttribute)
538 | updateFontFeatureSettingsAttribute(settingsAttribute)
539 | })
540 | } else if (sender.title() == 'Large') {
541 | let settingsAttributeSFSmall = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltFifteenOffSelector)
542 | let settingsAttributeSFLarge = getSettingsAttributeForKey_Value(kStylisticAlternativesType, kStylisticAltSixteenOnSelector)
543 | let settingsAttributes = [settingsAttributeSFSmall,settingsAttributeSFLarge]
544 | settingsAttributes.forEach(settingsAttribute => {
545 | //console.log(settingsAttribute)
546 | updateFontFeatureSettingsAttribute(settingsAttribute)
547 | })
548 | } else {
549 | logWarning("Out of sfSymbolSizeDropdown bounds")
550 | }
551 | }
552 |
553 | sfSymbolSizePopupButton.setCOSJSTargetFunction(sender => sfSymbolSizeTargetFunction(sender))
554 |
555 | var row5 = NSStackView.alloc().initWithFrame(NSMakeRect(0,0,mainViewWidth,25))
556 | row5.setOrientation(NSUserInterfaceLayoutOrientationHorizontal)
557 | row5.setAlignment(NSLayoutAttributeFirstBaseline)
558 | row5.setSpacing(columnSpacing)
559 | row5.setViews_inGravity([sfSymbolSizeLabel,sfSymbolSizePopupButton],NSStackViewGravityLeading)
560 |
561 | threadDictionary[sfSymbolSizeRow] = row5
562 | row5.setHidden(true)
563 |
564 | // MARK: Combine rows together
565 | var mainContentView = NSStackView.stackViewWithViews([row1,row2,row3, row4, row5])
566 | mainContentView.setOrientation(NSUserInterfaceLayoutOrientationVertical)
567 | mainContentView.setAlignment(NSLayoutAttributeLeading)
568 | mainContentView.setSpacing(8)
569 | mainContentView.setTranslatesAutoresizingMaskIntoConstraints(false)
570 |
571 | panel.contentView().addSubview(mainContentView)
572 | panel.contentView().setFlipped(true)
573 | fitSubviewToView(mainContentView,panel.contentView(),[16.0,16.0,8.0,16.0])
574 |
575 | addVibrancyView(panel.contentView())
576 |
577 | threadDictionary[identifier] = panel;
578 |
579 | var closeButton = panel.standardWindowButton(NSWindowCloseButton)
580 | closeButton.setCOSJSTargetFunction(function(sender) {
581 | closePanel(panel, threadDictionary, identifier)
582 | })
583 | }
584 |
585 | function addVibrancyView(superview) {
586 | var vibrancy = NSView.alloc().initWithFrame(NSMakeRect(0, 0, panelWidth, panelHeight))
587 | vibrancy.setBackgroundColor(NSColor.colorWithRed_green_blue_alpha(0.0,0.0,0.0,0.6))
588 | vibrancy.setWantsLayer(true)
589 |
590 | superview.addSubview(vibrancy)
591 | fitSubviewToView(vibrancy,superview, [0.0,0.0,0.0,0.0])
592 |
593 | var fontWarning = createTextField({
594 | text: "Selected text doesn't contain any supported font features. Please try a different typeface.",
595 | frame: NSMakeRect(0,0,panelWidth,17),
596 | alignment: NSTextAlignmentCenter,
597 | fontSize: 12
598 | })
599 | fontWarning.setTextColor(NSColor.whiteColor())
600 |
601 | vibrancy.addSubview(fontWarning)
602 | fontWarning.setTranslatesAutoresizingMaskIntoConstraints(false)
603 | addEdgeConstraint(NSLayoutAttributeCenterX, fontWarning, vibrancy, 0)
604 | addEdgeConstraint(NSLayoutAttributeTop, fontWarning, vibrancy, 40.0)
605 | fontWarning.addConstraint(NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
606 | fontWarning,
607 | NSLayoutAttributeWidth,
608 | NSLayoutRelationEqual,
609 | nil,
610 | NSLayoutAttributeNotAnAttribute,
611 | 1.0,
612 | 260
613 | ))
614 |
615 | vibrancy.setHidden(true)
616 | let threadDictionary = NSThread.mainThread().threadDictionary()
617 | threadDictionary[vibrancyViewID] = vibrancy
618 | }
619 |
620 | function getBlurFilter() {
621 | let blurFilter = CIFilter.filterWithName("CIGaussianBlur")
622 | blurFilter.setDefaults()
623 | blurFilter.setValue_forKey(2, 'inputRadius')
624 | blurFilter.setName("blur")
625 |
626 | return blurFilter
627 | }
628 |
629 | function fitSubviewToView(subview, view, constants) {
630 | subview.setTranslatesAutoresizingMaskIntoConstraints(false)
631 |
632 | addEdgeConstraint(NSLayoutAttributeTop, subview, view, constants[0])
633 | addEdgeConstraint(NSLayoutAttributeTrailing, subview, view, constants[1])
634 | addEdgeConstraint(NSLayoutAttributeBottom, subview, view, constants[2])
635 | addEdgeConstraint(NSLayoutAttributeLeading, subview, view, constants[3])
636 | }
637 |
638 | function addEdgeConstraint(layoutAttribute, subview, view, constant) {
639 | view.addConstraint(
640 | NSLayoutConstraint.constraintWithItem_attribute_relatedBy_toItem_attribute_multiplier_constant(
641 | subview,
642 | layoutAttribute,
643 | NSLayoutRelationEqual,
644 | view,
645 | layoutAttribute,
646 | 1.0,
647 | constant
648 | )
649 | )
650 | }
651 |
652 | function createTextField({text, frame, alignment, fontSize = 13}) {
653 | const label = NSTextField.alloc().initWithFrame(frame)
654 | label.setStringValue(text)
655 | label.setAlignment(alignment)
656 | label.setFont(NSFont.systemFontOfSize(fontSize))
657 | label.setBezeled(false)
658 | label.setDrawsBackground(false)
659 | label.setEditable(false)
660 | label.setSelectable(false)
661 | return label
662 | }
663 |
664 | function updateFontFeatureSettingsAttribute(settingsAttribute) {
665 | var document = sketch.getSelectedDocument();
666 | var selectedLayers = document.selectedLayers.layers
667 | var textLayers = selectedLayers.filter(layer => layer.type == "Text")
668 |
669 | textLayers.forEach(textLayer => {
670 | let font = textLayer.sketchObject.font()
671 | let fontSize = font.pointSize()
672 | let fontFeatureSettings = font
673 | .fontDescriptor()
674 | .fontAttributes()[NSFontFeatureSettingsAttribute]
675 | let descriptor = font.fontDescriptor().fontDescriptorByAddingAttributes(settingsAttribute)
676 | let newFont = NSFont.fontWithDescriptor_size(descriptor,fontSize)
677 | textLayer.sketchObject.setFont(newFont)
678 |
679 | if (textLayer.sketchObject.isEditingText() == 1) {
680 | let textView = textLayer.sketchObject.editingDelegate().textView()
681 | let textStorage = textView.textStorage()
682 | let fonts = getFontsFromTextLayer(textLayer)
683 | fonts.forEach(fontWithRange => {
684 | let font = fontWithRange.font
685 | let range = fontWithRange.range
686 | let fontSize = font.pointSize()
687 |
688 | let descriptor = font
689 | .fontDescriptor()
690 | .fontDescriptorByAddingAttributes(settingsAttribute)
691 |
692 | let newFont = NSFont.fontWithDescriptor_size(descriptor,fontSize)
693 | let attrsDict = NSDictionary.dictionaryWithObject_forKey(newFont,NSFontAttributeName)
694 | textStorage.addAttributes_range(attrsDict,range)
695 | })
696 | textView.didChangeText()
697 | }
698 | })
699 | document.sketchObject.inspectorController().reload()
700 | }
701 |
702 | function getFontForKey_Value(key, value) {
703 | let defaultButtonFont = NSFont.boldSystemFontOfSize(13)
704 | let settingsAttribute = getSettingsAttributeForKey_Value(key, value)
705 | var fontFeatureSettings = defaultButtonFont.fontDescriptor().fontAttributes()[NSFontFeatureSettingsAttribute]
706 | const descriptor = defaultButtonFont.fontDescriptor().fontDescriptorByAddingAttributes(settingsAttribute)
707 | const newFont = NSFont.fontWithDescriptor_size(descriptor,13)
708 | return newFont
709 | }
710 |
711 | function getSettingsAttributeForKey_Value(key, value) {
712 | let settingsAttribute = {
713 | [NSFontFeatureSettingsAttribute]: [{
714 | [NSFontFeatureTypeIdentifierKey]: key,
715 | [NSFontFeatureSelectorIdentifierKey]: value
716 | }]
717 | }
718 |
719 | return settingsAttribute
720 | }
721 |
722 | function updateUI(useFullSelection = false) {
723 | var document = sketch.getSelectedDocument()
724 | var selectedLayers = document.selectedLayers.layers
725 | let threadDictionary = NSThread.mainThread().threadDictionary()
726 |
727 | let warning = threadDictionary[vibrancyViewID]
728 |
729 | if (selectedLayers == null) {
730 | disableUI(threadDictionary)
731 | if (!warning.isHidden()) {
732 | warning.layer().setBackgroundFilters([])
733 | warning.setHidden(true)
734 | }
735 | return
736 | }
737 |
738 | var textLayers = selectedLayers.filter(layer => layer.type == "Text")
739 | if (textLayers.length == 0) {
740 | disableUI(threadDictionary)
741 | if (!warning.isHidden()) {
742 | warning.layer().setBackgroundFilters([])
743 | warning.setHidden(true)
744 | }
745 | return
746 | }
747 |
748 | var textLayersFeatureSettings = []
749 |
750 | let fontSettingsObjects = []
751 | textLayers.forEach(textLayer => {
752 | let fonts = getFontsFromTextLayer(textLayer, useFullSelection)
753 | fonts.forEach(fontWithRange => {
754 | checkToShowSFSymbolOption(fontWithRange.font)
755 | let currentSettings = getSettingsForFont(fontWithRange.font)
756 | fontSettingsObjects.push(currentSettings)
757 | })
758 | })
759 |
760 |
761 | // Settings object looks like this
762 | //{
763 | // smallCapsLowerCase: true
764 | // smallCapsUpperCase: false
765 | // sfSymbolSize: 'medium'
766 | // numberCase: 'disabled'
767 | // verticalPosition: 'default'
768 | // numberSpacing: 'proportional'
769 | // }
770 |
771 | let updatedUISettings = {
772 | "smallCapsLowerCase": [],
773 | "smallCapsUpperCase": [],
774 | "sfSymbolSize": [],
775 | "numberCase": [],
776 | "verticalPosition": [],
777 | "numberSpacing": []
778 | }
779 | fontSettingsObjects.forEach(fontSetting => {
780 | Object.keys(fontSetting).forEach(key => {
781 | updatedUISettings[key].push(fontSetting[key])
782 | })
783 | })
784 |
785 | // Deduplicate settingsCollection to only have unique entries
786 | for (var property in updatedUISettings) {
787 | updatedUISettings[property] = updatedUISettings[property].filter(onlyUnique)
788 | }
789 |
790 | function onlyUnique(value,index,self) {
791 | return self.indexOf(value) === index
792 | }
793 |
794 | let showWarningMessage = true
795 | for (var setting in updatedUISettings) {
796 | if (updatedUISettings[setting].length > 1 ) {
797 | showWarningMessage = false
798 | break;
799 | }
800 |
801 | if (updatedUISettings[setting][0] !== "disabled") {
802 | showWarningMessage = false
803 | break;
804 | }
805 | }
806 | if ((showWarningMessage) && (warning.isHidden())) {
807 | warning.setHidden(false)
808 | warning.layer().setBackgroundFilters([getBlurFilter()])
809 | } else if ((!showWarningMessage) && (!warning.isHidden())) {
810 | warning.layer().setBackgroundFilters([])
811 | warning.setHidden(true)
812 | } else if ((showWarningMessage) && (!warning.isHidden())) {
813 | logWarning("Warning already being shown")
814 | } else {
815 | logWarning("Warning is already hidden")
816 | }
817 |
818 | //Update UI Panel with only one update (to prevent flickering)
819 | for (var uiSetting in updatedUISettings) {
820 | if (uiSetting == 'verticalPosition') {
821 |
822 | let verticalPositionPopupButton = threadDictionary[verticalPositionPopupButtonID]
823 | verticalPositionPopupButton.setEnabled(true)
824 |
825 | //Clear mixed state items before setting them
826 | clearVerticalPositionPopupButtonState()
827 |
828 | if (updatedUISettings[uiSetting].length > 1) {
829 | verticalPositionPopupButton.selectItemWithTitle('Multiple')
830 |
831 | updatedUISettings[uiSetting].forEach(verticalPositionSetting => {
832 | if (verticalPositionSetting == 'default') {
833 | verticalPositionPopupButton.itemWithTitle('Default Position').setState(NSControlStateValueMixed)
834 | } else if (verticalPositionSetting == 'superscript') {
835 | verticalPositionPopupButton.itemWithTitle('Superscript').setState(NSControlStateValueMixed)
836 | } else if (verticalPositionSetting == 'subscript') {
837 | verticalPositionPopupButton.itemWithTitle('Subscript').setState(NSControlStateValueMixed)
838 | } else if (verticalPositionSetting == 'ordinals') {
839 | verticalPositionPopupButton.itemWithTitle('Ordinals').setState(NSControlStateValueMixed)
840 | } else if (verticalPositionSetting == 'scientific inferiors') {
841 | verticalPositionPopupButton.itemWithTitle('Scientific Notation').setState(NSControlStateValueMixed)
842 | }
843 | })
844 | } else {
845 | if (updatedUISettings[uiSetting][0] == 'default') {
846 | // console.log('Setting UI: Vertical Position = Default Position')
847 | verticalPositionPopupButton.selectItemWithTitle('Default Position')
848 | verticalPositionPopupButton.itemWithTitle('Default Position').setState(NSControlStateValueOn)
849 | } else if (updatedUISettings[uiSetting][0] == 'superscript') {
850 | // console.log('Setting UI: Vertical Position = Superscript')
851 | verticalPositionPopupButton.selectItemWithTitle('Superscript')
852 | verticalPositionPopupButton.itemWithTitle('Superscript').setState(NSControlStateValueOn)
853 | } else if (updatedUISettings[uiSetting][0] == 'subscript') {
854 | // console.log('Setting UI: Vertical Position = Subscript')
855 | verticalPositionPopupButton.selectItemWithTitle('Subscript')
856 | verticalPositionPopupButton.itemWithTitle('Subscript').setState(NSControlStateValueOn)
857 | } else if (updatedUISettings[uiSetting][0] == 'ordinals') {
858 | // console.log('Setting UI: Vertical Position = Ordinals')
859 | verticalPositionPopupButton.selectItemWithTitle('Ordinals')
860 | verticalPositionPopupButton.itemWithTitle('Ordinals').setState(NSControlStateValueOn)
861 | } else if (updatedUISettings[uiSetting][0] == 'scientific inferiors') {
862 | // console.log('Setting UI: Vertical Position = Scientific Notation')
863 | verticalPositionPopupButton.selectItemWithTitle('Scientific Notation')
864 | verticalPositionPopupButton.itemWithTitle('Scientific Notation').setState(NSControlStateValueOn)
865 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
866 | verticalPositionPopupButton.setEnabled(false)
867 | } else {
868 | logWarning('BetterTypeTool: ERROR Attempting update panel state - Out of scope of verticalPosition options')
869 | }
870 | }
871 |
872 | } else if (uiSetting == 'numberSpacing') {
873 | let radioButtonProportional = threadDictionary[radioButtonProportionalID]
874 | let radioButtonMonospacedOrTabular = threadDictionary[radioButtonMonospacedOrTabularID]
875 | radioButtonProportional.setEnabled(true)
876 | radioButtonMonospacedOrTabular.setEnabled(true)
877 |
878 | if (updatedUISettings[uiSetting].length > 1) {
879 | radioButtonProportional.setState(NSOffState)
880 | radioButtonMonospacedOrTabular.setState(NSOffState)
881 | } else {
882 | if (updatedUISettings[uiSetting][0] == 'proportional') {
883 | // console.log('Setting UI: Number Spacing = Proportional')
884 | radioButtonProportional.setState(NSOnState)
885 | radioButtonMonospacedOrTabular.setState(NSOffState)
886 | } else if (updatedUISettings[uiSetting][0] == 'monospaced') {
887 | // console.log('Setting UI: Number Spacing == Monospaced/Tabular')
888 | radioButtonProportional.setState(NSOffState)
889 | radioButtonMonospacedOrTabular.setState(NSOnState)
890 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
891 | radioButtonProportional.setState(NSOffState)
892 | radioButtonMonospacedOrTabular.setState(NSOffState)
893 | radioButtonProportional.setEnabled(false)
894 | radioButtonMonospacedOrTabular.setEnabled(false)
895 | } else {
896 | logWarning('BetterTypeTool: ERROR Attempting update panel state - Out of scope of numberSpacing options')
897 | }
898 | }
899 |
900 | } else if (uiSetting == 'numberCase') {
901 | let radioButtonLiningFigures = threadDictionary[radioButtonLiningFiguresID]
902 | let radioButtonOldStyleFigures = threadDictionary[radioButtonOldStyleFiguresID]
903 | radioButtonLiningFigures.setEnabled(true)
904 | radioButtonOldStyleFigures.setEnabled(true)
905 |
906 | if (updatedUISettings[uiSetting].length > 1) {
907 | radioButtonLiningFigures.setState(NSOffState)
908 | radioButtonOldStyleFigures.setState(NSOffState)
909 | } else {
910 | if (updatedUISettings[uiSetting][0] == 'lining') {
911 | // console.log('Setting UI: Number Case = Lining figures')
912 | radioButtonLiningFigures.setState(NSOnState)
913 | radioButtonOldStyleFigures.setState(NSOffState)
914 | } else if (updatedUISettings[uiSetting][0] == 'oldStyle') {
915 | // console.log('Setting UI: Number Case = Old-style figures')
916 | radioButtonLiningFigures.setState(NSOffState)
917 | radioButtonOldStyleFigures.setState(NSOnState)
918 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
919 | radioButtonLiningFigures.setState(NSOffState)
920 | radioButtonOldStyleFigures.setState(NSOffState)
921 | radioButtonLiningFigures.setEnabled(false)
922 | radioButtonOldStyleFigures.setEnabled(false)
923 | } else {
924 | logWarning('BetterTypeTool: ERROR Attempting to update panel state - Out of scope of numberCase options')
925 | }
926 | }
927 |
928 | } else if (uiSetting == 'smallCapsUpperCase') {
929 | let pushOnOffButtonUpperCase = threadDictionary[pushOnOffButtonUpperCaseID]
930 | pushOnOffButtonUpperCase.setEnabled(true)
931 |
932 | if (updatedUISettings[uiSetting].length > 1) {
933 | pushOnOffButtonUpperCase.setState(NSOffState)
934 | } else {
935 | if (updatedUISettings[uiSetting][0] == false) {
936 | // console.log('Setting UI: Small Caps Upper Case = Off')
937 | pushOnOffButtonUpperCase.setState(NSOffState)
938 | } else if (updatedUISettings[uiSetting][0] == true) {
939 | // console.log('Setting UI: Small Caps Upper Case = On')
940 | pushOnOffButtonUpperCase.setState(NSOnState)
941 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
942 | pushOnOffButtonUpperCase.setState(NSOffState)
943 | pushOnOffButtonUpperCase.setEnabled(false)
944 | } else {
945 | logWarning('BetterTypeTool: ERROR Attempting to update panel state - Out of scope of smallCapsUpperCase options')
946 | }
947 | }
948 |
949 | } else if (uiSetting == 'smallCapsLowerCase') {
950 | let pushOnOffButtonLowerCase = threadDictionary[pushOnOffButtonLowerCaseID]
951 | pushOnOffButtonLowerCase.setEnabled(true)
952 |
953 | if (updatedUISettings[uiSetting].length > 1) {
954 | pushOnOffButtonLowerCase.setState(NSOffState)
955 | } else {
956 | if (updatedUISettings[uiSetting][0] == false) {
957 | // console.log('Setting UI: Small Caps Lower Case = Off')
958 | pushOnOffButtonLowerCase.setState(NSOffState)
959 | } else if (updatedUISettings[uiSetting][0] == true) {
960 | // console.log('Setting UI: Small Caps Lower Case = On')
961 | pushOnOffButtonLowerCase.setState(NSOnState)
962 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
963 | pushOnOffButtonLowerCase.setState(NSOffState)
964 | pushOnOffButtonLowerCase.setEnabled(false)
965 | } else {
966 | logWarning('BetterTypeTool: ERROR Attempting to update panel state - Out of scope of smallCapsLowerCase options')
967 | }
968 | }
969 |
970 | } else if (uiSetting == 'sfSymbolSize') {
971 | let sfSymbolSizePopupButton = threadDictionary[sfSymbolSizePopupButtonID]
972 | sfSymbolSizePopupButton.setEnabled(true)
973 |
974 | clearSFSymbolSizePopupButton()
975 |
976 | if (updatedUISettings[uiSetting].length > 1) {
977 | sfSymbolSizePopupButton.selectItemWithTitle('Multiple')
978 |
979 | updatedUISettings[uiSetting].forEach(sfSymbolSizeSetting => {
980 | if (sfSymbolSizeSetting == 'small') {
981 | sfSymbolSizePopupButton.itemWithTitle('Small').setState(NSControlStateValueMixed)
982 | } else if (sfSymbolSizeSetting == 'medium') {
983 | sfSymbolSizePopupButton.itemWithTitle('Medium').setState(NSControlStateValueMixed)
984 | } else if (sfSymbolSizeSetting == 'large') {
985 | sfSymbolSizePopupButton.itemWithTitle('Large').setState(NSControlStateValueMixed)
986 | }
987 | })
988 | } else {
989 | if (updatedUISettings[uiSetting][0] == 'small') {
990 | sfSymbolSizePopupButton.selectItemWithTitle('Small')
991 | sfSymbolSizePopupButton.itemWithTitle('Small').setState(NSControlStateValueOn)
992 | } else if (updatedUISettings[uiSetting][0] == 'medium') {
993 | sfSymbolSizePopupButton.selectItemWithTitle('Medium')
994 | sfSymbolSizePopupButton.itemWithTitle('Medium').setState(NSControlStateValueOn)
995 | } else if (updatedUISettings[uiSetting][0] == 'large'){
996 | sfSymbolSizePopupButton.selectItemWithTitle('Large')
997 | sfSymbolSizePopupButton.itemWithTitle('Large').setState(NSControlStateValueOn)
998 | } else if (updatedUISettings[uiSetting][0] == 'disabled') {
999 | sfSymbolSizePopupButton.setEnabled(false)
1000 | } else {
1001 | logWarning('BetterTypeTool: ERROR Attempting update panel state - Out of scope of sfSymbolSize options')
1002 | }
1003 | }
1004 | } else {
1005 | logWarning('Error: Unhandled uiSetting Property')
1006 | logWarning(updatedUISettings[uiSetting])
1007 | }
1008 | }
1009 | }
1010 |
1011 | function disableUI(threadDictionary, optionsToDisableArray = ['all']) {
1012 | // optionsToDisable is an array that can include "all", "verticalPosition", "numberSpacing", "numberCase", "smallCapsUppercase", "smallCapsLowerCase", "sfSymbolSize"
1013 | //TODO: Maybe reset the state to the deault params when UI is disabled
1014 |
1015 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('verticalPosition')) {
1016 | let verticalPositionPopupButton = threadDictionary[verticalPositionPopupButtonID]
1017 | verticalPositionPopupButton.setEnabled(false)
1018 | }
1019 |
1020 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('numberSpacing')) {
1021 | let radioButtonProportional = threadDictionary[radioButtonProportionalID]
1022 | let radioButtonMonospacedOrTabular = threadDictionary[radioButtonMonospacedOrTabularID]
1023 | radioButtonProportional.setEnabled(false)
1024 | radioButtonMonospacedOrTabular.setEnabled(false)
1025 | }
1026 |
1027 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('numberCase')) {
1028 | let radioButtonLiningFigures = threadDictionary[radioButtonLiningFiguresID]
1029 | let radioButtonOldStyleFigures = threadDictionary[radioButtonOldStyleFiguresID]
1030 | radioButtonLiningFigures.setEnabled(false)
1031 | radioButtonOldStyleFigures.setEnabled(false)
1032 | }
1033 |
1034 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('smallCapsUpperCase')) {
1035 | let pushOnOffButtonUpperCase = threadDictionary[pushOnOffButtonUpperCaseID]
1036 | pushOnOffButtonUpperCase.setEnabled(false)
1037 | }
1038 |
1039 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('smallCapsLowerCase')) {
1040 | let pushOnOffButtonLowerCase = threadDictionary[pushOnOffButtonLowerCaseID]
1041 | pushOnOffButtonLowerCase.setEnabled(false)
1042 | }
1043 |
1044 | if (optionsToDisableArray.includes('all') || optionsToDisableArray.includes('sfSymbolSize')) {
1045 | let sfSymbolSizePopupButton = threadDictionary[sfSymbolSizePopupButtonID]
1046 | sfSymbolSizePopupButton.setEnabled(false)
1047 | }
1048 | }
1049 |
1050 | function closePanel(panel, threadDictionary, threadIdentifier) {
1051 | panel.close()
1052 |
1053 | try {
1054 | setupFramework()
1055 | let main = HSMain.alloc().init()
1056 | // Stop text selection listening
1057 | main.stopObservingTextViewSelectionChanges()
1058 | } catch(error) {
1059 | console.error(error);
1060 | }
1061 |
1062 | // Remove the reference to the panel
1063 | threadDictionary.removeObjectForKey(threadIdentifier)
1064 | threadDictionary.panelOpen = false
1065 |
1066 | // Stop this script
1067 | COScript.currentCOScript().setShouldKeepAround_(false)
1068 | }
1069 |
1070 | //Start with Default Settings
1071 | function getDefaultUISettings() {
1072 | return {
1073 | 'verticalPosition': 'default', // 'default', 'superscript', 'subscript', 'ordinals', 'scientific inferiors', 'disabled'
1074 | 'numberSpacing': 'proportional', // 'proportional', 'monospaced', 'disabled'
1075 | 'numberCase': 'lining', // 'lining', 'oldStyle', 'disabled'
1076 | 'smallCapsLowerCase': false, // bool, 'disabled'
1077 | 'smallCapsUpperCase': false, // bool, 'disabled'
1078 | 'sfSymbolSize': 'medium' // 'small', 'medium', 'large', 'disabled'
1079 | // If updating this list remember to update the default updatedUISettings
1080 | // TODO: Refactor so that the Default UI settings is in one place.
1081 | }
1082 | }
1083 |
1084 | // TODO: Make more generic to support both popupbuttons
1085 | function clearVerticalPositionPopupButtonState() {
1086 | let threadDictionary = NSThread.mainThread().threadDictionary()
1087 | let verticalPositionPopupButton = threadDictionary[verticalPositionPopupButtonID]
1088 | verticalPositionPopupButton.itemWithTitle('Default Position').setState(NSControlStateValueOff)
1089 | verticalPositionPopupButton.itemWithTitle('Superscript').setState(NSControlStateValueOff)
1090 | verticalPositionPopupButton.itemWithTitle('Subscript').setState(NSControlStateValueOff)
1091 | verticalPositionPopupButton.itemWithTitle('Ordinals').setState(NSControlStateValueOff)
1092 | verticalPositionPopupButton.itemWithTitle('Scientific Notation').setState(NSControlStateValueOff)
1093 | }
1094 |
1095 | function clearSFSymbolSizePopupButton() {
1096 | let threadDictionary = NSThread.mainThread().threadDictionary()
1097 | let sfSymbolSizePopupButton = threadDictionary[sfSymbolSizePopupButtonID]
1098 | sfSymbolSizePopupButton.itemWithTitle('Small').setState(NSControlStateValueOff)
1099 | sfSymbolSizePopupButton.itemWithTitle('Medium').setState(NSControlStateValueOff)
1100 | sfSymbolSizePopupButton.itemWithTitle('Large').setState(NSControlStateValueOff)
1101 | }
1102 |
1103 | function logWarning(warning) {
1104 | //console.log(warning)
1105 | }
1106 |
1107 | function getFontsFromTextLayer(textLayer, useFullSelection = false) {
1108 | let msTextLayer = textLayer.sketchObject
1109 | let effectiveRange = MOPointer.alloc().init()
1110 |
1111 | // if editing text then need to use the textStorage rather than the attrString
1112 | let mutableAttrString
1113 | let selectedRange
1114 | let textView
1115 |
1116 | // infer editing by checking if textView exists
1117 | let textViewExists = true
1118 | try {
1119 | textView = msTextLayer.editingDelegate().textView()
1120 | } catch {
1121 | textViewExists = false
1122 | }
1123 |
1124 | if (textViewExists) {
1125 | let textStorage = textView.textStorage()
1126 | selectedRange = textView.selectedRange()
1127 | mutableAttrString = textStorage
1128 |
1129 | // need this because selected range is 0 when going from editing state to selected frame state
1130 | if (useFullSelection) {
1131 | selectedRange = NSMakeRange(0,textLayer.text.length)
1132 | }
1133 | } else {
1134 | let attributedString = msTextLayer.attributedStringValue()
1135 | selectedRange = NSMakeRange(0,textLayer.text.length)
1136 | mutableAttrString = attributedString
1137 | }
1138 |
1139 | let fonts = []
1140 |
1141 | if (selectedRange.length == 0) {
1142 | let font = mutableAttrString.attribute_atIndex_longestEffectiveRange_inRange(
1143 | NSFontAttributeName,
1144 | selectedRange.location,
1145 | effectiveRange,
1146 | selectedRange
1147 | )
1148 | fonts.push({"font": font, "range": effectiveRange.value()})
1149 | }
1150 |
1151 | while (selectedRange.length > 0) {
1152 | let font = mutableAttrString.attribute_atIndex_longestEffectiveRange_inRange(
1153 | NSFontAttributeName,
1154 | selectedRange.location,
1155 | effectiveRange,
1156 | selectedRange
1157 | )
1158 | selectedRange = NSMakeRange(
1159 | NSMaxRange(effectiveRange.value()),
1160 | NSMaxRange(selectedRange) - NSMaxRange(effectiveRange.value())
1161 | )
1162 |
1163 | fonts.push({"font": font, "range": effectiveRange.value()})
1164 | }
1165 | return fonts
1166 | }
1167 |
1168 | function checkToShowSFSymbolOption(font) {
1169 | let familyName = font.familyName()
1170 | let showSFSymbolOption = false
1171 |
1172 | let supportedFontFamilies = [
1173 | "SF Pro Text",
1174 | "SF Pro Rounded",
1175 | "SF Pro Display",
1176 | "SF Compact Text",
1177 | "SF Compact Rounded",
1178 | "SF Compact Display"
1179 | ]
1180 |
1181 | let threadDictionary = NSThread.mainThread().threadDictionary()
1182 | let row5 = threadDictionary[sfSymbolSizeRow]
1183 | let panel = threadDictionary[threadIdentifier]
1184 |
1185 | let panelX = panel.frame().origin.x
1186 | let panelY = panel.frame().origin.y
1187 | let panelWidth = panel.frame().size.height
1188 | let panelHeight = panel.frame().size.height
1189 | supportedFontFamilies.forEach(fontFamily => {
1190 | if (familyName == fontFamily) {
1191 | showSFSymbolOption = true
1192 | // TODO Don't hard code these values
1193 | if (panelHeight != 235) {
1194 | panel.setFrame_display_animate(NSMakeRect(panelX, panelY - 25, 312, 210 + 25), true, true)
1195 | row5.setHidden(false)
1196 | }
1197 | return;
1198 | }
1199 | })
1200 |
1201 | if (!showSFSymbolOption) {
1202 | // hide UI
1203 | if (panelHeight != 210) {
1204 | row5.setHidden(true)
1205 | panel.setFrame_display_animate(NSMakeRect(panelX, panelY + 25, 312, 210), true, true)
1206 | }
1207 | }
1208 | }
1209 |
1210 | function getOptionsToDisableFromFont(font) {
1211 | framework('CoreText')
1212 | let optionsToDisableForFont = []
1213 |
1214 | try {
1215 | setupFramework()
1216 | let main = HSMain.alloc().init()
1217 |
1218 | const coreTextFont = CTFontCreateWithName(font.fontName(), font.pointSize(), null)
1219 | const features = CTFontCopyFeatures(coreTextFont)
1220 | let featuresArray = main.bridgeArray(features)
1221 |
1222 | optionsToDisableForFont = getOptionsToDisableFromFeaturesArray(featuresArray)
1223 | } catch (e) {
1224 | console.error(e)
1225 | }
1226 |
1227 | let familyName = font.familyName().toLowerCase().trim()
1228 | let supportedFontFamilies = [
1229 | "sf pro text",
1230 | "sf pro rounded",
1231 | "sf pro display",
1232 | "sf compact text",
1233 | "sf compact rounded",
1234 | "sf compact display"
1235 | ]
1236 | if (!supportedFontFamilies.includes(familyName)) {
1237 | optionsToDisableForFont.push('sfSymbolSize')
1238 | }
1239 |
1240 | return optionsToDisableForFont
1241 | }
1242 |
1243 | function getOptionsToDisableFromFeaturesArray(featuresArray) {
1244 | let optionsToDisableForFont = []
1245 |
1246 | if(!featuresArray) {
1247 | optionsToDisableForFont.push('verticalPosition','numberSpacing','numberCase', 'smallCapsLowerCase', 'smallCapsUpperCase')
1248 | } else {
1249 | let featureIDs = []
1250 | featuresArray.forEach(feature => {
1251 | featureIDs.push(Number(feature["CTFeatureTypeIdentifier"]))
1252 | })
1253 |
1254 | if (!featureIDs.includes(10)) {
1255 | // Vertical Position
1256 | optionsToDisableForFont.push('verticalPosition')
1257 | }
1258 | if (!featureIDs.includes(6)) {
1259 | // Number Spacing
1260 | optionsToDisableForFont.push('numberSpacing')
1261 | }
1262 | if (!featureIDs.includes(21)) {
1263 | // Number Case
1264 | optionsToDisableForFont.push('numberCase')
1265 | }
1266 | if (!featureIDs.includes(37)) {
1267 | // Small Caps Lower Case
1268 | optionsToDisableForFont.push('smallCapsLowerCase')
1269 | }
1270 | if (!featureIDs.includes(38)) {
1271 | // Small Caps Upper Case
1272 | optionsToDisableForFont.push('smallCapsUpperCase')
1273 | }
1274 | }
1275 | return optionsToDisableForFont
1276 | }
1277 |
1278 | function getSettingsForFont(font) {
1279 | let currentOptions = getDefaultUISettings()
1280 | let disableOptions = getOptionsToDisableFromFont(font)
1281 |
1282 | disableOptions.forEach(option => {
1283 | switch (option) {
1284 | case "verticalPosition":
1285 | currentOptions.verticalPosition = "disabled"
1286 | break;
1287 | case "numberSpacing":
1288 | currentOptions.numberSpacing = "disabled"
1289 | break;
1290 | case "numberCase":
1291 | currentOptions.numberCase = "disabled"
1292 | break;
1293 | case "smallCapsLowerCase":
1294 | currentOptions.smallCapsLowerCase = "disabled"
1295 | break;
1296 | case "smallCapsUpperCase":
1297 | currentOptions.smallCapsUpperCase = "disabled"
1298 | break;
1299 | case "sfSymbolSize":
1300 | currentOptions.sfSymbolSize = "disabled"
1301 | break;
1302 | }
1303 | })
1304 |
1305 | let fontFeatureSettings = font.fontDescriptor().fontAttributes()[NSFontFeatureSettingsAttribute]
1306 | if (fontFeatureSettings) {
1307 | fontFeatureSettings.forEach(featureSetting => {
1308 | const featureTypeIdentifierKey = Number(featureSetting[NSFontFeatureTypeIdentifierKey])
1309 | const featureSelectorIdentifierKey = Number(featureSetting[NSFontFeatureSelectorIdentifierKey])
1310 |
1311 | switch (featureTypeIdentifierKey) {
1312 | // kVerticalPosition
1313 | case 10:
1314 | switch (featureSelectorIdentifierKey) {
1315 | // kNormalPositionSelector
1316 | case 0:
1317 | currentOptions.verticalPosition = 'default'
1318 | break;
1319 |
1320 | // kSuperiorsSelector
1321 | case 1:
1322 | currentOptions.verticalPosition = 'superscript'
1323 | break;
1324 |
1325 | // kInferiorsSelector
1326 | case 2:
1327 | currentOptions.verticalPosition = 'subscript'
1328 | break;
1329 |
1330 | // kOrdinalsSelector
1331 | case 3:
1332 | currentOptions.verticalPosition = 'ordinals'
1333 | break;
1334 |
1335 | // kScientificInferiorsSelector
1336 | case 4:
1337 | currentOptions.verticalPosition = 'scientific inferiors'
1338 | break;
1339 |
1340 | default:
1341 | logWarning("BetterTypeTool: Unknown Feature for Vertical Position")
1342 | break;
1343 | }
1344 | break;
1345 |
1346 | // kNumberSpacing
1347 | case 6:
1348 | switch(featureSelectorIdentifierKey) {
1349 | // kMonospacedNumbersSelector
1350 | case 0:
1351 | currentOptions.numberSpacing = 'monospaced'
1352 | break;
1353 |
1354 | // kProportionalNumbersSelector
1355 | case 1:
1356 | currentOptions.numberSpacing = 'proportional'
1357 | break;
1358 |
1359 | // kThirdWidthNumbersSelector
1360 | case 2:
1361 | logWarning("BetterTypeTool: Unsupported Number Spacing Feature - Third-width Numerals (Thin numerals)")
1362 | break;
1363 |
1364 | // kQuarterWidthNumbersSelector
1365 | case 3:
1366 | logWarning("BetterTypeTool: Unsupported Number Spacing Feature - Quarter-width Numerals (Very Yhin Numerals")
1367 | break;
1368 |
1369 | default:
1370 | logWarning("BetterTypeTool: Unknown feature for Number Spacing")
1371 | break;
1372 | }
1373 | break;
1374 |
1375 | // kNumberCaseType
1376 | case 21:
1377 | switch(featureSelectorIdentifierKey) {
1378 | // kLowerCaseNumbersSelector
1379 | case 0:
1380 | currentOptions.numberCase = 'oldStyle'
1381 | break;
1382 |
1383 | // kUpperCaseNumbersSelector
1384 | case 1:
1385 | currentOptions.numberCase = 'lining'
1386 | break;
1387 |
1388 | default:
1389 | logWarning("BetterTypeTool: Unknown feature for Number Case")
1390 | break;
1391 | }
1392 | break;
1393 |
1394 | // kLowerCase
1395 | case 37:
1396 | switch(featureSelectorIdentifierKey) {
1397 | // kDefaultLowerCaseSelector (aka OFF)
1398 | case 0:
1399 | currentOptions.smallCapsLowerCase = false
1400 | break;
1401 |
1402 | // kLowerCaseSmallCapsSelector
1403 | case 1:
1404 | currentOptions.smallCapsLowerCase = true
1405 | break;
1406 |
1407 | // kLowerCasePetiteCapsSelector
1408 | case 2:
1409 | logWarning("Unsupported Lower Case Small Caps Feature - Lower Case Petite Caps")
1410 | break;
1411 |
1412 | default:
1413 | logWarning("BetterTypeTool: Unknown feature for Lower Case Small Caps")
1414 | break;
1415 | }
1416 | break;
1417 |
1418 | // kUpperCase
1419 | case 38:
1420 | switch(featureSelectorIdentifierKey) {
1421 | // kDefaultUpperCaseSelector (aka OFF)
1422 | case 0:
1423 | currentOptions.smallCapsUpperCase = false
1424 | break;
1425 |
1426 | // kUpperCaseSmallCapsSelector
1427 | case 1:
1428 | currentOptions.smallCapsUpperCase = true
1429 | break;
1430 |
1431 | // kUpperCasePetiteCapsSelector
1432 | case 2:
1433 | logWarning("Unsupported Upper Case Small Caps Feature - Upper Case Petite Caps")
1434 | break;
1435 |
1436 | default:
1437 | logWarning("BetterTypeTool: Unknown feature for Upper Case Small Caps")
1438 | break;
1439 | }
1440 | break;
1441 |
1442 |
1443 | // kStylisticAlternatives
1444 | case 35:
1445 | switch(featureSelectorIdentifierKey) {
1446 | // kStylisticAltFifteenOnSelector
1447 | case 30:
1448 | currentOptions.sfSymbolSize = 'small'
1449 | break;
1450 |
1451 | // kStylisticAltFifteenOffSelector
1452 | case 31:
1453 | logWarning("WARNING: Unhandled Attempt to Set 15th Stylistic Alternative off")
1454 | break;
1455 |
1456 | // kStylisticAltSixteenOnSelector
1457 | case 32:
1458 | currentOptions.sfSymbolSize = 'large'
1459 | break;
1460 |
1461 | // kStylisticAltSixteenOffSelector
1462 | case 33:
1463 | logWarning("WARNING: Unhandled Attempt to Set 16th Stylistic Alternative off")
1464 | break;
1465 | }
1466 | break;
1467 | }
1468 | })
1469 | }
1470 |
1471 | return currentOptions
1472 | }
1473 |
--------------------------------------------------------------------------------