├── Example.sketch ├── docs ├── banner@2x.png ├── example@2x.png ├── native@2x.png ├── runner@2x.png ├── updatelayout.gif └── editconstraints.gif ├── .gitignore ├── Sketch Constraints.sketchplugin └── Contents │ ├── Resources │ ├── icon@2x.png │ ├── settings@2x.png │ ├── constraints@2x.png │ ├── updateLayout@2x.png │ └── editConstraints@2x.png │ └── Sketch │ ├── manifest.json │ ├── main.cocoascript │ └── utils.js ├── LICENSE ├── .appcast.xml └── README.md /Example.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Example.sketch -------------------------------------------------------------------------------- /docs/banner@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/banner@2x.png -------------------------------------------------------------------------------- /docs/example@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/example@2x.png -------------------------------------------------------------------------------- /docs/native@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/native@2x.png -------------------------------------------------------------------------------- /docs/runner@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/runner@2x.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ._* 4 | .Spotlight-V100 5 | .Trashes 6 | ehthumbs.db 7 | Thumbs.db 8 | -------------------------------------------------------------------------------- /docs/updatelayout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/updatelayout.gif -------------------------------------------------------------------------------- /docs/editconstraints.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/docs/editconstraints.gif -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Resources/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Sketch Constraints.sketchplugin/Contents/Resources/icon@2x.png -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Resources/settings@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Sketch Constraints.sketchplugin/Contents/Resources/settings@2x.png -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Resources/constraints@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Sketch Constraints.sketchplugin/Contents/Resources/constraints@2x.png -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Resources/updateLayout@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Sketch Constraints.sketchplugin/Contents/Resources/updateLayout@2x.png -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Resources/editConstraints@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcbouchenoire/sketch-constraints/HEAD/Sketch Constraints.sketchplugin/Contents/Resources/editConstraints@2x.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Marc Bouchenoire 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 | 23 | -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sketch Constraints", 3 | "description": 4 | "A plugin that integrates constraints in Sketch to lay out layers.", 5 | "author": "Marc Bouchenoire", 6 | "authorEmail": "mail@marcbouchenoire.com", 7 | "homepage": "https://github.com/bouchenoiremarc/Sketch-Constraints", 8 | "version": "1.0.2", 9 | "identifier": "com.bouchenoiremarc.sketch-constraints", 10 | "appcast": 11 | "https://raw.githubusercontent.com/bouchenoiremarc/Sketch-Constraints/master/.appcast.xml", 12 | "icon": "settings@2x.png", 13 | "commands": [ 14 | { 15 | "script": "main.cocoascript", 16 | "shortcut": "cmd e", 17 | "name": "Edit Constraints", 18 | "identifier": "editConstraints", 19 | "description": "Set constraints on the current layer.", 20 | "icon": "editConstraints@2x.png" 21 | }, 22 | { 23 | "script": "main.cocoascript", 24 | "handler": "updateLayout", 25 | "shortcut": "cmd l", 26 | "name": "Update Layout", 27 | "identifier": "updateLayout", 28 | "description": "Apply every constraints in the document.", 29 | "icon": "updateLayout@2x.png" 30 | } 31 | ], 32 | "menu": { 33 | "items": ["editConstraints", "updateLayout"] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sketch Constraints 5 | https://github.com/bouchenoiremarc/Sketch-Constraints 6 | A plugin that integrates constraints in Sketch to lay out layers. 7 | en 8 | 9 | Sketch Constraints 1.0.2 10 | 11 | 13 |
  • New icon for Sketch 50+
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 | 20 | Sketch Constraints 1.0.1 21 | 22 | 24 |
  • Added Sketch Runner integration
  • 25 | 26 | ]]> 27 |
    28 | 29 |
    30 | 31 | Sketch Constraints 1.0 32 | 33 | 34 |
    35 |
    36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📏 Sketch Constraints 2 | 3 | ![Banner](docs/banner@2x.png) 4 | 5 | Sketch Constraints is a plugin that integrates constraints in Sketch to lay out layers. These constraints are relative to the parent, either a group or an artboard. 6 | 7 | [Constraints are now available natively in Sketch. 🎉](#native) 8 | 9 | ## Usage 10 | 11 | ### Edit Constraints `⌘ + E` 12 | 13 | Edit Constraints example 14 | 15 | ### Update Layout `⌘ + L` 16 | 17 | Update Layout example 18 | 19 | ### Example 20 | 21 | iOS 9 Lockscreen example 22 | 23 | [👀 Watch on Vimeo](https://vimeo.com/140962822) 24 | 25 | ## Installation 26 | 27 | ### Using Sketch Runner 28 | 29 | With Sketch Runner, just go to the `install` command and search for `Sketch Constraints`. Runner allows you to manage plugins and do much more to speed up your workflow in Sketch. [Download Runner here](http://www.sketchrunner.com). 30 | 31 | Install Sketch Constraints with Sketch Runner 32 | 33 | ### Manually 34 | 35 | Make sure you have the latest version of Sketch installed. **(Sketch 40+)** 36 | 37 | 1. [Download the ZIP file of this repository](https://github.com/bouchenoiremarc/Sketch-Constraints/archive/master.zip) 38 | 2. Double click on `Sketch Constraints.sketchplugin` 39 | 40 | ## Notes 41 | 42 | * **Constraints are not relative to other layers, only to the parent.** 43 | * `Update Layout` updates every artboard of the current page. 44 | * When a group is resized, all the children layers are resized. If you want a child layer to keep its size, check `Width` and/or `Height`. 45 | 46 | ## Inspiration 47 | 48 | * This [Medium article](https://medium.com/bridge-collection/modern-design-tools-adaptive-layouts-e236070856e3) from [Josh Puckett](https://twitter.com/joshpuckett). 49 | * [Bind](https://github.com/almonk/Bind) from [Alasdair Monk](https://twitter.com/almonk). 50 | * [Sketch Flex Layout](https://github.com/hrescak/Sketch-Flex-Layout) from [Matej Hrescak](https://twitter.com/mhrescak). 51 | 52 | ## Native 53 | 54 | As of [Version 44](https://sketchapp.com/updates/#version-44), constraints are available natively in Sketch. 🎉 It's similar to Sketch Constraints except that the constraints are based on current value **only**. 55 | 56 | Sketch 44 native constraints 57 | 58 | ## License 59 | 60 | Sketch Constraints is released under the MIT license. See [LICENSE](LICENSE) for details. 61 | -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Sketch/main.cocoascript: -------------------------------------------------------------------------------- 1 | @import "utils.js" 2 | 3 | var updateLayout = function(context) { 4 | // Global variables 5 | initContext(context) 6 | // Get all artboards in the current page 7 | var artboards = page.artboards(), 8 | artboardLoop = artboards.objectEnumerator() 9 | // Create a loop which iterates through every artboard 10 | while (artboard = artboardLoop.nextObject()) { 11 | // Get all layers from the current artboard in the loop 12 | var layers = artboard.layers(), 13 | layerLoop = layers.objectEnumerator() 14 | loopThrough(layerLoop, function(layer) { 15 | var constraintsForLayer = command.valueForKey_onLayer(constraintsKey, layer) 16 | processLayer(layer, JSON.parse(constraintsForLayer)) 17 | }) 18 | } 19 | } 20 | 21 | var onRun = function(context) { 22 | // Global variables 23 | initContext(context) 24 | // "Edit Constraints" window 25 | function editConstraints() { 26 | // Get the current layer 27 | var currentLayer = (selection.count() > 0) ? selection[0] : false 28 | // Check if constraints can be applied to the current layer 29 | if (error(selection, currentLayer, artboard)) { 30 | return 31 | } 32 | // Get constraints and create window 33 | var constraintsForLayer = command.valueForKey_onLayer(constraintsKey, currentLayer) 34 | var window = createWindow(currentLayer), 35 | alert = window[0], 36 | inputs = window[1] 37 | // Fill inputs with current values if the current layer already has constraints 38 | if (constraintsForLayer) { 39 | var constraintsContent = JSON.parse(constraintsForLayer) 40 | layerConstraints = fillInputs(constraintsContent, inputs) 41 | } 42 | // Get response from the window 43 | var response = alert.runModal() 44 | // Store values from inputs 45 | var values = { 46 | top: getStringValue(inputs[6]), 47 | right: getStringValue(inputs[7]), 48 | bottom: getStringValue(inputs[8]), 49 | left: getStringValue(inputs[9]), 50 | horizontally: getStateValue(inputs[4]), 51 | vertically: getStateValue(inputs[5]), 52 | width: (Number(inputs[0].state()) && !Number(inputs[10].state())) ? getStringValue(inputs[1]) : null, 53 | widthProportion: (Number(inputs[0].state()) && Number(inputs[10].state())) ? getStringValue(inputs[1]) : null, 54 | height: (Number(inputs[2].state()) && !Number(inputs[11].state())) ? getStringValue(inputs[3]) : null, 55 | heightProportion: (Number(inputs[2].state()) && Number(inputs[11].state())) ? getStringValue(inputs[3]) : null 56 | } 57 | // Delete null values 58 | for (var key in values) { 59 | if (values[key] == null) delete values[key] 60 | } 61 | // If "OK" button is pressed 62 | if (response == "1000") { 63 | updateConstraintsForLayer(currentLayer, values) 64 | // Update Layout 65 | updateLayout(context) 66 | } 67 | // If "Cancel" button is pressed and the current layer already has constraints 68 | else if (response == "1001" && layerConstraints) { 69 | updateConstraintsForLayer(currentLayer, values) 70 | } 71 | // If "Remove Constraints" button is pressed 72 | else if (response == "1002") { 73 | // Update constraints if the current layer already has constraints 74 | if (layerConstraints) updateConstraintsForLayer(currentLayer, values) 75 | // Relaunch window 76 | editConstraints() 77 | } 78 | } 79 | // Launch window 80 | editConstraints() 81 | } 82 | -------------------------------------------------------------------------------- /Sketch Constraints.sketchplugin/Contents/Sketch/utils.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------// 2 | // Context // 3 | //--------------------------------------// 4 | 5 | var app = NSApplication.sharedApplication(), 6 | selection, 7 | plugin, 8 | command, 9 | doc, 10 | page, 11 | artboard, 12 | resourcesPath = "constraints@2x.png", 13 | constraintsKey = "@constraints" 14 | 15 | function initContext(context) { 16 | doc = context.document, 17 | plugin = context.plugin, 18 | command = context.command, 19 | page = doc.currentPage(), 20 | artboard = page.currentArtboard(), 21 | selection = context.selection 22 | } 23 | 24 | //--------------------------------------// 25 | // Warnings // 26 | //--------------------------------------// 27 | 28 | function error(selection, currentLayer, artboard) { 29 | if (!currentLayer) { 30 | showMessage("📏 No layers selected.") 31 | return true 32 | } else if (selection.count() > 1) { 33 | showMessage("📏 You can only select one layer.") 34 | return true 35 | } else if (!artboard) { 36 | showMessage("📏 The layer has to be in an artboard.") 37 | return true 38 | } else if (is.artboard(currentLayer)) { 39 | showMessage("📏 You can't put constraints on artboards.") 40 | return true 41 | } 42 | } 43 | 44 | //--------------------------------------// 45 | // JS // 46 | //--------------------------------------// 47 | 48 | function exists(el) { 49 | var value = false 50 | if (typeof el !== "undefined" && el !== null) { 51 | value = true 52 | } 53 | 54 | return value 55 | } 56 | 57 | //--------------------------------------// 58 | // Layers // 59 | //--------------------------------------// 60 | 61 | function loopThrough(layerLoop, callback) { 62 | while (layer = layerLoop.nextObject()) { 63 | if (is.group(layer)) { 64 | var layers = layer.layers(), 65 | layersInsideLoop = layers.objectEnumerator() 66 | loopThrough(layersInsideLoop, callback) 67 | } else { 68 | callback(layer) 69 | } 70 | } 71 | } 72 | 73 | //--------------------------------------// 74 | // Layer Types // 75 | //--------------------------------------// 76 | 77 | var is = { 78 | it: function (layer, layerClass) { 79 | return layer.class() === layerClass 80 | }, 81 | page : function (layer) { 82 | return is.it(layer, MSPage) 83 | }, 84 | artboard : function (layer) { 85 | return is.it(layer, MSArtboardGroup) 86 | }, 87 | group : function (layer) { 88 | return is.it(layer, MSLayerGroup) 89 | }, 90 | text : function (layer) { 91 | return is.it(layer, MSTextLayer) 92 | }, 93 | shape : function (layer) { 94 | return is.it(layer, MSShapeGroup) 95 | } 96 | } 97 | 98 | //--------------------------------------// 99 | // Constraints // 100 | //--------------------------------------// 101 | 102 | function updateConstraintsForLayer(layer, content) { 103 | var formattedContent = JSON.stringify(content, null, "\t") 104 | command.setValue_forKey_onLayer(formattedContent, constraintsKey, layer) 105 | } 106 | 107 | //--------------------------------------// 108 | // Get Size & Position // 109 | //--------------------------------------// 110 | 111 | var getSize = { 112 | width : function(layer) { 113 | return layer.frame().width() 114 | }, 115 | widthProportion : function(layer) { 116 | return layer.frame().width()/layer.parentGroup().frame().width() 117 | }, 118 | height : function(layer) { 119 | return layer.frame().height() 120 | }, 121 | heightProportion : function(layer) { 122 | return layer.frame().height()/layer.parentGroup().frame().height() 123 | }, 124 | } 125 | 126 | var getPosition = { 127 | top : function (layer) { 128 | return layer.frame().y() 129 | }, 130 | left : function (layer) { 131 | return layer.frame().x() 132 | }, 133 | right : function (layer) { 134 | return layer.parentGroup().frame().width() - layer.frame().x() - layer.frame().width() 135 | }, 136 | bottom : function (layer) { 137 | return layer.parentGroup().frame().height() - layer.frame().y() - layer.frame().height() 138 | } 139 | } 140 | 141 | //--------------------------------------// 142 | // Layout // 143 | //--------------------------------------// 144 | 145 | var layout = { 146 | alignHorizontally : function(layer) { 147 | layer.frame().setMidX(layer.parentGroup().frame().width() / 2) 148 | }, 149 | alignVertically : function(layer) { 150 | layer.frame().setMidY(layer.parentGroup().frame().height() / 2) 151 | }, 152 | setTop : function(layer, constraint) { 153 | layer.frame().setY(constraint) 154 | }, 155 | setRight : function(layer, constraint, oppositeConstraint, size) { 156 | if (exists(size) && exists(constraint) && exists(oppositeConstraint)) return 157 | else if (exists(oppositeConstraint)) { 158 | layer.frame().setWidth(Math.round(layer.parentGroup().frame().width()) - constraint - Math.round(layer.frame().x())) 159 | } else { 160 | layer.frame().setX(Math.round(layer.parentGroup().frame().width()) - constraint - Math.round(layer.frame().width())) 161 | } 162 | }, 163 | setBottom : function(layer, constraint, oppositeConstraint, size) { 164 | if (exists(size) && exists(constraint) && exists(oppositeConstraint)) return 165 | else if (exists(oppositeConstraint)) { 166 | layer.frame().setHeight(Math.round(layer.parentGroup().frame().height()) - constraint - Math.round(layer.frame().y())) 167 | } else { 168 | layer.frame().setY(Math.round(layer.parentGroup().frame().height()) - constraint - Math.round(layer.frame().height())) 169 | } 170 | }, 171 | setLeft : function(layer, constraint) { 172 | layer.frame().setX(constraint) 173 | }, 174 | setWidth : function(layer, constraint) { 175 | layer.frame().setWidth(constraint) 176 | }, 177 | setHeight : function(layer, constraint) { 178 | layer.frame().setHeight(constraint) 179 | }, 180 | setWidthProportion : function(layer, constraint) { 181 | var parentWidth = layer.parentGroup().frame().width() 182 | var propWidth = (Number(constraint)*parentWidth/100) 183 | layer.frame().setWidth(propWidth) 184 | }, 185 | setHeightProportion : function(layer, constraint) { 186 | var parentHeight = layer.parentGroup().frame().height() 187 | var propHeight = (Number(constraint)*parentHeight/100) 188 | layer.frame().setHeight(propHeight) 189 | }, 190 | update : function(layer, constraints) { 191 | if (exists(constraints.width)) layout.setWidth(layer, constraints.width) 192 | if (exists(constraints.widthProportion)) layout.setWidthProportion(layer, constraints.widthProportion) 193 | if (exists(constraints.height)) layout.setHeight(layer, constraints.height) 194 | if (exists(constraints.heightProportion)) layout.setHeightProportion(layer, constraints.heightProportion) 195 | if (exists(constraints.top)) layout.setTop(layer, constraints.top) 196 | if (exists(constraints.left)) layout.setLeft(layer, constraints.left) 197 | if (exists(constraints.bottom)) layout.setBottom(layer, constraints.bottom, constraints.top, constraints.height) 198 | if (exists(constraints.right)) layout.setRight(layer, constraints.right, constraints.left, constraints.width) 199 | if (is.text(layer)) layer.adjustFrameToFit() 200 | if (constraints.horizontally && !constraints.right && !constraints.left) layout.alignHorizontally(layer) 201 | if (constraints.vertically && !constraints.top && !constraints.bottom) layout.alignVertically(layer) 202 | } 203 | } 204 | 205 | function processLayer(layer, constraints) { 206 | if (exists(constraints)) layout.update(layer, constraints) 207 | } 208 | 209 | //--------------------------------------// 210 | // Sketch UI // 211 | //--------------------------------------// 212 | 213 | function showMessage(message) { 214 | doc.showMessage(message) 215 | } 216 | 217 | function displayDialog(message, title) { 218 | if (title) { 219 | app.displayDialog(message).withTitle(title) 220 | } else { 221 | app.displayDialog(message) 222 | } 223 | } 224 | 225 | //--------------------------------------// 226 | // Inputs // 227 | //--------------------------------------// 228 | 229 | function fillInputs(constraintsLayerContent, inputs) { 230 | var constraints = constraintsLayerContent 231 | inputs[0].setState((exists(constraints.width)) ? NSOnState : NSOffState) 232 | inputs[2].setState((exists(constraints.height)) ? NSOnState : NSOffState) 233 | inputs[4].setState((constraints.horizontally) ? NSOnState : NSOffState) 234 | inputs[5].setState((constraints.vertically) ? NSOnState : NSOffState) 235 | inputs[6].setStringValue((exists(constraints.top)) ? constraints.top : "") 236 | inputs[7].setStringValue((exists(constraints.right)) ? constraints.right : "") 237 | inputs[8].setStringValue((exists(constraints.bottom)) ? constraints.bottom : "") 238 | inputs[9].setStringValue((exists(constraints.left)) ? constraints.left : "") 239 | 240 | if (!exists(constraints.width)) { 241 | inputs[0].setState((exists(constraints.widthProportion)) ? NSOnState : NSOffState) 242 | if (exists(constraints.widthProportion)) inputs[1].setStringValue(constraints.widthProportion) 243 | inputs[10].setState((exists(constraints.widthProportion)) ? NSOnState : NSOffState) 244 | }; 245 | if (!exists(constraints.height)) { 246 | inputs[2].setState((exists(constraints.heightProportion)) ? NSOnState : NSOffState) 247 | if (exists(constraints.heightProportion)) inputs[3].setStringValue(constraints.heightProportion) 248 | inputs[11].setState((exists(constraints.heightProportion)) ? NSOnState : NSOffState) 249 | } 250 | 251 | return (exists(constraintsLayerContent)) 252 | } 253 | 254 | function getStringValue(input) { 255 | var value = parseFloat(input.stringValue()) 256 | 257 | return isNaN(value) ? null : value 258 | } 259 | 260 | function getStateValue(checkbox) { 261 | var state = Number(checkbox.state()) 262 | 263 | return (state) ? state : null 264 | } 265 | 266 | //--------------------------------------// 267 | // Cocoa UI // 268 | //--------------------------------------// 269 | 270 | function createLabel(text, fontSize, bold, frame) { 271 | var label = NSTextField.alloc().initWithFrame(frame) 272 | label.setStringValue(text) 273 | label.setFont((bold) ? NSFont.boldSystemFontOfSize(fontSize) : NSFont.systemFontOfSize(fontSize)) 274 | label.setBezeled(false) 275 | label.setDrawsBackground(false) 276 | label.setEditable(false) 277 | label.setSelectable(false) 278 | 279 | return label 280 | } 281 | 282 | function createCheckbox(text, checked, frame) { 283 | checked = (checked == false) ? NSOffState : NSOnState 284 | var checkbox = NSButton.alloc().initWithFrame(frame) 285 | checkbox.setButtonType(NSSwitchButton) 286 | checkbox.setBezelStyle(0) 287 | checkbox.setTitle(text) 288 | checkbox.setState(checked) 289 | 290 | return checkbox 291 | } 292 | 293 | function createSelect(frame, currentValue){ 294 | var combo = NSComboBox.alloc().initWithFrame(frame) 295 | combo.addItemsWithObjectValues([currentValue]) 296 | 297 | return combo 298 | } 299 | 300 | function createWindow(currentLayer) { 301 | var alert = COSAlertWindow.new() 302 | alert.addButtonWithTitle("OK") 303 | alert.addButtonWithTitle("Cancel") 304 | alert.addButtonWithTitle("Remove Constraints") 305 | alert.setMessageText("Sketch Constraints") 306 | alert.setInformativeText("Set constraints on the current layer. These constraints are relative to the parent, either a group or an artboard. You can select the current value in the combo boxes.") 307 | alert.setIcon(NSImage.alloc().initByReferencingFile(plugin.urlForResourceNamed("icon@2x.png").path())); 308 | 309 | var mainView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 300, 83)), 310 | alignLabel = createLabel("Alignment", 12, true, NSMakeRect(0, 58, 300, 20)), 311 | alignHorizontallyCheckbox = createCheckbox("Align Horizontally", false, NSMakeRect(0, 31, 300, 20)), 312 | alignVerticallyCheckbox = createCheckbox("Align Vertically", false, NSMakeRect(0, 6, 300, 21)), 313 | sizeLabel = createLabel("Size", 12, true, NSMakeRect(130, 58, 300, 20)), 314 | widthView = NSView.alloc().initWithFrame(NSMakeRect(130, 31, 300, 20)), 315 | widthCheckbox = createCheckbox("Width", false, NSMakeRect(0, 0, 300, 20)), 316 | widthTextfield = NSTextField.alloc().initWithFrame(NSMakeRect(70, 0, 60, 20)), 317 | widthProportionCheckbox = createCheckbox("\%", false, NSMakeRect(135, 0, 300, 20)), 318 | heightView = NSView.alloc().initWithFrame(NSMakeRect(130, 8, 300, 20)), 319 | heightCheckbox = createCheckbox("Height", false, NSMakeRect(0, 0, 300, 20)), 320 | heightTextfield = NSTextField.alloc().initWithFrame(NSMakeRect(70, 0, 60, 20)), 321 | heightProportionCheckbox = createCheckbox("\%", false, NSMakeRect(135, 0, 300, 20)), 322 | constraintsLabel = createLabel("Constraints", 12, true, NSMakeRect(0, 0, 300, 20)), 323 | constraintsView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 300, 150)), 324 | imageView = NSImageView.alloc().initWithFrame(NSMakeRect(0, 0, 300, 150)), 325 | backgroundImage = NSImage.alloc().initByReferencingFile(plugin.urlForResourceNamed(resourcesPath).path()), 326 | topComboBox = createSelect(NSMakeRect(100,122,100,25), getPosition.top(currentLayer)), 327 | rightComboBox = createSelect(NSMakeRect(200,61,100,25), getPosition.right(currentLayer)), 328 | bottomComboBox = createSelect(NSMakeRect(100,1,100,25), getPosition.bottom(currentLayer)), 329 | leftComboBox = createSelect(NSMakeRect(3,61,100,25), getPosition.left(currentLayer)) 330 | 331 | mainView.addSubview(alignLabel) 332 | mainView.addSubview(alignHorizontallyCheckbox) 333 | mainView.addSubview(alignVerticallyCheckbox) 334 | mainView.addSubview(sizeLabel) 335 | widthTextfield.setStringValue(getSize.width(currentLayer)) 336 | widthView.addSubview(widthCheckbox) 337 | widthView.addSubview(widthTextfield) 338 | widthView.addSubview(widthProportionCheckbox) 339 | mainView.addSubview(widthView) 340 | heightTextfield.setStringValue(getSize.height(currentLayer)) 341 | heightView.addSubview(heightCheckbox) 342 | heightView.addSubview(heightTextfield) 343 | heightView.addSubview(heightProportionCheckbox) 344 | mainView.addSubview(heightView) 345 | alert.addAccessoryView(mainView) 346 | alert.addAccessoryView(constraintsLabel) 347 | alert.addAccessoryView(constraintsView) 348 | imageView.setImage(backgroundImage) 349 | constraintsView.addSubview(imageView) 350 | constraintsView.addSubview(topComboBox) 351 | constraintsView.addSubview(rightComboBox) 352 | constraintsView.addSubview(bottomComboBox) 353 | constraintsView.addSubview(leftComboBox) 354 | 355 | alert.alert().window().setInitialFirstResponder(topComboBox); 356 | topComboBox.setNextKeyView(rightComboBox); 357 | rightComboBox.setNextKeyView(bottomComboBox); 358 | bottomComboBox.setNextKeyView(leftComboBox); 359 | 360 | var inputs = [widthCheckbox, widthTextfield, heightCheckbox, heightTextfield, 361 | alignHorizontallyCheckbox, alignVerticallyCheckbox, 362 | topComboBox, rightComboBox, bottomComboBox, leftComboBox, 363 | widthProportionCheckbox, heightProportionCheckbox] 364 | return [alert, inputs] 365 | } 366 | --------------------------------------------------------------------------------