-
1. Re: UI panel and document CS4
pkahrel Feb 10, 2010 12:57 AM (in response to Sara Peroni)With a dialog open, no. With a panel displayed, yes. But maybe you should explain in some more detail what you want.
Peter
-
2. Re: UI panel and document CS4
Kasyan Servetsky Feb 10, 2010 1:02 AM (in response to Sara Peroni)If you want to create a modeless dialog then define its type as palette:
var myDialog = new Window('palette', 'Sample Dialog');
Kasyan
-
3. Re: UI panel and document CS4
Sara Peroni Feb 10, 2010 1:10 AM (in response to pkahrel)I want select a item group displayed in page document.
The UI is a new Window('dialog') with a "panel"...
The target is assign a script label to item.
-
example.jpg 42.6 K
-
-
4. Re: UI panel and document CS4
Kasyan Servetsky Feb 10, 2010 1:43 AM (in response to Sara Peroni)If you want to interact with page items – e.g. assign a script label – with your dialog staying open, you should do two things:
- define its type as palette
- run your script in a custom engine – for example:
#targetengine "session"
-
5. Re: UI panel and document CS4
Sara Peroni Feb 10, 2010 5:34 AM (in response to Sara Peroni)Thank guys, it work!
-
6. Re: UI panel and document CS4
Sara Peroni Feb 10, 2010 8:19 AM (in response to Sara Peroni)I've another problem.
I create a window with fixed dimentions.
Inside it put a group with a dinamic number of rows.
I need to add a "scrollbar" o something that permit to scroll the elements if these are not visible.Is possible?
-
7. Re: UI panel and document CS4
Kasyan Servetsky Feb 10, 2010 9:16 AM (in response to Sara Peroni)I don't think it's possible -- you can create a scrollable text, but not control elements.
-
8. Re: UI panel and document CS4
Marc Autret Jun 14, 2010 11:14 AM (in response to Kasyan Servetsky)Actually you can create scrollable widgets or scrollable groups of widgets, but you need to connect and manage the Scrollbar events by yourself. Here is a script (CS4/CS5) that illustrates ScriptUI scrolling:
http://www.indiscripts.com/post/2010/06/claquos2-pie-chart-builder-for-indesign
Regards,
Marc - http://www.indiscripts.com
-
9. Re: UI panel and document CS4
Kasyan Servetsky Jun 15, 2010 7:01 AM (in response to Marc Autret)Hi Marc,
Since your script is in binary format, could post a sample code illustrating how you do this?
Regards,
Kasyan
-
10. Re: UI panel and document CS4
Marc Autret Jun 15, 2010 9:21 AM (in response to Kasyan Servetsky)Hey Kasyan,
Since I don't write "native ScriptUI" code anymore, you force me to disclose a part of my secret libraries ;-)
That's OK, here is a sample:
// testScroller.js // Demonstrates ScriptUI Scrollbar usage //------------------------------------------------- //------------------------------------------------ // THE LIBRARIES //------------------------------------------------ var DOMEX = DOMEX||(function() // Extended DOM //------------------------------------------------ { Window.prototype.focus = (function() //---------------------------- // ScriptUI Window focus method { var getActivableControls = function() { // this: UI object var r = [], i, c; for ( i=0 ; i<this.children.length ; ++i ) { c = this.children[i]; if (c.constructor == StaticText) continue; // exclude StaticText focus if (c.constructor == Scrollbar) continue; // exclude Scrollbar focus if (c.noTabFocus ) continue; // exclude explicit noTabFocus ctrl if ('active' in c) {r.push(c);continue;} r = r.concat(arguments.callee.call(c)); } return r; }; return function(/*int*/step, /*?ctrl*/ac) { // this: Window this.controls = this.controls || getActivableControls.call(this); ac = ac||false; var sz = this.controls.length, i, c; for( i=0 ; i<sz ; ++i ) { c = this.controls[i]; if( ac && c==ac ) break; if( (!ac) && c.active ) {ac=c; break;} } if( !ac ) return; if( !step ) return ac; i = (i+step+sz)%sz; step = (step<0)?-1:1; while( ac !=(c=this.controls[i]) ) { if( c.visible && c.enabled ) break; i = (i+step+sz)%sz; } c.active = true; return c; }; })(); Window.prototype.setTabKey = function() //---------------------------- // ScriptUI Window set tab key { // this: Window this.addEventListener('keydown', function(kev) { if( kev.keyName != 'Tab' ) return; if( kev.ctrlKey || kev.altKey || kev.metaKey ) return; kev.preventDefault(); this.focus( ((kev.shiftKey)?-1:1 ) ); },false/*==bubble*/); }; /*STATIC*/ Window.UI = function(ui) { // constructor var flat = function(container) { // this: Window.UI var p,c; for( p in container ) { c = container[p]; if( ( !c ) || ( typeof c != 'object' ) || ( c.propertyIsEnumerable(p) ) || ( !('window' in c) ) || ( p.substr(0,8) == 'private_' ) ) continue; this[p] = this[p]||c; flat.call(this,c); } }; var connect = function() { // this: UI container var cs = this.children, i = cs.length, ci; while( i-- ) { ci = cs[i]; if( 'alignChildren' in ci ) connect.call(ci); if( XW && ('xType' in ci) ) XW.connector(ci.xType).call(ci); } }; this.window = new Window(ui.toSource(). slice(1,-1). replace(RegExp('(?:\\{_([^:]+):)','g'),'$1'). replace(RegExp('}}','g'),'}') ); this.window.layout.layout(); flat.call(this,this.window); connect.call(this.window); this.window.setTabKey(); }; return true; })(); var XW = XW||(function() // Extended Widgets [v.10-06-14] //------------------------------------------------ { var MAC_OS = (File.fs == "Macintosh"); var widgets = {}; // IconButton // -------- widgets.PngButton = { ICON_PLUS: String("\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\x04gAMA\x00\x00\u00AF\u00C87\x05\u008A\u00E9\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\u00C9e<\x00\x00\x02oIDAT8\u00CB\u00A5\u0093\u00EBK\u0093a\x18\u0087\u00FD[\u00B6/\x05\u0083\u00A4\u0086Y(\u00A8)%X(o\u00D9l\u008A\u008AN\u00DB\u0096sk\u008A\u00CE\u00F6n.\u009D\u00BA\u00CD\x03-\u009D\x13\x0F\x15\u00B5\u00A1\u00DBh\u00A4;8\x0F\u00C3\x03f\u00E2\u00D4\u00D2E\u00EA\u00EBP\u00A2\u008D\"\bj\u00C3\u00AF\u00BF\u00DE\x15MG\u00CB\u0088\x1E\u00F8}y\u00E0\u00BA\u009E\u00E7\u00BE\u00B9\u00EF$\x00I\u00FF\u0093\u00DF.t\u00D3u\u00CC\x0E\u008F\u0084lu\u0089\u00A8\u00E6\tAX\u00FE\u00A2:\u00DC\u00F0\u00BC\u0082\u0092Z\u00CBH\u00D1h1\u00F3D\u0081nZJ\u00B4OJB\u00CF\u00D6{\u00B1\x1AZ\u0084\u00FF\u00F3\x06\u00D6?\u00AD`2`\u0087\u00D2S\x03\u00BE\u0089\x13\u00E2=\u00B9N$\x14\u00FC\u0084\u00C5\u0091\u00E9=;\x0E\u00BE\u00EDa\u00F6\u0083\x0B&j\x10\u008Fw\f\u00B0\u00EF\u009B\u00B0\u00F4q\x0E\u00DA\x05\x19JG\u00F2#\u00DC\u00C1<\"N\u00A0\u009D\u00922h8\u00E8\u00DE\u00B5`\u00EF\u00EB6\u0086\u00B7\u00F5x\u00B8\u00D6\u0081n_+\f~\x1D\u00FA\u00FCZto\u00B6\u00C0}`\x07\u00E9\x11\u00A2\u00D0x%X\u00D0\u009B\u00CD\u0088\thX\u00D1\u00BF\u00AC\u00C6\u00BB/\u009B\u00F4\u008B}\u00E8\u00DD\u00D2B\u00F3J\u0089_G\u00BD&\u0083|Q\b\u00C5r-\u009C\u00FB6\u00DC\x1C\u00C9A\u00DE\u0083\fEL\u00D0\u00E2\u00AC\u00A1\\\x01\x1B\u00FDU3:WUh[\u0091C6+\u008A\t\x046.\x1Af\u00CA \u009D*\u00C6\u00C0\x1B\x1D\u00F4K\u00CD\u00B8\u00DC\u009DF\u00C5\x04\u008Aq\u00FE\u00A1\u00F7\u00BD\x0B\u00FDou4\u00DC\u0084?\x1D\u00F1d\x11\x14\u00F3|X\u00FC\u00C3\u00C8\u00D2\u00A5\x1E\u00C6\x04Mv\u00DE\u00E1D`\f\u00DA\r\x12*_\u00FD\u0089\x02\u00D2[\r\u00AB\x7F\b\u00E9\x1D\u00EC#A\u00BD\u00AD\u009C2\u00FA\u00B40li\u00D0\u00F4R\b\u00F1|\x05x\u00D6\x1Bq`4w=\\\u00F4\u00FB\u00D4\u00E8\u00F2\u00DE\u00C3\x05u\u00F2Q\t\u00E2\u00B1\x12\u00C5m+\x01G\u00C0\x02\u00D9|%$\u00DE\u00D2\x1F5\x1F\x17\u0088\u009C\x1C\u00D4\u00B9\u008B\u00E1\u00D85\u00E3RO*\u00D8\u00F7YGM\x14\u009A\u008B\x18UO\x0B\u0083\u00A4G\u0080qj4\u00D6\u00B0(X\u00EB\u008C&\ns1\u00B1c\u00C2\x1D\u00CB\u00AD(\x1CLV\u009Ef\u00C4\rR\u00F9\u00A3\x02\u00A2d\u00E8j\u00A4\u00D1Q\t'-1\u00FA\u00DAA\u00CETA>U\t\u00E3j\x1B\x1C4,\u00B4p\u00C0V\u00B1\"4L$\x1Ce\u00CE@.A\x18rB\u00F9\x03\u0099\u00E8Y a~m\u0080y\u00C3\x00\u008D\u00B7\x11Y])Q8t\x1CN\u00B8L\u00D7\u00F4\u0099\u00CC\u00DC\u009Et2\u00BB\u00F3\"\u0095\u00A1I\t\u00A7\u00B5\u009F\r\u009Fo=C\u009DS\u00B1\u00C8d\u00E5)\u00E6_\u00B7\u00F1_\u00F3\x1DAF\u00CB\x1F\x00(\u00D3\u00C1\x00\x00\x00\x00IEND\u00AEB`\u0082"), ICON_MINUS: String("\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x03\x00\x00\x00(-\x0FS\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq\u00C9e<\x00\x00\x000PLTE\u00D4\u00A7\u0096\u00E2\u00B5\u00A4\u00E4\u00C5\u00BA\u00CAz[\u00A7cH\u00EF\u00D7\u00CE\u0093Q7\u00F4\u00E8\u00E4\u00CE\u0084h\u00BFdA\u00D3\u0090v\u00D8\u009D\u0086\u00C8uU\u00C5nL\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00A53\x0E}\x00\x00\x00\x10tRNS\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\u00FF\x00\u00E0#]\x19\x00\x00\x00\u0087IDATx\u00DA\\\u008F[\x12\u00C4 \b\x04y\u00898\u00E8\u00E6\u00FE\u00B7\r&\u00BB\u00A9\u00CA\u00F6\u0087U\u00B4:\x00\x1D\x7F\u00D0>\u00BA@5\u00B8\u00FFDCJk\u00ACSn\u00D1\u00EAN\u0088Xb\u00C8\x16=\u00B8e\"3Y\u00AD\u0097\x104J\x04P)F%\u00C0\f\u00CC\u008B\u0084\u0095\u0098\u0092\u00F8\\,%\u00DF\u00A2\x1E\u00BCDd\u00C6\\\x17\u0088\u00FD\u0085GMp\x1B\u00F6\x1D\u00DA\u0097\u00F2\u00B7\x1E\u00BE\u00DB\x1Eb\x15\u00A3\x13U\u00CB=\u00BA\u0098\x05e\u00B8\u00CB\u00B3\x1C\u0099\u00BB\u00D3\u00B3\u00DC\u008BS\u0080\x01\x005\u00C3\t\u00EA\u00DEA\u00F0S\x00\x00\x00\x00IEND\u00AEB`\u0082"), ui: function(ss) { var r = {_IconButton:{xType:'PngButton', settings:ss, noTabFocus:1, preferredSize:[20,20], helpTip:ss.helpTip||'', properties:{style:ss.style||'toolbutton'}, }}; return r; }, connect: function() { // <this> IconButton (function(/*str*/pngStr) { // Set image var f = new File(Folder.temp.absoluteURI + '/ib' + (+new Date()) + '.png' ); f.encoding = 'BINARY'; f.open('w'); f.write(pngStr); f.close(); this.image = ScriptUI.newImage(f.fsName); f.remove(); }).call(this, widgets[this.xType][this.settings.icon]); } }; // PanelListBox // -------- // Sample: // XW.PanelListBox({clusterType:'{_EditText:{characters:5}}', viewCount:5, defaultItem:{text:'default'}, items:['{text:"aaa"}','{text:"bbb"}']}); widgets.PanelListBox = { ui: function(ss) { ss.viewCount = ss.viewCount||5; var r = {_Panel:{ xType: 'PanelListBox', settings:ss, orientation:'row', alignment:'left', alignChildren: 'fill', margins:0, spacing:0, private_gClusters:{_Group:{orientation:'column', alignChildren:'fill', margins:0, spacing:0}}, private_gScroll:{_Group:{orientation: 'row',alignChildren: 'fill', margins:0, spacing:0, private_sbStepper:{_Scrollbar:{preferredSize:[20, -1]}} }}, }}; var i, iMax = ss.viewCount-1, cs = r._Panel.private_gClusters._Group, buildCluster = function(){ var ret = {_Group:{orientation:'row', alignChildren:'fill', margins:1, spacing:5, private_xClusterIn: false, private_xTrash: XW.PngButton({icon:'ICON_MINUS',action:'remove', helpTip:ss.trashTip||"Remove the item."}), private_xPlus: XW.PngButton({icon:'ICON_PLUS',action:'add', helpTip:ss.addTip||"Add an item."}), }}; ret._Group.private_xClusterIn = (new Function('return '+ss.clusterType+';'))(); return ret; }; for( i=0 ; i <= iMax ; i++ ) cs['private_g'+i] = {_Group:{orientation:'column', alignChildren:'fill', margins:0, spacing:0, private_xClusterOut: buildCluster(), private_pLine: (i==iMax)?false:{_Panel:{alignment:'fill'}} }}; return r; }, connect: function() { // <this> UI Panel var plb = this; var evChangeCount = ScriptUI.events.createEvent('UIEvent'); evChangeCount.initEvent('changeCount',true,false,true); evChangeCount.value = 0; var changeCount = function() { evChangeCount.value = scroller.itemCount(); plb.dispatchEvent(evChangeCount); }; (function() { this.offset = this.offset||0; this.items = this.items||[]; this.maxCount = Math.max(this.items.length, this.maxCount||this.viewCount); }).call(this.settings); var scroller = (function(ss, sb) { // this: Group (private_gClusters) var dModel, iSize, vSize, oMax, ofs; dModel = (new Function('return '+ss.defaultItem+';'))(); var setItem = function(/*?obj*/v, /*index*/i, /*max*/mx, /*bool*/ ADDING) { // this: {obj} var p,s; var N_VAR = i+1, A_VAR = !!ADDING, M_VAR = (+mx)||1; v = v||{}; for( p in dModel ) { if( p in v ) this[p] = v[p]; if( !(p in this) ) { this[p]=dModel[p]; if( (typeof i != 'undefined') && (typeof this[p] == 'string' ) ) { s = this[p]. replace(/%N%/g,N_VAR). replace(/%A%/g,A_VAR). replace(/%M%/g,M_VAR); if( s[0] == '{' ) { try { s = (new Function('return '+s.substr(1)+';'))(); } catch(_){} } this[p] = s; } } } }; var cloneItem = function() { // this: {obj} var p, r = {}; for( p in dModel ) r[p]=this[p]; return r; }; var data = (function(/*str[]*/strItems, /*int*/maxCount) { var items = [], i = Math.min(strItems.length, maxCount), v; while( i-- ) { v = (new Function('return '+strItems[i]+';'))(); setItem.call(v,undefined,i,i+1); items.unshift(v); } return { length: function(){return items.length;}, get: function(i){return items[i];}, set: function(i,v){setItem.call(items[i],v,i,items.length);}, remove: function(i) {items.splice(i,1); return items.length;}, add: function(i){var v={};setItem.call(v,undefined,i+1,1+items.length,true);items.splice(i+1,0,v);return items.length;}, getItems: function(/*bool*/NO_CLONE) { if( NO_CLONE ) return items; var i = items.length, r = []; while( i-- ) r.unshift(cloneItem.call(items[i])); return r; } }; })(ss.items, ss.maxCount); iSize = data.length(); vSize = Math.min(ss.viewCount,iSize); oMax = +((iSize>vSize)&&(iSize-vSize)); ofs = ss.offset; var rawOffset = function(/*int*/v, /*bool*/SET_DATA, /*bool*/SET_VIEW) { var s = (v<ofs)?1:0, kLim = (vSize*s)-(1-s), k = (1-s)*(vSize-1), s = 2*s-1; while( k != kLim ) { if( SET_DATA ) data.set(k+ofs, view.get(k)); if( SET_VIEW ) view.set(k, data.get(k+v)); k+=s; } return ofs=v; }; var view = (function() { var clusters = [], i = ss.viewCount, cOut, reActive; while( i-- ) { cOut = this['private_g'+i]['private_xClusterOut']; cOut.index = i; cOut.addEventListener('keydown', function(ev) { // up/down keys reActive = false; var iFoc = 0; var locs, loc, t, chi; var ii = ((ev.keyName == 'Up')&&-(ofs > 0 || this.index > 0 )) || ((ev.keyName == 'Down')&&+( ofs < oMax || this.index < vSize-1 )) || ((ev.keyName == 'Tab')&&((ev.shiftKey)?-( ofs > 0 && this.index == 0 ):+( ofs < oMax && this.index == vSize-1 ))); if(! ii ) return; ii += this.index; if( iFoc = (-(ii==-1)+(ii==vSize)) ) { if( ev.target.constructor == EditText ) ev.target.notify(); // force onChange sb.safeSet(rawOffset(ofs+iFoc, /*set_data*/true, /*set_view*/true)); reActive = true; if( ev.keyName != 'Tab' ) return; else ii = this.index-iFoc; } // searching the corresponding widget up/down locs = [(t=ev.target).location]; while( (t=t.parent) != this ) { locs.push(t.location); } t = clusters[ii].parent; while( loc=locs.pop() ) { t = t.children; for( ii = t.length-1 ; ii>=0 ; ii-- ) { chi = t[ii]; if( chi.location.x != loc.x ) continue; if( chi.location.y != loc.y ) continue; t = chi; break; } if( !chi ) {t=false; break;} } if( t ) t.active = true; }); cOut.addEventListener('keyup', function(ev) { if( reActive && (ev.keyName == 'Up' || ev.keyName == 'Down') ) { ev.target.active = false; ev.target.active = true; reActive = false; } }); cOut.visible = i < vSize; cOut.private_xTrash.enabled = iSize>0; cOut.private_xPlus.enabled = iSize<ss.maxCount; clusters.unshift(cOut['private_xClusterIn']); } return { get: function(i){return clusters[i];}, set: function(i,v){setItem.call(clusters[i],v,i,data.length());}, }; }).call(this); return { maxOffset: function(){return oMax;}, viewSize: function(){return vSize;}, itemCount: function(){return iSize;}, offset: function(/*?int*/v, /*?bool*/NO_DATA_SET) { if( typeof v == 'undefined' || v > oMax) return ofs; return rawOffset(v,/*set data*/!NO_DATA_SET,/*set_view*/true); }, remove: function(/*int*/cid) { if( iSize <= 1 ) return; rawOffset(ofs,/*set_data*/true,/*set_view*/false); // backup every data, don't touch ofs // cid is the cluster-id of the DEL button var iData = cid + ofs; // iData is the data-id from which DEL is called iSize = data.remove(iData); // remove the data and update iSize if( iData == iSize ) iData--; // now iData is the data-id to select finally // if necessary, dec vSize and hide the last cluster if( vSize > iSize ) { --vSize; view.get(vSize).parent.visible = false; } // vSize now reflects the number of visible clusters (updated) oMax = +((iSize>vSize)&&(iSize-vSize)); // oMax is the new max offset // we need to un-scroll(-1) only if ofs>oMax if( ofs > oMax ) --ofs; // now update the view (from ofs) and update the scrollbar rawOffset(ofs,/*set_data*/false,/*set_view*/true); sb.safeSet(ofs, oMax); // update TRASH / ADD button availability view.get(0).parent.private_xTrash.enabled = ( iSize > 1 ); if( iSize == ss.maxCount-1 ) { cid = ss.viewCount; while( cid-- ) view.get(cid).parent.private_xPlus.enabled = true; } // then activate the remaining cluster cid = iData-ofs; if( 'activate' in view.get(cid) ) view.get(cid).activate(); changeCount(); }, add: function(/*int*/cid) { if( iSize >= ss.maxCount ) return; rawOffset(ofs,/*set_data*/true,/*set_view*/false); // backup every data, don't touch ofs // cid is the cluster-id of the ADD button var iData = cid + ofs; // iData is the data-id from which ADD is called iSize = data.add(iData); // add a new default data AFTER iData, update iSize ++iData; // iData is now the added data-id // if necessary, avail a new visible cluster and inc vSize if( vSize < ss.viewCount ) { view.get(vSize).parent.visible = true; ++vSize; } // vSize now reflects the number of available clusters (updated) oMax = +((iSize>vSize)&&(iSize-vSize)); // oMax is the new max offset // we need to scroll(+1) only if cid is the last cluster in scroll mode ( cid== ss.viewCount-1 ) if( cid == ss.viewCount-1 ) ++ofs; // now update the view (from ofs) and update the scrollbar rawOffset(ofs,/*set_data*/false,/*set_view*/true); sb.safeSet(ofs,oMax); // update TRASH / ADD button availability view.get(0).parent.private_xTrash.enabled = ( iSize > 1 ); if( iSize == ss.maxCount ) { cid = ss.viewCount; while( cid-- ) view.get(cid).parent.private_xPlus.enabled = false; } // then activate the added data line cid = iData-ofs; if( 'activate' in view.get(cid) ) view.get(cid).activate(); changeCount(); }, getItems: function(/*?bool*/NO_CLONE) { rawOffset(ofs,/*set_data*/true,/*set_view*/false); // backup data return data.getItems(!!NO_CLONE); }, updateView: function() { rawOffset(ofs,/*set_data*/false,/*set_view*/true); // backup data }, activate: function(cid) { if( (cid < vSize) && 'activate' in view.get(cid) ) view.get(cid).activate(); }, }; }).call(this.private_gClusters, this.settings, this.private_gScroll.private_sbStepper); (function() { // this: scrollbar var SAFE_SETTING = false; this.safeSet = function(v,mx) { SAFE_SETTING = true; if( typeof mx == 'undefined' ) { this.value = v; SAFE_SETTING = false; return; } if( mx > 0 && !this.enabled ) this.enabled = true; if( v >= this.maxvalue ) { this.maxvalue = mx; this.value = v; } else { this.value = v; this.maxvalue = mx; } if( mx <= 0 && this.enabled ) this.enabled = false; SAFE_SETTING = false; }; var elChange = function(ev) { // this: scrollbar if( SAFE_SETTING ) return; var v = ~~this.value; if( v!=scroller.offset() ) scroller.offset(v); }; this.minvalue = 0; this.maxvalue = scroller.maxOffset(); this.value = scroller.offset(); this.stepdelta = 1; this.jumpdelta = 20; // !!important!! this.enabled = this.maxvalue>0; this.addEventListener('change', elChange); this.addEventListener('changing', elChange); scroller.offset(this.value, /*NO_DATA_SET*/true); }).call(this.private_gScroll.private_sbStepper); (function() { // this: Group (private_gClusters) <- trash/add button this.addEventListener('click', function(ev) { var tg = ev.target; if( !tg.xType ) return; if( tg.xType != 'PngButton' ) return; if( !tg.settings.action ) return; scroller[tg.settings.action](tg.parent.index); }); }).call(this.private_gClusters); this.getItems = scroller.getItems; this.updateView = scroller.updateView; this.activate = scroller.activate; this.itemCount = scroller.itemCount; }, }; // XW INTERFACE // -------- var xwItf = {}, xt; for( xt in widgets ) xwItf[xt] = widgets[xt].ui; xwItf.connector = function(/*str*/t) { if(! (t in widgets) ) return false; return widgets[t].connect; }; xwItf.addWidget = function(/*str*/_xt, /*obj*/_wg) { widgets[_xt] = _wg; xwItf[_xt] = _wg.ui; }; return xwItf; })(); //------------------------------------------------ // THE SCRIPT //------------------------------------------------ var UserInterface = (function() { XW.addWidget('TestRack', { ui: function(ss) { var r = {_Group:{ xType: 'TestRack', settings:ss, orientation:'row', margins:[10,4,2,4], spacing:1, alignChildren: 'center', helpTip: ss.helpTip||"", private_sTest:{_StaticText:{text:'', characters:10, helpTip:"Group..."}}, private_eTest:{_EditText:{text:'',characters:20, helpTip:"Enter the Label of this group."}}, private_bTest:{_Button:{text:'',size:[100,20], helpTip:"Click here to add the label."}}, }}; return r; }, connect: function() { // <this> UI Group // 'static text' linking (function(root,sTest) { root.sTestValue = ''; root.watch('sTestValue',function(p,ov,nv) { sTest.text = nv; return nv; }); })(this, this.private_sTest); // 'edittext' linking (function(root,eTest) { root.eTestValue = ''; eTest.addEventListener('change', function(ev) { root.eTestValue = this.text; }); root.watch('eTestValue',function(p,ov,nv) { eTest.text = nv; return nv; }); })(this, this.private_eTest); // 'button' linking (function(root,bTest) { root.bTestValue = ''; bTest.addEventListener('click',function() { root.bTestValue = bTest.text; root.private_eTest.active = true; // focus on label }); root.watch('bTestValue',function(p,ov,nv) { bTest.text = nv; return nv; }); })(this, this.private_bTest); this.activate = function() { this.private_eTest.active = true; }; }, }); var ui = new Window.UI({_dialog:{ text: 'Set Group Label', properties: {closeOnKey: 'OSCmnd+W'}, orientation: 'column', plTestScroller: XW.PanelListBox({clusterType:'XW.TestRack({})', viewCount:4, maxCount:20, trashTip: "Delete this line", addTip: "Insert a new line below", defaultItem: '{sTestValue:"Group %N%", eTestValue:"Label %N%", bTestValue:"Button %N%"}', items: ['{}','{}','{}'], }), gValid: {_Group:{ margins:10, spacing:10, orientation: 'row', alignChildren: ['center','top'], bOK: {_Button: {text:"OK"}}, bCancel: {_Button: {text:"Cancel"}}, }}, }}); var w = ui.window; // tweak // --------------- w.cancelElement = ui.bCancel; w.defaultElement = ui.bOK; var r = ui.window.show(); if( r==2 ) return 'cancel'; return 'ok'; })();The important scrolling stuff is in the widgets.PanelListBox component.
@+
Marc
-
11. Re: UI panel and document CS4
Harbs. Jun 15, 2010 9:41 AM (in response to Kasyan Servetsky)Kasyan,
You didn't expect something that could be read by mere mortals. Did you?
Harbs
-
12. Re: UI panel and document CS4
[Jongware] Jun 15, 2010 1:16 PM (in response to Harbs.)I gave up when I saw how long this listing was ...
-
13. Re: UI panel and document CS4
Marc Autret Jun 15, 2010 3:55 PM (in response to [Jongware])Well, I admit that my listing is unnecessarily long to illustrate the principle. It contains a lot of extra stuff because it's based on a widget library which manages various other things. And I have not had the courage to rebuild a sample code from scratch...
However, the principle of scrolling is not that complex. Let's try to explain:
1) You create a group that only contains placeholders that I'll call clusters. You have a predefined number of clusters in the group, e.g. 4, which correspond to the maximum visible lines in the scroll area at the same time. A cluster can wrap any configuration of ScriptUI components, how ever complicated it is. The scrolling system will only interact with the cluster interface, it doesn't have to know what events occur within a cluster --it's you job to handle inner events for your specific goal (button actions, edittext behaviors, etc.).
2) Basically, you attach a Scrollbar to the cluster group and you group all in a Panel or a Group which is the root of the scrolling system. The Scrollbar events (change, changing) allow you to manage an offset between zero (minvalue) and a maxvalue which must accordingly reflects the difference between the total number of items in the system and the number of available clusters.
3) An important idea is that items and clusters are separate entities. Suppose you have 10 items to handle, and 4 available clusters. Each time a Scrollbar event occurs you must record the data changes and load items[i], items[i+1], items[i+2], and items[i+3] in the clusters, where i represents the Scrollbar offset.
4) As you have understood the scrolling system doesn't actually "scroll" anything. It emulates the effect by updating the contents of immobile widgets. The whole challenge is to link user events and data events from the root system to the inner components, and from the inner components to the root, without presuming any particular configuration within the clusters —since we want to keep the class abstract and reusable for any project). Here is the trickiest part of the code. It is based on three technical points:
a) Event propagation and management: how work listeners in capturing, bubbling, and at-target phases, how to create custom events, etc.
b) The watch method: a very powerful tool which allows you to intercept property setting in any object, including ScriptUI widgets, so you can link data management to event triggering.
c) Code injection through String objects: you can use “evaluable” string at any point of your code by using something like:
var result = (new Function('return '+myStringCode+';'))();
This technique allows to move or delay some processes into the server object while the code can only be defined at the level of the client object. I use it to create on the fly the inner contents of the clusters, or to set the default items. The scrolling system *does not* know anything about what it is actually building inside the clusters, but it knows when it must build something and how to do it, thanks to code strings provided by the client object.
That's it.
@+
Marc
-
14. Re: UI panel and document CS4
Bob Stucky Jun 15, 2010 4:18 PM (in response to Marc Autret)On Scrollable panels...
Here's a scrolling panel that I did back in the CS2 days. I have no clue if it would still work or not.
Not sure if it'll help, but here it is anyway, and it's not quite as long as Marc's example....
Good luck.
ScrollingImagePanel = function( container, title, width, height, margin, spacing ) {
this.container = container;
this.title = title;
this.width = width;
this.height = height;
this.spacing = spacing || 5;
this.margin = margin || 5;
this.group = container.add( "group" );
this.panel = this.group.add( "panel", [ 0, 0, width, height ], title, { borderStyle: "sunken" } );
this.scrollbar = this.panel.add( "scrollbar", [ ( width - 22 ), 0, ( width - 2 ), height - 10 ] );
this.objects = new Array();
this.scrollbar.objects = this.objects;
this.objSize = this.width - ( this.margin * 2 ) - 20; // total width - margins - width of scroll bar
if ( this.objSize > this.height ) {
this.objSize = this.height - ( this.margin * 2 ) - this.spacing;
}
this.scrollbar.panelHeight = this.panel.bounds.bottom - this.panel.bounds.top;
this.scrollbar.maxvalue = this.objects.length * this.objSize - this.objSize + 20;
this.scrollbar.stepdelta = this.objSize + this.spacing;
this.scrollbar.jumpdelta = this.scrollbar.panelHeight - this.objSize + this.spacing;this.scrollbar.scroll = function( scrollTo ) {
for ( var i = 0; i < this.objects.length; i++ ) {
this.objects[ i ].scroll( scrollTo );
}
}
this.scrollbar.onChange = function() {
this.scroll( this.value );
}
this.scrollbar.onChanging = function() {
this.scroll( this.value );
}
}
ScrollingImagePanel.prototype.add = function( image, imageHilited, text ) {
var btnTop = this.spacing + ( this.objects.length * ( ( this.objSize - 8 ) ) );
var bounds = [ this.margin, btnTop, this.objSize,( btnTop + this.objSize - 10 ) ];
var obj = new ScrollingObject( this, bounds, image, imageHilited, text );
obj.btn.containingWindow = this.container;
this.objects.push( obj );
this.scrollbar.maxvalue = this.objects.length * this.objSize - this.objSize + 20;
this.scrollbar.minvalue = 0;
return obj;
}
ScrollingObject = function( panel, bounds, image, imageHilited, text ) {
this.panel = panel;
this.image = image;
this.originalTop = bounds[1];
this.originalBottom = bounds[3];
this.imageHilited = imageHilited;
this.group = panel.panel.add( "panel", bounds, undefined, { panelStyle: "gray" } );
var iconSize = 50;
var wAvail = bounds[2] - bounds[0];
var hAvail = bounds[3] - bounds[1];
var imgMargin = Math.round( ( wAvail - iconSize ) / 2 );
var imageBounds = [ imgMargin, this.panel.margin, ( bounds[2] - bounds[0] - ( imgMargin ) ), ( this.panel.margin + iconSize ) ];
this.group.alignChildren = "center";
this.btn = this.group.add( "iconbutton", imageBounds, this.image, { style: "button" } );
this.btn.onClick = function() {
}
var textBounds = [ 2, imageBounds[3] + 5, bounds[2] - 12, imageBounds[3] + 45 ];
var stat = this.group.add( "statictext", textBounds, text, { multiline: true } );
stat.justify = "center";
}
ScrollingObject.prototype.scroll = function( scrollTo ) {
this.group.bounds.top = this.originalTop - scrollTo;
this.group.bounds.bottom = this.originalBottom - scrollTo;
}
ScrollingObject.prototype.toString = function() {
return "[object ScrollingObject]";
} -
15. Re: UI panel and document CS4
Kasyan Servetsky Jun 16, 2010 4:52 AM (in response to Marc Autret)This is tremendous, guys! My special thanks to Marc and Bob.
Kasyan,
You didn't expect something that could be read by mere mortals. Did you?I couldn't even imagine that it's possible to do such complex things with Script UI. And surely it's not the stuff I can understand from reading it once -- I'll have to spent some time and make some effort to sort it out.
Eh, how little I know so far!
Thank you again.
Kasyan
-
16. Re: UI panel and document CS4
Kasyan Servetsky Jun 21, 2010 3:33 AM (in response to Marc Autret)Hi Marc,
I stumbled upon a problem while exploring your script: when I run it, it stops on this line
var evChangeCount = ScriptUI.events.createEvent('UIEvent');
throwing error -- undefined is not an object
I tied to comment out the lines relating to the evChangeCount variable and the dialog appeared without an error, but obviously the script doesn't work as expected after that.
I am trying to run it on CS3 (Win). May be your script requires CS4? If not, could you give me a clue how to solve this? Your programming skills are far beyond my level, so I doubt that I can sort it out on my own.
Thank you in advance.
Kasyan
-
17. Re: UI panel and document CS4
Marc Autret Jun 21, 2010 9:18 AM (in response to Kasyan Servetsky)Hi Kasyan,
My script has been written for ID CS4/CS5 and it probably requires CS4 at least :-(
Apparently ScriptUI.events is not defined... The fact is that the UIEvent class provides a direct constructor in CS3, while CS4 requires you tu use the ScriptUI.events.createEvent() method first:
// CS3 syntax
//----------------------
var myEvent = new UIEvent (eventName, canBubble, cancelable, view, detail);
// CS4 syntax
//----------------------
var myEvent = ScriptUI.events.createEvent('UIEvent');
// then...
myEvent.initEvent(eventName, canBubble, cancelable, view, detail);
So maybe you could try to replace the wrong lines by:
// CS3 (not tested!)
var evChangeCount = new UIEvent('changeCount',true,false,true);
evChangeCount.value = 0;I'm not sure it works.
I also remember that CS3 has a critical bug in managing JS closures that I use a lot (see http://forums.adobe.com/message/2311472).
@+Marc
-
18. Re: UI panel and document CS4
Kasyan Servetsky Jun 22, 2010 6:38 AM (in response to Marc Autret)Hi Marc,
This solved the problem -- now it works like a charm.
Thank you very much.
I noticed that some guys on this forum -- e.g. you and Bob -- use object oriented approach. A few years ago I found and studied Bob's library -- BobsScriptLibrary.jsx -- (thanks to Bob a lot for sharing it, it is very useful).
In this library, he creates an object first
BobsLib = {};
And then adds new objects and methods to it
BobsLib.Log = function( logFile, includeDate ) {
...
BobsLib.Log.getDate = function() {
...
Adds new methods to predefined File and Folder objects
Folder.prototype.verify = function() {
...
Then you can include the library into your script and use these methods.
But you use a totally different approach which is difficult to comprehend for me. As far as I understand, there are two libraries in your script: DOMEX and XW -- and they are 'wrapped' inside anonymous functions.
var DOMEX = DOMEX||(function()
{
.....
})();
If variable DOMEX is false, then the function is executed and returns true. Right?
It relates to the point C in your post, doesn't it?
Could you expand more on this or point me where I can read about it?
I feel that I'm asking silly questions, but I'm a newbie in comparison with you, and want to learn how you do this.
Regards,
Kasyan
-
19. Re: UI panel and document CS4
Harbs. Jun 22, 2010 7:36 AM (in response to Kasyan Servetsky)Hi Kasyan,
While Marc is very good at what he does, I don't suggest following his style.
In my opinion, readable code is at least as important as functional code. While anonymous functions have their place, using them as much as Marc does, can make your code very difficult to read and/or debug...
Also, I'm a big fan of longer variable names where a quick glance tells you what it is...
Harbs
-
20. Re: UI panel and document CS4
Marc Autret Jun 22, 2010 9:42 AM (in response to Kasyan Servetsky)Hi Kasyan,
Indeed there are different approaches to create or extend objects. Each has its advantages and counterparts, but generally there is good reason to choose one over another.
An important point to me is to separate the implementation from the interface of an object. Think a library as a server, and the code that uses it as a client. The implementation is about what is under the hood. The client doesn't need to know how the server makes the job, it just needs to access to the interface, which is a set of public methods provided by the server.
JavaScript doesn't allow to explicitly define private vs. public class properties and methods, but we can emulate private members by wrapping the data and the functions in local variables that the interface can internally and permanently access to.
Now, by declaring a simple class (as an Object or a Function) at the global level:
BobsLib = {}; // single instance (no constructor needed)
or
BobsLib = function(){}; // allows instanciation (constructor)
you open many possibilities like adding static properties or methods (BobsLib.staticMember = ...), or declaring prototypal members (BobsLib.prototype.member = ...) to be injected into any future instance through var myInstance = new BobsLib() -- provided that you declare BobsLib as a Function (constructor).
OK. The limitation of this approach is that you won't be able to isolate and wrap the implementation, in the sense that everything remains visible on the surface. All BobsLib members are fully available to the client code, and the object itself could be changed from outside. If this is your goal, that's OK and the above syntax is the good one.
If you need more "encapsulation", you can create the library through an anonymous function which only returns the interface:
var BobsLib = (function() { // IMPLEMENTATION (PRIVATE) var myPrivateProp = /*something*/; var myPrivateMethod = function(){/*do something*/}; /*etc.*/ // INTERFACE (PUBLIC) return { myPublicProp: /*something*/, myPublicMethod: function(){/*do something*/}, /*etc.*/ }; })(); // the wrapping function is executed now!Here BobsLib is nothing more than the object returned by the anonymous function. So the client can use BobsLib.myPublicMethod(), but it has no access to the private stuff. JavaScript is a magical language in that myPublicMethod can still access to, use and handle the private stuff. For example, the myPrivateProp variable is visible from and available to the body of myPublicMethod. This mechanism is called ‘closure’ --you'll find a lot of web articles on this subject.
That's the general pattern that I use to create the XW library. It's often an effective approach to create this kind of tools because a library is basically a singleton (you don't need to create multiple instances through a constructor).
The syntax:
var XW = XW || (...);
is used to prevent the installer from being re-executed when the object already exists (in a session engine).
DOMEX works differently in that the installer only returns true, so DOMEX is not an object, it's a flag that indicates that the installer has been executed. The job of the DOMEX installer is to extend the DOM. It sets a few utilities in the global scope and create additive methods to the DOM objects, like Window.prototype.focus, etc. When DOMEX is installed (=included), I know that from any point of my code I can use the helpers functions.
There is much to say about variables scope, managing references, mutable objects, etc., but this could become very boring!...
Now, I agree with Harbs on an essential point. My coding style is only one approach among many others which are certainly as effective.Is it "human readable"? It depends on your background in programming. It is well for my situation because I have a very bad memory and I often work on large scripts, so I need a lot of modularity and reusability (the basic principle of object encapsulation). I don't think that private variables (implementation) need to be as long and friendly than the interface. That's just my opinion. The interface stuff must be as clear as possible (because it is the bridge to the object, the visible part offered to the client code), but within an implementation short variable names suit me better and allow me to write a more compact code with my own conventions.
Another advantage of (hierarchical) modularity is that I can easily expand or collapse the parts I'm working on in my code editor (UltraEdit). Conversely I have great difficulty in reading linear and/or redundant scripts which may seem more accessible to other programmers. It's really a matter of habit. Harbs believes that the structure of my code makes it difficult to debug. This is not the case for me. The internal structure is systematically arranged in the same way. Implementations are decoupled as much as possible so when something does not work I quickly found out where specific action is needed, without worry about impacting the rest of the application. Finally, I also try to optimize performance and secure my code, which implies a much narrower use of local variables, and sometimes condensed syntax that I know to be more effective.
@+
Marc
-
21. Re: UI panel and document CS4
Harbs. Jun 22, 2010 11:43 AM (in response to Marc Autret)Marc,
I didn't mean to imply that your way is "bad" at all. I love looking at your code! It's kind of poetic...
I think the bottom line is that everyone should just stick with what they are used to. Copying someone else does not offer very much gain at all (and a lot to lose in terms of programming time).The only situation where conforming to a single style is useful is on cooperative projects.
I totally agree with what you wrote about programming background. I've found my style has changed a lot as I've learned more programming languages. In terms of (re)using code, I agree that a clear interface is very important. However, when I come back to debug code that I haven't looked at in months (or years), I find that clear variables cuts down a lot on the time it takes me to figure out how it works...
Harbs
-
22. Re: UI panel and document CS4
Kasyan Servetsky Jun 23, 2010 2:51 AM (in response to Marc Autret)Hi Marc and Harbs,
Thank you both for your feedback.
Thank you, Marc, for very detailed and intelligible explanation. It's so interesting to glance behind the master's shoulder and learn new things that seemed to be impossible to do. Your posts are worth reading a dozen books! BTW, I am a regular visitor of your site -- waiting for more tips and tricks.
From your posts, I see that you have strong C programming language background -- you use terms from SDK documentation: implementation, interface, etc. Have you developed plug-ins for InDesign or other Adobe applications?
Thank you again.
Regards,
Kasyan
-
23. Re: UI panel and document CS4
Marc Autret Jun 23, 2010 6:18 AM (in response to Kasyan Servetsky)Hi Kasyan,
Thank you for the compliments ;-)
It's a pleasure to share about our programming know-how. Each brings his stone to the building, and I reciprocally learn so much here from Dave, Jongware, Harbs, Peter, you, and many others. Actually one can never claim to be a "master" in anything about programming. There are so many methodologies, technologies, sub-languages, API, to assimilate! So let's say I'm particularly interested in algorithmic aspects and abstraction. As Jongware, I practice different programming languages as a hobby for many years — and you're right that I spent more time with C, especially C++ — but strangely I haven't yet developed a single true plugin for InDesign. I came to realize over the years that the language is not the key. . . although I am really unable to return to Basic!
At the Web beginnings I really hated JavaScript. I was quite a C++ evangelist and an advocate of "pure" OOP. I discovered that I was completely wrong. Each programming language — even PHP! — brings something new in how to approach a problem, but all languages today share a common reason which leads us to another level. That's the point. The book that really opened my eyes to this subject is the famous Design Patterns: Elements of Reusable Object-Oriented Software from the "Gang of Four" (http://en.wikipedia.org/wiki/Design_Patterns). I highly recommend this reading to anyone who wants to improve its understanding of basic bricks and its capacity to build a program. Many concepts used in the InDesign SDK (for example) will be immediately familiar to you.
Turning specifically to the JavaScript, I learned most everything that I know from the JS gurus, especially David Flanagan (http://www.davidflanagan.com/), James Padolsey (http://james.padolsey.com/), Richard Cornford (http://jibbering.com/faq/notes/closures/), and obviously John Resig (http://ejohn.org/apps/learn/) and Douglas Crockford (http://www.crockford.com/). To conclude, never forget the RTFM rule ;-) http://www.ecma-international.org/publications/standards/Ecma-262.htm
@+,
Marc
-
24. Re: UI panel and document CS4
Kasyan Servetsky Jun 23, 2010 8:25 AM (in response to Marc Autret)Thanks for the interesting links, Marc. I will check them out without fail.
I, forone, familiar, more or less, with three scripting languages: JS, AS and VB.From time to time, guys on this forum argue which of them is better, but I believethat knowing all of them provides me with much more ample opportunity. I made apitiful attempt to learn C++ and InDesign SDK -- even procured debug version ofInDesign -- but without any success so far. The main problem is lack of freetime to learn it. I don't even imagine how to approach this task.
Regards,
Kasyan
-
25. Re: UI panel and document CS4
[Jongware] Jun 23, 2010 2:31 PM (in response to Kasyan Servetsky)I'm fairly proficient in C, less so with C++ -- being entirely self-taught, I marvel at Marc's constructions
The single real plugin I ever wrote was a C version of Peter Kahrel's Compose script for CS (ID 3), because I added so much combo characters to it that the javascript slowed to a crawl, especially when at the end of a document. Fortunately, that particular issue was fixed with CS2, and I'm happily using his script 3 versions of ID later.
At the time, I was impressed by some of the "freebies" it automatically gained as a plugin, such as an automatic history dropdown list (that'd be useful in the javascript version as well ...). After a particularly headache-inducing session trying to make No-Break visible (!), I've never dared touching the SDK again.







