Skip to content

Commit

Permalink
- [Docs] Fixed text in TabStrip docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
pusolito committed Jun 28, 2024
1 parent eb54b33 commit c77a31e
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 79 deletions.
4 changes: 2 additions & 2 deletions DocApps/src/jsMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import io.nacular.doodle.examples.contacts.showcase
import io.nacular.doodle.examples.contacts.showcaseModules
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicCircularProgressIndicatorBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicLabelBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinnerBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinButtonBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeHyperLinkBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeScrollPanelBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeTextFieldBehavior
Expand Down Expand Up @@ -106,7 +106,7 @@ fun photos(element: HTMLElement) {
DragDropModule,
basicLabelBehavior(),
nativeTextFieldBehavior(spellCheck = false),
basicMutableSpinnerBehavior(),
basicMutableSpinButtonBehavior(),
basicCircularProgressIndicatorBehavior(thickness = 18.0),
Module(name = "AppModule") {
bindSingleton<Animator> { AnimatorImpl (instance(), instance()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import io.nacular.doodle.application.Application
import io.nacular.doodle.controls.buttons.PushButton
import io.nacular.doodle.controls.itemVisualizer
import io.nacular.doodle.controls.range.CircularSlider
import io.nacular.doodle.controls.spinner.MutableIntSpinnerModel
import io.nacular.doodle.controls.spinner.MutableSpinner
import io.nacular.doodle.controls.spinner.Spinner
import io.nacular.doodle.controls.spinner.SpinnerModel
import io.nacular.doodle.controls.spinner.spinnerEditor
import io.nacular.doodle.controls.spinner.MutableIntSpinButtonModel
import io.nacular.doodle.controls.spinner.MutableSpinButton
import io.nacular.doodle.controls.spinner.SpinButton
import io.nacular.doodle.controls.spinner.SpinButtonModel
import io.nacular.doodle.controls.spinner.spinButtonEditor
import io.nacular.doodle.controls.text.Label
import io.nacular.doodle.controls.theme.simpleButtonRenderer
import io.nacular.doodle.core.Container
Expand Down Expand Up @@ -54,11 +54,12 @@ import io.nacular.doodle.layout.WidthSource.Parent
import io.nacular.doodle.layout.constraints.Bounds
import io.nacular.doodle.layout.constraints.ConstraintDslContext
import io.nacular.doodle.layout.constraints.constrain
import io.nacular.doodle.layout.constraints.fill
import io.nacular.doodle.system.Cursor.Companion.Text
import io.nacular.doodle.theme.ThemeManager
import io.nacular.doodle.theme.adhoc.DynamicTheme
import io.nacular.doodle.theme.basic.range.BasicCircularSliderBehavior
import io.nacular.doodle.theme.basic.spinner.SpinnerTextEditOperation
import io.nacular.doodle.theme.basic.spinner.SpinButtonTextEditOperation
import io.nacular.doodle.utils.Dimension.Width
import io.nacular.doodle.utils.PropertyObserver
import io.nacular.doodle.utils.PropertyObservers
Expand Down Expand Up @@ -86,17 +87,17 @@ import io.nacular.doodle.datatransport.Image as ImageType
* Panel used to display editable properties of an image.
*/
private class PropertyPanel(private val focusManager: FocusManager): Container() {
private val spinnerHeight = 24.0
private val spinButtonHeight = 24.0

/**
* Simple View with a [MutableSpinner] and [Label] that displays a numeric property.
* Simple View with a [MutableSpinButton] and [Label] that displays a numeric property.
*/
private inner class Property(
private val property : KMutableProperty0<Double>,
private val updateWhen: PropertyObservers<Any, Any>,
suffix : String = ""
): View() {
private fun <T, M: SpinnerModel<T>> spinnerVisualizer(suffix: String = "") = itemVisualizer { item: Int, previous: View?, context: Spinner<T, M> ->
private fun <T, M: SpinButtonModel<T>> spinButtonVisualizer(suffix: String = "") = itemVisualizer { item: Int, previous: View?, context: SpinButton<T, M> ->
when (previous) {
is Label -> previous.also { it.text = "$item$suffix" }
else -> Label("$item$suffix").apply {
Expand All @@ -106,27 +107,25 @@ private class PropertyPanel(private val focusManager: FocusManager): Container()
foregroundColor = previous?.foregroundColor
backgroundColor = previous?.backgroundColor ?: Transparent

(context as? MutableSpinner<*,*>)?.let { spinner ->
(context as? MutableSpinButton<*,*>)?.let { spinButton ->
pointerFilter += pressed { event ->
spinner.startEditing()
spinButton.startEditing()
event.consume()
}
}
}
}
}

private val spinner = MutableSpinner(
MutableIntSpinnerModel(Int.MIN_VALUE..Int.MAX_VALUE, property.get().toInt()),
spinnerVisualizer(suffix)
private val spinButton = MutableSpinButton(
MutableIntSpinButtonModel(Int.MIN_VALUE..Int.MAX_VALUE, property.get().toInt()),
spinButtonVisualizer(suffix)
).apply {
cellAlignment = {
it.edges eq parent.edges
}
cellAlignment = fill

// Make the spinner editable
editor = spinnerEditor { spinner, value, current ->
object: SpinnerTextEditOperation<Int>(focusManager, ToStringIntEncoder, spinner, value, current) {
// Make the spinButton editable
editor = spinButtonEditor { button, value, current ->
object: SpinButtonTextEditOperation<Int>(focusManager, ToStringIntEncoder, button, value, current) {
init {
textField.selectionBackgroundColor = Darkgray
}
Expand All @@ -142,22 +141,22 @@ private class PropertyPanel(private val focusManager: FocusManager): Container()
}
}

private val callBack: PropertyObserver<Any, Any> = { _,_,_ -> spinner.set(property.get().toInt()) }
private val callBack: PropertyObserver<Any, Any> = { _,_,_ -> spinButton.set(property.get().toInt()) }

init {
updateWhen += callBack

children += listOf(spinner, Label(property.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }))

// Label is centered below spinner, which is stretched to fit its parent's width
layout = constrain(children[0], children[1]) { spinner, label ->
spinner.top eq 0
spinner.left eq 0
spinner.right eq parent.right
spinner.height eq spinnerHeight
label.top eq spinner.bottom + 2
label.centerX eq parent.centerX
label.height eq label.height.readOnly
children += listOf(spinButton, Label(property.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }))

// Label is centered below spinButton, which is stretched to fit its parent's width
layout = constrain(children[0], children[1]) { spinButton, label ->
spinButton.top eq 0
spinButton.left eq 0
spinButton.right eq parent.right
spinButton.height eq spinButtonHeight
label.top eq spinButton.bottom + 2
label.centerX eq parent.centerX
label.height eq label.height.readOnly
}
}

Expand All @@ -177,7 +176,7 @@ private class PropertyPanel(private val focusManager: FocusManager): Container()
layout = constrain(children[0], property1, property2) { label, first, second ->
label.top eq second.top
label.left eq spacing
label.height eq spinnerHeight
label.height eq spinButtonHeight
first.centerY eq parent.centerY.writable
first.right eq second.left - spacing
first.width eq second.width
Expand Down Expand Up @@ -218,16 +217,16 @@ private class PropertyPanel(private val focusManager: FocusManager): Container()
}
}

val rotationSlider = CircularSlider(0.0 .. 359.0).apply {
val rotationSlider = CircularSlider(0 * degrees .. 360 * degrees).apply {
size = Size(50)
value = photoAngle.angle
value = photoAngle.angle * degrees
behavior = BasicCircularSliderBehavior(thickness = 18.0)
changed += { _,_,new -> photoAngle.angle = new }
changed += { _,_,new -> photoAngle.angle = new `in` degrees }
}

photo.transformChanged += { _,_,_ ->
photoAngle.angle = computeAngle(photo) `in` degrees
rotationSlider.value = photoAngle.angle
rotationSlider.value = photoAngle.angle * degrees
}

children += propertyGroup("Size", Property(photo::width, photoBoundsChanged), Property(photo::height, photoBoundsChanged ))
Expand Down
4 changes: 2 additions & 2 deletions PhotosRunner/src/jsMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.nacular.doodle.application.Modules.Companion.KeyboardModule
import io.nacular.doodle.application.application
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicCircularProgressIndicatorBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicLabelBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinnerBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinButtonBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeTextFieldBehavior
import org.kodein.di.DI.Module
import org.kodein.di.bindSingleton
Expand All @@ -27,7 +27,7 @@ fun main() {
DragDropModule,
basicLabelBehavior(),
nativeTextFieldBehavior(spellCheck = false),
basicMutableSpinnerBehavior(),
basicMutableSpinButtonBehavior(),
basicCircularProgressIndicatorBehavior(thickness = 18.0),
Module(name = "AppModule") {
bindSingleton<Animator> { AnimatorImpl(instance(), instance()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.nacular.doodle.application.Modules.Companion.KeyboardModule
import io.nacular.doodle.application.application
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicCircularProgressIndicatorBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicLabelBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinnerBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinButtonBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeTextFieldBehavior
import org.kodein.di.DI.Module
import org.kodein.di.bindSingleton
Expand All @@ -23,7 +23,7 @@ fun main() {
DragDropModule,
basicLabelBehavior(),
nativeTextFieldBehavior(),
basicMutableSpinnerBehavior(),
basicMutableSpinButtonBehavior(),
basicCircularProgressIndicatorBehavior(thickness = 18.0),
Module(name = "AppModule") {
bindSingleton<Animator> { AnimatorImpl(instance(), instance()) }
Expand Down
4 changes: 2 additions & 2 deletions PhotosRunner/src/wasmJsMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.nacular.doodle.application.Modules.Companion.KeyboardModule
import io.nacular.doodle.application.application
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicCircularProgressIndicatorBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicLabelBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinnerBehavior
import io.nacular.doodle.theme.basic.BasicTheme.Companion.basicMutableSpinButtonBehavior
import io.nacular.doodle.theme.native.NativeTheme.Companion.nativeTextFieldBehavior
import org.kodein.di.DI.Module
import org.kodein.di.bindSingleton
Expand All @@ -26,7 +26,7 @@ fun main() {
DragDropModule,
basicLabelBehavior(),
nativeTextFieldBehavior(spellCheck = false),
basicMutableSpinnerBehavior(),
basicMutableSpinButtonBehavior(),
basicCircularProgressIndicatorBehavior(thickness = 18.0),
Module(name = "AppModule") {
bindSingleton<Animator> { AnimatorImpl(instance(), instance()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,14 @@ class TabStrip(private val animate: Animator, private val pathMetrics: PathMetri

// Overall animation handle
private var animation: Animation<*>? by autoCanceling()
private val secondaryAnimations = mutableSetOf<Animation<*>>()

// Monitor changes to the selected item to handle animation
private var selectedItem by observable(items.first()) { _,selected ->
// cancel any ongoing secondary animations and hide droplet
// hide droplet
dropletYAboveIndicator = 0.0
secondaryAnimations.forEach { it.cancel() }
secondaryAnimations.clear()

// Animation blocks roll all top-level animations (those created while in the block) into a common
// parent animation. Canceling that animation cancels all the children.
// However, our code creates additional animations that are created when top-level ones are completed.
// These animations are NOT tracked as part of the returned animation group. So they need to be tracked
// separately, so we can cancel them if anything changes mid-flight.
// We do that using the secondaryAnimations set.
animation = animate {
// All deselected items move back to normal
items.filter { it != selected && !it.atDefaults }.forEach { deselected ->
Expand All @@ -134,34 +127,31 @@ class TabStrip(private val animate: Animator, private val pathMetrics: PathMetri
}

// Indicator moves to selected item
(indicatorCenter.x to selected.centerX using (tweenDouble(easeInOutCubic, slideDuration)) { indicatorCenter = Point(it, height) }).onCompleted {
indicatorCenter.x to selected.centerX using (tweenDouble(easeInOutCubic, slideDuration)) { indicatorCenter = Point(it, height) } then {
// Selected item moves down
(selected.moveProgress to 1f using (tweenFloat(linear, itemMoveDownDuration)) { selected.moveProgress = it }).also { secondaryAnimations += it }
selected.moveProgress to 1f using (tweenFloat(linear, itemMoveDownDuration)) { selected.moveProgress = it }
}

// Indicator primes as it travels to selected item
(indicatorHeight to minIndicatorHeight using (tweenDouble(linear, primeDuration)) { indicatorHeight = it }).onCompleted {
// NOTE: All these are secondary animations that won't be attached to the outer animation, since it would have been
// completed at this point. So they need to be tracked using our secondaryAnimation set.

indicatorHeight to minIndicatorHeight using (tweenDouble(linear, primeDuration)) { indicatorHeight = it } then {
// Indicator fires at selected item
(indicatorHeight to maxIndicatorHeight using (tweenDouble(linear, fireDuration)) { indicatorHeight = it }).onCompleted {
indicatorHeight to maxIndicatorHeight using (tweenDouble(linear, fireDuration)) { indicatorHeight = it } then {
// Indicator height returns to normal
(indicatorHeight to defaultIndicatorHeight using (tweenDouble(linear, recoilDuration)) { indicatorHeight = it }).also { secondaryAnimations += it }
indicatorHeight to defaultIndicatorHeight using (tweenDouble(linear, recoilDuration)) { indicatorHeight = it }

// Droplet moves up to item
(dropletYAboveIndicator to dropletMaxY using (tweenDouble(linear, dropletTravelDuration)) { dropletYAboveIndicator = it }).onCompleted {
dropletYAboveIndicator to dropletMaxY using (tweenDouble(linear, dropletTravelDuration)) { dropletYAboveIndicator = it } then {
// Droplet is instantly hidden
dropletYAboveIndicator = 0.0

// Selected item moves up
(selected.moveProgress to 0f using (tweenFloat(linear, itemMoveUpDuration)) { selected.moveProgress = it }).also { secondaryAnimations += it }
selected.moveProgress to 0f using (tweenFloat(linear, itemMoveUpDuration)) { selected.moveProgress = it }

// Selected item animates droplet within it
(selected.selectionProgress to 1f using (tweenFloat(linear, itemFillDuration)) { selected.selectionProgress = it }).also { secondaryAnimations += it }
}.also { secondaryAnimations += it }
}.also { secondaryAnimations += it }
}.also { secondaryAnimations += it }
selected.selectionProgress to 1f using (tweenFloat(linear, itemFillDuration)) { selected.selectionProgress = it }
}
}
}
}
}

Expand Down Expand Up @@ -275,20 +265,17 @@ class TabStrip(private val animate: Animator, private val pathMetrics: PathMetri
private fun updateIndicatorPath() {
val indicatorHalfWidth = indicatorWidth / 2

val controlPoint1 = Point(x = -indicatorHalfWidth) + Point(19.6113, -5.2466)
val controlPoint2 = Point(x = -11.4078, -indicatorHeight)
val controlPoint3 = controlPoint2 + Point(x = 2 * 11.4078)
val controlPoint4 = Point(x = indicatorHalfWidth) + Point(-19.6113, -5.2466)
val offset = Point(19.6113, -5.2466)
val controlPoint2X = -11.4078

val controlPoint1 = Point(x = -indicatorHalfWidth) + offset
val controlPoint2 = Point(x = controlPoint2X, -indicatorHeight)
val controlPoint3 = controlPoint2 + Point(x = 2 * -controlPoint2X)
val controlPoint4 = Point(x = indicatorHalfWidth) + offset.run { Point(x = -x, y) }

indicatorPath = path(Point(x = -indicatorHalfWidth)).
cubicTo(Point(y = -indicatorHeight ), controlPoint1, controlPoint2).
cubicTo(Point(x = indicatorHalfWidth), controlPoint3, controlPoint4).
close()
}

private fun <T> Animation<T>.onCompleted(block: () -> Unit): Animation<T> = this.apply {
completed += {
block()
}
}
}
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[versions]
kotlinVersion = { strictly = "1.9.22" }
kotlinVersion = { strictly = "1.9.23" }
coroutinesVersion = { strictly = "1.8.0" }
kodeinVersion = { strictly = "7.21.1" }
doodleVersion = { strictly = "0.10.1" }
doodleVersion = { strictly = "0.10.2" }
slf4jVersion = { strictly = "2.0.9" }
mockkVersion = { strictly = "1.13.7" }
jupiterVersion = { strictly = "5.10.0" }
Expand Down

0 comments on commit c77a31e

Please sign in to comment.