Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Range slider #1723

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
32 changes: 28 additions & 4 deletions modules/ensemble/lib/util/utils.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'package:ensemble/ensemble.dart';
import 'package:ensemble/ensemble_app.dart';
import 'package:ensemble/framework/stub/location_manager.dart';
import 'package:ensemble/framework/theme/theme_manager.dart';
import 'package:ensemble_ts_interpreter/invokables/UserLocale.dart';
Expand All @@ -11,7 +9,6 @@ import 'package:path/path.dart' as p;
import 'package:ensemble/framework/error_handling.dart';
import 'package:ensemble/framework/extensions.dart';
import 'package:ensemble/framework/model.dart';
import 'package:ensemble/framework/scope.dart';
import 'package:ensemble/widget/helpers/controllers.dart';
import 'package:ensemble_ts_interpreter/invokables/invokableprimitives.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -77,6 +74,32 @@ class Utils {
return rtn;
}

/// Expects a number or a String containing 2 numbers values separated by whitespace
/// (e.g. 1 or "1 4")
static RangeValues? getRangeValues(dynamic value) {
if (value is num) {
final start = optionalDouble(value);
if (start != null) {
return RangeValues(start, start);
}
} else if (value is String) {
final List<String> valuesList = value.split(RegExp('\\s+'));
if (valuesList.length == 1) {
final start = optionalDouble(valuesList[0]);
if (start != null) {
return RangeValues(start, start);
}
} else if (valuesList.length == 2) {
final start = optionalDouble(valuesList[0]);
final end = optionalDouble(valuesList[1]);
if (start != null && end != null) {
return RangeValues(start, end);
}
}
}
return null;
}

/// expect a value in seconds
static Duration? getDuration(dynamic value) {
double? number = optionalDouble(value, min: 0);
Expand Down Expand Up @@ -549,7 +572,8 @@ class Utils {
decorationStyle:
TextDecorationStyle.values.from(style['decorationStyle']),
decorationColor: Utils.getColor(style['decorationColor']),
decorationThickness: Utils.optionalDouble(style['decorationThickness']),
decorationThickness:
Utils.optionalDouble(style['decorationThickness']),
overflow: TextOverflow.values.from(style['overflow']),
letterSpacing: Utils.optionalDouble(style['letterSpacing']),
wordSpacing: Utils.optionalDouble(style['wordSpacing']));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ class ThumbStyleComposite extends WidgetCompositeProperty {
Utils.getDouble(payload['elevation'], fallback: 1.0);
composite.pressedElevation =
Utils.getDouble(payload['pressedElevation'], fallback: 2.0);
composite.thumbColor =
Utils.getColor(payload['thumbColor']);
composite.thumbColor = Utils.getColor(payload['thumbColor']);
composite.disabledThumbColor =
Utils.getColor(payload['disabledThumbColor']);
composite.borderWidth =
Expand All @@ -94,14 +93,12 @@ class ThumbStyleComposite extends WidgetCompositeProperty {

@override
Map<String, Function> setters() => {
'radius': (value) =>
radius = Utils.getDouble(value, fallback: 10.0),
'radius': (value) => radius = Utils.getDouble(value, fallback: 10.0),
'elevation': (value) =>
elevation = Utils.getDouble(value, fallback: 1.0),
'pressedElevation': (value) =>
pressedElevation = Utils.getDouble(value, fallback: 2.0),
'thumbColor': (value) =>
thumbColor = Utils.getColor(value),
'thumbColor': (value) => thumbColor = Utils.getColor(value),
'disabledThumbColor': (value) =>
disabledThumbColor = Utils.getColor(value),
'borderWidth': (value) =>
Expand Down Expand Up @@ -132,4 +129,12 @@ class ThumbStyleComposite extends WidgetCompositeProperty {
borderColor: borderColor,
);
}
}

RangeSliderThumbShape getRangeThumbShape() {
return RoundRangeSliderThumbShape(
enabledThumbRadius: radius,
elevation: elevation,
pressedElevation: pressedElevation,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,15 @@ class TrackStyleComposite extends WidgetCompositeProperty {
return RectangularSliderTrackShape();
}
}
}

RangeSliderTrackShape getRangeTrackShape() {
switch (shape) {
case 'rectangular':
return RectangularRangeSliderTrackShape();
case 'circle':
return RoundedRectRangeSliderTrackShape();
default:
return RectangularRangeSliderTrackShape();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ class ValueIndicatorStyleComposite extends WidgetCompositeProperty {
: super(widgetController);

ShowValueIndicator visibility = ShowValueIndicator.onlyForDiscrete;
ValueIndicatorShape shape = ValueIndicatorShape.drop;
ValueIndicatorShape shape = ValueIndicatorShape.drop;
Color? color;
TextStyle? textStyle;

factory ValueIndicatorStyleComposite.from(
ChangeNotifier widgetController, dynamic payload) {
ValueIndicatorStyleComposite composite = ValueIndicatorStyleComposite(widgetController);
ValueIndicatorStyleComposite composite =
ValueIndicatorStyleComposite(widgetController);
if (payload is Map) {
composite.visibility = ShowValueIndicator.values.from(payload['visibility']) ??
ShowValueIndicator.onlyForDiscrete;

composite.shape = ValueIndicatorShape.values.from(payload['shape']) ??
ValueIndicatorShape.drop;

composite.visibility =
ShowValueIndicator.values.from(payload['visibility']) ??
ShowValueIndicator.onlyForDiscrete;

composite.shape = ValueIndicatorShape.values.from(payload['shape']) ??
ValueIndicatorShape.drop;

composite.color = Utils.getColor(payload['color']);
composite.textStyle = Utils.getTextStyle(payload['textStyle']);
}
Expand All @@ -30,12 +32,11 @@ class ValueIndicatorStyleComposite extends WidgetCompositeProperty {

@override
Map<String, Function> setters() => {
'visibility': (value) =>
visibility = ShowValueIndicator.values.from(value) ??
'visibility': (value) => visibility =
ShowValueIndicator.values.from(value) ??
ShowValueIndicator.onlyForDiscrete,
'shape': (value) =>
shape = ValueIndicatorShape.values.from(value) ??
ValueIndicatorShape.drop,
'shape': (value) => shape =
ValueIndicatorShape.values.from(value) ?? ValueIndicatorShape.drop,
'color': (value) => color = Utils.getColor(value),
'textStyle': (value) => textStyle = Utils.getTextStyle(value),
};
Expand All @@ -58,15 +59,22 @@ class ValueIndicatorStyleComposite extends WidgetCompositeProperty {
case ValueIndicatorShape.paddle:
return PaddleSliderValueIndicatorShape();
case ValueIndicatorShape.drop:
return DropSliderValueIndicatorShape();
return DropSliderValueIndicatorShape();
default:
return DropSliderValueIndicatorShape();
}
}

RangeSliderValueIndicatorShape? getRangeIndicatorShape() {
switch (shape) {
case ValueIndicatorShape.rectangular:
return RectangularRangeSliderValueIndicatorShape();
case ValueIndicatorShape.paddle:
return PaddleRangeSliderValueIndicatorShape();
default:
return DropSliderValueIndicatorShape();
return RectangularRangeSliderValueIndicatorShape();
}
}
}

enum ValueIndicatorShape {
drop,
paddle,
rectangular
}
enum ValueIndicatorShape { drop, paddle, rectangular }
17 changes: 10 additions & 7 deletions modules/ensemble/lib/widget/input/slider/slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class EnsembleSlider extends StatefulWidget
Map<String, Function> setters() {
return {
// Basic Properties
'initialValue': (value) =>
_controller.value = Utils.optionalDouble(value) ?? 0,
'initialValue': (value) => _controller.value =
Utils.getRangeValues(value) ?? const RangeValues(0, 0),
'min': (value) =>
_controller.minValue = Utils.getDouble(value, fallback: 0.0),
'max': (value) =>
Expand All @@ -60,11 +60,11 @@ class EnsembleSlider extends StatefulWidget
_controller.trackStyle = TrackStyleComposite.from(_controller, value),
'tickMarkStyle': (value) => _controller.tickMarkStyle =
TickMarkStyleComposite.from(_controller, value),
'thumbStyle': (value) => _controller.thumbStyle =
ThumbStyleComposite.from(_controller, value),
'overlayStyle': (value) => _controller.overlayStyle =
'thumbStyle': (value) =>
_controller.thumbStyle = ThumbStyleComposite.from(_controller, value),
'overlayStyle': (value) => _controller.overlayStyle =
OverlayStyleComposite.from(_controller, value),
'valueIndicatorStyle': (value) => _controller.valueIndicatorStyle =
'valueIndicatorStyle': (value) => _controller.valueIndicatorStyle =
ValueIndicatorStyleComposite.from(_controller, value),

// @deprecated properties
Expand All @@ -85,6 +85,9 @@ class EnsembleSlider extends StatefulWidget
// Event Handler
'onChange': (definition) => _controller.onChange =
EnsembleAction.from(definition, initiator: this),

'isRange': (value) =>
_controller.isRange = Utils.getBool(value, fallback: false),
};
}
}
}
11 changes: 7 additions & 4 deletions modules/ensemble/lib/widget/input/slider/slider_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'composites/value_indicator_style.dart';

class SliderController extends FormFieldController {
// Basic Values
double value = 0.0;
RangeValues value = const RangeValues(0.0, 1.0);
double minValue = 0.0;
double maxValue = 1.0;
int? divisions;
Expand All @@ -26,7 +26,7 @@ class SliderController extends FormFieldController {
set tickMarkStyle(TickMarkStyleComposite value) => _tickMarkStyle = value;

ThumbStyleComposite? _thumbStyle;
ThumbStyleComposite get thumbStyle =>
ThumbStyleComposite get thumbStyle =>
_thumbStyle ??= ThumbStyleComposite(this);
set thumbStyle(ThumbStyleComposite value) => _thumbStyle = value;

Expand All @@ -38,7 +38,7 @@ class SliderController extends FormFieldController {
ValueIndicatorStyleComposite? _valueIndicatorStyle;
ValueIndicatorStyleComposite get valueIndicatorStyle =>
_valueIndicatorStyle ??= ValueIndicatorStyleComposite(this);
set valueIndicatorStyle(ValueIndicatorStyleComposite value) =>
set valueIndicatorStyle(ValueIndicatorStyleComposite value) =>
_valueIndicatorStyle = value;

// @deprecated. backward compatibility
Expand All @@ -52,4 +52,7 @@ class SliderController extends FormFieldController {

// Event Handler
EnsembleAction? onChange;
}

// Optional property to determine if the slider is a range slider
bool isRange = false;
}
69 changes: 48 additions & 21 deletions modules/ensemble/lib/widget/input/slider/slider_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,37 +79,64 @@ class SliderState extends FormFieldWidgetState<EnsembleSlider> {
widget.controller.valueIndicatorStyle.textStyle,
valueIndicatorShape:
widget.controller.valueIndicatorStyle.getIndicatorShape(),

// Range Slider Properties
rangeValueIndicatorShape:
widget.controller.valueIndicatorStyle.getRangeIndicatorShape(),
rangeThumbShape: widget.controller.thumbStyle.getRangeThumbShape(),
rangeTrackShape: widget.controller.trackStyle.getRangeTrackShape(),
);

return SliderTheme(
data: themeData,
child: Slider(
value: widget.controller.value,
min: widget.controller.minValue,
max: widget.controller.maxValue,
divisions: widget.controller.divisions,
label: widget.controller.value.toStringAsFixed(decimalPlaces),
onChanged: isEnabled()
? (value) {
setState(() {
widget.controller.value = value;
});
if (widget.controller.onChange != null) {
ScreenController().executeAction(
context,
widget.controller.onChange!,
event: EnsembleEvent(widget),
);
}
}
: null,
),
child: widget.controller.isRange
? RangeSlider(
labels: RangeLabels(
widget.controller.value.start
.toStringAsFixed(decimalPlaces),
widget.controller.value.end
.toStringAsFixed(decimalPlaces),
),
min: widget.controller.minValue,
max: widget.controller.maxValue,
values: widget.controller.value,
divisions: widget.controller.divisions,
onChanged: _onChanged,
)
: Slider(
value: widget.controller.value.start,
min: widget.controller.minValue,
max: widget.controller.maxValue,
divisions: widget.controller.divisions,
label: widget.controller.value.start
.toStringAsFixed(decimalPlaces),
onChanged: _onChanged,
),
);
},
),
);
}

void _onChanged(dynamic value) {
if (!isEnabled()) return;

setState(() {
if (value is RangeValues) {
widget.controller.value = value;
} else if (value is double) {
widget.controller.value = RangeValues(value, value);
}
});
if (widget.controller.onChange != null) {
ScreenController().executeAction(
context,
widget.controller.onChange!,
event: EnsembleEvent(widget),
);
}
}

int calculateDecimalPlaces(double min, double max, int? divisions) {
if (divisions == null) return 1;
double interval = (max - min) / divisions;
Expand Down
Loading