14 |
15 | @implementation RNCSliderManager
16 | {
17 | BOOL _isSliding;
18 | }
19 |
20 | RCT_EXPORT_MODULE()
21 |
22 | - (UIView *)view
23 | {
24 | RNCSlider *slider = [RNCSlider new];
25 | [slider addTarget:self action:@selector(sliderValueChanged:)
26 | forControlEvents:UIControlEventValueChanged];
27 | [slider addTarget:self action:@selector(sliderTouchStart:)
28 | forControlEvents:UIControlEventTouchDown];
29 | [slider addTarget:self action:@selector(sliderTouchEnd:)
30 | forControlEvents:(UIControlEventTouchUpInside |
31 | UIControlEventTouchUpOutside |
32 | UIControlEventTouchCancel)];
33 |
34 | UITapGestureRecognizer *tapGesturer;
35 | tapGesturer = [[UITapGestureRecognizer alloc] initWithTarget: self action:@selector(tapHandler:)];
36 | [tapGesturer setNumberOfTapsRequired: 1];
37 | [slider addGestureRecognizer:tapGesturer];
38 |
39 | return slider;
40 | }
41 |
42 | - (void)tapHandler:(UITapGestureRecognizer *)gesture {
43 | // Ignore this tap if in the middle of a slide.
44 | if (_isSliding) {
45 | return;
46 | }
47 |
48 | // Bail out if the source view of the gesture isn't an RNCSlider.
49 | if ([gesture.view class] != [RNCSlider class]) {
50 | return;
51 | }
52 | RNCSlider *slider = (RNCSlider *)gesture.view;
53 |
54 | if (!slider.tapToSeek) {
55 | return;
56 | }
57 |
58 | CGPoint touchPoint = [gesture locationInView:slider];
59 | float rangeWidth = slider.maximumValue - slider.minimumValue;
60 | float sliderPercent = touchPoint.x / slider.bounds.size.width;
61 | float value = slider.minimumValue + (rangeWidth * sliderPercent);
62 |
63 | [slider setValue:discreteValue(slider, value) animated: YES];
64 |
65 | // Trigger onValueChange to address https://github.com/react-native-community/react-native-slider/issues/212
66 | if (slider.onRNCSliderValueChange) {
67 | slider.onRNCSliderValueChange(@{
68 | @"value": @(slider.value),
69 | });
70 | }
71 |
72 | if (slider.onRNCSliderSlidingComplete) {
73 | slider.onRNCSliderSlidingComplete(@{
74 | @"value": @(slider.value),
75 | });
76 | }
77 | }
78 |
79 | static float discreteValue(RNCSlider *sender, float value) {
80 | // If step is set and less than or equal to difference between max and min values,
81 | // pick the closest discrete multiple of step to return.
82 |
83 | if (sender.step > 0 && sender.step <= (sender.maximumValue - sender.minimumValue)) {
84 |
85 | // Round up when increase, round down when decrease.
86 | double (^_round)(double) = ^(double x) {
87 | if (!UIAccessibilityIsVoiceOverRunning()) {
88 | return round(x);
89 | } else if (sender.lastValue > value) {
90 | return floor(x);
91 | } else {
92 | return ceil(x);
93 | }
94 | };
95 |
96 | return
97 | MAX(sender.minimumValue,
98 | MIN(sender.maximumValue,
99 | sender.minimumValue + _round((value - sender.minimumValue) / sender.step) * sender.step
100 | )
101 | );
102 | }
103 |
104 | // Otherwise, leave value unchanged.
105 | return value;
106 | }
107 |
108 | static void RNCSendSliderEvent(RNCSlider *sender, BOOL continuous, BOOL isSlidingStart)
109 | {
110 | float value = discreteValue(sender, sender.value);
111 |
112 | [sender setValue:value animated:NO];
113 |
114 | if (continuous) {
115 | if (sender.onRNCSliderValueChange && sender.lastValue != value) {
116 | sender.onRNCSliderValueChange(@{
117 | @"value": @(value),
118 | });
119 | }
120 | } else {
121 | if (sender.onRNCSliderSlidingComplete && !isSlidingStart) {
122 | sender.onRNCSliderSlidingComplete(@{
123 | @"value": @(value),
124 | });
125 | }
126 | if (sender.onRNCSliderSlidingStart && isSlidingStart) {
127 | sender.onRNCSliderSlidingStart(@{
128 | @"value": @(value),
129 | });
130 | }
131 | }
132 |
133 | sender.lastValue = value;
134 | }
135 |
136 | - (void)sliderValueChanged:(RNCSlider *)sender
137 | {
138 | RNCSendSliderEvent(sender, YES, NO);
139 | }
140 |
141 | - (void)sliderTouchStart:(RNCSlider *)sender
142 | {
143 | RNCSendSliderEvent(sender, NO, YES);
144 | _isSliding = YES;
145 | }
146 |
147 | - (void)sliderTouchEnd:(RNCSlider *)sender
148 | {
149 | RNCSendSliderEvent(sender, NO, NO);
150 | _isSliding = NO;
151 | }
152 |
153 | RCT_EXPORT_VIEW_PROPERTY(value, float);
154 | RCT_EXPORT_VIEW_PROPERTY(step, float);
155 | RCT_EXPORT_VIEW_PROPERTY(trackImage, UIImage);
156 | RCT_EXPORT_VIEW_PROPERTY(minimumTrackImage, UIImage);
157 | RCT_EXPORT_VIEW_PROPERTY(maximumTrackImage, UIImage);
158 | RCT_EXPORT_VIEW_PROPERTY(minimumValue, float);
159 | RCT_EXPORT_VIEW_PROPERTY(maximumValue, float);
160 | RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor);
161 | RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor);
162 | RCT_EXPORT_VIEW_PROPERTY(onRNCSliderValueChange, RCTBubblingEventBlock);
163 | RCT_EXPORT_VIEW_PROPERTY(onRNCSliderSlidingStart, RCTBubblingEventBlock);
164 | RCT_EXPORT_VIEW_PROPERTY(onRNCSliderSlidingComplete, RCTBubblingEventBlock);
165 | RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor);
166 | RCT_EXPORT_VIEW_PROPERTY(thumbImage, UIImage);
167 | RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL);
168 | RCT_EXPORT_VIEW_PROPERTY(tapToSeek, BOOL);
169 | RCT_EXPORT_VIEW_PROPERTY(accessibilityUnits, NSString);
170 | RCT_EXPORT_VIEW_PROPERTY(accessibilityIncrements, NSArray);
171 |
172 | RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RNCSlider)
173 | {
174 | if (json) {
175 | view.enabled = !([RCTConvert BOOL:json]);
176 | } else {
177 | view.enabled = defaultView.enabled;
178 | }
179 | }
180 |
181 | @end
182 |
--------------------------------------------------------------------------------
/src/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.vspscc
94 | *.vssscc
95 | .builds
96 | *.pidb
97 | *.svclog
98 | *.scc
99 |
100 | # Chutzpah Test files
101 | _Chutzpah*
102 |
103 | # Visual C++ cache files
104 | ipch/
105 | *.aps
106 | *.ncb
107 | *.opendb
108 | *.opensdf
109 | *.sdf
110 | *.cachefile
111 | *.VC.db
112 | *.VC.VC.opendb
113 |
114 | # Visual Studio profiler
115 | *.psess
116 | *.vsp
117 | *.vspx
118 | *.sap
119 |
120 | # Visual Studio Trace Files
121 | *.e2e
122 |
123 | # TFS 2012 Local Workspace
124 | $tf/
125 |
126 | # Guidance Automation Toolkit
127 | *.gpState
128 |
129 | # ReSharper is a .NET coding add-in
130 | _ReSharper*/
131 | *.[Rr]e[Ss]harper
132 | *.DotSettings.user
133 |
134 | # TeamCity is a build add-in
135 | _TeamCity*
136 |
137 | # DotCover is a Code Coverage Tool
138 | *.dotCover
139 |
140 | # AxoCover is a Code Coverage Tool
141 | .axoCover/*
142 | !.axoCover/settings.json
143 |
144 | # Coverlet is a free, cross platform Code Coverage Tool
145 | coverage*.json
146 | coverage*.xml
147 | coverage*.info
148 |
149 | # Visual Studio code coverage results
150 | *.coverage
151 | *.coveragexml
152 |
153 | # NCrunch
154 | _NCrunch_*
155 | .*crunch*.local.xml
156 | nCrunchTemp_*
157 |
158 | # MightyMoose
159 | *.mm.*
160 | AutoTest.Net/
161 |
162 | # Web workbench (sass)
163 | .sass-cache/
164 |
165 | # Installshield output folder
166 | [Ee]xpress/
167 |
168 | # DocProject is a documentation generator add-in
169 | DocProject/buildhelp/
170 | DocProject/Help/*.HxT
171 | DocProject/Help/*.HxC
172 | DocProject/Help/*.hhc
173 | DocProject/Help/*.hhk
174 | DocProject/Help/*.hhp
175 | DocProject/Help/Html2
176 | DocProject/Help/html
177 |
178 | # Click-Once directory
179 | publish/
180 |
181 | # Publish Web Output
182 | *.[Pp]ublish.xml
183 | *.azurePubxml
184 | # Note: Comment the next line if you want to checkin your web deploy settings,
185 | # but database connection strings (with potential passwords) will be unencrypted
186 | *.pubxml
187 | *.publishproj
188 |
189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
190 | # checkin your Azure Web App publish settings, but sensitive information contained
191 | # in these scripts will be unencrypted
192 | PublishScripts/
193 |
194 | # NuGet Packages
195 | *.nupkg
196 | # NuGet Symbol Packages
197 | *.snupkg
198 | # The packages folder can be ignored because of Package Restore
199 | **/[Pp]ackages/*
200 | # except build/, which is used as an MSBuild target.
201 | !**/[Pp]ackages/build/
202 | # Uncomment if necessary however generally it will be regenerated when needed
203 | #!**/[Pp]ackages/repositories.config
204 | # NuGet v3's project.json files produces more ignorable files
205 | *.nuget.props
206 | *.nuget.targets
207 |
208 | # Microsoft Azure Build Output
209 | csx/
210 | *.build.csdef
211 |
212 | # Microsoft Azure Emulator
213 | ecf/
214 | rcf/
215 |
216 | # Windows Store app package directories and files
217 | AppPackages/
218 | BundleArtifacts/
219 | Package.StoreAssociation.xml
220 | _pkginfo.txt
221 | *.appx
222 | *.appxbundle
223 | *.appxupload
224 |
225 | # Visual Studio cache files
226 | # files ending in .cache can be ignored
227 | *.[Cc]ache
228 | # but keep track of directories ending in .cache
229 | !?*.[Cc]ache/
230 |
231 | # Others
232 | ClientBin/
233 | ~$*
234 | *~
235 | *.dbmdl
236 | *.dbproj.schemaview
237 | *.jfm
238 | *.pfx
239 | *.publishsettings
240 | orleans.codegen.cs
241 |
242 | # Including strong name files can present a security risk
243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
244 | #*.snk
245 |
246 | # Since there are multiple workflows, uncomment next line to ignore bower_components
247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
248 | #bower_components/
249 |
250 | # RIA/Silverlight projects
251 | Generated_Code/
252 |
253 | # Backup & report files from converting an old project file
254 | # to a newer Visual Studio version. Backup files are not needed,
255 | # because we have git ;-)
256 | _UpgradeReport_Files/
257 | Backup*/
258 | UpgradeLog*.XML
259 | UpgradeLog*.htm
260 | ServiceFabricBackup/
261 | *.rptproj.bak
262 |
263 | # SQL Server files
264 | *.mdf
265 | *.ldf
266 | *.ndf
267 |
268 | # Business Intelligence projects
269 | *.rdl.data
270 | *.bim.layout
271 | *.bim_*.settings
272 | *.rptproj.rsuser
273 | *- [Bb]ackup.rdl
274 | *- [Bb]ackup ([0-9]).rdl
275 | *- [Bb]ackup ([0-9][0-9]).rdl
276 |
277 | # Microsoft Fakes
278 | FakesAssemblies/
279 |
280 | # GhostDoc plugin setting file
281 | *.GhostDoc.xml
282 |
283 | # Node.js Tools for Visual Studio
284 | .ntvs_analysis.dat
285 | node_modules/
286 |
287 | # Visual Studio 6 build log
288 | *.plg
289 |
290 | # Visual Studio 6 workspace options file
291 | *.opt
292 |
293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
294 | *.vbw
295 |
296 | # Visual Studio LightSwitch build output
297 | **/*.HTMLClient/GeneratedArtifacts
298 | **/*.DesktopClient/GeneratedArtifacts
299 | **/*.DesktopClient/ModelManifest.xml
300 | **/*.Server/GeneratedArtifacts
301 | **/*.Server/ModelManifest.xml
302 | _Pvt_Extensions
303 |
304 | # Paket dependency manager
305 | .paket/paket.exe
306 | paket-files/
307 |
308 | # FAKE - F# Make
309 | .fake/
310 |
311 | # CodeRush personal settings
312 | .cr/personal
313 |
314 | # Python Tools for Visual Studio (PTVS)
315 | __pycache__/
316 | *.pyc
317 |
318 | # Cake - Uncomment if you are using it
319 | # tools/**
320 | # !tools/packages.config
321 |
322 | # Tabs Studio
323 | *.tss
324 |
325 | # Telerik's JustMock configuration file
326 | *.jmconfig
327 |
328 | # BizTalk build output
329 | *.btp.cs
330 | *.btm.cs
331 | *.odx.cs
332 | *.xsd.cs
333 |
334 | # OpenCover UI analysis results
335 | OpenCover/
336 |
337 | # Azure Stream Analytics local run output
338 | ASALocalRun/
339 |
340 | # MSBuild Binary and Structured Log
341 | *.binlog
342 |
343 | # NVidia Nsight GPU debugger configuration file
344 | *.nvuser
345 |
346 | # MFractors (Xamarin productivity tool) working folder
347 | .mfractor/
348 |
349 | # Local History for Visual Studio
350 | .localhistory/
351 |
352 | # BeatPulse healthcheck temp database
353 | healthchecksdb
354 |
355 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
356 | MigrationBackup/
357 |
358 | # Ionide (cross platform F# VS Code tools) working folder
359 | .ionide/
360 |
361 | # Fody - auto-generated XML schema
362 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation
19 | * entryFile: "index.android.js",
20 | *
21 | * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
22 | * bundleCommand: "ram-bundle",
23 | *
24 | * // whether to bundle JS and assets in debug mode
25 | * bundleInDebug: false,
26 | *
27 | * // whether to bundle JS and assets in release mode
28 | * bundleInRelease: true,
29 | *
30 | * // whether to bundle JS and assets in another build variant (if configured).
31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
32 | * // The configuration property can be in the following formats
33 | * // 'bundleIn${productFlavor}${buildType}'
34 | * // 'bundleIn${buildType}'
35 | * // bundleInFreeDebug: true,
36 | * // bundleInPaidRelease: true,
37 | * // bundleInBeta: true,
38 | *
39 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
40 | * // for example: to disable dev mode in the staging build type (if configured)
41 | * devDisabledInStaging: true,
42 | * // The configuration property can be in the following formats
43 | * // 'devDisabledIn${productFlavor}${buildType}'
44 | * // 'devDisabledIn${buildType}'
45 | *
46 | * // the root of your project, i.e. where "package.json" lives
47 | * root: "../../",
48 | *
49 | * // where to put the JS bundle asset in debug mode
50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
51 | *
52 | * // where to put the JS bundle asset in release mode
53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
54 | *
55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
56 | * // require('./image.png')), in debug mode
57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
58 | *
59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
60 | * // require('./image.png')), in release mode
61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
62 | *
63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
67 | * // for example, you might want to remove it from here.
68 | * inputExcludes: ["android/**", "ios/**"],
69 | *
70 | * // override which node gets called and with what additional arguments
71 | * nodeExecutableAndArgs: ["node"],
72 | *
73 | * // supply additional arguments to the packager
74 | * extraPackagerArgs: []
75 | * ]
76 | */
77 |
78 | project.ext.react = [
79 | entryFile: "example/index.js",
80 | // TODO: remove once @esemesek's patch is merged
81 | cliPath: "../node_modules/react-native/cli.js"
82 | ]
83 |
84 | apply from: "../../../node_modules/react-native/react.gradle"
85 |
86 | /**
87 | * Set this to true to create two separate APKs instead of one:
88 | * - An APK that only works on ARM devices
89 | * - An APK that only works on x86 devices
90 | * The advantage is the size of the APK is reduced by about 4MB.
91 | * Upload all the APKs to the Play Store and people will download
92 | * the correct one based on the CPU architecture of their device.
93 | */
94 | def enableSeparateBuildPerCPUArchitecture = false
95 |
96 | /**
97 | * Run Proguard to shrink the Java bytecode in release builds.
98 | */
99 | def enableProguardInReleaseBuilds = false
100 |
101 | /**
102 | * Use international variant JavaScriptCore
103 | * International variant includes ICU i18n library and necessary data allowing to use
104 | * e.g. Date.toLocaleString and String.localeCompare that give correct results
105 | * when using with locales other than en-US.
106 | * Note that this variant is about 6MiB larger per architecture than default.
107 | */
108 | def useIntlJsc = false
109 |
110 | android {
111 | compileSdkVersion rootProject.ext.compileSdkVersion
112 |
113 | compileOptions {
114 | sourceCompatibility JavaVersion.VERSION_1_8
115 | targetCompatibility JavaVersion.VERSION_1_8
116 | }
117 |
118 | defaultConfig {
119 | applicationId "com.example"
120 | minSdkVersion rootProject.ext.minSdkVersion
121 | targetSdkVersion rootProject.ext.targetSdkVersion
122 | versionCode 1
123 | versionName "1.0"
124 | }
125 | splits {
126 | abi {
127 | reset()
128 | enable enableSeparateBuildPerCPUArchitecture
129 | universalApk false // If true, also generate a universal APK
130 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
131 | }
132 | }
133 | signingConfigs {
134 | debug {
135 | storeFile file('debug.keystore')
136 | storePassword 'android'
137 | keyAlias 'androiddebugkey'
138 | keyPassword 'android'
139 | }
140 | }
141 | buildTypes {
142 | debug {
143 | signingConfig signingConfigs.debug
144 | }
145 | release {
146 | // Caution! In production, you need to generate your own keystore file.
147 | // see https://facebook.github.io/react-native/docs/signed-apk-android.
148 | signingConfig signingConfigs.debug
149 | minifyEnabled enableProguardInReleaseBuilds
150 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
151 | }
152 | }
153 | // applicationVariants are e.g. debug, release
154 | applicationVariants.all { variant ->
155 | variant.outputs.each { output ->
156 | // For each separate APK per architecture, set a unique version code as described here:
157 | // https://developer.android.com/studio/build/configure-apk-splits.html
158 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
159 | def abi = output.getFilter(OutputFile.ABI)
160 | if (abi != null) { // null for the universal-debug, universal-release variants
161 | output.versionCodeOverride =
162 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
163 | }
164 | }
165 | }
166 | }
167 |
168 | dependencies {
169 | implementation fileTree(dir: "libs", include: ["*.jar"])
170 | implementation "com.facebook.react:react-native:+" // From node_modules
171 |
172 | // JSC from node_modules
173 | if (useIntlJsc) {
174 | implementation 'org.webkit:android-jsc-intl:+'
175 | } else {
176 | implementation 'org.webkit:android-jsc:+'
177 | }
178 | }
179 |
180 | // Run this once to be able to run the application with BUCK
181 | // puts all compile dependencies into folder libs for BUCK to use
182 | task copyDownloadableDepsToLibs(type: Copy) {
183 | from configurations.compile
184 | into 'libs'
185 | }
186 |
187 | apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
188 | // set custom root because we're in a monorepo
189 | applyNativeModulesAppBuildGradle(project)
190 |
--------------------------------------------------------------------------------
/src/android/src/main/java/com/reactnativecommunity/slider/ReactSlider.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.reactnativecommunity.slider;
8 |
9 | import android.content.Context;
10 | import android.graphics.Bitmap;
11 | import android.graphics.BitmapFactory;
12 | import android.graphics.drawable.BitmapDrawable;
13 | import android.os.Build;
14 | import android.util.AttributeSet;
15 | import android.view.accessibility.AccessibilityEvent;
16 | import android.view.accessibility.AccessibilityManager;
17 | import androidx.appcompat.widget.AppCompatSeekBar;
18 | import java.net.URL;
19 | import java.util.List;
20 | import java.util.Timer;
21 | import java.util.TimerTask;
22 | import java.util.concurrent.Callable;
23 | import java.util.concurrent.ExecutorService;
24 | import java.util.concurrent.Executors;
25 | import java.util.concurrent.Future;
26 | import javax.annotation.Nullable;
27 |
28 | /**
29 | * Slider that behaves more like the iOS one, for consistency.
30 | *
31 | *
On iOS, the value is 0..1. Android SeekBar only supports integer values. For consistency, we
32 | * pretend in JS that the value is 0..1 but set the SeekBar value to 0..100.
33 | *
34 | *
Note that the slider is _not_ a controlled component (setValue isn't called during dragging).
35 | */
36 | public class ReactSlider extends AppCompatSeekBar {
37 |
38 | /**
39 | * If step is 0 (unset) we default to this total number of steps. Don't use 100 which leads to
40 | * rounding errors (0.200000000001).
41 | */
42 | private static int DEFAULT_TOTAL_STEPS = 128;
43 |
44 | /**
45 | * We want custom min..max range. Android only supports 0..max range so we implement this
46 | * ourselves.
47 | */
48 | private double mMinValue = 0;
49 |
50 | private double mMaxValue = 0;
51 |
52 | /**
53 | * Value sent from JS (setState). Doesn't get updated during drag (slider is not a controlled
54 | * component).
55 | */
56 | private double mValue = 0;
57 |
58 | /** If zero it's determined automatically. */
59 | private double mStep = 0;
60 |
61 | private double mStepCalculated = 0;
62 |
63 | private String mAccessibilityUnits;
64 |
65 | private List mAccessibilityIncrements;
66 |
67 | public ReactSlider(Context context, @Nullable AttributeSet attrs, int style) {
68 | super(context, attrs, style);
69 | disableStateListAnimatorIfNeeded();
70 | }
71 |
72 | private void disableStateListAnimatorIfNeeded() {
73 | // We disable the state list animator for Android 6 and 7; this is a hack to prevent T37452851
74 | // and https://github.com/facebook/react-native/issues/9979
75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
76 | && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
77 | super.setStateListAnimator(null);
78 | }
79 | }
80 |
81 | /* package */ void setMaxValue(double max) {
82 | mMaxValue = max;
83 | updateAll();
84 | }
85 |
86 | /* package */ void setMinValue(double min) {
87 | mMinValue = min;
88 | updateAll();
89 | }
90 |
91 | /* package */ void setValue(double value) {
92 | mValue = value;
93 | updateValue();
94 | }
95 |
96 | /* package */ void setStep(double step) {
97 | mStep = step;
98 | updateAll();
99 | }
100 |
101 | void setAccessibilityUnits(String accessibilityUnits) {
102 | mAccessibilityUnits = accessibilityUnits;
103 | }
104 |
105 | void setAccessibilityIncrements(List accessibilityIncrements) {
106 | mAccessibilityIncrements = accessibilityIncrements;
107 | }
108 |
109 | @Override
110 | public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
111 | super.onPopulateAccessibilityEvent(event);
112 | if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED ||
113 | (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SELECTED && this.isAccessibilityFocused())) {
114 | this.setupAccessibility();
115 | }
116 | }
117 |
118 | @Override
119 | public void announceForAccessibility(CharSequence text) {
120 | Context ctx = this.getContext();
121 | final AccessibilityManager manager = (AccessibilityManager) ctx.getSystemService(Context.ACCESSIBILITY_SERVICE);
122 |
123 | if (manager.isEnabled()) {
124 | final AccessibilityEvent e = AccessibilityEvent.obtain();
125 | e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
126 | e.setClassName(this.getClass().getName());
127 | e.setPackageName(ctx.getPackageName());
128 | e.getText().add(text);
129 |
130 | TimerTask task = new TimerTask() {
131 | @Override
132 | public void run() {
133 | manager.sendAccessibilityEvent(e);
134 | }
135 | };
136 |
137 | Timer timer = new Timer();
138 | timer.schedule(task, 1000);
139 | }
140 | }
141 |
142 | private void setupAccessibility() {
143 | if (mAccessibilityUnits != null && mAccessibilityIncrements != null && mAccessibilityIncrements.size() - 1 == (int)mMaxValue) {
144 | int index = (int)mValue;
145 | String sliderValue = mAccessibilityIncrements.get(index);
146 | int stringLength = mAccessibilityUnits.length();
147 |
148 | String spokenUnits = mAccessibilityUnits;
149 | if (sliderValue != null && Integer.parseInt(sliderValue) == 1) {
150 | spokenUnits = spokenUnits.substring(0, stringLength - 1);
151 | }
152 |
153 | this.announceForAccessibility(String.format("%s %s", sliderValue, spokenUnits));
154 | }
155 | }
156 |
157 |
158 |
159 | /**
160 | * Convert SeekBar's native progress value (e.g. 0..100) to a value passed to JS (e.g. -1.0..2.5).
161 | */
162 | public double toRealProgress(int seekBarProgress) {
163 | if (seekBarProgress == getMax()) {
164 | return mMaxValue;
165 | }
166 | return seekBarProgress * getStepValue() + mMinValue;
167 | }
168 |
169 | /** Update underlying native SeekBar's values. */
170 | private void updateAll() {
171 | if (mStep == 0) {
172 | mStepCalculated = (mMaxValue - mMinValue) / (double) DEFAULT_TOTAL_STEPS;
173 | }
174 | setMax(getTotalSteps());
175 | updateValue();
176 | }
177 |
178 | /** Update value only (optimization in case only value is set). */
179 | private void updateValue() {
180 | setProgress((int) Math.round((mValue - mMinValue) / (mMaxValue - mMinValue) * getTotalSteps()));
181 | }
182 |
183 | private int getTotalSteps() {
184 | return (int) Math.ceil((mMaxValue - mMinValue) / getStepValue());
185 | }
186 |
187 | private double getStepValue() {
188 | return mStep > 0 ? mStep : mStepCalculated;
189 | }
190 |
191 | private BitmapDrawable getBitmapDrawable(final String uri) {
192 | BitmapDrawable bitmapDrawable = null;
193 | ExecutorService executorService = Executors.newSingleThreadExecutor();
194 | Future future = executorService.submit(new Callable() {
195 | @Override
196 | public BitmapDrawable call() {
197 | BitmapDrawable bitmapDrawable = null;
198 | try {
199 | Bitmap bitmap = null;
200 | if (uri.startsWith("http://") || uri.startsWith("https://") ||
201 | uri.startsWith("file://") || uri.startsWith("asset://") || uri.startsWith("data:")) {
202 | bitmap = BitmapFactory.decodeStream(new URL(uri).openStream());
203 | } else {
204 | int drawableId = getResources()
205 | .getIdentifier(uri, "drawable", getContext()
206 | .getPackageName());
207 | bitmap = BitmapFactory.decodeResource(getResources(), drawableId);
208 | }
209 |
210 | bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
211 | } catch (Exception e) {
212 | e.printStackTrace();
213 | }
214 | return bitmapDrawable;
215 | }
216 | });
217 | try {
218 | bitmapDrawable = future.get();
219 | } catch (Exception e) {
220 | e.printStackTrace();
221 | }
222 | return bitmapDrawable;
223 | }
224 |
225 | public void setThumbImage(final String uri) {
226 | if (uri != null) {
227 | setThumb(getBitmapDrawable(uri));
228 | // Enable alpha channel for the thumbImage
229 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
230 | setSplitTrack(false);
231 | }
232 | } else {
233 | setThumb(getThumb());
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/android/src/main/java/com/reactnativecommunity/slider/ReactSliderManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | package com.reactnativecommunity.slider;
9 |
10 | import android.os.Build;
11 | import android.graphics.PorterDuff;
12 | import android.graphics.drawable.Drawable;
13 | import android.graphics.drawable.LayerDrawable;
14 | import android.view.View;
15 | import android.widget.SeekBar;
16 | import com.facebook.react.bridge.ReactContext;
17 | import com.facebook.react.bridge.ReadableArray;
18 | import com.facebook.react.bridge.ReadableMap;
19 | import com.facebook.react.common.MapBuilder;
20 | import com.facebook.react.uimanager.LayoutShadowNode;
21 | import com.facebook.react.uimanager.SimpleViewManager;
22 | import com.facebook.react.uimanager.ThemedReactContext;
23 | import com.facebook.react.uimanager.UIManagerModule;
24 | import com.facebook.react.uimanager.ViewProps;
25 | import com.facebook.react.uimanager.annotations.ReactProp;
26 | import com.facebook.yoga.YogaMeasureFunction;
27 | import com.facebook.yoga.YogaMeasureMode;
28 | import com.facebook.yoga.YogaMeasureOutput;
29 | import com.facebook.yoga.YogaNode;
30 | import java.util.ArrayList;
31 | import java.util.List;
32 | import java.util.Map;
33 | import javax.annotation.Nullable;
34 |
35 | /**
36 | * Manages instances of {@code ReactSlider}.
37 | *
38 | * Note that the slider is _not_ a controlled component.
39 | */
40 | public class ReactSliderManager extends SimpleViewManager {
41 |
42 | private static final int STYLE = android.R.attr.seekBarStyle;
43 |
44 | public static final String REACT_CLASS = "RNCSlider";
45 |
46 | static class ReactSliderShadowNode extends LayoutShadowNode implements
47 | YogaMeasureFunction {
48 |
49 | private int mWidth;
50 | private int mHeight;
51 | private boolean mMeasured;
52 |
53 | private ReactSliderShadowNode() {
54 | initMeasureFunction();
55 | }
56 |
57 | private void initMeasureFunction() {
58 | setMeasureFunction(this);
59 | }
60 |
61 | @Override
62 | public long measure(
63 | YogaNode node,
64 | float width,
65 | YogaMeasureMode widthMode,
66 | float height,
67 | YogaMeasureMode heightMode) {
68 | if (!mMeasured) {
69 | SeekBar reactSlider = new ReactSlider(getThemedContext(), null, STYLE);
70 | final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
71 | reactSlider.measure(spec, spec);
72 | mWidth = reactSlider.getMeasuredWidth();
73 | mHeight = reactSlider.getMeasuredHeight();
74 | mMeasured = true;
75 | }
76 |
77 | return YogaMeasureOutput.make(mWidth, mHeight);
78 | }
79 | }
80 |
81 | private static final SeekBar.OnSeekBarChangeListener ON_CHANGE_LISTENER =
82 | new SeekBar.OnSeekBarChangeListener() {
83 | @Override
84 | public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
85 | ReactContext reactContext = (ReactContext) seekbar.getContext();
86 | reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
87 | new ReactSliderEvent(
88 | seekbar.getId(),
89 | ((ReactSlider) seekbar).toRealProgress(progress),
90 | fromUser));
91 | }
92 |
93 | @Override
94 | public void onStartTrackingTouch(SeekBar seekbar) {
95 | ReactContext reactContext = (ReactContext) seekbar.getContext();
96 | reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
97 | new ReactSlidingStartEvent(
98 | seekbar.getId(),
99 | ((ReactSlider) seekbar).toRealProgress(seekbar.getProgress())));
100 | }
101 |
102 | @Override
103 | public void onStopTrackingTouch(SeekBar seekbar) {
104 | ReactContext reactContext = (ReactContext) seekbar.getContext();
105 | reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(
106 | new ReactSlidingCompleteEvent(
107 | seekbar.getId(),
108 | ((ReactSlider) seekbar).toRealProgress(seekbar.getProgress())));
109 | }
110 | };
111 |
112 | @Override
113 | public String getName() {
114 | return REACT_CLASS;
115 | }
116 |
117 | @Override
118 | public LayoutShadowNode createShadowNodeInstance() {
119 | return new ReactSliderShadowNode();
120 | }
121 |
122 | @Override
123 | public Class getShadowNodeClass() {
124 | return ReactSliderShadowNode.class;
125 | }
126 |
127 | @Override
128 | protected ReactSlider createViewInstance(ThemedReactContext context) {
129 | ReactSlider slider = new ReactSlider(context, null, STYLE);
130 |
131 | if (Build.VERSION.SDK_INT >= 21) {
132 | /**
133 | * The "splitTrack" parameter should have "false" value,
134 | * otherwise the SeekBar progress line doesn't appear when it is rotated.
135 | */
136 | slider.setSplitTrack(false);
137 | }
138 |
139 | return slider;
140 | }
141 |
142 | @ReactProp(name = ViewProps.ENABLED, defaultBoolean = true)
143 | public void setEnabled(ReactSlider view, boolean enabled) {
144 | view.setEnabled(enabled);
145 | }
146 |
147 | @ReactProp(name = "value", defaultDouble = 0d)
148 | public void setValue(ReactSlider view, double value) {
149 | view.setOnSeekBarChangeListener(null);
150 | view.setValue(value);
151 | view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
152 | }
153 |
154 | @ReactProp(name = "minimumValue", defaultDouble = 0d)
155 | public void setMinimumValue(ReactSlider view, double value) {
156 | view.setMinValue(value);
157 | }
158 |
159 | @ReactProp(name = "maximumValue", defaultDouble = 1d)
160 | public void setMaximumValue(ReactSlider view, double value) {
161 | view.setMaxValue(value);
162 | }
163 |
164 | @ReactProp(name = "step", defaultDouble = 0d)
165 | public void setStep(ReactSlider view, double value) {
166 | view.setStep(value);
167 | }
168 |
169 | @ReactProp(name = "thumbTintColor", customType = "Color")
170 | public void setThumbTintColor(ReactSlider view, Integer color) {
171 | if (view.getThumb() != null) {
172 | if (color == null) {
173 | view.getThumb().clearColorFilter();
174 | } else {
175 | view.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
176 | }
177 | }
178 | }
179 |
180 | @ReactProp(name = "minimumTrackTintColor", customType = "Color")
181 | public void setMinimumTrackTintColor(ReactSlider view, Integer color) {
182 | LayerDrawable drawable = (LayerDrawable) view.getProgressDrawable().getCurrent();
183 | Drawable progress = drawable.findDrawableByLayerId(android.R.id.progress);
184 | if (color == null) {
185 | progress.clearColorFilter();
186 | } else {
187 | progress.setColorFilter(color, PorterDuff.Mode.SRC_IN);
188 | }
189 | }
190 |
191 | @ReactProp(name = "thumbImage")
192 | public void setThumbImage(ReactSlider view, @Nullable ReadableMap source) {
193 | String uri = null;
194 | if (source != null) {
195 | uri = source.getString("uri");
196 | }
197 | view.setThumbImage(uri);
198 | }
199 |
200 | @ReactProp(name = "maximumTrackTintColor", customType = "Color")
201 | public void setMaximumTrackTintColor(ReactSlider view, Integer color) {
202 | LayerDrawable drawable = (LayerDrawable) view.getProgressDrawable().getCurrent();
203 | Drawable background = drawable.findDrawableByLayerId(android.R.id.background);
204 | if (color == null) {
205 | background.clearColorFilter();
206 | } else {
207 | background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
208 | }
209 | }
210 |
211 | @ReactProp(name = "inverted", defaultBoolean = false)
212 | public void setInverted(ReactSlider view, boolean inverted) {
213 | if (inverted) view.setScaleX(-1f);
214 | else view.setScaleX(1f);
215 | }
216 |
217 | @ReactProp(name = "accessibilityUnits")
218 | public void setAccessibilityUnits(ReactSlider view, String accessibilityUnits) {
219 | view.setAccessibilityUnits(accessibilityUnits);
220 | }
221 |
222 | @ReactProp(name = "accessibilityIncrements")
223 | public void setAccessibilityIncrements(ReactSlider view, ReadableArray accessibilityIncrements) {
224 | List objectList = accessibilityIncrements.toArrayList();
225 | List stringList = new ArrayList<>();
226 | for(Object item: objectList) {
227 | stringList.add((String)item);
228 | }
229 | view.setAccessibilityIncrements(stringList);
230 | }
231 |
232 | @Override
233 | protected void addEventEmitters(final ThemedReactContext reactContext, final ReactSlider view) {
234 | view.setOnSeekBarChangeListener(ON_CHANGE_LISTENER);
235 | }
236 |
237 | @Override
238 | public Map getExportedCustomDirectEventTypeConstants() {
239 | return MapBuilder.of(ReactSlidingCompleteEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRNCSliderSlidingComplete"),
240 | ReactSlidingStartEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRNCSliderSlidingStart"));
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/src/js/Slider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | *
7 | * @format
8 | * @flow
9 | */
10 |
11 | 'use strict';
12 |
13 | import React from 'react';
14 | import {Image, Platform, StyleSheet} from 'react-native';
15 | import RCTSliderNativeComponent from './RNCSliderNativeComponent';
16 |
17 | import type {Ref} from 'react';
18 | import type {NativeComponent} from 'react-native/Libraries/Renderer/shims/ReactNative';
19 | import type {ImageSource} from 'react-native/Libraries/Image/ImageSource';
20 | import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
21 | import type {ColorValue} from 'react-native/Libraries/StyleSheet/StyleSheetTypes';
22 | import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
23 | import type {SyntheticEvent} from 'react-native/Libraries/Types/CoreEventTypes';
24 |
25 | type Event = SyntheticEvent<
26 | $ReadOnly<{|
27 | value: number,
28 | /**
29 | * Android Only.
30 | */
31 | fromUser?: boolean,
32 | |}>,
33 | >;
34 |
35 | type WindowsProps = $ReadOnly<{|
36 | /**
37 | * If true the slider will be inverted.
38 | * Default value is false.
39 | */
40 | vertical?: ?boolean,
41 | |}>;
42 |
43 | type IOSProps = $ReadOnly<{|
44 | /**
45 | * Assigns a single image for the track. Only static images are supported.
46 | * The center pixel of the image will be stretched to fill the track.
47 | */
48 | trackImage?: ?ImageSource,
49 |
50 | /**
51 | * Assigns a minimum track image. Only static images are supported. The
52 | * rightmost pixel of the image will be stretched to fill the track.
53 | */
54 | minimumTrackImage?: ?ImageSource,
55 |
56 | /**
57 | * Assigns a maximum track image. Only static images are supported. The
58 | * leftmost pixel of the image will be stretched to fill the track.
59 | */
60 | maximumTrackImage?: ?ImageSource,
61 | |}>;
62 |
63 | type Props = $ReadOnly<{|
64 | ...ViewProps,
65 | ...IOSProps,
66 | ...WindowsProps,
67 |
68 | /**
69 | * Used to style and layout the `Slider`. See `StyleSheet.js` and
70 | * `DeprecatedViewStylePropTypes.js` for more info.
71 | */
72 | style?: ?ViewStyleProp,
73 |
74 | /**
75 | * Initial value of the slider. The value should be between minimumValue
76 | * and maximumValue, which default to 0 and 1 respectively.
77 | * Default value is 0.
78 | *
79 | * *This is not a controlled component*, you don't need to update the
80 | * value during dragging.
81 | */
82 | value?: ?number,
83 |
84 | /**
85 | * Step value of the slider. The value should be
86 | * between 0 and (maximumValue - minimumValue).
87 | * Default value is 0.
88 | */
89 | step?: ?number,
90 |
91 | /**
92 | * Initial minimum value of the slider. Default value is 0.
93 | */
94 | minimumValue?: ?number,
95 |
96 | /**
97 | * Initial maximum value of the slider. Default value is 1.
98 | */
99 | maximumValue?: ?number,
100 |
101 | /**
102 | * The color used for the track to the left of the button.
103 | * Overrides the default blue gradient image on iOS.
104 | */
105 | minimumTrackTintColor?: ?ColorValue,
106 |
107 | /**
108 | * The color used for the track to the right of the button.
109 | * Overrides the default blue gradient image on iOS.
110 | */
111 | maximumTrackTintColor?: ?ColorValue,
112 | /**
113 | * The color used to tint the default thumb images on iOS, or the
114 | * color of the foreground switch grip on Android.
115 | */
116 | thumbTintColor?: ?ColorValue,
117 |
118 | /**
119 | * If true the user won't be able to move the slider.
120 | * Default value is false.
121 | */
122 | disabled?: ?boolean,
123 |
124 | /**
125 | * Callback continuously called while the user is dragging the slider.
126 | */
127 | onValueChange?: ?(value: number) => void,
128 |
129 | /**
130 | * Callback that is called when the user touches the slider,
131 | * regardless if the value has changed. The current value is passed
132 | * as an argument to the callback handler.
133 | */
134 |
135 | onSlidingStart?: ?(value: number) => void,
136 |
137 | /**
138 | * Callback that is called when the user releases the slider,
139 | * regardless if the value has changed. The current value is passed
140 | * as an argument to the callback handler.
141 | */
142 | onSlidingComplete?: ?(value: number) => void,
143 |
144 | /**
145 | * Used to locate this view in UI automation tests.
146 | */
147 | testID?: ?string,
148 |
149 | /**
150 | * Sets an image for the thumb. Only static images are supported.
151 | */
152 | thumbImage?: ?ImageSource,
153 |
154 | /**
155 | * If true the slider will be inverted.
156 | * Default value is false.
157 | */
158 | inverted?: ?boolean,
159 |
160 | /**
161 | * A string of one or more words to be announced by the screen reader.
162 | * Otherwise, it will announce the value as a percentage.
163 | * Requires passing a value to `accessibilityIncrements` to work correctly.
164 | * Should be a plural word, as singular units will be handled.
165 | */
166 | accessibilityUnits?: string,
167 |
168 | /**
169 | * An array of values that represent the different increments displayed
170 | * by the slider. All the values passed into this prop must be strings.
171 | * Requires passing a value to `accessibilityUnits` to work correctly.
172 | * The number of elements must be the same as `maximumValue`.
173 | */
174 | accessibilityIncrements?: Array,
175 | |}>;
176 |
177 | /**
178 | * A component used to select a single value from a range of values.
179 | *
180 | * ### Usage
181 | *
182 | * The example below shows how to use `Slider` to change
183 | * a value used by `Text`. The value is stored using
184 | * the state of the root component (`App`). The same component
185 | * subscribes to the `onValueChange` of `Slider` and changes
186 | * the value using `setState`.
187 | *
188 | *```
189 | * import React from 'react';
190 | * import { StyleSheet, Text, View, Slider } from 'react-native';
191 | *
192 | * export default class App extends React.Component {
193 | * constructor(props) {
194 | * super(props);
195 | * this.state = {
196 | * value: 50
197 | * }
198 | * }
199 | *
200 | * change(value) {
201 | * this.setState(() => {
202 | * return {
203 | * value: parseFloat(value)
204 | * };
205 | * });
206 | * }
207 | *
208 | * render() {
209 | * const {value} = this.state;
210 | * return (
211 | *
212 | * {String(value)}
213 | *
218 | *
219 | * );
220 | * }
221 | * }
222 | *
223 | * const styles = StyleSheet.create({
224 | * container: {
225 | * flex: 1,
226 | * flexDirection: 'column',
227 | * justifyContent: 'center'
228 | * },
229 | * text: {
230 | * fontSize: 50,
231 | * textAlign: 'center'
232 | * }
233 | * });
234 | *```
235 | *
236 | */
237 | const SliderComponent = (
238 | props: Props,
239 | forwardedRef?: ?Ref,
240 | ) => {
241 | const style = StyleSheet.compose(
242 | styles.slider,
243 | props.style,
244 | );
245 |
246 | const {
247 | onValueChange,
248 | onSlidingStart,
249 | onSlidingComplete,
250 | ...localProps
251 | } = props;
252 |
253 | const onValueChangeEvent = onValueChange
254 | ? (event: Event) => {
255 | let userEvent = true;
256 | if (Platform.OS === 'android') {
257 | // On Android there's a special flag telling us the user is
258 | // dragging the slider.
259 | userEvent =
260 | event.nativeEvent.fromUser != null && event.nativeEvent.fromUser;
261 | }
262 | userEvent && onValueChange(event.nativeEvent.value);
263 | }
264 | : null;
265 |
266 | const onChangeEvent = onValueChangeEvent;
267 | const onSlidingStartEvent = onSlidingStart
268 | ? (event: Event) => {
269 | onSlidingStart(event.nativeEvent.value);
270 | }
271 | : null;
272 | const onSlidingCompleteEvent = onSlidingComplete
273 | ? (event: Event) => {
274 | onSlidingComplete(event.nativeEvent.value);
275 | }
276 | : null;
277 |
278 | return (
279 | true}
294 | onResponderTerminationRequest={() => false}
295 | />
296 | );
297 | };
298 |
299 | const SliderWithRef = React.forwardRef(SliderComponent);
300 |
301 | /* $FlowFixMe(>=0.89.0 site=react_native_fb) This comment suppresses an error
302 | * found when Flow v0.89 was deployed. To see the error, delete this comment
303 | * and run Flow. */
304 |
305 | SliderWithRef.defaultProps = {
306 | disabled: false,
307 | value: 0,
308 | minimumValue: 0,
309 | maximumValue: 1,
310 | step: 0,
311 | inverted: false,
312 | tapToSeek: false,
313 | };
314 |
315 | let styles;
316 | if (Platform.OS === 'ios') {
317 | styles = StyleSheet.create({
318 | slider: {
319 | height: 40,
320 | },
321 | });
322 | } else {
323 | styles = StyleSheet.create({
324 | slider: {},
325 | });
326 | }
327 |
328 | /* $FlowFixMe(>=0.89.0 site=react_native_fb) This comment suppresses an error
329 | * found when Flow v0.89 was deployed. To see the error, delete this comment
330 | * and run Flow. */
331 | const Slider = (SliderWithRef: Class>);
332 | export default Slider;
333 |
--------------------------------------------------------------------------------
/src/ios/RNCSlider.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 28C79A22220DC7760061DE82 /* RNCSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C79A1F220DC7760061DE82 /* RNCSliderManager.m */; };
11 | 28C79A23220DC7760061DE82 /* RNCSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C79A21220DC7760061DE82 /* RNCSlider.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | 28C79A07220DC4CC0061DE82 /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = "include/$(PRODUCT_NAME)";
19 | dstSubfolderSpec = 16;
20 | files = (
21 | );
22 | runOnlyForDeploymentPostprocessing = 0;
23 | };
24 | /* End PBXCopyFilesBuildPhase section */
25 |
26 | /* Begin PBXFileReference section */
27 | 28C79A09220DC4CC0061DE82 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCSlider.a; sourceTree = BUILT_PRODUCTS_DIR; };
28 | 28C79A1E220DC7760061DE82 /* RNCSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCSlider.h; sourceTree = ""; };
29 | 28C79A1F220DC7760061DE82 /* RNCSliderManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCSliderManager.m; sourceTree = ""; };
30 | 28C79A20220DC7760061DE82 /* RNCSliderManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCSliderManager.h; sourceTree = ""; };
31 | 28C79A21220DC7760061DE82 /* RNCSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCSlider.m; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 28C79A06220DC4CC0061DE82 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 28C79A00220DC4CC0061DE82 = {
46 | isa = PBXGroup;
47 | children = (
48 | 28C79A1E220DC7760061DE82 /* RNCSlider.h */,
49 | 28C79A21220DC7760061DE82 /* RNCSlider.m */,
50 | 28C79A20220DC7760061DE82 /* RNCSliderManager.h */,
51 | 28C79A1F220DC7760061DE82 /* RNCSliderManager.m */,
52 | 28C79A0A220DC4CC0061DE82 /* Products */,
53 | );
54 | sourceTree = "";
55 | };
56 | 28C79A0A220DC4CC0061DE82 /* Products */ = {
57 | isa = PBXGroup;
58 | children = (
59 | 28C79A09220DC4CC0061DE82 /* libRNCSlider.a */,
60 | );
61 | name = Products;
62 | sourceTree = "";
63 | };
64 | /* End PBXGroup section */
65 |
66 | /* Begin PBXNativeTarget section */
67 | 28C79A08220DC4CC0061DE82 /* RNCSlider */ = {
68 | isa = PBXNativeTarget;
69 | buildConfigurationList = 28C79A12220DC4CC0061DE82 /* Build configuration list for PBXNativeTarget "RNCSlider" */;
70 | buildPhases = (
71 | 28C79A05220DC4CC0061DE82 /* Sources */,
72 | 28C79A06220DC4CC0061DE82 /* Frameworks */,
73 | 28C79A07220DC4CC0061DE82 /* CopyFiles */,
74 | );
75 | buildRules = (
76 | );
77 | dependencies = (
78 | );
79 | name = RNCSlider;
80 | productName = RNCSlider;
81 | productReference = 28C79A09220DC4CC0061DE82 /* libRNCSlider.a */;
82 | productType = "com.apple.product-type.library.static";
83 | };
84 | /* End PBXNativeTarget section */
85 |
86 | /* Begin PBXProject section */
87 | 28C79A01220DC4CC0061DE82 /* Project object */ = {
88 | isa = PBXProject;
89 | attributes = {
90 | LastUpgradeCheck = 1010;
91 | ORGANIZATIONNAME = "React Native Community";
92 | TargetAttributes = {
93 | 28C79A08220DC4CC0061DE82 = {
94 | CreatedOnToolsVersion = 10.1;
95 | };
96 | };
97 | };
98 | buildConfigurationList = 28C79A04220DC4CC0061DE82 /* Build configuration list for PBXProject "RNCSlider" */;
99 | compatibilityVersion = "Xcode 9.3";
100 | developmentRegion = en;
101 | hasScannedForEncodings = 0;
102 | knownRegions = (
103 | en,
104 | );
105 | mainGroup = 28C79A00220DC4CC0061DE82;
106 | productRefGroup = 28C79A0A220DC4CC0061DE82 /* Products */;
107 | projectDirPath = "";
108 | projectRoot = "";
109 | targets = (
110 | 28C79A08220DC4CC0061DE82 /* RNCSlider */,
111 | );
112 | };
113 | /* End PBXProject section */
114 |
115 | /* Begin PBXSourcesBuildPhase section */
116 | 28C79A05220DC4CC0061DE82 /* Sources */ = {
117 | isa = PBXSourcesBuildPhase;
118 | buildActionMask = 2147483647;
119 | files = (
120 | 28C79A23220DC7760061DE82 /* RNCSlider.m in Sources */,
121 | 28C79A22220DC7760061DE82 /* RNCSliderManager.m in Sources */,
122 | );
123 | runOnlyForDeploymentPostprocessing = 0;
124 | };
125 | /* End PBXSourcesBuildPhase section */
126 |
127 | /* Begin XCBuildConfiguration section */
128 | 28C79A10220DC4CC0061DE82 /* Debug */ = {
129 | isa = XCBuildConfiguration;
130 | buildSettings = {
131 | ALWAYS_SEARCH_USER_PATHS = NO;
132 | CLANG_ANALYZER_NONNULL = YES;
133 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
135 | CLANG_CXX_LIBRARY = "libc++";
136 | CLANG_ENABLE_MODULES = YES;
137 | CLANG_ENABLE_OBJC_ARC = YES;
138 | CLANG_ENABLE_OBJC_WEAK = YES;
139 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
140 | CLANG_WARN_BOOL_CONVERSION = YES;
141 | CLANG_WARN_COMMA = YES;
142 | CLANG_WARN_CONSTANT_CONVERSION = YES;
143 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
145 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
146 | CLANG_WARN_EMPTY_BODY = YES;
147 | CLANG_WARN_ENUM_CONVERSION = YES;
148 | CLANG_WARN_INFINITE_RECURSION = YES;
149 | CLANG_WARN_INT_CONVERSION = YES;
150 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
151 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
152 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
154 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
155 | CLANG_WARN_STRICT_PROTOTYPES = YES;
156 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
157 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
158 | CLANG_WARN_UNREACHABLE_CODE = YES;
159 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
160 | COPY_PHASE_STRIP = NO;
161 | ENABLE_STRICT_OBJC_MSGSEND = YES;
162 | ENABLE_TESTABILITY = YES;
163 | GCC_C_LANGUAGE_STANDARD = gnu99;
164 | GCC_DYNAMIC_NO_PIC = NO;
165 | GCC_NO_COMMON_BLOCKS = YES;
166 | GCC_OPTIMIZATION_LEVEL = 0;
167 | GCC_PREPROCESSOR_DEFINITIONS = (
168 | "DEBUG=1",
169 | "$(inherited)",
170 | );
171 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
172 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
173 | GCC_WARN_UNDECLARED_SELECTOR = YES;
174 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
175 | GCC_WARN_UNUSED_FUNCTION = YES;
176 | GCC_WARN_UNUSED_VARIABLE = YES;
177 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
178 | MTL_ENABLE_DEBUG_INFO = YES;
179 | MTL_FAST_MATH = YES;
180 | ONLY_ACTIVE_ARCH = YES;
181 | SDKROOT = iphoneos;
182 | };
183 | name = Debug;
184 | };
185 | 28C79A11220DC4CC0061DE82 /* Release */ = {
186 | isa = XCBuildConfiguration;
187 | buildSettings = {
188 | ALWAYS_SEARCH_USER_PATHS = NO;
189 | CLANG_ANALYZER_NONNULL = YES;
190 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
191 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
192 | CLANG_CXX_LIBRARY = "libc++";
193 | CLANG_ENABLE_MODULES = YES;
194 | CLANG_ENABLE_OBJC_ARC = YES;
195 | CLANG_ENABLE_OBJC_WEAK = YES;
196 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
197 | CLANG_WARN_BOOL_CONVERSION = YES;
198 | CLANG_WARN_COMMA = YES;
199 | CLANG_WARN_CONSTANT_CONVERSION = YES;
200 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
202 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
203 | CLANG_WARN_EMPTY_BODY = YES;
204 | CLANG_WARN_ENUM_CONVERSION = YES;
205 | CLANG_WARN_INFINITE_RECURSION = YES;
206 | CLANG_WARN_INT_CONVERSION = YES;
207 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
208 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
209 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
210 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
211 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
212 | CLANG_WARN_STRICT_PROTOTYPES = YES;
213 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
214 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
215 | CLANG_WARN_UNREACHABLE_CODE = YES;
216 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
217 | COPY_PHASE_STRIP = YES;
218 | ENABLE_NS_ASSERTIONS = NO;
219 | ENABLE_STRICT_OBJC_MSGSEND = YES;
220 | GCC_C_LANGUAGE_STANDARD = gnu99;
221 | GCC_NO_COMMON_BLOCKS = YES;
222 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
223 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
224 | GCC_WARN_UNDECLARED_SELECTOR = YES;
225 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
226 | GCC_WARN_UNUSED_FUNCTION = YES;
227 | GCC_WARN_UNUSED_VARIABLE = YES;
228 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
229 | MTL_ENABLE_DEBUG_INFO = NO;
230 | MTL_FAST_MATH = YES;
231 | SDKROOT = iphoneos;
232 | VALIDATE_PRODUCT = YES;
233 | };
234 | name = Release;
235 | };
236 | 28C79A13220DC4CC0061DE82 /* Debug */ = {
237 | isa = XCBuildConfiguration;
238 | buildSettings = {
239 | OTHER_LDFLAGS = "-ObjC";
240 | PRODUCT_NAME = "$(TARGET_NAME)";
241 | SKIP_INSTALL = YES;
242 | };
243 | name = Debug;
244 | };
245 | 28C79A14220DC4CC0061DE82 /* Release */ = {
246 | isa = XCBuildConfiguration;
247 | buildSettings = {
248 | OTHER_LDFLAGS = "-ObjC";
249 | PRODUCT_NAME = "$(TARGET_NAME)";
250 | SKIP_INSTALL = YES;
251 | };
252 | name = Release;
253 | };
254 | /* End XCBuildConfiguration section */
255 |
256 | /* Begin XCConfigurationList section */
257 | 28C79A04220DC4CC0061DE82 /* Build configuration list for PBXProject "RNCSlider" */ = {
258 | isa = XCConfigurationList;
259 | buildConfigurations = (
260 | 28C79A10220DC4CC0061DE82 /* Debug */,
261 | 28C79A11220DC4CC0061DE82 /* Release */,
262 | );
263 | defaultConfigurationIsVisible = 0;
264 | defaultConfigurationName = Release;
265 | };
266 | 28C79A12220DC4CC0061DE82 /* Build configuration list for PBXNativeTarget "RNCSlider" */ = {
267 | isa = XCConfigurationList;
268 | buildConfigurations = (
269 | 28C79A13220DC4CC0061DE82 /* Debug */,
270 | 28C79A14220DC4CC0061DE82 /* Release */,
271 | );
272 | defaultConfigurationIsVisible = 0;
273 | defaultConfigurationName = Release;
274 | };
275 | /* End XCConfigurationList section */
276 | };
277 | rootObject = 28C79A01220DC4CC0061DE82 /* Project object */;
278 | }
279 |
--------------------------------------------------------------------------------
/src/windows/SliderWindows/SliderView.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | #include "pch.h"
5 |
6 | #include "JSValueXaml.h"
7 |
8 | #include "SliderView.h"
9 | #include "SliderView.g.cpp"
10 |
11 | #include "winrt/Windows.UI.Xaml.Media.h"
12 | #include "winrt/Windows.UI.Xaml.Media.Imaging.h"
13 | #include "winrt/Windows.UI.Xaml.Input.h"
14 |
15 | namespace winrt {
16 | using namespace Microsoft::ReactNative;
17 | using namespace Windows::Foundation;
18 | using namespace Windows::UI;
19 | using namespace Windows::UI::Xaml::Media;
20 | using namespace Windows::UI::Xaml::Media::Imaging;
21 | }
22 |
23 | namespace winrt::SliderWindows::implementation {
24 |
25 | SliderView::SliderView(winrt::IReactContext const& reactContext) : m_reactContext(reactContext) {
26 | RegisterEvents();
27 | }
28 |
29 | void SliderView::RegisterEvents() {
30 | //Set this so that Slider passes on Manipulation* events.
31 | this->ManipulationMode(xaml::Input::ManipulationModes::All);
32 |
33 | m_sliderValueChangedRevoker = this->ValueChanged(winrt::auto_revoke,
34 | [ref = get_weak()](auto const& sender, auto const& args) {
35 | if (auto self = ref.get()) {
36 | self->OnValueChangedHandler(sender, args);
37 | }
38 | });
39 |
40 | m_sliderManipulationStartingRevoker = this->ManipulationStarting(winrt::auto_revoke,
41 | [ref = get_weak()](auto const& sender, auto const& args) {
42 | if (auto self = ref.get()) {
43 | self->OnManipulationStartingHandler(sender, args);
44 | }
45 | });
46 |
47 | m_sliderManipulationCompletedRevoker = this->ManipulationCompleted(winrt::auto_revoke,
48 | [ref = get_weak()](auto const& sender, auto const& args) {
49 | if (auto self = ref.get()) {
50 | self->OnManipulationCompletedHandler(sender, args);
51 | }
52 | });
53 | }
54 |
55 | void SliderView::UpdateProperties(winrt::IJSValueReader const& reader) {
56 | m_updating = true;
57 |
58 | bool updatedValue = false;
59 | bool updatedMaxValue = false;
60 | bool updatedMinValue = false;
61 |
62 | auto const& propertyMap = JSValueObject::ReadFrom(reader);
63 |
64 | for (auto const& pair : propertyMap) {
65 | auto const& propertyName = pair.first;
66 | auto const& propertyValue = pair.second;
67 |
68 | if (propertyName == "value") {
69 | if (propertyValue.IsNull()) {
70 | this->ClearValue(xaml::Controls::Primitives::RangeBase::ValueProperty());
71 | }
72 | else {
73 | updatedValue = true;
74 | m_value = propertyValue.AsDouble();
75 | }
76 | }
77 | else if (propertyName == "maximumValue") {
78 | if (propertyValue.IsNull()) {
79 | this->ClearValue(xaml::Controls::Primitives::RangeBase::MaximumProperty());
80 | }
81 | else {
82 | updatedMaxValue = true;
83 | m_maxValue = propertyValue.AsDouble();
84 | }
85 | }
86 | else if (propertyName == "minimumValue") {
87 | if (propertyValue.IsNull()) {
88 | this->ClearValue(xaml::Controls::Primitives::RangeBase::MinimumProperty());
89 | }
90 | else {
91 | updatedMinValue = true;
92 | m_minValue = propertyValue.AsDouble();
93 | }
94 | }
95 | else if (propertyName == "step") {
96 | if (propertyValue.IsNull()) {
97 | this->ClearValue(xaml::Controls::Slider::StepFrequencyProperty());
98 | }
99 | else {
100 | auto&& step = CalculateStepFrequencyPercentageValue(propertyValue.AsDouble());
101 | this->StepFrequency(step);
102 | }
103 | }
104 | else if (propertyName == "inverted") {
105 | if (propertyValue.IsNull()) {
106 | this->ClearValue(xaml::Controls::Slider::IsDirectionReversedProperty());
107 | }
108 | else {
109 | this->IsDirectionReversed(propertyValue.AsBoolean());
110 | }
111 | }
112 | else if (propertyName == "disabled") {
113 | if (propertyValue.IsNull()) {
114 | this->ClearValue(xaml::Controls::Control::IsEnabledProperty());
115 | }
116 | else {
117 | this->IsEnabled(!propertyValue.AsBoolean());
118 | }
119 | }
120 | else if (propertyName == "vertical") {
121 | if (propertyValue.IsNull()) {
122 | this->ClearValue(xaml::Controls::Slider::OrientationProperty());
123 | }
124 | else {
125 | this->Orientation(propertyValue.AsBoolean() ?
126 | xaml::Controls::Orientation::Vertical :
127 | xaml::Controls::Orientation::Horizontal);
128 | }
129 | }
130 | else if (propertyName == "minimumTrackTintColor") {
131 | if (!propertyValue.IsNull()) {
132 | auto resDict = this->Resources();
133 |
134 | resDict.Insert(winrt::box_value(L"SliderTrackValueFill"), propertyValue.To());
135 | resDict.Insert(winrt::box_value(L"SliderTrackValueFillPointerOver"), propertyValue.To());
136 | resDict.Insert(winrt::box_value(L"SliderTrackValueFillPressed"), propertyValue.To());
137 | }
138 | }
139 | else if (propertyName == "maximumTrackTintColor") {
140 | if (!propertyValue.IsNull()) {
141 | auto resDict = this->Resources();
142 |
143 | resDict.Insert(winrt::box_value(L"SliderTrackFill"), propertyValue.To());
144 | resDict.Insert(winrt::box_value(L"SliderTrackFillPointerOver"), propertyValue.To());
145 | resDict.Insert(winrt::box_value(L"SliderTrackFillPressed"), propertyValue.To());
146 | }
147 | }
148 | else if (propertyName == "thumbTintColor") {
149 | if (!propertyValue.IsNull()) {
150 | auto resDict = this->Resources();
151 |
152 | resDict.Insert(winrt::box_value(L"SliderThumbBackground"), propertyValue.To());
153 | resDict.Insert(winrt::box_value(L"SliderThumbBackgroundPointerOver"), propertyValue.To());
154 | resDict.Insert(winrt::box_value(L"SliderThumbBackgroundPressed"), propertyValue.To());
155 | }
156 | }
157 | else if (propertyName == "thumbImage") {
158 | if (!propertyValue.IsNull()) {
159 | auto imgUriString = propertyValue.AsObject()["uri"].AsString();
160 | BitmapImage bitmap = BitmapImage(Uri(to_hstring(imgUriString)));
161 | ImageBrush imgB = ImageBrush();
162 | imgB.ImageSource(bitmap);
163 |
164 | auto resDict = this->Resources();
165 | resDict.Insert(winrt::box_value(L"SliderThumbBackground"), imgB);
166 | resDict.Insert(winrt::box_value(L"SliderThumbBackgroundPointerOver"), imgB);
167 | resDict.Insert(winrt::box_value(L"SliderThumbBackgroundPressed"), imgB);
168 | }
169 | }
170 | }
171 |
172 | // This is to mitigate platform bugs related to the order of setting min/max values in certain XAML controls.
173 | if (updatedMaxValue) {
174 | this->Maximum(m_maxValue);
175 | }
176 | if (updatedMinValue) {
177 | this->Minimum(m_minValue);
178 | }
179 | if (updatedValue) {
180 | this->Value(m_value);
181 | }
182 |
183 | m_updating = false;
184 | }
185 |
186 | void SliderView::OnValueChangedHandler(winrt::IInspectable const& /*sender*/,
187 | xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& args){
188 |
189 | if (!m_updating) {
190 | m_reactContext.DispatchEvent(
191 | *this,
192 | L"topChange",
193 | [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept {
194 | eventDataWriter.WriteObjectBegin();
195 | {
196 | WriteProperty(eventDataWriter, L"value", args.NewValue());
197 | }
198 | eventDataWriter.WriteObjectEnd();
199 | });
200 | }
201 | }
202 |
203 | void SliderView::OnManipulationStartingHandler(winrt::IInspectable const& sender,
204 | xaml::Input::ManipulationStartingRoutedEventArgs const& args) {
205 | if (!m_updating) {
206 | auto self = sender.try_as();
207 |
208 | m_reactContext.DispatchEvent(
209 | *this,
210 | L"topSlidingStart",
211 | [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept {
212 | eventDataWriter.WriteObjectBegin();
213 | {
214 | WriteProperty(eventDataWriter, L"value", self.Value());
215 | }
216 | eventDataWriter.WriteObjectEnd();
217 | });
218 | }
219 | }
220 |
221 | void SliderView::OnManipulationCompletedHandler(winrt::IInspectable const& sender,
222 | xaml::Input::ManipulationCompletedRoutedEventArgs const& args) {
223 | if (!m_updating) {
224 | auto self = sender.try_as();
225 |
226 | m_reactContext.DispatchEvent(
227 | *this,
228 | L"topSlidingComplete",
229 | [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept {
230 | eventDataWriter.WriteObjectBegin();
231 | {
232 | WriteProperty(eventDataWriter, L"value", self.Value());
233 | }
234 | eventDataWriter.WriteObjectEnd();
235 | });
236 | }
237 | }
238 |
239 | const double SliderView::CalculateStepFrequencyPercentageValue(const double& stepPropertyValue) const noexcept {
240 | if (stepPropertyValue != 0) {
241 | return stepPropertyValue;
242 | }
243 | else {
244 | return (m_maxValue - m_minValue) / 100.f;
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/example/windows/SliderTestWindows/SliderTestWindows.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | true
7 | true
8 | {942a8514-0854-4f75-b43b-61e8ffc678cb}
9 | SliderTestWindows
10 | SliderTestWindows
11 | en-US
12 | 16.0
13 | true
14 | Windows Store
15 | 10.0
16 | 10.0.18362.0
17 | 10.0.16299.0
18 | SliderTestWindows_TemporaryKey.pfx
19 | 2B865F049A8EC254802CBE811120971BD5F2CDB5
20 | password
21 |
22 |
23 |
24 |
25 | Debug
26 | ARM
27 |
28 |
29 | Debug
30 | ARM64
31 |
32 |
33 | Debug
34 | Win32
35 |
36 |
37 | Debug
38 | x64
39 |
40 |
41 | Release
42 | ARM
43 |
44 |
45 | Release
46 | ARM64
47 |
48 |
49 | Release
50 | Win32
51 |
52 |
53 | Release
54 | x64
55 |
56 |
57 |
58 | Application
59 | Unicode
60 |
61 |
62 | true
63 | true
64 |
65 |
66 | false
67 | true
68 | false
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | Use
86 | pch.h
87 | $(IntDir)pch.pch
88 | Level4
89 | %(AdditionalOptions) /bigobj
90 | 4453;28204
91 |
92 |
93 |
94 |
95 | _DEBUG;%(PreprocessorDefinitions)
96 |
97 |
98 |
99 |
100 | NDEBUG;%(PreprocessorDefinitions)
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | App.xaml
109 |
110 |
111 |
112 |
113 | Designer
114 |
115 |
116 |
117 |
118 | Designer
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | Create
135 |
136 |
137 | App.xaml
138 |
139 |
140 |
141 |
142 |
143 | App.xaml
144 |
145 |
146 |
147 |
148 |
149 |
150 | false
151 |
152 |
153 |
154 |
155 | {685a83ae-36bc-4e9d-bdc6-417ebf168463}
156 |
157 |
158 |
159 |
160 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | This project references targets in your node_modules\react-native-windows folder. The missing file is {0}.
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
180 |
181 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------