eclipsesource / tabris-js

Create native mobile apps in JavaScript or TypeScript.
https://tabrisjs.com
BSD 3-Clause "New" or "Revised" License
1.4k stars 170 forks source link

Concept and PoC: Platform specific properties #499

Closed jkrause closed 9 years ago

jkrause commented 9 years ago

Currelty tabris.js focuses on properties that are commonly available for iOS and Android. But sometimes platforms also support additional properties. How can we find a simple way to allow accessing those properties.

jkrause commented 9 years ago

The iOS UISwitch seems to be a good candidate for a PoC

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UISwitch_Class/index.html

ralfstx commented 9 years ago

Interesting. Neither did I change the title nor add and remove tags. Seems that waffle board did that "for me".

ralfstx commented 9 years ago

@mpost @patrykmol For a PoC, I suggest that the native platforms extend the type definition of existing widgets and add properties to be passed directly to the client. This will allow you to experiment with this feature and evaluate for how many properties it is feasible at all.

For example, to add a property fooColor to the CheckBox, you could enable it in a snippet like so:

tabris.CheckBox._properties.fooColor = {type: true}

This will omit type checks and pass the property to the client as is.

jonek commented 9 years ago

To get an idea of the properties that could be made accessible via the proposed generic mechanism here comes a short analysis.

On Android the Tabris.js widgets have the following framework classes in their type hierarchy. Listed are all public setter methods with up to four parameters of which all are either of a primitive type or String.

android.support.v4.widget.SwipeRefreshLayout (8):
  setSize( int )
  setProgressViewOffset( boolean, int, int )
  setProgressViewEndTarget( boolean, int )
  setRefreshing( boolean )
  setProgressBackgroundColor( int )
  setProgressBackgroundColorSchemeResource( int )
  setProgressBackgroundColorSchemeColor( int )
  setDistanceToTriggerSync( int )

android.support.v7.widget.AppCompatButton (2):
  setAllCaps( boolean )
  setBackgroundResource( int )

android.support.v7.widget.AppCompatCheckBox (1):
  setButtonDrawable( int )

android.support.v7.widget.AppCompatEditText (1):
  setBackgroundResource( int )

android.support.v7.widget.AppCompatRadioButton (1):
  setButtonDrawable( int )

android.support.v7.widget.AppCompatSpinner (1):
  setBackgroundResource( int )

android.view.SurfaceView (4):
  setVisibility( int )
  setZOrderMediaOverlay( boolean )
  setZOrderOnTop( boolean )
  setSecure( boolean )

android.view.View (81):
  setBackgroundResource( int )
  setEnabled( boolean )
  setPadding( int, int, int, int )
  setPaddingRelative( int, int, int, int )
  setSelected( boolean )
  setFadingEdgeLength( int )
  setVerticalScrollbarPosition( int )
  setLabelFor( int )
  setScrollContainer( boolean )
  setDrawingCacheQuality( int )
  setKeepScreenOn( boolean )
  setNextFocusLeftId( int )
  setNextFocusRightId( int )
  setNextFocusUpId( int )
  setNextFocusDownId( int )
  setNextFocusForwardId( int )
  setFitsSystemWindows( boolean )
  setVisibility( int )
  setFocusable( boolean )
  setFocusableInTouchMode( boolean )
  setSoundEffectsEnabled( boolean )
  setHapticFeedbackEnabled( boolean )
  setLayoutDirection( int )
  setHasTransientState( boolean )
  setWillNotDraw( boolean )
  setWillNotCacheDrawing( boolean )
  setClickable( boolean )
  setLongClickable( boolean )
  setPressed( boolean )
  setSaveEnabled( boolean )
  setFilterTouchesWhenObscured( boolean )
  setSaveFromParentEnabled( boolean )
  setAccessibilityLiveRegion( int )
  setImportantForAccessibility( int )
  setHovered( boolean )
  setScrollX( int )
  setScrollY( int )
  setCameraDistance( float )
  setRotation( float )
  setRotationY( float )
  setRotationX( float )
  setScaleX( float )
  setScaleY( float )
  setPivotX( float )
  setPivotY( float )
  setAlpha( float )
  setTop( int )
  setBottom( int )
  setLeft( int )
  setRight( int )
  setX( float )
  setY( float )
  setZ( float )
  setElevation( float )
  setTranslationX( float )
  setTranslationY( float )
  setTranslationZ( float )
  setClipToOutline( boolean )
  setHorizontalFadingEdgeEnabled( boolean )
  setVerticalFadingEdgeEnabled( boolean )
  setHorizontalScrollBarEnabled( boolean )
  setVerticalScrollBarEnabled( boolean )
  setScrollbarFadingEnabled( boolean )
  setScrollBarDefaultDelayBeforeFade( int )
  setScrollBarFadeDuration( int )
  setScrollBarSize( int )
  setScrollBarStyle( int )
  setDuplicateParentStateEnabled( boolean )
  setDrawingCacheEnabled( boolean )
  setDrawingCacheBackgroundColor( int )
  setBackgroundColor( int )
  setActivated( boolean )
  setId( int )
  setMinimumHeight( int )
  setMinimumWidth( int )
  setSystemUiVisibility( int )
  setOverScrollMode( int )
  setNestedScrollingEnabled( boolean )
  setTextDirection( int )
  setTextAlignment( int )
  setTransitionName( String )

android.view.ViewGroup (11):
  setDescendantFocusability( int )
  setTouchscreenBlocksFocus( boolean )
  setMotionEventSplittingEnabled( boolean )
  setTransitionGroup( boolean )
  setClipChildren( boolean )
  setClipToPadding( boolean )
  setAnimationCacheEnabled( boolean )
  setAlwaysDrawnWithCacheEnabled( boolean )
  setPersistentDrawingCache( int )
  setLayoutMode( int )
  setAddStatesFromChildren( boolean )

android.webkit.WebView (10):
  setInitialScale( int )
  setWebContentsDebuggingEnabled( boolean )
  setMapTrackballToArrowKeys( boolean )
  setHorizontalScrollbarOverlay( boolean )
  setVerticalScrollbarOverlay( boolean )
  setHttpAuthUsernamePassword( String, String, String, String )
  setNetworkAvailable( boolean )
  setScrollBarStyle( int )
  setBackgroundColor( int )
  setOverScrollMode( int )

android.widget.AbsSeekBar (4):
  setMax( int )
  setThumbOffset( int )
  setSplitTrack( boolean )
  setKeyProgressIncrement( int )

android.widget.AbsSpinner (2):
  setSelection( int )
  setSelection( int, boolean )

android.widget.AbsoluteLayout (0):

android.widget.AdapterView (3):
  setSelection( int )
  setFocusable( boolean )
  setFocusableInTouchMode( boolean )

android.widget.Button (0):

android.widget.CheckBox (0):

android.widget.CompoundButton (2):
  setButtonDrawable( int )
  setChecked( boolean )

android.widget.EditText (2):
  setSelection( int )
  setSelection( int, int )

android.widget.FrameLayout (3):
  setMeasureAllChildren( boolean )
  setForegroundGravity( int )
  setVisibility( int )

android.widget.HorizontalScrollView (3):
  setFillViewport( boolean )
  setSmoothScrollingEnabled( boolean )
  setOverScrollMode( int )

android.widget.ImageView (13):
  setImageResource( int )
  setImageLevel( int )
  setMaxHeight( int )
  setMaxWidth( int )
  setSelected( boolean )
  setCropToPadding( boolean )
  setBaseline( int )
  setBaselineAlignBottom( boolean )
  setColorFilter( int )
  setImageAlpha( int )
  setAdjustViewBounds( boolean )
  setVisibility( int )
  setAlpha( int )

android.widget.LinearLayout (10):
  setGravity( int )
  setHorizontalGravity( int )
  setVerticalGravity( int )
  setOrientation( int )
  setShowDividers( int )
  setDividerPadding( int )
  setBaselineAligned( boolean )
  setMeasureWithLargestChildEnabled( boolean )
  setBaselineAlignedChildIndex( int )
  setWeightSum( float )

android.widget.ProgressBar (5):
  setProgress( int )
  setMax( int )
  setIndeterminate( boolean )
  setVisibility( int )
  setSecondaryProgress( int )

android.widget.RadioButton (0):

android.widget.RelativeLayout (4):
  setGravity( int )
  setIgnoreGravity( int )
  setHorizontalGravity( int )
  setVerticalGravity( int )

android.widget.ScrollView (3):
  setFillViewport( boolean )
  setSmoothScrollingEnabled( boolean )
  setOverScrollMode( int )

android.widget.SeekBar (0):

android.widget.Spinner (7):
  setDropDownVerticalOffset( int )
  setDropDownHorizontalOffset( int )
  setDropDownWidth( int )
  setPopupBackgroundResource( int )
  setPromptId( int )
  setEnabled( boolean )
  setGravity( int )

android.widget.Switch (8):
  setChecked( boolean )
  setSwitchPadding( int )
  setSwitchMinWidth( int )
  setThumbTextPadding( int )
  setTrackResource( int )
  setThumbResource( int )
  setShowText( boolean )
  setSplitTrack( boolean )

android.widget.TextView (52):
  setAllCaps( boolean )
  setTextColor( int )
  setEnabled( boolean )
  setCompoundDrawablesWithIntrinsicBounds( int, int, int, int )
  setCompoundDrawablesRelativeWithIntrinsicBounds( int, int, int, int )
  setCompoundDrawablePadding( int )
  setPadding( int, int, int, int )
  setPaddingRelative( int, int, int, int )
  setTextSize( float )
  setTextSize( int, float )
  setTextScaleX( float )
  setElegantTextHeight( boolean )
  setLetterSpacing( float )
  setFontFeatureSettings( String )
  setHighlightColor( int )
  setShowSoftInputOnFocus( boolean )
  setShadowLayer( float, float, float, int )
  setAutoLinkMask( int )
  setLinksClickable( boolean )
  setHintTextColor( int )
  setLinkTextColor( int )
  setGravity( int )
  setPaintFlags( int )
  setHorizontallyScrolling( boolean )
  setMinLines( int )
  setMinHeight( int )
  setMaxLines( int )
  setMaxHeight( int )
  setLines( int )
  setHeight( int )
  setMinEms( int )
  setMinWidth( int )
  setMaxEms( int )
  setMaxWidth( int )
  setEms( int )
  setWidth( int )
  setLineSpacing( float, float )
  setFreezesText( boolean )
  setText( int )
  setHint( int )
  setInputType( int )
  setRawInputType( int )
  setImeOptions( int )
  setPrivateImeOptions( String )
  setInputExtras( int )
  setTextIsSelectable( boolean )
  setIncludeFontPadding( boolean )
  setSingleLine( boolean )
  setMarqueeRepeatLimit( int )
  setSelectAllOnFocus( boolean )
  setCursorVisible( boolean )
  setSelected( boolean )

android.widget.ToggleButton (1):
  setChecked( boolean )

android.widget.VideoView (1):
  setVideoPath( String )
jonek commented 9 years ago

Today I did a very simple PoC for the property "elevation" on android.view.View.

Here is the snipped I used:

// enable the platform specific property "elevation" on ImageView
tabris.ImageView._properties.elevation = {type: true};

var page = tabris.create("Page", {
  title: "Image View",
  topLevel: true
});

var imageView = tabris.create("ImageView", {
    layoutData: {left: 10, top: [page.children().last() || "0%", 10], width: 250, height: 100},
    image: {src: "images/target_200.png"},
    background: "#aaaaaa",
    scaleMode: "fit"
}).appendTo(page);
var elevation = 10.0;
console.log("elevation = " + elevation)
imageView.set("elevation", elevation);

page.open();

Things I did not implement:

Problems I observed:

tbuschto commented 9 years ago

@jonek What properties can't be accessed this way (.e.g. to due type conversion, widget composition)? What about getter support? Are there non-setter methods that may be interesting to access?

ralfstx commented 9 years ago

Additional problems we've discussed (as I understood it, feel free to edit this comment):

Most of us (including myself) expressed the concern that this feature could create more confusion than actual value.

jonek commented 9 years ago

@tbuschto We can not access this properties:

tbuschto commented 9 years ago

@jonek What would you estimate is the ratio of (useful) inaccessible properties to (useful) accessible ones? I mean, you listed a lot of properties above, are these the majority, or rather a fraction of properties that one may want to set?

mpost commented 9 years ago

I totally agree with all the points @ralfstx listed above.

I have looked through the properties and applied my Jedi skills to get a feel for the properties. From my point of view there are around 4-5 properties that i would use on a daily basis:

All the others could be used but won't have much of an impact. Your millage my vary.

Since the above list contains only methods that start with "set" we could broughten the scope a bit to include other methods not starting with "set". I would add another 3-4 properties of interest to the list.

tbuschto commented 9 years ago

@mpost You also told me that some of those are Android 5 only, so that's even less (for now).

mpost commented 9 years ago

@tbuschto Yeah good point. elevation and translationZ is Android 5+ only.

patrykmol commented 9 years ago

On iOS it is a matter of whitelisting properties and forwarding set calls from wrapper to actual UI widget for properties that we want to make available to user. However some properties are not as easily accessed as the others. For example button object can have different texts for different states and this is not available as property. With this comes and issue of translating flags (most are enums but some are bit masks that can be summed). This might not a problem when we decide. Another common problem would be setting properties that are not a primitive or string type (eg. text color, state-dependant tints on switch). Also for some widgets all relevant properties are already available (eg. text view).

jonek commented 9 years ago

We came to the following decision:

ralfstx commented 9 years ago

To move forward with platform specific color properties, Tim opened #523