I have an SpTablePresenter and I would like to edit the cell contents.
In many frameworks it is possible to edit the contents of a table directly in place, without needing to open a new component (dialog or master-detail style). How can I do this with Spec ?
Tables and tree tables in Spec inplement a mechanism to edit string columns in place
in Spec, string columns can be edited by just declaring the column to be editable sending the beEditable message and adding a callback to process the edition by sending onAcceptEdition:, which receives two parameters, the object being editing and the edited string.
The following code will show how this can be done:
app := SpApplication new.
app useBackend: #Gtk.
presenter := SpPresenter new.
presenter application: app.
presenter layout: (SpBoxLayout newTopToBottom
add: (tablePresenter := presenter newTable);
yourself).
tablePresenter
addColumn: (SpStringTableColumn title: 'R/O' evaluated: #key);
addColumn: ((SpStringTableColumn
title: 'Editable'
evaluated: #value)
beEditable;
onAcceptEdition: [ :anAssociation :aString | anAssociation value: aString ];
yourself).
tablePresenter items: { 1 -> 'One'. 2 -> 'Two'. 3 -> 'Three' }.
presenter asWindow
title: 'Example editing cells';
open
This will produce (with the Gtk3 backend) this output:
Related
is there a way in SpTextPresenter and SpTextInputFieldPresenter to use ctrl+S (or cmd+S in mac) to save text entry?
Old pharo components (notably old spec but this comes since before, when components when built on plain morphic) were allowing to "accept" contents by pressing <meta+S> (and cancelling the edition by using <meta+L>).
Is there a way to replicate this behavior in current Spec?
Spec permits to define "default" submit/reset events to provide old behavior.
Ok, this is an easy answer, but somehow complicated for some users since they expect the old behavior and this does not works like that anymore.
So first I need to explain why old behavior is no longer available :)
Thing is, old components where a mix of different things: they were plain UI widgets while also model containers in the spirit of old MVC (Model View Controller). So they mixed Model (the keep of the status) and the view (the display of the component). For this reason, old components had an initial status and you needed to accept that status (you got it, using <meta+S>) to make it being transferred to the model part.
This mix of responsibilities lead to different workarounds, like the addition of the autoAccept property to make the component copy its value it change of it.
When designing the new version of Spec we decided to not keep this behavior that looked hacky and was causing inconsistencies in the API and in consequence anyone wanting the old behavior need to make it explicitly in their own components.
So, how to get old behavior?
This is the question after all!
We have added two methods to allow somehow same functionality: whenSubmitDo: and whenResetDo:. This can be combined with whenTextChangedDo: to mark/unmark a dirty property.
Here is an example, Is a little bit verbose, but is also easy to create your own components with this behavior predefined and reuse them in your application:
app := SpApplication new.
"If using Morphic"
app addStyleSheetFromString: '.application [
.dirty [ Container { #borderColor: #red, #borderWidth: 1 } ],
.notDirty [ Container { #borderColor: #transparent, #borderWidth: 1 } ]
]'.
"If using GTK (you need to choose one, both options are not possible at the same time)"
app useBackend: #Gtk.
app addCSSProviderFromString: '
.dirty {
border-color: red;
border-width: 1px; }
'.
presenter := SpPresenter new.
presenter application: app.
presenter layout: (SpBoxLayout newTopToBottom
add: (textPresenter := presenter newTextInput) expand: false;
yourself).
text := ''.
textPresenter
text: text;
whenTextChangedDo: [ :aString |
aString = text
ifTrue: [ textPresenter removeStyle: 'dirty'; addStyle: 'notDirty' ]
ifFalse: [ textPresenter removeStyle: 'notDirty'; addStyle: 'dirty' ] ];
whenSubmitDo: [
text := textPresenter text.
('Submitted ', text) crTrace.
textPresenter
removeStyle: 'dirty';
addStyle: 'notDirty' ];
whenResetDo: [
textPresenter
text: text;
removeStyle: 'dirty';
addStyle: 'notDirty' ].
presenter asWindow
title: 'Example submit/reset text component';
open
This will produce (with the Gtk3 backend) this output:
I have to provide a copy/paste functionality using the dagre layout. The idea is the user copies a node and where ever they decide to "paste" it, the hierarchy of nodes copied, will be created there. This would mean that all nodes in the way would have to move.
I first thought maybe I could call layout again but that doesn't "fit" them in.
I'm still learning cytoscape.js so if this is a simple question, please excuse me.
This would be something quite hard to achieve, so hear me out:
First step:
Cytoscape has a extension called context-menus (demo)
Another extension is called clipboard (demo)
make yourself familiar with these two
Second step:
create your graph with these two extensions and a dagre graph
when you copy these nodes, make the copy function behave like this:
add the nodes with their edges in the right hierarchy
when you right click on a node, add a insert into hierarchy function, which adds the copied nodes into the graph
Code examples:
var options1 = {
// List of initial menu items
menuItems: [
{
id: 'addToHierarchy',
content: 'Add to hierarchy',
tooltipText: 'Add nodes to hierarchy here',
selector: 'node',
onClickFunction: function () {
// your handlerFunction
},
disabled: false
}
]
};
var instance = cy.contextMenus( options1 );
var options2 = {
clipboardSize: 0,
// The following 4 options allow the user to provide custom behavior to
// the extension. They can be used to maintain consistency of some data
// when elements are duplicated.
// These 4 options are set to null by default. The function prototypes
// are provided below for explanation purpose only.
// Function executed on the collection of elements being copied, before
// they are serialized in the clipboard
beforeCopy: function(eles) {},
// Function executed on the clipboard just after the elements are copied.
// clipboard is of the form: {nodes: json, edges: json}
afterCopy: function(clipboard) {},
// Function executed on the clipboard right before elements are pasted,
// when they are still in the clipboard.
beforePaste: function(clipboard) {},
// Function executed on the collection of pasted elements, after they
// are pasted.
afterPaste: function(eles) {}
};
var clipboard = cy.clipboard(options2);
I'm modifying the IterationSummary app from the 2.0RC3 SDK, and adding more iteration info to it. For some reason, I am not able to retrieve the 'Theme' for the iteration, although I am able to query for other fields from the iteration object. Starting with the sample, I simply added the following lines #192
{
cls: 'theme',
html: iteration.get('Theme')
},
I can get 'Name' but I can't get the 'Theme' value even though it is clearly set on the iteration, and I verified that value using the REST API to query that same iteration. And querying other fields such as 'Name' works well. Any idea why 'Theme' is not being returned?
Do you fetch 'Theme' ?
You may see a general example that builds a grid of iterations that fall within a release (selected in the releasecombobox) that has Theme column populated as long as an iteration has a theme entered is in this github repo.
This example is different from the IterationSummary app because in my example I explicitly create Rally.data.wsapi.Store for Iteration object and fetch Theme.
Customizing the IterationSummary app will still require explicit fetching of Theme field, but you are correct that it is not obvious from the IterationSummary app how other fields are being fetched, e.g. State of iteration. The iteration object in that app is returned from this.getContext().getTimeboxScope().getRecord() and if you print out that object in the console, theme will be empty. The fields that exist on this.getContext().getTimeboxScope().getRecord() are limited and cannot be customized for performance reasons.
In order to modify this app to display Theme, the Iteration model has to be accessed and Theme fetched explicitly. Here are the steps I took to modify the app:
added getTheme function:
getTheme: function(){
var iteration = this.getContext().getTimeboxScope().getRecord();
return iteration.self.load(iteration.getId(), {
fetch: ['Theme']
});
}
In rc3 every time when we have a record, .self will give its model, so there is no need to do this manually:
Rally.data.ModelFactory.getModel({
type: 'Iteration',
//...
Note fetch: ['Theme']
Next, inside _addContent method getTheme() is called
return Deft.Promise.all([this.getTheme(), this.calculateTimeboxInfo()]).then({
success: function(results) {
var theme = results[0].get('Theme');
and then finally theme variable's value is passed to:
{
cls: 'theme',
html: theme
}
The full code is available in this github repo.
I want to create a controller with a ref to a specific item in a List, is this possible?
So really I need the ComponentQuery to reference a listitem. I am setting the data inline in the list:
Ext.define('MyApp.view.MyView', {
extend : 'Ext.List',
alias : 'widget.mylist',
config : {
title : 'Title',
itemTpl : ['{displayName}'].join(''),
data : [{
displayName : 'One',
id : 1
}, {
displayName : 'Two',
id : 2
}, {
displayName : 'Three',
id : 3
}, {
displayName : 'Four',
id : 4
}]
}
});
Then on in my controller (this is obviously not working):
refs : {
oneListItem: 'mylist.list[id=1]',
twoListItem: 'mylist.list[id=2]',
threeListItem: 'mylist.list[id=3]',
fourListItem: 'mylist.list[id=4]'
}
Hopefully this is possible. If it is not I can listen for an 'itemtap' on the list and call a method from there, however, I would prefer not have to do that for cleanliness.
Thanks in advance.
Brad
First off: adding id to the objects in your data section will not have any effect, that is just the data that's taken and substituted in the itemTpl you specified before.
As to your question: Sencha Touch 2 Documentation does not mention a way to have Ext.ComponentQuery select the n-th child of a Component, so, assuming you want to create your list objects with inline data, the only way I see is to use the id that Sencha automatically applies to each listItem on creation.
These ids are of the #ext-listitem-1 kind, see an example of how to use them in this Sencha Fiddle demo.
This is however bad practice, because you cannot rely on those id, if for example you add another list to your application they can change breaking your code.
I do not see any valid reason why you shouldn't use itemtap to act on the list, since with the event you get all the data you need to manipulate the proper list item:
itemtap( this, index, target, record, e, eOpts )
For completeness sake, I must mention that you could also add your items dynamically with Ext.dataview.List.add(), that way you should be able to create your dataitems with a custom id.
You might try Ext.select('.x-list-item-first'); or Ext.select('.x-list-item-last'); if you want a handle on the first or last item. I would encourage you to add an xtype set to "mylist" instead of using alias.
How can i change a xtype in Sencha Architect?
Example:
from:
xtype: 'list'
to
xtype: 'RefreshableList'
As a disclaimer, I am one of the lead engineers on the Sencha Architect product.
Drag out a List as a top level component. All top level components are their own classes. Set the userAlias and userClassName configurations to values like 'refreshablelist' and 'RefreshableList'. Take a look at the code generated for this.
Drag out a Panel as a top level component, drag the existing RefreshableList in the inspector into the new Panel. A prompt will ask you if you would like to Move, Copy or Link the list, choose "Link". This will create an instance of your subclass RefreshableList.
This is currently the best way of going about this task inside of Architect. In the case that you built your RefreshableList component outside of Architect and would like to link it in the process will be a little different. You will have to create an override and change the xtype you are instantiating there. We are attempting to address this limitation in Sencha Architect 2.2. You will be able to specify what we are currently calling a createAlias. This is what alias (xtype, ptype, type, etc) to create.
For example if you dragged out a Panel and then put a list inside of it, you could then select the list in the inspector and configure the createAlias to 'RefreshableList'. This will replace the xtype in the generated code from 'list' to 'RefreshableList'. It will NOT change what is rendered on the canvas inside of Architect. You would have to load your RefreshableList class via a JS Resource and/or the dynamic loader/requires functionality.
You have to create your own class by extending the list class and give it your own widget alias. This tutorial has all you need: http://www.sencha.com/learn/how-to-use-classes-in-sencha-touch-2/
UPDATE
Here is some code for a very basic custom list
//this follows the MVC structure, if you wanted you could also do something like ux.RefreshableList
Ext.define('myAppName.view.RefreshableList', {
extend: 'Ext.dataview.List',
xtype: 'RefreshableList',
config: {
fullscreen: true,
itemTpl: '{title}',
data: [
{ title: 'Item 1' },
{ title: 'Item 2' },
{ title: 'Item 3' },
{ title: 'Item 4' }
]
},
initialize: function() {
this.callParent();
}
});