Compare commits

..

No commits in common. "a2dfc858f46804b913db2e76d3cbd356b15f8951" and "52acfeb5da5f23271693fea89caeb12752839897" have entirely different histories.

19 changed files with 251 additions and 399 deletions

View File

@ -1,127 +0,0 @@
package com.marvinelsen.willow
import com.marvinelsen.willow.domain.SearchMode
import com.marvinelsen.willow.domain.SqliteDictionary
import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.ui.undo.Command
import com.marvinelsen.willow.ui.util.ClipboardHelper
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
@Suppress("TooManyFunctions")
class Interactor(
private val model: Model,
private val dictionary: SqliteDictionary,
) {
private val coroutineScope = MainScope()
fun copyHeadwordOfSelectedEntry() {
model.selectedEntry?.let { ClipboardHelper.copyString(it.traditional) }
}
fun copyPronunciationOfSelectedEntry() {
model.selectedEntry?.let { ClipboardHelper.copyString(it.pinyinWithToneMarks) }
}
fun undoSelection() {
model.undoManager.undo()
}
fun redoSelection() {
model.undoManager.redo()
}
fun search(query: String, searchMode: SearchMode) {
coroutineScope.launch {
model.isSearching = true
model.searchResults.setAll(dictionary.search(query, searchMode))
model.isSearching = false
}
}
fun findWordsBeginning() {
coroutineScope.launch {
model.isFindingWordsBeginning = true
model.wordsBeginning.setAll(
model.selectedEntry?.let {
dictionary
.findWordsBeginningWith(it)
}
)
model.isFindingWordsBeginning = false
model.finishedFindingWordsBeginning = true
}
}
fun findWordsContaining() {
coroutineScope.launch {
model.isFindingWordsContaining = true
model.wordsContaining.setAll(
model.selectedEntry?.let {
dictionary
.findWordsContaining(it)
}
)
model.isFindingWordsContaining = false
model.finishedFindingWordsContaining = true
}
}
fun findCharacters() {
coroutineScope.launch {
model.isFindingCharacters = true
model.characters.setAll(
model.selectedEntry?.let {
dictionary
.findCharactersOf(it)
}
)
model.isFindingCharacters = false
model.finishedFindingCharacters = true
}
}
fun findSentences() {
coroutineScope.launch {
model.isFindingSentences = true
model.sentences.setAll(
model.selectedEntry?.let {
dictionary
.findExampleSentencesFor(it)
}
)
model.isFindingSentences = false
model.finishedFindingSentences = true
}
}
fun deepDive(entry: DictionaryEntry) {
model.undoManager.execute(object : Command {
private val previouslySelectedEntry = model.selectedEntry
override fun execute() {
select(entry)
}
override fun undo() {
if (previouslySelectedEntry == null) return
select(previouslySelectedEntry)
}
})
}
fun normalSelect(entry: DictionaryEntry) {
model.undoManager.reset()
select(entry)
}
private fun select(entry: DictionaryEntry) {
model.finishedFindingCharacters = false
model.finishedFindingWordsBeginning = false
model.finishedFindingWordsContaining = false
model.finishedFindingSentences = false
model.selectedEntry = entry
}
}

View File

@ -1,110 +1,169 @@
package com.marvinelsen.willow package com.marvinelsen.willow
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.SearchMode
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.SqliteDictionary
import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.domain.entities.Sentence
import com.marvinelsen.willow.ui.undo.Command
import com.marvinelsen.willow.ui.undo.UndoManager import com.marvinelsen.willow.ui.undo.UndoManager
import com.marvinelsen.willow.ui.util.ClipboardHelper
import javafx.beans.property.BooleanProperty import javafx.beans.property.BooleanProperty
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
import javafx.beans.property.ReadOnlyBooleanProperty import javafx.beans.property.ReadOnlyBooleanProperty
import javafx.beans.property.ReadOnlyObjectProperty
import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections import javafx.collections.FXCollections
import javafx.collections.ObservableList import javafx.collections.ObservableList
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
class Model( class Model(
val undoManager: UndoManager, private val dictionary: SqliteDictionary,
private val undoManager: UndoManager,
) { ) {
private val _selectedEntry: ObjectProperty<DictionaryEntry?> = SimpleObjectProperty<DictionaryEntry>() private val internalSelectedEntry: ObjectProperty<DictionaryEntry> = SimpleObjectProperty()
private val internalSearchResults: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
private val internalWordsBeginning: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
private val internalWordsContaining: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
private val internalCharacters: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
private val internalSentences: ObservableList<Sentence> = FXCollections.observableArrayList()
private val _isSearching: BooleanProperty = SimpleBooleanProperty(false) private val internalIsSearching: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false) private val internalIsFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false) private val internalIsFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingCharacters: BooleanProperty = SimpleBooleanProperty(false) private val internalIsFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingSentences: BooleanProperty = SimpleBooleanProperty(false) private val internalIsFindingSentences: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false) private val internalFinishedFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false) private val internalFinishedFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingCharacters: BooleanProperty = SimpleBooleanProperty(false) private val internalFinishedFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingSentences: BooleanProperty = SimpleBooleanProperty(false) private val internalFinishedFindingSentences: BooleanProperty = SimpleBooleanProperty(false)
val searchResults: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
val wordsBeginning: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
val wordsContaining: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
val characters: ObservableList<DictionaryEntry> = FXCollections.observableArrayList()
val sentences: ObservableList<Sentence> = FXCollections.observableArrayList()
val canUndo: ReadOnlyBooleanProperty = undoManager.canUndoProperty val canUndo: ReadOnlyBooleanProperty = undoManager.canUndoProperty
val canRedo: ReadOnlyBooleanProperty = undoManager.canRedoProperty val canRedo: ReadOnlyBooleanProperty = undoManager.canRedoProperty
var selectedEntry: DictionaryEntry? val selectedEntry: ReadOnlyObjectProperty<DictionaryEntry> = internalSelectedEntry
get() = _selectedEntry.value
set(value) {
_selectedEntry.value = value
}
var isSearching: Boolean val searchResults: ObservableList<DictionaryEntry> =
get() = _isSearching.value FXCollections.unmodifiableObservableList(internalSearchResults)
set(value) { val wordsBeginning: ObservableList<DictionaryEntry> =
_isSearching.value = value FXCollections.unmodifiableObservableList(internalWordsBeginning)
} val wordsContaining: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalWordsContaining)
val characters: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalCharacters)
val sentences: ObservableList<Sentence> =
FXCollections.unmodifiableObservableList(internalSentences)
var isFindingWordsBeginning: Boolean val isSearching: ReadOnlyBooleanProperty = internalIsSearching
get() = _isFindingWordsBeginning.value val isFindingWordsBeginning: ReadOnlyBooleanProperty = internalIsFindingWordsBeginning
set(value) { val isFindingWordsContaining: ReadOnlyBooleanProperty = internalIsFindingWordsContaining
_isFindingWordsBeginning.value = value val isFindingCharacters: ReadOnlyBooleanProperty = internalIsFindingCharacters
} val isFindingSentences: ReadOnlyBooleanProperty = internalIsFindingSentences
var isFindingWordsContaining: Boolean val finishedFindingWordsBeginning: ReadOnlyBooleanProperty = internalFinishedFindingWordsBeginning
get() = _isFindingWordsContaining.value val finishedFindingWordsContaining: ReadOnlyBooleanProperty = internalFinishedFindingWordsContaining
set(value) { val finishedFindingCharacters: ReadOnlyBooleanProperty = internalFinishedFindingCharacters
_isFindingWordsContaining.value = value val finishedFindingSentences: ReadOnlyBooleanProperty = internalFinishedFindingSentences
}
var isFindingCharacters: Boolean private val coroutineScope = MainScope()
get() = _isFindingCharacters.value
set(value) {
_isFindingCharacters.value = value
}
var isFindingSentences: Boolean fun copyHeadwordOfSelectedEntry() {
get() = _isFindingSentences.value ClipboardHelper.copyString(internalSelectedEntry.value.traditional)
set(value) { }
_isFindingSentences.value = value
}
var finishedFindingWordsBeginning: Boolean fun copyPronunciationOfSelectedEntry() {
get() = _finishedFindingWordsBeginning.value ClipboardHelper.copyString(internalSelectedEntry.value.pinyinWithToneMarks)
set(value) { }
_finishedFindingWordsBeginning.value = value
}
var finishedFindingWordsContaining: Boolean fun undoSelection() {
get() = _finishedFindingWordsContaining.value undoManager.undo()
set(value) { }
_finishedFindingWordsContaining.value = value
}
var finishedFindingCharacters: Boolean fun redoSelection() {
get() = _finishedFindingCharacters.value undoManager.redo()
set(value) { }
_finishedFindingCharacters.value = value
}
var finishedFindingSentences: Boolean fun search(query: String, searchMode: SearchMode) {
get() = _finishedFindingSentences.value coroutineScope.launch {
set(value) { internalIsSearching.value = true
_finishedFindingSentences.value = value internalSearchResults.setAll(dictionary.search(query, searchMode))
internalIsSearching.value = false
} }
}
fun selectedEntryProperty(): ObjectProperty<DictionaryEntry?> = _selectedEntry fun findWordsBeginning() {
fun isSearchingProperty(): BooleanProperty = _isSearching coroutineScope.launch {
fun isFindingWordsBeginningProperty(): BooleanProperty = _isFindingWordsBeginning internalIsFindingWordsBeginning.value = true
fun isFindingWordsContainingProperty(): BooleanProperty = _isFindingWordsContaining internalWordsBeginning.setAll(
fun isFindingCharactersProperty(): BooleanProperty = _isFindingCharacters dictionary
fun isFindingSentencesProperty(): BooleanProperty = _isFindingSentences .findWordsBeginningWith(internalSelectedEntry.value)
fun finishedFindingWordsBeginning(): BooleanProperty = _finishedFindingWordsBeginning )
fun finishedFindingWordsContaining(): BooleanProperty = _finishedFindingWordsContaining internalIsFindingWordsBeginning.value = false
fun finishedFindingCharacters(): BooleanProperty = _finishedFindingCharacters internalFinishedFindingWordsBeginning.value = true
fun finishedFindingSentences(): BooleanProperty = _finishedFindingSentences }
}
fun findWordsContaining() {
coroutineScope.launch {
internalIsFindingWordsContaining.value = true
internalWordsContaining.setAll(
dictionary
.findWordsContaining(internalSelectedEntry.value)
)
internalIsFindingWordsContaining.value = false
internalFinishedFindingWordsContaining.value = true
}
}
fun findCharacters() {
coroutineScope.launch {
internalIsFindingCharacters.value = true
internalCharacters.setAll(
dictionary
.findCharactersOf(internalSelectedEntry.value)
)
internalIsFindingCharacters.value = false
internalFinishedFindingCharacters.value = true
}
}
fun findSentences() {
coroutineScope.launch {
internalIsFindingSentences.value = true
internalSentences.setAll(
dictionary
.findExampleSentencesFor(internalSelectedEntry.value)
)
internalIsFindingSentences.value = false
internalFinishedFindingSentences.value = true
}
}
fun selectEntry(entry: DictionaryEntry) {
undoManager.execute(object : Command {
private val previouslySelectedEntry = internalSelectedEntry.value
override fun execute() {
select(entry)
}
override fun undo() {
if (previouslySelectedEntry == null) return
select(previouslySelectedEntry)
}
})
}
private fun select(entry: DictionaryEntry) {
internalFinishedFindingCharacters.value = false
internalFinishedFindingWordsBeginning.value = false
internalFinishedFindingWordsContaining.value = false
internalFinishedFindingSentences.value = false
internalSelectedEntry.value = entry
}
} }

View File

@ -43,8 +43,10 @@ class WillowApplication : Application() {
} }
val dictionary = SqliteDictionary(connection) val dictionary = SqliteDictionary(connection)
val undoManager = UndoManager() val undoManager = UndoManager()
val model = Model(undoManager) val model = Model(
val interactor = Interactor(model, dictionary) dictionary,
undoManager
)
val config = Config() val config = Config()
config.load() config.load()
@ -55,10 +57,10 @@ class WillowApplication : Application() {
fxmlLoader.controllerFactory = Callback { type -> fxmlLoader.controllerFactory = Callback { type ->
when (type) { when (type) {
MainController::class.java -> MainController(model) MainController::class.java -> MainController(model)
MenuController::class.java -> MenuController(model, interactor, config) MenuController::class.java -> MenuController(model, config)
DetailsController::class.java -> DetailsController(model, interactor, config, hostServices) DetailsController::class.java -> DetailsController(model, config, hostServices)
SearchController::class.java -> SearchController(model, interactor) SearchController::class.java -> SearchController(model)
SearchResultsController::class.java -> SearchResultsController(model, interactor, config, hostServices) SearchResultsController::class.java -> SearchResultsController(model, config, hostServices)
else -> error("Trying to instantiate unknown controller type $type") else -> error("Trying to instantiate unknown controller type $type")
} }
} }

View File

@ -1,7 +1,7 @@
package com.marvinelsen.willow.domain package com.marvinelsen.willow.domain
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.entities.Sentence
interface Dictionary { interface Dictionary {
suspend fun search(query: String, searchMode: SearchMode): List<DictionaryEntry> suspend fun search(query: String, searchMode: SearchMode): List<DictionaryEntry>

View File

@ -8,8 +8,8 @@ import com.github.houbb.segment.support.segment.impl.Segments
import com.github.houbb.segment.support.segment.mode.impl.SegmentModes import com.github.houbb.segment.support.segment.mode.impl.SegmentModes
import com.github.houbb.segment.support.segment.result.impl.SegmentResultHandlers import com.github.houbb.segment.support.segment.result.impl.SegmentResultHandlers
import com.github.houbb.segment.support.tagging.pos.tag.impl.SegmentPosTaggings import com.github.houbb.segment.support.tagging.pos.tag.impl.SegmentPosTaggings
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.entities.Sentence
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -31,21 +31,21 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
.posData(SegmentPosDatas.define()) .posData(SegmentPosDatas.define())
private val searchSimplifiedSql = """ private val searchSimplifiedSql = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
FROM entry FROM entry
WHERE simplified GLOB ? WHERE simplified GLOB ?
ORDER BY character_count ASC ORDER BY character_count ASC
""".trimIndent() """.trimIndent()
private val searchTraditionalSql = """ private val searchTraditionalSql = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
FROM entry FROM entry
WHERE traditional GLOB ? WHERE traditional GLOB ?
ORDER BY character_count ASC ORDER BY character_count ASC
""".trimIndent() """.trimIndent()
private val searchPinyinSql = """ private val searchPinyinSql = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
FROM entry FROM entry
WHERE searchable_pinyin GLOB ? WHERE searchable_pinyin GLOB ?
OR searchable_pinyin_with_tone_numbers GLOB ? OR searchable_pinyin_with_tone_numbers GLOB ?
@ -54,21 +54,21 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
private val searchSegmentsSql = """ private val searchSegmentsSql = """
WITH cte(id, segment) AS (VALUES ?) WITH cte(id, segment) AS (VALUES ?)
SELECT entry.traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT entry.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.segment = entry.traditional OR cte.segment = entry.simplified ON cte.segment = entry.traditional OR cte.segment = entry.simplified
ORDER BY cte.id ORDER BY cte.id
""".trimIndent() """.trimIndent()
private val findWordsBeginningSql = """ private val findWordsBeginningSql = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
FROM entry FROM entry
WHERE traditional GLOB ? WHERE traditional GLOB ?
ORDER BY character_count ASC ORDER BY character_count ASC
""".trimIndent() """.trimIndent()
private val findWordsContainingSql = """ private val findWordsContainingSql = """
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions
FROM entry FROM entry
WHERE traditional LIKE ? WHERE traditional LIKE ?
ORDER BY character_count ASC ORDER BY character_count ASC
@ -76,7 +76,7 @@ class SqliteDictionary(private val connection: Connection) : Dictionary {
private val findCharactersSql = """ private val findCharactersSql = """
WITH cte(id, character, pinyin) AS (VALUES ?) WITH cte(id, character, pinyin) AS (VALUES ?)
SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions, tones 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
WHERE cte.pinyin = entry.pinyin_with_tone_numbers WHERE cte.pinyin = entry.pinyin_with_tone_numbers
@ -214,7 +214,6 @@ private fun ResultSet.toDictionaryEntry() = DictionaryEntry(
cedictDefinitions = Json.decodeFromString(this.getString(6)), cedictDefinitions = Json.decodeFromString(this.getString(6)),
crossStraitsDefinitions = Json.decodeFromString(this.getString(7)), crossStraitsDefinitions = Json.decodeFromString(this.getString(7)),
moedictDefinitions = Json.decodeFromString(this.getString(8)), moedictDefinitions = Json.decodeFromString(this.getString(8)),
tones = Json.decodeFromString(this.getString(9)),
) )
private fun ResultSet.toListOfDictionaryEntries() = buildList { private fun ResultSet.toListOfDictionaryEntries() = buildList {

View File

@ -1,7 +1,7 @@
package com.marvinelsen.willow.domain.objects package com.marvinelsen.willow.domain.entities
import com.marvinelsen.willow.domain.objects.definitions.CrossStraitsDefinition import com.marvinelsen.willow.domain.entities.definitions.CrossStraitsDefinition
import com.marvinelsen.willow.domain.objects.definitions.MoedictDefinition import com.marvinelsen.willow.domain.entities.definitions.MoedictDefinition
data class DictionaryEntry( data class DictionaryEntry(
val traditional: String, val traditional: String,
@ -12,5 +12,4 @@ data class DictionaryEntry(
val cedictDefinitions: List<List<String>>, val cedictDefinitions: List<List<String>>,
val crossStraitsDefinitions: List<CrossStraitsDefinition>, val crossStraitsDefinitions: List<CrossStraitsDefinition>,
val moedictDefinitions: List<MoedictDefinition>, val moedictDefinitions: List<MoedictDefinition>,
val tones: List<Int>,
) )

View File

@ -1,4 +1,4 @@
package com.marvinelsen.willow.domain.objects package com.marvinelsen.willow.domain.entities
data class Sentence( data class Sentence(
val traditional: String, val traditional: String,

View File

@ -1,4 +1,4 @@
package com.marvinelsen.willow.domain.objects.definitions package com.marvinelsen.willow.domain.entities.definitions
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.marvinelsen.willow.domain.objects.definitions package com.marvinelsen.willow.domain.entities.definitions
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -2,8 +2,8 @@ package com.marvinelsen.willow.ui.cells
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Pronunciation import com.marvinelsen.willow.config.Pronunciation
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.ui.components.TextFlowWithToneColors import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.ui.util.createContextMenuForEntry import com.marvinelsen.willow.ui.util.createContextMenuForEntry
import javafx.application.HostServices import javafx.application.HostServices
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
@ -39,8 +39,8 @@ private class EntryCell(
private val hostServices: HostServices, private val hostServices: HostServices,
) : ListCell<DictionaryEntry?>() { ) : ListCell<DictionaryEntry?>() {
private val textFlowHeadword by lazy { private val labelHeadword by lazy {
TextFlowWithToneColors(config).apply { Label().apply {
styleClass.add("headword") styleClass.add("headword")
styleProperty().bind( styleProperty().bind(
Bindings.concat( Bindings.concat(
@ -83,7 +83,7 @@ private class EntryCell(
} }
private val flowPane by lazy { private val flowPane by lazy {
FlowPane(textFlowHeadword, labelPronunciation).apply { FlowPane(labelHeadword, labelPronunciation).apply {
hgap = FLOW_PANE_HGAP hgap = FLOW_PANE_HGAP
rowValignment = VPos.BASELINE rowValignment = VPos.BASELINE
} }
@ -105,8 +105,17 @@ private class EntryCell(
graphic = null graphic = null
contextMenu = null contextMenu = null
} else { } else {
textFlowHeadword.entry.value = entry labelHeadword.textProperty().bind(
Bindings.createStringBinding(
{
when (config.script.value!!) {
Script.SIMPLIFIED -> entry.simplified
Script.TRADITIONAL -> entry.traditional
}
},
config.script
)
)
labelPronunciation.textProperty().bind( labelPronunciation.textProperty().bind(
Bindings.createStringBinding( Bindings.createStringBinding(
{ {
@ -135,7 +144,6 @@ private class EntryCell(
else -> error("No definition for entry") else -> error("No definition for entry")
} }
labelDefinition.text = definition labelDefinition.text = definition
contextMenu = createContextMenuForEntry(entry, resources, hostServices) contextMenu = createContextMenuForEntry(entry, resources, hostServices)

View File

@ -2,7 +2,7 @@ package com.marvinelsen.willow.ui.cells
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Script import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.entities.Sentence
import com.marvinelsen.willow.ui.util.createContextMenuForSentence import com.marvinelsen.willow.ui.util.createContextMenuForSentence
import javafx.application.HostServices import javafx.application.HostServices
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings

View File

@ -1,55 +0,0 @@
package com.marvinelsen.willow.ui.components
import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.domain.objects.DictionaryEntry
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.paint.Color
import javafx.scene.text.Text
import javafx.scene.text.TextFlow
class TextFlowWithToneColors(private val config: Config) : TextFlow() {
init {
config.script.addListener { _, _, newValue ->
updateTextFlow(newValue)
}
}
val entry = SimpleObjectProperty<DictionaryEntry?>().apply {
addListener { _, _, _ ->
updateTextFlow(config.script.value)
}
}
private val invalidCharacterRegex = """[a-zA-Z0-9·\[\]{}()+]""".toRegex()
@Suppress("MagicNumber")
private fun updateTextFlow(script: Script) {
children.clear()
val headword = when (script) {
Script.SIMPLIFIED -> entry.value?.simplified
Script.TRADITIONAL -> entry.value?.traditional
} ?: return
var index = 0
for (char in headword) {
val text = Text(char.toString())
val color = if (invalidCharacterRegex.matches(char.toString())) {
Color.BLACK
} else {
when (entry.value?.tones?.getOrNull(index++)) {
1 -> Color.RED
2 -> Color.GREEN
3 -> Color.BLUE
4 -> Color.PURPLE
5 -> Color.DARKGRAY
else -> Color.BLACK
}
}
text.fill = color
children.add(text)
}
}
}

View File

@ -1,12 +1,11 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Interactor
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.config.Config 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.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.entities.Sentence
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.cells.SentenceCellFactory
import com.marvinelsen.willow.ui.util.createContextMenuForEntry import com.marvinelsen.willow.ui.util.createContextMenuForEntry
@ -35,7 +34,6 @@ import java.util.ResourceBundle
@Suppress("UnusedPrivateMember", "TooManyFunctions") @Suppress("UnusedPrivateMember", "TooManyFunctions")
class DetailsController( class DetailsController(
private val model: Model, private val model: Model,
private val interactor: Interactor,
private val config: Config, private val config: Config,
private val hostServices: HostServices, private val hostServices: HostServices,
) { ) {
@ -114,13 +112,14 @@ class DetailsController(
textProperty().bind( textProperty().bind(
Bindings.createStringBinding( Bindings.createStringBinding(
{ {
val selectedEntry = model.selectedEntry.value
when (config.script.value!!) { when (config.script.value!!) {
Script.SIMPLIFIED -> model.selectedEntry?.simplified Script.SIMPLIFIED -> selectedEntry?.simplified
Script.TRADITIONAL -> model.selectedEntry?.traditional Script.TRADITIONAL -> selectedEntry?.traditional
} }
}, },
config.script, config.script,
model.selectedEntryProperty() model.selectedEntry
) )
) )
styleProperty().bind( styleProperty().bind(
@ -138,14 +137,15 @@ class DetailsController(
textProperty().bind( textProperty().bind(
Bindings.createStringBinding( Bindings.createStringBinding(
{ {
val selectedEntry = model.selectedEntry.value
when (config.details.pronunciation.value!!) { when (config.details.pronunciation.value!!) {
Pronunciation.PINYIN_WITH_TONE_MARKS -> model.selectedEntry?.pinyinWithToneMarks Pronunciation.PINYIN_WITH_TONE_MARKS -> selectedEntry?.pinyinWithToneMarks
Pronunciation.PINYIN_WITH_TONE_NUMBERS -> model.selectedEntry?.pinyinWithToneNumbers Pronunciation.PINYIN_WITH_TONE_NUMBERS -> selectedEntry?.pinyinWithToneNumbers
Pronunciation.ZHUYIN -> model.selectedEntry?.zhuyin Pronunciation.ZHUYIN -> selectedEntry?.zhuyin
} }
}, },
config.details.pronunciation, config.details.pronunciation,
model.selectedEntryProperty() model.selectedEntry
) )
) )
styleProperty().bind( styleProperty().bind(
@ -160,16 +160,16 @@ class DetailsController(
private fun initializeTabPaneDetails() { private fun initializeTabPaneDetails() {
tabPaneDetails.apply { tabPaneDetails.apply {
disableProperty().bind(Bindings.isNull(model.selectedEntryProperty())) disableProperty().bind(Bindings.isNull(model.selectedEntry))
selectionModel.selectedItemProperty().addListener { _, _, selectedTab -> selectionModel.selectedItemProperty().addListener { _, _, selectedTab ->
if (model.selectedEntry == null) return@addListener if (model.selectedEntry.value == null) return@addListener
lazyUpdateTabContent(selectedTab.id) lazyUpdateTabContent(selectedTab.id)
} }
} }
model.selectedEntryProperty().addListener { _, _, newEntry -> model.selectedEntry.addListener { _, _, newEntry ->
if (newEntry == null) return@addListener if (newEntry == null) return@addListener
tabPaneDetails.selectionModel.select(0) tabPaneDetails.selectionModel.select(0)
@ -178,9 +178,9 @@ class DetailsController(
tabCharacters.disableProperty().bind( tabCharacters.disableProperty().bind(
Bindings.createBooleanBinding( Bindings.createBooleanBinding(
{ {
(model.selectedEntry?.traditional?.length ?: 0) < 2 (model.selectedEntry.value?.traditional?.length ?: 0) < 2
}, },
model.selectedEntryProperty() model.selectedEntry
) )
) )
} }
@ -190,12 +190,12 @@ class DetailsController(
cellFactory = SentenceCellFactory(resources, config, hostServices) cellFactory = SentenceCellFactory(resources, config, hostServices)
items = model.sentences items = model.sentences
disableProperty().bind(Bindings.or(model.isFindingSentencesProperty(), Bindings.isEmpty(model.sentences))) disableProperty().bind(Bindings.or(model.isFindingSentences, Bindings.isEmpty(model.sentences)))
} }
progressIndicatorSentences.visibleProperty().bind(model.isFindingSentencesProperty()) progressIndicatorSentences.visibleProperty().bind(model.isFindingSentences)
labelNoSentencesFound labelNoSentencesFound
.visibleProperty() .visibleProperty()
.bind(Bindings.and(Bindings.isEmpty(model.sentences), Bindings.not(model.isFindingSentencesProperty()))) .bind(Bindings.and(Bindings.isEmpty(model.sentences), Bindings.not(model.isFindingSentences)))
} }
private fun initializeListViewWordsContaining() { private fun initializeListViewWordsContaining() {
@ -203,24 +203,17 @@ class DetailsController(
cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) cellFactory = DictionaryEntryCellFactory(resources, config, hostServices)
items = model.wordsContaining items = model.wordsContaining
disableProperty().bind( disableProperty().bind(Bindings.or(model.isFindingWordsContaining, Bindings.isEmpty(model.wordsContaining)))
Bindings.or(model.isFindingWordsContainingProperty(), Bindings.isEmpty(model.wordsContaining))
)
selectionModel.selectedItemProperty().addListener { _, _, newEntry -> selectionModel.selectedItemProperty().addListener { _, _, newEntry ->
if (newEntry == null) return@addListener if (newEntry == null) return@addListener
interactor.deepDive(newEntry) model.selectEntry(newEntry)
} }
} }
progressIndicatorWordsContaining.visibleProperty().bind(model.isFindingWordsContainingProperty()) progressIndicatorWordsContaining.visibleProperty().bind(model.isFindingWordsContaining)
labelNoWordsContainingFound labelNoWordsContainingFound
.visibleProperty() .visibleProperty()
.bind( .bind(Bindings.and(Bindings.isEmpty(model.wordsContaining), Bindings.not(model.isFindingWordsContaining)))
Bindings.and(
Bindings.isEmpty(model.wordsContaining),
Bindings.not(model.isFindingWordsContainingProperty())
)
)
} }
private fun initializeListViewWordsBeginning() { private fun initializeListViewWordsBeginning() {
@ -228,24 +221,17 @@ class DetailsController(
cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) cellFactory = DictionaryEntryCellFactory(resources, config, hostServices)
items = model.wordsBeginning items = model.wordsBeginning
disableProperty().bind( disableProperty().bind(Bindings.or(model.isFindingWordsBeginning, Bindings.isEmpty(model.wordsBeginning)))
Bindings.or(model.isFindingWordsBeginningProperty(), Bindings.isEmpty(model.wordsBeginning))
)
selectionModel.selectedItemProperty().addListener { _, _, newEntry -> selectionModel.selectedItemProperty().addListener { _, _, newEntry ->
if (newEntry == null) return@addListener if (newEntry == null) return@addListener
interactor.deepDive(newEntry) model.selectEntry(newEntry)
} }
} }
progressIndicatorWordsBeginning.visibleProperty().bind(model.isFindingWordsBeginningProperty()) progressIndicatorWordsBeginning.visibleProperty().bind(model.isFindingWordsBeginning)
labelNoWordsBeginningFound labelNoWordsBeginningFound
.visibleProperty() .visibleProperty()
.bind( .bind(Bindings.and(Bindings.isEmpty(model.wordsBeginning), Bindings.not(model.isFindingWordsBeginning)))
Bindings.and(
Bindings.isEmpty(model.wordsBeginning),
Bindings.not(model.isFindingWordsBeginningProperty())
)
)
} }
private fun initializeListViewCharacters() { private fun initializeListViewCharacters() {
@ -253,17 +239,17 @@ class DetailsController(
cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) cellFactory = DictionaryEntryCellFactory(resources, config, hostServices)
items = model.characters items = model.characters
disableProperty().bind(Bindings.or(model.isFindingCharactersProperty(), Bindings.isEmpty(model.characters))) disableProperty().bind(Bindings.or(model.isFindingCharacters, Bindings.isEmpty(model.characters)))
selectionModel.selectedItemProperty().addListener { _, _, newEntry -> selectionModel.selectedItemProperty().addListener { _, _, newEntry ->
if (newEntry == null) return@addListener if (newEntry == null) return@addListener
interactor.deepDive(newEntry) model.selectEntry(newEntry)
} }
} }
progressIndicatorCharacters.visibleProperty().bind(model.isFindingCharactersProperty()) progressIndicatorCharacters.visibleProperty().bind(model.isFindingCharacters)
labelNoCharactersFound labelNoCharactersFound
.visibleProperty() .visibleProperty()
.bind(Bindings.and(Bindings.isEmpty(model.characters), Bindings.not(model.isFindingCharactersProperty()))) .bind(Bindings.and(Bindings.isEmpty(model.characters), Bindings.not(model.isFindingCharacters)))
} }
private fun initializeWebViewDefinition() { private fun initializeWebViewDefinition() {
@ -271,7 +257,7 @@ class DetailsController(
engine.userStyleSheetLocation = this::class.java.getResource("/css/definitions.css")!!.toExternalForm() engine.userStyleSheetLocation = this::class.java.getResource("/css/definitions.css")!!.toExternalForm()
} }
model.selectedEntryProperty().addListener { _, _, newEntry -> model.selectedEntry.addListener { _, _, newEntry ->
if (newEntry == null) return@addListener if (newEntry == null) return@addListener
webViewDefinition.engine.loadContent(createDefinitionHtml(newEntry)) webViewDefinition.engine.loadContent(createDefinitionHtml(newEntry))
@ -280,9 +266,9 @@ class DetailsController(
@FXML @FXML
private fun headerOnContextMenuRequested(contextMenuEvent: ContextMenuEvent) { private fun headerOnContextMenuRequested(contextMenuEvent: ContextMenuEvent) {
if (model.selectedEntry == null) return if (model.selectedEntry.value == null) return
createContextMenuForEntry(model.selectedEntry!!, resources, hostServices).show( createContextMenuForEntry(model.selectedEntry.value, resources, hostServices).show(
flowPaneHeader.scene.window, flowPaneHeader.scene.window,
contextMenuEvent.screenX, contextMenuEvent.screenX,
contextMenuEvent.screenY contextMenuEvent.screenY
@ -293,24 +279,24 @@ class DetailsController(
private fun lazyUpdateTabContent(selectedTabId: String?) { private fun lazyUpdateTabContent(selectedTabId: String?) {
when (selectedTabId) { when (selectedTabId) {
"tabWords" -> { "tabWords" -> {
if (!model.finishedFindingWordsContaining) { if (!model.finishedFindingWordsContaining.value) {
interactor.findWordsContaining() model.findWordsContaining()
} }
if (!model.finishedFindingWordsBeginning) { if (!model.finishedFindingWordsBeginning.value) {
interactor.findWordsBeginning() model.findWordsBeginning()
} }
} }
"tabCharacters" -> { "tabCharacters" -> {
if (model.finishedFindingCharacters) return if (model.finishedFindingCharacters.value) return
interactor.findCharacters() model.findCharacters()
} }
"tabSentences" -> { "tabSentences" -> {
if (model.finishedFindingSentences) return if (model.finishedFindingSentences.value) return
interactor.findSentences() model.findSentences()
} }
else -> {} else -> {}

View File

@ -1,6 +1,5 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Interactor
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.ui.dialogs.PreferencesDialog import com.marvinelsen.willow.ui.dialogs.PreferencesDialog
@ -12,7 +11,7 @@ import javafx.scene.control.MenuItem
import java.util.ResourceBundle import java.util.ResourceBundle
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
class MenuController(private val model: Model, private val interactor: Interactor, private val config: Config) { class MenuController(private val model: Model, private val config: Config) {
@FXML @FXML
private lateinit var resources: ResourceBundle private lateinit var resources: ResourceBundle
@ -27,8 +26,8 @@ class MenuController(private val model: Model, private val interactor: Interacto
@FXML @FXML
private fun initialize() { private fun initialize() {
menuItemCopyPronunciation.disableProperty().bind(Bindings.isNull(model.selectedEntryProperty())) menuItemCopyPronunciation.disableProperty().bind(Bindings.isNull(model.selectedEntry))
menuItemCopyHeadword.disableProperty().bind(Bindings.isNull(model.selectedEntryProperty())) menuItemCopyHeadword.disableProperty().bind(Bindings.isNull(model.selectedEntry))
} }
@FXML @FXML
@ -48,11 +47,11 @@ class MenuController(private val model: Model, private val interactor: Interacto
@FXML @FXML
private fun onMenuItemCopyPronunciationAction() { private fun onMenuItemCopyPronunciationAction() {
interactor.copyPronunciationOfSelectedEntry() model.copyPronunciationOfSelectedEntry()
} }
@FXML @FXML
private fun onMenuItemCopyHeadwordAction() { private fun onMenuItemCopyHeadwordAction() {
interactor.copyHeadwordOfSelectedEntry() model.copyHeadwordOfSelectedEntry()
} }
} }

View File

@ -1,6 +1,5 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Interactor
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.domain.SearchMode import com.marvinelsen.willow.domain.SearchMode
import javafx.fxml.FXML import javafx.fxml.FXML
@ -8,7 +7,7 @@ import javafx.scene.control.Button
import javafx.scene.control.TextField import javafx.scene.control.TextField
import javafx.scene.control.ToggleGroup import javafx.scene.control.ToggleGroup
class SearchController(private val model: Model, private val interactor: Interactor) { class SearchController(private val model: Model) {
@FXML @FXML
private lateinit var buttonUndo: Button private lateinit var buttonUndo: Button
@ -39,14 +38,14 @@ class SearchController(private val model: Model, private val interactor: Interac
return return
} }
interactor.search(searchQuery, searchMode) model.search(searchQuery, searchMode)
} }
fun onButtonRedoAction() { fun onButtonRedoAction() {
interactor.redoSelection() model.redoSelection()
} }
fun onButtonUndoAction() { fun onButtonUndoAction() {
interactor.undoSelection() model.undoSelection()
} }
} }

View File

@ -1,9 +1,8 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Interactor
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory
import javafx.application.HostServices import javafx.application.HostServices
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
@ -15,7 +14,6 @@ import java.util.ResourceBundle
class SearchResultsController( class SearchResultsController(
private val model: Model, private val model: Model,
private val interactor: Interactor,
private val config: Config, private val config: Config,
private val hostServices: HostServices, private val hostServices: HostServices,
) { ) {
@ -40,18 +38,18 @@ class SearchResultsController(
listViewSearchResults.items = model.searchResults listViewSearchResults.items = model.searchResults
listViewSearchResults listViewSearchResults
.disableProperty() .disableProperty()
.bind(Bindings.or(model.isSearchingProperty(), Bindings.isEmpty(model.searchResults))) .bind(Bindings.or(model.isSearching, Bindings.isEmpty(model.searchResults)))
progressIndicatorEntries.visibleProperty().bind(model.isSearchingProperty()) progressIndicatorEntries.visibleProperty().bind(model.isSearching)
labelNoEntriesFound labelNoEntriesFound
.visibleProperty() .visibleProperty()
.bind(Bindings.and(Bindings.isEmpty(model.searchResults), Bindings.not(model.isSearchingProperty()))) .bind(Bindings.and(Bindings.isEmpty(model.searchResults), Bindings.not(model.isSearching)))
listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntry? -> listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntry? ->
if (newValue == null) { if (newValue == null) {
return@addListener return@addListener
} }
interactor.normalSelect(newValue) model.selectEntry(newValue)
} }
} }
} }

View File

@ -49,6 +49,13 @@ class PreferencesDialogController(private val config: Config) {
@FXML @FXML
private lateinit var spinnerDefinitionFontSizeSearchResults: Spinner<Int> private lateinit var spinnerDefinitionFontSizeSearchResults: Spinner<Int>
private val entryHeadwordFontSizeObjectProperty = config.details.headwordFontSize.asObject()
private val entryPronunciationFontSizeObjectProperty = config.details.pronunciationFontSize.asObject()
private val searchResultHeadwordFontSizeObjectProperty = config.searchResults.headwordFontSize.asObject()
private val searchResultPronunciationFontSizeObjectProperty = config.searchResults.pronunciationFontSize.asObject()
private val searchResultDefinitionFontSizeObjectProperty = config.searchResults.definitionFontSize.asObject()
@FXML @FXML
private fun initialize() { private fun initialize() {
comboBoxLocale.items.addAll( comboBoxLocale.items.addAll(
@ -75,42 +82,27 @@ class PreferencesDialogController(private val config: Config) {
with(spinnerHeadwordFontSizeDetails) { with(spinnerHeadwordFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter() editor.textFormatter = FontSizeTextFormatter()
valueFactory.value = config.details.headwordFontSize.value valueFactory.valueProperty().bindBidirectional(entryHeadwordFontSizeObjectProperty)
valueProperty().addListener { _, _, newValue ->
config.details.headwordFontSize.value = newValue
}
} }
with(spinnerPronunciationFontSizeDetails) { with(spinnerPronunciationFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter() editor.textFormatter = FontSizeTextFormatter()
valueFactory.value = config.details.pronunciationFontSize.value valueFactory.valueProperty().bindBidirectional(entryPronunciationFontSizeObjectProperty)
valueProperty().addListener { _, _, newValue ->
config.details.pronunciationFontSize.value = newValue
}
} }
with(spinnerHeadwordFontSizeSearchResults) { with(spinnerHeadwordFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter() editor.textFormatter = FontSizeTextFormatter()
valueFactory.value = config.searchResults.headwordFontSize.value valueFactory.valueProperty().bindBidirectional(searchResultHeadwordFontSizeObjectProperty)
valueProperty().addListener { _, _, newValue ->
config.searchResults.headwordFontSize.value = newValue
}
} }
with(spinnerPronunciationFontSizeSearchResults) { with(spinnerPronunciationFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter() editor.textFormatter = FontSizeTextFormatter()
valueFactory.value = config.searchResults.pronunciationFontSize.value valueFactory.valueProperty().bindBidirectional(searchResultPronunciationFontSizeObjectProperty)
valueProperty().addListener { _, _, newValue ->
config.searchResults.pronunciationFontSize.value = newValue
}
} }
with(spinnerDefinitionFontSizeSearchResults) { with(spinnerDefinitionFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter() editor.textFormatter = FontSizeTextFormatter()
valueFactory.value = config.searchResults.definitionFontSize.value valueFactory.valueProperty().bindBidirectional(searchResultDefinitionFontSizeObjectProperty)
valueProperty().addListener { _, _, newValue ->
config.searchResults.definitionFontSize.value = newValue
}
} }
} }
} }

View File

@ -15,7 +15,7 @@ class UndoManager {
undoStack.push(command).execute() undoStack.push(command).execute()
canUndoProperty.value = !undoStack.isEmpty() canUndoProperty.value = undoStack.size > 1
canRedoProperty.value = false canRedoProperty.value = false
} }
@ -24,7 +24,7 @@ class UndoManager {
redoStack.push(undoStack.pop()).undo() redoStack.push(undoStack.pop()).undo()
canUndoProperty.value = !undoStack.isEmpty() canUndoProperty.value = undoStack.size > 1
canRedoProperty.value = true canRedoProperty.value = true
} }
@ -36,11 +36,4 @@ class UndoManager {
canUndoProperty.value = true canUndoProperty.value = true
canRedoProperty.value = !redoStack.isEmpty() canRedoProperty.value = !redoStack.isEmpty()
} }
fun reset() {
undoStack.clear()
redoStack.clear()
canUndoProperty.value = false
canRedoProperty.value = false
}
} }

View File

@ -1,7 +1,7 @@
package com.marvinelsen.willow.ui.util package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.domain.objects.DictionaryEntry import com.marvinelsen.willow.domain.entities.DictionaryEntry
import com.marvinelsen.willow.domain.objects.Sentence import com.marvinelsen.willow.domain.entities.Sentence
import javafx.application.HostServices import javafx.application.HostServices
import javafx.event.EventHandler import javafx.event.EventHandler
import javafx.scene.control.ContextMenu import javafx.scene.control.ContextMenu