├── 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 | 
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 |
14 |
15 | ### Update Layout `⌘ + L`
16 |
17 |
18 |
19 | ### Example
20 |
21 |
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 |
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 |
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 |
--------------------------------------------------------------------------------