Refactor model and interactor
All checks were successful
Pull Request / build (pull_request) Successful in 4m24s

This commit is contained in:
Marvin Elsen 2024-11-15 14:52:29 +01:00
parent 46ddb69a52
commit a2dfc858f4
Signed by: marvinelsen
GPG Key ID: 820672408CC318C2
19 changed files with 348 additions and 269 deletions

View File

@ -0,0 +1,127 @@
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

@ -0,0 +1,110 @@
package com.marvinelsen.willow
import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.domain.objects.Sentence
import com.marvinelsen.willow.ui.undo.UndoManager
import javafx.beans.property.BooleanProperty
import javafx.beans.property.ObjectProperty
import javafx.beans.property.ReadOnlyBooleanProperty
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.collections.ObservableList
@Suppress("TooManyFunctions")
class Model(
val undoManager: UndoManager,
) {
private val _selectedEntry: ObjectProperty<DictionaryEntry?> = SimpleObjectProperty<DictionaryEntry>()
private val _isSearching: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val _isFindingSentences: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val _finishedFindingSentences: 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 canRedo: ReadOnlyBooleanProperty = undoManager.canRedoProperty
var selectedEntry: DictionaryEntry?
get() = _selectedEntry.value
set(value) {
_selectedEntry.value = value
}
var isSearching: Boolean
get() = _isSearching.value
set(value) {
_isSearching.value = value
}
var isFindingWordsBeginning: Boolean
get() = _isFindingWordsBeginning.value
set(value) {
_isFindingWordsBeginning.value = value
}
var isFindingWordsContaining: Boolean
get() = _isFindingWordsContaining.value
set(value) {
_isFindingWordsContaining.value = value
}
var isFindingCharacters: Boolean
get() = _isFindingCharacters.value
set(value) {
_isFindingCharacters.value = value
}
var isFindingSentences: Boolean
get() = _isFindingSentences.value
set(value) {
_isFindingSentences.value = value
}
var finishedFindingWordsBeginning: Boolean
get() = _finishedFindingWordsBeginning.value
set(value) {
_finishedFindingWordsBeginning.value = value
}
var finishedFindingWordsContaining: Boolean
get() = _finishedFindingWordsContaining.value
set(value) {
_finishedFindingWordsContaining.value = value
}
var finishedFindingCharacters: Boolean
get() = _finishedFindingCharacters.value
set(value) {
_finishedFindingCharacters.value = value
}
var finishedFindingSentences: Boolean
get() = _finishedFindingSentences.value
set(value) {
_finishedFindingSentences.value = value
}
fun selectedEntryProperty(): ObjectProperty<DictionaryEntry?> = _selectedEntry
fun isSearchingProperty(): BooleanProperty = _isSearching
fun isFindingWordsBeginningProperty(): BooleanProperty = _isFindingWordsBeginning
fun isFindingWordsContainingProperty(): BooleanProperty = _isFindingWordsContaining
fun isFindingCharactersProperty(): BooleanProperty = _isFindingCharacters
fun isFindingSentencesProperty(): BooleanProperty = _isFindingSentences
fun finishedFindingWordsBeginning(): BooleanProperty = _finishedFindingWordsBeginning
fun finishedFindingWordsContaining(): BooleanProperty = _finishedFindingWordsContaining
fun finishedFindingCharacters(): BooleanProperty = _finishedFindingCharacters
fun finishedFindingSentences(): BooleanProperty = _finishedFindingSentences
}

View File

@ -1,174 +0,0 @@
package com.marvinelsen.willow
import com.marvinelsen.willow.domain.SearchMode
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.util.ClipboardHelper
import javafx.beans.property.BooleanProperty
import javafx.beans.property.ObjectProperty
import javafx.beans.property.ReadOnlyBooleanProperty
import javafx.beans.property.ReadOnlyObjectProperty
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
@Suppress("TooManyFunctions")
class State(
private val dictionary: SqliteDictionary,
private val undoManager: UndoManager,
) {
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 internalIsSearching: BooleanProperty = SimpleBooleanProperty(false)
private val internalIsFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val internalIsFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val internalIsFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val internalIsFindingSentences: BooleanProperty = SimpleBooleanProperty(false)
private val internalFinishedFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false)
private val internalFinishedFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false)
private val internalFinishedFindingCharacters: BooleanProperty = SimpleBooleanProperty(false)
private val internalFinishedFindingSentences: BooleanProperty = SimpleBooleanProperty(false)
val canUndo: ReadOnlyBooleanProperty = undoManager.canUndoProperty
val canRedo: ReadOnlyBooleanProperty = undoManager.canRedoProperty
val selectedEntry: ReadOnlyObjectProperty<DictionaryEntry> = internalSelectedEntry
val searchResults: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalSearchResults)
val wordsBeginning: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalWordsBeginning)
val wordsContaining: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalWordsContaining)
val characters: ObservableList<DictionaryEntry> =
FXCollections.unmodifiableObservableList(internalCharacters)
val sentences: ObservableList<Sentence> =
FXCollections.unmodifiableObservableList(internalSentences)
val isSearching: ReadOnlyBooleanProperty = internalIsSearching
val isFindingWordsBeginning: ReadOnlyBooleanProperty = internalIsFindingWordsBeginning
val isFindingWordsContaining: ReadOnlyBooleanProperty = internalIsFindingWordsContaining
val isFindingCharacters: ReadOnlyBooleanProperty = internalIsFindingCharacters
val isFindingSentences: ReadOnlyBooleanProperty = internalIsFindingSentences
val finishedFindingWordsBeginning: ReadOnlyBooleanProperty = internalFinishedFindingWordsBeginning
val finishedFindingWordsContaining: ReadOnlyBooleanProperty = internalFinishedFindingWordsContaining
val finishedFindingCharacters: ReadOnlyBooleanProperty = internalFinishedFindingCharacters
val finishedFindingSentences: ReadOnlyBooleanProperty = internalFinishedFindingSentences
private val coroutineScope = MainScope()
fun copyHeadwordOfSelectedEntry() {
ClipboardHelper.copyString(internalSelectedEntry.value.traditional)
}
fun copyPronunciationOfSelectedEntry() {
ClipboardHelper.copyString(internalSelectedEntry.value.pinyinWithToneMarks)
}
fun undoSelection() {
undoManager.undo()
}
fun redoSelection() {
undoManager.redo()
}
fun search(query: String, searchMode: SearchMode) {
coroutineScope.launch {
internalIsSearching.value = true
internalSearchResults.setAll(dictionary.search(query, searchMode))
internalIsSearching.value = false
}
}
fun findWordsBeginning() {
coroutineScope.launch {
internalIsFindingWordsBeginning.value = true
internalWordsBeginning.setAll(
dictionary
.findWordsBeginningWith(internalSelectedEntry.value)
)
internalIsFindingWordsBeginning.value = false
internalFinishedFindingWordsBeginning.value = true
}
}
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 deepDive(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)
}
})
}
fun normalSelect(entry: DictionaryEntry) {
undoManager.reset()
select(entry)
}
private fun select(entry: DictionaryEntry) {
internalFinishedFindingCharacters.value = false
internalFinishedFindingWordsBeginning.value = false
internalFinishedFindingWordsContaining.value = false
internalFinishedFindingSentences.value = false
internalSelectedEntry.value = entry
}
}

View File

@ -43,10 +43,8 @@ class WillowApplication : Application() {
} }
val dictionary = SqliteDictionary(connection) val dictionary = SqliteDictionary(connection)
val undoManager = UndoManager() val undoManager = UndoManager()
val state = State( val model = Model(undoManager)
dictionary, val interactor = Interactor(model, dictionary)
undoManager
)
val config = Config() val config = Config()
config.load() config.load()
@ -56,11 +54,11 @@ class WillowApplication : Application() {
fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", config.locale.value) fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", config.locale.value)
fxmlLoader.controllerFactory = Callback { type -> fxmlLoader.controllerFactory = Callback { type ->
when (type) { when (type) {
MainController::class.java -> MainController(state) MainController::class.java -> MainController(model)
MenuController::class.java -> MenuController(state, config) MenuController::class.java -> MenuController(model, interactor, config)
DetailsController::class.java -> DetailsController(state, config, hostServices) DetailsController::class.java -> DetailsController(model, interactor, config, hostServices)
SearchController::class.java -> SearchController(state) SearchController::class.java -> SearchController(model, interactor)
SearchResultsController::class.java -> SearchResultsController(state, config, hostServices) SearchResultsController::class.java -> SearchResultsController(model, interactor, 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.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.domain.entities.Sentence import com.marvinelsen.willow.domain.objects.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.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.domain.entities.Sentence import com.marvinelsen.willow.domain.objects.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

View File

@ -1,7 +1,7 @@
package com.marvinelsen.willow.domain.entities package com.marvinelsen.willow.domain.objects
import com.marvinelsen.willow.domain.entities.definitions.CrossStraitsDefinition import com.marvinelsen.willow.domain.objects.definitions.CrossStraitsDefinition
import com.marvinelsen.willow.domain.entities.definitions.MoedictDefinition import com.marvinelsen.willow.domain.objects.definitions.MoedictDefinition
data class DictionaryEntry( data class DictionaryEntry(
val traditional: String, val traditional: String,

View File

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

View File

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

View File

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

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.Pronunciation import com.marvinelsen.willow.config.Pronunciation
import com.marvinelsen.willow.domain.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.ui.components.TextFlowWithToneColors import com.marvinelsen.willow.ui.components.TextFlowWithToneColors
import com.marvinelsen.willow.ui.util.createContextMenuForEntry import com.marvinelsen.willow.ui.util.createContextMenuForEntry
import javafx.application.HostServices import javafx.application.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.entities.Sentence import com.marvinelsen.willow.domain.objects.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

@ -2,7 +2,7 @@ package com.marvinelsen.willow.ui.components
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.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.DictionaryEntry
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.scene.text.Text import javafx.scene.text.Text

View File

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

View File

@ -1,10 +1,10 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.State import com.marvinelsen.willow.Model
import javafx.fxml.FXML import javafx.fxml.FXML
@Suppress("UnusedPrivateProperty", "UnusedPrivateMember") @Suppress("UnusedPrivateProperty", "UnusedPrivateMember")
class MainController(private val state: State) { class MainController(private val model: Model) {
@FXML @FXML
private fun initialize() { private fun initialize() {
// no-op // no-op

View File

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

View File

@ -1,13 +1,14 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.State import com.marvinelsen.willow.Interactor
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
import javafx.scene.control.Button 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 state: State) { class SearchController(private val model: Model, private val interactor: Interactor) {
@FXML @FXML
private lateinit var buttonUndo: Button private lateinit var buttonUndo: Button
@ -26,8 +27,8 @@ class SearchController(private val state: State) {
textFieldSearch.textProperty().addListener { _, _, _ -> search() } textFieldSearch.textProperty().addListener { _, _, _ -> search() }
searchModeToggleGroup.selectedToggleProperty().addListener { _, _, _ -> search() } searchModeToggleGroup.selectedToggleProperty().addListener { _, _, _ -> search() }
buttonUndo.disableProperty().bind(state.canUndo.not()) buttonUndo.disableProperty().bind(model.canUndo.not())
buttonRedo.disableProperty().bind(state.canRedo.not()) buttonRedo.disableProperty().bind(model.canRedo.not())
} }
private fun search() { private fun search() {
@ -38,14 +39,14 @@ class SearchController(private val state: State) {
return return
} }
state.search(searchQuery, searchMode) interactor.search(searchQuery, searchMode)
} }
fun onButtonRedoAction() { fun onButtonRedoAction() {
state.redoSelection() interactor.redoSelection()
} }
fun onButtonUndoAction() { fun onButtonUndoAction() {
state.undoSelection() interactor.undoSelection()
} }
} }

View File

@ -1,8 +1,9 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.State import com.marvinelsen.willow.Interactor
import com.marvinelsen.willow.Model
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.domain.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.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
@ -13,7 +14,8 @@ import javafx.scene.control.ProgressIndicator
import java.util.ResourceBundle import java.util.ResourceBundle
class SearchResultsController( class SearchResultsController(
private val state: State, private val model: Model,
private val interactor: Interactor,
private val config: Config, private val config: Config,
private val hostServices: HostServices, private val hostServices: HostServices,
) { ) {
@ -35,21 +37,21 @@ class SearchResultsController(
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
private fun initialize() { private fun initialize() {
listViewSearchResults.cellFactory = DictionaryEntryCellFactory(resources, config, hostServices) listViewSearchResults.cellFactory = DictionaryEntryCellFactory(resources, config, hostServices)
listViewSearchResults.items = state.searchResults listViewSearchResults.items = model.searchResults
listViewSearchResults listViewSearchResults
.disableProperty() .disableProperty()
.bind(Bindings.or(state.isSearching, Bindings.isEmpty(state.searchResults))) .bind(Bindings.or(model.isSearchingProperty(), Bindings.isEmpty(model.searchResults)))
progressIndicatorEntries.visibleProperty().bind(state.isSearching) progressIndicatorEntries.visibleProperty().bind(model.isSearchingProperty())
labelNoEntriesFound labelNoEntriesFound
.visibleProperty() .visibleProperty()
.bind(Bindings.and(Bindings.isEmpty(state.searchResults), Bindings.not(state.isSearching))) .bind(Bindings.and(Bindings.isEmpty(model.searchResults), Bindings.not(model.isSearchingProperty())))
listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntry? -> listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntry? ->
if (newValue == null) { if (newValue == null) {
return@addListener return@addListener
} }
state.normalSelect(newValue) interactor.normalSelect(newValue)
} }
} }
} }

View File

@ -1,7 +1,7 @@
package com.marvinelsen.willow.ui.util package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.domain.entities.DictionaryEntry import com.marvinelsen.willow.domain.objects.DictionaryEntry
import com.marvinelsen.willow.domain.entities.Sentence import com.marvinelsen.willow.domain.objects.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