-
Notifications
You must be signed in to change notification settings - Fork 1
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
Kj recordable view refactor #471
Conversation
.../kotlin/org/wycliffeassociates/otter/jvm/app/ui/resourcetakes/view/RecordResourceFragment.kt
Outdated
Show resolved
Hide resolved
...kotlin/org/wycliffeassociates/otter/jvm/app/ui/resourcetakes/view/ResourceTabPaneFragment.kt
Outdated
Show resolved
Hide resolved
.../org/wycliffeassociates/otter/jvm/app/ui/resourcetakes/viewmodel/ResourceTabPaneViewModel.kt
Show resolved
Hide resolved
|
||
fun dragTargetBottom(runOnNode: (VBox.() -> Unit)? = null): Node = dragTargetBottom.apply { | ||
runOnNode?.let { it() } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the receiver function + apply + let, this little guy is a doozy for me. Please rework for clarity. Something like this works better at least for me:
fun dragTargetBottom(runOnNode: (VBox.() -> Unit)? = null): Node = dragTargetBottom.also {
runOnNode?.let { dragTargetBottom.runOnNode() }
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single expression formats are meant to be a single expression; this is too complex for that style of formatting.
If it was fine, this would apply:
https://kotlinlang.org/docs/reference/coding-conventions.html#expression-body-formatting
I believe it was referenced in clean code though I couldn't find it, but basically, how you organize your space is important for readability. We read code top to bottom, and each line left to right. We don't read each line though, often just scan top down across the left hand side, so it's helpful when the leftmost space gives us a lot of context to what the rest of the line does, and we follow further down the line (left to right) if we need more details.
This makes a critical assumption though; that every major step begins on a new line. So in this code example, scanning top down, I see dragTargetBottom.... then the first instruction is "runonNode?..." which is actually incorrect. The fact that the closing } appears to close the function block further adds to misleading a reader just glancing at the code.
Your existing code should look like:
fun dragTargetBottom(runOnNode: (VBox.() -> Unit)? = null): Node {
return dragTargetBottom.apply {
runOnNode?.let { it() }
}
}
I know this is done elsewhere in the code; I would appreciate if we could all be on the lookout and clean them up as we come across it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, @aunger is right, this is really difficult to read; especially with overloading the variable name with the method name.
it might help to prefix the private variable with _; so _dragTargetBottom?
But the name is also really unhelpful to begin with; what is it exactly? is it the takeGridDragTarget?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to rework all of this, probably into a separate component, something like DragTakeTarget
...ain/kotlin/org/wycliffeassociates/otter/jvm/app/ui/takemanagement/view/RecordableFragment.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/org/wycliffeassociates/otter/jvm/app/widgets/takecard/events/StartDragEvent.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/org/wycliffeassociates/otter/jvm/testapp/app/RecordableVMTestApp.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/org/wycliffeassociates/otter/jvm/testapp/app/RecordableVMTestApp.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/org/wycliffeassociates/otter/jvm/testapp/app/RecordableVMTestView.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/org/wycliffeassociates/otter/jvm/utils/TornadoFXUtils.kt
Show resolved
Hide resolved
5f662a1
to
e9215f5
Compare
427f445
to
4430ed7
Compare
//selected take and drag target | ||
stackpane { | ||
// drag target glow | ||
add(dragComponents |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code formatting on all of these add methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these add methods are going away since I'm making a new drag target component anyway
recordableTab(ContentType.BODY, 1) | ||
) | ||
|
||
private fun recordableTab(contentType: ContentType, sort: Int) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use single expression function syntax here
|
||
class TakesListView( | ||
items: ObservableList<Take>, | ||
audioPlayer: () -> IAudioPlayer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this a lambda?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know- that was silly :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember now- TakesListView constructs TakeCards for each take in the list, and it needed to instantiate an AudioPlayer for each of the TakeCards. In reality this parameter should have been named something like createAudioPlayer
. I just pushed a commit that removes this parameter as well as lastPlayOrPauseEvent
, in favor of accepting a lambda function called createTakeNode
that just constructs the card given a take object.
Here's another thought- I don't know if this entire class is really needed or not. I could just create a vanilla ListView instead in RecordResourceFragment
, but perhaps this class still encapsulates the takes list view and reduces the code length of RecordResourceFragment
... especially with the lengthy comment explaining the lack of caching the cells.
// Take at the top to compare to an existing selected take | ||
private var draggingTakeProperty = SimpleObjectProperty<OldTakeCard>() | ||
class RecordScriptureFragment | ||
: RecordableFragment(RecordableViewModelProvider().get()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you use di scopes here instead of the get?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into this, but I haven't figured out how to make it work for this situation
isFillWidth = false | ||
val placeholder = vbox { | ||
addClass(RecordScriptureStyles.placeholder) | ||
add(dragComponents |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
formatting on all the adds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These adds are gone now that I created a DragTarget control
dragContainer.relocate(newX, newY) | ||
} | ||
|
||
private fun startDrag(evt: StartDragEvent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we often aren't going out of our way to abbreviate things; event is already short, can we just use the full word?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure! done
false, | ||
EventHandler { | ||
audioPluginViewModel.addPlugin(true, false) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these have been fixed. I also just reread parts of the Kotlin Coding Conventions to help with this, since I was unclear about proper formatting for chained call wrapping.
bottomAnchor = 0.0 | ||
topAnchor = 0.0 | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
add(JFXButton("", MaterialIconView(MaterialIcon.DELETE, "18px"))) | ||
add(deleteButton.apply { | ||
graphic = MaterialIconView(MaterialIcon.DELETE, "18px") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
add(deleteButton.apply { | ||
text = messages["delete"] | ||
graphic = MaterialIconView(MaterialIcon.DELETE, "18px") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
) | ||
addClass(TakeCardStyles.defaultButton) | ||
addClass(TakeCardStyles.editButton) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
fireEvent( | ||
EditTakeEvent(take) { | ||
player.load(take.file) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
})
e9215f5
to
2351c2b
Compare
b331ff4
to
1207275
Compare
b7c9d78
to
007b69e
Compare
Updating alternateTakes in a selectedTakeProperty listener was too complicated since we want to leave alternateTakes alone when the user switches the recordable.
Replaced OldTakeCard with TakeCard controls. Refactored event handling for play/pause, edit, and delete. Edit still doesn't work due to problem with starting the audio player from the app. Dragging takes is also broken temporarily. Fix and view layer refactor to come.
DragTakeFragment now has everything needed to handle dragging takes. Components can be added into the derived class from the base class (DragTakeFragment.) RecordScriptureFragment uses it, but RecordResourceFragment needs to be refactored to use it.
1207275
to
ecd4d4a
Compare
/** Add custom components to this container, rather than root*/ | ||
protected val mainContainer = VBox() | ||
|
||
protected val lastPlayOrPauseEvent: SimpleObjectProperty<PlayOrPauseEvent?> = SimpleObjectProperty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why nullable?
|
||
class TakesFlowPane( | ||
alternateTakes: ObservableList<Take>, | ||
private val audioPlayer: () -> IAudioPlayer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
audioPlayerProvider or audioPlayerBuilder... idk a lambda isn't an audio player and the name should reflect what this is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, although I'm working on this file now and I plan to replace that parameter and the next one with a lambda function called createTakeCard
that just knows how to create the take card and doesn't care about the take card's details
return vbox(10.0) { | ||
alignment = Pos.CENTER | ||
addClass(TakeCardStyles.scriptureTakeCardPlaceholder, TakeCardStyles.scriptureTakeCardDropShadow) | ||
label("blank") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this need text?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't, this was just for testing. I'm going to remove that too as I'm working on TakesFlowPane now to implement the dynamic blank cards
.observeOnFx() | ||
.subscribe { result -> | ||
showPluginActive = false | ||
when (result) { | ||
EditTake.Result.NO_EDITOR -> snackBarObservable.onNext(messages["noEditor"]) | ||
EditTake.Result.SUCCESS -> {} | ||
EditTake.Result.SUCCESS -> editTakeEvent.onComplete() | ||
null -> {} // This cannot happen but the compiler complains if null branch does not exist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, if you specify the type of result explicitly as non-null, this branch is unneeded. I have no problem with this as-is though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Great point.
} | ||
} | ||
|
||
private fun createTakeCard( | ||
take: Take, | ||
player: IAudioPlayer, | ||
takeEventObservable: Observable<TakeEvent?>, | ||
playOrPauseEventObservable: Observable<PlayOrPauseEvent?>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we had an issue putting nullables in observables?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yeah, this is easy to forget (and you don't find out until a null is observed at runtime).
5fc53c0
to
4700f6e
Compare
Recording, deleting, and selecting takes works on both the scripture and resource side.
Requires common #115.
To test, checkout branch kj-all-recordings-testapp. First, you will need to run Main.kt to import ulb and tn, if you haven't already. Then create a project and add an audio plugin.
Then, run
main
in RecordScriptureTestApp.kt to work with takes on the scripture sidemain
in RecordResourceTestApp.kt to work with takes on the resource sideIf you use these entry points, the app will load the first project, first chapter in that project, and first verse in that chapter. (Navigating strictly down through the app rather than using these separate entry points should work, but I created these entry points to make it faster to test the takes pages alone.)
Try performing record, delete and select (by dragging). Editing takes does not work yet due to an unknown issue with ocenaudio being unable to save edited files.
The most important part of this PR is the creation of a base class called RecordableFragment that handles all of the commonalities between the derived classes RecordScriptureFragment and RecordResourceFragment, namely dragging. Other view and viewmodels have also been renamed for clarity.
Also, I elected to keep the FlowPane as a FlowPane in RecordScriptureFragment. In lieu of finding any helpful examples of lists (particularly grids) with different data/node types, (most people were just talking about using different data types in a TableView, which doesn't solve our problem) I did some investigation:
ObservableList
of some kind of model. Besides point 1, this observable list would be different than but dependent on thealternateTakes
list used in RecordableViewModel, which is already anObservableList
. Responding to a change in an observable list that is responds to changes in another observable list is slow (I tried it.)