Browse Source

Helios related changes:
- new tab switch widget and command
- modal widgets refactorings

Nicolas Petton 10 years ago
parent
commit
cf1da39dac
6 changed files with 1296 additions and 214 deletions
  1. 50 4
      js/Helios-Commands-Core.deploy.js
  2. 62 6
      js/Helios-Commands-Core.js
  3. 427 72
      js/Helios-Core.deploy.js
  4. 563 92
      js/Helios-Core.js
  5. 21 1
      st/Helios-Commands-Core.st
  6. 173 39
      st/Helios-Core.st

+ 50 - 4
js/Helios-Commands-Core.deploy.js

@@ -621,13 +621,59 @@ smalltalk.method({
 selector: "execute",
 fn: function (){
 var self=this;
-function $HLBrowser(){return smalltalk.HLBrowser||(typeof HLBrowser=="undefined"?nil:HLBrowser)}
+var activeTab;
+function $HLTabSelectionWidget(){return smalltalk.HLTabSelectionWidget||(typeof HLTabSelectionWidget=="undefined"?nil:HLTabSelectionWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+activeTab=self._selectedTab();
+$2=_st($HLTabSelectionWidget())._new();
+_st($2)._tabs_(self._tabs());
+_st($2)._selectedTab_(self._selectedTab());
+_st($2)._selectCallback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+return _st(tab)._activate();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._confirmCallback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+return _st(tab)._focus();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._cancelCallback_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(activeTab)._activate();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$3=_st($2)._show();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"execute",{activeTab:activeTab},smalltalk.HLSwitchTabCommand)})},
+messageSends: ["selectedTab", "tabs:", "tabs", "new", "selectedTab:", "selectCallback:", "activate", "confirmCallback:", "focus", "cancelCallback:", "show"]}),
+smalltalk.HLSwitchTabCommand);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab",
+fn: function (){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-$1=_st($HLBrowser())._openAsTab();
+$1=_st(_st($HLManager())._current())._activeTab();
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"execute",{},smalltalk.HLSwitchTabCommand)})},
-messageSends: ["openAsTab"]}),
+}, function($ctx1) {$ctx1.fill(self,"selectedTab",{},smalltalk.HLSwitchTabCommand)})},
+messageSends: ["activeTab", "current"]}),
+smalltalk.HLSwitchTabCommand);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs",
+fn: function (){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st($HLManager())._current())._tabs();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"tabs",{},smalltalk.HLSwitchTabCommand)})},
+messageSends: ["tabs", "current"]}),
 smalltalk.HLSwitchTabCommand);
 
 

+ 62 - 6
js/Helios-Commands-Core.js

@@ -847,16 +847,72 @@ selector: "execute",
 category: 'executing',
 fn: function (){
 var self=this;
-function $HLBrowser(){return smalltalk.HLBrowser||(typeof HLBrowser=="undefined"?nil:HLBrowser)}
+var activeTab;
+function $HLTabSelectionWidget(){return smalltalk.HLTabSelectionWidget||(typeof HLTabSelectionWidget=="undefined"?nil:HLTabSelectionWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $2,$3,$1;
+activeTab=self._selectedTab();
+$2=_st($HLTabSelectionWidget())._new();
+_st($2)._tabs_(self._tabs());
+_st($2)._selectedTab_(self._selectedTab());
+_st($2)._selectCallback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+return _st(tab)._activate();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._confirmCallback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+return _st(tab)._focus();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._cancelCallback_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(activeTab)._activate();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+$3=_st($2)._show();
+$1=$3;
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"execute",{activeTab:activeTab},smalltalk.HLSwitchTabCommand)})},
+args: [],
+source: "execute\x0a\x09| activeTab |\x0a\x09\x0a\x09activeTab := self selectedTab.\x0a\x09\x0a\x09^ HLTabSelectionWidget new\x0a\x09\x09tabs: self tabs;\x0a\x09\x09selectedTab: self selectedTab;\x0a\x09\x09selectCallback: [ :tab | tab activate ];\x0a\x09\x09confirmCallback: [ :tab | tab focus ];\x0a\x09\x09cancelCallback: [ activeTab activate ];\x0a\x09\x09show",
+messageSends: ["selectedTab", "tabs:", "tabs", "new", "selectedTab:", "selectCallback:", "activate", "confirmCallback:", "focus", "cancelCallback:", "show"],
+referencedClasses: ["HLTabSelectionWidget"]
+}),
+smalltalk.HLSwitchTabCommand);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-$1=_st($HLBrowser())._openAsTab();
+$1=_st(_st($HLManager())._current())._activeTab();
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"execute",{},smalltalk.HLSwitchTabCommand)})},
+}, function($ctx1) {$ctx1.fill(self,"selectedTab",{},smalltalk.HLSwitchTabCommand)})},
 args: [],
-source: "execute\x0a\x09^ HLBrowser openAsTab",
-messageSends: ["openAsTab"],
-referencedClasses: ["HLBrowser"]
+source: "selectedTab\x0a\x09^ HLManager current activeTab",
+messageSends: ["activeTab", "current"],
+referencedClasses: ["HLManager"]
+}),
+smalltalk.HLSwitchTabCommand);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs",
+category: 'accessing',
+fn: function (){
+var self=this;
+function $HLManager(){return smalltalk.HLManager||(typeof HLManager=="undefined"?nil:HLManager)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st($HLManager())._current())._tabs();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"tabs",{},smalltalk.HLSwitchTabCommand)})},
+args: [],
+source: "tabs\x0a\x09^ HLManager current tabs",
+messageSends: ["tabs", "current"],
+referencedClasses: ["HLManager"]
 }),
 smalltalk.HLSwitchTabCommand);
 

+ 427 - 72
js/Helios-Core.deploy.js

@@ -2333,6 +2333,63 @@ messageSends: ["model:", "new", "yourself"]}),
 smalltalk.HLToolListWidget.klass);
 
 
+smalltalk.addClass('HLTabListWidget', smalltalk.HLListWidget, ['callback'], 'Helios-Core');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "callback",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@callback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"callback",{},smalltalk.HLTabListWidget)})},
+messageSends: ["ifNil:"]}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "callback:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@callback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"callback:",{aBlock:aBlock},smalltalk.HLTabListWidget)})},
+messageSends: []}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderItemLabel:on:",
+fn: function (aTab,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(html)._with_(_st(aTab)._label());
+return self}, function($ctx1) {$ctx1.fill(self,"renderItemLabel:on:",{aTab:aTab,html:html},smalltalk.HLTabListWidget)})},
+messageSends: ["with:", "label"]}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectItem:",
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabListWidget.superclass.fn.prototype._selectItem_.apply(_st(self), [aTab]);
+_st(self._callback())._value_(aTab);
+return self}, function($ctx1) {$ctx1.fill(self,"selectItem:",{aTab:aTab},smalltalk.HLTabListWidget)})},
+messageSends: ["selectItem:", "value:", "callback"]}),
+smalltalk.HLTabListWidget);
+
+
+
 smalltalk.addClass('HLManager', smalltalk.HLWidget, ['tabs', 'activeTab', 'environment', 'history'], 'Helios-Core');
 smalltalk.addMethod(
 smalltalk.method({
@@ -2909,6 +2966,17 @@ return self}, function($ctx1) {$ctx1.fill(self,"cancel",{},smalltalk.HLModalWidg
 messageSends: ["remove"]}),
 smalltalk.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._remove();
+return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLModalWidget)})},
+messageSends: ["remove"]}),
+smalltalk.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "cssClass",
@@ -2920,6 +2988,17 @@ return "";
 messageSends: []}),
 smalltalk.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "hasButtons",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"hasButtons",{},smalltalk.HLModalWidget)})},
+messageSends: []}),
+smalltalk.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "remove",
@@ -2941,9 +3020,34 @@ smalltalk.method({
 selector: "renderButtonsOn:",
 fn: function (html){
 var self=this;
+var confirmButton;
 return smalltalk.withContext(function($ctx1) { 
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLModalWidget)})},
-messageSends: []}),
+var $1,$3,$4,$5,$6,$2;
+$1=_st(html)._div();
+_st($1)._class_("buttons");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_("button");
+_st($3)._with_("Cancel");
+$4=_st($3)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return self._cancel();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+$4;
+$5=_st(html)._button();
+_st($5)._class_("button default");
+_st($5)._with_("Confirm");
+$6=_st($5)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return self._confirm();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+confirmButton=$6;
+return confirmButton;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+_st(_st(confirmButton)._asJQuery())._focus();
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},smalltalk.HLModalWidget)})},
+messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "focus", "asJQuery"]}),
 smalltalk.HLModalWidget);
 
 smalltalk.addMethod(
@@ -2953,21 +3057,22 @@ fn: function (html){
 var self=this;
 var confirmButton;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$3,$4,$2;
+var $1,$3,$2;
 _st(_st(html)._div())._id_("overlay");
 $1=_st(html)._div();
 _st($1)._class_("dialog ".__comma(self._cssClass()));
 $2=_st($1)._with_((function(){
 return smalltalk.withContext(function($ctx2) {
-$3=self;
-_st($3)._renderMainOn_(html);
-$4=_st($3)._renderButtonsOn_(html);
-return $4;
+self._renderMainOn_(html);
+$3=self._hasButtons();
+if(smalltalk.assert($3)){
+return self._renderButtonsOn_(html);
+};
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 _st(".dialog"._asJQuery())._addClass_("active");
 self._setupKeyBindings();
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html,confirmButton:confirmButton},smalltalk.HLModalWidget)})},
-messageSends: ["id:", "div", "class:", ",", "cssClass", "with:", "renderMainOn:", "renderButtonsOn:", "addClass:", "asJQuery", "setupKeyBindings"]}),
+messageSends: ["id:", "div", "class:", ",", "cssClass", "with:", "renderMainOn:", "ifTrue:", "renderButtonsOn:", "hasButtons", "addClass:", "asJQuery", "setupKeyBindings"]}),
 smalltalk.HLModalWidget);
 
 smalltalk.addMethod(
@@ -2985,17 +3090,18 @@ smalltalk.method({
 selector: "setupKeyBindings",
 fn: function (){
 var self=this;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
 _st(".dialog"._asJQuery())._keyup_((function(e){
 return smalltalk.withContext(function($ctx2) {
-$1=_st(_st(e)._keyCode()).__eq((27));
+$1=_st(_st(e)._keyCode()).__eq(_st(_st($String())._esc())._asciiValue());
 if(smalltalk.assert($1)){
 return self._cancel();
 };
 }, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"setupKeyBindings",{},smalltalk.HLModalWidget)})},
-messageSends: ["keyup:", "ifTrue:", "cancel", "=", "keyCode", "asJQuery"]}),
+messageSends: ["keyup:", "ifTrue:", "cancel", "=", "asciiValue", "esc", "keyCode", "asJQuery"]}),
 smalltalk.HLModalWidget);
 
 smalltalk.addMethod(
@@ -3050,9 +3156,9 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st(self._cancelBlock())._value();
-self._remove();
+smalltalk.HLConfirmationWidget.superclass.fn.prototype._cancel.apply(_st(self), []);
 return self}, function($ctx1) {$ctx1.fill(self,"cancel",{},smalltalk.HLConfirmationWidget)})},
-messageSends: ["value", "cancelBlock", "remove"]}),
+messageSends: ["value", "cancelBlock", "cancel"]}),
 smalltalk.HLConfirmationWidget);
 
 smalltalk.addMethod(
@@ -3092,10 +3198,10 @@ selector: "confirm",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLConfirmationWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
 _st(self._actionBlock())._value();
-self._remove();
 return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLConfirmationWidget)})},
-messageSends: ["value", "actionBlock", "remove"]}),
+messageSends: ["confirm", "value", "actionBlock"]}),
 smalltalk.HLConfirmationWidget);
 
 smalltalk.addMethod(
@@ -3127,57 +3233,6 @@ return self}, function($ctx1) {$ctx1.fill(self,"confirmationString:",{aString:aS
 messageSends: []}),
 smalltalk.HLConfirmationWidget);
 
-smalltalk.addMethod(
-smalltalk.method({
-selector: "remove",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-_st(".dialog"._asJQuery())._removeClass_("active");
-_st((function(){
-return smalltalk.withContext(function($ctx2) {
-_st("#overlay"._asJQuery())._remove();
-return _st(".dialog"._asJQuery())._remove();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((300));
-return self}, function($ctx1) {$ctx1.fill(self,"remove",{},smalltalk.HLConfirmationWidget)})},
-messageSends: ["removeClass:", "asJQuery", "valueWithTimeout:", "remove"]}),
-smalltalk.HLConfirmationWidget);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "renderButtonsOn:",
-fn: function (html){
-var self=this;
-var confirmButton;
-return smalltalk.withContext(function($ctx1) { 
-var $1,$3,$4,$5,$6,$2;
-$1=_st(html)._div();
-_st($1)._class_("buttons");
-$2=_st($1)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
-$3=_st(html)._button();
-_st($3)._class_("button");
-_st($3)._with_("Cancel");
-$4=_st($3)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
-return self._cancel();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
-$4;
-$5=_st(html)._button();
-_st($5)._class_("button default");
-_st($5)._with_("Confirm");
-$6=_st($5)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
-return self._confirm();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
-confirmButton=$6;
-return confirmButton;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-_st(_st(confirmButton)._asJQuery())._focus();
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},smalltalk.HLConfirmationWidget)})},
-messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "focus", "asJQuery"]}),
-smalltalk.HLConfirmationWidget);
-
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderMainOn:",
@@ -3198,10 +3253,10 @@ selector: "confirm",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLRequestWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
 _st(self._actionBlock())._value_(_st(_st(self["@input"])._asJQuery())._val());
-self._remove();
 return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLRequestWidget)})},
-messageSends: ["value:", "val", "asJQuery", "actionBlock", "remove"]}),
+messageSends: ["confirm", "value:", "val", "asJQuery", "actionBlock"]}),
 smalltalk.HLRequestWidget);
 
 smalltalk.addMethod(
@@ -3309,6 +3364,17 @@ return self}, function($ctx1) {$ctx1.fill(self,"flush",{},smalltalk.HLProgressWi
 messageSends: ["do:", "removeProgressBar:", "progressBars"]}),
 smalltalk.HLProgressWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "hasButtons",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"hasButtons",{},smalltalk.HLProgressWidget)})},
+messageSends: []}),
+smalltalk.HLProgressWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isVisible",
@@ -3407,10 +3473,10 @@ $1=self._isVisible();
 if(! smalltalk.assert($1)){
 self["@visible"]=true;
 self["@visible"];
-self._appendToJQuery_("body"._asJQuery());
+smalltalk.HLProgressWidget.superclass.fn.prototype._show.apply(_st(self), []);
 };
 return self}, function($ctx1) {$ctx1.fill(self,"show",{},smalltalk.HLProgressWidget)})},
-messageSends: ["ifFalse:", "appendToJQuery:", "asJQuery", "isVisible"]}),
+messageSends: ["ifFalse:", "show", "isVisible"]}),
 smalltalk.HLProgressWidget);
 
 
@@ -3435,16 +3501,305 @@ messageSends: ["ifNil:", "new"]}),
 smalltalk.HLProgressWidget.klass);
 
 
-smalltalk.addClass('HLTabSelectionWidget', smalltalk.HLModalWidget, [], 'Helios-Core');
+smalltalk.addClass('HLTabSelectionWidget', smalltalk.HLModalWidget, ['tabs', 'tabList', 'selectedTab', 'selectCallback', 'cancelCallback', 'confirmCallback'], 'Helios-Core');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancel",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._cancel.apply(_st(self), []);
+_st(self._cancelCallback())._value();
+return self}, function($ctx1) {$ctx1.fill(self,"cancel",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["cancel", "value", "cancelCallback"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelCallback",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@cancelCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"cancelCallback",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["ifNil:"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelCallback:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@cancelCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"cancelCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
+_st(self._confirmCallback())._value_(self._selectedTab());
+return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["confirm", "value:", "selectedTab", "confirmCallback"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirmCallback",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@confirmCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"confirmCallback",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["ifNil:"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirmCallback:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@confirmCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"confirmCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderContentOn:",
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._renderContentOn_.apply(_st(self), [html]);
+_st(self._tabList())._focus();
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["renderContentOn:", "focus", "tabList"]}),
+smalltalk.HLTabSelectionWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderMainOn:",
 fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(html)._with_("hello");
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_("title");
+$2=_st($1)._with_("Tab selection");
+_st(html)._with_(self._tabList());
 return self}, function($ctx1) {$ctx1.fill(self,"renderMainOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
-messageSends: ["with:"]}),
+messageSends: ["class:", "div", "with:", "tabList"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderTab:on:",
+fn: function (aTab,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(html)._span();
+_st($1)._class_(_st(aTab)._cssClass());
+$2=_st($1)._with_(_st(aTab)._label());
+return self}, function($ctx1) {$ctx1.fill(self,"renderTab:on:",{aTab:aTab,html:html},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["class:", "cssClass", "span", "with:", "label"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderTabsOn:",
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+_st(self._tabs())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(html)._li())._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+$1=_st(html)._a();
+_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx4) {
+return self._renderTab_on_(each,html);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3)})}));
+$2=_st($1)._onClick_((function(){
+return smalltalk.withContext(function($ctx4) {
+return self._selectTab_(each);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3)})}));
+return $2;
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderTabsOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["do:", "with:", "renderTab:on:", "a", "onClick:", "selectTab:", "li", "tabs"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectCallback",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@selectCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"selectCallback",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["ifNil:"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectCallback:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@selectCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"selectCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectTab:",
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._selectedTab_(aTab);
+_st(self._selectCallback())._value_(aTab);
+return self}, function($ctx1) {$ctx1.fill(self,"selectTab:",{aTab:aTab},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["selectedTab:", "value:", "selectCallback"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@selectedTab"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"selectedTab",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab:",
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@selectedTab"]=aTab;
+return self}, function($ctx1) {$ctx1.fill(self,"selectedTab:",{aTab:aTab},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "setupKeyBindings",
+fn: function (){
+var self=this;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._setupKeyBindings.apply(_st(self), []);
+_st(".dialog"._asJQuery())._keyup_((function(e){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(_st(e)._keyCode()).__eq(_st(_st($String())._cr())._asciiValue());
+if(smalltalk.assert($1)){
+return self._confirm();
+};
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"setupKeyBindings",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["setupKeyBindings", "keyup:", "ifTrue:", "confirm", "=", "asciiValue", "cr", "keyCode", "asJQuery"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabList",
+fn: function (){
+var self=this;
+function $HLTabListWidget(){return smalltalk.HLTabListWidget||(typeof HLTabListWidget=="undefined"?nil:HLTabListWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+$1=self["@tabList"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self["@tabList"]=_st($HLTabListWidget())._new();
+self["@tabList"];
+$2=self["@tabList"];
+_st($2)._callback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+self._selectTab_(tab);
+return _st(self["@tabList"])._focus();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._selectedItem_(self._selectedTab());
+$3=_st($2)._items_(self._tabs());
+$3;
+} else {
+$1;
+};
+$4=self["@tabList"];
+return $4;
+}, function($ctx1) {$ctx1.fill(self,"tabList",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["ifNil:", "new", "callback:", "selectTab:", "focus", "selectedItem:", "selectedTab", "items:", "tabs"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@tabs"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=[];
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"tabs",{},smalltalk.HLTabSelectionWidget)})},
+messageSends: ["ifNil:"]}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs:",
+fn: function (aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@tabs"]=aCollection;
+return self}, function($ctx1) {$ctx1.fill(self,"tabs:",{aCollection:aCollection},smalltalk.HLTabSelectionWidget)})},
+messageSends: []}),
 smalltalk.HLTabSelectionWidget);
 
 

+ 563 - 92
js/Helios-Core.js

@@ -3079,6 +3079,84 @@ referencedClasses: []
 smalltalk.HLToolListWidget.klass);
 
 
+smalltalk.addClass('HLTabListWidget', smalltalk.HLListWidget, ['callback'], 'Helios-Core');
+smalltalk.HLTabListWidget.comment="I am a widget used to display a list of helios tabs.\x0a\x0aWhen a tab is selected, `callback` is evaluated with the selected tab as argument.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "callback",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@callback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"callback",{},smalltalk.HLTabListWidget)})},
+args: [],
+source: "callback\x0a\x09^ callback ifNil: [ [] ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "callback:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@callback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"callback:",{aBlock:aBlock},smalltalk.HLTabListWidget)})},
+args: ["aBlock"],
+source: "callback: aBlock\x0a\x09callback := aBlock",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderItemLabel:on:",
+category: 'rendering',
+fn: function (aTab,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(html)._with_(_st(aTab)._label());
+return self}, function($ctx1) {$ctx1.fill(self,"renderItemLabel:on:",{aTab:aTab,html:html},smalltalk.HLTabListWidget)})},
+args: ["aTab", "html"],
+source: "renderItemLabel: aTab on: html\x0a\x09html with: aTab label",
+messageSends: ["with:", "label"],
+referencedClasses: []
+}),
+smalltalk.HLTabListWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectItem:",
+category: 'actions',
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabListWidget.superclass.fn.prototype._selectItem_.apply(_st(self), [aTab]);
+_st(self._callback())._value_(aTab);
+return self}, function($ctx1) {$ctx1.fill(self,"selectItem:",{aTab:aTab},smalltalk.HLTabListWidget)})},
+args: ["aTab"],
+source: "selectItem: aTab\x0a\x09super selectItem: aTab.\x0a\x09self callback value: aTab",
+messageSends: ["selectItem:", "value:", "callback"],
+referencedClasses: []
+}),
+smalltalk.HLTabListWidget);
+
+
+
 smalltalk.addClass('HLManager', smalltalk.HLWidget, ['tabs', 'activeTab', 'environment', 'history'], 'Helios-Core');
 smalltalk.addMethod(
 smalltalk.method({
@@ -3816,6 +3894,22 @@ referencedClasses: []
 }),
 smalltalk.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm",
+category: 'actions',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._remove();
+return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLModalWidget)})},
+args: [],
+source: "confirm\x0a\x09\x22Override in subclasses\x22\x0a\x09self remove",
+messageSends: ["remove"],
+referencedClasses: []
+}),
+smalltalk.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "cssClass",
@@ -3832,6 +3926,22 @@ referencedClasses: []
 }),
 smalltalk.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "hasButtons",
+category: 'rendering',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return true;
+}, function($ctx1) {$ctx1.fill(self,"hasButtons",{},smalltalk.HLModalWidget)})},
+args: [],
+source: "hasButtons\x0a\x09^ true",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "remove",
@@ -3859,11 +3969,36 @@ selector: "renderButtonsOn:",
 category: 'rendering',
 fn: function (html){
 var self=this;
+var confirmButton;
 return smalltalk.withContext(function($ctx1) { 
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},smalltalk.HLModalWidget)})},
+var $1,$3,$4,$5,$6,$2;
+$1=_st(html)._div();
+_st($1)._class_("buttons");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(html)._button();
+_st($3)._class_("button");
+_st($3)._with_("Cancel");
+$4=_st($3)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return self._cancel();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+$4;
+$5=_st(html)._button();
+_st($5)._class_("button default");
+_st($5)._with_("Confirm");
+$6=_st($5)._onClick_((function(){
+return smalltalk.withContext(function($ctx3) {
+return self._confirm();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+confirmButton=$6;
+return confirmButton;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
+_st(_st(confirmButton)._asJQuery())._focus();
+return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},smalltalk.HLModalWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html",
-messageSends: [],
+source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: 'Cancel';\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: 'Confirm';\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09confirmButton asJQuery focus",
+messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "focus", "asJQuery"],
 referencedClasses: []
 }),
 smalltalk.HLModalWidget);
@@ -3876,23 +4011,24 @@ fn: function (html){
 var self=this;
 var confirmButton;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$3,$4,$2;
+var $1,$3,$2;
 _st(_st(html)._div())._id_("overlay");
 $1=_st(html)._div();
 _st($1)._class_("dialog ".__comma(self._cssClass()));
 $2=_st($1)._with_((function(){
 return smalltalk.withContext(function($ctx2) {
-$3=self;
-_st($3)._renderMainOn_(html);
-$4=_st($3)._renderButtonsOn_(html);
-return $4;
+self._renderMainOn_(html);
+$3=self._hasButtons();
+if(smalltalk.assert($3)){
+return self._renderButtonsOn_(html);
+};
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
 _st(".dialog"._asJQuery())._addClass_("active");
 self._setupKeyBindings();
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html,confirmButton:confirmButton},smalltalk.HLModalWidget)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div id: 'overlay'.\x0a\x09html div \x0a\x09\x09class: 'dialog ', self cssClass;\x0a\x09\x09with: [\x0a\x09\x09\x09self\x0a\x09\x09\x09\x09renderMainOn: html;\x0a\x09\x09\x09\x09renderButtonsOn: html ].\x0a\x0a\x09'.dialog' asJQuery addClass: 'active'.\x0a\x09self setupKeyBindings",
-messageSends: ["id:", "div", "class:", ",", "cssClass", "with:", "renderMainOn:", "renderButtonsOn:", "addClass:", "asJQuery", "setupKeyBindings"],
+source: "renderContentOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div id: 'overlay'.\x0a\x09html div \x0a\x09\x09class: 'dialog ', self cssClass;\x0a\x09\x09with: [\x0a\x09\x09\x09self renderMainOn: html.\x0a\x09\x09\x09self hasButtons ifTrue: [ \x0a\x09\x09\x09\x09self renderButtonsOn: html ] ].\x0a\x0a\x09'.dialog' asJQuery addClass: 'active'.\x0a\x09self setupKeyBindings",
+messageSends: ["id:", "div", "class:", ",", "cssClass", "with:", "renderMainOn:", "ifTrue:", "renderButtonsOn:", "hasButtons", "addClass:", "asJQuery", "setupKeyBindings"],
 referencedClasses: []
 }),
 smalltalk.HLModalWidget);
@@ -3918,20 +4054,21 @@ selector: "setupKeyBindings",
 category: 'rendering',
 fn: function (){
 var self=this;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
 _st(".dialog"._asJQuery())._keyup_((function(e){
 return smalltalk.withContext(function($ctx2) {
-$1=_st(_st(e)._keyCode()).__eq((27));
+$1=_st(_st(e)._keyCode()).__eq(_st(_st($String())._esc())._asciiValue());
 if(smalltalk.assert($1)){
 return self._cancel();
 };
 }, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"setupKeyBindings",{},smalltalk.HLModalWidget)})},
 args: [],
-source: "setupKeyBindings\x0a\x09'.dialog' asJQuery keyup: [ :e |\x0a\x09\x09e keyCode = 27 ifTrue: [ self cancel ] ]",
-messageSends: ["keyup:", "ifTrue:", "cancel", "=", "keyCode", "asJQuery"],
-referencedClasses: []
+source: "setupKeyBindings\x0a\x09'.dialog' asJQuery keyup: [ :e |\x0a\x09\x09e keyCode = String esc asciiValue ifTrue: [ self cancel ] ]",
+messageSends: ["keyup:", "ifTrue:", "cancel", "=", "asciiValue", "esc", "keyCode", "asJQuery"],
+referencedClasses: ["String"]
 }),
 smalltalk.HLModalWidget);
 
@@ -4004,11 +4141,11 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 _st(self._cancelBlock())._value();
-self._remove();
+smalltalk.HLConfirmationWidget.superclass.fn.prototype._cancel.apply(_st(self), []);
 return self}, function($ctx1) {$ctx1.fill(self,"cancel",{},smalltalk.HLConfirmationWidget)})},
 args: [],
-source: "cancel\x0a\x09self cancelBlock value.\x0a\x09self remove",
-messageSends: ["value", "cancelBlock", "remove"],
+source: "cancel\x0a\x09self cancelBlock value.\x0a\x09super cancel",
+messageSends: ["value", "cancelBlock", "cancel"],
 referencedClasses: []
 }),
 smalltalk.HLConfirmationWidget);
@@ -4061,12 +4198,12 @@ category: 'actions',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLConfirmationWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
 _st(self._actionBlock())._value();
-self._remove();
 return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLConfirmationWidget)})},
 args: [],
-source: "confirm\x0a\x09self actionBlock value.\x0a\x09self remove",
-messageSends: ["value", "actionBlock", "remove"],
+source: "confirm\x0a\x09super confirm.\x0a\x09self actionBlock value",
+messageSends: ["confirm", "value", "actionBlock"],
 referencedClasses: []
 }),
 smalltalk.HLConfirmationWidget);
@@ -4110,67 +4247,6 @@ referencedClasses: []
 }),
 smalltalk.HLConfirmationWidget);
 
-smalltalk.addMethod(
-smalltalk.method({
-selector: "remove",
-category: 'actions',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-_st(".dialog"._asJQuery())._removeClass_("active");
-_st((function(){
-return smalltalk.withContext(function($ctx2) {
-_st("#overlay"._asJQuery())._remove();
-return _st(".dialog"._asJQuery())._remove();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}))._valueWithTimeout_((300));
-return self}, function($ctx1) {$ctx1.fill(self,"remove",{},smalltalk.HLConfirmationWidget)})},
-args: [],
-source: "remove\x0a\x09'.dialog' asJQuery removeClass: 'active'.\x0a\x09[ \x0a\x09\x09'#overlay' asJQuery remove.\x0a\x09\x09'.dialog' asJQuery remove\x0a\x09] valueWithTimeout: 300",
-messageSends: ["removeClass:", "asJQuery", "valueWithTimeout:", "remove"],
-referencedClasses: []
-}),
-smalltalk.HLConfirmationWidget);
-
-smalltalk.addMethod(
-smalltalk.method({
-selector: "renderButtonsOn:",
-category: 'rendering',
-fn: function (html){
-var self=this;
-var confirmButton;
-return smalltalk.withContext(function($ctx1) { 
-var $1,$3,$4,$5,$6,$2;
-$1=_st(html)._div();
-_st($1)._class_("buttons");
-$2=_st($1)._with_((function(){
-return smalltalk.withContext(function($ctx2) {
-$3=_st(html)._button();
-_st($3)._class_("button");
-_st($3)._with_("Cancel");
-$4=_st($3)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
-return self._cancel();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
-$4;
-$5=_st(html)._button();
-_st($5)._class_("button default");
-_st($5)._with_("Confirm");
-$6=_st($5)._onClick_((function(){
-return smalltalk.withContext(function($ctx3) {
-return self._confirm();
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
-confirmButton=$6;
-return confirmButton;
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})}));
-_st(_st(confirmButton)._asJQuery())._focus();
-return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},smalltalk.HLConfirmationWidget)})},
-args: ["html"],
-source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: 'Cancel';\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: 'Confirm';\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09confirmButton asJQuery focus",
-messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "focus", "asJQuery"],
-referencedClasses: []
-}),
-smalltalk.HLConfirmationWidget);
-
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderMainOn:",
@@ -4198,12 +4274,12 @@ category: 'actions',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLRequestWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
 _st(self._actionBlock())._value_(_st(_st(self["@input"])._asJQuery())._val());
-self._remove();
 return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLRequestWidget)})},
 args: [],
-source: "confirm\x0a\x09self actionBlock value: input asJQuery val.\x0a\x09self remove",
-messageSends: ["value:", "val", "asJQuery", "actionBlock", "remove"],
+source: "confirm\x0a\x09super confirm.\x0a\x09self actionBlock value: input asJQuery val",
+messageSends: ["confirm", "value:", "val", "asJQuery", "actionBlock"],
 referencedClasses: []
 }),
 smalltalk.HLRequestWidget);
@@ -4349,6 +4425,22 @@ referencedClasses: []
 }),
 smalltalk.HLProgressWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "hasButtons",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return false;
+}, function($ctx1) {$ctx1.fill(self,"hasButtons",{},smalltalk.HLProgressWidget)})},
+args: [],
+source: "hasButtons\x0a\x09^ false",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLProgressWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "isVisible",
@@ -4473,12 +4565,12 @@ $1=self._isVisible();
 if(! smalltalk.assert($1)){
 self["@visible"]=true;
 self["@visible"];
-self._appendToJQuery_("body"._asJQuery());
+smalltalk.HLProgressWidget.superclass.fn.prototype._show.apply(_st(self), []);
 };
 return self}, function($ctx1) {$ctx1.fill(self,"show",{},smalltalk.HLProgressWidget)})},
 args: [],
-source: "show\x0a\x09self isVisible ifFalse: [\x0a\x09\x09visible := true.\x0a\x09\x09self appendToJQuery: 'body' asJQuery ]",
-messageSends: ["ifFalse:", "appendToJQuery:", "asJQuery", "isVisible"],
+source: "show\x0a\x09self isVisible ifFalse: [\x0a\x09\x09visible := true.\x0a\x09\x09super show ]",
+messageSends: ["ifFalse:", "show", "isVisible"],
 referencedClasses: []
 }),
 smalltalk.HLProgressWidget);
@@ -4510,8 +4602,141 @@ referencedClasses: []
 smalltalk.HLProgressWidget.klass);
 
 
-smalltalk.addClass('HLTabSelectionWidget', smalltalk.HLModalWidget, [], 'Helios-Core');
-smalltalk.HLTabSelectionWidget.comment="I am used to select or create tabs.";
+smalltalk.addClass('HLTabSelectionWidget', smalltalk.HLModalWidget, ['tabs', 'tabList', 'selectedTab', 'selectCallback', 'cancelCallback', 'confirmCallback'], 'Helios-Core');
+smalltalk.HLTabSelectionWidget.comment="I am a modal window used to select or create tabs.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancel",
+category: 'actions',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._cancel.apply(_st(self), []);
+_st(self._cancelCallback())._value();
+return self}, function($ctx1) {$ctx1.fill(self,"cancel",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "cancel\x0a\x09super cancel.\x0a\x09self cancelCallback value",
+messageSends: ["cancel", "value", "cancelCallback"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelCallback",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@cancelCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"cancelCallback",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "cancelCallback\x0a\x09^ cancelCallback ifNil: [ [] ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelCallback:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@cancelCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"cancelCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+args: ["aBlock"],
+source: "cancelCallback: aBlock\x0a\x09cancelCallback := aBlock",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm",
+category: 'actions',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._confirm.apply(_st(self), []);
+_st(self._confirmCallback())._value_(self._selectedTab());
+return self}, function($ctx1) {$ctx1.fill(self,"confirm",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "confirm\x0a\x09super confirm.\x0a\x09self confirmCallback value: self selectedTab",
+messageSends: ["confirm", "value:", "selectedTab", "confirmCallback"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirmCallback",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@confirmCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"confirmCallback",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "confirmCallback\x0a\x09^ confirmCallback ifNil: [ [] ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirmCallback:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@confirmCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"confirmCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+args: ["aBlock"],
+source: "confirmCallback: aBlock\x0a\x09confirmCallback := aBlock",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderContentOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._renderContentOn_.apply(_st(self), [html]);
+_st(self._tabList())._focus();
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
+args: ["html"],
+source: "renderContentOn: html\x0a\x09super renderContentOn: html.\x0a\x09self tabList focus",
+messageSends: ["renderContentOn:", "focus", "tabList"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderMainOn:",
@@ -4519,11 +4744,257 @@ category: 'rendering',
 fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(html)._with_("hello");
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_("title");
+$2=_st($1)._with_("Tab selection");
+_st(html)._with_(self._tabList());
 return self}, function($ctx1) {$ctx1.fill(self,"renderMainOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
 args: ["html"],
-source: "renderMainOn: html\x0a\x09html with: 'hello'",
-messageSends: ["with:"],
+source: "renderMainOn: html\x0a\x09html div \x0a\x09\x09class: 'title'; \x0a\x09\x09with: 'Tab selection'.\x0a\x09\x0a\x09html with: self tabList",
+messageSends: ["class:", "div", "with:", "tabList"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderTab:on:",
+category: 'rendering',
+fn: function (aTab,html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(html)._span();
+_st($1)._class_(_st(aTab)._cssClass());
+$2=_st($1)._with_(_st(aTab)._label());
+return self}, function($ctx1) {$ctx1.fill(self,"renderTab:on:",{aTab:aTab,html:html},smalltalk.HLTabSelectionWidget)})},
+args: ["aTab", "html"],
+source: "renderTab: aTab on: html\x0a\x09html \x0a\x09\x09span \x0a\x09\x09\x09class: aTab cssClass;\x0a\x09\x09\x09with: aTab label",
+messageSends: ["class:", "cssClass", "span", "with:", "label"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderTabsOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+_st(self._tabs())._do_((function(each){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(html)._li())._with_((function(){
+return smalltalk.withContext(function($ctx3) {
+$1=_st(html)._a();
+_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx4) {
+return self._renderTab_on_(each,html);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3)})}));
+$2=_st($1)._onClick_((function(){
+return smalltalk.withContext(function($ctx4) {
+return self._selectTab_(each);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3)})}));
+return $2;
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2)})}));
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"renderTabsOn:",{html:html},smalltalk.HLTabSelectionWidget)})},
+args: ["html"],
+source: "renderTabsOn: html\x0a\x09self tabs do: [ :each |\x0a\x09\x09html li with: [ \x0a\x09\x09\x09html a \x0a\x09\x09\x09\x09with: [ \x0a\x09\x09\x09\x09\x09self renderTab: each on: html ];\x0a\x09\x09\x09\x09onClick: [ self selectTab: each ] ] ]",
+messageSends: ["do:", "with:", "renderTab:on:", "a", "onClick:", "selectTab:", "li", "tabs"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectCallback",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@selectCallback"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=(function(){
+return smalltalk.withContext(function($ctx2) {
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1)})});
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"selectCallback",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "selectCallback\x0a\x09^ selectCallback ifNil: [ [] ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectCallback:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@selectCallback"]=aBlock;
+return self}, function($ctx1) {$ctx1.fill(self,"selectCallback:",{aBlock:aBlock},smalltalk.HLTabSelectionWidget)})},
+args: ["aBlock"],
+source: "selectCallback: aBlock\x0a\x09selectCallback := aBlock",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectTab:",
+category: 'actions',
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._selectedTab_(aTab);
+_st(self._selectCallback())._value_(aTab);
+return self}, function($ctx1) {$ctx1.fill(self,"selectTab:",{aTab:aTab},smalltalk.HLTabSelectionWidget)})},
+args: ["aTab"],
+source: "selectTab: aTab\x0a\x09self selectedTab: aTab.\x0a\x09self selectCallback value: aTab",
+messageSends: ["selectedTab:", "value:", "selectCallback"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@selectedTab"];
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"selectedTab",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "selectedTab\x0a\x09^ selectedTab",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "selectedTab:",
+category: 'accessing',
+fn: function (aTab){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@selectedTab"]=aTab;
+return self}, function($ctx1) {$ctx1.fill(self,"selectedTab:",{aTab:aTab},smalltalk.HLTabSelectionWidget)})},
+args: ["aTab"],
+source: "selectedTab: aTab\x0a\x09selectedTab := aTab",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "setupKeyBindings",
+category: 'actions',
+fn: function (){
+var self=this;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+smalltalk.HLTabSelectionWidget.superclass.fn.prototype._setupKeyBindings.apply(_st(self), []);
+_st(".dialog"._asJQuery())._keyup_((function(e){
+return smalltalk.withContext(function($ctx2) {
+$1=_st(_st(e)._keyCode()).__eq(_st(_st($String())._cr())._asciiValue());
+if(smalltalk.assert($1)){
+return self._confirm();
+};
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"setupKeyBindings",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "setupKeyBindings\x0a\x09super setupKeyBindings.\x0a\x09'.dialog' asJQuery keyup: [ :e |\x0a\x09\x09e keyCode = String cr asciiValue ifTrue: [ self confirm ] ]",
+messageSends: ["setupKeyBindings", "keyup:", "ifTrue:", "confirm", "=", "asciiValue", "cr", "keyCode", "asJQuery"],
+referencedClasses: ["String"]
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabList",
+category: 'rendering',
+fn: function (){
+var self=this;
+function $HLTabListWidget(){return smalltalk.HLTabListWidget||(typeof HLTabListWidget=="undefined"?nil:HLTabListWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+$1=self["@tabList"];
+if(($receiver = $1) == nil || $receiver == undefined){
+self["@tabList"]=_st($HLTabListWidget())._new();
+self["@tabList"];
+$2=self["@tabList"];
+_st($2)._callback_((function(tab){
+return smalltalk.withContext(function($ctx2) {
+self._selectTab_(tab);
+return _st(self["@tabList"])._focus();
+}, function($ctx2) {$ctx2.fillBlock({tab:tab},$ctx1)})}));
+_st($2)._selectedItem_(self._selectedTab());
+$3=_st($2)._items_(self._tabs());
+$3;
+} else {
+$1;
+};
+$4=self["@tabList"];
+return $4;
+}, function($ctx1) {$ctx1.fill(self,"tabList",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "tabList\x0a\x09tabList ifNil: [ \x0a\x09\x09tabList := HLTabListWidget new.\x0a\x09\x09tabList\x0a\x09\x09\x09callback: [ :tab | self selectTab: tab. tabList focus ];\x0a\x09\x09\x09selectedItem: self selectedTab;\x0a\x09\x09\x09items: self tabs ].\x0a\x09\x0a\x09^ tabList",
+messageSends: ["ifNil:", "new", "callback:", "selectTab:", "focus", "selectedItem:", "selectedTab", "items:", "tabs"],
+referencedClasses: ["HLTabListWidget"]
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@tabs"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=[];
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"tabs",{},smalltalk.HLTabSelectionWidget)})},
+args: [],
+source: "tabs\x0a\x09^ tabs ifNil: [ #() ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+smalltalk.HLTabSelectionWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "tabs:",
+category: 'accessing',
+fn: function (aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self["@tabs"]=aCollection;
+return self}, function($ctx1) {$ctx1.fill(self,"tabs:",{aCollection:aCollection},smalltalk.HLTabSelectionWidget)})},
+args: ["aCollection"],
+source: "tabs: aCollection\x0a\x09tabs := aCollection",
+messageSends: [],
 referencedClasses: []
 }),
 smalltalk.HLTabSelectionWidget);

+ 21 - 1
st/Helios-Commands-Core.st

@@ -277,10 +277,30 @@ HLCommand subclass: #HLSwitchTabCommand
 	instanceVariableNames: ''
 	package: 'Helios-Commands-Core'!
 
+!HLSwitchTabCommand methodsFor: 'accessing'!
+
+selectedTab
+	^ HLManager current activeTab
+!
+
+tabs
+	^ HLManager current tabs
+! !
+
 !HLSwitchTabCommand methodsFor: 'executing'!
 
 execute
-	^ HLBrowser openAsTab
+	| activeTab |
+	
+	activeTab := self selectedTab.
+	
+	^ HLTabSelectionWidget new
+		tabs: self tabs;
+		selectedTab: self selectedTab;
+		selectCallback: [ :tab | tab activate ];
+		confirmCallback: [ :tab | tab focus ];
+		cancelCallback: [ activeTab activate ];
+		show
 ! !
 
 !HLSwitchTabCommand class methodsFor: 'accessing'!

+ 173 - 39
st/Helios-Core.st

@@ -1089,6 +1089,37 @@ on: aModel
         yourself
 ! !
 
+HLListWidget subclass: #HLTabListWidget
+	instanceVariableNames: 'callback'
+	package: 'Helios-Core'!
+!HLTabListWidget commentStamp!
+I am a widget used to display a list of helios tabs.
+
+When a tab is selected, `callback` is evaluated with the selected tab as argument.!
+
+!HLTabListWidget methodsFor: 'accessing'!
+
+callback
+	^ callback ifNil: [ [] ]
+!
+
+callback: aBlock
+	callback := aBlock
+! !
+
+!HLTabListWidget methodsFor: 'actions'!
+
+selectItem: aTab
+	super selectItem: aTab.
+	self callback value: aTab
+! !
+
+!HLTabListWidget methodsFor: 'rendering'!
+
+renderItemLabel: aTab on: html
+	html with: aTab label
+! !
+
 HLWidget subclass: #HLManager
 	instanceVariableNames: 'tabs activeTab environment history'
 	package: 'Helios-Core'!
@@ -1351,6 +1382,11 @@ cancel
 	self remove
 !
 
+confirm
+	"Override in subclasses"
+	self remove
+!
+
 remove
 	'.dialog' asJQuery removeClass: 'active'.
 	[ 
@@ -1365,7 +1401,26 @@ show
 
 !HLModalWidget methodsFor: 'rendering'!
 
+hasButtons
+	^ true
+!
+
 renderButtonsOn: html
+	| confirmButton |
+	
+	html div 
+		class: 'buttons';
+		with: [
+			html button
+				class: 'button';
+				with: 'Cancel';
+				onClick: [ self cancel ].
+			confirmButton := html button
+				class: 'button default';
+				with: 'Confirm';
+				onClick: [ self confirm ] ].
+
+	confirmButton asJQuery focus
 !
 
 renderContentOn: html
@@ -1375,9 +1430,9 @@ renderContentOn: html
 	html div 
 		class: 'dialog ', self cssClass;
 		with: [
-			self
-				renderMainOn: html;
-				renderButtonsOn: html ].
+			self renderMainOn: html.
+			self hasButtons ifTrue: [ 
+				self renderButtonsOn: html ] ].
 
 	'.dialog' asJQuery addClass: 'active'.
 	self setupKeyBindings
@@ -1388,7 +1443,7 @@ renderMainOn: html
 
 setupKeyBindings
 	'.dialog' asJQuery keyup: [ :e |
-		e keyCode = 27 ifTrue: [ self cancel ] ]
+		e keyCode = String esc asciiValue ifTrue: [ self cancel ] ]
 ! !
 
 HLModalWidget subclass: #HLConfirmationWidget
@@ -1429,42 +1484,16 @@ confirmationString: aString
 
 cancel
 	self cancelBlock value.
-	self remove
+	super cancel
 !
 
 confirm
-	self actionBlock value.
-	self remove
-!
-
-remove
-	'.dialog' asJQuery removeClass: 'active'.
-	[ 
-		'#overlay' asJQuery remove.
-		'.dialog' asJQuery remove
-	] valueWithTimeout: 300
+	super confirm.
+	self actionBlock value
 ! !
 
 !HLConfirmationWidget methodsFor: 'rendering'!
 
-renderButtonsOn: html
-	| confirmButton |
-	
-	html div 
-		class: 'buttons';
-		with: [
-			html button
-				class: 'button';
-				with: 'Cancel';
-				onClick: [ self cancel ].
-			confirmButton := html button
-				class: 'button default';
-				with: 'Confirm';
-				onClick: [ self confirm ] ].
-
-	confirmButton asJQuery focus
-!
-
 renderMainOn: html
 	html span with: self confirmationString
 ! !
@@ -1494,8 +1523,8 @@ value: aString
 !HLRequestWidget methodsFor: 'actions'!
 
 confirm
-	self actionBlock value: input asJQuery val.
-	self remove
+	super confirm.
+	self actionBlock value: input asJQuery val
 ! !
 
 !HLRequestWidget methodsFor: 'rendering'!
@@ -1565,7 +1594,7 @@ removeProgressBar: aProgressBar
 show
 	self isVisible ifFalse: [
 		visible := true.
-		self appendToJQuery: 'body' asJQuery ]
+		super show ]
 ! !
 
 !HLProgressWidget methodsFor: 'rendering'!
@@ -1577,6 +1606,10 @@ renderMainOn: html
 
 !HLProgressWidget methodsFor: 'testing'!
 
+hasButtons
+	^ false
+!
+
 isVisible
 	^ visible ifNil: [ false ]
 ! !
@@ -1590,15 +1623,116 @@ default
 ! !
 
 HLModalWidget subclass: #HLTabSelectionWidget
-	instanceVariableNames: ''
+	instanceVariableNames: 'tabs tabList selectedTab selectCallback cancelCallback confirmCallback'
 	package: 'Helios-Core'!
 !HLTabSelectionWidget commentStamp!
-I am used to select or create tabs.!
+I am a modal window used to select or create tabs.!
+
+!HLTabSelectionWidget methodsFor: 'accessing'!
+
+cancelCallback
+	^ cancelCallback ifNil: [ [] ]
+!
+
+cancelCallback: aBlock
+	cancelCallback := aBlock
+!
+
+confirmCallback
+	^ confirmCallback ifNil: [ [] ]
+!
+
+confirmCallback: aBlock
+	confirmCallback := aBlock
+!
+
+selectCallback
+	^ selectCallback ifNil: [ [] ]
+!
+
+selectCallback: aBlock
+	selectCallback := aBlock
+!
+
+selectedTab
+	^ selectedTab
+!
+
+selectedTab: aTab
+	selectedTab := aTab
+!
+
+tabs
+	^ tabs ifNil: [ #() ]
+!
+
+tabs: aCollection
+	tabs := aCollection
+! !
+
+!HLTabSelectionWidget methodsFor: 'actions'!
+
+cancel
+	super cancel.
+	self cancelCallback value
+!
+
+confirm
+	super confirm.
+	self confirmCallback value: self selectedTab
+!
+
+selectTab: aTab
+	self selectedTab: aTab.
+	self selectCallback value: aTab
+!
+
+setupKeyBindings
+	super setupKeyBindings.
+	'.dialog' asJQuery keyup: [ :e |
+		e keyCode = String cr asciiValue ifTrue: [ self confirm ] ]
+! !
 
 !HLTabSelectionWidget methodsFor: 'rendering'!
 
+renderContentOn: html
+	super renderContentOn: html.
+	self tabList focus
+!
+
 renderMainOn: html
-	html with: 'hello'
+	html div 
+		class: 'title'; 
+		with: 'Tab selection'.
+	
+	html with: self tabList
+!
+
+renderTab: aTab on: html
+	html 
+		span 
+			class: aTab cssClass;
+			with: aTab label
+!
+
+renderTabsOn: html
+	self tabs do: [ :each |
+		html li with: [ 
+			html a 
+				with: [ 
+					self renderTab: each on: html ];
+				onClick: [ self selectTab: each ] ] ]
+!
+
+tabList
+	tabList ifNil: [ 
+		tabList := HLTabListWidget new.
+		tabList
+			callback: [ :tab | self selectTab: tab. tabList focus ];
+			selectedItem: self selectedTab;
+			items: self tabs ].
+	
+	^ tabList
 ! !
 
 HLWidget subclass: #HLProgressBarWidget