├── HandyTools.sketchplugin └── Contents │ └── Sketch │ ├── .idea │ ├── .name │ ├── Sketch.iml │ ├── encodings.xml │ ├── misc.xml │ ├── modules.xml │ └── workspace.xml │ ├── algin.js │ ├── hidden_others.js │ ├── libs │ └── common.js │ ├── manifest.json │ ├── space.js │ ├── unhidden_all.js │ └── unhidden_all_artboard.js ├── README.md └── Screenshot ├── AlignTo.gif ├── dir_info.png ├── hidden.png └── spacing.gif /HandyTools.sketchplugin/Contents/Sketch/.idea/.name: -------------------------------------------------------------------------------- 1 | Sketch -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/.idea/Sketch.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | true 86 | 87 | 88 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 127 | 128 | 129 | 130 | 133 | 134 | 137 | 138 | 139 | 140 | 143 | 144 | 147 | 148 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 1449326249031 165 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 195 | 196 | 197 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/algin.js: -------------------------------------------------------------------------------- 1 | @import 'libs/common.js' 2 | 3 | var lastDir = getConfig('last_algin_dir'); 4 | lastDir = lastDir == null ? 1 : parseInt(lastDir); 5 | 6 | function disableCell(cell) 7 | { 8 | [cell setEnabled:false] 9 | [cell setTransparent:true] 10 | } 11 | 12 | function qq(cellArr,idxArr,call) 13 | { 14 | for (var i= 0;i 0){ 12 | processAllLayers(g_layers,function(layer){ 13 | [layer setIsVisible:false]; 14 | }); 15 | } 16 | 17 | function processAllLayers(layers,callback) { 18 | for (var i = 0; i < [layers count]; i++) { 19 | var layer = [layers objectAtIndex:i]; 20 | var isSkip = [selection indexOfObject:layer] < MAX_COUNT 21 | if (isGroup(layer)) { 22 | if (isSkip) continue; 23 | processAllLayers([layer layers], callback); 24 | }else{ 25 | if (!isSkip) callback(layer); 26 | } 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/libs/common.js: -------------------------------------------------------------------------------- 1 | // const page = [doc currentPage], 2 | // artboard = [page currentArtboard], 3 | // artboards = [doc artboards], 4 | // selection = context.selection, 5 | // doc = context.document, 6 | // current = artboard ? artboard : page, 7 | const prefix = 'kiny', 8 | measureRegEx = /\$SIZE|\$WIDTH|\$HEIGHT|\$DISTANCE|\$PROPERTY|\$LABEL|\$OVERLAYER|\$COORDINATE/; 9 | 10 | 11 | function absRect2CGRect(rect) 12 | { 13 | return CGRectMake(rect.x(),rect.y(),rect.width(),rect.height()) 14 | } 15 | 16 | function isMeasure(layer) 17 | { 18 | return measureRegEx.exec([layer name]); 19 | } 20 | 21 | function is(layer, theClass){ 22 | var cls = [layer class]; 23 | return cls === theClass; 24 | } 25 | 26 | function isPage(layer){ 27 | return is(layer, MSPage); 28 | } 29 | 30 | function isGroup(layer){ 31 | return is(layer, MSLayerGroup); 32 | } 33 | 34 | function isText(layer){ 35 | return is(layer, MSTextLayer); 36 | } 37 | 38 | function isShape(layer){ 39 | return is(layer, MSShapeGroup); 40 | } 41 | 42 | function getConfig(key) { 43 | var defaults = [NSUserDefaults standardUserDefaults]; 44 | return [defaults objectForKey: '-' + prefix + '-' + key]; 45 | } 46 | 47 | function setConfig(key, value) { 48 | var defaults = [NSUserDefaults standardUserDefaults], 49 | configs = [NSMutableDictionary dictionary]; 50 | [configs setObject: value forKey: '-' + prefix + '-' + key] 51 | return [defaults registerDefaults: configs]; 52 | } 53 | 54 | function toJSArray(arr) { 55 | var len = [arr count], 56 | res = []; 57 | while(len--){ 58 | res.push(arr[len]); 59 | } 60 | return res; 61 | } 62 | 63 | function createLabel(text, frame ,editable) { 64 | editable = editable || false; 65 | var label = [[NSTextField alloc] initWithFrame:frame]; 66 | [label setStringValue:text]; 67 | [label setFont:[NSFont boldSystemFontOfSize:12]]; 68 | [label setBezeled:false]; 69 | [label setDrawsBackground:editable]; 70 | [label setEditable:editable]; 71 | [label setSelectable:editable]; 72 | return label; 73 | } -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Handy Tools", 3 | "description": "Some handy tools for Sketch", 4 | "author": "webpatch", 5 | "homepage": "https://github.com/webpatch/Handy-Tools", 6 | "version": 1.1, 7 | "identifier": "com.hikiny.sketch.handytools", 8 | "compatibleVersion": 3, 9 | "commands": [ 10 | { 11 | "name": "Algin to...", 12 | "identifier": "algin", 13 | "shortcut": "ctrl shift a", 14 | "script": "algin.js", 15 | "handler": "onRun" 16 | }, 17 | { 18 | "name": "Space...", 19 | "identifier": "space", 20 | "shortcut": "ctrl shift s", 21 | "script": "space.js", 22 | "handler": "onRun" 23 | }, 24 | { 25 | "name": "Hidden others", 26 | "identifier": "hideOthers", 27 | "shortcut": "", 28 | "script": "hidden_others.js", 29 | "handler": "onRun" 30 | }, 31 | { 32 | "name": "Unhidden all", 33 | "identifier": "unhiddenAll", 34 | "shortcut": "", 35 | "script": "unhidden_all.js", 36 | "handler": "onRun" 37 | }, 38 | { 39 | "name": "Unhidden all Artboards", 40 | "identifier": "unhiddenAllArtboards", 41 | "shortcut": "", 42 | "script": "unhidden_all_artboard.js", 43 | "handler": "onRun" 44 | } 45 | ], 46 | "menu": { 47 | "items": [ 48 | "algin", 49 | "space", 50 | { 51 | "title": "Hidden & Unhidden", 52 | "items": [ 53 | "hideOthers", 54 | "unhiddenAll", 55 | "unhiddenAllArtboards" 56 | ] 57 | } 58 | ], 59 | "title": "Handy Tools" 60 | } 61 | } -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/space.js: -------------------------------------------------------------------------------- 1 | @import 'libs/common.js' 2 | 3 | var lastDir = getConfig('last_spacing_dir'); 4 | lastDir = lastDir == null ? 1 : parseInt(lastDir) 5 | 6 | function createAlert(msg) 7 | { 8 | var viewBox = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 320, 40)]; 9 | [viewBox addSubview:createLabel("Direction:",NSMakeRect(0, 1, 70, 25),false)]; 10 | 11 | var prototype = [[NSButtonCell alloc] init]; 12 | [prototype setBezelStyle: NSRegularSquareBezelStyle]; 13 | [prototype setButtonType:NSPushOnPushOffButton]; 14 | var myMatrix = [[NSMatrix alloc] initWithFrame:NSMakeRect(65, 0, 260, 35) 15 | mode:NSRadioModeMatrix 16 | prototype:prototype 17 | numberOfRows:1 18 | numberOfColumns:2]; 19 | var cellArray = [myMatrix cells]; 20 | [myMatrix setCellSize:CGSizeMake(40,35)]; 21 | [myMatrix selectCellAtRow:0 column:lastDir]; 22 | [[cellArray objectAtIndex:0] setTitle:"⬌"]; 23 | [[cellArray objectAtIndex:1] setTitle:"⬍"]; 24 | [viewBox addSubview:myMatrix]; 25 | 26 | [viewBox addSubview:createLabel("Spacing:",NSMakeRect(160, 1, 60, 25),false)]; 27 | 28 | var input = createLabel("10",NSMakeRect(225, 6, 90, 22),true); 29 | [input setFont:[NSFont systemFontOfSize:17]]; 30 | [viewBox addSubview:input]; 31 | 32 | var alert = [[NSAlert alloc] init]; 33 | [alert setMessageText:msg]; 34 | [alert addButtonWithTitle:"OK"]; 35 | [alert addButtonWithTitle:"Cancel"]; 36 | [alert setAccessoryView:viewBox]; 37 | 38 | var responseCode = [alert runModal]; 39 | var sel = [input integerValue]; 40 | var dir = [[myMatrix selectedCell] title].trim(); 41 | return [responseCode, sel,dir]; 42 | } 43 | 44 | function sort_by_position_left(a,b){ 45 | return [[a absoluteRect] x] - [[b absoluteRect] x]; 46 | } 47 | 48 | function sort_by_position_top(a,b){ 49 | return [[a absoluteRect] y] - [[b absoluteRect] y]; 50 | } 51 | 52 | function horizontalSpacing(spacing,selection) 53 | { 54 | var sorted_selection = toJSArray(selection).sort(sort_by_position_left), 55 | first_element = sorted_selection[0], 56 | left_position = [[first_element absoluteRect] x]; 57 | 58 | sorted_selection.forEach(function(layer){ 59 | var parentRect = [[layer parentGroup] absoluteRect]; 60 | [[layer frame] setX:left_position-parentRect.x()]; 61 | var f = [layer absoluteRect] 62 | left_position = f.x() + f.width() + spacing; 63 | }); 64 | } 65 | 66 | function verticallSpacing(spacing,selection) 67 | { 68 | var sorted_selection = toJSArray(selection).sort(sort_by_position_top), 69 | first_element = sorted_selection[0], 70 | top_position = [[first_element absoluteRect] y]; 71 | 72 | sorted_selection.forEach(function(layer){ 73 | var parentRect = [[layer parentGroup] absoluteRect]; 74 | [[layer frame] setY:top_position-parentRect.y()]; 75 | var f = [layer absoluteRect] 76 | top_position = f.y() + f.height() + spacing; 77 | }); 78 | } 79 | 80 | var onRun = function(context) { 81 | var doc = context.document; 82 | var selection = context.selection; 83 | 84 | if([selection count] < 2){ 85 | [doc showMessage:"Please select 2 or more layers."]; 86 | }else { 87 | var choice = createAlert("Layers Spacing"); 88 | if (choice[0] == 1000) 89 | { 90 | var spacing = choice[1]; 91 | switch(choice[2]){ 92 | case "⬌": 93 | horizontalSpacing(spacing,selection); 94 | setConfig('last_spacing_dir',0); 95 | break; 96 | case "⬍": 97 | verticallSpacing(spacing,selection); 98 | setConfig('last_spacing_dir',1); 99 | break; 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/unhidden_all.js: -------------------------------------------------------------------------------- 1 | @import 'libs/common.js' 2 | 3 | var onRun = function(context){ 4 | const selection = context.selection, 5 | doc = context.document, 6 | page = [doc currentPage], 7 | artboard = [page currentArtboard], 8 | layers = [artboard layers]; 9 | 10 | function processAllLayers(layers, callback) { 11 | for (var i = 0; i < [layers count]; i++) { 12 | var layer = [layers objectAtIndex:i]; 13 | if (isMeasure(layer)) continue; 14 | if ([layer isMemberOfClass:[MSLayerGroup class]]) { 15 | callback(layer); 16 | processAllLayers([layer layers], callback); 17 | } 18 | else { 19 | callback(layer); 20 | } 21 | } 22 | } 23 | 24 | processAllLayers(layers,function(layer){ 25 | if(![layer isVisible])[layer setIsVisible:true]; 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /HandyTools.sketchplugin/Contents/Sketch/unhidden_all_artboard.js: -------------------------------------------------------------------------------- 1 | @import 'libs/common.js' 2 | 3 | var onRun = function(context){ 4 | const doc = context.document, 5 | artboards = [doc artboards]; 6 | 7 | function processAllLayers(layers, callback) { 8 | for (var i = 0; i < [layers count]; i++) { 9 | var layer = [layers objectAtIndex:i]; 10 | if (isMeasure(layer)) continue; 11 | if ([layer isMemberOfClass:[MSLayerGroup class]]) { 12 | callback(layer); 13 | processAllLayers([layer layers], callback); 14 | }else { 15 | callback(layer); 16 | } 17 | } 18 | } 19 | 20 | for (var j = 0; j < [artboards count]; j++){ 21 | var artboard = [artboards objectAtIndex:j]; 22 | var layers = [artboard layers]; 23 | processAllLayers(layers,function(layer){ 24 | if(![layer isVisible]) [layer setIsVisible:true]; 25 | }) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Handy-Tools 2 | Some handy tools for Sketch 3 | 4 | ## Require 5 | Sketch 3.3+ 6 | 7 | ## Changelog 8 | ### v1.1 9 | * Solved multi-level layer's align and space problem 10 | 11 | ## Installing Plugin 12 | 13 | 1. [Download Plugin](https://github.com/webpatch/Handy-Tools/releases/download/v1.1/HandyTools.sketchplugin.zip) 14 | 2. Extract the zip file, dobule click the "HandyTools.sketchplugin", it will install automatic. 15 | 3. You can find these tools in Sketch's "plugins" menu 16 | 17 | ## Feature 18 | ### Align To Key Object 19 | ![AlignTo Gif](https://github.com/webpatch/Handy-Tools/raw/master/Screenshot/AlignTo.gif) 20 | 21 | Align Direction 22 | 23 | ![Direction Info](https://github.com/webpatch/Handy-Tools/raw/master/Screenshot/dir_info.png) 24 | 25 | ### Equal Spacing 26 | ![spacing gif](https://raw.githubusercontent.com/webpatch/Handy-Tools/master/Screenshot/spacing.gif) 27 | 28 | ### Hidden/Unhidden 29 | 30 | ![hidden preview](https://raw.githubusercontent.com/webpatch/Handy-Tools/master/Screenshot/hidden.png) 31 | 32 | * Hidden other layers (exclude selected layers) 33 | * Unhidden all layers 34 | * Unhidden all artboard's layers 35 | -------------------------------------------------------------------------------- /Screenshot/AlignTo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpatch/Handy-Tools/cf08b8de9ec81d1cc75f5ae4d30deb2e7b3912fe/Screenshot/AlignTo.gif -------------------------------------------------------------------------------- /Screenshot/dir_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpatch/Handy-Tools/cf08b8de9ec81d1cc75f5ae4d30deb2e7b3912fe/Screenshot/dir_info.png -------------------------------------------------------------------------------- /Screenshot/hidden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpatch/Handy-Tools/cf08b8de9ec81d1cc75f5ae4d30deb2e7b3912fe/Screenshot/hidden.png -------------------------------------------------------------------------------- /Screenshot/spacing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpatch/Handy-Tools/cf08b8de9ec81d1cc75f5ae4d30deb2e7b3912fe/Screenshot/spacing.gif --------------------------------------------------------------------------------