diff options
| author | Robert Jaros <rjaros@finn.pl> | 2018-02-22 12:43:43 +0100 |
|---|---|---|
| committer | Robert Jaros <rjaros@finn.pl> | 2018-02-22 12:43:43 +0100 |
| commit | e40c13c62de806169368adc7c2c4212f7df6413b (patch) | |
| tree | 250e39d407cd2c6ef19c9a8ea0176a3215a4a818 | |
| parent | 1aefed336f9ef305f59d3668abc8a39c550e921d (diff) | |
| download | kvision-e40c13c62de806169368adc7c2c4212f7df6413b.tar.gz kvision-e40c13c62de806169368adc7c2c4212f7df6413b.tar.bz2 kvision-e40c13c62de806169368adc7c2c4212f7df6413b.zip | |
Property values delegated to a map (idea by tieskedh)
Closes #2
27 files changed, 202 insertions, 805 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt index f0ceb587..d130ec0b 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/StyledComponent.kt @@ -22,187 +22,102 @@ package pl.treksoft.kvision.core import pl.treksoft.kvision.utils.asString +import kotlin.reflect.KProperty /** * Base class for components supporting CSS styling. */ abstract class StyledComponent : Component { + private val propertyValues: MutableMap<String, Any?> = mutableMapOf() + /** * Width of the current component. */ - open var width: CssSize? = null - set(value) { - field = value - refresh() - } + open var width: CssSize? by refreshOnUpdate() /** * Minimal width of the current component. */ - var minWidth: CssSize? = null - set(value) { - field = value - refresh() - } + var minWidth: CssSize? by refreshOnUpdate() /** * Maximal width of the current component. */ - var maxWidth: CssSize? = null - set(value) { - field = value - refresh() - } + var maxWidth: CssSize? by refreshOnUpdate() /** * Height of the current component. */ - var height: CssSize? = null - set(value) { - field = value - refresh() - } + var height: CssSize? by refreshOnUpdate() /** * Minimal height of the current component. */ - var minHeight: CssSize? = null - set(value) { - field = value - refresh() - } + var minHeight: CssSize? by refreshOnUpdate() /** * Maximal height of the current component. */ - var maxHeight: CssSize? = null - set(value) { - field = value - refresh() - } + var maxHeight: CssSize? by refreshOnUpdate() /** * Border of the current component. */ - var border: Border? = null - set(value) { - field = value - refresh() - } + var border: Border? by refreshOnUpdate() /** * Top border of the current component. */ - var borderTop: Border? = null - set(value) { - field = value - refresh() - } + var borderTop: Border? by refreshOnUpdate() /** * Right border of the current component. */ - var borderRight: Border? = null - set(value) { - field = value - refresh() - } + var borderRight: Border? by refreshOnUpdate() /** * Bottom border of the current component. */ - var borderBottom: Border? = null - set(value) { - field = value - refresh() - } + var borderBottom: Border? by refreshOnUpdate() /** * Left border of the current component. */ - var borderLeft: Border? = null - set(value) { - field = value - refresh() - } + var borderLeft: Border? by refreshOnUpdate() /** * Margin of the current component. */ - var margin: CssSize? = null - set(value) { - field = value - refresh() - } + var margin: CssSize? by refreshOnUpdate() /** * Top margin of the current component. */ - var marginTop: CssSize? = null - set(value) { - field = value - refresh() - } + var marginTop: CssSize? by refreshOnUpdate() /** * Right margin of the current component. */ - var marginRight: CssSize? = null - set(value) { - field = value - refresh() - } + var marginRight: CssSize? by refreshOnUpdate() /** * Bottom margin of the current component. */ - var marginBottom: CssSize? = null - set(value) { - field = value - refresh() - } + var marginBottom: CssSize? by refreshOnUpdate() /** * Left margin of the current component. */ - var marginLeft: CssSize? = null - set(value) { - field = value - refresh() - } + var marginLeft: CssSize? by refreshOnUpdate() /** * Padding of the current component. */ - var padding: CssSize? = null - set(value) { - field = value - refresh() - } + var padding: CssSize? by refreshOnUpdate() /** * Top padding of the current component. */ - var paddingTop: CssSize? = null - set(value) { - field = value - refresh() - } + var paddingTop: CssSize? by refreshOnUpdate() /** * Right padding of the current component. */ - var paddingRight: CssSize? = null - set(value) { - field = value - refresh() - } + var paddingRight: CssSize? by refreshOnUpdate() /** * Bottom padding of the current component. */ - var paddingBottom: CssSize? = null - set(value) { - field = value - refresh() - } + var paddingBottom: CssSize? by refreshOnUpdate() /** * Left padding of the current component. */ - var paddingLeft: CssSize? = null - set(value) { - field = value - refresh() - } + var paddingLeft: CssSize? by refreshOnUpdate() /** * Text color for the current component. */ - var color: Color? = null - set(value) { - field = value - refresh() - } + var color: Color? by refreshOnUpdate() /** * Text color for the current component given in hex format (write only). * @@ -234,19 +149,11 @@ abstract class StyledComponent : Component { /** * Opacity of the current component. */ - var opacity: Double? = null - set(value) { - field = value - refresh() - } + var opacity: Double? by refreshOnUpdate() /** * Background of the current component. */ - var background: Background? = null - set(value) { - field = value - refresh() - } + var background: Background? by refreshOnUpdate() private var snStyleCache: List<StringPair>? = null @@ -351,4 +258,36 @@ abstract class StyledComponent : Component { } return snstyle } + + internal fun <T> refreshOnUpdate(refreshFunction: ((T) -> Unit) = { this.refresh() }) = + RefreshDelegateProvider<T>(null, refreshFunction) + + internal fun <T> refreshOnUpdate(initialValue: T, refreshFunction: ((T) -> Unit) = { this.refresh() }) = + RefreshDelegateProvider(initialValue, refreshFunction) + + internal inner class RefreshDelegateProvider<T>( + private val initialValue: T?, private val refreshFunction: (T) -> Unit + ) { + operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): RefreshDelegate<T> { + if (initialValue != null) propertyValues[prop.name] = initialValue + return RefreshDelegate(refreshFunction) + } + } + + internal inner class RefreshDelegate<T>(private val refreshFunction: ((T) -> Unit)) { + @Suppress("UNCHECKED_CAST") + operator fun getValue(thisRef: StyledComponent, property: KProperty<*>): T { + val value = propertyValues[property.name] + return if (value != null) { + value as T + } else { + null as T + } + } + + operator fun setValue(thisRef: StyledComponent, property: KProperty<*>, value: T) { + propertyValues[property.name] = value + refreshFunction(value) + } + } } diff --git a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt index f50b1679..a746ffa4 100644 --- a/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt +++ b/src/main/kotlin/pl/treksoft/kvision/core/Widget.kt @@ -65,32 +65,18 @@ open class Widget(classes: Set<String> = setOf()) : StyledComponent() { /** * A title attribute of generated HTML element. */ - var title: String? = null - set(value) { - field = value - refresh() - } + var title: String? by refreshOnUpdate() /** * An ID attribute of generated HTML element. */ - var id: String? = null - set(value) { - field = value - refresh() - } + var id: String? by refreshOnUpdate() /** * A role attribute of generated HTML element. */ - var role: String? = null - set(value) { - field = value - refresh() - } - internal var surroundingSpan: Boolean = false - set(value) { - field = value - refresh() - } + var role: String? by refreshOnUpdate() + + internal var surroundingSpan by refreshOnUpdate(false) + internal var eventTarget: Widget? = null private var vnode: VNode? = null diff --git a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt index 0f653d30..1370f25a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt +++ b/src/main/kotlin/pl/treksoft/kvision/dropdown/DropDown.kt @@ -70,11 +70,7 @@ open class DropDown( set(value) { button.text = value } - private var elements = elements - set(value) { - field = value - setChildrenFromElements() - } + private var elements by refreshOnUpdate(elements, { setChildrenFromElements() }) /** * The icon of the dropdown button. */ @@ -126,11 +122,7 @@ open class DropDown( /** * Determines if the dropdown is showing upwards. */ - var dropup = false - set(value) { - field = value - refresh() - } + var dropup by refreshOnUpdate(false) /** * Width of the dropdown button. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt index 7a8b5905..e76a1c7a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckBox.kt @@ -107,27 +107,15 @@ open class CheckBox( /** * The style (one of Bootstrap standard colors) of the input. */ - var style: CHECKBOXSTYLE? = null - set(value) { - field = value - refresh() - } + var style: CHECKBOXSTYLE? by refreshOnUpdate() /** * Determines if the checkbox is rendered as a circle. */ - var circled: Boolean = false - set(value) { - field = value - refresh() - } + var circled by refreshOnUpdate(false) /** * Determines if the checkbox is rendered inline. */ - var inline: Boolean = false - set(value) { - field = value - refresh() - } + var inline by refreshOnUpdate(false) /** * The size of the input. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt index 46e5395c..7c67c957 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/CheckInput.kt @@ -66,63 +66,34 @@ open class CheckInput( /** * The selection state of the input. */ - var value: Boolean = value - set(value) { - field = value - refreshState() - } + var value by refreshOnUpdate(value, { refreshState() }) /** * The value attribute of the generated HTML input element. * * This value is placed directly in generated HTML code, while the [value] property is dynamically * bound to the input selection state. */ - var startValue: Boolean = value - set(value) { - field = value - this.value = value - refresh() - } + var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) /** * The type of the generated HTML input element. */ - var type: CHECKINPUTTYPE = type - set(value) { - field = value - refresh() - } + var type by refreshOnUpdate(type) /** * The name attribute of the generated HTML input element. */ - var name: String? = null - set(value) { - field = value - refresh() - } + var name: String? by refreshOnUpdate() /** * Determines if the field is disabled. */ - var disabled: Boolean = false - set(value) { - field = value - refresh() - } + var disabled by refreshOnUpdate(false) /** * The additional String value used for the radio button group. */ - var extraValue: String? = null - set(value) { - field = value - refresh() - } + var extraValue: String? by refreshOnUpdate() /** * The size of the input. */ - var size: INPUTSIZE? = null - set(value) { - field = value - refresh() - } + var size: INPUTSIZE? by refreshOnUpdate() override fun render(): VNode { return render("input") diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt index ef5d6eb9..0cf85fc5 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/Radio.kt @@ -117,27 +117,15 @@ open class Radio( /** * The style (one of Bootstrap standard colors) of the input. */ - var style: RADIOSTYLE? = null - set(value) { - field = value - refresh() - } + var style: RADIOSTYLE? by refreshOnUpdate() /** * Determines if the radio button is rendered as a square. */ - var squared: Boolean = false - set(value) { - field = value - refresh() - } + var squared by refreshOnUpdate(false) /** * Determines if the radio button is rendered inline. */ - var inline: Boolean = false - set(value) { - field = value - refresh() - } + var inline by refreshOnUpdate(false) /** * The size of the input. */ diff --git a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt index aa29de34..e5b04a47 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/check/RadioGroup.kt @@ -54,29 +54,17 @@ open class RadioGroup( /** * A list of options (label to value pairs) for the group. */ - var options = options - set(value) { - field = value - setChildrenFromOptions() - } + var options by refreshOnUpdate(options, { setChildrenFromOptions() }) /** * A value of the selected option. */ - override var value = value - set(value) { - field = value - setValueToChildren(value) - } + override var value by refreshOnUpdate(value, { setValueToChildren(it) }) /** * Determines if the options are rendered inline. */ - var inline: Boolean = inline - set(value) { - field = value - refresh() - } + var inline by refreshOnUpdate(inline) override var disabled get() = getDisabledFromChildren() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt index f90401ce..b0b7f4b3 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectInput.kt @@ -65,124 +65,68 @@ open class SelectInput( /** * A list of options (label to value pairs) for the select control. */ - internal var options = options - set(value) { - field = value - setChildrenFromOptions() - } + internal var options by refreshOnUpdate(options, { setChildrenFromOptions() }) /** * A value of the selected option. */ - var value: String? = value - set(value) { - field = value - refreshState() - } + var value by refreshOnUpdate(value, { refreshState() }) /** * The name attribute of the generated HTML select element. */ - var name: String? = null - set(value) { - field = value - refresh() - } + var name: String? by refreshOnUpdate() /** * Determines if multiple value selection is allowed. */ - var multiple: Boolean = multiple - set(value) { - field = value - refresh() - } + var multiple by refreshOnUpdate(multiple) /** * Additional options for remote (AJAX) data source. */ - var ajaxOptions: AjaxOptions? = ajaxOptions - set(value) { - field = value - if (value != null) liveSearch = true - refresh() + var ajaxOptions by refreshOnUpdate(ajaxOptions, { + if (it != null) { + liveSearch = true } + refresh() + }) /** * Maximal number of selected options. */ - var maxOptions: Int? = null - set(value) { - field = value - refresh() - } + var maxOptions: Int? by refreshOnUpdate() /** * Determines if live search is available. */ - var liveSearch: Boolean = false - set(value) { - field = value - refresh() - } + var liveSearch by refreshOnUpdate(false) /** * The placeholder for the select control. */ - var placeholder: String? = null - set(value) { - field = value - refresh() - } + var placeholder: String? by refreshOnUpdate() /** * The style of the select control. */ - var style: BUTTONSTYLE? = null - set(value) { - field = value - refresh() - } + var style: BUTTONSTYLE? by refreshOnUpdate() /** * The width of the select control. */ - var selectWidth: CssSize? = null - set(value) { - field = value - refresh() - } + var selectWidth: CssSize? by refreshOnUpdate() /** * The width type of the select control. */ - var selectWidthType: SELECTWIDTHTYPE? = null - set(value) { - field = value - refresh() - } + var selectWidthType: SELECTWIDTHTYPE? by refreshOnUpdate() /** * Determines if an empty option is automatically generated. */ - var emptyOption: Boolean = false - set(value) { - field = value - setChildrenFromOptions() - } + var emptyOption by refreshOnUpdate(false, { setChildrenFromOptions() }) /** * Determines if the field is disabled. */ - var disabled: Boolean = false - set(value) { - field = value - refresh() - } + var disabled by refreshOnUpdate(false) /** * Determines if the select is automatically focused. */ - var autofocus: Boolean? = null - set(value) { - field = value - refresh() - } + var autofocus: Boolean? by refreshOnUpdate() /** * The size of the input. */ - var size: INPUTSIZE? = null - set(value) { - field = value - refresh() - } + var size: INPUTSIZE? by refreshOnUpdate() init { setChildrenFromOptions() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt index 6819961f..95d8f178 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOptGroup.kt @@ -45,35 +45,19 @@ open class SelectOptGroup( /** * A label for the group. */ - var label: String = label - set(value) { - field = value - refresh() - } + var label by refreshOnUpdate(label) /** * A list of options (label to value pairs) for the group. */ - var options = options - set(value) { - field = value - setChildrenFromOptions() - } + var options by refreshOnUpdate(options, { setChildrenFromOptions() }) /** * Maximal number of selected options in the group. */ - var maxOptions: Int? = maxOptions - set(value) { - field = value - refresh() - } + var maxOptions by refreshOnUpdate(maxOptions) /** * Determines if the group is disabled. */ - var disabled: Boolean = disabled - set(value) { - field = value - refresh() - } + var disabled by refreshOnUpdate(disabled) init { setChildrenFromOptions() diff --git a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt index e7a49120..141eff28 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/select/SelectOption.kt @@ -46,51 +46,27 @@ open class SelectOption( /** * The value of the option. */ - var value: String? = value - set(value) { - field = value - refresh() - } + var value by refreshOnUpdate(value) /** * The label of the option. */ - var label: String? = label - set(value) { - field = value - refresh() - } + var label by refreshOnUpdate(label) /** * The subtext after the label of the option. */ - var subtext: String? = subtext - set(value) { - field = value - refresh() - } + var subtext by refreshOnUpdate(subtext) /** * The icon before the label of the option. */ - var icon: String? = icon - set(value) { - field = value - refresh() - } + var icon by refreshOnUpdate(icon) /** * Determines if the option should be rendered as divider. */ - var divider: Boolean = divider - set(value) { - field = value - refresh() - } + var divider by refreshOnUpdate(divider) /** * Determines if the option should be disabled. */ - var disabled: Boolean = disabled - set(value) { - field = value - refresh() - } + var disabled by refreshOnUpdate(disabled) override fun render(): VNode { return if (!divider) { diff --git a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt index a3f72b75..a5565d2a 100644 --- a/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt +++ b/src/main/kotlin/pl/treksoft/kvision/form/spinner/SpinnerInput.kt @@ -97,119 +97,62 @@ open class SpinnerInput( /** * Spinner value. */ - var value: Number? = value - set(value) { - field = value - refreshState() - } + var value by refreshOnUpdate(value, { refreshState() }) /** * The value attribute of the generated HTML input element. * * This value is placed directly in generated HTML code, while the [value] property is dynamically * bound to the spinner input value. */ - var startValue: Number? = value - set(value) { - field = value - this.value = value - refresh() - } + var startValue by refreshOnUpdate(value, { this.value = it; refresh() }) /** * Minimal value. */ - var min: Int = min - set(value) { - field = value - refreshSpinner() - } + var min by refreshOnUpdate(min, { refreshSpinner() }) /** * Maximal value. */ - var max: Int = max - set(value) { - field = value - refreshSpinner() - } + var max by refreshOnUpdate(max, { refreshSpinner() }) /** * Step value. */ - var step: Double = step - set(value) { - field = value - refreshSpinner() - } + var step by refreshOnUpdate(step, { refreshSpinner() }) /** * Number of decimal digits value. */ - var decimals: Int = decimals - set(value) { - field = value - refreshSpinner() - } + var decimals by refr |
