半自动化创建Ext的theme
photoshop CS3 extended 10.0支持javascript,可以通过脚本的方式来实现主题的创建,步骤如下:
- 创建一个?imageParser.jsx文件。
- 用photoshop打开extjs/resources/images/default/panel/tool-sprites.gif。
- 调整色相/饱和度值,确定着色复选框被选中。
- 记住以上调整的值,后面将会使用。
- 关闭tool-sprites.gif文件。
- 用文本文件打开imageParser.jsx。
- 根据以上记录的值,改变变量h,s,和l的值。
- 保存文件。
- 创建一个文件夹,作为脚本的输出。如extjs/resources/images/lihj-theme。
- 在photoshop中,选择 文件->脚本->浏览…。
- 选择?imageParser.jsx。
- 选择输入文件夹,通常使用extjs/resources/images/default作为输入。
- 选择输出文件夹,用9创建的文件夹。
- 选择OK。
CSS文件的更新:
- 打开extjs/resources/css/ext-all.css。
- 用lihj-theme/替换所有的default/ 。
- 保存这个CSS文件,重命名为lihj-theme.css。
通过以上步骤,即完成了一个ext主题的制作。
注意:以上脚本只是创建了已经存在文件的着色版本,任何感官上的变化都不在本帖讨论的范围。
本文翻译自:http://www.extjs.com/forum/showthread.php?t=15757
imageParser.jsx:
// written for Adobe Photoshop CS3 Extended 10.0 - may not work with your version // ExtJs Theme Modifier #target photoshop app.bringToFront(); // bring top $.localize = true; // Enable ZString localization // // Change the hue/saturation/light values // // These values are easily determined by opening a file in photoshop // altering the h/s/l and recording the values. // -- colorize was turned on for me // // h = 0, s = 25, l = 0 is a nice bronzish color // h = 113, s = 38, l = -1 is a greenish color // var h = 0; // hue var s = 25; // saturation var l = 0; // light (this is the letter L, not the number 1) // debug settings var debug = true; var debugFile = '~/Desktop/image-parser.log'; var debugFh, linefeed; // don't modify these. debug file handle and linefeed char // files to skip during the hue/saturation/light step var exclude = { '':{ // top level dir 's.gif':true, 'shadow-c.png':true, 'shadow-lr.png':true, 'shadow.png':true }, dd:{ 'drop-add.gif':true, 'drop-no.gif':true, 'drop-yes.gif':true }, editor:{ 'tb-sprite.gif':true }, form:{ 'error-tip-corners.gif':true, 'exclamation.gif':true }, grid:{ 'arrow-left-white.gif':true, 'arrow-right-white.gif':true, 'columns.gif':true, 'dirty.gif':true, 'done.gif':true, 'drop-no.gif':true, 'drop-yes.gif':true, 'grid-loading.gif':true, 'group-by.gif':true, 'hd-pop.gif':true, 'hmenu-asc.gif':true, 'hmenu-desc.gif':true, 'hmenu-lock.gif':true, 'hmenu-lock.png':true, 'hmenu-unlock.gif':true, 'hmenu-unlock.png':true, 'invalid_line.gif':true, 'loading.gif':true, 'nowait.gif':true, 'page-first-disabled.gif':true, 'page-last-disabled.gif':true, 'page-next-disabled.gif':true, 'page-prev-disabled.gif':true, 'refresh.gif':true, 'wait.gif':true }, layout:{ 'mini-bottom.gif':true, 'mini-left.gif':true, 'mini-right.gif':true, 'mini-top.gif':true }, shared:{ 'blue-loading.gif':true, 'calendar.gif':true, 'large-loading.gif':true, 'warning.gif':true }, tabs:{ 'tab-strip-bg.png':true // empty image?? }, tree:{ 'drop-add.gif':true, 'drop-between.gif':true, 'drop-no.gif':true, 'drop-over.gif':true, 'drop-under.gif':true, 'drop-yes.gif':true, 'folder.gif':true, 'folder-open.gif':true, 'leaf.gif':true, 'loading.gif':true, 's.gif':true }, window:{ 'icon-error.gif':true, 'icon-info.gif':true, 'icon-question.gif':true, 'icon-warning.gif':true } }; // modify nothing beneath this line // hue/saturation/light function gotten off of the internets function hueSaturationLight(hue, saturation, light){ var aDesc = new ActionDescriptor(); var userInput = new ActionDescriptor(); var aList = new ActionList(); with(userInput){ putInteger(charIDToTypeID("H "), hue); putInteger(charIDToTypeID("Strt"), saturation); putInteger(charIDToTypeID("Lght"), light); } aDesc.putBoolean(charIDToTypeID("Clrz"), true); aList.putObject(charIDToTypeID("Hst2"), userInput); aDesc.putList(charIDToTypeID("Adjs"), aList); executeAction(charIDToTypeID("HStr"), aDesc, DialogModes.NO); } // save the current preferences var startDisplayDialogs = app.displayDialogs; // set no dialogs app.displayDialogs = DialogModes.NO; // ask the user for the input folder var inputFolder = Folder.selectDialog("Select a folder for the input files. Example: extjs/resources/images/default"); // ask the user for the output folder var outputFolder = Folder.selectDialog("Select a folder for the input files. Example: extjs/resources/images/bronze"); function log(string){ if (!debug)return; if (!debugFile)return; if (!linefeed){ if ($.os.search(/windows/i) != -1){ linefeed = "windows"; } else { linefeed = "macintosh"; } } if (!debugFh) { // create a reference to the logfile debugFh = new File(debugFile); debugFh.lineFeed = linefeed; debugFh.open('w', "TEXT", "????"); debugFh.write('Debug Report for imageParser.jsx: '+ new Date() + '\n'); } if (debugFh){ // write the string to the file var string = string || ''; debugFh.write(string+'\n'); } } function processFiles(args){ var folder = args.folder; var f = folder.getFiles(); if (f && f.length > 0){ for (var i = 0; i < f.length; i++){ if (f[i] instanceof Folder) { // traverse into this folder log(f[i].name+' is a Folder.. traverse'); processFiles({folder:f[i]}); } else { log(f[i]+' ... checking'); var processFile = true; // exclude index files if ( -1 != f[i].fsName.indexOf('Thumbs.db'))continue; if ( -1 != f[i].fsName.indexOf('.DS_Store'))continue; if ( -1 != f[i].fsName.indexOf('.psd'))continue; // only process files that contain a .gif, .png, or .jpg if ( ! (f[i].fsName.indexOf('.gif') > -1 || f[i].fsName.indexOf('.png') > -1 || f[i].fsName.indexOf('jpg') > -1 ) ) { log(' ... not a gif, png, or jpg'); processFile = false; } // check to see if the current folder is the top-level one var pName = (f[i].parent.name === inputFolder.name) ? '' : f[i].parent.name; // don't process this file if it is in our 'exclude' list if (exclude[pName] && exclude[pName][f[i].name]){ log(' ... is in the exclude list'); processFile = false; } var doc = app.open(File(f[i])); if (doc){ if (processFile){ log(' ... performing hue/sat/light'); hueSaturationLight(h,s,l); // vars set at teh top of the file } // Determine which file save settings to use. // I couldn't find an image filetype parameter so i'm parsing the filename // This of course, is easily broken by funky filenames var saveOptions; if (f[i].fsName.indexOf('.gif') > -1){ saveOptions = new GIFSaveOptions(); saveOptions.transparency = true; } else if (f[i].fsName.indexOf('.png') > -1){ saveOptions = new PNGSaveOptions(); } else if (f[i].fsName.indexOf('.jpg') > -1){ saveOptions = new JPEGSaveOptions(); } else { // not one of the three types } log(' ... setting save options'); if (saveOptions){ // save the file to the folder/subfolder requested by the user var sFile = outputFolder+'/'; if (pName) { sFile += pName +'/'; } sFile += f[i].name; if (pName){ // if not the top-level folder var tFolder = new Folder(outputFolder+'/'+pName); if (!tFolder.exists){ tFolder.create(); } } doc.saveAs(new File(sFile), saveOptions); log(' ... saved: '+sFile); } // close orig file. do not save changes doc.close(SaveOptions.DONOTSAVECHANGES); } } } } log(); // blank line for readability } // work with the folders selected by the user if (inputFolder !== null && outputFolder !== null){ log(); // blank line for readability log('Input Folder: '+inputFolder); log('Output Folder: '+outputFolder); log(); // blank line for readability // if the input folder isn't the output folder // try to play nicely.. not overwrite the source file if (inputFolder !== outputFolder){ processFiles({folder:inputFolder}); } else { log('Input and Output folders are the same'); alert('Sorry. Input and output folders can not be the same folder.'); } } function cleanup(){ // nullify var and close file handles if (debugFh){ debugFh.close(); alert('Log file saved to: '+debugFile); } // restore settings if (startDisplayDialogs){ app.displayDialogs = startDisplayDialogs; } } cleanup();