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 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:
I use vuecli3 and vuetify,
the problem is offset in RTL have margin-left instead of right.
Also
i config properly my vuetify.js file and html lang direction too.
Vue.use(Vuetify, {
iconfont: 'md',
rtl: true,
});
Also i tried to add these line to main.styl file
grid-offset-rtl()
for $size in $grid-breakpoints
for n in (0..$grid-columns)
&.offset-{$size}{n}
margin-left: 0
margin-right: (n / $grid-columns * 100)%
rtl(grid-offset-rtl, "flex")
but still doesn't work.
just update Vueitfy package using npm, it is fixed now!
npm update vuetify
I simply solved that by using conditional props
<v-col :offset-lg="isRtl ? 2 : 0" lg="5">
isRtl is computed property that checks for rtl mode:
isRtl() {
return this.$vuetify.rtl;
}
Where can I get a list of the style properties supported by React Native for each type of component?
Here's a cheatsheet:
React Native Styling Cheat Sheet
The supported styles are in the official documentation for each component. Here are the links for View and Text components:
View : https://reactnative.dev/docs/view#style
Text: https://reactnative.dev/docs/text#style
Note that where it says View Style Props... on the top of styles for Text, it means it also supports (most of) the styles that View supports.
I have extracted a list of valid styles from react native logs.
Below collection help me alot during regular work.
[
"alignContent",
"alignItems",
"alignSelf",
"aspectRatio",
"backfaceVisibility",
"backgroundColor",
"borderBottomColor",
"borderBottomLeftRadius",
"borderBottomRightRadius",
"borderBottomWidth",
"borderColor",
"borderLeftColor",
"borderLeftWidth",
"borderRadius",
"borderRightColor",
"borderRightWidth",
"borderStyle",
"borderTopColor",
"borderTopLeftRadius",
"borderTopRightRadius",
"borderTopWidth",
"borderWidth",
"bottom",
"color",
"decomposedMatrix",
"direction",
"display",
"elevation",
"flex",
"flexBasis",
"flexDirection",
"flexGrow",
"flexShrink",
"flexWrap",
"fontFamily",
"fontSize",
"fontStyle",
"fontVariant",
"fontWeight",
"height",
"includeFontPadding",
"justifyContent",
"left",
"letterSpacing",
"lineHeight",
"margin",
"marginBottom",
"marginHorizontal",
"marginLeft",
"marginRight",
"marginTop",
"marginVertical",
"maxHeight",
"maxWidth",
"minHeight",
"minWidth",
"opacity",
"overflow",
"overlayColor",
"padding",
"paddingBottom",
"paddingHorizontal",
"paddingLeft",
"paddingRight",
"paddingTop",
"paddingVertical",
"position",
"resizeMode",
"right",
"rotation",
"scaleX",
"scaleY",
"shadowColor",
"shadowOffset",
"shadowOpacity",
"shadowRadius",
"textAlign",
"textAlignVertical",
"textDecorationColor",
"textDecorationLine",
"textDecorationStyle",
"textShadowColor",
"textShadowOffset",
"textShadowRadius",
"tintColor",
"top",
"transform",
"transformMatrix",
"translateX",
"translateY",
"width",
"writingDirection",
"zIndex"
]
Implementation details are here:
https://github.com/vhpoet/react-native-styling-cheat-sheet/blob/master/README.md
Please consider this simple rebol2 code to illustrate my problem:
REBOL []
a: make face [
offset: 0x0
color: yellow
size: 20x20
]
b: make face [
offset: 0x0
color: red
size: 60x60
pane: reduce [
make a [offset: 0x0]
make a [offset: 10x10]
make a [offset: 10x20]
]
]
view layout [
box 200x200 white with [
pane: reduce [
make b [offset: 0x30] ;; one 'instance' of b
]
]
]
The main point here is for a layout (or face) to be able to display a bunch of faces inside its pane block in such a manner that multiple creations of the same face (b in this case) should be possible. The shown code works well, and the only instance (let me call it this way) of b is displayed as it should be.
But now suppose I change the code so I have, say, 2 instances of b:
view layout [
box 200x200 white with [
pane: reduce [
make b [offset: 0x30]
make b [offset: 0x10]
]
]
]
At this point I get the error
** Script Error: Face object reused (in more than one pane): none
** Where: view
** Near: show scr-face
if new [do-events]
From the message I presume here that face b is somehow getting reused and messing exactly what I'm trying to achieve. I've done lots of research on this and at some point I found that it is possible to get around it by cloning (using make) the face to be passed to pane; that's what I thought I was doing, but with no success at all.
Given this scenario, my question is: how can I go about to solve this? is rebol2 ok to provide this "face-instantiation" or it is best to try something else outside rebol2 (perhaps rebol3)?
Any help will be greatly appreciated.
Rebol2 is definitely ok to do this.
When you MAKE b second time you are using the same instance of a. That is the problem.
You can write a function that creates necessary faces and append them to a block and return.
Don't forget to create 'a (first face) every time.
Additionally, check for the iterated faces in the documentation.
Here I added an example:
REBOL []
make-pane: func [ofst [pair! block!] /local a b faces] [
a: make face [
offset: 0x0
color: yellow
size: 20x20
]
faces: copy []
either block? ofst [
foreach o ofst [
append faces make a [offset: o]
]
] [
append faces make a [offset: ofst]
]
b: make face [
offset: 0x0
color: red
size: 60x60
pane: faces
]
]
view layout [
box 200x200 white with [
pane: make-pane [5x30 0x10 20x5]
]
]
You can change the function to get more parameters to change color and other facets as well.
As is already pointed out the problem is that a is reused, not b!
the layout function uses a field called init for handling things like this. As I understand it init is first bound to the face and then called with do after the face itself is instantiated (at least partially).
In this case I would be using the style command in layout (still partially using face object a )
view layout [
style
bb box 60x60
with [
append init [
pane reduce [
make a [offset: 0x0]
make a [offset: 10x10]
make a [offset: 10x20]
]
]
]
panel 200x200 white [
at 30x0 bb
at 0x0 bb
]
]
The other alternative, a bit more similar to your would be:
b: make face [
offset: 0x0
color: red
size: 60x60
init: [
pane: reduce [
make a [offset: 0x0]
make a [offset: 10x10]
make a [offset: 10x20]
]
]
]
view layout [
box 200x200
with [
append init [
pane: reduce [
make b [ offset: 0x0 do init ]
make b [ offset: 0x60 do init ]
]
]
]
]
Note that init is manually called within the make clause in this case. I'm not all sure why it is needed.
Finally the everything could elegantly be solved with style
view layout [
style a box yellow 20x20
style b panel red 60x60 [
at 0x0 a ; we can in this style use the just defined a style
at 10x10 a
at 10x20 a
]
at 0x0 b
at 0x60 b
]
I've said on a comment that I would get back to share my findings and I think I got something interesting. As well pointed out by #endo64, iterated faces are tricky and perhaps not best suited to what I intended to do when I first asked the question - to achieve a simple/straigthforward way to instantiate objects thru panels.
I came up with the code bellow, which implements a kind of instantiator. It was inspired in part by the face-maker approach of #endo64 along with some tinkering with iterated faces. This instantiator has a core limitation, which is not accepting multiple types of objects being passed to the constructor to be created at same pane.
Anyway, I found it was an interesting exercise and I would like to post it here in case it could be useful to someone.
I use the same code from the question, now solving/circumventing the limitation of creating multiple b objects inside the main layout pane. a and b now hold an instantiator object, which receives an object to create inside its pane and a block of positions (pair offsets) where objects should be placed.
a: make face [
offset: 0x0
color: yellow
size: 30x20
]
b: make face [
offset: 0x0
color: red
size: 100x100
inst_b: _instantiator/new reduce a [10x10 10x70 80x80 30x30] ; instantiator here
pane: get in inst_b 'pane_function
]
The instantiator code is:
_instantiator: make object! [
_obj: copy []
_offsets: copy []
new: func [
obj [object!] "object to create inside pane"
offs [block!] "instances offsets"
][
make self [
_obj: obj
_offsets: offs
]
]
pane_function: func [face index] [
if integer? index [
if index <= length? _offsets [
_obj/offset: to-pair reduce [_offsets/:index/x _offsets/:index/y]
_obj
]
]
]
]
Code for main layout is:
_inst: _instantiator/new reduce b [0x0 50x50 130x130] ;;3 b objects are created in the positions indicated in the pairs block
_lo: layout [
mybox: box 500x500 white with [
offset: 0x0
pane: get in _inst 'pane_function
]
]
view center-face _lo