Implement find characters

This commit is contained in:
Marvin Elsen 2024-10-03 12:11:50 +02:00
parent cb52240eea
commit d9b7b82c60
Signed by: marvinelsen
GPG Key ID: 820672408CC318C2
6 changed files with 54 additions and 12 deletions

View File

@ -3,6 +3,7 @@ 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.util.ClipboardHelper import com.marvinelsen.willow.ui.util.ClipboardHelper
import com.marvinelsen.willow.ui.util.FindCharacterService
import com.marvinelsen.willow.ui.util.FindWordsService import com.marvinelsen.willow.ui.util.FindWordsService
import com.marvinelsen.willow.ui.util.SearchService import com.marvinelsen.willow.ui.util.SearchService
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
@ -13,10 +14,11 @@ import javafx.collections.FXCollections
import javafx.collections.ObservableList import javafx.collections.ObservableList
import javafx.event.EventHandler import javafx.event.EventHandler
class Model(private val searchService: SearchService, private val findWordsService: FindWordsService) { class Model(private val searchService: SearchService, private val findWordsService: FindWordsService, private val findCharacterService: FindCharacterService) {
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 internalWordsContaining: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList() private val internalWordsContaining: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
private val internalCharacters: ObservableList<DictionaryEntryFx> = FXCollections.observableArrayList()
val selectedEntry: ReadOnlyObjectProperty<DictionaryEntryFx> = internalSelectedEntry val selectedEntry: ReadOnlyObjectProperty<DictionaryEntryFx> = internalSelectedEntry
@ -24,6 +26,8 @@ class Model(private val searchService: SearchService, private val findWordsServi
FXCollections.unmodifiableObservableList(internalSearchResults) FXCollections.unmodifiableObservableList(internalSearchResults)
val wordsContaining: ObservableList<DictionaryEntryFx> = val wordsContaining: ObservableList<DictionaryEntryFx> =
FXCollections.unmodifiableObservableList(internalWordsContaining) FXCollections.unmodifiableObservableList(internalWordsContaining)
val characters: ObservableList<DictionaryEntryFx> =
FXCollections.unmodifiableObservableList(internalCharacters)
val isSearching: ReadOnlyBooleanProperty = searchService.runningProperty() val isSearching: ReadOnlyBooleanProperty = searchService.runningProperty()
val isFindingWords: ReadOnlyBooleanProperty = findWordsService.runningProperty() val isFindingWords: ReadOnlyBooleanProperty = findWordsService.runningProperty()
@ -35,6 +39,9 @@ class Model(private val searchService: SearchService, private val findWordsServi
findWordsService.onSucceeded = EventHandler { findWordsService.onSucceeded = EventHandler {
internalWordsContaining.setAll(findWordsService.value) internalWordsContaining.setAll(findWordsService.value)
} }
findCharacterService.onSucceeded = EventHandler {
internalCharacters.setAll(findCharacterService.value)
}
} }
fun search(query: String, searchMode: SearchMode) { fun search(query: String, searchMode: SearchMode) {
@ -48,6 +55,11 @@ class Model(private val searchService: SearchService, private val findWordsServi
findWordsService.restart() findWordsService.restart()
} }
fun findCharacters() {
findCharacterService.entry = internalSelectedEntry.value
findCharacterService.restart()
}
fun selectEntry(entry: DictionaryEntryFx) { fun selectEntry(entry: DictionaryEntryFx) {
internalSelectedEntry.value = entry internalSelectedEntry.value = entry
} }

View File

@ -7,6 +7,7 @@ import com.marvinelsen.willow.ui.controllers.MainController
import com.marvinelsen.willow.ui.controllers.MenuController 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.util.FindCharacterService
import com.marvinelsen.willow.ui.util.FindWordsService import com.marvinelsen.willow.ui.util.FindWordsService
import com.marvinelsen.willow.ui.util.SearchService import com.marvinelsen.willow.ui.util.SearchService
import javafx.application.Application import javafx.application.Application
@ -44,7 +45,8 @@ class WillowApplication : Application() {
val dictionary = SqliteDictionary(connection) val dictionary = SqliteDictionary(connection)
val searchService = SearchService(dictionary) val searchService = SearchService(dictionary)
val findWordsService = FindWordsService(dictionary) val findWordsService = FindWordsService(dictionary)
val model = Model(searchService, findWordsService) val findCharacterService = FindCharacterService(dictionary)
val model = Model(searchService, findWordsService, findCharacterService)
val config = Config() val config = Config()
config.load() config.load()

View File

@ -78,9 +78,11 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
} }
private val findCharacters = """ private val findCharacters = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions WITH cte(id, character) AS (VALUES ?)
FROM cedict SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, definitions
WHERE traditional IN (?) FROM cedict INNER JOIN cte
ON cte.character = cedict.traditional OR cte.character = cedict.simplified
ORDER BY cte.id
""".trimIndent() """.trimIndent()
override fun search(query: String, searchMode: SearchMode) = when (searchMode) { override fun search(query: String, searchMode: SearchMode) = when (searchMode) {
@ -107,7 +109,8 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
val characterList = entry.traditional val characterList = entry.traditional
.split("") .split("")
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.joinToString(",") { "'$it'" } .mapIndexed { index, s -> "($index, '$s')" }
.joinToString(",")
val query = findCharacters.replace("?", characterList) val query = findCharacters.replace("?", characterList)

View File

@ -49,6 +49,7 @@ class DetailsController(private val model: Model, private val config: Config) {
initializeLabelHeadword() initializeLabelHeadword()
initializeTabPaneDetails() initializeTabPaneDetails()
initializeListViewWords() initializeListViewWords()
initializeListViewCharacters()
initializeWebViewDefinition() initializeWebViewDefinition()
model.selectedEntry.addListener { _, _, newEntry -> model.selectedEntry.addListener { _, _, newEntry ->
@ -59,6 +60,10 @@ class DetailsController(private val model: Model, private val config: Config) {
model.findWords() model.findWords()
} }
"tabCharacters" -> {
model.findCharacters()
}
else -> {} else -> {}
} }
webViewDefinition.engine.loadContent(newEntry.createCedictDefinitionHtml()) webViewDefinition.engine.loadContent(newEntry.createCedictDefinitionHtml())
@ -78,6 +83,13 @@ class DetailsController(private val model: Model, private val config: Config) {
} }
} }
private fun initializeListViewCharacters() {
listViewCharacters.apply {
cellFactory = DictionaryEntryCellFactory(resources, config)
items = model.characters
}
}
private fun initializeTabPaneDetails() { private fun initializeTabPaneDetails() {
tabPaneDetails.apply { tabPaneDetails.apply {
disableProperty().bind(Bindings.isNull(model.selectedEntry)) disableProperty().bind(Bindings.isNull(model.selectedEntry))
@ -90,6 +102,10 @@ class DetailsController(private val model: Model, private val config: Config) {
model.findWords() model.findWords()
} }
"tabCharacters" -> {
model.findCharacters()
}
else -> {} else -> {}
} }
} }

View File

@ -30,3 +30,13 @@ class FindWordsService(private val dictionary: Dictionary) : Service<ObservableL
FXCollections.observableList(dictionary.findWordsContaining(entry.toDomain()).map { it.toFx() }) FXCollections.observableList(dictionary.findWordsContaining(entry.toDomain()).map { it.toFx() })
} }
} }
class FindCharacterService(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.findCharacters(entry.toDomain()).map { it.toFx() })
}
}

View File

@ -14,17 +14,16 @@
</padding> </padding>
</Label> </Label>
<TabPane fx:id="tabPaneDetails" tabClosingPolicy="UNAVAILABLE" disable="true" VBox.vgrow="ALWAYS"> <TabPane fx:id="tabPaneDetails" tabClosingPolicy="UNAVAILABLE" disable="true" VBox.vgrow="ALWAYS">
<Tab closable="false" disable="false" text="%tab.definition"> <Tab closable="false" text="%tab.definition">
<WebView fx:id="webViewDefinition" minHeight="-1.0" minWidth="-1.0" prefHeight="-1.0" <WebView fx:id="webViewDefinition" minHeight="-1.0" minWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0"/>
prefWidth="-1.0"/>
</Tab> </Tab>
<Tab id="tabSentences" closable="false" disable="false" text="%tab.sentences"> <Tab id="tabSentences" closable="false" text="%tab.sentences">
<ListView fx:id="listviewSentences"/> <ListView fx:id="listviewSentences"/>
</Tab> </Tab>
<Tab id="tabWords" closable="false" disable="false" text="%tab.words"> <Tab id="tabWords" closable="false" text="%tab.words">
<ListView fx:id="listViewWords"/> <ListView fx:id="listViewWords"/>
</Tab> </Tab>
<Tab closable="false" disable="false" text="%tab.characters"> <Tab id="tabCharacters" closable="false" text="%tab.characters">
<ListView fx:id="listViewCharacters"/> <ListView fx:id="listViewCharacters"/>
</Tab> </Tab>
</TabPane> </TabPane>