• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Creating a Stepper with ScriptUI?

Guest
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

Has anyone created a stepper using ScriptUI? I'm working on a palette that provides an alternative to using the Margins and Columns window that also helps build strong typographic grids (kind of a combination Margins, Columns and Create Guides window). I don't want the palette to be too different than the existing Margins and Columns window, but ScriptUI doesn't seem to provide a simple way to create a stepper widget like is currently used in the Margins and Columns windows:

Margins and Columns.jpg

Am I just missing it in the scripting guide books, or does this type of widget require scripting it from scratch?

Thanks for any help you can provide.

TOPICS
Scripting

Views

1.7K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

You pretty much have to script it from scratch. Sorry.

I am attaching a script that I wrote for CS4 that makes a ScriptUI edit text act like a measurement edit box. It allows you to use arrow keys to increment/decrement. That should at least help get you started.

Regards

Bob

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

Attempting to attach the file again...

Tried to upload twice, failed twice. Email me for the code...

bostucky@adobe.com

Bob

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

Bob (Adobe Engineer) wrote:

You pretty much have to script it from scratch. Sorry.

I am attaching a script that I wrote for CS4 that makes a ScriptUI edit text act like a measurement edit box. It allows you to use arrow keys to increment/decrement. That should at least help get you started.

Regards

Bob

Hi Bob,

What ScriptUI widget(s) do you use as the base of the stepper? Buttons?

Thanks.

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

That script wasn't a "stepper" control. It was an approximation if ID's measurement edit control.

My experience was that when I tried to write custom controls (including drawing the controls), things that worked in the ESTK did not always work in InDesign. I've since been using PatchPanel and CSXS, and pretty much stopped using ScriptUI.

Regards

Bob

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

I'm working on a similar script and I've created an eXtendedWidget class to encapsulate the component you need

In the rough sample code below, checkout the usage of ScrollEditText.

ScrollEditText can even emulates the behavior of a measurementEditBox, with specified units and min/max :

myStepper: XW.ScrollEditText({title:"Top:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),

To see how it works, try this:

var toUIString = function()
     { // <this> : ui object
     var r = this.toSource().
          replace(/\\u([0-9a- f]{4})/gi, function(_,$1){return String.fromCharCode(Number('0x'+$1));} ).
          replace(/(?:\{_([^:]+):)/g,'$1').
          replace(/\}\}/g,'}').
          replace(/(^\()|(\)$ )/g,'');
     return r;
     };

Window.prototype.focus = (function()
{ //Window.prototype.focus
var getControls = function()
     {
     var r = [];
     for ( var i=0, c ; i<this.children.length ; i++ )
          {
          c = this.children;
          if (c.constructor == StaticText) continue;      // exclude StaticText focus
          if (c.constructor == Scrollbar) continue;     // exclude Scrollbar focus
          if ('active' in c) {r.push(c);continue;}
          r = r.concat(arguments.callee.call(c));
          }
     return r;
     };
return function(/*int*/step)
     {
     this.controls = this.controls || getControls.call(this);
     var sz = this.controls.length;
     for (var i=0 ; i<sz ; i++)
          {
          if (this.controls.active == true) break;
          }
     if (!step) return this.controls;
     i = (i+step+sz)%sz;
     step = (step<0)?-1:1;
     while(!this.controls.enabled) i+=step;
     this.controls.active = true;
     return this.controls;
     };
})();


var XW = (function()
// Extended Widgets
//------------------------------------------------
{
var widgets = {}, xWidgets = {};
    
var xWidget = function(/*str*/xType)
     { // constructor
     this.xType = xType;
     this.widget = widgets[this.xType];
     };
xWidget.prototype.ui = function(settings_)
     {
     var ui = this.widget.ui(settings_);
     var ac = this.widget.access;
     ui[ac].settings = settings_;
     ui[ac].xType = this.xType;
     return ui;
     };
xWidget.prototype.connect = function(parent)
     {
     var c = parent.children;
     for (var i = c.length-1 ; i>=0 ; i-- )
          {
          if ( c.xType && (c.xType == this.xType) )
             {
             this.widget.connect.call(c);
             continue;
             }
          if (c.children.length) this.connect(c);
          }
     };


// DropListBox
// --------
widgets.DropListBox =
     {
     access: '_Group',
     ui: function(settings_)
          {return {_Group:{
             margins:0, spacing:0, orientation: 'row', alignChildren: ['left','center'],
             lTit: (settings_.title)?{_StaticText:{characters:12,text:settings_.title,justify:'right'}}:null,
             lSpa: (settings_.title)?{_StaticText:{characters:1}}:null,
             ddList: {_DropDownList: {maximumSize:[100,25]}}
             }};
          },
     connect: function()
          { // <this> UI Group
          (function(items_)
             { // ddList : load items
             for(var i=0, tp ; i<items_.length ; i++)
                  {
                  tp = (items_ == '-') ? 'separator' : 'item';
                  this.add(tp,items_);
                  }
             }).call(this.ddList,this.settings.items);
          }
     };


// ScrollEditText  (this is your 'stepper')
// --------
widgets.ScrollEditText =
     {
     access: '_Group',
     ui: function(settings_)
          {return {_Group:{
             margins:0, spacing:0, orientation: 'row', alignChildren: ['left','center'],
             lTit: (settings_.title)?{_StaticText:{characters:12,text:settings_.title,justify:'right'}}:null,
             lSpa: (settings_.title)?{_StaticText:{characters:1}}:null,
             sBar: {_Scrollbar:{size:[16,24]}},
             eTxt: {_EditText:{characters:settings_.characters||8}}
             }};
          },
     connect: function()
          { // <this> Group
          (function()
             {
             this.units = this.units||'';
             this.decimals = (typeof this.decimals=='undefined')?-1:this.decimals;
             this.characters = this.characters||6;
             this.step = this.step||1;
             this.jump = this.jump||this.step*5;
             }).call(this.settings);

          this.parseUnit = (this.settings.units)?
             function(/*str*/s)
                  {
                  var m = s.match(/^-?(\d+)([cp])([\d\.]+)/i); // #p# ou #c#
                  if (m && m.length) s = ((m[0][0]=='-')?'-':'') + ((m[1]-0)+(m[3]/12)) + ((m[2]=='c')?'ci':'pc');
                  m = s.match(/agt|cm|ci|in|mm|pc|pt/i);
                  return (m && m.length) ?
                       UnitValue (s).as(this.settings.units) :
                       Number(s. replace(/[^0-9\.-]/g,''));
                  }:
             function(/*str*/s) {return Number(s.replace(/[^0-9\.-]/g,''));};

          this.parseStrValue = function(/*str*/s)
             {
             var v = this.parseUnit(s.replace(',','.'));
             return (isNaN(v)) ? null : v;
             };

          this.fixDecimals = (this.settings.decimals >=0 )?
             function(/*num*/v){return v.toFixed(this.settings.decimals)-0;}:
             function(/*num*/v){return v;};

          this.displayValue = (this.settings.units)?
             function(/*num*/v)
                  {
                  var u = this.settings.units;
                  if ( u=='pc' || u=='ci' )
                       { // #,#pc -> $p$  ou  #,#ci -> $c$
                       var sg = (v<0)?-1:1;
                       v = v*sg;
                       var sx = Math.floor(v);
                       var sy = (v-sx)*12;
                       return sx*sg + u[0] + sy.toLocaleString().substr(0,this.settings.characters);
                       }
                  else
                       {
                       var s = v.toLocaleString().substr(0,this.settings.characters);
                       return s + ' ' + u;
                       }
                  } :
             function(/*num*/v)
                  {
                  return v.toLocaleString().substr(0,this.settings.characters);
                  };

          this.settings.minvalue = (this.settings.minvalue)?this.parseStrValue(''+this.settings.minvalue):0;
          this.settings.maxvalue = (this.settings.maxvalue)?this.parseStrValue(''+this.settings.maxvalue):100;
          if (typeof this.settings.value == 'undefined') this.settings.value = this.fixDecimals(this.settings.minvalue);

          this.offsetValue = function(/*num*/delta)
             {
             this.changeValue(this.eTxt.text,'NO_DISPLAY');
             this.changeValue(Math.round(this.settings.value + delta));
             };
         
          this.changeValue = function(/*var*/vs,/*var*/noDisplay,/*var*/noEvent)
             {
             var v = (typeof vs == 'string') ? this.parseStrValue(vs) : vs;
             v = ( typeof v == 'number' ) ? this.fixDecimals(v) : this.settings.value;
             if ( v < this.settings.minvalue ) v = this.settings.minvalue;
             if ( v > this.settings.maxvalue ) v = this.settings.maxvalue;
             var noChange = (this.settings.value == v);
             if (!noChange) this.settings.value = v;
             if (!noDisplay) this.eTxt.text = this.displayValue(this.settings.value);
             if ((!noChange) && (!noEvent)) this.eTxt.notify();
             };

          (function()
             { // scrollbar
             this.minvalue = -1;
             this.maxvalue = 1;
             this.value = 0;
             this.onChanging = function()
                  {
                  this.parent.offsetValue(-this.value*this.parent.settings.step);
                  this.value = 0;
                  };
             }).call(this.sBar);
         
          (function()
             { // edittext
             this.addEventListener('blur', function(ev)
                  { // lost focus
                  this.parent.changeValue(this.text);
                  });
             this.addEventListener('keydown', function(ev)
                  { // up/down keys
                  var delta = 1;
                  switch(ev.keyName)
                       {
                       case 'Down' : delta = -1;
                       case 'Up' :
                        delta *= this.parent.settings.step;
                        if (ev.shiftKey) delta *= this.parent.settings.jump;
                        this.parent.offsetValue(delta);
                        break;
                       default:;
                       }
                  });
             }).call(this.eTxt);
         
          this.changeValue();
          }
     };

// XW interface
// --------
var r = {};
for(var w in widgets)
     {
     r = (function()
          {
          var w_ = w;
          return function(/*obj*/ settings)
             {
             xWidgets[w_] = xWidgets[w_] || new xWidget(w_);
             return xWidgets[w_].ui(settings);
             };
          })();
     };
r.connectAll = function(/*Window*/parent)
     {
     for each(var xw in xWidgets) xw.connect(parent);
     }
return r;
})();



//==================================================
// sample code
//==================================================

// if you need a palette Window, replace _dialog by _palette
// (and use #targetengine)

var ui = toUIString.call({_dialog:{
     text: "Column Rules",
     properties: {closeOnKey: 'OSCmnd+W'},
     orientation: 'row', alignChildren: ['fill','top'],
     gTextFrame: {_Group:{
          margins:0, spacing:10, orientation: 'column',
          pInsetSpacing: {_Panel:{
             margins:10, spacing:2, alignChildren: ['fill','fill'],
             text: "Inset Spacing",
             seTop: XW.ScrollEditText({title:"Top:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),
             seBot: XW.ScrollEditText({title:"Bottom:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),
             cLinked: {_Checkbox:{text: "Linked"}},
             seLeft: XW.ScrollEditText({title:"Left:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),
             seRight: XW.ScrollEditText({title:"Right:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),
             }},
          pColumns: {_Panel:{
             margins:10, spacing:2, alignChildren: ['fill','fill'],
             text: "Columns",
             seNumber: XW.ScrollEditText({title:"Number:",minvalue:1,maxvalue:40}),
             seGutter: XW.ScrollEditText({title:"Gutter:",minvalue:0,maxvalue:'8640pt',decimals:3,units:'mm'}),
             }},
          cIgnoreTextWrap: {_Checkbox:{text: "Ignore Text Wrap", alignment:'left'}},
          }},
     gProcess: {_Group:{
          margins:10, spacing:8, alignChildren: ['center','fill'],
          orientation: 'column',
          gValid: {_Group:{
             margins:10, spacing:10, orientation: 'row',
             bOK: {_Button: {text:"OK"}},
             bCancel: {_Button: {text:"Cancel"}},
             }},
          }},
     }});

// create the main window
// ---------------------------
var w = new Window(ui);

// connect XWidgets
// ---------------------------
XW.connectAll(w);

// manage the tab key
// ---------------------------
(function(){
     this.addEventListener('keydown', function(ev)
          { // Tab
          if (ev.keyName == 'Tab')
             {
             ev.preventDefault();
             this.focus( ((ev.shiftKey)?-1:1 ) );
             }
          });
     }).call(w);

// controls event manager
// ...
// here you add the event listeners for w.gTextFrame.pInsetSpacing, etc.
// ...

w.show();

You will notice I use a special format rather than UI string resource. This allows to create compact object declaration, using finally the toUIString helper function. The XW object invokes also that stuff.

The ScrollEditText component encapsulates the stepper+editText association. You don't have to manage the interaction. Each time the value changes, the component notify a change event on the editText target.

Hope it could hep you.

@+

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

Marc:

Thanks so much for the code. If anything, your approach to building a UI is awesome and will surely be helpful to not only me but others. Great work.

However, on my Mac (running CS4 on Leopard), your code produced this:

Picture 1.png

Instead of a scrollbar, should it be two buttons?

You've given me a lot to play with, though, so thank you.

I'm surprised this sort of widget, given how prevalent it is throughout the CS interface, is not standard within ScriptUI.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

Well, interesting result

Here is the capture for the same code on Win OS :

dialog.png

As said, my script is still a work-in-progress. Apparently, I'll have to deal with cross-platform issues --always the same old story with scriptUI layout...

Using one scrollbar for each up/down stepper may not be the good solution, but perhaps that's just a "cosmetic" problem and the underlying implementation could stay the same...

I'm surprised this sort of widget, given how prevalent it is throughout the CS interface, is not standard within ScriptUI.


You're so right !

@+

Marc

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Oct 12, 2009 Oct 12, 2009

Copy link to clipboard

Copied

LATEST

Hey Marc,

can you give me some pointers on moving an object (or group) incrementally based on location within a book.

ie a graphic element that moves down the page as book progresses.

new to scripting in InDesign but have basic code skills.

on a MAC but could run this on PC. Noticed the cross platfrom issues before.

Cheers

D

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Sep 30, 2009 Sep 30, 2009

Copy link to clipboard

Copied

I'm surprised this sort of widget, given how prevalent it is throughout the CS interface, is not standard within ScriptUI.

Then I guess you'll really be surprised to know that even in the SDK this widget doesn't exist. Only by combining a EditBoxWidget with a NudgeControlWidget and placing them right next to each other and linking them do you get your stepper widget.

I f anything the question is if we can get the NudgeControlWidget for scriptUI.

Steven Bryant

http://scriptui.com

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines