Compare commits

...

4 Commits

Author SHA1 Message Date
6ebdf4051c
More dark theme WIP
All checks were successful
Pull Request / build (pull_request) Successful in 3m22s
2024-10-03 21:33:19 +02:00
ee989b8997
Split preferences dialog into Dialog and Controller 2024-10-03 21:33:07 +02:00
d48c1b2bf2
Split up services into their own files 2024-10-03 16:26:01 +02:00
d473eafe35
Continue WIP work on darkmode 2024-10-03 16:22:51 +02:00
13 changed files with 234 additions and 169 deletions

View File

@ -2,10 +2,10 @@ 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.services.FindCharacterService
import com.marvinelsen.willow.ui.services.FindWordsService
import com.marvinelsen.willow.ui.services.SearchService
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.SearchService
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.ReadOnlyObjectProperty

View File

@ -7,9 +7,9 @@ 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.services.FindCharacterService
import com.marvinelsen.willow.ui.util.FindWordsService import com.marvinelsen.willow.ui.services.FindWordsService
import com.marvinelsen.willow.ui.util.SearchService import com.marvinelsen.willow.ui.services.SearchService
import javafx.application.Application import javafx.application.Application
import javafx.fxml.FXMLLoader import javafx.fxml.FXMLLoader
import javafx.scene.Scene import javafx.scene.Scene
@ -66,6 +66,7 @@ class WillowApplication : Application() {
val root = fxmlLoader.load(javaClass.getResourceAsStream("/fxml/main.fxml")) as BorderPane val root = fxmlLoader.load(javaClass.getResourceAsStream("/fxml/main.fxml")) as BorderPane
val primaryScene = Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT) val primaryScene = Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT)
// primaryScene.stylesheets.add(javaClass.getResource("/css/dark.css")?.toExternalForm()!!)
primaryStage.apply { primaryStage.apply {
title = WINDOW_TITLE title = WINDOW_TITLE

View File

@ -8,9 +8,13 @@ import javafx.beans.binding.Bindings
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.scene.control.MenuBar import javafx.scene.control.MenuBar
import javafx.scene.control.MenuItem import javafx.scene.control.MenuItem
import java.util.ResourceBundle
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
class MenuController(private val model: Model, private val config: Config) { class MenuController(private val model: Model, private val config: Config) {
@FXML
private lateinit var resources: ResourceBundle
@FXML @FXML
private lateinit var menuBar: MenuBar private lateinit var menuBar: MenuBar
@ -28,10 +32,10 @@ class MenuController(private val model: Model, private val config: Config) {
@FXML @FXML
private fun onMenuItemPreferencesAction() { private fun onMenuItemPreferencesAction() {
PreferencesDialog(menuBar.scene.window, config).showAndWait().ifPresent { result -> PreferencesDialog(menuBar.scene.window, config, resources).showAndWait().ifPresent { result ->
when (result) { when (result) {
PreferencesDialog.Result.CHANGES -> config.save() PreferencesDialog.Result.SAVE_CHANGES -> config.save()
PreferencesDialog.Result.NO_CHANGES -> config.load() PreferencesDialog.Result.DO_NOT_SAVE_CHANGES -> config.load()
} }
} }
} }

View File

@ -0,0 +1,108 @@
package com.marvinelsen.willow.ui.controllers.dialogs
import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Pronunciation
import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.config.Theme
import com.marvinelsen.willow.ui.formatters.FontSizeTextFormatter
import javafx.fxml.FXML
import javafx.scene.control.CheckBox
import javafx.scene.control.ComboBox
import javafx.scene.control.Spinner
import java.util.Locale
@Suppress("UnusedPrivateMember")
class PreferencesDialogController(private val config: Config) {
@FXML
private lateinit var comboBoxLocale: ComboBox<Locale>
@FXML
private lateinit var comboBoxTheme: ComboBox<Theme>
@FXML
private lateinit var comboBoxScript: ComboBox<Script>
@FXML
private lateinit var comboBoxPronunciationSearchResults: ComboBox<Pronunciation>
@FXML
private lateinit var comboBoxPronunciationDetails: ComboBox<Pronunciation>
@FXML
private lateinit var checkBoxShowPronunciationSearchResults: CheckBox
@FXML
private lateinit var checkBoxShowDefinitionSearchResults: CheckBox
@FXML
private lateinit var spinnerHeadwordFontSizeDetails: Spinner<Int>
@FXML
private lateinit var spinnerPronunciationFontSizeDetails: Spinner<Int>
@FXML
private lateinit var spinnerHeadwordFontSizeSearchResults: Spinner<Int>
@FXML
private lateinit var spinnerPronunciationFontSizeSearchResults: Spinner<Int>
@FXML
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
private fun initialize() {
comboBoxLocale.items.addAll(
setOf(
Locale.ENGLISH,
Locale.GERMAN,
Locale.CHINA,
Locale.TAIWAN
)
)
comboBoxTheme.valueProperty().bindBidirectional(config.theme)
comboBoxScript.valueProperty().bindBidirectional(config.script)
comboBoxLocale.valueProperty().bindBidirectional(config.locale)
comboBoxPronunciationSearchResults.valueProperty().bindBidirectional(config.searchResults.pronunciation)
comboBoxPronunciationDetails.valueProperty().bindBidirectional(config.details.pronunciation)
checkBoxShowDefinitionSearchResults
.selectedProperty()
.bindBidirectional(config.searchResults.shouldShowDefinition)
checkBoxShowPronunciationSearchResults
.selectedProperty()
.bindBidirectional(config.searchResults.shouldShowPronunciation)
with(spinnerHeadwordFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(entryHeadwordFontSizeObjectProperty)
}
with(spinnerPronunciationFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(entryPronunciationFontSizeObjectProperty)
}
with(spinnerHeadwordFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultHeadwordFontSizeObjectProperty)
}
with(spinnerPronunciationFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultPronunciationFontSizeObjectProperty)
}
with(spinnerDefinitionFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultDefinitionFontSizeObjectProperty)
}
}
}

View File

@ -1,82 +1,42 @@
package com.marvinelsen.willow.ui.dialogs package com.marvinelsen.willow.ui.dialogs
import com.marvinelsen.willow.WillowApplication
import com.marvinelsen.willow.config.Config import com.marvinelsen.willow.config.Config
import com.marvinelsen.willow.config.Pronunciation import com.marvinelsen.willow.ui.controllers.dialogs.PreferencesDialogController
import com.marvinelsen.willow.config.Script
import com.marvinelsen.willow.config.Theme
import com.marvinelsen.willow.ui.formatters.FontSizeTextFormatter
import javafx.fxml.FXML
import javafx.fxml.FXMLLoader import javafx.fxml.FXMLLoader
import javafx.scene.control.ButtonType import javafx.scene.control.ButtonType
import javafx.scene.control.CheckBox
import javafx.scene.control.ComboBox
import javafx.scene.control.Dialog import javafx.scene.control.Dialog
import javafx.scene.control.DialogPane import javafx.scene.control.DialogPane
import javafx.scene.control.Spinner
import javafx.stage.Modality import javafx.stage.Modality
import javafx.stage.Stage import javafx.stage.Stage
import javafx.stage.Window import javafx.stage.Window
import javafx.util.Callback import javafx.util.Callback
import java.util.Locale import java.util.ResourceBundle
class PreferencesDialog(owner: Window?, config: Config) : Dialog<PreferencesDialog.Result>() { class PreferencesDialog(
owner: Window?,
config: Config,
resources: ResourceBundle,
) : Dialog<PreferencesDialog.Result>() {
companion object { companion object {
private const val DIALOG_MIN_HEIGHT = 400.0 private const val MIN_HEIGHT = 400.0
private const val DIALOG_MIN_WIDTH = 400.0 private const val MIN_WIDTH = 400.0
} }
enum class Result { enum class Result {
CHANGES, NO_CHANGES SAVE_CHANGES, DO_NOT_SAVE_CHANGES
} }
@FXML
private lateinit var comboBoxLocale: ComboBox<Locale>
@FXML
private lateinit var comboBoxTheme: ComboBox<Theme>
@FXML
private lateinit var comboBoxScript: ComboBox<Script>
@FXML
private lateinit var comboBoxPronunciationSearchResults: ComboBox<Pronunciation>
@FXML
private lateinit var comboBoxPronunciationDetails: ComboBox<Pronunciation>
@FXML
private lateinit var checkBoxShowPronunciationSearchResults: CheckBox
@FXML
private lateinit var checkBoxShowDefinitionSearchResults: CheckBox
@FXML
private lateinit var spinnerHeadwordFontSizeDetails: Spinner<Int>
@FXML
private lateinit var spinnerPronunciationFontSizeDetails: Spinner<Int>
@FXML
private lateinit var spinnerHeadwordFontSizeSearchResults: Spinner<Int>
@FXML
private lateinit var spinnerPronunciationFontSizeSearchResults: Spinner<Int>
@FXML
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()
init { init {
val loader = FXMLLoader(WillowApplication::class.java.getResource("/fxml/dialogs/preferences.fxml")) val fxmlLoader = FXMLLoader()
loader.setController(this) fxmlLoader.resources = resources
val root: DialogPane = loader.load() fxmlLoader.controllerFactory = Callback { type ->
when (type) {
PreferencesDialogController::class.java -> PreferencesDialogController(config)
else -> error("Trying to instantiate unknown controller type $type")
}
}
val root = fxmlLoader.load(javaClass.getResourceAsStream("/fxml/dialogs/preferences.fxml")) as DialogPane
dialogPane = root dialogPane = root
title = "Settings" title = "Settings"
@ -86,55 +46,8 @@ class PreferencesDialog(owner: Window?, config: Config) : Dialog<PreferencesDial
initModality(Modality.APPLICATION_MODAL) initModality(Modality.APPLICATION_MODAL)
(dialogPane.scene.window as Stage).apply { (dialogPane.scene.window as Stage).apply {
minWidth = DIALOG_MIN_WIDTH minWidth = MIN_WIDTH
minHeight = DIALOG_MIN_HEIGHT minHeight = MIN_HEIGHT
}
comboBoxLocale.items.addAll(
setOf(
Locale.ENGLISH,
Locale.GERMAN,
Locale.CHINA,
Locale.TAIWAN
)
)
comboBoxTheme.valueProperty().bindBidirectional(config.theme)
comboBoxScript.valueProperty().bindBidirectional(config.script)
comboBoxLocale.valueProperty().bindBidirectional(config.locale)
comboBoxPronunciationSearchResults.valueProperty().bindBidirectional(config.searchResults.pronunciation)
comboBoxPronunciationDetails.valueProperty().bindBidirectional(config.details.pronunciation)
checkBoxShowDefinitionSearchResults
.selectedProperty()
.bindBidirectional(config.searchResults.shouldShowDefinition)
checkBoxShowPronunciationSearchResults
.selectedProperty()
.bindBidirectional(config.searchResults.shouldShowPronunciation)
with(spinnerHeadwordFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(entryHeadwordFontSizeObjectProperty)
}
with(spinnerPronunciationFontSizeDetails) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(entryPronunciationFontSizeObjectProperty)
}
with(spinnerHeadwordFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultHeadwordFontSizeObjectProperty)
}
with(spinnerPronunciationFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultPronunciationFontSizeObjectProperty)
}
with(spinnerDefinitionFontSizeSearchResults) {
editor.textFormatter = FontSizeTextFormatter()
valueFactory.valueProperty().bindBidirectional(searchResultDefinitionFontSizeObjectProperty)
} }
resultConverter = Callback(::convertToResult) resultConverter = Callback(::convertToResult)
@ -142,7 +55,7 @@ class PreferencesDialog(owner: Window?, config: Config) : Dialog<PreferencesDial
private fun convertToResult(buttonType: ButtonType) = private fun convertToResult(buttonType: ButtonType) =
when (buttonType) { when (buttonType) {
ButtonType.APPLY -> Result.CHANGES ButtonType.APPLY -> Result.SAVE_CHANGES
else -> Result.NO_CHANGES else -> Result.DO_NOT_SAVE_CHANGES
} }
} }

View File

@ -0,0 +1,20 @@
package com.marvinelsen.willow.ui.services
import com.marvinelsen.willow.domain.Dictionary
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.toDomain
import com.marvinelsen.willow.ui.toFx
import com.marvinelsen.willow.ui.util.task
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.concurrent.Service
class 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

@ -0,0 +1,20 @@
package com.marvinelsen.willow.ui.services
import com.marvinelsen.willow.domain.Dictionary
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.toDomain
import com.marvinelsen.willow.ui.toFx
import com.marvinelsen.willow.ui.util.task
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.concurrent.Service
class FindWordsService(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.findWordsContaining(entry.toDomain()).map { it.toFx() })
}
}

View File

@ -0,0 +1,22 @@
package com.marvinelsen.willow.ui.services
import com.marvinelsen.willow.domain.Dictionary
import com.marvinelsen.willow.domain.SearchMode
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.toFx
import com.marvinelsen.willow.ui.util.task
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.concurrent.Service
class SearchService(private val dictionary: Dictionary) : Service<ObservableList<DictionaryEntryFx>>() {
lateinit var searchQuery: String
lateinit var searchMode: SearchMode
override fun createTask() = task {
if (!this::searchQuery.isInitialized) error("Search query is not initialized")
if (!this::searchMode.isInitialized) error("Search mode is not initialized")
FXCollections.observableList(dictionary.search(searchQuery, searchMode).map { it.toFx() })
}
}

View File

@ -1,42 +0,0 @@
package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.domain.Dictionary
import com.marvinelsen.willow.domain.SearchMode
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.toDomain
import com.marvinelsen.willow.ui.toFx
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.concurrent.Service
class SearchService(private val dictionary: Dictionary) : Service<ObservableList<DictionaryEntryFx>>() {
lateinit var searchQuery: String
lateinit var searchMode: SearchMode
override fun createTask() = task {
if (!this::searchQuery.isInitialized) error("Search query is not initialized")
if (!this::searchMode.isInitialized) error("Search mode is not initialized")
FXCollections.observableList(dictionary.search(searchQuery, searchMode).map { it.toFx() })
}
}
class FindWordsService(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.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

@ -4,13 +4,21 @@ https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html
*/ */
.root { .root {
willow-background: #2D2D30; willow-background: #282828;
willow-foreground: #ffffff; willow-foreground: #dfdfdf;
-fx-base: willow-background;
-fx-accent: #5c7654;
-fx-focus-color: #3e8f25;
-fx-background-color: willow-background; -fx-background-color: willow-background;
background-color: willow-background; -fx-text-fill: willow-foreground;
} }
/*.split-pane {
-fx-background-color: willow-background;
}*/
.label { .label {
-fx-text-fill: willow-foreground; -fx-text-fill: willow-foreground;
} }
@ -18,3 +26,13 @@ https://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html
.radio-button { .radio-button {
-fx-text-fill: willow-foreground; -fx-text-fill: willow-foreground;
} }
.list-cell:even {
-fx-text-fill: willow-foreground;
-fx-background: #1e1e1e;
}
.list-cell:odd {
-fx-text-fill: willow-foreground;
-fx-background: #292929;
}

View File

@ -7,7 +7,7 @@
<VBox xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1" <VBox xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.marvinelsen.willow.ui.controllers.DetailsController" fx:controller="com.marvinelsen.willow.ui.controllers.DetailsController"
stylesheets="/css/details.css"> stylesheets="/css/details.css">
<FlowPane fx:id="flowPaneHeader" hgap="8.0" onContextMenuRequested="#headerOnContextMenuRequested" <FlowPane fx:id="flowPaneHeader" styleClass="flow-pane-header" hgap="8.0" onContextMenuRequested="#headerOnContextMenuRequested"
prefHeight="38.0" prefWidth="412.0" rowValignment="BASELINE" vgap="8.0" VBox.vgrow="NEVER"> prefHeight="38.0" prefWidth="412.0" rowValignment="BASELINE" vgap="8.0" VBox.vgrow="NEVER">
<padding> <padding>
<Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/> <Insets bottom="6.0" left="6.0" right="6.0" top="6.0"/>

View File

@ -7,7 +7,8 @@
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<DialogPane styleClass="preferences-dialog" xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1" <DialogPane styleClass="preferences-dialog" xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
stylesheets="/css/preferences.css"> stylesheets="/css/preferences.css"
fx:controller="com.marvinelsen.willow.ui.controllers.dialogs.PreferencesDialogController">
<fx:define> <fx:define>
<FXCollections fx:factory="observableArrayList" fx:id="phoneticAlphabets"> <FXCollections fx:factory="observableArrayList" fx:id="phoneticAlphabets">
<Pronunciation fx:value="PINYIN_WITH_TONE_MARKS"/> <Pronunciation fx:value="PINYIN_WITH_TONE_MARKS"/>
@ -169,6 +170,6 @@
</Tab> </Tab>
</TabPane> </TabPane>
</content> </content>
<ButtonType fx:id="buttonTypeApply" fx:constant="APPLY"/> <ButtonType fx:constant="APPLY"/>
<ButtonType fx:constant="CANCEL"/> <ButtonType fx:constant="CANCEL"/>
</DialogPane> </DialogPane>

View File

@ -15,7 +15,7 @@
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/> <Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
</BorderPane.margin> </BorderPane.margin>
<fx:include source="/fxml/search.fxml"/> <fx:include source="/fxml/search.fxml"/>
<SplitPane dividerPositions="0.33" VBox.vgrow="ALWAYS"> <SplitPane styleClass="split-pane" dividerPositions="0.33" VBox.vgrow="ALWAYS">
<fx:include source="/fxml/search-results.fxml"/> <fx:include source="/fxml/search-results.fxml"/>
<fx:include source="/fxml/details.fxml"/> <fx:include source="/fxml/details.fxml"/>
</SplitPane> </SplitPane>