Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.gradle/
.idea/
.vscode/
build/
lib/
out/
bin/
gradlew
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'rhmodding'
version '1.4.2'
version '1.4.3'

buildscript {
repositories {
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/rhmodding/bread/Bread.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Bread : Application() {
}
const val GITHUB: String = "https://github.com/rhmodding/bread"
const val LICENSE_NAME: String = "Apache License 2.0"
val VERSION: Version = Version(1, 4, 2)
val VERSION: Version = Version(1, 4, 3)
val rootFolder: File = File(System.getProperty("user.home")).resolve(".rhmodding/bread/").apply { mkdirs() }
val windowIcons: List<Image> by lazy { listOf(BreadIcon.icon32, BreadIcon.icon64) }

Expand Down
103 changes: 103 additions & 0 deletions src/main/kotlin/rhmodding/bread/editor/AnimationsTab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import rhmodding.bread.util.em
import rhmodding.bread.util.intSpinnerFactory
import rhmodding.bread.util.spinnerArrowKeysAndScroll
import java.io.File
import java.util.Collections
import javax.imageio.ImageIO
import kotlin.math.roundToInt

Expand All @@ -42,6 +43,7 @@ open class AnimationsTab<F : IDataModel>(editor: Editor<F>) : EditorSubTab<F>(ed
isFillWidth = true
}
val disableStepControls: BooleanProperty = SimpleBooleanProperty(false)
val disablePasteControls: BooleanProperty = SimpleBooleanProperty(true)
val stepPropertiesVBox: VBox = VBox().apply {
disableProperty().bind(disableStepControls)
}
Expand All @@ -68,6 +70,8 @@ open class AnimationsTab<F : IDataModel>(editor: Editor<F>) : EditorSubTab<F>(ed
val currentAnimationStep: IAnimationStep
get() = currentAnimation.steps[aniStepSpinner.value]

var copyStep: IAnimationStep = editor.createAnimationStep()

var currentTimeline: ObjectProperty<Timeline?> = SimpleObjectProperty(null as Timeline?).apply {
addListener { _, old, new ->
old?.stop()
Expand Down Expand Up @@ -142,6 +146,7 @@ open class AnimationsTab<F : IDataModel>(editor: Editor<F>) : EditorSubTab<F>(ed
editor.repaintCanvas()
}


children += Button("Add New Step").apply {
setOnAction {
editor.addAnimationStep(currentAnimation, editor.createAnimationStep())
Expand Down Expand Up @@ -174,6 +179,75 @@ open class AnimationsTab<F : IDataModel>(editor: Editor<F>) : EditorSubTab<F>(ed
}
}
}
children += HBox().apply {
styleClass += "hbox"
alignment = Pos.CENTER_LEFT
children += Button("Move Up").apply {
disableProperty().bind(disableStepControls)
setOnAction {
if (aniStepSpinner.value < currentAnimation.steps.size - 1) {
Collections.swap(currentAnimation.steps, aniStepSpinner.value, aniStepSpinner.value + 1)
aniStepSpinner.increment(1)
}
}
}
children += Button("Move Down").apply {
disableProperty().bind(disableStepControls)
setOnAction {
if (aniStepSpinner.value > 0) {
Collections.swap(currentAnimation.steps, aniStepSpinner.value, aniStepSpinner.value - 1)
aniStepSpinner.decrement(1)
}
}
}
}
children += HBox().apply {
fun updateStepSpinners(goToMax: Boolean) {
(aniStepSpinner.valueFactory as SpinnerValueFactory.IntegerSpinnerValueFactory).also {
it.max = (currentAnimation.steps.size - 1).coerceAtLeast(0)
it.value = if (goToMax) it.max else it.value.coerceAtMost(it.max)
}
updateFieldsForStep()
editor.repaintCanvas()
}

styleClass += "hbox"
alignment = Pos.CENTER_LEFT
children += Button("Copy").apply {
disableProperty().bind(disableStepControls)
setOnAction {
disablePasteControls.value = false
copyStep = currentAnimationStep
}
}
children += Button("Cut").apply {
disableProperty().bind(disableStepControls)
setOnAction {
disablePasteControls.value = false
copyStep = currentAnimationStep
if (currentAnimation.steps.isNotEmpty()) {
val alert = Alert(Alert.AlertType.CONFIRMATION)
editor.app.addBaseStyleToDialog(alert.dialogPane)
alert.title = "Cut this animation step?"
alert.headerText = "Cut this animation step?"
alert.contentText = "Are you sure you want to cut this animation step?\nYou won't be able to undo this action."
if (alert.showAndWait().get() == ButtonType.OK) {
editor.removeAnimationStep(currentAnimation, currentAnimationStep)
updateStepSpinners(false)
}
}
}
}
children += Button("Paste").apply {
disableProperty().bind(disablePasteControls)
setOnAction {
if (currentAnimation.steps.isNotEmpty()) {
editor.addAnimationStep(currentAnimation, copyStep.copy())
updateStepSpinners(true)
}
}
}
}
children += HBox().apply {
styleClass += "hbox"
alignment = Pos.CENTER_LEFT
Expand Down Expand Up @@ -486,6 +560,35 @@ open class AnimationsTab<F : IDataModel>(editor: Editor<F>) : EditorSubTab<F>(ed
}
}

open fun updateFieldsForAnim() {
numAnimationsLabel.text = "(${data.animations.size} total animation${if (data.animations.size == 1) "" else "s"})"
numAniStepsLabel.text = "(${currentAnimation.steps.size} total step${if (currentAnimation.steps.size == 1) "" else "s"})"
if (currentAnimation.steps.isEmpty()) {
disableStepControls.value = true
return
}
val step = currentAnimationStep
disableStepControls.value = false

stepSpriteSpinner.valueFactoryProperty().get().value = step.spriteIndex.toInt()
stepDelaySpinner.valueFactoryProperty().get().value = step.delay.toInt()
stepStretchXSpinner.valueFactoryProperty().get().value = step.stretchX.toDouble()
stepStretchYSpinner.valueFactoryProperty().get().value = step.stretchY.toDouble()
stepRotationSpinner.valueFactoryProperty().get().value = step.rotation.toDouble()
stepOpacitySpinner.valueFactoryProperty().get().value = step.opacity.toInt()
playbackSlider.apply {
this.min = 0.0
this.max = (currentAnimation.steps.size - 1).toDouble()
this.blockIncrement = 1.0
this.majorTickUnit = if (max <= 5.0) 1.0 else if (max < 8.0) 2.0 else 4.0
this.minorTickCount = (majorTickUnit.toInt() - 1).coerceAtMost(max.toInt() - 1)
this.isShowTickMarks = true
this.isShowTickLabels = true
this.isSnapToTicks = true
this.value = aniStepSpinner.value.toDouble()
}
}

protected open fun getAnimationNameForGifExport(): String {
val spinnerValue: Int = animationSpinner.value
return "animation_${spinnerValue.toString().padStart(data.animations.size.toString().length, '0')}"
Expand Down
58 changes: 58 additions & 0 deletions src/main/kotlin/rhmodding/bread/editor/BCCADEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,64 @@ class BCCADEditor(app: Bread, mainPane: MainPane, dataFile: File, data: BCCAD, i
}
})
sectionAnimation.children.add(2, HBox().apply {
styleClass += "hbox"
alignment = Pos.CENTER_LEFT
fun updateAnimSpinners(goToMax: Boolean) {
(animationSpinner.valueFactory as SpinnerValueFactory.IntegerSpinnerValueFactory).also {
it.max = (data.animations.size - 1).coerceAtLeast(0)
it.value = if (goToMax) it.max else it.value.coerceAtMost(it.max)
}
// updateFieldsForAnim()
editor.repaintCanvas()
}


children += Button("Add animation").apply {
setOnAction {
TextInputDialog().apply {
this.title = "Adding animation"
this.headerText = "Add animation named:\n"
editor.app.addBaseStyleToDialog(this.dialogPane)
}.showAndWait().ifPresent { newName ->
if (newName.isNotBlank()) {
val n = newName.take(127)
val newAnimation: Animation = Animation()
newAnimation.name = n
animationNameLabel.text = n
data.animations.add(newAnimation)
updateAnimSpinners(true)
editor.updateContextMenu()
}
}
}
}
children += Button("Duplicate").apply {
setOnAction {
editor.addAnimation(currentAnimation.copy())
val animation = data.animations.last()
val curAnim = currentAnimation as Animation
animation.name = curAnim.name + "_dup"
animationNameLabel.text = curAnim.name + "_dup"
updateAnimSpinners(true)
editor.updateContextMenu()
}
}
children += Button("Remove").apply {
setOnAction {
val alert = Alert(Alert.AlertType.CONFIRMATION)
editor.app.addBaseStyleToDialog(alert.dialogPane)
alert.title = "Remove this animation?"
alert.headerText = "Remove this animation?"
alert.contentText = "Are you sure you want to remove this animation?\nYou won't be able to undo this action."
if (alert.showAndWait().get() == ButtonType.OK) {
editor.removeAnimation(currentAnimation)
updateAnimSpinners(true)
editor.updateContextMenu()
}
}
}
})
sectionAnimation.children.add(3, HBox().apply {
styleClass += "hbox"
alignment = Pos.CENTER_LEFT
children += Label("Is interpolated?:").apply {
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/rhmodding/bread/editor/Editor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ abstract class Editor<F : IDataModel>(val app: Bread, val mainPane: MainPane, va
}
} else {
if (evt.deltaX > 0 || evt.deltaY > 0) {
zoomFactor *= 2.0.pow(1 / 8.0)
zoomFactor *= 1.190507733
} else {
zoomFactor /= 2.0.pow(1 / 8.0)
}
Expand Down Expand Up @@ -271,7 +271,7 @@ abstract class Editor<F : IDataModel>(val app: Bread, val mainPane: MainPane, va
val blockColorEven = canvasColors[if (!darkGrid) 0 else 2]
val blockColorOdd = canvasColors[if (!darkGrid) 1 else 3]

// val blockStartX = ((canvas.width / 2 - canvas.width / zoomFactor / 2.0) / blockSize).toInt() - 2
// val blockStartX = ((canvas.width / 2 - canvas.width / zoomFactor / 2.0) / blockSize).toInt() - 2

val blocksInViewableAreaX = (canvas.width / blockSize / zoomFactor).toInt()
val blocksInViewableAreaY = (canvas.height / blockSize / zoomFactor).toInt()
Expand All @@ -295,7 +295,7 @@ abstract class Editor<F : IDataModel>(val app: Bread, val mainPane: MainPane, va
// Origin lines
if (originLines) {
g.save()
// g.transform(getCanvasCameraTransformation())
// g.transform(getCanvasCameraTransformation())
g.transform(Affine(Translate(panX, panY)))
val originLineWidth = 1.0
val xAxis = if (darkGrid && showGrid) Color(0.5, 0.5, 1.0, 0.75) else Color(0.0, 0.0, 1.0, 0.75)
Expand Down
Loading