Simple Board table.
This commit is contained in:
parent
ad68d10a3c
commit
d6bc50d9f6
|
@ -22,10 +22,10 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
implementation("org.kodein.di:kodein-di:7.0.0")
|
implementation("org.kodein.di:kodein-di:7.1.0")
|
||||||
implementation("org.kodein.di:kodein-di-generic-jvm:6.5.5")
|
implementation("org.kodein.di:kodein-di-generic-jvm:6.5.5")
|
||||||
implementation("org.kodein.di:kodein-di-framework-tornadofx-jvm:7.0.0")
|
implementation("org.kodein.di:kodein-di-framework-tornadofx-jvm:7.1.0")
|
||||||
implementation("io.github.microutils:kotlin-logging:1.5.9")
|
implementation("io.github.microutils:kotlin-logging-jvm:2.0.3")
|
||||||
implementation("org.slf4j:slf4j-simple:1.7.26")
|
implementation("org.slf4j:slf4j-simple:1.7.26")
|
||||||
// implementation("org.dizitart:potassium-nitrite:3.4.2")
|
// implementation("org.dizitart:potassium-nitrite:3.4.2")
|
||||||
implementation("no.tornado:tornadofx:1.7.20")
|
implementation("no.tornado:tornadofx:1.7.20")
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.model
|
|
||||||
|
|
||||||
import javafx.beans.property.SimpleLongProperty
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
|
||||||
import tornadofx.getValue
|
|
||||||
|
|
||||||
open class Board(
|
|
||||||
id: Long,
|
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
backgroundImage: BackgroundImage
|
|
||||||
) {
|
|
||||||
val idProperty by lazy { SimpleLongProperty(this, "id", id) }
|
|
||||||
val id by idProperty
|
|
||||||
|
|
||||||
val nameProperty by lazy { SimpleStringProperty(this, "name", name) }
|
|
||||||
val name by nameProperty
|
|
||||||
|
|
||||||
val descriptionProperty by lazy { SimpleStringProperty(this, "description", description) }
|
|
||||||
val description by descriptionProperty
|
|
||||||
|
|
||||||
val backgroundImageProperty by lazy { SimpleObjectProperty(this, "backgroundImage", backgroundImage) }
|
|
||||||
val backgroundImage by backgroundImageProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
fun boardFactory(
|
|
||||||
id: Long = 0L,
|
|
||||||
name: String = "board",
|
|
||||||
description: String = "description",
|
|
||||||
backgroundImage: BackgroundImage = backgroundImageFactory()
|
|
||||||
): Board {
|
|
||||||
return Board(id, name, description, backgroundImage)
|
|
||||||
}
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package dev.fyloz.plannervio.core.model
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty
|
||||||
|
import javafx.beans.property.SimpleLongProperty
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import tornadofx.getValue
|
||||||
|
import tornadofx.setValue
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
open class Board(
|
||||||
|
id: Long,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
remote: Boolean = false,
|
||||||
|
location: String? = null,
|
||||||
|
private: Boolean = false,
|
||||||
|
favorite: Boolean = false,
|
||||||
|
lastVisited: LocalDateTime? = null
|
||||||
|
) {
|
||||||
|
val idProperty by lazy { SimpleLongProperty(this, "id", id) }
|
||||||
|
var id: Long by idProperty
|
||||||
|
|
||||||
|
val nameProperty by lazy { SimpleStringProperty(this, "name", name) }
|
||||||
|
var name: String by nameProperty
|
||||||
|
|
||||||
|
val descriptionProperty by lazy { SimpleStringProperty(this, "description", description) }
|
||||||
|
var description: String by descriptionProperty
|
||||||
|
|
||||||
|
val remoteProperty by lazy { SimpleBooleanProperty(this, "remote", remote) }
|
||||||
|
var isRemote: Boolean by remoteProperty
|
||||||
|
|
||||||
|
val locationProperty by lazy { SimpleStringProperty(this, "location", location) }
|
||||||
|
var location: String? by locationProperty
|
||||||
|
|
||||||
|
val privateProperty by lazy { SimpleBooleanProperty(this, "private", private) }
|
||||||
|
var isPrivate: Boolean by privateProperty
|
||||||
|
|
||||||
|
val favoriteProperty by lazy { SimpleBooleanProperty(this, "favorite", favorite) }
|
||||||
|
var favorite: Boolean by favoriteProperty
|
||||||
|
|
||||||
|
val lastVisitedProperty by lazy { SimpleObjectProperty(this, "lastVisited", lastVisited) }
|
||||||
|
var lastVisited: LocalDateTime? by lastVisitedProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DSL for [Board]s. */
|
||||||
|
fun board(
|
||||||
|
id: Long = 0L,
|
||||||
|
name: String = "board_name",
|
||||||
|
description: String = "board_description",
|
||||||
|
remote: Boolean = false,
|
||||||
|
location: String? = null,
|
||||||
|
private: Boolean = false,
|
||||||
|
favorite: Boolean = false,
|
||||||
|
lastVisited: LocalDateTime? = null,
|
||||||
|
op: Board.() -> Unit = {}
|
||||||
|
) = Board(id, name, description, remote, location, private, favorite, lastVisited).apply(op)
|
|
@ -1,32 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.model
|
|
||||||
|
|
||||||
import javafx.beans.property.SimpleListProperty
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
|
||||||
import tornadofx.getValue
|
|
||||||
import tornadofx.toObservable
|
|
||||||
|
|
||||||
class PrivateBoard(
|
|
||||||
id: Long,
|
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
backgroundImage: BackgroundImage,
|
|
||||||
password: String,
|
|
||||||
users: MutableList<User>
|
|
||||||
) : Board(id, name, description, backgroundImage) {
|
|
||||||
val passwordProperty = SimpleStringProperty(this, "password", password)
|
|
||||||
val password by passwordProperty
|
|
||||||
|
|
||||||
val usersProperty = SimpleListProperty(this, "users", users.toObservable())
|
|
||||||
val users by usersProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
fun privateBoardFactory(
|
|
||||||
id: Long = 0L,
|
|
||||||
name: String = "private board",
|
|
||||||
description: String = "description",
|
|
||||||
backgroundImage: BackgroundImage = backgroundImageFactory(),
|
|
||||||
password: String = "password",
|
|
||||||
users: MutableList<User> = mutableListOf()
|
|
||||||
): PrivateBoard {
|
|
||||||
return PrivateBoard(id, name, description, backgroundImage, password, users)
|
|
||||||
}
|
|
|
@ -1,8 +1,16 @@
|
||||||
package dev.fyloz.plannervio.core.model
|
package dev.fyloz.plannervio.core.model
|
||||||
|
|
||||||
data class User(
|
data class User(
|
||||||
val username: String,
|
var username: String,
|
||||||
val email: String,
|
var email: String,
|
||||||
val password: String
|
var password: String
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** DSL for [User]s. */
|
||||||
|
fun user(
|
||||||
|
username: String = "user_username",
|
||||||
|
email: String = "user_email",
|
||||||
|
password: String = "user_password",
|
||||||
|
op: User.() -> Unit = {}
|
||||||
|
) = User(username, email, password).apply(op)
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.model.view
|
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.Board
|
|
||||||
import tornadofx.ItemViewModel
|
|
||||||
|
|
||||||
class BoardViewModel(board: Board? = null) : ItemViewModel<Board>() {
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (board != null) item = board
|
|
||||||
}
|
|
||||||
|
|
||||||
val id = bind(Board::idProperty)
|
|
||||||
val name = bind(Board::nameProperty)
|
|
||||||
val description = bind(Board::descriptionProperty)
|
|
||||||
val backgroundImage = bind(Board::backgroundImageProperty)
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.fyloz.plannervio.core.model.view
|
||||||
|
|
||||||
|
import dev.fyloz.plannervio.core.model.Board
|
||||||
|
import tornadofx.ItemViewModel
|
||||||
|
|
||||||
|
open class BoardViewModel(board: Board? = null) : ItemViewModel<Board>() {
|
||||||
|
init {
|
||||||
|
if (board != null) item = board
|
||||||
|
}
|
||||||
|
|
||||||
|
val id = bind(Board::idProperty)
|
||||||
|
val name = bind(Board::nameProperty)
|
||||||
|
val description = bind(Board::descriptionProperty)
|
||||||
|
val isRemote = bind(Board::remoteProperty)
|
||||||
|
val location = bind(Board::locationProperty)
|
||||||
|
val isPrivate = bind(Board::privateProperty)
|
||||||
|
val favorite = bind(Board::favoriteProperty)
|
||||||
|
val lastVisited = bind(Board::lastVisitedProperty)
|
||||||
|
}
|
|
@ -1,80 +1,76 @@
|
||||||
package dev.fyloz.plannervio.core.repository.memory
|
package dev.fyloz.plannervio.core.repository.memory
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.*
|
|
||||||
import dev.fyloz.plannervio.core.model.Board
|
import dev.fyloz.plannervio.core.model.Board
|
||||||
|
import dev.fyloz.plannervio.core.model.board
|
||||||
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class MemoryBoardRepository : IBoardRepository {
|
class MemoryBoardRepository : IBoardRepository {
|
||||||
private val boards = mutableListOf<Board>()
|
private val boards = mutableListOf<Board>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
0L,
|
id = 0L
|
||||||
"Board 1",
|
name = "Board 1"
|
||||||
"Voluptates facere maiores quis suscipit nostrum. Et accusamus quas animi. Similique reiciendis ea iste cum sunt aut minus. Aut cupiditate consequatur dolor vel doloremque aut eius voluptas. Sed reprehenderit veniam odio voluptatem numquam doloribus. Consequuntur esse assumenda aut expedita voluptatibus.",
|
description = "Voluptates facere maiores quis suscipit nostrum. Et accusamus quas animi. Similique reiciendis ea iste cum sunt aut minus. Aut cupiditate consequatur dolor vel doloremque aut eius voluptas. Sed reprehenderit veniam odio voluptatem numquam doloribus. Consequuntur esse assumenda aut expedita voluptatibus."
|
||||||
BackgroundImage("bundled_background_0.jpg")
|
favorite = true
|
||||||
)
|
lastVisited = LocalDateTime.now()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
1L,
|
id = 1L
|
||||||
"test",
|
name = "Board 2"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
privateBoardFactory(
|
board {
|
||||||
2L,
|
id = 2L
|
||||||
"Board 3",
|
name = "Board 3"
|
||||||
"Pariatur error dolores beatae et rem minus a quia. Facilis nam esse repellat consequatur aperiam nostrum aut vel. Non ratione aut maiores beatae molestiae maxime ipsum molestiae. Asperiores est id in quia tempore soluta voluptatem atque.",
|
description = "Pariatur error dolores beatae et rem minus a quia. Facilis nam esse repellat consequatur aperiam nostrum aut vel. Non ratione aut maiores beatae molestiae maxime ipsum molestiae. Asperiores est id in quia tempore soluta voluptatem atque."
|
||||||
BackgroundImage("bundled_background_3.jpg"),
|
isRemote = true
|
||||||
"password",
|
location = "plannervio.fyloz.dev"
|
||||||
mutableListOf(
|
isPrivate = true
|
||||||
User("username", "username@email.com", "password")
|
}
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
3L,
|
id = 3L
|
||||||
"test",
|
name = "Board 4"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
4L,
|
id = 4L
|
||||||
"test",
|
name = "Board 5"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
5L,
|
id = 5L
|
||||||
"test",
|
name = "Board 6"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
isRemote = true
|
||||||
)
|
location = "todo.google.ca"
|
||||||
|
}
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
6L,
|
id = 6L
|
||||||
"test",
|
name = "Board 7"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
save(
|
save(
|
||||||
boardFactory(
|
board {
|
||||||
7L,
|
id = 7L
|
||||||
"test",
|
name = "Board 8"
|
||||||
"Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non.",
|
description = "Libero rerum sed dolore repudiandae id voluptatem. In iusto nesciunt nihil quia vel ut qui quae. Nihil eos similique corporis fugit autem reiciendis vel. Aut neque quas reprehenderit reiciendis officia non."
|
||||||
BackgroundImage("bundled_background_1.jpg")
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.BackgroundImage
|
|
||||||
import javafx.scene.image.Image
|
|
||||||
import mu.KotlinLogging
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class BackgroundImageService : IBackgroundImageService {
|
|
||||||
private val logger = KotlinLogging.logger { }
|
|
||||||
|
|
||||||
override fun loadImage(image: BackgroundImage): Image? {
|
|
||||||
val stream = getImageInputStream(image)
|
|
||||||
return if (stream != null)
|
|
||||||
Image(stream)
|
|
||||||
else {
|
|
||||||
logger.warn("Could not load image '${image.path}'")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the input stream for a given background image.
|
|
||||||
*
|
|
||||||
* @param image The background image
|
|
||||||
*/
|
|
||||||
fun getImageInputStream(image: BackgroundImage): InputStream? {
|
|
||||||
return if (image.bundled) {
|
|
||||||
getBundledImageInputStream(image)
|
|
||||||
} else {
|
|
||||||
getCustomImageInputStream(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the input stream for a given bundled background image.
|
|
||||||
*
|
|
||||||
* @param image The background image
|
|
||||||
*/
|
|
||||||
fun getBundledImageInputStream(image: BackgroundImage): InputStream? {
|
|
||||||
return ClassLoader.getSystemResourceAsStream("images/${image.path}")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the input stream for a given custom background image.
|
|
||||||
*
|
|
||||||
* @param image The background image
|
|
||||||
*/
|
|
||||||
fun getCustomImageInputStream(image: BackgroundImage): InputStream? {
|
|
||||||
return FileInputStream(image.path)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,11 +5,16 @@ import dev.fyloz.plannervio.core.model.Board
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class BoardService(di: DI) : IBoardService {
|
/** A service for boards. */
|
||||||
override val repository: IBoardRepository by di.instance()
|
interface IBoardService {
|
||||||
|
/** Gets an immutable list containing all boards. */
|
||||||
|
fun getBoards(): List<Board>
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoardServiceImpl(di: DI) : IBoardService {
|
||||||
|
private val repository: IBoardRepository by di.instance()
|
||||||
|
|
||||||
override fun getBoards(): List<Board> {
|
override fun getBoards(): List<Board> {
|
||||||
return repository.findAll()
|
return repository.findAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -28,7 +28,7 @@ private const val keywordsServiceKey = "KEYWORDS"
|
||||||
private val keywordsBundle: ResourceBundle = getBundle(keywordsServiceKey.toLowerCase())
|
private val keywordsBundle: ResourceBundle = getBundle(keywordsServiceKey.toLowerCase())
|
||||||
|
|
||||||
private val i18nServices: MutableMap<String, I18nService> = mutableMapOf(
|
private val i18nServices: MutableMap<String, I18nService> = mutableMapOf(
|
||||||
keywordsServiceKey to I18nService(null, keywordsBundle)
|
// keywordsServiceKey to I18nService(null, keywordsBundle)
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.BackgroundImage
|
|
||||||
import javafx.scene.image.Image
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A service for background images.
|
|
||||||
*/
|
|
||||||
interface IBackgroundImageService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the JavaFX image for a given BackgroundImage.
|
|
||||||
*
|
|
||||||
* If the file cannot be loaded, null will be returned.
|
|
||||||
*
|
|
||||||
* @param image The image to load
|
|
||||||
* @return The JavaFX image for the BackgroundImage
|
|
||||||
*/
|
|
||||||
fun loadImage(image: BackgroundImage): Image?
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
|
||||||
import dev.fyloz.plannervio.core.model.Board
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A service for boards.
|
|
||||||
*/
|
|
||||||
interface IBoardService {
|
|
||||||
val repository: IBoardRepository
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all boards.
|
|
||||||
*
|
|
||||||
* @return An immutable list containing all boards
|
|
||||||
*/
|
|
||||||
fun getBoards(): List<Board>
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
|
||||||
|
|
||||||
import com.beust.klaxon.Klaxon
|
|
||||||
import com.jfoenix.svg.SVGGlyph
|
|
||||||
import javafx.scene.paint.Color
|
|
||||||
import mu.KotlinLogging
|
|
||||||
import tornadofx.SVGIcon
|
|
||||||
|
|
||||||
private val logger = KotlinLogging.logger { }
|
|
||||||
private val storedSvgPaths: List<SvgPath> = loadSvgPaths()
|
|
||||||
|
|
||||||
fun svgGlyph(pathName: SvgPathName, size: Double = 16.0, color: Color = Color.BLACK): SVGGlyph? {
|
|
||||||
val svgPath = svgPath(pathName) ?: return null
|
|
||||||
val glyph = SVGGlyph(svgPath.path, color)
|
|
||||||
glyph.setSize(size, size)
|
|
||||||
return glyph
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadSvgPaths(): List<SvgPath> {
|
|
||||||
val doc = SvgPath::class.java.getResource("/images/icons.json").readText()
|
|
||||||
return Klaxon().parseArray(doc) ?: throw IllegalStateException("Could not load svg paths.")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun svgPath(pathName: SvgPathName): SvgPath? {
|
|
||||||
val svgPath = storedSvgPaths.firstOrNull {
|
|
||||||
pathName.pathName == it.name
|
|
||||||
}
|
|
||||||
if (svgPath == null) {
|
|
||||||
logger.warn("Svg path ${pathName.pathName} not loaded or not found")
|
|
||||||
}
|
|
||||||
return svgPath
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class SvgPath(val name: String, val path: String)
|
|
||||||
|
|
||||||
enum class SvgPathName(val pathName: String) {
|
|
||||||
FORMAT_LIST_BULLETED("format_list_bulleted"),
|
|
||||||
STAR("star"),
|
|
||||||
HISTORY("history"),
|
|
||||||
COG("cog")
|
|
||||||
}
|
|
|
@ -2,12 +2,12 @@ package dev.fyloz.plannervio.ui
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
||||||
import dev.fyloz.plannervio.core.repository.memory.MemoryBoardRepository
|
import dev.fyloz.plannervio.core.repository.memory.MemoryBoardRepository
|
||||||
import dev.fyloz.plannervio.core.service.BackgroundImageService
|
import dev.fyloz.plannervio.core.service.BoardServiceImpl
|
||||||
import dev.fyloz.plannervio.core.service.BoardService
|
|
||||||
import dev.fyloz.plannervio.core.service.IBackgroundImageService
|
|
||||||
import dev.fyloz.plannervio.core.service.IBoardService
|
import dev.fyloz.plannervio.core.service.IBoardService
|
||||||
import dev.fyloz.plannervio.ui.style.Style
|
import dev.fyloz.plannervio.ui.style.Style
|
||||||
import dev.fyloz.plannervio.ui.view.MainView
|
import dev.fyloz.plannervio.ui.view.MainView
|
||||||
|
import mu.KLogger
|
||||||
|
import mu.KotlinLogging
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.bind
|
import org.kodein.di.bind
|
||||||
|
@ -22,14 +22,18 @@ fun main(args: Array<String>) {
|
||||||
launch<Plannervio>(args)
|
launch<Plannervio>(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val logger: KLogger by lazy {
|
||||||
|
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace")
|
||||||
|
KotlinLogging.logger(Plannervio::class.simpleName!!)
|
||||||
|
}
|
||||||
|
|
||||||
class Plannervio : App(MainView::class, Style::class), DIAware {
|
class Plannervio : App(MainView::class, Style::class), DIAware {
|
||||||
override val di: DI
|
override val di: DI
|
||||||
get() = DI {
|
get() = DI {
|
||||||
installTornadoSource()
|
installTornadoSource()
|
||||||
|
|
||||||
bind<IBoardRepository>() with singleton { MemoryBoardRepository() }
|
bind<IBoardRepository>() with singleton { MemoryBoardRepository() }
|
||||||
bind<IBoardService>() with singleton { BoardService(di) }
|
bind<IBoardService>() with singleton { BoardServiceImpl(di) }
|
||||||
bind<IBackgroundImageService>() with singleton { BackgroundImageService() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -8,28 +8,16 @@ import java.util.*
|
||||||
* @param initBundles The bundles contained by the merged bundle
|
* @param initBundles The bundles contained by the merged bundle
|
||||||
*/
|
*/
|
||||||
class MergedResourceBundle(vararg initBundles: ResourceBundle) : ResourceBundle() {
|
class MergedResourceBundle(vararg initBundles: ResourceBundle) : ResourceBundle() {
|
||||||
private val bundles: MutableList<ResourceBundle>
|
private val bundles: MutableList<ResourceBundle> = initBundles.toMutableList()
|
||||||
|
|
||||||
init {
|
/** Adds a [bundle] to the contained bundles. */
|
||||||
bundles = initBundles.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a bundle to contained bundles.
|
|
||||||
*
|
|
||||||
* @param bundle The bundle to add
|
|
||||||
*/
|
|
||||||
operator fun plus(bundle: ResourceBundle) {
|
operator fun plus(bundle: ResourceBundle) {
|
||||||
bundles + bundle
|
bundles += bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Adds [bundles] to the contained bundles. */
|
||||||
* Removes a bundle from contained bundles.
|
operator fun plus(bundles: Iterable<ResourceBundle>) {
|
||||||
*
|
this.bundles += bundles
|
||||||
* @param bundle The bundle to remove
|
|
||||||
*/
|
|
||||||
operator fun minus(bundle: ResourceBundle) {
|
|
||||||
bundles - bundle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleGetObject(key: String): Any? {
|
override fun handleGetObject(key: String): Any? {
|
||||||
|
|
|
@ -1,39 +1,45 @@
|
||||||
package dev.fyloz.plannervio.ui.style
|
package dev.fyloz.plannervio.ui.style
|
||||||
|
|
||||||
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.Cursor
|
import javafx.scene.Cursor
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import javafx.scene.paint.Paint
|
import javafx.scene.paint.Paint
|
||||||
|
import javafx.scene.text.TextAlignment
|
||||||
import kfoenix.JFXStylesheet.Companion.jfxButton
|
import kfoenix.JFXStylesheet.Companion.jfxButton
|
||||||
|
import kfoenix.JFXStylesheet.Companion.jfxListView
|
||||||
import kfoenix.JFXStylesheet.Companion.jfxRippler
|
import kfoenix.JFXStylesheet.Companion.jfxRippler
|
||||||
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
|
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
|
||||||
|
import kfoenix.JFXStylesheet.Companion.jfxVerticalGap
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
val primaryTheme = mapOf(
|
val primaryTheme = mapOf(
|
||||||
50 to c("#fcf2e7"),
|
50 to c("#fcf2e7"),
|
||||||
100 to c("#f8dec3"),
|
100 to c("#f8dec3"),
|
||||||
200 to c("#f3c89c"),
|
200 to c("#f3c89c"),
|
||||||
300 to c("#eeb274"),
|
300 to c("#eeb274"),
|
||||||
400 to c("#eaa256"),
|
400 to c("#eaa256"),
|
||||||
500 to c("#e69138"),
|
500 to c("#e69138"),
|
||||||
600 to c("#e38932"),
|
600 to c("#e38932"),
|
||||||
700 to c("#df7e2b"),
|
700 to c("#df7e2b"),
|
||||||
800 to c("#db7424"),
|
800 to c("#db7424"),
|
||||||
900 to c("#d56217")
|
900 to c("#d56217")
|
||||||
)
|
)
|
||||||
|
|
||||||
val accentTheme = mapOf(
|
val accentTheme = mapOf(
|
||||||
50 to c("#e8f0f8"),
|
50 to c("#e8f0f8"),
|
||||||
100 to c("#c5daee"),
|
100 to c("#c5daee"),
|
||||||
200 to c("#9ec2e3"),
|
200 to c("#9ec2e3"),
|
||||||
300 to c("#77aad7"),
|
300 to c("#77aad7"),
|
||||||
400 to c("#5a97cf"),
|
400 to c("#5a97cf"),
|
||||||
500 to c("#3d85c6"),
|
500 to c("#3d85c6"),
|
||||||
600 to c("#377dc0"),
|
600 to c("#377dc0"),
|
||||||
700 to c("#2f72b9"),
|
700 to c("#2f72b9"),
|
||||||
800 to c("#2768b1"),
|
800 to c("#2768b1"),
|
||||||
900 to c("#1a55a4")
|
900 to c("#1a55a4")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val background = Color.WHITE
|
||||||
|
|
||||||
class Style : Stylesheet() {
|
class Style : Stylesheet() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -47,8 +53,10 @@ class Style : Stylesheet() {
|
||||||
// Classes
|
// Classes
|
||||||
val active by cssclass()
|
val active by cssclass()
|
||||||
val appNameLabel by cssclass()
|
val appNameLabel by cssclass()
|
||||||
val navBar by cssclass()
|
val centered by cssclass()
|
||||||
val jfxSvgGlyphWrapper by cssclass()
|
val jfxSvgGlyphWrapper by cssclass()
|
||||||
|
val navBar by cssclass()
|
||||||
|
val test by cssclass()
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -100,5 +108,44 @@ class Style : Stylesheet() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tableView {
|
||||||
|
backgroundColor += background
|
||||||
|
borderColor += box(c(0.0, 0.0, 0.0, 0.12))
|
||||||
|
|
||||||
|
columnHeader {
|
||||||
|
backgroundColor += background
|
||||||
|
prefHeight = 56.px
|
||||||
|
|
||||||
|
label {
|
||||||
|
alignment = Pos.CENTER_LEFT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filler {
|
||||||
|
backgroundColor += background
|
||||||
|
}
|
||||||
|
|
||||||
|
tableRowCell {
|
||||||
|
prefHeight = 52.px
|
||||||
|
borderColor += box(c(0.0, 0.0, 0.0, 0.12), background, background, background)
|
||||||
|
borderWidth += box(1.px, 0.px, 0.px, 0.px)
|
||||||
|
backgroundColor += background
|
||||||
|
|
||||||
|
and(hover) {
|
||||||
|
backgroundColor += c(0.0, 0.0, 0.0, 0.04)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableColumn {
|
||||||
|
alignment = Pos.CENTER_LEFT
|
||||||
|
borderColor += box(Color.TRANSPARENT)
|
||||||
|
padding = box(0.px, 16.px)
|
||||||
|
|
||||||
|
and(centered) {
|
||||||
|
alignment = Pos.CENTER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,34 @@
|
||||||
package dev.fyloz.plannervio.ui.view
|
package dev.fyloz.plannervio.ui.view
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXButton
|
import dev.fyloz.plannervio.core.model.Board
|
||||||
import com.jfoenix.controls.JFXTextField
|
import dev.fyloz.plannervio.core.service.IBoardService
|
||||||
import dev.fyloz.plannervio.core.service.SvgPathName
|
|
||||||
import dev.fyloz.plannervio.ui.style.Style
|
import dev.fyloz.plannervio.ui.style.Style
|
||||||
import javafx.beans.property.SimpleBooleanProperty
|
import javafx.application.Platform
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
|
import javafx.scene.control.Label
|
||||||
|
import javafx.scene.layout.HBox
|
||||||
import javafx.scene.layout.Priority
|
import javafx.scene.layout.Priority
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
import kfoenix.jfxtextfield
|
||||||
|
import org.kodein.di.instance
|
||||||
|
import org.kodein.di.tornadofx.kodeinDI
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.FormatStyle
|
||||||
|
|
||||||
/** The app's main view */
|
/** The app's main view */
|
||||||
class MainView : AbstractView() {
|
class MainView : View() {
|
||||||
private val presenter: MainViewPresenter by inject()
|
private val presenter: MainViewPresenter by inject()
|
||||||
|
private var activeTabView: View = presenter.activeView
|
||||||
|
set(value) {
|
||||||
|
activeTabView.replaceWith(value)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
messages = messages("lang.views.main")
|
||||||
|
}
|
||||||
|
|
||||||
override val root = borderpane {
|
override val root = borderpane {
|
||||||
left = vbox {
|
left = vbox {
|
||||||
|
@ -24,24 +40,153 @@ class MainView : AbstractView() {
|
||||||
alignment = Pos.CENTER
|
alignment = Pos.CENTER
|
||||||
text = "Plannervio"
|
text = "Plannervio"
|
||||||
}
|
}
|
||||||
this += JFXTextField().apply {
|
this += jfxtextfield {
|
||||||
vgrow = Priority.NEVER
|
vgrow = Priority.NEVER
|
||||||
isLabelFloat = true
|
isLabelFloat = true
|
||||||
promptText = messages["search"] // TODO
|
promptText = messages[MainViewKeywords.NAV_SEARCH_LABEL]
|
||||||
vboxMargin(horizontal = 16, vertical = 20)
|
vboxMargin(horizontal = 16, vertical = 20)
|
||||||
}
|
}
|
||||||
this += tabButton(Tab.BOARDS, presenter::onTabButtonClick).apply {
|
for (t in Tab.values()) {
|
||||||
presenter.switchTab(this) // Default tab
|
this += tabButton {
|
||||||
|
tab = t
|
||||||
|
alignment = Pos.BOTTOM_LEFT
|
||||||
|
textFill = Color.RED
|
||||||
|
graphic = hbox {
|
||||||
|
paddingTop = 2
|
||||||
|
this += pane {
|
||||||
|
hboxMargin(top = 8)
|
||||||
|
addClass(Style.jfxSvgGlyphWrapper)
|
||||||
|
this += svgGlyph(t.icon, size = 16.0, color = Color.WHITE)
|
||||||
|
}
|
||||||
|
this += Label(messages[t.messageKey])
|
||||||
|
}
|
||||||
|
setOnMouseClicked { presenter.onTabButtonClick(this) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this += tabButton(Tab.FAVORITES, presenter::onTabButtonClick)
|
|
||||||
this += tabButton(Tab.HISTORY, presenter::onTabButtonClick)
|
|
||||||
this += tabButton(Tab.SETTINGS, presenter::onTabButtonClick)
|
|
||||||
}
|
}
|
||||||
|
center = vbox {
|
||||||
|
isFillWidth = true
|
||||||
|
padding = insets(10.0)
|
||||||
|
this += activeTabView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetCurrentTab() {
|
||||||
|
val view = presenter.activeView
|
||||||
|
activeTabView = view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoardsView : View() {
|
||||||
|
private val presenter by inject<BoardsTabViewPresenter>()
|
||||||
|
private val headerRowHeightRatio = 1.15
|
||||||
|
private val rowHeight = 52.0
|
||||||
|
|
||||||
|
init {
|
||||||
|
messages = messages("lang.views.main")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultBoardLocation = messages[Keywords.BOARD_LOCATION_LOCAL]
|
||||||
|
|
||||||
|
override val root = tableview(presenter.boards.toObservable()) {
|
||||||
|
fixedCellSize = rowHeight
|
||||||
|
prefHeightProperty().bind(fixedCellSizeProperty() * (Bindings.size(items) + headerRowHeightRatio))
|
||||||
|
|
||||||
|
columns.setAll(
|
||||||
|
column(messages[Keywords.BOARD_NAME], String::class) {
|
||||||
|
minWidth = 150.0
|
||||||
|
value { it.value.name }
|
||||||
|
},
|
||||||
|
column(messages[Keywords.BOARD_LOCATION], String::class) {
|
||||||
|
value { it.value.location ?: defaultBoardLocation }
|
||||||
|
minWidth = 150.0
|
||||||
|
setComparator { a, b ->
|
||||||
|
when {
|
||||||
|
a != defaultBoardLocation && b == defaultBoardLocation -> 1
|
||||||
|
a == defaultBoardLocation && b != defaultBoardLocation -> -1
|
||||||
|
a == defaultBoardLocation && b == defaultBoardLocation -> 0
|
||||||
|
else -> a.compareTo(b) * -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
column(messages[Keywords.BOARD_PROTECTION], HBox::class) {
|
||||||
|
fixedWidth(120.0)
|
||||||
|
value {
|
||||||
|
if (it.value.isRemote) {
|
||||||
|
val private = it.value.isPrivate
|
||||||
|
LabeledIcon(
|
||||||
|
if (private) SvgIconName.LOCK else SvgIconName.EARTH,
|
||||||
|
if (private) messages[Keywords.BOARD_PROTECTION_PRIVATE] else messages[Keywords.BOARD_PROTECTION_PUBLIC]
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
column(messages[Keywords.BOARD_FAVORITE], ToggleableIconButton::class) {
|
||||||
|
fixedWidth(100.0)
|
||||||
|
value {
|
||||||
|
toggleableIconButton(SvgIconName.STAR, Color.GOLD, Color.GREY) {
|
||||||
|
enabled = it.value.favorite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
column(messages[Keywords.BOARD_LAST_VISITED], String::class) {
|
||||||
|
value { it.value.lastVisited?.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)) }
|
||||||
|
fixedWidth(150.0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Prevent selection
|
||||||
|
selectionModel.selectedIndexProperty().addListener { _, _, _ -> Platform.runLater { selectionModel.clearSelection() } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FavoritesView : View() {
|
||||||
|
override val root = pane {
|
||||||
|
this += Label("FAVORITES")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecentsView : View() {
|
||||||
|
override val root = pane {
|
||||||
|
this += Label("RECENTS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsView : View() {
|
||||||
|
override val root = pane {
|
||||||
|
this += Label("SETTINGS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainViewPresenter : Controller() {
|
class MainViewPresenter : Controller() {
|
||||||
private var activeTabButton: TabButton? = null
|
private val view: MainView by inject()
|
||||||
|
|
||||||
|
private val boardSelectionView = find<BoardsView>()
|
||||||
|
private val favoritesView = find<FavoritesView>()
|
||||||
|
private val recentView = find<RecentsView>()
|
||||||
|
private val settingsView = find<SettingsView>()
|
||||||
|
|
||||||
|
private val defaultActiveView = boardSelectionView
|
||||||
|
|
||||||
|
/** The currently active [TabButton]. **/
|
||||||
|
var activeTabButton: TabButton? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/** The currently active [Tab]. */
|
||||||
|
private val activeTab: Tab?
|
||||||
|
get() = activeTabButton?.tab
|
||||||
|
|
||||||
|
/** The currently active view. */
|
||||||
|
val activeView: View
|
||||||
|
get() {
|
||||||
|
val tab = activeTab
|
||||||
|
return if (tab == null) defaultActiveView else when (tab) {
|
||||||
|
Tab.BOARDS -> boardSelectionView
|
||||||
|
Tab.SETTINGS -> settingsView
|
||||||
|
Tab.FAVORITES -> favoritesView
|
||||||
|
Tab.HISTORY -> recentView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Called when a [TabButton] has been clicked. */
|
/** Called when a [TabButton] has been clicked. */
|
||||||
fun onTabButtonClick(button: TabButton) {
|
fun onTabButtonClick(button: TabButton) {
|
||||||
|
@ -54,32 +199,38 @@ class MainViewPresenter : Controller() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Switch the active tab to [button]'s tab. */
|
/** Switch the active tab to [button]'s tab. */
|
||||||
fun switchTab(button: TabButton) {
|
private fun switchTab(button: TabButton) {
|
||||||
activeTabButton?.active = false
|
activeTabButton?.active = false
|
||||||
button.active = true
|
button.active = true
|
||||||
activeTabButton = button
|
activeTabButton = button
|
||||||
|
view.resetCurrentTab()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The [TabButton] is a button linked to a [tab]. */
|
abstract class TabViewPresenter : Controller() {
|
||||||
class TabButton(tab: Tab, active: Boolean = false) : JFXButton() {
|
protected abstract val view: View
|
||||||
/** Specifies the [Tab] linked to the button. */
|
protected val boardService: IBoardService by kodeinDI().instance()
|
||||||
val tabProperty by lazy { SimpleObjectProperty(tab) }
|
|
||||||
var tab: Tab by tabProperty
|
|
||||||
|
|
||||||
/** Specifies if the button is active. */
|
|
||||||
val activeProperty = SimpleBooleanProperty(this, "active", active)
|
|
||||||
var active: Boolean
|
|
||||||
get() = activeProperty.get()
|
|
||||||
set(value) {
|
|
||||||
toggleClass(Style.active, value)
|
|
||||||
activeProperty.set(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Tab(val icon: SvgPathName, val i18nText: String) {
|
class BoardsTabViewPresenter : TabViewPresenter() {
|
||||||
BOARDS(SvgPathName.FORMAT_LIST_BULLETED, "tab.boards.label"),
|
override val view: BoardsView by inject()
|
||||||
FAVORITES(SvgPathName.STAR, "tab.favorites.label"),
|
|
||||||
HISTORY(SvgPathName.HISTORY, "tab.history.label"),
|
val boards: List<Board>
|
||||||
SETTINGS(SvgPathName.COG, "tab.settings.label")
|
get() = boardService.getBoards()
|
||||||
|
}
|
||||||
|
|
||||||
|
object MainViewKeywords {
|
||||||
|
const val NAV_SEARCH_LABEL = "nav.search.label"
|
||||||
|
const val NAV_TAB_BOARDS_LABEL = "nav.tab.boards.label"
|
||||||
|
const val NAV_TAB_FAVORITES_LABEL = "nav.tab.favorites.label"
|
||||||
|
const val NAV_TAB_HISTORY_LABEL = "nav.tab.history.label"
|
||||||
|
const val NAV_TAB_SETTINGS_LABEL = "nav.tab.settings.label"
|
||||||
|
const val BOARDS_VIEW_OPEN_LABEL = "view.boards.open.label"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Tab(val icon: SvgIconName, val messageKey: String) {
|
||||||
|
BOARDS(SvgIconName.FORMAT_LIST_BULLETED, MainViewKeywords.NAV_TAB_BOARDS_LABEL),
|
||||||
|
FAVORITES(SvgIconName.STAR, MainViewKeywords.NAV_TAB_FAVORITES_LABEL),
|
||||||
|
HISTORY(SvgIconName.HISTORY, MainViewKeywords.NAV_TAB_HISTORY_LABEL),
|
||||||
|
SETTINGS(SvgIconName.COG, MainViewKeywords.NAV_TAB_SETTINGS_LABEL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,105 +1,210 @@
|
||||||
package dev.fyloz.plannervio.ui.view
|
package dev.fyloz.plannervio.ui.view
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.service.locateI18nService
|
import com.beust.klaxon.Klaxon
|
||||||
import dev.fyloz.plannervio.core.service.svgGlyph
|
import com.jfoenix.controls.JFXButton
|
||||||
|
import com.jfoenix.svg.SVGGlyph
|
||||||
|
import dev.fyloz.plannervio.ui.Plannervio
|
||||||
|
import dev.fyloz.plannervio.ui.i18n.MergedResourceBundle
|
||||||
|
import dev.fyloz.plannervio.ui.logger
|
||||||
import dev.fyloz.plannervio.ui.style.Style
|
import dev.fyloz.plannervio.ui.style.Style
|
||||||
|
import javafx.beans.property.ReadOnlyStringWrapper
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty
|
||||||
|
import javafx.beans.property.SimpleObjectProperty
|
||||||
|
import javafx.beans.property.SimpleStringProperty
|
||||||
|
import javafx.event.EventTarget
|
||||||
import javafx.geometry.Pos
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.control.Label
|
import javafx.scene.Node
|
||||||
import javafx.scene.layout.HBox
|
import javafx.scene.layout.HBox
|
||||||
import javafx.scene.layout.Pane
|
|
||||||
import javafx.scene.layout.Region
|
|
||||||
import javafx.scene.layout.VBox
|
import javafx.scene.layout.VBox
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
|
|
||||||
import kfoenix.jfxrippler
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
const val KEYWORDS_BUNDLE_NAME = "lang.keywords"
|
||||||
* A view. Inheriting this class allows to automatically link the FXML file and the CSS files for the current theme to the fragment.
|
|
||||||
* If a presenter class is provided, the presenter will also be created.
|
|
||||||
*
|
|
||||||
* @constructor Initialize the view, linking the FXML and the CSS files.
|
|
||||||
*/
|
|
||||||
abstract class AbstractView : View() {
|
|
||||||
/**
|
|
||||||
* Gets the name of the view's files in kebab-case.
|
|
||||||
*/
|
|
||||||
val filename by lazy {
|
|
||||||
javaClass.simpleName
|
|
||||||
.split("(?=[A-Z])".toRegex())
|
|
||||||
.map { it.toLowerCase() }
|
|
||||||
.filter { it.isNotEmpty() }
|
|
||||||
.filter { it != "view" }
|
|
||||||
.joinToString("-")
|
|
||||||
}
|
|
||||||
|
|
||||||
protected val i18nService = locateI18nService(filename)
|
/** A SVG icon. */
|
||||||
|
data class SvgIcon(val name: String, val author: String, val path: String)
|
||||||
|
|
||||||
init {
|
/** The path to the file containing svg icons. */
|
||||||
messages = i18nService.bundle
|
private const val SVG_ICONS_FILE_PATH = "/images/icons.json"
|
||||||
}
|
|
||||||
|
|
||||||
// final override val root: Pane = loadFXML("/fxml/views/${filename}.fxml")
|
/** A [List] containing all available [SvgIcon]s. */
|
||||||
|
val svgIcons = loadSvgIcons()
|
||||||
|
|
||||||
|
/** Load all [SvgIcon]s from the file at the path specified by [SVG_ICONS_FILE_PATH]. */
|
||||||
|
private fun loadSvgIcons(): List<SvgIcon> {
|
||||||
|
val doc = Plannervio::class.java.getResource(SVG_ICONS_FILE_PATH).readText()
|
||||||
|
val allIcons: List<SvgIcon> = Klaxon().parseArray(doc) ?: throw IllegalStateException("Could not load SVG icons.")
|
||||||
|
logger.debug("Loaded ${allIcons.size} SVG icons.")
|
||||||
|
return allIcons
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Create a [SVGGlyph] for the given [svgIconName] with the given [size] and [color]. */
|
||||||
* A fragment of the view. Inheriting this class allows to automatically link the FXML file and the CSS files for the current theme to the fragment.
|
fun svgGlyph(svgIconName: SvgIconName, size: Double = 16.0, color: Color = Color.BLACK): SVGGlyph {
|
||||||
*
|
val svgIcon = svgIcons.firstOrNull { svgIconName.iconName == it.name }
|
||||||
* @property view The view which the fragment is linked to.
|
?: throw IllegalStateException("Cannot create a SVGGlyph for the icon ${svgIconName.iconName} because no icon loaded was found with this name.")
|
||||||
* @constructor Initialize the fragment, linking the FXML and the CSS files.
|
val glyph = SVGGlyph(svgIcon.path, color)
|
||||||
*/
|
glyph.size = size
|
||||||
abstract class AbstractFragment(val view: AbstractView? = null) : Fragment() {
|
return glyph
|
||||||
protected val i18nService = locateI18nService(view?.filename)
|
|
||||||
|
|
||||||
init {
|
|
||||||
messages = i18nService.bundle
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shorthand to create a [TabButton] in a [View]. */
|
/** A button linked to a [tab]. */
|
||||||
fun View.tabButton(tab: Tab, onClick: (TabButton) -> Unit): TabButton {
|
class TabButton(tab: Tab, active: Boolean = false) : JFXButton() {
|
||||||
return TabButton(tab).apply {
|
/** The [Tab] linked to the button. */
|
||||||
alignment = Pos.BOTTOM_LEFT
|
val tabProperty by lazy { SimpleObjectProperty(tab) }
|
||||||
textFill = Color.RED
|
var tab: Tab by tabProperty
|
||||||
graphic = hbox {
|
|
||||||
paddingTop = 2
|
/** If the button is active. */
|
||||||
this += pane {
|
val activeProperty = SimpleBooleanProperty(this, "active", active)
|
||||||
hboxMargin(top = 8)
|
var active: Boolean
|
||||||
addClass(Style.jfxSvgGlyphWrapper)
|
get() = activeProperty.get()
|
||||||
this += svgGlyph(tab.icon, size = 16.0, color = Color.WHITE)!!
|
set(value) {
|
||||||
}
|
toggleClass(Style.active, value)
|
||||||
this += Label(messages[tab.i18nText])
|
activeProperty.set(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A [SvgIcon] and a [Label] wrapped in a [HBox]. */
|
||||||
|
class LabeledIcon(svgIconName: SvgIconName, text: String, iconSize: Double = 16.0, iconColor: Color = Color.BLACK) : HBox() {
|
||||||
|
/** The name of the [SvgIcon]. */
|
||||||
|
val iconNameProperty by lazy { ReadOnlyStringWrapper(svgIconName.iconName) }
|
||||||
|
val iconName: String by iconNameProperty
|
||||||
|
|
||||||
|
/** The text of the [Label]. */
|
||||||
|
val textProperty by lazy { SimpleStringProperty(text) }
|
||||||
|
var text: String by textProperty
|
||||||
|
|
||||||
|
override fun getUserAgentStylesheet(): String = LabeledIconStyle().base64URL.toExternalForm()
|
||||||
|
|
||||||
|
init {
|
||||||
|
addClass(LabeledIconStyle.labeledIcon)
|
||||||
|
children.setAll(
|
||||||
|
svgGlyph(svgIconName, iconSize, iconColor),
|
||||||
|
label(textProperty)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LabeledIconStyle : Stylesheet() {
|
||||||
|
companion object {
|
||||||
|
val labeledIcon by cssclass()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
labeledIcon {
|
||||||
|
alignment = Pos.CENTER_LEFT
|
||||||
|
|
||||||
|
label {
|
||||||
|
padding = box(0.px, 0.px, 0.px, 10.px)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setOnMouseClicked { onClick(this) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [all] margins of a [Region] contained in a [VBox] to the given size. */
|
/** A toggleable icon [JFXButton], with an [enabledColor] and a [disabledColor]. */
|
||||||
fun Region.vboxMargin(all: Number = 0) {
|
class ToggleableIconButton(svgIconName: SvgIconName, enabledColor: Color, disabledColor: Color, backgroundColor: Color = Color.WHITE, size: Double = 16.0, enabled: Boolean = false) : JFXButton() {
|
||||||
|
val enabledProperty by lazy { SimpleBooleanProperty(enabled) }
|
||||||
|
var enabled by enabledProperty
|
||||||
|
|
||||||
|
val enabledColorProperty by lazy { SimpleObjectProperty(enabledColor) }
|
||||||
|
val enabledColor: Color by enabledColorProperty
|
||||||
|
|
||||||
|
val disabledColorProperty by lazy { SimpleObjectProperty(disabledColor) }
|
||||||
|
val disabledColor: Color by disabledColorProperty
|
||||||
|
|
||||||
|
private val svgIcon: SVGGlyph
|
||||||
|
private val iconColor: Color
|
||||||
|
get() = if (enabled) enabledColor else disabledColor
|
||||||
|
|
||||||
|
init {
|
||||||
|
svgIcon = svgGlyph(svgIconName, size, color = iconColor)
|
||||||
|
graphic = svgIcon
|
||||||
|
ripplerFill = backgroundColor
|
||||||
|
|
||||||
|
enabledProperty.onChange {
|
||||||
|
this.enabled = it
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnMouseClicked {
|
||||||
|
this.enabled = !this.enabled
|
||||||
|
toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggle() {
|
||||||
|
svgIcon.fill = iconColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DSL for [TabButton]s. */
|
||||||
|
fun EventTarget.tabButton(tab: Tab = Tab.BOARDS, op: TabButton.() -> Unit = {}) = TabButton(tab).attachTo(this, op)
|
||||||
|
|
||||||
|
/** DSL for [ToggleableIconButton]s. */
|
||||||
|
fun EventTarget.toggleableIconButton(
|
||||||
|
svgIconName: SvgIconName,
|
||||||
|
enabledColor: Color,
|
||||||
|
disabledColor: Color,
|
||||||
|
backgroundColor: Color = Color.WHITE,
|
||||||
|
size: Double = 16.0,
|
||||||
|
enabled: Boolean = false,
|
||||||
|
op: ToggleableIconButton.() -> Unit = {}) =
|
||||||
|
ToggleableIconButton(svgIconName, enabledColor, disabledColor, backgroundColor, size, enabled).attachTo(this, op)
|
||||||
|
|
||||||
|
fun messages(vararg names: String): ResourceBundle {
|
||||||
|
val bundle = MergedResourceBundle(ResourceBundle.getBundle(KEYWORDS_BUNDLE_NAME))
|
||||||
|
bundle + names.map { ResourceBundle.getBundle(it) }
|
||||||
|
return bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets [all] margins of a [Node] contained in a [VBox] to the given size. */
|
||||||
|
fun Node.vboxMargin(all: Number = 0) {
|
||||||
this.vboxMargin(all, all)
|
this.vboxMargin(all, all)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [VBox] to the given size. */
|
/** Sets [horizontal] and [vertical] margins of a [Node] contained in a [VBox] to the given size. */
|
||||||
fun Region.vboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
fun Node.vboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||||
this.vboxMargin(vertical, horizontal, vertical, horizontal)
|
this.vboxMargin(vertical, horizontal, vertical, horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [VBox] to the given size. */
|
/** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [VBox] to the given size. */
|
||||||
fun Region.vboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
fun Node.vboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
||||||
VBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
|
VBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [all] margins of a [Region] contained in a [HBox] to the given size. */
|
/** Sets [all] margins of a [Node] contained in a [HBox] to the given size. */
|
||||||
fun Region.hboxMargin(all: Number = 0) {
|
fun Node.hboxMargin(all: Number = 0) {
|
||||||
this.hboxMargin(all, all)
|
this.hboxMargin(all, all)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [HBox] to the given size. */
|
/** Sets [horizontal] and [vertical] margins of a [Node] contained in a [HBox] to the given size. */
|
||||||
fun Region.hboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
fun Node.hboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||||
this.hboxMargin(vertical, horizontal, vertical, horizontal)
|
this.hboxMargin(vertical, horizontal, vertical, horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [HBox] to the given size. */
|
/** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [HBox] to the given size. */
|
||||||
fun Region.hboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
fun Node.hboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
||||||
HBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
|
HBox.setMargin(this, insets(top.toDouble(), right.toDouble(), bottom.toDouble(), left.toDouble()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Keywords {
|
||||||
|
const val BOARD_NAME = "board.name"
|
||||||
|
const val BOARD_DESCRIPTION = "board.description"
|
||||||
|
const val BOARD_LAST_VISITED = "board.lastVisited"
|
||||||
|
const val BOARD_FAVORITE = "board.favorite"
|
||||||
|
const val BOARD_PROTECTION = "board.protection"
|
||||||
|
const val BOARD_PROTECTION_PRIVATE = "board.protection.private"
|
||||||
|
const val BOARD_PROTECTION_PUBLIC = "board.protection.public"
|
||||||
|
const val BOARD_LOCATION = "board.location"
|
||||||
|
const val BOARD_LOCATION_LOCAL = "board.location.local"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An enum containing the name of used [SvgIcon]s, for easy access. Not all available icons are specified in this enum. */
|
||||||
|
enum class SvgIconName(val iconName: String) {
|
||||||
|
COG("cog"),
|
||||||
|
EARTH("earth"),
|
||||||
|
FORMAT_LIST_BULLETED("format-list-bulleted"),
|
||||||
|
HISTORY("history"),
|
||||||
|
LOCK("lock"),
|
||||||
|
STAR("star")
|
||||||
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,9 @@
|
||||||
members={0} Members
|
board.name=Name
|
||||||
private=Private
|
board.description=Description
|
||||||
public=Public
|
board.lastVisited=Last visit
|
||||||
name=Name
|
board.favorite=Favorite
|
||||||
description=Description
|
board.protection=Protection
|
||||||
open=OPEN
|
board.location.local=This computer
|
||||||
search=Search...
|
board.location=Location
|
||||||
|
board.protection.private=Private
|
||||||
|
board.protection.public=Public
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
private=Privé
|
board.name=Nom
|
||||||
public=Publique
|
board.description=Description
|
||||||
members={0} Membres
|
board.lastVisited=Dernière visite
|
||||||
name=Nom
|
board.favorite=Favoris
|
||||||
description=Description
|
board.protection=Protection
|
||||||
open=OUVRIR
|
board.location.local=Cet ordinateur
|
||||||
search=Recherche...
|
board.location=Emplacement
|
||||||
|
board.protection.private=Privé
|
||||||
|
board.protection.public=Publique
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
tab.boards.label=Boards
|
|
||||||
tab.favorites.label=Favorites
|
|
||||||
tab.history.label=Recents
|
|
||||||
tab.settings.label=Settings
|
|
|
@ -1,4 +0,0 @@
|
||||||
tab.boards.label=Tableaux
|
|
||||||
tab.favorites.label=Favoris
|
|
||||||
tab.history.label=Récents
|
|
||||||
tab.settings.label=Paramètres
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
nav.search.label=Search...
|
||||||
|
nav.tab.boards.label=Boards
|
||||||
|
nav.tab.favorites.label=Favorites
|
||||||
|
nav.tab.history.label=Recents
|
||||||
|
nav.tab.settings.label=Settings
|
||||||
|
view.boards.open.label=Open
|
|
@ -0,0 +1,6 @@
|
||||||
|
nav.search.label=Recherche...
|
||||||
|
nav.tab.boards.label=Tableaux
|
||||||
|
nav.tab.favorites.label=Favoris
|
||||||
|
nav.tab.history.label=Récents
|
||||||
|
nav.tab.settings.label=Paramètres
|
||||||
|
view.boards.open.label=Ouvrir
|
|
@ -1,67 +0,0 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.BackgroundImage
|
|
||||||
import io.mockk.*
|
|
||||||
import org.junit.jupiter.api.BeforeEach
|
|
||||||
import org.junit.jupiter.api.Nested
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import java.io.InputStream
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
class BackgroundImageServiceTest {
|
|
||||||
private val service: BackgroundImageService = spyk()
|
|
||||||
private val fakeInputStream: InputStream = mockk()
|
|
||||||
private val backgroundImage = BackgroundImage("image", true)
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
internal fun setUp() {
|
|
||||||
clearAllMocks()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class LoadImage {
|
|
||||||
@Test
|
|
||||||
fun `Image is loaded`() {
|
|
||||||
every { service.getImageInputStream(backgroundImage) } returns fakeInputStream
|
|
||||||
|
|
||||||
val found = service.loadImage(backgroundImage)
|
|
||||||
|
|
||||||
assertNotNull(found)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `Image is null`() {
|
|
||||||
every { service.getImageInputStream(backgroundImage) } returns null
|
|
||||||
|
|
||||||
val found = service.loadImage(backgroundImage)
|
|
||||||
|
|
||||||
assertNull(found)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
inner class GetImageInputStream {
|
|
||||||
@Test
|
|
||||||
fun `call getBundledImageInputStream when bundled`() {
|
|
||||||
every { service.getBundledImageInputStream(backgroundImage) } returns fakeInputStream
|
|
||||||
|
|
||||||
val found = service.getImageInputStream(backgroundImage)
|
|
||||||
|
|
||||||
verify(exactly = 1) { service.getBundledImageInputStream(backgroundImage) }
|
|
||||||
assertEquals(fakeInputStream, found)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `call getCustomImageInputStream when custom`() {
|
|
||||||
val customBackgroundImage = BackgroundImage("image", false)
|
|
||||||
every { service.getCustomImageInputStream(customBackgroundImage) } returns fakeInputStream
|
|
||||||
|
|
||||||
val found = service.getImageInputStream(customBackgroundImage)
|
|
||||||
|
|
||||||
verify(exactly = 1) { service.getCustomImageInputStream(customBackgroundImage) }
|
|
||||||
assertEquals(fakeInputStream, found)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.fyloz.plannervio.core.service
|
package dev.fyloz.plannervio.core.service
|
||||||
|
|
||||||
import dev.fyloz.plannervio.core.model.Board
|
import dev.fyloz.plannervio.core.model.Board
|
||||||
import dev.fyloz.plannervio.core.model.boardFactory
|
import dev.fyloz.plannervio.core.model.board
|
||||||
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
import dev.fyloz.plannervio.core.repository.IBoardRepository
|
||||||
import io.mockk.clearAllMocks
|
import io.mockk.clearAllMocks
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
@ -15,13 +15,13 @@ import org.kodein.di.singleton
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class BoardServiceTest {
|
class BoardServiceImplTest {
|
||||||
private val di: DI = DI {
|
private val di: DI = DI {
|
||||||
bind<IBoardRepository>() with singleton { repository }
|
bind<IBoardRepository>() with singleton { repository }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val repository: IBoardRepository = mockk()
|
private val repository: IBoardRepository = mockk()
|
||||||
private val service: IBoardService = BoardService(di)
|
private val service: IBoardService = BoardServiceImpl(di)
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
@ -33,8 +33,8 @@ class BoardServiceTest {
|
||||||
@Test
|
@Test
|
||||||
fun `all boards are included`() {
|
fun `all boards are included`() {
|
||||||
val boards: List<Board> = listOf(
|
val boards: List<Board> = listOf(
|
||||||
boardFactory(0L),
|
board(0L),
|
||||||
boardFactory(1L)
|
board(1L)
|
||||||
)
|
)
|
||||||
every { repository.findAll() } returns boards
|
every { repository.findAll() } returns boards
|
||||||
|
|
Loading…
Reference in New Issue