From 662051357e1cf7bd836945862340ebe4bbe55613 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Mon, 12 Aug 2019 00:43:19 +0200 Subject: [PATCH 01/11] feat(base-tab): create stackpane tab type --- src/main/xerus/monstercat/tabs/BaseTab.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/xerus/monstercat/tabs/BaseTab.kt b/src/main/xerus/monstercat/tabs/BaseTab.kt index 3438ee1..b23e122 100644 --- a/src/main/xerus/monstercat/tabs/BaseTab.kt +++ b/src/main/xerus/monstercat/tabs/BaseTab.kt @@ -2,6 +2,7 @@ package xerus.monstercat.tabs import javafx.scene.control.Control import javafx.scene.layout.Pane +import javafx.scene.layout.StackPane import javafx.scene.layout.VBox import mu.KotlinLogging import org.controlsfx.validation.decoration.GraphicValidationDecoration @@ -26,3 +27,11 @@ abstract class VTab : VBox(), BaseTab { styleClass.add("vtab") } } + +abstract class StackTab : StackPane(), BaseTab { + protected val logger = KotlinLogging.logger(javaClass.name) + + init { + styleClass.add("vtab") + } +} \ No newline at end of file From 25ae9682294cc9ae1ba93276ba21598607f6f25b Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Mon, 12 Aug 2019 00:43:55 +0200 Subject: [PATCH 02/11] feat(release): add gold early access json key and bump cache version --- src/main/xerus/monstercat/api/Cache.kt | 2 +- src/main/xerus/monstercat/api/response/Release.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/xerus/monstercat/api/Cache.kt b/src/main/xerus/monstercat/api/Cache.kt index 8892d23..9adb481 100644 --- a/src/main/xerus/monstercat/api/Cache.kt +++ b/src/main/xerus/monstercat/api/Cache.kt @@ -18,7 +18,7 @@ import xerus.monstercat.downloader.CONNECTSID import xerus.monstercat.globalDispatcher import java.io.File -private const val cacheVersion = 5 +private const val cacheVersion = 6 object Cache: Refresher() { private val logger = KotlinLogging.logger { } diff --git a/src/main/xerus/monstercat/api/response/Release.kt b/src/main/xerus/monstercat/api/response/Release.kt index 08658bb..ed0ba81 100644 --- a/src/main/xerus/monstercat/api/response/Release.kt +++ b/src/main/xerus/monstercat/api/response/Release.kt @@ -14,6 +14,7 @@ data class Release( @Key var renderedArtists: String = "", @Key override var title: String = "", @Key var coverUrl: String = "", + @Key("inEarlyAccess") var earlyAccess: Boolean = false, @Key var downloadable: Boolean = false): MusicItem() { @Key var isCollection: Boolean = false From 2fe0a576b0afa64e663342e0e28df39909d4c458 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Mon, 12 Aug 2019 00:44:46 +0200 Subject: [PATCH 03/11] feat(covers): create cover fetching function that only retrieves cached --- src/main/xerus/monstercat/api/Covers.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/xerus/monstercat/api/Covers.kt b/src/main/xerus/monstercat/api/Covers.kt index e1017f8..307dac6 100644 --- a/src/main/xerus/monstercat/api/Covers.kt +++ b/src/main/xerus/monstercat/api/Covers.kt @@ -55,6 +55,16 @@ object Covers { return coverFile.inputStream() } + fun getCachedCover(coverUrl: String, cachedSize: Int, imageSize: Int): Image? { + val coverFile = coverCacheFile(coverUrl, cachedSize) + return try { + val imageStream = coverFile.inputStream() + createImage(imageStream, imageSize) + } catch(e: Exception) { + null + } + } + /** Fetches the given [coverUrl] with an [APIConnection] in the requested [size]. * @param coverUrl the base url to fetch the cover * @param size the size of the cover to be fetched from the api, with all powers of 2 being available. From d7a1d381897ea7ec94f090154df71d122ef6e90e Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Mon, 12 Aug 2019 01:03:41 +0200 Subject: [PATCH 04/11] feat(releases-tab): create release browser tab with --- src/main/xerus/monstercat/MonsterUtilities.kt | 7 +- src/main/xerus/monstercat/tabs/TabReleases.kt | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/main/xerus/monstercat/tabs/TabReleases.kt diff --git a/src/main/xerus/monstercat/MonsterUtilities.kt b/src/main/xerus/monstercat/MonsterUtilities.kt index 7694406..26f9ef2 100644 --- a/src/main/xerus/monstercat/MonsterUtilities.kt +++ b/src/main/xerus/monstercat/MonsterUtilities.kt @@ -33,11 +33,7 @@ import xerus.ktutil.to import xerus.monstercat.api.DiscordRPC import xerus.monstercat.api.Player import xerus.monstercat.downloader.TabDownloader -import xerus.monstercat.tabs.BaseTab -import xerus.monstercat.tabs.TabCatalog -import xerus.monstercat.tabs.TabGenres -import xerus.monstercat.tabs.TabSettings -import xerus.monstercat.tabs.TabSound +import xerus.monstercat.tabs.* import java.io.File import java.net.URL import java.net.UnknownHostException @@ -101,6 +97,7 @@ class MonsterUtilities(checkForUpdate: Boolean): JFXMessageDisplay { addTab(TabCatalog::class) addTab(TabGenres::class) addTab(TabDownloader::class) + addTab(TabReleases::class) addTab(TabSound::class) addTab(TabSettings::class) if(currentVersion != Settings.LASTVERSION.get()) { diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt new file mode 100644 index 0000000..96c84df --- /dev/null +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -0,0 +1,104 @@ +package xerus.monstercat.tabs + +import javafx.collections.FXCollections +import javafx.geometry.Insets +import javafx.geometry.Pos +import javafx.scene.Group +import javafx.scene.control.Label +import javafx.scene.control.Tooltip +import javafx.scene.effect.GaussianBlur +import javafx.scene.image.ImageView +import javafx.scene.layout.Background +import javafx.scene.layout.BackgroundFill +import javafx.scene.layout.CornerRadii +import javafx.scene.layout.HBox +import javafx.scene.layout.StackPane +import javafx.scene.paint.Color +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.controlsfx.control.GridCell +import org.controlsfx.control.GridView +import xerus.ktutil.javafx.add +import xerus.ktutil.javafx.createButton +import xerus.ktutil.javafx.onFx +import xerus.ktutil.javafx.properties.SimpleObservable +import xerus.ktutil.javafx.properties.listen +import xerus.monstercat.api.Cache +import xerus.monstercat.api.Covers +import xerus.monstercat.api.response.Release +import xerus.monstercat.monsterUtilities + +class TabReleases: StackTab() { + private var cols = SimpleObservable(3) + val cellSize: Double + get() = monsterUtilities.window.width / cols.value - 16.0 * (cols.value + 1) + + private val releases = FXCollections.observableArrayList() + + private val gridView = GridView().apply { + setCellFactory { + ReleasesGridCell(this@TabReleases).apply { setOnMouseClicked { showRelease(this.item) } } + } + horizontalCellSpacing = 16.0 + verticalCellSpacing = 16.0 + + fun setCellSize() { + cellWidth = cellSize + cellHeight = cellSize + } + setCellSize() + cols.listen { setCellSize() } + monsterUtilities.window.widthProperty().listen { setCellSize() } + } + + init { + gridView.items = releases + GlobalScope.launch { + val releases = Cache.getReleases() + onFx { this@TabReleases.releases.setAll(releases) } + } + val colEditor = Group(HBox(0.0, + createButton("-") { cols.value = (cols.value - 1).coerceAtLeast(2) }, + createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) })) + add(gridView) + add(colEditor) + StackPane.setAlignment(colEditor, Pos.BOTTOM_LEFT) + } + + private fun showRelease(release: Release) { + val parent = HBox() + add(parent) + } + + class ReleasesGridCell(private val context: TabReleases): GridCell() { + override fun updateItem(item: Release?, empty: Boolean) { + super.updateItem(item, empty) + if(empty || item == null) { + graphic = null + } else { + val coverImage = Covers.getCachedCover(item.coverUrl, 1024, 256) + ?: Covers.getThumbnailImage(item.coverUrl, 256) + val cover = ImageView(coverImage) + + cover.fitHeight = context.cellSize + cover.fitWidth = context.cellSize + context.cols.listen { + cover.fitHeight = context.cellSize + cover.fitWidth = context.cellSize + } + cover.effect = GaussianBlur(5.0) + graphic = StackPane(cover, + Label(item.toString()).apply { + background = Background(BackgroundFill(Color.BLACK.apply { setOpacity(0.6) }, CornerRadii(8.0), Insets.EMPTY)) + padding = Insets(8.0) + translateY = -16.0 + } + ).apply { + alignment = Pos.BOTTOM_CENTER; tooltip = Tooltip(item.toString()) + if(item.earlyAccess) style += "-fx-border-color: gold; -fx-border-width: 4px; -fx-border-radius: 8px" + } + } + } + } +} + From ccd708bc06d37f6ffc6228f0aaf41b9609f4f1fe Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Mon, 12 Aug 2019 19:54:08 +0200 Subject: [PATCH 05/11] feat(releases-tab): create release viewer --- src/main/xerus/monstercat/tabs/TabReleases.kt | 100 +++++++++++++++--- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index 96c84df..97115b0 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -1,33 +1,38 @@ package xerus.monstercat.tabs +import javafx.animation.FadeTransition import javafx.collections.FXCollections import javafx.geometry.Insets +import javafx.geometry.Orientation import javafx.geometry.Pos import javafx.scene.Group import javafx.scene.control.Label +import javafx.scene.control.ListCell +import javafx.scene.control.ListView +import javafx.scene.control.Separator import javafx.scene.control.Tooltip import javafx.scene.effect.GaussianBlur import javafx.scene.image.ImageView -import javafx.scene.layout.Background -import javafx.scene.layout.BackgroundFill -import javafx.scene.layout.CornerRadii -import javafx.scene.layout.HBox -import javafx.scene.layout.StackPane +import javafx.scene.input.KeyCode +import javafx.scene.layout.* import javafx.scene.paint.Color +import javafx.util.Duration import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.controlsfx.control.GridCell import org.controlsfx.control.GridView -import xerus.ktutil.javafx.add -import xerus.ktutil.javafx.createButton -import xerus.ktutil.javafx.onFx +import xerus.ktutil.javafx.* import xerus.ktutil.javafx.properties.SimpleObservable import xerus.ktutil.javafx.properties.listen +import xerus.ktutil.nullIfEmpty import xerus.monstercat.api.Cache import xerus.monstercat.api.Covers +import xerus.monstercat.api.Player import xerus.monstercat.api.response.Release +import xerus.monstercat.api.response.Track import xerus.monstercat.monsterUtilities + class TabReleases: StackTab() { private var cols = SimpleObservable(3) val cellSize: Double @@ -37,7 +42,7 @@ class TabReleases: StackTab() { private val gridView = GridView().apply { setCellFactory { - ReleasesGridCell(this@TabReleases).apply { setOnMouseClicked { showRelease(this.item) } } + ReleaseGridCell(this@TabReleases).apply { setOnMouseClicked { showRelease(this.item) } } } horizontalCellSpacing = 16.0 verticalCellSpacing = 16.0 @@ -62,15 +67,67 @@ class TabReleases: StackTab() { createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) })) add(gridView) add(colEditor) - StackPane.setAlignment(colEditor, Pos.BOTTOM_LEFT) + setAlignment(colEditor, Pos.BOTTOM_LEFT) } private fun showRelease(release: Release) { - val parent = HBox() - add(parent) + val parent = VBox() + + val tracks = FXCollections.observableArrayList(release.tracks) + val tracksView = ListView(tracks).apply { setCellFactory { TrackListCell() } } + + val infoHeader = HBox(16.0, + ImageView(Covers.getThumbnailImage(release.coverUrl, 256)).apply { + effect = GaussianBlur(10.0) + GlobalScope.launch { + val cover = Covers.getCoverImage(release.coverUrl, 256) + onFx { image = cover; effect = null } + } + }, + Separator(Orientation.VERTICAL) + ) + infoHeader.fill(VBox( + Label(release.title).apply { style += "-fx-font-size: 32px; -fx-font-weight: bold;" }, + Label(release.renderedArtists.nullIfEmpty()?.let { "by $it" } + ?: "Various Artists").apply { style += "-fx-font-size: 24px;" }, + HBox(Label(release.releaseDate), Label("${release.tracks.size} tracks")).apply { style += "-fx-font-size: 16px;" }, + HBox( + buttonWithId("play") { Player.play(release) }.styleClass("releaseButton"), + buttonWithId("add") { /* TODO : Playlist add when merged */ }.styleClass("releaseButton"), + buttonWithId("download") { /* TODO : Tick in Downloader tab */ }.styleClass("releaseButton") + ).apply { fill(pos = 0) } + ).apply { fill(pos = 3) }) + + parent.style += "-fx-background-color: -fx-background;" + parent.add(infoHeader) + parent.add(Separator(Orientation.HORIZONTAL)) + parent.fill(tracksView) + parent.addButton("Back") { + FadeTransition(Duration(300.0), parent).apply { + fromValue = 1.0 + toValue = 0.0 + setOnFinished { children.remove(parent) } + }.play() + } + + FadeTransition(Duration(300.0), parent).apply { + fromValue = 0.0 + toValue = 1.0 + }.play() + add(parent).toFront() + + setOnKeyPressed { + if(it.code == KeyCode.ESCAPE) { + FadeTransition(Duration(300.0), parent).apply { + fromValue = 1.0 + toValue = 0.0 + setOnFinished { children.remove(parent) } + }.play() + } + } } - class ReleasesGridCell(private val context: TabReleases): GridCell() { + class ReleaseGridCell(private val context: TabReleases): GridCell() { override fun updateItem(item: Release?, empty: Boolean) { super.updateItem(item, empty) if(empty || item == null) { @@ -100,5 +157,22 @@ class TabReleases: StackTab() { } } } + + class TrackListCell: ListCell() { + override fun updateItem(item: Track?, empty: Boolean) { + super.updateItem(item, empty) + if(empty || item == null) graphic = null + else { + val parent = HBox() + parent.add(HBox( + buttonWithId("play") { Player.playTrack(item) }.styleClass("releaseButton"), + buttonWithId("add") { /* TODO : Add to playlist once the branch is merged */ }.styleClass("releaseButton") + ).apply { alignment = Pos.CENTER_LEFT }) + parent.fill(HBox(Label(item.toString()) /* TODO : Unlicensable alert */), 0) + + graphic = parent + } + } + } } From 1853a61218ad385c8aa87cbd177289a7df8d7685 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Tue, 13 Aug 2019 00:34:50 +0200 Subject: [PATCH 06/11] feat(releases-tab): better UI * Now loads and caches 256x256 covers * Option to blur low-res covers which awaits a download/caching of high-res ones * More visible controls * Placeholder while releases are loading * Player buttons without background (using id #controls) --- src/main/xerus/monstercat/api/Covers.kt | 2 +- src/main/xerus/monstercat/tabs/TabReleases.kt | 95 ++++++++++++------- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/main/xerus/monstercat/api/Covers.kt b/src/main/xerus/monstercat/api/Covers.kt index 307dac6..cbced2f 100644 --- a/src/main/xerus/monstercat/api/Covers.kt +++ b/src/main/xerus/monstercat/api/Covers.kt @@ -29,7 +29,7 @@ object Covers { fun getCoverImage(coverUrl: String, size: Int = 1024, invalidate: Boolean = false): Image = getCover(coverUrl, 1024, invalidate).use { createImage(it, size) } - private fun createImage(content: InputStream, size: Number) = + fun createImage(content: InputStream, size: Number) = Image(content, size.toDouble(), size.toDouble(), false, false) /** diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index 97115b0..ecaa2b8 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -1,19 +1,16 @@ package xerus.monstercat.tabs import javafx.animation.FadeTransition +import javafx.beans.property.SimpleBooleanProperty import javafx.collections.FXCollections import javafx.geometry.Insets import javafx.geometry.Orientation import javafx.geometry.Pos import javafx.scene.Group -import javafx.scene.control.Label -import javafx.scene.control.ListCell -import javafx.scene.control.ListView -import javafx.scene.control.Separator -import javafx.scene.control.Tooltip +import javafx.scene.control.* import javafx.scene.effect.GaussianBlur +import javafx.scene.image.Image import javafx.scene.image.ImageView -import javafx.scene.input.KeyCode import javafx.scene.layout.* import javafx.scene.paint.Color import javafx.util.Duration @@ -23,6 +20,7 @@ import org.controlsfx.control.GridCell import org.controlsfx.control.GridView import xerus.ktutil.javafx.* import xerus.ktutil.javafx.properties.SimpleObservable +import xerus.ktutil.javafx.properties.addListener import xerus.ktutil.javafx.properties.listen import xerus.ktutil.nullIfEmpty import xerus.monstercat.api.Cache @@ -56,18 +54,44 @@ class TabReleases: StackTab() { monsterUtilities.window.widthProperty().listen { setCellSize() } } + private val blurLowRes = SimpleBooleanProperty(false) + init { gridView.items = releases GlobalScope.launch { val releases = Cache.getReleases() onFx { this@TabReleases.releases.setAll(releases) } } - val colEditor = Group(HBox(0.0, - createButton("-") { cols.value = (cols.value - 1).coerceAtLeast(2) }, - createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) })) - add(gridView) - add(colEditor) - setAlignment(colEditor, Pos.BOTTOM_LEFT) + val colEditor = Group( + HBox(0.0, + createButton("-") { cols.value = (cols.value - 1).coerceAtLeast(2) }, + createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) }, + CheckBox("Blur low-res").bind(blurLowRes).tooltip("May affect performance while loading covers, but looks less pixelated") + ).apply { + background = Background(BackgroundFill(Color(0.0, 0.0, 0.0, 0.7), CornerRadii(8.0), Insets.EMPTY)) + padding = Insets(8.0) + } + ) + + val placeholder = Group(HBox(ImageView(Image("img/loading-16.gif")), Label("Loading Releases..."))) + add(placeholder) + setAlignment(placeholder, Pos.CENTER) + + releases.listen { + onFx { + if(!it.isNullOrEmpty()) { + add(gridView) + add(colEditor) + setAlignment(colEditor, Pos.BOTTOM_LEFT) + children.remove(placeholder) + } else { + children.removeAll(gridView, colEditor) + add(placeholder) + setAlignment(placeholder, Pos.CENTER) + } + } + } + } private fun showRelease(release: Release) { @@ -80,7 +104,7 @@ class TabReleases: StackTab() { ImageView(Covers.getThumbnailImage(release.coverUrl, 256)).apply { effect = GaussianBlur(10.0) GlobalScope.launch { - val cover = Covers.getCoverImage(release.coverUrl, 256) + val cover = Covers.getCover(release.coverUrl, 256).use { Covers.createImage(it, 256) } onFx { image = cover; effect = null } } }, @@ -92,10 +116,10 @@ class TabReleases: StackTab() { ?: "Various Artists").apply { style += "-fx-font-size: 24px;" }, HBox(Label(release.releaseDate), Label("${release.tracks.size} tracks")).apply { style += "-fx-font-size: 16px;" }, HBox( - buttonWithId("play") { Player.play(release) }.styleClass("releaseButton"), - buttonWithId("add") { /* TODO : Playlist add when merged */ }.styleClass("releaseButton"), - buttonWithId("download") { /* TODO : Tick in Downloader tab */ }.styleClass("releaseButton") - ).apply { fill(pos = 0) } + buttonWithId("play") { Player.play(release) }, + buttonWithId("add") { /* TODO : Playlist add when merged */ }, // TODO : Add icon + buttonWithId("save") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO : Save icon + ).id("controls").apply { fill(pos = 0) } ).apply { fill(pos = 3) }) parent.style += "-fx-background-color: -fx-background;" @@ -108,23 +132,13 @@ class TabReleases: StackTab() { toValue = 0.0 setOnFinished { children.remove(parent) } }.play() - } + }.apply { isCancelButton = true } FadeTransition(Duration(300.0), parent).apply { fromValue = 0.0 toValue = 1.0 }.play() add(parent).toFront() - - setOnKeyPressed { - if(it.code == KeyCode.ESCAPE) { - FadeTransition(Duration(300.0), parent).apply { - fromValue = 1.0 - toValue = 0.0 - setOnFinished { children.remove(parent) } - }.play() - } - } } class ReleaseGridCell(private val context: TabReleases): GridCell() { @@ -133,9 +147,14 @@ class TabReleases: StackTab() { if(empty || item == null) { graphic = null } else { - val coverImage = Covers.getCachedCover(item.coverUrl, 1024, 256) - ?: Covers.getThumbnailImage(item.coverUrl, 256) + val lowRes = SimpleBooleanProperty(true) + val coverImage = Covers.getThumbnailImage(item.coverUrl, 256) val cover = ImageView(coverImage) + GlobalScope.launch { + val image = Covers.getCover(item.coverUrl, 256).use { Covers.createImage(it, 256) } + lowRes.value = false + onFx { cover.image = image } + } cover.fitHeight = context.cellSize cover.fitWidth = context.cellSize @@ -143,10 +162,15 @@ class TabReleases: StackTab() { cover.fitHeight = context.cellSize cover.fitWidth = context.cellSize } - cover.effect = GaussianBlur(5.0) + + cover.effect = if(context.blurLowRes.value && lowRes.value) GaussianBlur(5.0) else null + arrayOf(context.blurLowRes, lowRes).addListener { + cover.effect = if(context.blurLowRes.value && lowRes.value) GaussianBlur(5.0) else null + } + graphic = StackPane(cover, Label(item.toString()).apply { - background = Background(BackgroundFill(Color.BLACK.apply { setOpacity(0.6) }, CornerRadii(8.0), Insets.EMPTY)) + background = Background(BackgroundFill(Color(0.0, 0.0, 0.0, 0.7), CornerRadii(8.0), Insets.EMPTY)) padding = Insets(8.0) translateY = -16.0 } @@ -165,9 +189,10 @@ class TabReleases: StackTab() { else { val parent = HBox() parent.add(HBox( - buttonWithId("play") { Player.playTrack(item) }.styleClass("releaseButton"), - buttonWithId("add") { /* TODO : Add to playlist once the branch is merged */ }.styleClass("releaseButton") - ).apply { alignment = Pos.CENTER_LEFT }) + buttonWithId("play") { Player.playTrack(item) }, + buttonWithId("add") { /* TODO : Add to playlist once the branch is merged */ }, // TODO : Add icon + buttonWithId("save") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO: Save icon + ).id("controls").apply { alignment = Pos.CENTER_LEFT }) parent.fill(HBox(Label(item.toString()) /* TODO : Unlicensable alert */), 0) graphic = parent From 5ed1da35dee28aa38cf5582bb281d5f1f7623d76 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Tue, 13 Aug 2019 14:24:01 +0200 Subject: [PATCH 07/11] fix(releases-tab): fix covers reappearing at low-res after scrolling --- src/main/xerus/monstercat/api/Covers.kt | 2 +- src/main/xerus/monstercat/tabs/TabReleases.kt | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/xerus/monstercat/api/Covers.kt b/src/main/xerus/monstercat/api/Covers.kt index cbced2f..d586be1 100644 --- a/src/main/xerus/monstercat/api/Covers.kt +++ b/src/main/xerus/monstercat/api/Covers.kt @@ -55,7 +55,7 @@ object Covers { return coverFile.inputStream() } - fun getCachedCover(coverUrl: String, cachedSize: Int, imageSize: Int): Image? { + fun getCachedCover(coverUrl: String, cachedSize: Int, imageSize: Int = cachedSize): Image? { val coverFile = coverCacheFile(coverUrl, cachedSize) return try { val imageStream = coverFile.inputStream() diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index ecaa2b8..e47824d 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -32,7 +32,7 @@ import xerus.monstercat.monsterUtilities class TabReleases: StackTab() { - private var cols = SimpleObservable(3) + private var cols = SimpleObservable(2) val cellSize: Double get() = monsterUtilities.window.width / cols.value - 16.0 * (cols.value + 1) @@ -142,18 +142,26 @@ class TabReleases: StackTab() { } class ReleaseGridCell(private val context: TabReleases): GridCell() { - override fun updateItem(item: Release?, empty: Boolean) { + override fun updateItem(item: Release?, empty : Boolean) { super.updateItem(item, empty) if(empty || item == null) { graphic = null } else { val lowRes = SimpleBooleanProperty(true) - val coverImage = Covers.getThumbnailImage(item.coverUrl, 256) - val cover = ImageView(coverImage) - GlobalScope.launch { - val image = Covers.getCover(item.coverUrl, 256).use { Covers.createImage(it, 256) } + val cover = ImageView() + + val cachedCover = Covers.getCachedCover(item.coverUrl, 256, 256) + if(cachedCover != null) { + cover.image = cachedCover lowRes.value = false - onFx { cover.image = image } + } else { + cover.image = Covers.getThumbnailImage(item.coverUrl, 256) + lowRes.value = true + GlobalScope.launch { + val image = Covers.getCover(item.coverUrl, 256).use { Covers.createImage(it, 256) } + lowRes.value = false + onFx { cover.image = image } + } } cover.fitHeight = context.cellSize @@ -199,5 +207,4 @@ class TabReleases: StackTab() { } } } -} - +} \ No newline at end of file From 2e524405297be1aa2f7484ebd7390fd882cf4b3a Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Tue, 13 Aug 2019 15:09:36 +0200 Subject: [PATCH 08/11] feat(releases-tab): use updated util's Satin icons --- build.gradle.kts | 2 +- src/main/xerus/monstercat/tabs/TabReleases.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cf9c399..be6cd7f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,7 @@ repositories { dependencies { implementation(kotlin("reflect")) - implementation("com.github.Xerus2000.util", "javafx", "259dad93192584306dbf951721d3cf8e6bd25d1b") + implementation("com.github.defvs.util", "javafx", "36699cd09dfa0c8fd3991ea12533f06af5d51ec0") implementation("org.controlsfx", "controlsfx", "8.40.+") implementation("ch.qos.logback", "logback-classic", "1.2.+") diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index e47824d..01b22f6 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -117,8 +117,8 @@ class TabReleases: StackTab() { HBox(Label(release.releaseDate), Label("${release.tracks.size} tracks")).apply { style += "-fx-font-size: 16px;" }, HBox( buttonWithId("play") { Player.play(release) }, - buttonWithId("add") { /* TODO : Playlist add when merged */ }, // TODO : Add icon - buttonWithId("save") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO : Save icon + buttonWithId("satin-add") { /* TODO : Playlist add when merged */ }, // TODO : Add icon + buttonWithId("satin-open") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO : Save icon ).id("controls").apply { fill(pos = 0) } ).apply { fill(pos = 3) }) @@ -198,8 +198,8 @@ class TabReleases: StackTab() { val parent = HBox() parent.add(HBox( buttonWithId("play") { Player.playTrack(item) }, - buttonWithId("add") { /* TODO : Add to playlist once the branch is merged */ }, // TODO : Add icon - buttonWithId("save") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO: Save icon + buttonWithId("satin-add") { /* TODO : Add to playlist once the branch is merged */ }, // TODO : Add icon + buttonWithId("satin-open") { /* TODO : Tick in Downloader tab */ }.tooltip("Show in downloader") // TODO: Save icon ).id("controls").apply { alignment = Pos.CENTER_LEFT }) parent.fill(HBox(Label(item.toString()) /* TODO : Unlicensable alert */), 0) From badfec586b93844bd305671610950fb9241aec9e Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Tue, 13 Aug 2019 15:10:15 +0200 Subject: [PATCH 09/11] feat(releases-tab): make release info cover use cached version first --- src/main/xerus/monstercat/tabs/TabReleases.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index 01b22f6..8bf74f5 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -103,9 +103,16 @@ class TabReleases: StackTab() { val infoHeader = HBox(16.0, ImageView(Covers.getThumbnailImage(release.coverUrl, 256)).apply { effect = GaussianBlur(10.0) - GlobalScope.launch { - val cover = Covers.getCover(release.coverUrl, 256).use { Covers.createImage(it, 256) } - onFx { image = cover; effect = null } + val cachedCover = Covers.getCachedCover(release.coverUrl, 256, 256) + if(cachedCover != null) { + image = cachedCover + effect = null + } else { + image = Covers.getThumbnailImage(release.coverUrl, 256) + GlobalScope.launch { + val image = Covers.getCover(release.coverUrl, 256).use { Covers.createImage(it, 256) } + onFx { this@apply.image = image; effect = null } + } } }, Separator(Orientation.VERTICAL) From 0bbc41ebf53ad60f8c7ff5872b81ab17a5aadee2 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Tue, 17 Sep 2019 15:21:30 +0200 Subject: [PATCH 10/11] feat(release-tab): add click on art to open larger window --- src/main/xerus/monstercat/tabs/TabReleases.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index 8bf74f5..634d188 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -114,6 +114,7 @@ class TabReleases: StackTab() { onFx { this@apply.image = image; effect = null } } } + setOnMouseClicked { monsterUtilities.viewCover(release.coverUrl) } }, Separator(Orientation.VERTICAL) ) From 70375889920b7343a18b1426bad513b944c11486 Mon Sep 17 00:00:00 2001 From: Daniel Thirion Date: Sun, 22 Sep 2019 20:57:45 +0200 Subject: [PATCH 11/11] feat(release-tab): add list view --- src/main/xerus/monstercat/tabs/TabReleases.kt | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/main/xerus/monstercat/tabs/TabReleases.kt b/src/main/xerus/monstercat/tabs/TabReleases.kt index 634d188..b8c0945 100644 --- a/src/main/xerus/monstercat/tabs/TabReleases.kt +++ b/src/main/xerus/monstercat/tabs/TabReleases.kt @@ -13,6 +13,7 @@ import javafx.scene.image.Image import javafx.scene.image.ImageView import javafx.scene.layout.* import javafx.scene.paint.Color +import javafx.scene.text.Font import javafx.util.Duration import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -54,19 +55,39 @@ class TabReleases: StackTab() { monsterUtilities.window.widthProperty().listen { setCellSize() } } + private val listView = ListView().apply { + setCellFactory { + ReleaseListCell().apply { setOnMouseClicked { showRelease(this.item) } } + } + } + private val blurLowRes = SimpleBooleanProperty(false) init { gridView.items = releases + listView.items = releases GlobalScope.launch { val releases = Cache.getReleases() onFx { this@TabReleases.releases.setAll(releases) } } + val gridEditor = HBox(0.0, + createButton("-") { cols.value = (cols.value - 1).coerceAtLeast(2) }, + createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) }, + CheckBox("Blur low-res").bind(blurLowRes).tooltip("May affect performance while loading covers, but looks less pixelated") + ) val colEditor = Group( - HBox(0.0, - createButton("-") { cols.value = (cols.value - 1).coerceAtLeast(2) }, - createButton("+") { cols.value = (cols.value + 1).coerceAtMost(4) }, - CheckBox("Blur low-res").bind(blurLowRes).tooltip("May affect performance while loading covers, but looks less pixelated") + VBox( + CheckBox("Grid View").apply { isSelected = false }.onClick { + gridEditor.isDisable = !isSelected + val tab = this@TabReleases + tab.children.removeAll(listView, gridView) + if(isSelected) { + tab.children.add(0, gridView) + }else{ + tab.children.add(0, listView) + } + }, + gridEditor ).apply { background = Background(BackgroundFill(Color(0.0, 0.0, 0.0, 0.7), CornerRadii(8.0), Insets.EMPTY)) padding = Insets(8.0) @@ -80,12 +101,12 @@ class TabReleases: StackTab() { releases.listen { onFx { if(!it.isNullOrEmpty()) { - add(gridView) + add(listView) add(colEditor) setAlignment(colEditor, Pos.BOTTOM_LEFT) children.remove(placeholder) } else { - children.removeAll(gridView, colEditor) + children.removeAll(listView, gridView, colEditor) add(placeholder) setAlignment(placeholder, Pos.CENTER) } @@ -150,7 +171,7 @@ class TabReleases: StackTab() { } class ReleaseGridCell(private val context: TabReleases): GridCell() { - override fun updateItem(item: Release?, empty : Boolean) { + override fun updateItem(item: Release?, empty: Boolean) { super.updateItem(item, empty) if(empty || item == null) { graphic = null @@ -198,6 +219,18 @@ class TabReleases: StackTab() { } } + class ReleaseListCell: ListCell() { + override fun updateItem(item: Release?, empty: Boolean) { + super.updateItem(item, empty) + if(empty || item == null) { + graphic = null + } else { + val cover = ImageView(Covers.getThumbnailImage(item.coverUrl, 256)).apply { fitHeight = 64.0; fitWidth = 64.0; } + graphic = HBox(cover, Label(item.toString()).apply { font = Font(14.0); padding = Insets(16.0) }) + } + } + } + class TrackListCell: ListCell() { override fun updateItem(item: Track?, empty: Boolean) { super.updateItem(item, empty)