Compare commits
No commits in common. "c427af15b656f2286720f3358a189ebad94e2fe8" and "ea1973a3cf447ff2bba0ab836753cc755c958cfa" have entirely different histories.
c427af15b6
...
ea1973a3cf
@ -29,8 +29,6 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.ikonli.javafx)
|
implementation(libs.ikonli.javafx)
|
||||||
|
|
||||||
implementation(libs.slf4j.nop)
|
|
||||||
|
|
||||||
testImplementation(libs.kotest.core)
|
testImplementation(libs.kotest.core)
|
||||||
testImplementation(libs.kotest.assertions)
|
testImplementation(libs.kotest.assertions)
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ segment = "0.3.1"
|
|||||||
|
|
||||||
ikonli-javafx = "12.3.1"
|
ikonli-javafx = "12.3.1"
|
||||||
|
|
||||||
slf4j = "2.0.16"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Kotest
|
# Kotest
|
||||||
# See: https://kotest.io
|
# See: https://kotest.io
|
||||||
@ -29,7 +27,6 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
|
|||||||
kotlinx-html-jvm = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinx-html-jvm" }
|
kotlinx-html-jvm = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version.ref = "kotlinx-html-jvm" }
|
||||||
|
|
||||||
segment = { module = "com.github.houbb:segment", version.ref = "segment" }
|
segment = { module = "com.github.houbb:segment", version.ref = "segment" }
|
||||||
slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j" }
|
|
||||||
|
|
||||||
ikonli-javafx = { module = "org.kordamp.ikonli:ikonli-javafx", version.ref = "ikonli-javafx" }
|
ikonli-javafx = { module = "org.kordamp.ikonli:ikonli-javafx", version.ref = "ikonli-javafx" }
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@ package com.marvinelsen.willow
|
|||||||
|
|
||||||
import com.marvinelsen.willow.domain.SearchMode
|
import com.marvinelsen.willow.domain.SearchMode
|
||||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
||||||
import com.marvinelsen.willow.ui.SentenceFx
|
|
||||||
import com.marvinelsen.willow.ui.services.FindCharacterService
|
import com.marvinelsen.willow.ui.services.FindCharacterService
|
||||||
import com.marvinelsen.willow.ui.services.FindSentencesService
|
import com.marvinelsen.willow.ui.services.FindWordsService
|
||||||
import com.marvinelsen.willow.ui.services.FindWordsBeginningService
|
|
||||||
import com.marvinelsen.willow.ui.services.FindWordsContainingService
|
|
||||||
import com.marvinelsen.willow.ui.services.SearchService
|
import com.marvinelsen.willow.ui.services.SearchService
|
||||||
import com.marvinelsen.willow.ui.util.ClipboardHelper
|
import com.marvinelsen.willow.ui.util.ClipboardHelper
|
||||||
import javafx.beans.property.ObjectProperty
|
import javafx.beans.property.ObjectProperty
|
||||||
@ -19,53 +16,37 @@ import javafx.event.EventHandler
|
|||||||
|
|
||||||
class Model(
|
class Model(
|
||||||
private val searchService: SearchService,
|
private val searchService: SearchService,
|
||||||
private val findWordsBeginningService: FindWordsBeginningService,
|
private val findWordsService: FindWordsService,
|
||||||
private val findWordsContainingService: FindWordsContainingService,
|
|
||||||
private val findCharacterService: FindCharacterService,
|
private val findCharacterService: FindCharacterService,
|
||||||
private val findSentencesService: FindSentencesService,
|
|
||||||
) {
|
) {
|
||||||
private val internalSelectedEntry: ObjectProperty<DictionaryEntryFx> = SimpleObjectProperty()
|
private val internalSelectedEntry: ObjectProperty<DictionaryEntryFx> = SimpleObjectProperty()
|
||||||
private val internalSearchResults: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
private val internalSearchResults: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
||||||
private val internalWordsBeginning: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
|
||||||
private val internalWordsContaining: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
private val internalWordsContaining: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
||||||
private val internalCharacters: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
private val internalCharacters: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
|
||||||
private val internalSentences: ObservableList<SentenceFx> = FXCollections.observableArrayList()
|
|
||||||
|
|
||||||
val selectedEntry: ReadOnlyObjectProperty<DictionaryEntryFx> = internalSelectedEntry
|
val selectedEntry: ReadOnlyObjectProperty<DictionaryEntryFx> = internalSelectedEntry
|
||||||
|
|
||||||
val searchResults: ObservableList<DictionaryEntryFx> =
|
val searchResults: ObservableList<DictionaryEntryFx> =
|
||||||
FXCollections.unmodifiableObservableList(internalSearchResults)
|
FXCollections.unmodifiableObservableList(internalSearchResults)
|
||||||
val wordsBeginning: ObservableList<DictionaryEntryFx> =
|
|
||||||
FXCollections.unmodifiableObservableList(internalWordsBeginning)
|
|
||||||
val wordsContaining: ObservableList<DictionaryEntryFx> =
|
val wordsContaining: ObservableList<DictionaryEntryFx> =
|
||||||
FXCollections.unmodifiableObservableList(internalWordsContaining)
|
FXCollections.unmodifiableObservableList(internalWordsContaining)
|
||||||
val characters: ObservableList<DictionaryEntryFx> =
|
val characters: ObservableList<DictionaryEntryFx> =
|
||||||
FXCollections.unmodifiableObservableList(internalCharacters)
|
FXCollections.unmodifiableObservableList(internalCharacters)
|
||||||
val sentences: ObservableList<SentenceFx> =
|
|
||||||
FXCollections.unmodifiableObservableList(internalSentences)
|
|
||||||
|
|
||||||
val isSearching: ReadOnlyBooleanProperty = searchService.runningProperty()
|
val isSearching: ReadOnlyBooleanProperty = searchService.runningProperty()
|
||||||
val isFindingWordsBeginning: ReadOnlyBooleanProperty = findWordsBeginningService.runningProperty()
|
val isFindingWords: ReadOnlyBooleanProperty = findWordsService.runningProperty()
|
||||||
val isFindingWordsContaining: ReadOnlyBooleanProperty = findWordsContainingService.runningProperty()
|
|
||||||
val isFindingCharacters: ReadOnlyBooleanProperty = findCharacterService.runningProperty()
|
val isFindingCharacters: ReadOnlyBooleanProperty = findCharacterService.runningProperty()
|
||||||
val isFindingSentences: ReadOnlyBooleanProperty = findSentencesService.runningProperty()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
searchService.onSucceeded = EventHandler {
|
searchService.onSucceeded = EventHandler {
|
||||||
internalSearchResults.setAll(searchService.value)
|
internalSearchResults.setAll(searchService.value)
|
||||||
}
|
}
|
||||||
findWordsBeginningService.onSucceeded = EventHandler {
|
findWordsService.onSucceeded = EventHandler {
|
||||||
internalWordsBeginning.setAll(findWordsBeginningService.value)
|
internalWordsContaining.setAll(findWordsService.value)
|
||||||
}
|
|
||||||
findWordsContainingService.onSucceeded = EventHandler {
|
|
||||||
internalWordsContaining.setAll(findWordsContainingService.value)
|
|
||||||
}
|
}
|
||||||
findCharacterService.onSucceeded = EventHandler {
|
findCharacterService.onSucceeded = EventHandler {
|
||||||
internalCharacters.setAll(findCharacterService.value)
|
internalCharacters.setAll(findCharacterService.value)
|
||||||
}
|
}
|
||||||
findSentencesService.onSucceeded = EventHandler {
|
|
||||||
internalSentences.setAll(findSentencesService.value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun search(query: String, searchMode: SearchMode) {
|
fun search(query: String, searchMode: SearchMode) {
|
||||||
@ -74,14 +55,9 @@ class Model(
|
|||||||
searchService.restart()
|
searchService.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findWordsBeginning() {
|
fun findWords() {
|
||||||
findWordsBeginningService.entry = internalSelectedEntry.value
|
findWordsService.entry = internalSelectedEntry.value
|
||||||
findWordsBeginningService.restart()
|
findWordsService.restart()
|
||||||
}
|
|
||||||
|
|
||||||
fun findWordsContaining() {
|
|
||||||
findWordsContainingService.entry = internalSelectedEntry.value
|
|
||||||
findWordsContainingService.restart()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findCharacters() {
|
fun findCharacters() {
|
||||||
@ -89,16 +65,9 @@ class Model(
|
|||||||
findCharacterService.restart()
|
findCharacterService.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findSentences() {
|
|
||||||
findSentencesService.entry = internalSelectedEntry.value
|
|
||||||
findSentencesService.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun selectEntry(entry: DictionaryEntryFx) {
|
fun selectEntry(entry: DictionaryEntryFx) {
|
||||||
internalWordsBeginning.setAll(emptyList())
|
|
||||||
internalWordsContaining.setAll(emptyList())
|
internalWordsContaining.setAll(emptyList())
|
||||||
internalCharacters.setAll(emptyList())
|
internalCharacters.setAll(emptyList())
|
||||||
internalSentences.setAll(emptyList())
|
|
||||||
internalSelectedEntry.value = entry
|
internalSelectedEntry.value = entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@ import com.marvinelsen.willow.ui.controllers.MenuController
|
|||||||
import com.marvinelsen.willow.ui.controllers.SearchController
|
import com.marvinelsen.willow.ui.controllers.SearchController
|
||||||
import com.marvinelsen.willow.ui.controllers.SearchResultsController
|
import com.marvinelsen.willow.ui.controllers.SearchResultsController
|
||||||
import com.marvinelsen.willow.ui.services.FindCharacterService
|
import com.marvinelsen.willow.ui.services.FindCharacterService
|
||||||
import com.marvinelsen.willow.ui.services.FindSentencesService
|
import com.marvinelsen.willow.ui.services.FindWordsService
|
||||||
import com.marvinelsen.willow.ui.services.FindWordsBeginningService
|
|
||||||
import com.marvinelsen.willow.ui.services.FindWordsContainingService
|
|
||||||
import com.marvinelsen.willow.ui.services.SearchService
|
import com.marvinelsen.willow.ui.services.SearchService
|
||||||
import javafx.application.Application
|
import javafx.application.Application
|
||||||
import javafx.fxml.FXMLLoader
|
import javafx.fxml.FXMLLoader
|
||||||
@ -46,17 +44,9 @@ class WillowApplication : Application() {
|
|||||||
}
|
}
|
||||||
val dictionary = SqliteDictionary(connection)
|
val dictionary = SqliteDictionary(connection)
|
||||||
val searchService = SearchService(dictionary)
|
val searchService = SearchService(dictionary)
|
||||||
val findWordsBeginningService = FindWordsBeginningService(dictionary)
|
val findWordsService = FindWordsService(dictionary)
|
||||||
val findWordsContainingService = FindWordsContainingService(dictionary)
|
|
||||||
val findCharacterService = FindCharacterService(dictionary)
|
val findCharacterService = FindCharacterService(dictionary)
|
||||||
val findSentenceService = FindSentencesService(dictionary)
|
val model = Model(searchService, findWordsService, findCharacterService)
|
||||||
val model = Model(
|
|
||||||
searchService,
|
|
||||||
findWordsBeginningService,
|
|
||||||
findWordsContainingService,
|
|
||||||
findCharacterService,
|
|
||||||
findSentenceService
|
|
||||||
)
|
|
||||||
val config = Config()
|
val config = Config()
|
||||||
config.load()
|
config.load()
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@ package com.marvinelsen.willow.domain
|
|||||||
|
|
||||||
interface Dictionary {
|
interface Dictionary {
|
||||||
fun search(query: String, searchMode: SearchMode): List<DictionaryEntry>
|
fun search(query: String, searchMode: SearchMode): List<DictionaryEntry>
|
||||||
|
|
||||||
fun findWordsBeginning(entry: DictionaryEntry): List<DictionaryEntry>
|
|
||||||
fun findWordsContaining(entry: DictionaryEntry): List<DictionaryEntry>
|
fun findWordsContaining(entry: DictionaryEntry): List<DictionaryEntry>
|
||||||
|
fun findSentencesContaining(entry: DictionaryEntry): List<DictionaryEntry>
|
||||||
fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry>
|
fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry>
|
||||||
|
fun searchSegments(phrase: String): List<DictionaryEntry>
|
||||||
fun findSentencesContaining(entry: DictionaryEntry): List<Sentence>
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package com.marvinelsen.willow.domain
|
|
||||||
|
|
||||||
data class Sentence(
|
|
||||||
val traditional: String,
|
|
||||||
val simplified: String,
|
|
||||||
)
|
|
@ -66,17 +66,6 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
ORDER BY cte.id
|
ORDER BY cte.id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
private val findWordsBeginning: PreparedStatement by lazy {
|
|
||||||
connection.prepareStatement(
|
|
||||||
"""
|
|
||||||
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
|
|
||||||
FROM entry
|
|
||||||
WHERE traditional GLOB ?
|
|
||||||
ORDER BY character_count ASC
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val findWordsContaining: PreparedStatement by lazy {
|
private val findWordsContaining: PreparedStatement by lazy {
|
||||||
connection.prepareStatement(
|
connection.prepareStatement(
|
||||||
"""
|
"""
|
||||||
@ -92,70 +81,18 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
WITH cte(id, character, syllable) AS (VALUES ?)
|
WITH cte(id, character, syllable) AS (VALUES ?)
|
||||||
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
|
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
|
||||||
FROM entry INNER JOIN cte
|
FROM entry INNER JOIN cte
|
||||||
ON cte.character = entry.traditional
|
ON cte.character = entry.traditional OR cte.character = entry.simplified
|
||||||
WHERE cte.syllable = entry.pinyin_with_tone_numbers
|
WHERE cte.syllable = entry.pinyin_with_tone_numbers
|
||||||
ORDER BY cte.id
|
ORDER BY cte.id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
private val findSentences: PreparedStatement by lazy {
|
|
||||||
connection.prepareStatement(
|
|
||||||
"""
|
|
||||||
SELECT traditional, simplified
|
|
||||||
FROM sentence
|
|
||||||
WHERE traditional LIKE ?
|
|
||||||
ORDER BY character_count ASC
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
|
override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
|
||||||
|
SearchMode.PINYIN -> searchPinyin(query)
|
||||||
SearchMode.SIMPLIFIED -> searchSimplified(query)
|
SearchMode.SIMPLIFIED -> searchSimplified(query)
|
||||||
SearchMode.TRADITIONAL -> searchTraditional(query)
|
SearchMode.TRADITIONAL -> searchTraditional(query)
|
||||||
SearchMode.PINYIN -> searchPinyin(query)
|
|
||||||
SearchMode.SEGMENTS -> searchSegments(query)
|
SearchMode.SEGMENTS -> searchSegments(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchSimplified(query: String): List<DictionaryEntry> {
|
|
||||||
searchSimplifiedPreparedStatement.setString(1, "$query*")
|
|
||||||
|
|
||||||
val resultSet: ResultSet = searchSimplifiedPreparedStatement.executeQuery()
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun searchTraditional(query: String): List<DictionaryEntry> {
|
|
||||||
searchTraditionalPreparedStatement.setString(1, "$query*")
|
|
||||||
|
|
||||||
val resultSet: ResultSet = searchTraditionalPreparedStatement.executeQuery()
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun searchPinyin(query: String): List<DictionaryEntry> {
|
|
||||||
val sanitizedQuery = query.lowercase().replace(whitespaceRegex, "")
|
|
||||||
|
|
||||||
searchPinyinPreparedStatement.setString(1, "$sanitizedQuery*")
|
|
||||||
searchPinyinPreparedStatement.setString(2, "$sanitizedQuery*")
|
|
||||||
|
|
||||||
val resultSet: ResultSet = searchPinyinPreparedStatement.executeQuery()
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun searchSegments(phrase: String): List<DictionaryEntry> {
|
|
||||||
val segments = segmentBs.segment(phrase, SegmentResultHandlers.word())
|
|
||||||
|
|
||||||
val segmentsListString = segments
|
|
||||||
.mapIndexed { index, s -> "($index, '$s')" }
|
|
||||||
.joinToString(",")
|
|
||||||
|
|
||||||
val query = searchSegments.replace("?", segmentsListString)
|
|
||||||
|
|
||||||
val resultSet: ResultSet = connection.createStatement().executeQuery(query)
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findWordsContaining(entry: DictionaryEntry): List<DictionaryEntry> {
|
override fun findWordsContaining(entry: DictionaryEntry): List<DictionaryEntry> {
|
||||||
findWordsContaining.setString(1, "_%${entry.traditional}%")
|
findWordsContaining.setString(1, "_%${entry.traditional}%")
|
||||||
|
|
||||||
@ -164,12 +101,8 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
return resultSet.toListOfDictionaryEntries()
|
return resultSet.toListOfDictionaryEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findWordsBeginning(entry: DictionaryEntry): List<DictionaryEntry> {
|
override fun findSentencesContaining(entry: DictionaryEntry): List<DictionaryEntry> {
|
||||||
findWordsBeginning.setString(1, "${entry.traditional}?*")
|
return emptyList()
|
||||||
|
|
||||||
val resultSet: ResultSet = findWordsBeginning.executeQuery()
|
|
||||||
|
|
||||||
return resultSet.toListOfDictionaryEntries()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
|
override fun findCharacters(entry: DictionaryEntry): List<DictionaryEntry> {
|
||||||
@ -197,12 +130,45 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
|
|||||||
return resultSet.toListOfDictionaryEntries()
|
return resultSet.toListOfDictionaryEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findSentencesContaining(entry: DictionaryEntry): List<Sentence> {
|
override fun searchSegments(phrase: String): List<DictionaryEntry> {
|
||||||
findSentences.setString(1, "_%${entry.traditional}%")
|
val segments = segmentBs.segment(phrase, SegmentResultHandlers.word())
|
||||||
|
|
||||||
val resultSet: ResultSet = findSentences.executeQuery()
|
val segmentsListString = segments
|
||||||
|
.mapIndexed { index, s -> "($index, '$s')" }
|
||||||
|
.joinToString(",")
|
||||||
|
|
||||||
return resultSet.toListOfSentences()
|
val query = searchSegments.replace("?", segmentsListString)
|
||||||
|
|
||||||
|
val resultSet: ResultSet = connection.createStatement().executeQuery(query)
|
||||||
|
|
||||||
|
return resultSet.toListOfDictionaryEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchSimplified(query: String): List<DictionaryEntry> {
|
||||||
|
searchSimplifiedPreparedStatement.setString(1, "$query*")
|
||||||
|
|
||||||
|
val resultSet: ResultSet = searchSimplifiedPreparedStatement.executeQuery()
|
||||||
|
|
||||||
|
return resultSet.toListOfDictionaryEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchPinyin(query: String): List<DictionaryEntry> {
|
||||||
|
val sanitizedQuery = query.lowercase().replace(whitespaceRegex, "")
|
||||||
|
|
||||||
|
searchPinyinPreparedStatement.setString(1, "$sanitizedQuery*")
|
||||||
|
searchPinyinPreparedStatement.setString(2, "$sanitizedQuery*")
|
||||||
|
|
||||||
|
val resultSet: ResultSet = searchPinyinPreparedStatement.executeQuery()
|
||||||
|
|
||||||
|
return resultSet.toListOfDictionaryEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchTraditional(query: String): List<DictionaryEntry> {
|
||||||
|
searchTraditionalPreparedStatement.setString(1, "$query*")
|
||||||
|
|
||||||
|
val resultSet: ResultSet = searchTraditionalPreparedStatement.executeQuery()
|
||||||
|
|
||||||
|
return resultSet.toListOfDictionaryEntries()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,16 +191,3 @@ private fun ResultSet.toListOfDictionaryEntries() = buildList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ResultSet.toSentence() = Sentence(
|
|
||||||
traditional = this.getString(1),
|
|
||||||
simplified = this.getString(2),
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun ResultSet.toListOfSentences() = buildList {
|
|
||||||
this@toListOfSentences.use {
|
|
||||||
while (it.next()) {
|
|
||||||
add(it.toSentence())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package com.marvinelsen.willow.tatoeba
|
||||||
|
|
||||||
|
data class TatoebaSentence(val traditional: String)
|
@ -1,20 +0,0 @@
|
|||||||
package com.marvinelsen.willow.ui
|
|
||||||
|
|
||||||
import com.marvinelsen.willow.domain.Sentence
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
|
||||||
import javafx.beans.property.StringProperty
|
|
||||||
|
|
||||||
data class SentenceFx(
|
|
||||||
val traditionalProperty: StringProperty,
|
|
||||||
val simplifiedProperty: StringProperty,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Sentence.toFx() = SentenceFx(
|
|
||||||
traditionalProperty = SimpleStringProperty(this.traditional),
|
|
||||||
simplifiedProperty = SimpleStringProperty(this.simplified),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun SentenceFx.toDomain() = Sentence(
|
|
||||||
traditional = this.traditionalProperty.value,
|
|
||||||
simplified = this.simplifiedProperty.value,
|
|
||||||
)
|
|
@ -28,7 +28,7 @@ class DictionaryEntryCellFactory(private val resources: ResourceBundle, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EntryCell(private val resources: ResourceBundle, private val config: Config) :
|
internal class EntryCell(private val resources: ResourceBundle, private val config: Config) :
|
||||||
ListCell<DictionaryEntryFx?>() {
|
ListCell<DictionaryEntryFx?>() {
|
||||||
private val labelHeadword = Label().apply {
|
private val labelHeadword = Label().apply {
|
||||||
styleClass.add("headword")
|
styleClass.add("headword")
|
@ -1,56 +0,0 @@
|
|||||||
package com.marvinelsen.willow.ui.cells
|
|
||||||
|
|
||||||
import com.marvinelsen.willow.config.Config
|
|
||||||
import com.marvinelsen.willow.config.Script
|
|
||||||
import com.marvinelsen.willow.ui.SentenceFx
|
|
||||||
import javafx.beans.binding.Bindings
|
|
||||||
import javafx.scene.control.Label
|
|
||||||
import javafx.scene.control.ListCell
|
|
||||||
import javafx.scene.control.ListView
|
|
||||||
import javafx.scene.layout.VBox
|
|
||||||
import javafx.util.Callback
|
|
||||||
|
|
||||||
class SentenceCellFactory(private val config: Config) : Callback<ListView<SentenceFx?>, ListCell<SentenceFx?>> {
|
|
||||||
|
|
||||||
override fun call(listView: ListView<SentenceFx?>): ListCell<SentenceFx?> {
|
|
||||||
val sentenceCell = SentenceCell(config)
|
|
||||||
sentenceCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING))
|
|
||||||
return sentenceCell
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val CELL_PADDING = 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SentenceCell(private val config: Config) : ListCell<SentenceFx?>() {
|
|
||||||
private val labelSentence = Label().apply {
|
|
||||||
styleClass.add("sentence")
|
|
||||||
}
|
|
||||||
private val root = VBox(labelSentence)
|
|
||||||
|
|
||||||
init {
|
|
||||||
text = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateItem(sentence: SentenceFx?, empty: Boolean) {
|
|
||||||
super.updateItem(sentence, empty)
|
|
||||||
if (empty || sentence == null) {
|
|
||||||
graphic = null
|
|
||||||
} else {
|
|
||||||
labelSentence.textProperty().bind(
|
|
||||||
Bindings.createStringBinding(
|
|
||||||
{
|
|
||||||
when (config.script.value!!) {
|
|
||||||
Script.SIMPLIFIED -> sentence.simplifiedProperty.value
|
|
||||||
Script.TRADITIONAL -> sentence.traditionalProperty.value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
config.script
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
graphic = root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,9 +5,7 @@ import com.marvinelsen.willow.config.Config
|
|||||||
import com.marvinelsen.willow.config.Pronunciation
|
import com.marvinelsen.willow.config.Pronunciation
|
||||||
import com.marvinelsen.willow.config.Script
|
import com.marvinelsen.willow.config.Script
|
||||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
||||||
import com.marvinelsen.willow.ui.SentenceFx
|
|
||||||
import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory
|
import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory
|
||||||
import com.marvinelsen.willow.ui.cells.SentenceCellFactory
|
|
||||||
import com.marvinelsen.willow.ui.util.createContextMenuForEntry
|
import com.marvinelsen.willow.ui.util.createContextMenuForEntry
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
@ -19,7 +17,6 @@ import javafx.scene.control.TabPane
|
|||||||
import javafx.scene.input.ContextMenuEvent
|
import javafx.scene.input.ContextMenuEvent
|
||||||
import javafx.scene.layout.FlowPane
|
import javafx.scene.layout.FlowPane
|
||||||
import javafx.scene.web.WebView
|
import javafx.scene.web.WebView
|
||||||
import kotlinx.html.DIV
|
|
||||||
import kotlinx.html.body
|
import kotlinx.html.body
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.h1
|
import kotlinx.html.h1
|
||||||
@ -30,7 +27,7 @@ import kotlinx.html.span
|
|||||||
import kotlinx.html.stream.createHTML
|
import kotlinx.html.stream.createHTML
|
||||||
import java.util.ResourceBundle
|
import java.util.ResourceBundle
|
||||||
|
|
||||||
@Suppress("UnusedPrivateMember", "TooManyFunctions")
|
@Suppress("UnusedPrivateMember")
|
||||||
class DetailsController(private val model: Model, private val config: Config) {
|
class DetailsController(private val model: Model, private val config: Config) {
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var resources: ResourceBundle
|
private lateinit var resources: ResourceBundle
|
||||||
@ -54,10 +51,7 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
private lateinit var webViewDefinition: WebView
|
private lateinit var webViewDefinition: WebView
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var listViewWordsContaining: ListView<DictionaryEntryFx>
|
private lateinit var listViewWords: ListView<DictionaryEntryFx>
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var listViewWordsBeginning: ListView<DictionaryEntryFx>
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var listViewCharacters: ListView<DictionaryEntryFx>
|
private lateinit var listViewCharacters: ListView<DictionaryEntryFx>
|
||||||
@ -66,38 +60,21 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
private lateinit var progressIndicatorCharacters: ProgressIndicator
|
private lateinit var progressIndicatorCharacters: ProgressIndicator
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var progressIndicatorWordsContaining: ProgressIndicator
|
private lateinit var progressIndicatorWords: ProgressIndicator
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var progressIndicatorWordsBeginning: ProgressIndicator
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var labelNoCharactersFound: Label
|
private lateinit var labelNoCharactersFound: Label
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private lateinit var labelNoWordsContainingFound: Label
|
private lateinit var labelNoWordsFound: Label
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var labelNoWordsBeginningFound: Label
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var listViewSentences: ListView<SentenceFx>
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var progressIndicatorSentences: ProgressIndicator
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private lateinit var labelNoSentencesFound: Label
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
initializeLabelHeadword()
|
initializeLabelHeadword()
|
||||||
initializeLabelPronunciation()
|
initializeLabelPronunciation()
|
||||||
initializeTabPaneDetails()
|
initializeTabPaneDetails()
|
||||||
initializeListViewWordsContaining()
|
initializeListViewWords()
|
||||||
initializeListViewWordsBeginning()
|
|
||||||
initializeListViewCharacters()
|
initializeListViewCharacters()
|
||||||
initializeListViewSentences()
|
|
||||||
initializeWebViewDefinition()
|
initializeWebViewDefinition()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,43 +167,17 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeListViewSentences() {
|
private fun initializeListViewWords() {
|
||||||
listViewSentences.apply {
|
listViewWords.apply {
|
||||||
cellFactory = SentenceCellFactory(config)
|
|
||||||
items = model.sentences
|
|
||||||
|
|
||||||
disableProperty().bind(Bindings.or(model.isFindingSentences, Bindings.isEmpty(model.sentences)))
|
|
||||||
}
|
|
||||||
progressIndicatorSentences.visibleProperty().bind(model.isFindingSentences)
|
|
||||||
labelNoSentencesFound
|
|
||||||
.visibleProperty()
|
|
||||||
.bind(Bindings.and(Bindings.isEmpty(model.sentences), Bindings.not(model.isFindingSentences)))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeListViewWordsContaining() {
|
|
||||||
listViewWordsContaining.apply {
|
|
||||||
cellFactory = DictionaryEntryCellFactory(resources, config)
|
cellFactory = DictionaryEntryCellFactory(resources, config)
|
||||||
items = model.wordsContaining
|
items = model.wordsContaining
|
||||||
|
|
||||||
disableProperty().bind(Bindings.or(model.isFindingWordsContaining, Bindings.isEmpty(model.wordsContaining)))
|
disableProperty().bind(Bindings.or(model.isFindingWords, Bindings.isEmpty(model.wordsContaining)))
|
||||||
}
|
}
|
||||||
progressIndicatorWordsContaining.visibleProperty().bind(model.isFindingWordsContaining)
|
progressIndicatorWords.visibleProperty().bind(model.isFindingWords)
|
||||||
labelNoWordsContainingFound
|
labelNoWordsFound
|
||||||
.visibleProperty()
|
.visibleProperty()
|
||||||
.bind(Bindings.and(Bindings.isEmpty(model.wordsContaining), Bindings.not(model.isFindingWordsContaining)))
|
.bind(Bindings.and(Bindings.isEmpty(model.wordsContaining), Bindings.not(model.isFindingWords)))
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeListViewWordsBeginning() {
|
|
||||||
listViewWordsBeginning.apply {
|
|
||||||
cellFactory = DictionaryEntryCellFactory(resources, config)
|
|
||||||
items = model.wordsBeginning
|
|
||||||
|
|
||||||
disableProperty().bind(Bindings.or(model.isFindingWordsBeginning, Bindings.isEmpty(model.wordsBeginning)))
|
|
||||||
}
|
|
||||||
progressIndicatorWordsBeginning.visibleProperty().bind(model.isFindingWordsBeginning)
|
|
||||||
labelNoWordsBeginningFound
|
|
||||||
.visibleProperty()
|
|
||||||
.bind(Bindings.and(Bindings.isEmpty(model.wordsBeginning), Bindings.not(model.isFindingWordsBeginning)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeListViewCharacters() {
|
private fun initializeListViewCharacters() {
|
||||||
@ -250,7 +201,101 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
model.selectedEntry.addListener { _, _, newEntry ->
|
model.selectedEntry.addListener { _, _, newEntry ->
|
||||||
if (newEntry == null) return@addListener
|
if (newEntry == null) return@addListener
|
||||||
|
|
||||||
webViewDefinition.engine.loadContent(createDefinitionHtml(newEntry))
|
webViewDefinition.engine.loadContent(
|
||||||
|
createHTML().html {
|
||||||
|
body {
|
||||||
|
if (newEntry.cedictDefinitions.isNotEmpty()) {
|
||||||
|
div(classes = "cedict-definition") {
|
||||||
|
h1 {
|
||||||
|
+"CC-CEDICT"
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
for (definition in newEntry.cedictDefinitions) {
|
||||||
|
li {
|
||||||
|
+definition.joinToString(separator = "; ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newEntry.crossStraitsDefinitions.isNotEmpty()) {
|
||||||
|
div(classes = "cross-straits-definition") {
|
||||||
|
h1 {
|
||||||
|
+"Cross-Straits"
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
newEntry.crossStraitsDefinitions.forEach { definition ->
|
||||||
|
li {
|
||||||
|
span(classes = "definition") {
|
||||||
|
+definition.definition
|
||||||
|
}
|
||||||
|
if (definition.examples.isNotEmpty()) {
|
||||||
|
span(classes = "example") {
|
||||||
|
+definition.examples.joinToString(
|
||||||
|
prefix = "如:",
|
||||||
|
separator = "、",
|
||||||
|
postfix = "。"
|
||||||
|
) { "「$it」" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newEntry.moedictDefinitions.isNotEmpty()) {
|
||||||
|
div(classes = "moe-definition") {
|
||||||
|
h1 {
|
||||||
|
+"MOE"
|
||||||
|
}
|
||||||
|
newEntry.moedictDefinitions.groupBy {
|
||||||
|
it.type ?: ""
|
||||||
|
}.entries.forEach { (type, definitions) ->
|
||||||
|
if (type != "") {
|
||||||
|
span(classes = "type") {
|
||||||
|
+type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
definitions.forEach { definition ->
|
||||||
|
li {
|
||||||
|
span(classes = "definition") {
|
||||||
|
+definition.definition
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.examples.forEach { example ->
|
||||||
|
span(classes = "example") {
|
||||||
|
+example
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.quotes.forEach { quote ->
|
||||||
|
span(classes = "quote") {
|
||||||
|
+quote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.synonyms?.let {
|
||||||
|
span(classes = "synonyms") {
|
||||||
|
+"似:${it.replace(",", "、")}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.antonyms?.let {
|
||||||
|
span(classes = "antonyms") {
|
||||||
|
+"反:${it.replace(",", "、")}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,16 +310,12 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ReturnCount")
|
|
||||||
private fun lazyUpdateTabContent(selectedTabId: String?) {
|
private fun lazyUpdateTabContent(selectedTabId: String?) {
|
||||||
when (selectedTabId) {
|
when (selectedTabId) {
|
||||||
"tabWords" -> {
|
"tabWords" -> {
|
||||||
if (model.wordsContaining.isEmpty()) {
|
if (model.wordsContaining.isNotEmpty()) return
|
||||||
model.findWordsContaining()
|
|
||||||
}
|
model.findWords()
|
||||||
if (model.wordsBeginning.isEmpty()) {
|
|
||||||
model.findWordsBeginning()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"tabCharacters" -> {
|
"tabCharacters" -> {
|
||||||
@ -283,114 +324,7 @@ class DetailsController(private val model: Model, private val config: Config) {
|
|||||||
model.findCharacters()
|
model.findCharacters()
|
||||||
}
|
}
|
||||||
|
|
||||||
"tabSentences" -> {
|
|
||||||
if (model.sentences.isNotEmpty()) return
|
|
||||||
|
|
||||||
model.findSentences()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createDefinitionHtml(entry: DictionaryEntryFx) = createHTML().html {
|
|
||||||
body {
|
|
||||||
if (entry.cedictDefinitions.isNotEmpty()) {
|
|
||||||
div(classes = "cedict-definition") {
|
|
||||||
h1 {
|
|
||||||
+"CC-CEDICT"
|
|
||||||
}
|
|
||||||
cedictDefinition(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.crossStraitsDefinitions.isNotEmpty()) {
|
|
||||||
div(classes = "cross-straits-definition") {
|
|
||||||
h1 {
|
|
||||||
+"Cross-Straits"
|
|
||||||
}
|
|
||||||
crossStraitsDefinition(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.moedictDefinitions.isNotEmpty()) {
|
|
||||||
div(classes = "moe-definition") {
|
|
||||||
h1 {
|
|
||||||
+"MOE"
|
|
||||||
}
|
|
||||||
moeDefinition(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DIV.cedictDefinition(entry: DictionaryEntryFx) = ol {
|
|
||||||
for (definition in entry.cedictDefinitions) {
|
|
||||||
li {
|
|
||||||
+definition.joinToString(separator = "; ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DIV.crossStraitsDefinition(entry: DictionaryEntryFx) = ol {
|
|
||||||
entry.crossStraitsDefinitions.forEach { definition ->
|
|
||||||
li {
|
|
||||||
span(classes = "definition") {
|
|
||||||
+definition.definition
|
|
||||||
}
|
|
||||||
if (definition.examples.isNotEmpty()) {
|
|
||||||
span(classes = "example") {
|
|
||||||
+definition.examples.joinToString(
|
|
||||||
prefix = "如:",
|
|
||||||
separator = "、",
|
|
||||||
postfix = "。"
|
|
||||||
) { "「$it」" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DIV.moeDefinition(entry: DictionaryEntryFx) =
|
|
||||||
entry.moedictDefinitions.groupBy { it.type ?: "" }.entries.forEach { (type, definitions) ->
|
|
||||||
if (type != "") {
|
|
||||||
span(classes = "type") {
|
|
||||||
+type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
definitions.forEach { definition ->
|
|
||||||
li {
|
|
||||||
span(classes = "definition") {
|
|
||||||
+definition.definition
|
|
||||||
}
|
|
||||||
|
|
||||||
definition.examples.forEach { example ->
|
|
||||||
span(classes = "example") {
|
|
||||||
+example
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
definition.quotes.forEach { quote ->
|
|
||||||
span(classes = "quote") {
|
|
||||||
+quote
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
definition.synonyms?.let {
|
|
||||||
span(classes = "synonyms") {
|
|
||||||
+"似:${it.replace(",", "、")}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
definition.antonyms?.let {
|
|
||||||
span(classes = "antonyms") {
|
|
||||||
+"反:${it.replace(",", "、")}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package com.marvinelsen.willow.ui.services
|
|
||||||
|
|
||||||
import com.marvinelsen.willow.domain.Dictionary
|
|
||||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
|
||||||
import com.marvinelsen.willow.ui.SentenceFx
|
|
||||||
import com.marvinelsen.willow.ui.toDomain
|
|
||||||
import com.marvinelsen.willow.ui.toFx
|
|
||||||
import com.marvinelsen.willow.ui.util.task
|
|
||||||
import javafx.collections.FXCollections
|
|
||||||
import javafx.collections.ObservableList
|
|
||||||
import javafx.concurrent.Service
|
|
||||||
|
|
||||||
class FindSentencesService(private val dictionary: Dictionary) : Service<ObservableList<SentenceFx>>() {
|
|
||||||
lateinit var entry: DictionaryEntryFx
|
|
||||||
|
|
||||||
override fun createTask() = task {
|
|
||||||
if (!this::entry.isInitialized) error("Entry is not initialized")
|
|
||||||
|
|
||||||
FXCollections.observableList(dictionary.findSentencesContaining(entry.toDomain()).map { it.toFx() })
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package com.marvinelsen.willow.ui.services
|
|
||||||
|
|
||||||
import com.marvinelsen.willow.domain.Dictionary
|
|
||||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
|
||||||
import com.marvinelsen.willow.ui.toDomain
|
|
||||||
import com.marvinelsen.willow.ui.toFx
|
|
||||||
import com.marvinelsen.willow.ui.util.task
|
|
||||||
import javafx.collections.FXCollections
|
|
||||||
import javafx.collections.ObservableList
|
|
||||||
import javafx.concurrent.Service
|
|
||||||
|
|
||||||
class FindWordsBeginningService(private val dictionary: Dictionary) : Service<ObservableList<DictionaryEntryFx>>() {
|
|
||||||
lateinit var entry: DictionaryEntryFx
|
|
||||||
|
|
||||||
override fun createTask() = task {
|
|
||||||
if (!this::entry.isInitialized) error("Entry is not initialized")
|
|
||||||
|
|
||||||
FXCollections.observableList(dictionary.findWordsBeginning(entry.toDomain()).map { it.toFx() })
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ import javafx.collections.FXCollections
|
|||||||
import javafx.collections.ObservableList
|
import javafx.collections.ObservableList
|
||||||
import javafx.concurrent.Service
|
import javafx.concurrent.Service
|
||||||
|
|
||||||
class FindWordsContainingService(private val dictionary: Dictionary) : Service<ObservableList<DictionaryEntryFx>>() {
|
class FindWordsService(private val dictionary: Dictionary) : Service<ObservableList<DictionaryEntryFx>>() {
|
||||||
lateinit var entry: DictionaryEntryFx
|
lateinit var entry: DictionaryEntryFx
|
||||||
|
|
||||||
override fun createTask() = task {
|
override fun createTask() = task {
|
@ -9,8 +9,3 @@
|
|||||||
.list-view .headword {
|
.list-view .headword {
|
||||||
-fx-font-family: "Noto Sans TC";
|
-fx-font-family: "Noto Sans TC";
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-view .sentence {
|
|
||||||
-fx-font-family: "Noto Sans TC";
|
|
||||||
-fx-font-size: 14px;
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.web.WebView?>
|
<?import javafx.scene.web.WebView?>
|
||||||
<VBox stylesheets="/css/details.css" xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
|
<VBox xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="com.marvinelsen.willow.ui.controllers.DetailsController">
|
fx:controller="com.marvinelsen.willow.ui.controllers.DetailsController"
|
||||||
<FlowPane fx:id="flowPaneHeader" hgap="8.0" onContextMenuRequested="#headerOnContextMenuRequested" prefHeight="38.0"
|
stylesheets="/css/details.css">
|
||||||
prefWidth="412.0" rowValignment="BASELINE" styleClass="flow-pane-header" vgap="8.0" VBox.vgrow="NEVER">
|
<FlowPane fx:id="flowPaneHeader" styleClass="flow-pane-header" hgap="8.0" onContextMenuRequested="#headerOnContextMenuRequested"
|
||||||
|
prefHeight="38.0" prefWidth="412.0" rowValignment="BASELINE" vgap="8.0" VBox.vgrow="NEVER">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
|
<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>
|
||||||
</padding>
|
</padding>
|
||||||
@ -15,59 +16,25 @@
|
|||||||
<Label fx:id="labelPronunciation" styleClass="pronunciation">
|
<Label fx:id="labelPronunciation" styleClass="pronunciation">
|
||||||
</Label>
|
</Label>
|
||||||
</FlowPane>
|
</FlowPane>
|
||||||
<TabPane fx:id="tabPaneDetails" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
<TabPane fx:id="tabPaneDetails" tabClosingPolicy="UNAVAILABLE" disable="true" VBox.vgrow="ALWAYS">
|
||||||
<Tab closable="false" text="%tab.definition">
|
<Tab closable="false" text="%tab.definition">
|
||||||
<WebView fx:id="webViewDefinition" minHeight="-1.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0"/>
|
<WebView fx:id="webViewDefinition" minHeight="-1.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0"/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="tabSentences" closable="false" text="%tab.sentences">
|
<Tab id="tabSentences" closable="false" text="%tab.sentences">
|
||||||
|
<ListView fx:id="listViewSentences"/>
|
||||||
|
</Tab>
|
||||||
|
<Tab id="tabWords" closable="false" text="%tab.words">
|
||||||
<StackPane>
|
<StackPane>
|
||||||
<ProgressIndicator fx:id="progressIndicatorSentences" visible="false"/>
|
<ListView fx:id="listViewWords"/>
|
||||||
<Label fx:id="labelNoSentencesFound" text="%list.no_sentences_found" textAlignment="CENTER"
|
<Label fx:id="labelNoWordsFound" text="%list.no_words_found" textAlignment="CENTER"
|
||||||
visible="false"
|
visible="false" wrapText="true">
|
||||||
wrapText="true">
|
|
||||||
<padding>
|
<padding>
|
||||||
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
|
||||||
</padding>
|
</padding>
|
||||||
</Label>
|
</Label>
|
||||||
<ListView fx:id="listViewSentences"/>
|
<ProgressIndicator fx:id="progressIndicatorWords" visible="false"/>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="tabWords" closable="false" text="%tab.words">
|
|
||||||
<VBox maxHeight="1.7976931348623157E308">
|
|
||||||
<TitledPane animated="false" maxHeight="-Infinity" text="%list.words_beginning" VBox.vgrow="ALWAYS">
|
|
||||||
<StackPane>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="0.0" left="0.0" right="0.0" top="0.0"/>
|
|
||||||
</padding>
|
|
||||||
<ProgressIndicator fx:id="progressIndicatorWordsBeginning" visible="false"/>
|
|
||||||
<Label fx:id="labelNoWordsBeginningFound" text="%list.no_words_found" textAlignment="CENTER"
|
|
||||||
visible="false"
|
|
||||||
wrapText="true">
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
|
|
||||||
</padding>
|
|
||||||
</Label>
|
|
||||||
<ListView fx:id="listViewWordsBeginning" prefHeight="4000"/>
|
|
||||||
</StackPane>
|
|
||||||
</TitledPane>
|
|
||||||
<TitledPane animated="false" maxHeight="-Infinity" text="%list.words_containing" VBox.vgrow="ALWAYS">
|
|
||||||
<StackPane>
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="0.0" left="0.0" right="0.0" top="0.0"/>
|
|
||||||
</padding>
|
|
||||||
<ProgressIndicator fx:id="progressIndicatorWordsContaining" visible="false"/>
|
|
||||||
<Label fx:id="labelNoWordsContainingFound" text="%list.no_words_found" textAlignment="CENTER"
|
|
||||||
visible="false"
|
|
||||||
wrapText="true">
|
|
||||||
<padding>
|
|
||||||
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
|
|
||||||
</padding>
|
|
||||||
</Label>
|
|
||||||
<ListView fx:id="listViewWordsContaining" prefHeight="4000"/>
|
|
||||||
</StackPane>
|
|
||||||
</TitledPane>
|
|
||||||
</VBox>
|
|
||||||
</Tab>
|
|
||||||
<Tab fx:id="tabCharacters" id="tabCharacters" closable="false" text="%tab.characters">
|
<Tab fx:id="tabCharacters" id="tabCharacters" closable="false" text="%tab.characters">
|
||||||
<StackPane>
|
<StackPane>
|
||||||
<ListView fx:id="listViewCharacters"/>
|
<ListView fx:id="listViewCharacters"/>
|
||||||
|
@ -20,6 +20,3 @@ list.no_entries_found=No matching entries found
|
|||||||
search.mode.phrase=Phrase
|
search.mode.phrase=Phrase
|
||||||
list.no_characters_found=No characters found
|
list.no_characters_found=No characters found
|
||||||
list.no_words_found=No words found
|
list.no_words_found=No words found
|
||||||
list.no_sentences_found=No sentences found.
|
|
||||||
list.words_beginning=Words beginning
|
|
||||||
list.words_containing=Words containing
|
|
||||||
|
@ -20,6 +20,3 @@ list.no_entries_found=No matching entries found
|
|||||||
search.mode.phrase=Phrase
|
search.mode.phrase=Phrase
|
||||||
list.no_characters_found=No characters found
|
list.no_characters_found=No characters found
|
||||||
list.no_words_found=No words found
|
list.no_words_found=No words found
|
||||||
list.no_sentences_found=No sentences found.
|
|
||||||
list.words_beginning=Words beginning
|
|
||||||
list.words_containing=Words containing
|
|
||||||
|
@ -20,6 +20,3 @@ list.no_entries_found=No matching entries found
|
|||||||
search.mode.phrase=Phrase
|
search.mode.phrase=Phrase
|
||||||
list.no_characters_found=No characters found
|
list.no_characters_found=No characters found
|
||||||
list.no_words_found=No words found
|
list.no_words_found=No words found
|
||||||
list.no_sentences_found=No sentences found.
|
|
||||||
list.words_beginning=Words beginning
|
|
||||||
list.words_containing=Words containing
|
|
||||||
|
@ -20,6 +20,3 @@ list.no_entries_found=No matching entries found
|
|||||||
search.mode.phrase=Phrase
|
search.mode.phrase=Phrase
|
||||||
list.no_characters_found=No characters found
|
list.no_characters_found=No characters found
|
||||||
list.no_words_found=No words found
|
list.no_words_found=No words found
|
||||||
list.no_sentences_found=No sentences found.
|
|
||||||
list.words_beginning=Words beginning
|
|
||||||
list.words_containing=Words containing
|
|
||||||
|
@ -20,6 +20,3 @@ list.no_entries_found=No matching entries found
|
|||||||
search.mode.phrase=Phrase
|
search.mode.phrase=Phrase
|
||||||
list.no_characters_found=No characters found
|
list.no_characters_found=No characters found
|
||||||
list.no_words_found=No words found
|
list.no_words_found=No words found
|
||||||
list.no_sentences_found=No sentences found.
|
|
||||||
list.words_beginning=Words beginning
|
|
||||||
list.words_containing=Words containing
|
|
||||||
|
Loading…
Reference in New Issue
Block a user