Simple Board table.
This commit is contained in:
parent
ad68d10a3c
commit
d6bc50d9f6
|
@ -22,10 +22,10 @@ repositories {
|
|||
dependencies {
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
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-framework-tornadofx-jvm:7.0.0")
|
||||
implementation("io.github.microutils:kotlin-logging:1.5.9")
|
||||
implementation("org.kodein.di:kodein-di-framework-tornadofx-jvm:7.1.0")
|
||||
implementation("io.github.microutils:kotlin-logging-jvm:2.0.3")
|
||||
implementation("org.slf4j:slf4j-simple:1.7.26")
|
||||
// implementation("org.dizitart:potassium-nitrite:3.4.2")
|
||||
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
|
||||
|
||||
data class User(
|
||||
val username: String,
|
||||
val email: String,
|
||||
val password: String
|
||||
var username: String,
|
||||
var email: 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
|
||||
|
||||
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.repository.IBoardRepository
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class MemoryBoardRepository : IBoardRepository {
|
||||
private val boards = mutableListOf<Board>()
|
||||
|
||||
init {
|
||||
save(
|
||||
boardFactory(
|
||||
0L,
|
||||
"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.",
|
||||
BackgroundImage("bundled_background_0.jpg")
|
||||
)
|
||||
board {
|
||||
id = 0L
|
||||
name = "Board 1"
|
||||
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."
|
||||
favorite = true
|
||||
lastVisited = LocalDateTime.now()
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
1L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 1L
|
||||
name = "Board 2"
|
||||
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."
|
||||
}
|
||||
)
|
||||
save(
|
||||
privateBoardFactory(
|
||||
2L,
|
||||
"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.",
|
||||
BackgroundImage("bundled_background_3.jpg"),
|
||||
"password",
|
||||
mutableListOf(
|
||||
User("username", "username@email.com", "password")
|
||||
)
|
||||
)
|
||||
board {
|
||||
id = 2L
|
||||
name = "Board 3"
|
||||
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."
|
||||
isRemote = true
|
||||
location = "plannervio.fyloz.dev"
|
||||
isPrivate = true
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
3L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 3L
|
||||
name = "Board 4"
|
||||
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."
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
4L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 4L
|
||||
name = "Board 5"
|
||||
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."
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
5L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 5L
|
||||
name = "Board 6"
|
||||
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."
|
||||
isRemote = true
|
||||
location = "todo.google.ca"
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
6L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 6L
|
||||
name = "Board 7"
|
||||
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."
|
||||
}
|
||||
)
|
||||
save(
|
||||
boardFactory(
|
||||
7L,
|
||||
"test",
|
||||
"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")
|
||||
)
|
||||
board {
|
||||
id = 7L
|
||||
name = "Board 8"
|
||||
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."
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.instance
|
||||
|
||||
class BoardService(di: DI) : IBoardService {
|
||||
override val repository: IBoardRepository by di.instance()
|
||||
/** A service for boards. */
|
||||
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> {
|
||||
return repository.findAll()
|
||||
}
|
||||
|
||||
}
|
|
@ -28,7 +28,7 @@ private const val keywordsServiceKey = "KEYWORDS"
|
|||
private val keywordsBundle: ResourceBundle = getBundle(keywordsServiceKey.toLowerCase())
|
||||
|
||||
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.memory.MemoryBoardRepository
|
||||
import dev.fyloz.plannervio.core.service.BackgroundImageService
|
||||
import dev.fyloz.plannervio.core.service.BoardService
|
||||
import dev.fyloz.plannervio.core.service.IBackgroundImageService
|
||||
import dev.fyloz.plannervio.core.service.BoardServiceImpl
|
||||
import dev.fyloz.plannervio.core.service.IBoardService
|
||||
import dev.fyloz.plannervio.ui.style.Style
|
||||
import dev.fyloz.plannervio.ui.view.MainView
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.bind
|
||||
|
@ -22,14 +22,18 @@ fun main(args: Array<String>) {
|
|||
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 {
|
||||
override val di: DI
|
||||
get() = DI {
|
||||
installTornadoSource()
|
||||
|
||||
bind<IBoardRepository>() with singleton { MemoryBoardRepository() }
|
||||
bind<IBoardService>() with singleton { BoardService(di) }
|
||||
bind<IBackgroundImageService>() with singleton { BackgroundImageService() }
|
||||
bind<IBoardService>() with singleton { BoardServiceImpl(di) }
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
@ -8,28 +8,16 @@ import java.util.*
|
|||
* @param initBundles The bundles contained by the merged bundle
|
||||
*/
|
||||
class MergedResourceBundle(vararg initBundles: ResourceBundle) : ResourceBundle() {
|
||||
private val bundles: MutableList<ResourceBundle>
|
||||
private val bundles: MutableList<ResourceBundle> = initBundles.toMutableList()
|
||||
|
||||
init {
|
||||
bundles = initBundles.toMutableList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a bundle to contained bundles.
|
||||
*
|
||||
* @param bundle The bundle to add
|
||||
*/
|
||||
/** Adds a [bundle] to the contained bundles. */
|
||||
operator fun plus(bundle: ResourceBundle) {
|
||||
bundles + bundle
|
||||
bundles += bundle
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a bundle from contained bundles.
|
||||
*
|
||||
* @param bundle The bundle to remove
|
||||
*/
|
||||
operator fun minus(bundle: ResourceBundle) {
|
||||
bundles - bundle
|
||||
/** Adds [bundles] to the contained bundles. */
|
||||
operator fun plus(bundles: Iterable<ResourceBundle>) {
|
||||
this.bundles += bundles
|
||||
}
|
||||
|
||||
override fun handleGetObject(key: String): Any? {
|
||||
|
|
|
@ -1,39 +1,45 @@
|
|||
package dev.fyloz.plannervio.ui.style
|
||||
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Cursor
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.paint.Paint
|
||||
import javafx.scene.text.TextAlignment
|
||||
import kfoenix.JFXStylesheet.Companion.jfxButton
|
||||
import kfoenix.JFXStylesheet.Companion.jfxListView
|
||||
import kfoenix.JFXStylesheet.Companion.jfxRippler
|
||||
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
|
||||
import kfoenix.JFXStylesheet.Companion.jfxVerticalGap
|
||||
import tornadofx.*
|
||||
|
||||
val primaryTheme = mapOf(
|
||||
50 to c("#fcf2e7"),
|
||||
100 to c("#f8dec3"),
|
||||
200 to c("#f3c89c"),
|
||||
300 to c("#eeb274"),
|
||||
400 to c("#eaa256"),
|
||||
500 to c("#e69138"),
|
||||
600 to c("#e38932"),
|
||||
700 to c("#df7e2b"),
|
||||
800 to c("#db7424"),
|
||||
900 to c("#d56217")
|
||||
50 to c("#fcf2e7"),
|
||||
100 to c("#f8dec3"),
|
||||
200 to c("#f3c89c"),
|
||||
300 to c("#eeb274"),
|
||||
400 to c("#eaa256"),
|
||||
500 to c("#e69138"),
|
||||
600 to c("#e38932"),
|
||||
700 to c("#df7e2b"),
|
||||
800 to c("#db7424"),
|
||||
900 to c("#d56217")
|
||||
)
|
||||
|
||||
val accentTheme = mapOf(
|
||||
50 to c("#e8f0f8"),
|
||||
100 to c("#c5daee"),
|
||||
200 to c("#9ec2e3"),
|
||||
300 to c("#77aad7"),
|
||||
400 to c("#5a97cf"),
|
||||
500 to c("#3d85c6"),
|
||||
600 to c("#377dc0"),
|
||||
700 to c("#2f72b9"),
|
||||
800 to c("#2768b1"),
|
||||
900 to c("#1a55a4")
|
||||
50 to c("#e8f0f8"),
|
||||
100 to c("#c5daee"),
|
||||
200 to c("#9ec2e3"),
|
||||
300 to c("#77aad7"),
|
||||
400 to c("#5a97cf"),
|
||||
500 to c("#3d85c6"),
|
||||
600 to c("#377dc0"),
|
||||
700 to c("#2f72b9"),
|
||||
800 to c("#2768b1"),
|
||||
900 to c("#1a55a4")
|
||||
)
|
||||
|
||||
val background = Color.WHITE
|
||||
|
||||
class Style : Stylesheet() {
|
||||
|
||||
companion object {
|
||||
|
@ -47,8 +53,10 @@ class Style : Stylesheet() {
|
|||
// Classes
|
||||
val active by cssclass()
|
||||
val appNameLabel by cssclass()
|
||||
val navBar by cssclass()
|
||||
val centered by cssclass()
|
||||
val jfxSvgGlyphWrapper by cssclass()
|
||||
val navBar by cssclass()
|
||||
val test by cssclass()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import dev.fyloz.plannervio.core.service.SvgPathName
|
||||
import dev.fyloz.plannervio.core.model.Board
|
||||
import dev.fyloz.plannervio.core.service.IBoardService
|
||||
import dev.fyloz.plannervio.ui.style.Style
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.layout.HBox
|
||||
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 java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
/** The app's main view */
|
||||
class MainView : AbstractView() {
|
||||
class MainView : View() {
|
||||
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 {
|
||||
left = vbox {
|
||||
|
@ -24,24 +40,153 @@ class MainView : AbstractView() {
|
|||
alignment = Pos.CENTER
|
||||
text = "Plannervio"
|
||||
}
|
||||
this += JFXTextField().apply {
|
||||
this += jfxtextfield {
|
||||
vgrow = Priority.NEVER
|
||||
isLabelFloat = true
|
||||
promptText = messages["search"] // TODO
|
||||
promptText = messages[MainViewKeywords.NAV_SEARCH_LABEL]
|
||||
vboxMargin(horizontal = 16, vertical = 20)
|
||||
}
|
||||
this += tabButton(Tab.BOARDS, presenter::onTabButtonClick).apply {
|
||||
presenter.switchTab(this) // Default tab
|
||||
for (t in Tab.values()) {
|
||||
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() {
|
||||
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. */
|
||||
fun onTabButtonClick(button: TabButton) {
|
||||
|
@ -54,32 +199,38 @@ class MainViewPresenter : Controller() {
|
|||
}
|
||||
|
||||
/** Switch the active tab to [button]'s tab. */
|
||||
fun switchTab(button: TabButton) {
|
||||
private fun switchTab(button: TabButton) {
|
||||
activeTabButton?.active = false
|
||||
button.active = true
|
||||
activeTabButton = button
|
||||
view.resetCurrentTab()
|
||||
}
|
||||
}
|
||||
|
||||
/** The [TabButton] is a button linked to a [tab]. */
|
||||
class TabButton(tab: Tab, active: Boolean = false) : JFXButton() {
|
||||
/** Specifies the [Tab] linked to the button. */
|
||||
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)
|
||||
}
|
||||
abstract class TabViewPresenter : Controller() {
|
||||
protected abstract val view: View
|
||||
protected val boardService: IBoardService by kodeinDI().instance()
|
||||
}
|
||||
|
||||
enum class Tab(val icon: SvgPathName, val i18nText: String) {
|
||||
BOARDS(SvgPathName.FORMAT_LIST_BULLETED, "tab.boards.label"),
|
||||
FAVORITES(SvgPathName.STAR, "tab.favorites.label"),
|
||||
HISTORY(SvgPathName.HISTORY, "tab.history.label"),
|
||||
SETTINGS(SvgPathName.COG, "tab.settings.label")
|
||||
class BoardsTabViewPresenter : TabViewPresenter() {
|
||||
override val view: BoardsView by inject()
|
||||
|
||||
val boards: List<Board>
|
||||
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
|
||||
|
||||
import dev.fyloz.plannervio.core.service.locateI18nService
|
||||
import dev.fyloz.plannervio.core.service.svgGlyph
|
||||
import com.beust.klaxon.Klaxon
|
||||
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 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.scene.control.Label
|
||||
import javafx.scene.Node
|
||||
import javafx.scene.layout.HBox
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.Region
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Color
|
||||
import kfoenix.JFXStylesheet.Companion.jfxRipplerFill
|
||||
import kfoenix.jfxrippler
|
||||
import tornadofx.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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("-")
|
||||
}
|
||||
const val KEYWORDS_BUNDLE_NAME = "lang.keywords"
|
||||
|
||||
protected val i18nService = locateI18nService(filename)
|
||||
/** A SVG icon. */
|
||||
data class SvgIcon(val name: String, val author: String, val path: String)
|
||||
|
||||
init {
|
||||
messages = i18nService.bundle
|
||||
}
|
||||
/** The path to the file containing svg icons. */
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @property view The view which the fragment is linked to.
|
||||
* @constructor Initialize the fragment, linking the FXML and the CSS files.
|
||||
*/
|
||||
abstract class AbstractFragment(val view: AbstractView? = null) : Fragment() {
|
||||
protected val i18nService = locateI18nService(view?.filename)
|
||||
|
||||
init {
|
||||
messages = i18nService.bundle
|
||||
}
|
||||
/** Create a [SVGGlyph] for the given [svgIconName] with the given [size] and [color]. */
|
||||
fun svgGlyph(svgIconName: SvgIconName, size: Double = 16.0, color: Color = Color.BLACK): SVGGlyph {
|
||||
val svgIcon = svgIcons.firstOrNull { svgIconName.iconName == it.name }
|
||||
?: throw IllegalStateException("Cannot create a SVGGlyph for the icon ${svgIconName.iconName} because no icon loaded was found with this name.")
|
||||
val glyph = SVGGlyph(svgIcon.path, color)
|
||||
glyph.size = size
|
||||
return glyph
|
||||
}
|
||||
|
||||
/** Shorthand to create a [TabButton] in a [View]. */
|
||||
fun View.tabButton(tab: Tab, onClick: (TabButton) -> Unit): TabButton {
|
||||
return TabButton(tab).apply {
|
||||
alignment = Pos.BOTTOM_LEFT
|
||||
textFill = Color.RED
|
||||
graphic = hbox {
|
||||
paddingTop = 2
|
||||
this += pane {
|
||||
hboxMargin(top = 8)
|
||||
addClass(Style.jfxSvgGlyphWrapper)
|
||||
this += svgGlyph(tab.icon, size = 16.0, color = Color.WHITE)!!
|
||||
}
|
||||
this += Label(messages[tab.i18nText])
|
||||
/** A button linked to a [tab]. */
|
||||
class TabButton(tab: Tab, active: Boolean = false) : JFXButton() {
|
||||
/** The [Tab] linked to the button. */
|
||||
val tabProperty by lazy { SimpleObjectProperty(tab) }
|
||||
var tab: Tab by tabProperty
|
||||
|
||||
/** 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)
|
||||
}
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
fun Region.vboxMargin(all: Number = 0) {
|
||||
/** A toggleable icon [JFXButton], with an [enabledColor] and a [disabledColor]. */
|
||||
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)
|
||||
}
|
||||
|
||||
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [VBox] to the given size. */
|
||||
fun Region.vboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||
/** Sets [horizontal] and [vertical] margins of a [Node] contained in a [VBox] to the given size. */
|
||||
fun Node.vboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||
this.vboxMargin(vertical, horizontal, vertical, horizontal)
|
||||
}
|
||||
|
||||
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [VBox] to the given size. */
|
||||
fun Region.vboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
||||
/** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [VBox] to the given size. */
|
||||
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()))
|
||||
}
|
||||
|
||||
/** Sets [all] margins of a [Region] contained in a [HBox] to the given size. */
|
||||
fun Region.hboxMargin(all: Number = 0) {
|
||||
/** Sets [all] margins of a [Node] contained in a [HBox] to the given size. */
|
||||
fun Node.hboxMargin(all: Number = 0) {
|
||||
this.hboxMargin(all, all)
|
||||
}
|
||||
|
||||
/** Sets [horizontal] and [vertical] margins of a [Region] contained in a [HBox] to the given size. */
|
||||
fun Region.hboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||
/** Sets [horizontal] and [vertical] margins of a [Node] contained in a [HBox] to the given size. */
|
||||
fun Node.hboxMargin(horizontal: Number = 0, vertical: Number = 0) {
|
||||
this.hboxMargin(vertical, horizontal, vertical, horizontal)
|
||||
}
|
||||
|
||||
/** Sets [top], [right], [bottom] and [left] margins of a [Region] contained in a [HBox] to the given size. */
|
||||
fun Region.hboxMargin(top: Number = 0, right: Number = 0, bottom: Number = 0, left: Number = 0) {
|
||||
/** Sets [top], [right], [bottom] and [left] margins of a [Node] contained in a [HBox] to the given size. */
|
||||
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()))
|
||||
}
|
||||
|
||||
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
|
||||
private=Private
|
||||
public=Public
|
||||
name=Name
|
||||
description=Description
|
||||
open=OPEN
|
||||
search=Search...
|
||||
board.name=Name
|
||||
board.description=Description
|
||||
board.lastVisited=Last visit
|
||||
board.favorite=Favorite
|
||||
board.protection=Protection
|
||||
board.location.local=This computer
|
||||
board.location=Location
|
||||
board.protection.private=Private
|
||||
board.protection.public=Public
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
private=Privé
|
||||
public=Publique
|
||||
members={0} Membres
|
||||
name=Nom
|
||||
description=Description
|
||||
open=OUVRIR
|
||||
search=Recherche...
|
||||
board.name=Nom
|
||||
board.description=Description
|
||||
board.lastVisited=Dernière visite
|
||||
board.favorite=Favoris
|
||||
board.protection=Protection
|
||||
board.location.local=Cet ordinateur
|
||||
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
|
||||
|
||||
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 io.mockk.clearAllMocks
|
||||
import io.mockk.every
|
||||
|
@ -15,13 +15,13 @@ import org.kodein.di.singleton
|
|||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class BoardServiceTest {
|
||||
class BoardServiceImplTest {
|
||||
private val di: DI = DI {
|
||||
bind<IBoardRepository>() with singleton { repository }
|
||||
}
|
||||
|
||||
private val repository: IBoardRepository = mockk()
|
||||
private val service: IBoardService = BoardService(di)
|
||||
private val service: IBoardService = BoardServiceImpl(di)
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
|
@ -33,8 +33,8 @@ class BoardServiceTest {
|
|||
@Test
|
||||
fun `all boards are included`() {
|
||||
val boards: List<Board> = listOf(
|
||||
boardFactory(0L),
|
||||
boardFactory(1L)
|
||||
board(0L),
|
||||
board(1L)
|
||||
)
|
||||
every { repository.findAll() } returns boards
|
||||
|
Loading…
Reference in New Issue