├── .appcast.xml ├── .gitignore ├── Autocomplete.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDETemplateMacros.plist │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── Autocomplete.xcscheme ├── Autocomplete ├── Autocomplete.h ├── Info.plist └── Sources │ ├── Categories │ ├── NSColor+Autocomplete.h │ ├── NSColor+Autocomplete.m │ ├── NSView+Autocomplete.h │ └── NSView+Autocomplete.m │ ├── Controllers │ ├── ZPLEmojiController.h │ ├── ZPLEmojiController.m │ ├── ZPLSuggestionController.h │ ├── ZPLSuggestionController.m │ ├── ZPLSuggestionWindowController.h │ └── ZPLSuggestionWindowController.m │ ├── Models │ ├── Sketch │ │ └── ZPLTheme.h │ ├── ZPLEmoji.h │ ├── ZPLEmoji.m │ ├── ZPLSuggestion.h │ └── ZPLSuggestion.m │ ├── Views │ ├── ZPLFocusTableView.h │ ├── ZPLFocusTableView.m │ ├── ZPLRowView.h │ ├── ZPLRowView.m │ ├── ZPLSuggestionCell.h │ └── ZPLSuggestionCell.m │ ├── ZPLAutocompletePluginController.h │ └── ZPLAutocompletePluginController.m ├── LICENSE ├── README.md ├── fastlane ├── Fastfile └── README.md ├── img ├── demo.gif ├── iconTransparent.png └── logo.svg ├── plugin ├── img │ ├── icon.png │ ├── iconRunner.png │ └── iconRunnerDark.png ├── manifest.json └── src │ └── index.js └── pull_request_template.md /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Emoji Autocomplete Sketch Plugin 5 | Type “:” followed by the first few letters to autocomplete emojis. 6 | en 7 | 8 | Version 1.3 9 | 10 | 12 |
  • You can now add emojis from Unicode 12.0, 13.0, and 14.0.
  • 13 |
  • Make plugin compatible with Apple M1.
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 | 20 | Version 1.2 21 | 22 | 24 |
  • More emojis! You can now add emojis from Unicode 10.0 and 11.0.
  • 25 |
  • Suggestion window now respects the visible screen boundaries on both axis.getting out of bounds on Y axes.
  • 26 |
  • Check emoji tags when finding suggestions.
  • 27 | 28 | ]]> 29 |
    30 | 31 |
    32 | 33 | Version 1.1 34 | 35 | 37 |
  • Fixed text overrides emoji autocomplete not working on Sketch 52.
  • 38 |
  • Added dark mode icon for Runner.
  • 39 |
  • Fixed suggestion window getting out of bounds on X axis when editing text overrides.
  • 40 | 41 | ]]> 42 |
    43 | 44 |
    45 | 46 | Version 1.0 47 | 48 | 50 |
  • Added dark mode support, along with Sketch 52.
  • 51 | 52 | ]]> 53 |
    54 | 55 |
    56 | 57 | Version 0.2 58 | 59 | 61 |
  • You can now autocomplete emojis while editing overrides and layer names.
  • 62 |
  • Added icon for Runner.
  • 63 | 64 | ]]> 65 |
    66 | 67 |
    68 | 69 | Version 0.1 70 | 71 | 72 |
    73 |
    74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata 3 | build 4 | fastlane/report.xml 5 | Autocomplete/Resources/Emojis.plist 6 | -------------------------------------------------------------------------------- /Autocomplete.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3435B73920E6C0C100A04DBC /* Autocomplete.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B73720E6C0C100A04DBC /* Autocomplete.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 3435B74120E6C14D00A04DBC /* ZPLAutocompletePluginController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B73F20E6C14D00A04DBC /* ZPLAutocompletePluginController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 3435B74220E6C14D00A04DBC /* ZPLAutocompletePluginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B74020E6C14D00A04DBC /* ZPLAutocompletePluginController.m */; }; 13 | 3435B74620E6C48E00A04DBC /* Emojis.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3435B74520E6C48E00A04DBC /* Emojis.plist */; }; 14 | 3435B74A20E6C51F00A04DBC /* ZPLEmojiController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B74820E6C51F00A04DBC /* ZPLEmojiController.h */; }; 15 | 3435B74B20E6C51F00A04DBC /* ZPLEmojiController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B74920E6C51F00A04DBC /* ZPLEmojiController.m */; }; 16 | 3435B75320E6C57600A04DBC /* ZPLEmoji.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B75120E6C57600A04DBC /* ZPLEmoji.h */; }; 17 | 3435B75420E6C57600A04DBC /* ZPLEmoji.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B75220E6C57600A04DBC /* ZPLEmoji.m */; }; 18 | 3435B75820E6C64C00A04DBC /* ZPLSuggestionController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B75620E6C64C00A04DBC /* ZPLSuggestionController.h */; }; 19 | 3435B75920E6C64C00A04DBC /* ZPLSuggestionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B75720E6C64C00A04DBC /* ZPLSuggestionController.m */; }; 20 | 3435B75C20E6C6B500A04DBC /* ZPLSuggestion.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B75A20E6C6B500A04DBC /* ZPLSuggestion.h */; }; 21 | 3435B75D20E6C6B500A04DBC /* ZPLSuggestion.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B75B20E6C6B500A04DBC /* ZPLSuggestion.m */; }; 22 | 3435B76020E6C76100A04DBC /* ZPLSuggestionCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B75E20E6C76100A04DBC /* ZPLSuggestionCell.h */; }; 23 | 3435B76120E6C76100A04DBC /* ZPLSuggestionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B75F20E6C76100A04DBC /* ZPLSuggestionCell.m */; }; 24 | 3435B76520E6C7F800A04DBC /* ZPLFocusTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B76320E6C7F800A04DBC /* ZPLFocusTableView.h */; }; 25 | 3435B76620E6C7F800A04DBC /* ZPLFocusTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B76420E6C7F800A04DBC /* ZPLFocusTableView.m */; }; 26 | 3435B76920E6C83800A04DBC /* ZPLSuggestionWindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3435B76720E6C83800A04DBC /* ZPLSuggestionWindowController.h */; }; 27 | 3435B76A20E6C83800A04DBC /* ZPLSuggestionWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3435B76820E6C83800A04DBC /* ZPLSuggestionWindowController.m */; }; 28 | 343DDD0920F51B450087E1CB /* NSView+Autocomplete.h in Headers */ = {isa = PBXBuildFile; fileRef = 343DDD0720F51B450087E1CB /* NSView+Autocomplete.h */; }; 29 | 343DDD0A20F51B450087E1CB /* NSView+Autocomplete.m in Sources */ = {isa = PBXBuildFile; fileRef = 343DDD0820F51B450087E1CB /* NSView+Autocomplete.m */; }; 30 | C0840FD220F37E1D00AEAE81 /* ZPLRowView.h in Headers */ = {isa = PBXBuildFile; fileRef = C0840FD020F37E1D00AEAE81 /* ZPLRowView.h */; }; 31 | C0840FD320F37E1D00AEAE81 /* ZPLRowView.m in Sources */ = {isa = PBXBuildFile; fileRef = C0840FD120F37E1D00AEAE81 /* ZPLRowView.m */; }; 32 | C0840FD720F3B46400AEAE81 /* NSColor+Autocomplete.h in Headers */ = {isa = PBXBuildFile; fileRef = C0840FD520F3B46400AEAE81 /* NSColor+Autocomplete.h */; }; 33 | C0840FD820F3B46400AEAE81 /* NSColor+Autocomplete.m in Sources */ = {isa = PBXBuildFile; fileRef = C0840FD620F3B46400AEAE81 /* NSColor+Autocomplete.m */; }; 34 | C0C13FDB21699E2800E85491 /* ZPLTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = C0C13FDA21699E2800E85491 /* ZPLTheme.h */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 3435B73420E6C0C100A04DBC /* Autocomplete.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Autocomplete.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 3435B73720E6C0C100A04DBC /* Autocomplete.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Autocomplete.h; sourceTree = ""; }; 40 | 3435B73820E6C0C100A04DBC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 3435B73F20E6C14D00A04DBC /* ZPLAutocompletePluginController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLAutocompletePluginController.h; sourceTree = ""; }; 42 | 3435B74020E6C14D00A04DBC /* ZPLAutocompletePluginController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLAutocompletePluginController.m; sourceTree = ""; }; 43 | 3435B74520E6C48E00A04DBC /* Emojis.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Emojis.plist; sourceTree = ""; }; 44 | 3435B74820E6C51F00A04DBC /* ZPLEmojiController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLEmojiController.h; sourceTree = ""; }; 45 | 3435B74920E6C51F00A04DBC /* ZPLEmojiController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLEmojiController.m; sourceTree = ""; }; 46 | 3435B75120E6C57600A04DBC /* ZPLEmoji.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLEmoji.h; sourceTree = ""; }; 47 | 3435B75220E6C57600A04DBC /* ZPLEmoji.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLEmoji.m; sourceTree = ""; }; 48 | 3435B75620E6C64C00A04DBC /* ZPLSuggestionController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLSuggestionController.h; sourceTree = ""; }; 49 | 3435B75720E6C64C00A04DBC /* ZPLSuggestionController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLSuggestionController.m; sourceTree = ""; }; 50 | 3435B75A20E6C6B500A04DBC /* ZPLSuggestion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLSuggestion.h; sourceTree = ""; }; 51 | 3435B75B20E6C6B500A04DBC /* ZPLSuggestion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLSuggestion.m; sourceTree = ""; }; 52 | 3435B75E20E6C76100A04DBC /* ZPLSuggestionCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLSuggestionCell.h; sourceTree = ""; }; 53 | 3435B75F20E6C76100A04DBC /* ZPLSuggestionCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLSuggestionCell.m; sourceTree = ""; }; 54 | 3435B76320E6C7F800A04DBC /* ZPLFocusTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLFocusTableView.h; sourceTree = ""; }; 55 | 3435B76420E6C7F800A04DBC /* ZPLFocusTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLFocusTableView.m; sourceTree = ""; }; 56 | 3435B76720E6C83800A04DBC /* ZPLSuggestionWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLSuggestionWindowController.h; sourceTree = ""; }; 57 | 3435B76820E6C83800A04DBC /* ZPLSuggestionWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLSuggestionWindowController.m; sourceTree = ""; }; 58 | 343DDD0720F51B450087E1CB /* NSView+Autocomplete.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+Autocomplete.h"; sourceTree = ""; }; 59 | 343DDD0820F51B450087E1CB /* NSView+Autocomplete.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+Autocomplete.m"; sourceTree = ""; }; 60 | C0840FD020F37E1D00AEAE81 /* ZPLRowView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLRowView.h; sourceTree = ""; }; 61 | C0840FD120F37E1D00AEAE81 /* ZPLRowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPLRowView.m; sourceTree = ""; }; 62 | C0840FD520F3B46400AEAE81 /* NSColor+Autocomplete.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSColor+Autocomplete.h"; sourceTree = ""; }; 63 | C0840FD620F3B46400AEAE81 /* NSColor+Autocomplete.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSColor+Autocomplete.m"; sourceTree = ""; }; 64 | C0C13FDA21699E2800E85491 /* ZPLTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPLTheme.h; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 3435B73020E6C0C100A04DBC /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 3435B72A20E6C0C100A04DBC = { 79 | isa = PBXGroup; 80 | children = ( 81 | 3435B73620E6C0C100A04DBC /* Autocomplete */, 82 | 3435B73520E6C0C100A04DBC /* Products */, 83 | ); 84 | sourceTree = ""; 85 | }; 86 | 3435B73520E6C0C100A04DBC /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 3435B73420E6C0C100A04DBC /* Autocomplete.framework */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 3435B73620E6C0C100A04DBC /* Autocomplete */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 3435B74420E6C29C00A04DBC /* Sources */, 98 | 3435B74720E6C4A400A04DBC /* Resources */, 99 | 3435B73720E6C0C100A04DBC /* Autocomplete.h */, 100 | 3435B73820E6C0C100A04DBC /* Info.plist */, 101 | ); 102 | path = Autocomplete; 103 | sourceTree = ""; 104 | }; 105 | 3435B74420E6C29C00A04DBC /* Sources */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 3435B73F20E6C14D00A04DBC /* ZPLAutocompletePluginController.h */, 109 | 3435B74020E6C14D00A04DBC /* ZPLAutocompletePluginController.m */, 110 | 3435B75520E6C57B00A04DBC /* Models */, 111 | 3435B76220E6C76800A04DBC /* Views */, 112 | 3435B74C20E6C52200A04DBC /* Controllers */, 113 | C0840FD420F3B43C00AEAE81 /* Categories */, 114 | ); 115 | path = Sources; 116 | sourceTree = ""; 117 | }; 118 | 3435B74720E6C4A400A04DBC /* Resources */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 3435B74520E6C48E00A04DBC /* Emojis.plist */, 122 | ); 123 | path = Resources; 124 | sourceTree = ""; 125 | }; 126 | 3435B74C20E6C52200A04DBC /* Controllers */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 3435B74820E6C51F00A04DBC /* ZPLEmojiController.h */, 130 | 3435B74920E6C51F00A04DBC /* ZPLEmojiController.m */, 131 | 3435B75620E6C64C00A04DBC /* ZPLSuggestionController.h */, 132 | 3435B75720E6C64C00A04DBC /* ZPLSuggestionController.m */, 133 | 3435B76720E6C83800A04DBC /* ZPLSuggestionWindowController.h */, 134 | 3435B76820E6C83800A04DBC /* ZPLSuggestionWindowController.m */, 135 | ); 136 | path = Controllers; 137 | sourceTree = ""; 138 | }; 139 | 3435B75520E6C57B00A04DBC /* Models */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 3435B75120E6C57600A04DBC /* ZPLEmoji.h */, 143 | 3435B75220E6C57600A04DBC /* ZPLEmoji.m */, 144 | 3435B75A20E6C6B500A04DBC /* ZPLSuggestion.h */, 145 | 3435B75B20E6C6B500A04DBC /* ZPLSuggestion.m */, 146 | C0C13FD921699DF000E85491 /* Sketch */, 147 | ); 148 | path = Models; 149 | sourceTree = ""; 150 | }; 151 | 3435B76220E6C76800A04DBC /* Views */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 3435B76320E6C7F800A04DBC /* ZPLFocusTableView.h */, 155 | 3435B76420E6C7F800A04DBC /* ZPLFocusTableView.m */, 156 | 3435B75E20E6C76100A04DBC /* ZPLSuggestionCell.h */, 157 | 3435B75F20E6C76100A04DBC /* ZPLSuggestionCell.m */, 158 | C0840FD020F37E1D00AEAE81 /* ZPLRowView.h */, 159 | C0840FD120F37E1D00AEAE81 /* ZPLRowView.m */, 160 | ); 161 | path = Views; 162 | sourceTree = ""; 163 | }; 164 | C0840FD420F3B43C00AEAE81 /* Categories */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | C0840FD520F3B46400AEAE81 /* NSColor+Autocomplete.h */, 168 | C0840FD620F3B46400AEAE81 /* NSColor+Autocomplete.m */, 169 | 343DDD0720F51B450087E1CB /* NSView+Autocomplete.h */, 170 | 343DDD0820F51B450087E1CB /* NSView+Autocomplete.m */, 171 | ); 172 | path = Categories; 173 | sourceTree = ""; 174 | }; 175 | C0C13FD921699DF000E85491 /* Sketch */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | C0C13FDA21699E2800E85491 /* ZPLTheme.h */, 179 | ); 180 | path = Sketch; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXHeadersBuildPhase section */ 186 | 3435B73120E6C0C100A04DBC /* Headers */ = { 187 | isa = PBXHeadersBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 3435B76920E6C83800A04DBC /* ZPLSuggestionWindowController.h in Headers */, 191 | 3435B74120E6C14D00A04DBC /* ZPLAutocompletePluginController.h in Headers */, 192 | 3435B75820E6C64C00A04DBC /* ZPLSuggestionController.h in Headers */, 193 | 3435B76520E6C7F800A04DBC /* ZPLFocusTableView.h in Headers */, 194 | 3435B73920E6C0C100A04DBC /* Autocomplete.h in Headers */, 195 | C0840FD220F37E1D00AEAE81 /* ZPLRowView.h in Headers */, 196 | C0C13FDB21699E2800E85491 /* ZPLTheme.h in Headers */, 197 | 3435B75C20E6C6B500A04DBC /* ZPLSuggestion.h in Headers */, 198 | 3435B76020E6C76100A04DBC /* ZPLSuggestionCell.h in Headers */, 199 | 343DDD0920F51B450087E1CB /* NSView+Autocomplete.h in Headers */, 200 | 3435B75320E6C57600A04DBC /* ZPLEmoji.h in Headers */, 201 | C0840FD720F3B46400AEAE81 /* NSColor+Autocomplete.h in Headers */, 202 | 3435B74A20E6C51F00A04DBC /* ZPLEmojiController.h in Headers */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXHeadersBuildPhase section */ 207 | 208 | /* Begin PBXNativeTarget section */ 209 | 3435B73320E6C0C100A04DBC /* Autocomplete */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = 3435B73C20E6C0C100A04DBC /* Build configuration list for PBXNativeTarget "Autocomplete" */; 212 | buildPhases = ( 213 | 3435B72F20E6C0C100A04DBC /* Sources */, 214 | 3435B73020E6C0C100A04DBC /* Frameworks */, 215 | 3435B73120E6C0C100A04DBC /* Headers */, 216 | 3435B73220E6C0C100A04DBC /* Resources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | ); 222 | name = Autocomplete; 223 | productName = Autocomplete; 224 | productReference = 3435B73420E6C0C100A04DBC /* Autocomplete.framework */; 225 | productType = "com.apple.product-type.framework"; 226 | }; 227 | /* End PBXNativeTarget section */ 228 | 229 | /* Begin PBXProject section */ 230 | 3435B72B20E6C0C100A04DBC /* Project object */ = { 231 | isa = PBXProject; 232 | attributes = { 233 | LastUpgradeCheck = 1000; 234 | ORGANIZATIONNAME = "Zeplin, Inc."; 235 | TargetAttributes = { 236 | 3435B73320E6C0C100A04DBC = { 237 | CreatedOnToolsVersion = 9.4; 238 | }; 239 | }; 240 | }; 241 | buildConfigurationList = 3435B72E20E6C0C100A04DBC /* Build configuration list for PBXProject "Autocomplete" */; 242 | compatibilityVersion = "Xcode 9.3"; 243 | developmentRegion = en; 244 | hasScannedForEncodings = 0; 245 | knownRegions = ( 246 | en, 247 | ); 248 | mainGroup = 3435B72A20E6C0C100A04DBC; 249 | productRefGroup = 3435B73520E6C0C100A04DBC /* Products */; 250 | projectDirPath = ""; 251 | projectRoot = ""; 252 | targets = ( 253 | 3435B73320E6C0C100A04DBC /* Autocomplete */, 254 | ); 255 | }; 256 | /* End PBXProject section */ 257 | 258 | /* Begin PBXResourcesBuildPhase section */ 259 | 3435B73220E6C0C100A04DBC /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | 3435B74620E6C48E00A04DBC /* Emojis.plist in Resources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | /* End PBXResourcesBuildPhase section */ 268 | 269 | /* Begin PBXSourcesBuildPhase section */ 270 | 3435B72F20E6C0C100A04DBC /* Sources */ = { 271 | isa = PBXSourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 3435B75420E6C57600A04DBC /* ZPLEmoji.m in Sources */, 275 | 3435B74220E6C14D00A04DBC /* ZPLAutocompletePluginController.m in Sources */, 276 | 3435B76120E6C76100A04DBC /* ZPLSuggestionCell.m in Sources */, 277 | 3435B76620E6C7F800A04DBC /* ZPLFocusTableView.m in Sources */, 278 | C0840FD320F37E1D00AEAE81 /* ZPLRowView.m in Sources */, 279 | 3435B76A20E6C83800A04DBC /* ZPLSuggestionWindowController.m in Sources */, 280 | 3435B75920E6C64C00A04DBC /* ZPLSuggestionController.m in Sources */, 281 | 3435B75D20E6C6B500A04DBC /* ZPLSuggestion.m in Sources */, 282 | C0840FD820F3B46400AEAE81 /* NSColor+Autocomplete.m in Sources */, 283 | 343DDD0A20F51B450087E1CB /* NSView+Autocomplete.m in Sources */, 284 | 3435B74B20E6C51F00A04DBC /* ZPLEmojiController.m in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin XCBuildConfiguration section */ 291 | 3435B73A20E6C0C100A04DBC /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ALWAYS_SEARCH_USER_PATHS = NO; 295 | CLANG_ANALYZER_NONNULL = YES; 296 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 297 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 298 | CLANG_CXX_LIBRARY = "libc++"; 299 | CLANG_ENABLE_MODULES = YES; 300 | CLANG_ENABLE_OBJC_ARC = YES; 301 | CLANG_ENABLE_OBJC_WEAK = YES; 302 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 303 | CLANG_WARN_BOOL_CONVERSION = YES; 304 | CLANG_WARN_COMMA = YES; 305 | CLANG_WARN_CONSTANT_CONVERSION = YES; 306 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 308 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INFINITE_RECURSION = YES; 312 | CLANG_WARN_INT_CONVERSION = YES; 313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 318 | CLANG_WARN_STRICT_PROTOTYPES = YES; 319 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 320 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | CODE_SIGN_IDENTITY = "Mac Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | CURRENT_PROJECT_VERSION = 1; 326 | DEBUG_INFORMATION_FORMAT = dwarf; 327 | DEVELOPMENT_TEAM = 8U3Y4X5WDQ; 328 | ENABLE_STRICT_OBJC_MSGSEND = YES; 329 | ENABLE_TESTABILITY = YES; 330 | GCC_C_LANGUAGE_STANDARD = gnu11; 331 | GCC_DYNAMIC_NO_PIC = NO; 332 | GCC_NO_COMMON_BLOCKS = YES; 333 | GCC_OPTIMIZATION_LEVEL = 0; 334 | GCC_PREPROCESSOR_DEFINITIONS = ( 335 | "DEBUG=1", 336 | "$(inherited)", 337 | ); 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | MACOSX_DEPLOYMENT_TARGET = 10.11; 345 | MTL_ENABLE_DEBUG_INFO = YES; 346 | ONLY_ACTIVE_ARCH = YES; 347 | SDKROOT = macosx; 348 | VERSIONING_SYSTEM = "apple-generic"; 349 | VERSION_INFO_PREFIX = ""; 350 | }; 351 | name = Debug; 352 | }; 353 | 3435B73B20E6C0C100A04DBC /* Release */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | ALWAYS_SEARCH_USER_PATHS = NO; 357 | CLANG_ANALYZER_NONNULL = YES; 358 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 359 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 360 | CLANG_CXX_LIBRARY = "libc++"; 361 | CLANG_ENABLE_MODULES = YES; 362 | CLANG_ENABLE_OBJC_ARC = YES; 363 | CLANG_ENABLE_OBJC_WEAK = YES; 364 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 365 | CLANG_WARN_BOOL_CONVERSION = YES; 366 | CLANG_WARN_COMMA = YES; 367 | CLANG_WARN_CONSTANT_CONVERSION = YES; 368 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 369 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 370 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INFINITE_RECURSION = YES; 374 | CLANG_WARN_INT_CONVERSION = YES; 375 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 376 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 377 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 378 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 379 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 380 | CLANG_WARN_STRICT_PROTOTYPES = YES; 381 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 382 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | CODE_SIGN_IDENTITY = "Mac Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | CURRENT_PROJECT_VERSION = 1; 388 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 389 | DEVELOPMENT_TEAM = 8U3Y4X5WDQ; 390 | ENABLE_NS_ASSERTIONS = NO; 391 | ENABLE_STRICT_OBJC_MSGSEND = YES; 392 | GCC_C_LANGUAGE_STANDARD = gnu11; 393 | GCC_NO_COMMON_BLOCKS = YES; 394 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 395 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 396 | GCC_WARN_UNDECLARED_SELECTOR = YES; 397 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 398 | GCC_WARN_UNUSED_FUNCTION = YES; 399 | GCC_WARN_UNUSED_VARIABLE = YES; 400 | MACOSX_DEPLOYMENT_TARGET = 10.11; 401 | MTL_ENABLE_DEBUG_INFO = NO; 402 | SDKROOT = macosx; 403 | VERSIONING_SYSTEM = "apple-generic"; 404 | VERSION_INFO_PREFIX = ""; 405 | }; 406 | name = Release; 407 | }; 408 | 3435B73D20E6C0C100A04DBC /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | CODE_SIGN_IDENTITY = ""; 412 | COMBINE_HIDPI_IMAGES = YES; 413 | DEFINES_MODULE = YES; 414 | DYLIB_COMPATIBILITY_VERSION = 1; 415 | DYLIB_CURRENT_VERSION = 1; 416 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 417 | FRAMEWORK_VERSION = A; 418 | INFOPLIST_FILE = Autocomplete/Info.plist; 419 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 420 | LD_RUNPATH_SEARCH_PATHS = ( 421 | "$(inherited)", 422 | "@executable_path/../Frameworks", 423 | "@loader_path/Frameworks", 424 | ); 425 | PRODUCT_BUNDLE_IDENTIFIER = io.zeplin.Autocomplete; 426 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 427 | SKIP_INSTALL = YES; 428 | }; 429 | name = Debug; 430 | }; 431 | 3435B73E20E6C0C100A04DBC /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | CODE_SIGN_IDENTITY = ""; 435 | COMBINE_HIDPI_IMAGES = YES; 436 | DEFINES_MODULE = YES; 437 | DYLIB_COMPATIBILITY_VERSION = 1; 438 | DYLIB_CURRENT_VERSION = 1; 439 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 440 | FRAMEWORK_VERSION = A; 441 | INFOPLIST_FILE = Autocomplete/Info.plist; 442 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 443 | LD_RUNPATH_SEARCH_PATHS = ( 444 | "$(inherited)", 445 | "@executable_path/../Frameworks", 446 | "@loader_path/Frameworks", 447 | ); 448 | PRODUCT_BUNDLE_IDENTIFIER = io.zeplin.Autocomplete; 449 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 450 | SKIP_INSTALL = YES; 451 | }; 452 | name = Release; 453 | }; 454 | /* End XCBuildConfiguration section */ 455 | 456 | /* Begin XCConfigurationList section */ 457 | 3435B72E20E6C0C100A04DBC /* Build configuration list for PBXProject "Autocomplete" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | 3435B73A20E6C0C100A04DBC /* Debug */, 461 | 3435B73B20E6C0C100A04DBC /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | 3435B73C20E6C0C100A04DBC /* Build configuration list for PBXNativeTarget "Autocomplete" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | 3435B73D20E6C0C100A04DBC /* Debug */, 470 | 3435B73E20E6C0C100A04DBC /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | /* End XCConfigurationList section */ 476 | }; 477 | rootObject = 3435B72B20E6C0C100A04DBC /* Project object */; 478 | } 479 | -------------------------------------------------------------------------------- /Autocomplete.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Autocomplete.xcodeproj/project.xcworkspace/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | COPYRIGHT 6 | Copyright © ___YEAR___ ___ORGANIZATIONNAME___ 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | 27 | -------------------------------------------------------------------------------- /Autocomplete.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Autocomplete.xcodeproj/xcshareddata/xcschemes/Autocomplete.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 48 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 67 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 87 | 93 | 94 | 95 | 96 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Autocomplete/Autocomplete.h: -------------------------------------------------------------------------------- 1 | // 2 | // Autocomplete.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE 25 | // 26 | 27 | #import 28 | 29 | FOUNDATION_EXPORT double AutocompleteVersionNumber; 30 | FOUNDATION_EXPORT const unsigned char AutocompleteVersionString[]; 31 | 32 | #import 33 | -------------------------------------------------------------------------------- /Autocomplete/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.3 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2022 Zeplin, Inc. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Categories/NSColor+Autocomplete.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+Autocomplete.h 3 | // Autocomplete 4 | // 5 | // Created by K. Berk Cebi on 7/9/18. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @interface NSColor (Autocomplete) 30 | 31 | + (NSColor *)zpl_backgroundColor; 32 | + (NSColor *)zpl_selectionColor; 33 | + (NSColor *)zpl_textColor; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Categories/NSColor+Autocomplete.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+Autocomplete.m 3 | // Autocomplete 4 | // 5 | // Created by K. Berk Cebi on 7/9/18. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "NSColor+Autocomplete.h" 28 | 29 | #import "ZPLTheme.h" 30 | 31 | @implementation NSColor (Autocomplete) 32 | 33 | + (NSColor *)zpl_backgroundColor { 34 | NSObject *theme = [self zpl_currentTheme]; 35 | if (!theme) { 36 | return nil; 37 | } 38 | 39 | return [theme layerListBackgroundColor]; 40 | } 41 | 42 | + (NSColor *)zpl_selectionColor { 43 | NSObject *theme = [self zpl_currentTheme]; 44 | if (!theme) { 45 | return [NSColor colorWithDeviceRed:105.0f / 255.0f green:155.0f / 255.0f blue:228.0f / 255.0f alpha:1.0f]; 46 | } 47 | 48 | return [theme inspectorAccentColor]; 49 | } 50 | 51 | + (NSColor *)zpl_textColor { 52 | NSObject *theme = [self zpl_currentTheme]; 53 | if (!theme) { 54 | return nil; 55 | } 56 | 57 | return [theme inspectorLabelTextColor]; 58 | } 59 | 60 | #pragma mark - Private 61 | 62 | + (NSObject *)zpl_currentTheme { 63 | Class themeClass = NSClassFromString(ZPLThemeClassName); 64 | if (!themeClass) { 65 | return nil; 66 | } 67 | 68 | return [themeClass sharedTheme]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Categories/NSView+Autocomplete.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+Autocomplete.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 10.07.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @interface NSView (Autocomplete) 30 | 31 | - (BOOL)zpl_nextRespondersContainClassFromNames:(NSArray *)classNames; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Categories/NSView+Autocomplete.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+Autocomplete.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 10.07.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "NSView+Autocomplete.h" 28 | 29 | @implementation NSView (Autocomplete) 30 | 31 | - (BOOL)zpl_nextRespondersContainClassFromNames:(NSArray *)classNames { 32 | for (NSResponder *nextResponder = self.nextResponder; nextResponder; nextResponder = nextResponder.nextResponder) { 33 | for (NSString *className in classNames) { 34 | if ([nextResponder isKindOfClass:NSClassFromString(className)]) { 35 | return YES; 36 | } 37 | } 38 | } 39 | 40 | return NO; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLEmojiController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLEmojiController.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | #import "ZPLEmoji.h" 30 | 31 | @interface ZPLEmojiController : NSObject 32 | 33 | @property (strong, nonatomic) NSArray *emojis; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLEmojiController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLEmojiController.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLEmojiController.h" 28 | 29 | #import "ZPLAutocompletePluginController.h" 30 | 31 | @implementation ZPLEmojiController 32 | 33 | #pragma mark - Initializers 34 | 35 | - (instancetype)init { 36 | self = [super init]; 37 | if (!self) { 38 | return nil; 39 | } 40 | 41 | NSBundle *bundle = [NSBundle bundleForClass:[ZPLAutocompletePluginController class]]; 42 | NSString *emojiListPath = [bundle pathForResource:@"Emojis" ofType:@"plist"]; 43 | NSArray *emojiDictionaries = (NSArray *)[NSArray arrayWithContentsOfFile:emojiListPath]; 44 | 45 | NSMutableArray *emojis = [NSMutableArray array]; 46 | for (NSDictionary *emojiDictionary in emojiDictionaries) { 47 | ZPLEmoji *emoji = [[ZPLEmoji alloc] initWithDictionary:emojiDictionary]; 48 | 49 | if (!emoji) { 50 | continue; 51 | } 52 | 53 | [emojis addObject:emoji]; 54 | } 55 | 56 | self.emojis = emojis; 57 | 58 | return self; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLSuggestionController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionController.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @interface ZPLSuggestionController : NSObject 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLSuggestionController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionController.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLSuggestionController.h" 28 | 29 | #import "ZPLEmoji.h" 30 | #import "ZPLSuggestion.h" 31 | #import "ZPLEmojiController.h" 32 | #import "ZPLSuggestionWindowController.h" 33 | 34 | #import "NSView+Autocomplete.h" 35 | 36 | static NSString * const ZPLSuggestionDelimeter = @":"; 37 | static NSString * const MSTextLayerTextViewClassName = @"MSTextLayerTextView"; 38 | static NSString * const MSOverrideTextFieldClassName = @"MSOverrideTextField"; 39 | static NSString * const BCPageListViewControllerClassName = @"BCPageListViewController"; 40 | static NSString * const BCLayerListViewControllerClassName = @"BCLayerListViewController"; 41 | static NSString * const MSTextOverrideViewControllerClassName = @"MSTextOverrideViewController"; 42 | static NSString * const MSTextOverrideInspectorItemClassName = @"MSTextOverrideInspectorItem"; 43 | 44 | @interface ZPLSuggestionController () 45 | 46 | @property (strong, nonatomic) ZPLEmojiController *emojiController; 47 | @property (strong, nonatomic) ZPLSuggestionWindowController *windowController; 48 | 49 | @property (strong, nonatomic) NSCharacterSet *invertedCharacterSet; 50 | @property (strong, nonatomic) NSArray *responderClassNames; 51 | 52 | @property (strong, nonatomic) NSTextView *positioningTextView; 53 | 54 | @end 55 | 56 | @implementation ZPLSuggestionController 57 | 58 | #pragma mark - Initializers 59 | 60 | - (void)dealloc { 61 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 62 | } 63 | 64 | - (instancetype)init { 65 | self = [super init]; 66 | if (!self) { 67 | return nil; 68 | } 69 | 70 | _emojiController = [[ZPLEmojiController alloc] init]; 71 | _invertedCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyz0123456789_"] invertedSet]; 72 | _responderClassNames = @[BCPageListViewControllerClassName, BCLayerListViewControllerClassName, MSTextOverrideViewControllerClassName, MSTextOverrideInspectorItemClassName]; 73 | 74 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidChangeSelection:) name:NSTextViewDidChangeSelectionNotification object:nil]; 75 | 76 | return self; 77 | } 78 | 79 | #pragma mark - Private 80 | 81 | - (void)dismissWindowController { 82 | [self.windowController dismiss]; 83 | self.windowController = nil; 84 | } 85 | 86 | - (void)reloadSuggestionsForTextView:(NSTextView *)textView { 87 | NSArray *suggestions = [self suggestionsForTextView:textView]; 88 | 89 | if (suggestions.count == 0) { 90 | [self dismissWindowController]; 91 | 92 | return; 93 | } 94 | 95 | if (!self.windowController) { 96 | self.windowController = [[ZPLSuggestionWindowController alloc] init]; 97 | self.windowController.delegate = self; 98 | } 99 | 100 | self.positioningTextView = textView; 101 | [self.windowController presentWithSuggestions:suggestions positioningTextView:textView]; 102 | } 103 | 104 | - (NSArray *)suggestionsForTextView:(NSTextView *)textView { 105 | NSRange keywordRange = [self keywordRangeForTextView:textView]; 106 | 107 | if (keywordRange.location == NSNotFound) { 108 | return @[]; 109 | } 110 | 111 | NSString *text = [textView.string lowercaseString]; 112 | NSString *keyword = [text substringWithRange:keywordRange]; 113 | NSMutableArray *suggestions = [NSMutableArray array]; 114 | 115 | for (ZPLEmoji *emoji in self.emojiController.emojis) { 116 | BOOL suggestionAdded = NO; 117 | 118 | for (NSString *alias in emoji.aliases) { 119 | if ([alias hasPrefix:keyword]) { 120 | ZPLSuggestion *suggestion = [[ZPLSuggestion alloc] init]; 121 | suggestion.emoji = emoji; 122 | suggestion.alias = alias; 123 | 124 | [suggestions addObject:suggestion]; 125 | suggestionAdded = YES; 126 | 127 | break; 128 | } 129 | 130 | NSArray *aliasComponents = [alias componentsSeparatedByString:ZPLEmojiAliasSeparator]; 131 | 132 | if (aliasComponents.count <= 1) { 133 | continue; 134 | } 135 | 136 | for (NSString *aliasComponent in aliasComponents) { 137 | if ([aliasComponent hasPrefix:keyword]) { 138 | ZPLSuggestion *suggestion = [[ZPLSuggestion alloc] init]; 139 | suggestion.emoji = emoji; 140 | suggestion.alias = alias; 141 | 142 | [suggestions addObject:suggestion]; 143 | suggestionAdded = YES; 144 | 145 | break; 146 | } 147 | } 148 | } 149 | 150 | if (suggestionAdded) { 151 | continue; 152 | } 153 | 154 | for (NSString *tag in emoji.tags) { 155 | if ([tag hasPrefix:keyword]) { 156 | ZPLSuggestion *suggestion = [[ZPLSuggestion alloc] init]; 157 | suggestion.emoji = emoji; 158 | suggestion.alias = [[emoji aliases] firstObject]; 159 | 160 | [suggestions addObject:suggestion]; 161 | suggestionAdded = YES; 162 | 163 | break; 164 | } 165 | } 166 | } 167 | 168 | return suggestions; 169 | } 170 | 171 | - (NSRange)keywordRangeForTextView:(NSTextView *)textView { 172 | if (textView.selectedRanges.firstObject == nil) { 173 | return NSMakeRange(NSNotFound, 0); 174 | } 175 | 176 | NSRange range = textView.selectedRanges.firstObject.rangeValue; 177 | 178 | if (range.length != 0) { 179 | return NSMakeRange(NSNotFound, 0); 180 | } 181 | 182 | NSString *text = [textView.string lowercaseString]; 183 | NSString *leftText = [text substringToIndex:range.location]; 184 | NSString *rightText = [text substringFromIndex:range.location]; 185 | 186 | NSRange leftInvalidCharacterRange = [leftText rangeOfCharacterFromSet:self.invertedCharacterSet options:NSBackwardsSearch]; 187 | if (leftInvalidCharacterRange.location == NSNotFound 188 | || ![[leftText substringWithRange:leftInvalidCharacterRange] isEqualToString:ZPLSuggestionDelimeter]) { 189 | return NSMakeRange(NSNotFound, 0); 190 | } 191 | 192 | NSUInteger rightInvalidCharacterIndex = [rightText rangeOfCharacterFromSet:self.invertedCharacterSet].location; 193 | if (rightInvalidCharacterIndex == NSNotFound) { 194 | rightInvalidCharacterIndex = rightText.length; 195 | } 196 | 197 | NSUInteger keywordLocation = leftInvalidCharacterRange.location + ZPLSuggestionDelimeter.length; 198 | NSUInteger keywordLength = range.location + rightInvalidCharacterIndex - keywordLocation; 199 | 200 | return NSMakeRange(keywordLocation, keywordLength); 201 | } 202 | 203 | #pragma mark - Notifications 204 | 205 | - (void)textViewDidChangeSelection:(NSNotification *)notification { 206 | NSTextView *textView = (NSTextView *)notification.object; 207 | 208 | if (![textView isKindOfClass:NSClassFromString(MSTextLayerTextViewClassName)] && 209 | ![textView.delegate isKindOfClass:NSClassFromString(MSOverrideTextFieldClassName)] && 210 | ![textView zpl_nextRespondersContainClassFromNames:self.responderClassNames]) { 211 | [self dismissWindowController]; 212 | 213 | return; 214 | } 215 | 216 | [self reloadSuggestionsForTextView:textView]; 217 | } 218 | 219 | #pragma mark - ZPLSuggestionWindowControllerDelegate 220 | 221 | - (void)suggestionWindowController:(ZPLSuggestionWindowController *)suggestionWindowController didSelectSuggestion:(ZPLSuggestion *)suggestion { 222 | if (!self.positioningTextView) { 223 | return; 224 | } 225 | 226 | NSRange keywordRange = [self keywordRangeForTextView:self.positioningTextView]; 227 | 228 | if (keywordRange.location == NSNotFound) { 229 | return; 230 | } 231 | 232 | keywordRange = NSMakeRange(keywordRange.location - ZPLSuggestionDelimeter.length, keywordRange.length + ZPLSuggestionDelimeter.length); 233 | NSUInteger keywordMaxRange = NSMaxRange(keywordRange); 234 | 235 | BOOL hasWhitespaceSuffix = NO; 236 | if (keywordMaxRange < self.positioningTextView.string.length - 1) { 237 | NSString *nextCharacterString = [self.positioningTextView.string substringWithRange:NSMakeRange(keywordMaxRange, 1)]; 238 | 239 | if ([nextCharacterString rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]].location != NSNotFound) { 240 | hasWhitespaceSuffix = YES; 241 | } 242 | } 243 | 244 | NSString *replacement; 245 | if (hasWhitespaceSuffix) { 246 | replacement = [suggestion.emoji.value copy]; 247 | } else { 248 | replacement = [NSString stringWithFormat:@"%@ ", suggestion.emoji.value]; 249 | } 250 | 251 | [[self.positioningTextView textStorage] replaceCharactersInRange:keywordRange withString:replacement]; 252 | } 253 | 254 | - (NSRange)suggestionWindowController:(ZPLSuggestionWindowController *)suggestionWindowController keywordRangeForTextView:(NSTextView *)textView { 255 | return [self keywordRangeForTextView:textView]; 256 | } 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLSuggestionWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionWindowController.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @class ZPLSuggestion; 30 | 31 | @protocol ZPLSuggestionWindowControllerDelegate; 32 | 33 | @interface ZPLSuggestionWindowController : NSWindowController 34 | 35 | @property (weak, nonatomic) id delegate; 36 | 37 | - (void)presentWithSuggestions:(NSArray *)suggestions positioningTextView:(NSTextView *)positioningTextView; 38 | - (void)dismiss; 39 | 40 | @end 41 | 42 | @protocol ZPLSuggestionWindowControllerDelegate 43 | 44 | - (void)suggestionWindowController:(ZPLSuggestionWindowController *)suggestionWindowController didSelectSuggestion:(ZPLSuggestion *)suggestion; 45 | - (NSRange)suggestionWindowController:(ZPLSuggestionWindowController *)suggestionWindowController keywordRangeForTextView:(NSTextView *)textView; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Controllers/ZPLSuggestionWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionWindowController.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLSuggestionController.h" 28 | 29 | #import "ZPLSuggestionWindowController.h" 30 | 31 | #import "ZPLSuggestion.h" 32 | #import "ZPLFocusTableView.h" 33 | #import "ZPLSuggestionCell.h" 34 | #import "ZPLRowView.h" 35 | #import "NSColor+Autocomplete.h" 36 | 37 | static const CGFloat ZPLSuggestionWindowControllerContentViewCornerRadius = 3.0f; 38 | static const CGFloat ZPLSuggestionWindowControllerMargin = 8.0f; 39 | 40 | static const NSUInteger ZPLEventKeyCodeReturn = 36; 41 | static const NSUInteger ZPLEventKeyCodeTab = 48; 42 | static const NSUInteger ZPLEventKeyCodeEscape = 53; 43 | static const NSUInteger ZPLEventKeyCodeBottomArrow = 125; 44 | static const NSUInteger ZPLEventKeyCodeTopArrow = 126; 45 | 46 | static const NSSize ZPLSuggestionWindowControllerMaximumWindowSize = {.width = 190.0f, .height = 208.0f}; 47 | 48 | @interface ZPLSuggestionWindowController () 49 | 50 | @property (copy, nonatomic) NSArray *suggestions; 51 | 52 | @property (strong, nonatomic) ZPLFocusTableView *tableView; 53 | 54 | @property (strong, nonatomic) id mouseDownMonitor; 55 | @property (strong, nonatomic) id keyDownMonitor; 56 | 57 | @end 58 | 59 | @implementation ZPLSuggestionWindowController 60 | 61 | #pragma mark - Initializers 62 | 63 | - (void)dealloc { 64 | _tableView.delegate = nil; 65 | _tableView.dataSource = nil; 66 | 67 | if (_keyDownMonitor != nil) { 68 | [NSEvent removeMonitor:_keyDownMonitor]; 69 | } 70 | 71 | if (_mouseDownMonitor != nil) { 72 | [NSEvent removeMonitor:_mouseDownMonitor]; 73 | } 74 | 75 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 76 | } 77 | 78 | - (instancetype)init { 79 | self = [super initWithWindow:nil]; 80 | if (!self) { 81 | return nil; 82 | } 83 | 84 | NSView *contentView = [[NSView alloc] init]; 85 | contentView.wantsLayer = YES; 86 | contentView.layer.cornerRadius = ZPLSuggestionWindowControllerContentViewCornerRadius; 87 | 88 | _tableView = [[ZPLFocusTableView alloc] init]; 89 | _tableView.headerView = nil; 90 | _tableView.intercellSpacing = NSZeroSize; 91 | _tableView.delegate = self; 92 | _tableView.dataSource = self; 93 | _tableView.allowsEmptySelection = NO; 94 | 95 | NSColor *tableViewBackgroundColor = [NSColor zpl_backgroundColor]; 96 | if (tableViewBackgroundColor) { 97 | _tableView.backgroundColor = tableViewBackgroundColor; 98 | } 99 | 100 | NSScrollView *scrollView = [[NSScrollView alloc] init]; 101 | scrollView.documentView = _tableView; 102 | 103 | [contentView addSubview:scrollView]; 104 | 105 | contentView.translatesAutoresizingMaskIntoConstraints = NO; 106 | scrollView.translatesAutoresizingMaskIntoConstraints = NO; 107 | 108 | [NSLayoutConstraint activateConstraints:@[ 109 | [scrollView.topAnchor constraintEqualToAnchor:contentView.topAnchor], 110 | [scrollView.leftAnchor constraintEqualToAnchor:contentView.leftAnchor], 111 | [scrollView.rightAnchor constraintEqualToAnchor:contentView.rightAnchor], 112 | [scrollView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor] 113 | ]]; 114 | 115 | NSTableColumn *column = [[NSTableColumn alloc] init]; 116 | column.resizingMask = NSTableColumnAutoresizingMask; 117 | [_tableView addTableColumn:column]; 118 | 119 | NSWindow *window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO]; 120 | window.titleVisibility = NSWindowTitleHidden; 121 | window.hasShadow = YES; 122 | window.opaque = NO; 123 | window.backgroundColor = [NSColor clearColor]; 124 | window.contentView = contentView; 125 | 126 | self.window = window; 127 | 128 | return self; 129 | } 130 | 131 | #pragma mark - Public 132 | 133 | - (void)presentWithSuggestions:(NSArray *)suggestions positioningTextView:(NSTextView *)positioningTextView { 134 | self.suggestions = suggestions; 135 | 136 | [self.tableView reloadData]; 137 | 138 | if (![self adjustFrameWithPositioningTextView:positioningTextView]) { 139 | return; 140 | } 141 | 142 | if (self.window.isVisible == NO) { 143 | [positioningTextView.window addChildWindow:self.window ordered:NSWindowAbove]; 144 | 145 | __weak typeof(self) weakSelf = self; 146 | 147 | if (!self.keyDownMonitor) { 148 | self.keyDownMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown handler:^NSEvent *(NSEvent *event) { 149 | __strong typeof(weakSelf) strongSelf = weakSelf; 150 | 151 | switch (event.keyCode) { 152 | case ZPLEventKeyCodeTab: 153 | case ZPLEventKeyCodeReturn: { 154 | NSInteger selectedRow = strongSelf.tableView.selectedRow; 155 | 156 | if (selectedRow < 0) { 157 | return nil; 158 | } 159 | 160 | [strongSelf.delegate suggestionWindowController:strongSelf didSelectSuggestion:strongSelf.suggestions[selectedRow]]; 161 | 162 | return nil; 163 | } break; 164 | 165 | case ZPLEventKeyCodeEscape: { 166 | [strongSelf dismiss]; 167 | 168 | return nil; 169 | } break; 170 | 171 | case ZPLEventKeyCodeTopArrow: { 172 | BOOL isCommand = (event.modifierFlags & NSCommandKeyMask) == NSCommandKeyMask; 173 | 174 | NSInteger row = isCommand ? 0 : MAX(strongSelf.tableView.selectedRow - 1, 0); 175 | [strongSelf selectRowAtIndex:row]; 176 | 177 | return nil; 178 | } break; 179 | 180 | case ZPLEventKeyCodeBottomArrow: { 181 | BOOL isCommand = (event.modifierFlags & NSCommandKeyMask) == NSCommandKeyMask; 182 | 183 | NSInteger lastRow = strongSelf.suggestions.count - 1; 184 | NSInteger row = isCommand ? lastRow : MIN(strongSelf.tableView.selectedRow + 1, lastRow); 185 | [strongSelf selectRowAtIndex:row]; 186 | 187 | return nil; 188 | } break; 189 | 190 | default: { 191 | return event; 192 | } break; 193 | } 194 | }]; 195 | } 196 | 197 | if (!self.mouseDownMonitor) { 198 | self.mouseDownMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown | NSEventMaskOtherMouseDown handler:^ NSEvent *(NSEvent *event) { 199 | __strong typeof(weakSelf) strongSelf = weakSelf; 200 | 201 | if (strongSelf.window == event.window) { 202 | return event; 203 | } 204 | 205 | if (positioningTextView.window != event.window) { 206 | [strongSelf dismiss]; 207 | 208 | return event; 209 | } 210 | 211 | NSPoint point = [positioningTextView convertPoint:event.locationInWindow fromView:nil]; 212 | 213 | if (!NSPointInRect(point, positioningTextView.bounds)) { 214 | [strongSelf dismiss]; 215 | 216 | return nil; 217 | } 218 | 219 | return event; 220 | }]; 221 | } 222 | 223 | positioningTextView.postsFrameChangedNotifications = YES; 224 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(positioningTextViewFrameDidChange:) name:NSViewFrameDidChangeNotification object:positioningTextView]; 225 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parentWindowDidResignKey:) name:NSWindowDidResignKeyNotification object:positioningTextView.window]; 226 | } 227 | 228 | [self selectRowAtIndex:0]; 229 | } 230 | 231 | - (void)dismiss { 232 | if (self.window.isVisible == NO) { 233 | return; 234 | } 235 | 236 | if (self.keyDownMonitor != nil) { 237 | [NSEvent removeMonitor:self.keyDownMonitor]; 238 | self.keyDownMonitor = nil; 239 | } 240 | 241 | if (self.mouseDownMonitor != nil) { 242 | [NSEvent removeMonitor:self.mouseDownMonitor]; 243 | self.mouseDownMonitor = nil; 244 | } 245 | 246 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 247 | 248 | [self.window.parentWindow removeChildWindow:self.window]; 249 | [self.window orderOut:nil]; 250 | } 251 | 252 | #pragma mark - Private 253 | 254 | - (BOOL)adjustFrameWithPositioningTextView:(NSTextView *)positioningTextView { 255 | if (self.window == nil || positioningTextView.superview == nil || positioningTextView.window == nil) { 256 | return NO; 257 | } 258 | 259 | CGFloat windowHeight = MIN(self.suggestions.count * ZPLSuggestionCell.height, ZPLSuggestionWindowControllerMaximumWindowSize.height); 260 | 261 | NSRange keywordRange = [self.delegate suggestionWindowController:self keywordRangeForTextView:positioningTextView]; 262 | if (keywordRange.location == NSNotFound) { 263 | keywordRange = [positioningTextView selectedRange]; 264 | } 265 | 266 | NSRange glyphRange = [[positioningTextView layoutManager] glyphRangeForCharacterRange:keywordRange actualCharacterRange:nil]; 267 | NSRect characterRect = [[positioningTextView layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[positioningTextView textContainer]]; 268 | characterRect = NSInsetRect(characterRect, positioningTextView.textContainerOrigin.x, positioningTextView.textContainerOrigin.y); 269 | NSRect positioningTextViewRect = [positioningTextView convertRect:characterRect toView:nil]; 270 | NSRect rect = [positioningTextView.window convertRectToScreen:positioningTextViewRect]; 271 | 272 | rect.size.width = ZPLSuggestionWindowControllerMaximumWindowSize.width; 273 | rect.size.height = windowHeight; 274 | 275 | NSRect screenVisibleFrame = positioningTextView.window.screen.visibleFrame; 276 | CGFloat minX = ZPLSuggestionWindowControllerMargin; 277 | CGFloat maxX = NSMaxX(screenVisibleFrame) - ZPLSuggestionWindowControllerMargin; 278 | 279 | if (NSMaxX(rect) > maxX) { 280 | rect.origin.x = maxX - rect.size.width; 281 | } else if (NSMinX(rect) < minX) { 282 | rect.origin.x = minX; 283 | } 284 | 285 | CGFloat rectOriginY = rect.origin.y - windowHeight - ZPLSuggestionWindowControllerMargin; 286 | if (rectOriginY < NSMinY(screenVisibleFrame)) { 287 | rect.origin.y = rect.origin.y + positioningTextViewRect.size.height; 288 | } else { 289 | rect.origin.y = rectOriginY; 290 | } 291 | 292 | [self.window setFrame:rect display:NO]; 293 | 294 | return YES; 295 | } 296 | 297 | - (void)selectRowAtIndex:(NSInteger)index { 298 | if (self.suggestions.count == 0) { 299 | return; 300 | } 301 | 302 | NSInteger adjustedIndex = MAX(0, MIN(index, self.suggestions.count - 1)); 303 | 304 | [self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:adjustedIndex] byExtendingSelection:NO]; 305 | [self.tableView scrollRowToVisible:adjustedIndex]; 306 | } 307 | 308 | #pragma mark - NSTableViewDataSource 309 | 310 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 311 | return self.suggestions.count; 312 | } 313 | 314 | #pragma mark - ZPLFocusTableViewDelegate 315 | 316 | - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 317 | ZPLSuggestionCell *cell = [tableView makeViewWithIdentifier:[ZPLSuggestionCell reuseIdentifier] owner:self]; 318 | if (!cell) { 319 | cell = [[ZPLSuggestionCell alloc] init]; 320 | } 321 | 322 | cell.suggestion = self.suggestions[row]; 323 | 324 | return cell; 325 | } 326 | 327 | - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { 328 | return [ZPLSuggestionCell height]; 329 | } 330 | 331 | - (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row { 332 | return [[ZPLRowView alloc] init]; 333 | } 334 | 335 | - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row { 336 | return YES; 337 | } 338 | 339 | - (void)tableView:(NSTableView *)tableView didClickRow:(NSInteger)row { 340 | [self.delegate suggestionWindowController:self didSelectSuggestion:self.suggestions[row]]; 341 | } 342 | 343 | #pragma mark - Notifications 344 | 345 | - (void)positioningTextViewFrameDidChange:(NSNotification *)notification { 346 | NSTextView *positioningTextView = notification.object; 347 | if (!positioningTextView) { 348 | return; 349 | } 350 | 351 | [self adjustFrameWithPositioningTextView:positioningTextView]; 352 | } 353 | 354 | - (void)parentWindowDidResignKey:(NSNotification *)notification { 355 | [self dismiss]; 356 | } 357 | 358 | @end 359 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Models/Sketch/ZPLTheme.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLTheme.h 3 | // Autocomplete 4 | // 5 | // Created by K. Berk Cebi on 10/6/18. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | static NSString * const ZPLThemeClassName = @"MSTheme"; 30 | 31 | @protocol ZPLTheme 32 | 33 | + (instancetype)sharedTheme; 34 | 35 | @property (readonly, nonatomic) NSColor *inspectorLabelTextColor; 36 | @property (readonly, nonatomic) NSColor *inspectorAccentColor; 37 | @property (readonly, nonatomic) NSColor *layerListBackgroundColor; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Models/ZPLEmoji.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLEmoji.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | extern NSString * const ZPLEmojiAliasSeparator; 30 | 31 | @interface ZPLEmoji : NSObject 32 | 33 | @property (copy, nonatomic) NSString *value; 34 | @property (strong, nonatomic) NSArray *aliases; 35 | @property (strong, nonatomic) NSArray *tags; 36 | 37 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Models/ZPLEmoji.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLEmoji.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLEmoji.h" 28 | 29 | NSString * const ZPLEmojiAliasSeparator = @"_"; 30 | 31 | static NSString * const ZPLEmojiValueDictionaryKey = @"emoji"; 32 | static NSString * const ZPLEmojiAliasesDictionaryKey = @"aliases"; 33 | static NSString * const ZPLEmojiTagsDictionaryKey = @"tags"; 34 | static NSString * const ZPLEmojiUnicodeVersionDictionaryKey = @"unicode_version"; 35 | static NSString * const ZPLEmojiCategoryDictionaryKey = @"category"; 36 | static NSString * const ZPLEmojiFlagsCategoryDictionaryValue = @"Flags"; 37 | 38 | static NSString * const ZPLEmojiFlagTag = @"flag"; 39 | 40 | static const NSOperatingSystemVersion ZPLEmojiUnicode8OperationSystemVersion = {.majorVersion = 10, .minorVersion = 11, .patchVersion = 5}; 41 | static const NSOperatingSystemVersion ZPLEmojiUnicode9OperationSystemVersion = {.majorVersion = 10, .minorVersion = 12, .patchVersion = 2}; 42 | static const NSOperatingSystemVersion ZPLEmojiUnicode10OperationSystemVersion = {.majorVersion = 10, .minorVersion = 13, .patchVersion = 1}; 43 | static const NSOperatingSystemVersion ZPLEmojiUnicode11OperationSystemVersion = {.majorVersion = 10, .minorVersion = 14, .patchVersion = 1}; 44 | static const NSOperatingSystemVersion ZPLEmojiUnicode12OperationSystemVersion = {.majorVersion = 10, .minorVersion = 15, .patchVersion = 1}; 45 | static const NSOperatingSystemVersion ZPLEmojiUnicode13OperationSystemVersion = {.majorVersion = 11, .minorVersion = 3, .patchVersion = 0}; 46 | static const NSOperatingSystemVersion ZPLEmojiUnicode14OperationSystemVersion = {.majorVersion = 12, .minorVersion = 3, .patchVersion = 0}; 47 | 48 | @implementation ZPLEmoji 49 | 50 | + (BOOL)supportsUnicodeVersion:(NSString *)unicodeVersion { 51 | NSMutableArray *supportedUnicodeVersions = [NSMutableArray arrayWithArray:@[@"", @"3.0", @"3.2", @"4.0", @"4.1", @"5.1", @"5.2", @"6.0", @"6.1", @"7.0"]]; 52 | 53 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode8OperationSystemVersion]) { 54 | [supportedUnicodeVersions addObject:@"8.0"]; 55 | } 56 | 57 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode9OperationSystemVersion]) { 58 | [supportedUnicodeVersions addObject:@"9.0"]; 59 | } 60 | 61 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode10OperationSystemVersion]) { 62 | [supportedUnicodeVersions addObject:@"10.0"]; 63 | } 64 | 65 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode11OperationSystemVersion]) { 66 | [supportedUnicodeVersions addObject:@"11.0"]; 67 | } 68 | 69 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode12OperationSystemVersion]) { 70 | [supportedUnicodeVersions addObject:@"12.0"]; 71 | [supportedUnicodeVersions addObject:@"12.1"]; 72 | } 73 | 74 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode13OperationSystemVersion]) { 75 | [supportedUnicodeVersions addObject:@"13.0"]; 76 | [supportedUnicodeVersions addObject:@"13.1"]; 77 | } 78 | 79 | if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ZPLEmojiUnicode14OperationSystemVersion]) { 80 | [supportedUnicodeVersions addObject:@"14.0"]; 81 | } 82 | 83 | return [supportedUnicodeVersions containsObject:unicodeVersion]; 84 | } 85 | 86 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary { 87 | self = [super init]; 88 | if (!self) { 89 | return nil; 90 | } 91 | 92 | NSString *value = (NSString *)[dictionary objectForKey:ZPLEmojiValueDictionaryKey]; 93 | NSArray *aliases = (NSArray *)[dictionary objectForKey:ZPLEmojiAliasesDictionaryKey]; 94 | NSArray *tags = (NSArray *)[dictionary objectForKey:ZPLEmojiTagsDictionaryKey]; 95 | NSString *unicodeVersion = (NSString *)[dictionary objectForKey:ZPLEmojiUnicodeVersionDictionaryKey]; 96 | 97 | if (!value || !aliases || aliases.count == 0 || !unicodeVersion || ![ZPLEmoji supportsUnicodeVersion:unicodeVersion]) { 98 | return nil; 99 | } 100 | 101 | if (!tags) { 102 | tags = [NSArray array]; 103 | } 104 | 105 | NSString *category = (NSString *)[dictionary objectForKey:ZPLEmojiCategoryDictionaryKey]; 106 | 107 | if ([category isEqualToString:ZPLEmojiFlagsCategoryDictionaryValue] && ![tags containsObject:ZPLEmojiFlagTag]) { 108 | tags = [tags arrayByAddingObject:ZPLEmojiFlagTag]; 109 | } 110 | 111 | _value = [value copy]; 112 | _aliases = aliases; 113 | _tags = tags; 114 | 115 | return self; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Models/ZPLSuggestion.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestion.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @class ZPLEmoji; 30 | 31 | @interface ZPLSuggestion : NSObject 32 | 33 | @property (strong, nonatomic) ZPLEmoji *emoji; 34 | @property (copy, nonatomic) NSString *alias; 35 | 36 | - (instancetype)initWithEmoji:(ZPLEmoji *)emoji alias:(NSString *)alias; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Models/ZPLSuggestion.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestion.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLSuggestion.h" 28 | 29 | @implementation ZPLSuggestion 30 | 31 | - (instancetype)initWithEmoji:(ZPLEmoji *)emoji alias:(NSString *)alias { 32 | self = [super init]; 33 | if (!self) { 34 | return nil; 35 | } 36 | 37 | _emoji = emoji; 38 | _alias = [alias copy]; 39 | 40 | return self; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLFocusTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLFocusTableView.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @protocol ZPLFocusTableViewDelegate; 30 | 31 | @interface ZPLFocusTableView : NSTableView 32 | 33 | @end 34 | 35 | @protocol ZPLFocusTableViewDelegate 36 | @optional 37 | 38 | - (void)tableView:(NSTableView *)tableView didClickRow:(NSInteger)row; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLFocusTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLFocusTableView.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLFocusTableView.h" 28 | 29 | @interface ZPLFocusTableView () 30 | 31 | @property (strong, nonatomic) NSTrackingArea *trackingArea; 32 | 33 | @end 34 | 35 | @implementation ZPLFocusTableView 36 | 37 | #pragma mark - NSTableView 38 | 39 | - (void)reloadData { 40 | [super reloadData]; 41 | 42 | if (!self.window) { 43 | return; 44 | } 45 | 46 | [self reloadSelectionWithMouseLocationInWindow:self.window.mouseLocationOutsideOfEventStream]; 47 | } 48 | 49 | #pragma mark - NSResponder 50 | 51 | - (void)updateTrackingAreas { 52 | if (self.trackingArea != nil) { 53 | [self removeTrackingArea:self.trackingArea]; 54 | } 55 | 56 | self.trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingMouseMoved | NSTrackingActiveInActiveApp owner:self userInfo:nil]; 57 | [self addTrackingArea:self.trackingArea]; 58 | 59 | [super updateTrackingAreas]; 60 | } 61 | 62 | - (void)mouseDown:(NSEvent *)event { 63 | NSPoint convertedLocation = [self convertPoint:event.locationInWindow toView:nil]; 64 | NSInteger row = [self rowAtPoint:convertedLocation]; 65 | 66 | id delegate = (id )self.delegate; 67 | if (row >= 0 && [delegate respondsToSelector:@selector(tableView:didClickRow:)]) { 68 | [delegate tableView:self didClickRow:row]; 69 | } 70 | } 71 | 72 | - (void)mouseMoved:(NSEvent *)event { 73 | [self reloadSelectionWithMouseLocationInWindow:event.locationInWindow]; 74 | } 75 | 76 | #pragma mark - Private 77 | 78 | - (void)reloadSelectionWithMouseLocationInWindow:(NSPoint)mouseLocationInWindow { 79 | NSPoint convertedLocation = [self convertPoint:mouseLocationInWindow toView:nil]; 80 | NSInteger row = [self rowAtPoint:convertedLocation]; 81 | 82 | if (row < 0 || self.selectedRow == row) { 83 | return; 84 | } 85 | 86 | BOOL shouldSelect = [self.delegate tableView:self shouldSelectRow:row] ?: YES; 87 | 88 | if (shouldSelect) { 89 | [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; 90 | } 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLRowView.h 3 | // Autocomplete 4 | // 5 | // Created by K. Berk Cebi on 7/9/18. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @interface ZPLRowView : NSTableRowView 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLRowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLRowView.m 3 | // Autocomplete 4 | // 5 | // Created by K. Berk Cebi on 7/9/18. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLRowView.h" 28 | 29 | #import "NSColor+Autocomplete.h" 30 | 31 | @implementation ZPLRowView 32 | 33 | #pragma mark - ZPLRowView 34 | 35 | - (NSBackgroundStyle)interiorBackgroundStyle { 36 | return self.isSelected ? NSBackgroundStyleDark : NSBackgroundStyleLight; 37 | } 38 | 39 | - (void)drawSelectionInRect:(NSRect)dirtyRect { 40 | [[NSColor zpl_selectionColor] setFill]; 41 | 42 | [[NSBezierPath bezierPathWithRect:self.bounds] fill]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLSuggestionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionCell.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @class ZPLSuggestion; 30 | 31 | @interface ZPLSuggestionCell : NSTableCellView 32 | 33 | @property (strong, nonatomic) ZPLSuggestion *suggestion; 34 | 35 | + (CGFloat)height; 36 | + (NSString *)reuseIdentifier; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Autocomplete/Sources/Views/ZPLSuggestionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLSuggestionCell.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLSuggestionCell.h" 28 | 29 | #import "ZPLEmoji.h" 30 | #import "ZPLSuggestion.h" 31 | #import "NSColor+Autocomplete.h" 32 | 33 | static const CGFloat ZPLSuggestionCellHeight = 32.0f; 34 | static const CGFloat ZPLSuggestionCellMargin = 8.0f; 35 | 36 | @interface ZPLSuggestionCell () 37 | 38 | @property (strong, nonatomic) NSTextField *emojiTextField; 39 | @property (strong, nonatomic) NSTextField *aliasTextField; 40 | 41 | @end 42 | 43 | @implementation ZPLSuggestionCell 44 | 45 | + (CGFloat)height { 46 | return ZPLSuggestionCellHeight; 47 | } 48 | 49 | + (NSString *)reuseIdentifier { 50 | return NSStringFromClass([ZPLSuggestionCell class]); 51 | } 52 | 53 | #pragma mark - Initializers 54 | 55 | - (instancetype)initWithFrame:(NSRect)frame { 56 | self = [super initWithFrame:frame]; 57 | if (!self) { 58 | return nil; 59 | } 60 | 61 | self.identifier = [[self class] reuseIdentifier]; 62 | 63 | _emojiTextField = [[NSTextField alloc] init]; 64 | _emojiTextField.bordered = NO; 65 | _emojiTextField.drawsBackground = NO; 66 | _emojiTextField.editable = NO; 67 | _emojiTextField.selectable = NO; 68 | _emojiTextField.usesSingleLineMode = YES; 69 | _emojiTextField.font = [NSFont systemFontOfSize:14.0]; 70 | [_emojiTextField setContentCompressionResistancePriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationHorizontal]; 71 | [_emojiTextField setContentHuggingPriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationHorizontal]; 72 | 73 | _aliasTextField = [[NSTextField alloc] init]; 74 | _aliasTextField.bordered = NO; 75 | _aliasTextField.drawsBackground = NO; 76 | _aliasTextField.editable = NO; 77 | _aliasTextField.selectable = NO; 78 | _aliasTextField.usesSingleLineMode = YES; 79 | _aliasTextField.lineBreakMode = NSLineBreakByTruncatingMiddle; 80 | _aliasTextField.alignment = NSTextAlignmentLeft; 81 | _aliasTextField.font = [NSFont systemFontOfSize:11.0]; 82 | 83 | NSColor *aliasTextFieldTextColor = [NSColor zpl_textColor]; 84 | if (aliasTextFieldTextColor) { 85 | _aliasTextField.textColor = aliasTextFieldTextColor; 86 | } 87 | 88 | [_aliasTextField setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; 89 | [_aliasTextField setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; 90 | 91 | [self addSubview:_emojiTextField]; 92 | [self addSubview:_aliasTextField]; 93 | 94 | self.translatesAutoresizingMaskIntoConstraints = NO; 95 | _emojiTextField.translatesAutoresizingMaskIntoConstraints = NO; 96 | _aliasTextField.translatesAutoresizingMaskIntoConstraints = NO; 97 | 98 | [NSLayoutConstraint activateConstraints:@[ 99 | [_emojiTextField.leftAnchor constraintEqualToAnchor:self.leftAnchor constant:ZPLSuggestionCellMargin], 100 | [_emojiTextField.centerYAnchor constraintEqualToAnchor:self.centerYAnchor], 101 | [_aliasTextField.leftAnchor constraintEqualToAnchor:_emojiTextField.rightAnchor constant:ZPLSuggestionCellMargin], 102 | [_aliasTextField.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:-1.0f * ZPLSuggestionCellMargin], 103 | [_aliasTextField.centerYAnchor constraintEqualToAnchor:self.centerYAnchor] 104 | ]]; 105 | 106 | return self; 107 | } 108 | 109 | #pragma mark - Properties 110 | 111 | - (void)setSuggestion:(ZPLSuggestion *)suggestion { 112 | _suggestion = suggestion; 113 | 114 | self.emojiTextField.stringValue = suggestion.emoji.value; 115 | self.aliasTextField.stringValue = suggestion.alias; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /Autocomplete/Sources/ZPLAutocompletePluginController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLAutocompletePluginController.h 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import 28 | 29 | @interface ZPLAutocompletePluginController : NSObject 30 | 31 | @property (assign, nonatomic, getter=isEnabled) BOOL enabled; 32 | 33 | + (instancetype)sharedController; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Autocomplete/Sources/ZPLAutocompletePluginController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZPLAutocompletePluginController.m 3 | // Autocomplete 4 | // 5 | // Created by Yigitcan Yurtsever on 29.06.2018. 6 | // Copyright © 2018 Zeplin, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #import "ZPLAutocompletePluginController.h" 28 | 29 | #import "ZPLSuggestionController.h" 30 | 31 | @interface ZPLAutocompletePluginController () 32 | 33 | @property (strong, nonatomic) ZPLSuggestionController *suggestionController; 34 | 35 | @end 36 | 37 | @implementation ZPLAutocompletePluginController 38 | 39 | #pragma mark - Singleton 40 | 41 | + (instancetype)sharedController { 42 | static dispatch_once_t once; 43 | static id _sharedInstance = nil; 44 | dispatch_once(&once, ^{ 45 | _sharedInstance = [[self alloc] init]; 46 | }); 47 | 48 | return _sharedInstance; 49 | } 50 | 51 | #pragma mark - Properties 52 | 53 | - (void)setEnabled:(BOOL)enabled { 54 | if (_enabled == enabled) { 55 | return; 56 | } 57 | 58 | _enabled = enabled; 59 | 60 | if (enabled) { 61 | self.suggestionController = [[ZPLSuggestionController alloc] init]; 62 | } else { 63 | self.suggestionController = nil; 64 | } 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zeplin, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Emoji Autocomplete Sketch Plugin 4 | Type `:` followed by the first few letters to autocomplete emojis. 🍒 5 | 6 | ## Installation 7 | - [Download](https://github.com/zeplin/emoji-autocomplete-sketch-plugin/releases/download/v1.3/Emoji.Autocomplete.sketchplugin.zip) latest release, 1.2. 8 | - Unzip, if necessary, and double click `Emoji Autocomplete.sketchplugin`. 9 | 10 | ## Usage 11 | While editing text layers, overrides or layer names, type `:` followed by a few letters to display a list of emojis to autocomplete from. Use the arrow keys or your mouse to select one. 12 | 13 | 14 | 15 | ## License 16 | 17 | Emoji Autocomplete Sketch Plugin is released under the MIT license. See [LICENSE](LICENSE) for details. 18 | 19 | --- 20 | 21 | Zeplin Logo 22 | 23 | Emoji Autocomplete Sketch Plugin is crafted and maintained by the crew behind [Zeplin](https://zeplin.io). Follow [@zeplin](https://twitter.com/zeplin) on Twitter for project updates and releases. 24 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | require "fileutils" 2 | require "net/http" 3 | require "tempfile" 4 | 5 | BUILD_DIR = "build" 6 | FRAMEWORK_NAME = "Autocomplete.framework" 7 | PLUGIN_NAME = "Emoji Autocomplete.sketchplugin" 8 | 9 | default_platform(:mac) 10 | 11 | platform :mac do 12 | desc "Install dependencies" 13 | lane :bootstrap do 14 | emojis_uri = URI("https://raw.githubusercontent.com/kardeslik/gemoji/import-emoji-14/db/emoji.json") 15 | emojis_string = Net::HTTP.get(emojis_uri) 16 | 17 | emojis_tempfile = Tempfile.new("emojis") 18 | emojis_tempfile.write(emojis_string) 19 | 20 | resources_dir = File.join("..", "Autocomplete", "Resources") 21 | FileUtils.mkdir_p(resources_dir) 22 | 23 | emojis_path = File.join(resources_dir, "Emojis.plist") 24 | 25 | sh("plutil -convert xml1 #{emojis_tempfile.path} -o #{emojis_path}") 26 | 27 | emojis_tempfile.unlink 28 | end 29 | 30 | desc "Build framework" 31 | lane :build do 32 | xcodebuild( 33 | clean: true, 34 | build: true, 35 | scheme: "Autocomplete", 36 | configuration: "Release", 37 | xcargs: "CONFIGURATION_BUILD_DIR=#{BUILD_DIR}" 38 | ) 39 | 40 | File.expand_path(File.join("..", BUILD_DIR)) 41 | end 42 | 43 | desc "Package plugin, building framework unless provided" 44 | lane :package do |options| 45 | build_dir = options[:build_dir] 46 | unless build_dir 47 | build_dir = build 48 | end 49 | 50 | plugin_dir = File.join(build_dir, PLUGIN_NAME) 51 | FileUtils.rm_rf(plugin_dir) 52 | 53 | plugin_compressed_path = "#{plugin_dir}.zip" 54 | FileUtils.rm_f(plugin_compressed_path) 55 | 56 | FileUtils.mkdir_p(plugin_dir) 57 | 58 | contents_dir = File.join(plugin_dir, "Contents") 59 | FileUtils.mkdir_p(contents_dir) 60 | 61 | resources_dir = File.join(contents_dir, "Resources") 62 | FileUtils.mkdir_p(resources_dir) 63 | 64 | framework_dir = File.join(build_dir, FRAMEWORK_NAME) 65 | FileUtils.copy_entry(framework_dir, File.join(resources_dir, FRAMEWORK_NAME)) 66 | 67 | FileUtils.cp("../plugin/img/icon.png", resources_dir) 68 | FileUtils.cp("../plugin/img/iconRunner.png", resources_dir) 69 | FileUtils.cp("../plugin/img/iconRunnerDark.png", resources_dir) 70 | 71 | sketch_dir = File.join(contents_dir, "Sketch") 72 | FileUtils.mkdir_p(sketch_dir) 73 | 74 | FileUtils.cp("../plugin/manifest.json", sketch_dir) 75 | FileUtils.cp("../plugin/src/index.js", sketch_dir) 76 | 77 | sh("ditto -c -k --rsrc --keepParent \"#{plugin_dir}\" \"#{plugin_compressed_path}\"") 78 | 79 | plugin_dir 80 | end 81 | 82 | desc "Package and install plugin to Sketch" 83 | lane :install do |options| 84 | package_plugin_dir = package(options) 85 | plugin_dir = File.join(Dir.home, "Library", "Application Support", "com.bohemiancoding.sketch3", "Plugins", PLUGIN_NAME) 86 | 87 | FileUtils.rm_f(plugin_dir) 88 | FileUtils.copy_entry(package_plugin_dir, plugin_dir) 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## Mac 19 | ### mac bootstrap 20 | ``` 21 | fastlane mac bootstrap 22 | ``` 23 | Install dependencies 24 | ### mac build 25 | ``` 26 | fastlane mac build 27 | ``` 28 | Build framework 29 | ### mac package 30 | ``` 31 | fastlane mac package 32 | ``` 33 | Package plugin, building framework unless provided 34 | ### mac install 35 | ``` 36 | fastlane mac install 37 | ``` 38 | Package and install plugin to Sketch 39 | 40 | ---- 41 | 42 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 43 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 44 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 45 | -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/1bdb1aa3298fee26ca283aa22cdf2bb734da160e/img/demo.gif -------------------------------------------------------------------------------- /img/iconTransparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/1bdb1aa3298fee26ca283aa22cdf2bb734da160e/img/iconTransparent.png -------------------------------------------------------------------------------- /img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /plugin/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/1bdb1aa3298fee26ca283aa22cdf2bb734da160e/plugin/img/icon.png -------------------------------------------------------------------------------- /plugin/img/iconRunner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/1bdb1aa3298fee26ca283aa22cdf2bb734da160e/plugin/img/iconRunner.png -------------------------------------------------------------------------------- /plugin/img/iconRunnerDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/1bdb1aa3298fee26ca283aa22cdf2bb734da160e/plugin/img/iconRunnerDark.png -------------------------------------------------------------------------------- /plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Emoji Autocomplete", 3 | "description": "Type “:” followed by the first few letters to autocomplete emojis. 🍒", 4 | "author": "Zeplin, Inc.", 5 | "authorEmail": "support@zeplin.io", 6 | "homepage": "https://github.com/zeplin/emoji-autocomplete-sketch-plugin", 7 | "version": "1.3", 8 | "identifier": "io.zeplin.sketchPlugin.autocomplete", 9 | "appcast": "https://raw.githubusercontent.com/zeplin/emoji-autocomplete-sketch-plugin/master/.appcast.xml", 10 | "icon": "icon.png", 11 | "compatibleVersion": 3, 12 | "bundleVersion": 1, 13 | "commands": [ 14 | { 15 | "name": "Handle Actions", 16 | "identifier": "handle-actions", 17 | "script": "index.js", 18 | "handlers": { 19 | "actions": { 20 | "Startup": "onStartup", 21 | "Shutdown": "onShutdown" 22 | } 23 | } 24 | }, 25 | { 26 | "name": "About Emoji Autocomplete", 27 | "identifier": "about", 28 | "script": "index.js", 29 | "handler": "onSelectAboutMenuItem", 30 | "icon": "iconRunner.png", 31 | "iconDark": "iconRunnerDark.png", 32 | "description": "Learn more about the Emoji Autocomplete plugin." 33 | } 34 | ], 35 | "menu": { 36 | "items": [ 37 | "about" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugin/src/index.js: -------------------------------------------------------------------------------- 1 | var REPOSITORY_URL_STRING = "https://github.com/zeplin/emoji-autocomplete-sketch-plugin"; 2 | 3 | /** 4 | * Handles startup action. 5 | */ 6 | function onStartup(context) { 7 | if (!isFrameworkLoaded()) { 8 | var contentsPath = context.scriptPath.stringByDeletingLastPathComponent().stringByDeletingLastPathComponent(); 9 | var resourcesPath = contentsPath.stringByAppendingPathComponent("Resources"); 10 | 11 | var result = Mocha.sharedRuntime().loadFrameworkWithName_inDirectory("Autocomplete", resourcesPath); 12 | if (!result) { 13 | var alert = NSAlert.alloc().init(); 14 | alert.alertStyle = NSAlertStyleCritical; 15 | alert.messageText = "Loading framework for “Emoji Autocomplete” failed" 16 | alert.informativeText = "Please try disabling and enabling the plugin or restarting Sketch. Contact support@zeplin.io, if the issue continues." 17 | 18 | alert.runModal(); 19 | 20 | return; 21 | } 22 | } 23 | 24 | ZPLAutocompletePluginController.sharedController().enabled = true; 25 | } 26 | 27 | /** 28 | * Handles shutdown action. 29 | */ 30 | function onShutdown(context) { 31 | if (isFrameworkLoaded()) { 32 | ZPLAutocompletePluginController.sharedController().enabled = false; 33 | } 34 | } 35 | 36 | function isFrameworkLoaded() { 37 | return Boolean(NSClassFromString("ZPLAutocompletePluginController")); 38 | } 39 | 40 | /** 41 | * Handles about menu item. 42 | */ 43 | function onSelectAboutMenuItem(context) { 44 | var repositoryUrl = NSURL.URLWithString(REPOSITORY_URL_STRING); 45 | 46 | NSWorkspace.sharedWorkspace().openURL(repositoryUrl); 47 | } 48 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Change description 2 | 3 | > Description here 4 | 5 | ## Type of change 6 | - [ ] Bug fix (fixes an issue) 7 | - [ ] New feature (adds functionality) 8 | 9 | ## Related issues 10 | 11 | > Fix [#1]() 12 | 13 | ## Checklists 14 | 15 | ### Development 16 | 17 | - [ ] Lint rules pass locally 18 | - [ ] Application changes have been tested thoroughly 19 | - [ ] Automated tests covering modified code pass 20 | 21 | ### Security 22 | 23 | - [ ] Security impact of change has been considered 24 | - [ ] Code follows company security practices and guidelines 25 | 26 | ### Code review 27 | 28 | - [ ] Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary 29 | - [ ] "Ready for review" label attached and reviewers assigned 30 | - [ ] Changes have been reviewed by at least one other contributor 31 | - [ ] Pull request linked to task tracker where applicable 32 | --------------------------------------------------------------------------------