Implement preference dialog
This commit is contained in:
parent
cc1da1f7b5
commit
9ac6f51743
29
src/main/kotlin/com/marvinelsen/willow/Config.kt
Normal file
29
src/main/kotlin/com/marvinelsen/willow/Config.kt
Normal file
@ -0,0 +1,29 @@
|
||||
package com.marvinelsen.willow
|
||||
|
||||
import javafx.beans.property.IntegerProperty
|
||||
import javafx.beans.property.SimpleIntegerProperty
|
||||
import java.util.prefs.Preferences
|
||||
|
||||
class Config {
|
||||
companion object {
|
||||
private const val DETAIL_HEADWORD_FONT_SIZE_KEY = "detailHeadwordFontSize"
|
||||
|
||||
private const val DEFAULT_DETAIL_HEADWORD_FONT_SIZE = 16
|
||||
}
|
||||
|
||||
private val preferences = Preferences.userNodeForPackage(this::class.java)
|
||||
|
||||
val detailHeadwordFontSize: IntegerProperty = SimpleIntegerProperty(DEFAULT_DETAIL_HEADWORD_FONT_SIZE)
|
||||
|
||||
fun save() {
|
||||
preferences.putInt(DETAIL_HEADWORD_FONT_SIZE_KEY, detailHeadwordFontSize.value)
|
||||
}
|
||||
|
||||
fun load() {
|
||||
detailHeadwordFontSize.value = preferences.getInt(DETAIL_HEADWORD_FONT_SIZE_KEY, DEFAULT_DETAIL_HEADWORD_FONT_SIZE)
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
detailHeadwordFontSize.value = DEFAULT_DETAIL_HEADWORD_FONT_SIZE
|
||||
}
|
||||
}
|
@ -45,14 +45,16 @@ class WillowApplication : Application() {
|
||||
val searchService = SearchService(dictionary)
|
||||
val findWordsService = FindWordsService(dictionary)
|
||||
val model = Model(searchService, findWordsService)
|
||||
val config = Config()
|
||||
config.load()
|
||||
|
||||
val fxmlLoader = FXMLLoader()
|
||||
fxmlLoader.resources = resourceBundle
|
||||
fxmlLoader.controllerFactory = Callback { type ->
|
||||
when (type) {
|
||||
MainController::class.java -> MainController(model)
|
||||
MenuController::class.java -> MenuController(model)
|
||||
DetailsController::class.java -> DetailsController(model)
|
||||
MenuController::class.java -> MenuController(model, config)
|
||||
DetailsController::class.java -> DetailsController(model, config)
|
||||
SearchController::class.java -> SearchController(model)
|
||||
ListController::class.java -> ListController(model)
|
||||
else -> error("Trying to instantiate unknown controller type $type")
|
||||
|
@ -1,3 +1,15 @@
|
||||
package com.marvinelsen.willow.ui
|
||||
|
||||
class Configuration
|
||||
data class Configuration(
|
||||
val preferredScript: Script = Script.SIMPLIFIED,
|
||||
)
|
||||
|
||||
enum class Script {
|
||||
SIMPLIFIED, TRADITIONAL
|
||||
}
|
||||
|
||||
enum class Pronunciation {
|
||||
PINYIN_WITH_TONE_MARKS,
|
||||
PINYIN_WITH_TONE_NUMBERS,
|
||||
ZHUYIN
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.marvinelsen.willow.ui.controllers
|
||||
|
||||
import com.marvinelsen.willow.Config
|
||||
import com.marvinelsen.willow.Model
|
||||
import com.marvinelsen.willow.ui.DictionaryEntryFx
|
||||
import javafx.beans.binding.Bindings
|
||||
@ -43,6 +44,15 @@ class DetailsController(private val model: Model) {
|
||||
Bindings.createStringBinding({ model.selectedEntry.value?.traditionalProperty?.value }, model.selectedEntry)
|
||||
|
||||
labelHeadword.textProperty().bind(headwordObjectBinding)
|
||||
labelHeadword
|
||||
.styleProperty()
|
||||
.bind(
|
||||
Bindings.concat(
|
||||
"-fx-font-size: ",
|
||||
config.detailHeadwordFontSize.asString(),
|
||||
"px;"
|
||||
)
|
||||
)
|
||||
|
||||
tabPaneDetails.disableProperty().bind(Bindings.isNull(model.selectedEntry))
|
||||
|
||||
|
@ -1,13 +1,19 @@
|
||||
package com.marvinelsen.willow.ui.controllers
|
||||
|
||||
import com.marvinelsen.willow.Config
|
||||
import com.marvinelsen.willow.Model
|
||||
import com.marvinelsen.willow.ui.dialogs.SettingsDialog
|
||||
import javafx.application.Platform
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.fxml.FXML
|
||||
import javafx.scene.control.MenuBar
|
||||
import javafx.scene.control.MenuItem
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
class MenuController(private val model: Model) {
|
||||
class MenuController(private val model: Model, private val config: Config) {
|
||||
@FXML
|
||||
private lateinit var menuBar: MenuBar
|
||||
|
||||
@FXML
|
||||
private lateinit var menuItemCopyHeadword: MenuItem
|
||||
|
||||
@ -20,6 +26,16 @@ class MenuController(private val model: Model) {
|
||||
menuItemCopyHeadword.disableProperty().bind(Bindings.isNull(model.selectedEntry))
|
||||
}
|
||||
|
||||
@FXML
|
||||
private fun onMenuItemPreferencesAction() {
|
||||
SettingsDialog(menuBar.scene.window, config).showAndWait().ifPresent { result ->
|
||||
when (result) {
|
||||
SettingsDialog.Result.CHANGES -> config.save()
|
||||
SettingsDialog.Result.NO_CHANGES -> config.load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private fun onMenuItemQuitAction() {
|
||||
Platform.exit()
|
||||
|
@ -0,0 +1,14 @@
|
||||
package com.marvinelsen.willow.ui.converters
|
||||
|
||||
import com.marvinelsen.willow.ui.Pronunciation
|
||||
import javafx.util.StringConverter
|
||||
|
||||
class PronunciationStringConverter : StringConverter<Pronunciation>() {
|
||||
override fun toString(pronunciation: Pronunciation) = when (pronunciation) {
|
||||
Pronunciation.PINYIN_WITH_TONE_MARKS -> "Pinyin with tone marks"
|
||||
Pronunciation.PINYIN_WITH_TONE_NUMBERS -> "Pinyin with tone numbers"
|
||||
Pronunciation.ZHUYIN -> "Zhuyin"
|
||||
}
|
||||
|
||||
override fun fromString(string: String) = null
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.marvinelsen.willow.ui.dialogs
|
||||
|
||||
import com.marvinelsen.willow.Config
|
||||
import com.marvinelsen.willow.WillowApplication
|
||||
import com.marvinelsen.willow.ui.Pronunciation
|
||||
import com.marvinelsen.willow.ui.formatters.FontSizeTextFormatter
|
||||
import javafx.fxml.FXMLLoader
|
||||
import javafx.scene.control.ButtonType
|
||||
import javafx.scene.control.CheckBox
|
||||
import javafx.scene.control.ComboBox
|
||||
import javafx.scene.control.Dialog
|
||||
import javafx.scene.control.DialogPane
|
||||
import javafx.scene.control.Spinner
|
||||
import javafx.stage.Modality
|
||||
import javafx.stage.Stage
|
||||
import javafx.stage.Window
|
||||
import javafx.util.Callback
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class SettingsDialog(owner: Window?, config: Config) : Dialog<SettingsDialog.Result>() {
|
||||
companion object {
|
||||
private const val DIALOG_MIN_HEIGHT = 400.0
|
||||
private const val DIALOG_MIN_WIDTH = 400.0
|
||||
}
|
||||
|
||||
enum class Result {
|
||||
CHANGES, NO_CHANGES
|
||||
}
|
||||
|
||||
lateinit var comboBoxEntryPronunciation: ComboBox<Pronunciation>
|
||||
lateinit var comboBoxListPronunciation: ComboBox<Pronunciation>
|
||||
|
||||
lateinit var checkBoxListShowPronunciation: CheckBox
|
||||
lateinit var checkBoxListShowDefinition: CheckBox
|
||||
|
||||
lateinit var spinnerEntryHeadwordFontSize: Spinner<Int>
|
||||
lateinit var spinnerEntryPronunciationFontSize: Spinner<Int>
|
||||
lateinit var spinnerListHeadwordFontSize: Spinner<Int>
|
||||
lateinit var spinnerListPronunciationFontSize: Spinner<Int>
|
||||
lateinit var spinnerListDefinitionFontSize: Spinner<Int>
|
||||
|
||||
private val entryHeadwordFontSizeObjectProperty = config.detailHeadwordFontSize.asObject()
|
||||
|
||||
init {
|
||||
val loader = FXMLLoader(WillowApplication::class.java.getResource("/fxml/settings-dialog.fxml"))
|
||||
loader.setController(this)
|
||||
val root: DialogPane = loader.load()
|
||||
|
||||
dialogPane = root
|
||||
title = "Settings"
|
||||
isResizable = true
|
||||
|
||||
initOwner(owner)
|
||||
initModality(Modality.APPLICATION_MODAL)
|
||||
|
||||
(dialogPane.scene.window as Stage).apply {
|
||||
minWidth = DIALOG_MIN_WIDTH
|
||||
minHeight = DIALOG_MIN_HEIGHT
|
||||
}
|
||||
|
||||
with(spinnerEntryHeadwordFontSize) {
|
||||
editor.textFormatter = FontSizeTextFormatter()
|
||||
valueFactory.valueProperty().bindBidirectional(entryHeadwordFontSizeObjectProperty)
|
||||
}
|
||||
|
||||
resultConverter = Callback(::convertToResult)
|
||||
}
|
||||
|
||||
private fun convertToResult(buttonType: ButtonType) =
|
||||
when (buttonType) {
|
||||
ButtonType.APPLY -> Result.CHANGES
|
||||
else -> Result.NO_CHANGES
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.marvinelsen.willow.ui.formatters
|
||||
|
||||
import javafx.scene.control.TextFormatter
|
||||
import javafx.util.converter.IntegerStringConverter
|
||||
import java.util.function.UnaryOperator
|
||||
|
||||
private val digitRegex = """\d*""".toRegex()
|
||||
|
||||
class FontSizeTextFormatter : TextFormatter<Int>(
|
||||
IntegerStringConverter(),
|
||||
1,
|
||||
UnaryOperator { if (digitRegex.matches(it.text)) it else null }
|
||||
)
|
@ -4,10 +4,11 @@
|
||||
<?import javafx.scene.input.KeyCodeCombination?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<MenuBar xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:id="menuBar"
|
||||
fx:controller="com.marvinelsen.willow.ui.controllers.MenuController" BorderPane.alignment="CENTER"
|
||||
useSystemMenuBar="true">
|
||||
<Menu text="%menubar.file">
|
||||
<MenuItem text="%menubar.file.preferences">
|
||||
<MenuItem text="%menubar.file.preferences" onAction="#onMenuItemPreferencesAction">
|
||||
<accelerator>
|
||||
<KeyCodeCombination alt="UP" code="COMMA" control="UP" meta="UP" shift="UP"
|
||||
shortcut="DOWN"/>
|
||||
|
123
src/main/resources/fxml/settings-dialog.fxml
Normal file
123
src/main/resources/fxml/settings-dialog.fxml
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.marvinelsen.willow.ui.converters.PronunciationStringConverter?>
|
||||
<?import com.marvinelsen.willow.ui.*?>
|
||||
<?import javafx.collections.FXCollections?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<DialogPane styleClass="settings-dialog" xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<fx:define>
|
||||
<FXCollections fx:factory="observableArrayList" fx:id="phoneticAlphabets">
|
||||
<Pronunciation fx:value="PINYIN_WITH_TONE_MARKS"/>
|
||||
<Pronunciation fx:value="PINYIN_WITH_TONE_NUMBERS"/>
|
||||
<Pronunciation fx:value="ZHUYIN"/>
|
||||
</FXCollections>
|
||||
</fx:define>
|
||||
<content>
|
||||
<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||
tabClosingPolicy="UNAVAILABLE">
|
||||
<Tab closable="false" text="Entry">
|
||||
<GridPane alignment="TOP_CENTER" hgap="8.0" vgap="8.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="ALWAYS"/>
|
||||
<ColumnConstraints halignment="LEFT" hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
<padding>
|
||||
<Insets bottom="12.0" left="12.0" right="12.0" top="12.0"/>
|
||||
</padding>
|
||||
<Label text="Pronunciation:"/>
|
||||
<ComboBox fx:id="comboBoxEntryPronunciation" GridPane.columnIndex="1">
|
||||
<value>
|
||||
<Pronunciation fx:value="PINYIN_WITH_TONE_MARKS"/>
|
||||
</value>
|
||||
<converter>
|
||||
<PronunciationStringConverter/>
|
||||
</converter>
|
||||
</ComboBox>
|
||||
<Label text="Headword font size:" GridPane.rowIndex="2"/>
|
||||
<Label text="Pronunciation font size:" GridPane.rowIndex="3"/>
|
||||
<Spinner fx:id="spinnerEntryPronunciationFontSize" editable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="3">
|
||||
<valueFactory>
|
||||
<SpinnerValueFactory.IntegerSpinnerValueFactory amountToStepBy="1" max="300" min="1"/>
|
||||
</valueFactory>
|
||||
</Spinner>
|
||||
<Spinner fx:id="spinnerEntryHeadwordFontSize" editable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="2">
|
||||
<valueFactory>
|
||||
<SpinnerValueFactory.IntegerSpinnerValueFactory amountToStepBy="1" max="300" min="1"/>
|
||||
</valueFactory>
|
||||
</Spinner>
|
||||
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647" GridPane.rowIndex="1"/>
|
||||
</GridPane>
|
||||
</Tab>
|
||||
<Tab closable="false" text="List">
|
||||
<GridPane alignment="TOP_CENTER" hgap="8.0" vgap="8.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="ALWAYS"/>
|
||||
<ColumnConstraints halignment="LEFT" hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
<RowConstraints valignment="BASELINE" vgrow="NEVER"/>
|
||||
</rowConstraints>
|
||||
<padding>
|
||||
<Insets bottom="12.0" left="12.0" right="12.0" top="12.0"/>
|
||||
</padding>
|
||||
<Label text="Pronunciation:"/>
|
||||
<ComboBox fx:id="comboBoxListPronunciation" GridPane.columnIndex="1">
|
||||
<value>
|
||||
<Pronunciation fx:value="PINYIN_WITH_TONE_MARKS"/>
|
||||
</value>
|
||||
<converter>
|
||||
<PronunciationStringConverter/>
|
||||
</converter>
|
||||
</ComboBox>
|
||||
<Label text="Headword font size:" GridPane.rowIndex="5"/>
|
||||
<Label text="Pronunciation font size:" GridPane.rowIndex="6"/>
|
||||
<Spinner fx:id="spinnerListPronunciationFontSize" editable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="6">
|
||||
<valueFactory>
|
||||
<SpinnerValueFactory.IntegerSpinnerValueFactory amountToStepBy="1" max="300" min="1"/>
|
||||
</valueFactory>
|
||||
</Spinner>
|
||||
<Spinner fx:id="spinnerListHeadwordFontSize" editable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="5">
|
||||
<valueFactory>
|
||||
<SpinnerValueFactory.IntegerSpinnerValueFactory amountToStepBy="1" max="300" min="1"/>
|
||||
</valueFactory>
|
||||
</Spinner>
|
||||
<Spinner fx:id="spinnerListDefinitionFontSize" editable="true" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="7">
|
||||
<valueFactory>
|
||||
<SpinnerValueFactory.IntegerSpinnerValueFactory amountToStepBy="1" max="300" min="1"/>
|
||||
</valueFactory>
|
||||
</Spinner>
|
||||
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647" GridPane.rowIndex="4"/>
|
||||
<Label text="Definition font size:" GridPane.rowIndex="7"/>
|
||||
<Label text="Display:" GridPane.rowIndex="2"/>
|
||||
<CheckBox fx:id="checkBoxListShowPronunciation" mnemonicParsing="false" selected="true"
|
||||
text="Show pronunciation" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
<CheckBox fx:id="checkBoxListShowDefinition" mnemonicParsing="false" selected="true"
|
||||
text="Show definition" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647" GridPane.rowIndex="1"/>
|
||||
</GridPane>
|
||||
</Tab>
|
||||
</TabPane>
|
||||
</content>
|
||||
<ButtonType fx:id="buttonTypeApply" fx:constant="APPLY"/>
|
||||
<ButtonType fx:constant="CANCEL"/>
|
||||
</DialogPane>
|
Loading…
Reference in New Issue
Block a user