Skip to content

Commit

Permalink
Ability to use relative find paths!
Browse files Browse the repository at this point in the history
Write ":" in the end of a search expression, followed by any of the following, how many times you desire:
- ">" select all children
- "<" select parent
- "2" selects the third layer in parent (counting starts at 0)
- "2n" selects every other layer
- "+1" selects the layer after
- "-4" selects the layer four layers before
That means you could write something like this: input (\w+):<+1>3n+1 and in the replacement box write "$1 label decoration %I" to rename every third (skipping the first) layer, in the group right after the group containing the selected field. Better yet, it does so even if you would have tens (or thousands...) of those "named input fields with labels having lots of strange decoration"... Ok, but seriously, pretty handy.

Last field value now better saved

Helpers:
Added getLayerKind
Additional alert options:
- width: works now with text fields
- icon: filePath or NSImage
- buttons: added cmd-enter shortcut for second other button + made them easier accessible in .beforeShow
  • Loading branch information
leonardpauli committed Jan 31, 2017
1 parent fc452f2 commit 28a135c
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 62 deletions.
68 changes: 61 additions & 7 deletions LayerRenamer.sketchplugin/Contents/Sketch/helpers.cocoascript
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ function createDynamicClassInstanceWithMethods(methods) {
}


function getLayerKind(obj) {
var objClass = obj.class()
if (objClass == MSLayerGroup)
return 'Group'

var str = NSStringFromClass(objClass)
if (str.substr(0,2)=='MS') str = str.substr(2)
if (str.substr(-5)=='Group' || str.substr(-5)=='Layer')
str = str.substr(0,str.length-5)

if (str=='SymbolInstance') return 'Symbol'
if (str.length>5 && (str.substr(-5)=='Shape' || str=='ShapePath')) return 'Path'

return str}


// Padder helper
function paddStringToLength(str, len, append, char) {
char = char || ' '
Expand Down Expand Up @@ -65,6 +81,7 @@ var showAlert = function(opt) {

var self = {}
self._v = COSAlertWindow.new();
self.rawAlert = self._v.alert()

self._v.setMessageText(opt.title || '');
self._v.setInformativeText(opt.message || '');
Expand Down Expand Up @@ -97,7 +114,7 @@ var showAlert = function(opt) {
self.fields[fieldLabel] = field

if (i==0) {
self._v.alert().window().setInitialFirstResponder(field)
self.rawAlert.window().setInitialFirstResponder(field)
} else {
prevField.setNextKeyView(field)
}
Expand All @@ -121,18 +138,55 @@ var showAlert = function(opt) {

// Help
if (opt.showHelp) {
self._v.alert().showsHelp = true
self.rawAlert.showsHelp = true
var delegate = createDynamicClassInstanceWithMethods({'alertShowHelp:': opt.showHelp})
self._v.alert().delegate = delegate
self.rawAlert.delegate = delegate
}

// Show
self._v.layout()

// Set width
if (opt.width && !opt.fields) {
self._v.setAccessoryView(NSView.alloc().initWithFrame_(NSMakeRect(0, 0, opt.width, 0)))}
self.rawAlert.setAccessoryView(NSView.alloc().initWithFrame_(NSMakeRect(0, 0, opt.width, 0)))
} else if (opt.width) {
var frame = self.rawAlert.accessoryView().frame()
frame.size.width = opt.width
self.rawAlert.accessoryView().setFrame(frame)

Object.keys(self.fields).forEach(function(key) {
var field = self.fields[key]
var frame = field.frame()
frame.size.width = opt.width
field.setFrame(frame)
})
}

self._v.layout()
// Set icon
if (opt.icon) {
if (typeof opt.icon == "string")
opt.icon = NSImage.alloc().initWithContentsOfFile(opt.icon)
self.rawAlert.setIcon(opt.icon)
}

// Fix button shortcuts, and expose them
var alertButtons = self.rawAlert.buttons()
for (var i=0; i<alertButtons.count(); i++) {
var alertButton = alertButtons[i]
var button = self.buttons.find(function(button) {return button.index==i})
if (!button) button = self.buttons.push({index:i, title: undefined})
button._v = alertButton

if (i>=1 && i<=3 && i<alertButtons.count()-1) {
var mask = i==1? NSCommandKeyMask: i==2? NSAlternateKeyMask: NSShiftKeyMask
// mask | NSControlKeyMask
alertButton.setKeyEquivalentModifierMask(mask)
alertButton.setKeyEquivalent('\r')
}
}

// Show
if (opt.beforeShow) opt.beforeShow(self)
var selectedButtonIndex = self._v.alert().runModal() - 1000
var selectedButtonIndex = self.rawAlert.runModal() - 1000
self.selected = self.buttons.find(function (button) {return button.index == selectedButtonIndex})
self.canceled = selectedButtonIndex==0

Expand Down
144 changes: 89 additions & 55 deletions LayerRenamer.sketchplugin/Contents/Sketch/layerRenamer.sketchscript
Original file line number Diff line number Diff line change
Expand Up @@ -129,32 +129,24 @@ var layerRenamerSelect = function(context) {
showHelp: function (alert) {
showRegExHelpAlert(true)
},
beforeShow: function (alert) {
var rawAlert = alert._v.alert()

var filePath = scriptPath+'Resources/icons/layerRenamerSelect.png'
var image = NSImage.alloc().initWithContentsOfFile(filePath)
rawAlert.setIcon(image)

var frame = rawAlert.accessoryView().frame()
frame.size.width = 400
rawAlert.accessoryView().setFrame(frame)

Object.keys(alert.fields).forEach(function(key) {
var field = alert.fields[key]
var frame = field.frame()
frame.size.width = 400
field.setFrame(frame)
})
}
width: 400,
icon: scriptPath+'Resources/icons/layerRenamerSelect.png'
})
if (alert.selected.title=='Cancel') return;

var searchStr = alert.fields.search.value
if (!searchStr.length()) searchStr = defaultSearchStr
userDefaults.setObject_forKey_(searchStr, "LayerRenamer-search-value")

// Take out relative selection path string
var relativePathMatch = searchStr.match(/(?:[^\\]|^):([<>n\d+-]+)$/)
var relativePath = relativePathMatch ? relativePathMatch[1] : null
if (relativePath) searchStr = searchStr.substr(0,searchStr.length-relativePath.length-1)

var reg = new RegExp(searchStr, 'i')
var expressionStr = alert.fields.expression.value
if (!expressionStr.length()) expressionStr = defaultExpressionStr
userDefaults.setObject_forKey_(expressionStr, "LayerRenamer-expression-value")

var shouldFilterAll = alert.selected.title == 'Filter All'
var shouldFilterSelected = alert.selected.title == 'Filter selected'
Expand All @@ -165,18 +157,7 @@ var layerRenamerSelect = function(context) {
selectionCount = selection.count()
shouldFilterSelected = shouldFilterInside = true}

page.deselectAllLayers()
var firstSelected = false
function selectLayer(layer) {
layer.select_byExpandingSelection_(true, firstSelected)
firstSelected = true
}

var selectedTypesFreqs = {}
var addLayerKindToFreqs = function(layerKindName) {
if (!selectedTypesFreqs[layerKindName]) selectedTypesFreqs[layerKindName] = 0
selectedTypesFreqs[layerKindName]++}

var layersToBeSelected = []
var currIterationIdx = 0
var handleLayer = function(layer, depth) {
function digDeeper() {
Expand Down Expand Up @@ -207,9 +188,9 @@ var layerRenamerSelect = function(context) {

// Setup expression variables
var layerKindName = getLayerKind(layer)
var vars = 'Shape/Group/Artboard/Page/Slice/Bitmap/Text/Symbol/SymbolMaster/Path'.split('/').map(function(name) {
return 'var '+name.toLowerCase()+'='+(layerKindName==name?'true':'false')+';'
}).join('')
var vars = 'var '+'Shape/Group/Artboard/Page/Slice/Bitmap/Text/Symbol/SymbolMaster/Path'.split('/').map(function(name) {
return name.toLowerCase()+'='+(layerKindName==name?'true':'false')
}).join(', ')+';'
vars += 'var '+'hidden'+'='+(!layer.isVisible()?'true':'false')+';'
vars += 'var '+'locked'+'='+(layer.isLocked()?'true':'false')+';'

Expand All @@ -224,8 +205,7 @@ var layerRenamerSelect = function(context) {
+paddStringToLength(str, 40, true))

//layer.setIsSelected(shouldSelect)
if (shouldSelect) selectLayer(layer)
if (shouldSelect) addLayerKindToFreqs(layerKindName)
if (shouldSelect) layersToBeSelected.push(layer)

// Shape is really a group, even if it doesn't
// look that way if it only contains one
Expand Down Expand Up @@ -255,6 +235,73 @@ var layerRenamerSelect = function(context) {
selection.some(function(layer){return handleLayer(layer, 0)})



var findLayersUsingRelativePath = function (baseLayers, path) {
var layers = []
var direction = path.match(/^:?(([<>])|((\d+n)?(([+-])?\d+)?))/)
if (!direction) throw 'Path "'+path+'" isn\'t valid'
var restPath = path.substr(direction[0].length)

if (direction[2]=='<') {
baseLayers.forEach(function(layer) {
if (layer.parentGroup) {
var parent = layer.parentGroup()
if (layers.indexOf(parent)<0)
layers.push(parent)
}
})
} else if (direction[2]=='>') {
baseLayers.forEach(function(layer) {
if (layer.layers) layer.layers()
.forEach(function(l) {layers.push(l)})
})
} else if (!direction[3] || !direction[3].length) { // containing group
throw 'Path "'+path+'" isn\'t valid.'
} else {
var usingModulus = !!direction[4]
var k = usingModulus? parseInt(direction[4]) : 1
var m = direction[5]? parseInt(direction[5]) : 0
var doRelative = direction[6]

baseLayers.forEach(function(layer, idx) {
var index = baseLayers.length-idx-1
if (usingModulus) {
if ((index+m)%k == 0)
layers.push(layer)
} else {
if (!layer.parentGroup) return;
var parent = layer.parentGroup()
var siblings = parent.layers()

var targetIdx = !doRelative? siblings.length-1-m : siblings.indexOf(layer) - m
if (targetIdx<0) targetIdx += siblings.length
if (targetIdx>=0 && targetIdx<siblings.length)
layers.push(siblings[targetIdx])
}
})
}

return !restPath.length? layers:
findLayersUsingRelativePath(layers, restPath)
}


var selectedTypesFreqs = {}
var addLayerKindToFreqs = function(layerKindName) {
if (!selectedTypesFreqs[layerKindName]) selectedTypesFreqs[layerKindName] = 0
selectedTypesFreqs[layerKindName]++}

layersToBeSelected = !relativePath? layersToBeSelected:
findLayersUsingRelativePath(layersToBeSelected, relativePath)

page.deselectAllLayers()
layersToBeSelected.forEach(function(layer) {
if (layer.isSelected()) return;
addLayerKindToFreqs(getLayerKind(layer))
layer.select_byExpandingSelection_(true, true)
})


// Message
var selectedTypes = Object.keys(selectedTypesFreqs)
var newSelectionCount = selectedTypes.reduce(function(p,v) {return p+selectedTypesFreqs[v]}, 0)
Expand All @@ -276,10 +323,6 @@ var layerRenamerSelect = function(context) {
}

doc.showMessage(msg)

// Save for later
userDefaults.setObject_forKey_(searchStr, "LayerRenamer-search-value")
userDefaults.setObject_forKey_(expressionStr, "LayerRenamer-expression-value")
}

var layerRenamerRename = function(context) {
Expand Down Expand Up @@ -310,14 +353,10 @@ var layerRenamerRename = function(context) {
showHelp: function (alert) {
showRegExHelpAlert(false)
},
width: 400,
icon: scriptPath+'Resources/icons/layerRenamerRename.png',
beforeShow: function (alert) {
var rawAlert = alert._v.alert()

var filePath = scriptPath+'Resources/icons/layerRenamerRename.png'
var image = NSImage.alloc().initWithContentsOfFile(filePath)
rawAlert.setIcon(image)

rawAlert.window().setInitialFirstResponder(alert.fields.replace)
alert.rawAlert.window().setInitialFirstResponder(alert.fields.replace)
}
})
if (alert.selected.title=='Cancel') return;
Expand Down Expand Up @@ -387,9 +426,10 @@ function replaceLayerExpressionFlags(expression, layer, quoteStrings, selectionC
var parentLyr = lyr.parentGroup()
if (!parentLyr) return quoteStrings?null:'?'

var siblings = parentLyr.layers()
var indexInParent = parentLyr.indexOfLayer_(lyr)
if (flag.substr(0,1)=='i') return zeroPad(flag, indexInParent)
if (flag.substr(0,1)=='I') return zeroPad(flag, indexInParent+1)
if (flag.substr(0,1)=='i') return zeroPad(flag, !useReverse ? siblings.length-indexInParent-1 : indexInParent)
if (flag.substr(0,1)=='I') return zeroPad(flag, !useReverse ? siblings.length-indexInParent : indexInParent+1)

return all
})
Expand Down Expand Up @@ -456,13 +496,7 @@ function showRegExHelpAlert(isSearch) {
,
buttons: ['OK', 'GitHub', 'Regexr.com'],
width: 430,
beforeShow: function (alert) {
var rawAlert = alert._v.alert()

var filePath = scriptPath+'Resources/icons/layerRenamerSelect.png'
var image = NSImage.alloc().initWithContentsOfFile(filePath)
rawAlert.setIcon(image)
}
icon:scriptPath+'Resources/icons/layerRenamerSelect.png'
})
if (msgAlert.canceled) return;
var link = msgAlert.selected.title == 'Regexr.com' ? 'http://regexr.com':
Expand Down

0 comments on commit 28a135c

Please sign in to comment.