Spec - use ctrl-S (or cmd+s) to save text entry - smalltalk

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:

Related

Spec - how to change the color (or background color) of a presenter

I want to change the background color of a SpTextInputFieldPresenter
e.g. to provide a visual feedback of the input, I want to react to whenTextChangedDo: and change the background color of the field to show if the input is correct or wrong. I know this is not the best for everybody, but I still want to try it.
How can I do without hacking?
Spec previews the use of styles to change (up to a point) how a component looks.
Styles are added to an application (an instance of SpApplication or child of it) and can be used by any presenter that is part of the application.
Styles can be seen as CSS stylesheets, and in the case of Gtk they actually are CSS stylesheets, but in the case of Morphic backend they have a complete different implementation (you can see all properties you can define in the SpPropertyStyle hierarchy.
The following code will show how to
declare styles (in a scripting way, in a production scenario styles would be likely defined in a configuration for the application).
use them by adding or removing them.
app := SpApplication new.
"If using Morphic"
app addStyleSheetFromString: '.application [
.red [ Draw { #color: #red } ],
.green [ Draw { #color: #green } ]
]'.
"If using GTK (you need to choose one, both options are not possible at the same time)"
app useBackend: #Gtk.
app addCSSProviderFromString: '
.red { background-color: red }
.green { background-color: green }
'.
presenter := SpPresenter new.
presenter application: app.
presenter layout: (SpBoxLayout newTopToBottom
add: (textPresenter := presenter newTextInput) expand: false;
addLast: (SpBoxLayout newLeftToRight
add: (presenter newButton
label: 'Red';
action: [ textPresenter removeStyle: 'green'; addStyle: 'red' ];
yourself);
add: (presenter newButton
label: 'Green';
action: [ textPresenter removeStyle: 'red'; addStyle: 'green' ];
yourself);
yourself)
expand: false;
yourself).
presenter asWindow
title: 'Example applying styles';
open
This will produce (with the Gtk3 backend) this output:

Object reactivity of complex object

I have an issue with complex object reactivity.
I've read everything I can on stack to find a way to solve it, but nothing works. I've looked at object reactvity and array caveats on vuejs, but not working either.
So I'm asking some help please.
Let me explain the project:
I have 2 columns :
- on the left side, I CRUD my content
- on the right side, I display the results
I have my object, and I'm adding new elements on its "blocks" property (text, images, etc...)
[
{
"uid": 1573224607087,
"animation": "animationName",
"background": {
"bckColor": "#ff55ee",
...
},
"blocks": []
}
]
On click event, I add a new element via this method. Everything is ok, I can CRUD a block.
addBloc(el) {
if (el.type == "text") {
const datasA = {
type: "text",
uid: Date.now(),
slideId: this.pagination.currentPage,
content: el.content,
css: {
color: "#373737",
...
},
...
};
this.slides[this.pagination.currentPage].blocks.push(datasA);
this.$bus.$emit("newElement", datasA);
}
To modify the order of my elements on the display side, I added a drag and drop module to move my block on my DOM tree. Smooth dnd
The problem is, when I drang&drop my element, my object is updated correctly, but the DOM isn't. The dragged element goes back to its initial position.
What is strange, when I try to modify my block (the one I dragged), it modifies the other one.
I'me adding a small video, so you can see what's happening.
Small animation to show you what's going on
I add some more explainations.
I use event bus to communicate between my components, and the right side is using its own object!
I don't know how I can solve this issue.
Tell me if you need more information.
Thank you all !
EDIT 1 :
I added an id to each block to see what happens when I start Drag&Drop. ==> blocks are moving correctly. The problem is not coming from the method onDrop() but from my nested components if I understand well. They don't update. I'm going to search for this new issue.
I've added a new gif to show what's going on.
This is the nested structure
TheSidebar.vue => top container
<Container
:data-index="i"
#drop="onDrop(i,$event)"
:get-child-payload="itemIndex => getChildPayload(i, itemIndex)"
lock-axis="y"
>
<Draggable
v-show="pagination.currentPage === i"
v-for="(input, index) in slides[i].blocks"
:key="index.uid"
:id="'slideBlocksContainer'+index"
class="item"
>
blockId #{{input.uid}}
<AppContainer
v-if="input.type == 'text'"
:blocType="input.type"
:placeholder="input.content"
:id="index"
:slideId="i"
></AppContainer>
</Draggable>
</Container>
Then I have my AppContainer.vue file, which is a top level. In this I have the specific elements of each input type
And I have AppElement.vue file, which is common elements, I can use everywhere
Something like this
TheSidebar
--AppContainer
----AppElement
Know I don't know yet, how to force vue to update AppContainer.vue and AppElement.vue
EDIT 2 :
As suggested in this article I've changed the key of the component and now , when I drag and drop my elements, they stay where they are dropped.
What I see also, is that the AppElement inputs, are related to their own AppContainer. So everything is ok now, but I don't know if it is best practices.
The issue appears to be that the Smooth dnd library you are using is not updating the array of blocks that you are passing to it, it is likely making a copy of the array internally. So when you change the position of the blocks by dragging and dropping, you are not changing your blocks array, just the internal copy.
Looking at the Smooth dnd documentation, if you wanted to access the modified array you could try using the drag-end event handler:
onDragEnd (dragResult) {
const { isSource, payload, willAcceptDrop } = dragResult
}

Sencha Touch 2 Component in list emptyText

I have a list component that I want to display a button to send a suggestion for the data to be included if it turns up no results.
List component itself is implemented like this:
{
xtype: 'list',
itemTpl: '{name}',
// This is not ideal!
emptyText: [
'<div class="x-button-normal x-button">',
'<span class="x-button-label">',
'Suggest <i><span id="suggest-name"></i>',
'</span>',
'</div>'
].join(''),
store: 'TheStore'
}
And this is the handler for the search field that simply sets a substring filter on the store:
'keyup': function(self, e, eOpts) {
queryString = self.getValue();
 
var store = Ext.getStore('TheStore');
store.clearFilter();
 
if(queryString){
var thisRegEx = new RegExp(queryString, "i");
store.filterBy(function(record) {
if (thisRegEx.test(record.get('name'))) {
return true;
};
return false;
});
// Changes the button so it shows name
document.getElementById('suggest-name').innerText = queryString;
}
},
Right now, I have the emptyText set to some simple HTML that emulates the look of a Sencha Touch button, but this means I have none of the button behaviour since it's not tied into the component system (such as being depressed when tapped). How can I set the emptyText attribute (or emulate it) since a proper button is displayed instead?
Try to view the two screencasts below
Sencha Touch - Intro to Nested List Component
Sencha Touch 2 -
Intro to List Component
I know it's about 2 years too late... but I ran into the same problem as #Hampus Nilsson and when I found the solution, I figured if I was running into this 2 years later, others might run into it as well.
With that said... I'm currently running Sencha Touch version 2.3.1. The solution, as it pertains to that version, was really easy to implement, just super tricky to find. The problem is that Sencha has a CSS property on the emptyText component (x-list-emptytext class) that is ignoring all pointer interactions called pointer-events: none; (who knew?!)
This property is found in:
[sdk_root]/resources/themes/stylesheets/sencha-touch/base/src/dataview/_List.scss
.x-list-emptytext {
text-align: center;
pointer-events: none; // THIS ONE!!
font-color: #333333;
#include st-box();
#include st-box-orient(vertical);
#include st-box-pack(center);
}
To fix this, simply override that property in your own sass/css. I chose to override it with pointer-events: inherit; but your mileage may vary.
THEN, all you need to do is setup a listener on your list, I recommend in the initialize function of your list class, like the so:
this.emptyTextCmp.element.on({
delegate: '.x-button-normal',
scope: this,
tap: this.yourCustomFunction
});
Where this is your list component. It's important to note that you need a "." in front of the class name of your delegate. In the above example, I set the delegate to: '.x-button-normal', because that was one of the two classes listed in the question's code. I could've also used '.x-button'. If it were me, I'd give your html an additional class, to be used as the delegate, that helps identify it a little better, instead of just using the default Sencha class as your delegate. That's an opinion, not a requirement.
That's it, I hope this helps someone else!

ExtJS How to modify the textfield parameter autocomplete="off"

Some modern (Safari, chrom, firefox) browser records informations and allows you to autocomplete some textfields when you come back.
I want to do it in ExtJS. I have a piece of answer here :
How to get Chrome to remember login on forms?
But in ExtJS, I can not access to the parameter autocomplete. It is always hard coded autocomplete="off". In the doc, I do not found how to modify it : http://docs.sencha.com/ext-js/4-1/#!/api/Ext.form.field.Text
Is someone has a simple answer to modify this parameter ?
You want to add an afterrender listener to the textfield, get a reference to the input element, and set its autocomplete attribute to "on". You probably also want to set its name (as that is how the browser remembers the value).
Example:
http://jsfiddle.net/4TSDu/19/
{
xtype:'textfield',
fieldLabel:'some field',
name:'somefield',
listeners:{
afterrender:function(cmp){
cmp.inputEl.set({
autocomplete:'on'
});
}
}
}
Note that to make LastPass work you might need to remove size attribute from textfields. For some weird reasons Sencha is setting size=1 which is weird (was that because of IE6? maybe even IE5.5?).
Anyway this makes LastPass work. I guess you might want to remove autocomplete attribute as well for other password managers. Tested with ExtJS 6.5.
{
xtype: 'textfield',
name: 'username',
fieldLabel: 'Username',
listeners:{
afterrender:function(cmp){
cmp.inputEl.dom.removeAttribute('size')
}
},
},

Set a default UI across all components in Sencha Touch

Within Sencha Touch, is it possible to define a default UI , like "light" or "dark", that applies to all components (unless overwritten explicitly)?
The aim is to avoid having to declare ui: "dark", or any custom UI that is made, for every element.
Cheers!
You can try this:
Ext.apply(Ext.Component.prototype, {
getUi: function() {
var defaultUi = 'light';
// value of [this.config.ui] is ignored here
// we can use something like forcedUi
return (this.forcedUi) ? this.forcedUi : defaultUi;
}
})
The disadvantage of this code is that we need to specify another variable for applying ui different from 'light' (because variable 'ui' via getUi() will always return 'light'):
...
items: [{
xtype: 'button',
forcedUi: 'dark'
}]
...
I am stuck on Touch 1.1 so sunsay's solution didn't work for me, but this did:
Ext.CustomToolbar = Ext.extend(Ext.Toolbar,
{
ui:'app'
});
Ext.reg('toolbar', Ext.CustomToolbar);
So, it's still component-by-component-type, but not component-by-component-instance. And since you can overwrite the "reg", no need for custom x-types all over the place, either.
I assume that you know about sencha touch styles and themes. Otherwise you can download a pdf file from this link which clearly describes about how to do it...
http://f.cl.ly/items/d9df79f57b67e6e876c6/SenchaTouchThemes.pdf
In it they are mentioning about scss file where you can specify the base-color, ie
$base-color: #4bb8f0 ;
$base-gradient: 'glossy';
Then run it ... you can see the toolbars and buttons created with the color and gradient you have mentioned.