Compare commits

...

9 Commits

Author SHA1 Message Date
bc9d09c39f
Fix preferences dialog CSS styling
All checks were successful
Pull Request / build (pull_request) Successful in 3m22s
2024-09-26 23:26:45 +02:00
6003bc678e
Rename settings dialog to preferences dialog 2024-09-26 23:14:10 +02:00
fee7cbf260
Fix detekt issues 2024-09-26 23:12:00 +02:00
02257b7e6b
Refactor resource bundle handling 2024-09-26 23:10:40 +02:00
9ac6f51743
Implement preference dialog 2024-09-26 23:10:07 +02:00
cc1da1f7b5
Rename settings to preferences 2024-09-26 23:08:10 +02:00
8350cb3b3d
Refactor ClipboardHelper 2024-09-26 10:33:31 +02:00
efde0dcdb4
Show label for no entries found 2024-09-26 10:09:55 +02:00
9508412427
Refactor listview to its own FXML file and controller 2024-09-26 09:18:40 +02:00
25 changed files with 414 additions and 88 deletions

View File

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

View File

@ -53,10 +53,10 @@ class Model(private val searchService: SearchService, private val findWordsServi
} }
fun copyHeadwordOfSelectedEntry() { fun copyHeadwordOfSelectedEntry() {
ClipboardHelper.copyHeadword(internalSelectedEntry.get()) ClipboardHelper.copyString(internalSelectedEntry.value.traditionalProperty.value)
} }
fun copyPronunciationOfSelectedEntry() { fun copyPronunciationOfSelectedEntry() {
ClipboardHelper.copyPronunciation(internalSelectedEntry.get()) ClipboardHelper.copyString(internalSelectedEntry.value.pinyinWithToneMarksProperty.value)
} }
} }

View File

@ -2,6 +2,7 @@ package com.marvinelsen.willow
import com.marvinelsen.willow.domain.SqliteDictionary import com.marvinelsen.willow.domain.SqliteDictionary
import com.marvinelsen.willow.ui.controllers.DetailsController import com.marvinelsen.willow.ui.controllers.DetailsController
import com.marvinelsen.willow.ui.controllers.ListController
import com.marvinelsen.willow.ui.controllers.MainController 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
@ -29,7 +30,6 @@ class WillowApplication : Application() {
private const val FONT_SIZE = 12.0 private const val FONT_SIZE = 12.0
private const val JDBC_CONNECTION_STRING = "jdbc:sqlite:dictionary.db" private const val JDBC_CONNECTION_STRING = "jdbc:sqlite:dictionary.db"
val resourceBundle: ResourceBundle = ResourceBundle.getBundle("i18n/willow", Locale.US)
} }
override fun init() { override fun init() {
@ -44,15 +44,18 @@ class WillowApplication : Application() {
val searchService = SearchService(dictionary) val searchService = SearchService(dictionary)
val findWordsService = FindWordsService(dictionary) val findWordsService = FindWordsService(dictionary)
val model = Model(searchService, findWordsService) val model = Model(searchService, findWordsService)
val config = Config()
config.load()
val fxmlLoader = FXMLLoader() val fxmlLoader = FXMLLoader()
fxmlLoader.resources = resourceBundle fxmlLoader.resources = ResourceBundle.getBundle("i18n/willow", Locale.getDefault())
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) MenuController::class.java -> MenuController(model, config)
DetailsController::class.java -> DetailsController(model) DetailsController::class.java -> DetailsController(model, config)
SearchController::class.java -> SearchController(model) SearchController::class.java -> SearchController(model)
ListController::class.java -> ListController(model)
else -> error("Trying to instantiate unknown controller type $type") else -> error("Trying to instantiate unknown controller type $type")
} }
} }

View File

@ -1,3 +1,15 @@
package com.marvinelsen.willow.ui 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
}

View File

@ -9,10 +9,12 @@ import javafx.scene.control.ListView
import javafx.scene.layout.FlowPane import javafx.scene.layout.FlowPane
import javafx.scene.layout.VBox import javafx.scene.layout.VBox
import javafx.util.Callback import javafx.util.Callback
import java.util.ResourceBundle
class DictionaryEntryCellFactory : Callback<ListView<DictionaryEntryFx?>, ListCell<DictionaryEntryFx?>> { class DictionaryEntryCellFactory(private val resources: ResourceBundle) :
Callback<ListView<DictionaryEntryFx?>, ListCell<DictionaryEntryFx?>> {
override fun call(listView: ListView<DictionaryEntryFx?>): ListCell<DictionaryEntryFx?> { override fun call(listView: ListView<DictionaryEntryFx?>): ListCell<DictionaryEntryFx?> {
val entryCell = EntryCell() val entryCell = EntryCell(resources)
entryCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING)) entryCell.prefWidthProperty().bind(listView.widthProperty().subtract(CELL_PADDING))
return entryCell return entryCell
} }
@ -22,7 +24,7 @@ class DictionaryEntryCellFactory : Callback<ListView<DictionaryEntryFx?>, ListCe
} }
} }
internal class EntryCell : ListCell<DictionaryEntryFx?>() { internal class EntryCell(private val resources: ResourceBundle) : ListCell<DictionaryEntryFx?>() {
private val labelHeadword = Label().apply { private val labelHeadword = Label().apply {
styleClass.add("list-view-entry") styleClass.add("list-view-entry")
} }
@ -58,7 +60,7 @@ internal class EntryCell : ListCell<DictionaryEntryFx?>() {
val definition = entry.definitions.joinToString(separator = " / ") { it.joinToString(separator = "; ") } val definition = entry.definitions.joinToString(separator = " / ") { it.joinToString(separator = "; ") }
labelDefinition.text = definition labelDefinition.text = definition
contextMenu = createContextMenuForEntry(entry) contextMenu = createContextMenuForEntry(entry, resources)
graphic = root graphic = root
} }
} }

View File

@ -1,7 +1,9 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Config
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.scene.control.Label import javafx.scene.control.Label
@ -14,8 +16,12 @@ import kotlinx.html.html
import kotlinx.html.li import kotlinx.html.li
import kotlinx.html.ol import kotlinx.html.ol
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import java.util.ResourceBundle
class DetailsController(private val model: Model, private val config: Config) {
@FXML
private lateinit var resources: ResourceBundle
class DetailsController(private val model: Model) {
@FXML @FXML
private lateinit var tabPaneDetails: TabPane private lateinit var tabPaneDetails: TabPane
@ -43,9 +49,19 @@ class DetailsController(private val model: Model) {
Bindings.createStringBinding({ model.selectedEntry.value?.traditionalProperty?.value }, model.selectedEntry) Bindings.createStringBinding({ model.selectedEntry.value?.traditionalProperty?.value }, model.selectedEntry)
labelHeadword.textProperty().bind(headwordObjectBinding) labelHeadword.textProperty().bind(headwordObjectBinding)
labelHeadword
.styleProperty()
.bind(
Bindings.concat(
"-fx-font-size: ",
config.detailHeadwordFontSize.asString(),
"px;"
)
)
tabPaneDetails.disableProperty().bind(Bindings.isNull(model.selectedEntry)) tabPaneDetails.disableProperty().bind(Bindings.isNull(model.selectedEntry))
listViewWords.cellFactory = DictionaryEntryCellFactory(resources)
listViewWords.items = model.wordsContaining listViewWords.items = model.wordsContaining
tabPaneDetails.selectionModel.selectedItemProperty().addListener { _, _, selectedTab -> tabPaneDetails.selectionModel.selectedItemProperty().addListener { _, _, selectedTab ->
if (model.selectedEntry.value == null) return@addListener if (model.selectedEntry.value == null) return@addListener
@ -69,6 +85,13 @@ class DetailsController(private val model: Model) {
if (newValue == null) { if (newValue == null) {
return@addListener return@addListener
} }
when (tabPaneDetails.selectionModel.selectedItem.id) {
"tabWords" -> {
model.findWords()
}
else -> {}
}
webViewDefinition.engine.loadContent( webViewDefinition.engine.loadContent(
createHTML().html { createHTML().html {
body { body {

View File

@ -0,0 +1,48 @@
package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Model
import com.marvinelsen.willow.ui.DictionaryEntryFx
import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory
import javafx.beans.binding.Bindings
import javafx.fxml.FXML
import javafx.scene.control.Label
import javafx.scene.control.ListView
import javafx.scene.control.ProgressIndicator
import java.util.ResourceBundle
class ListController(private val model: Model) {
@FXML
private lateinit var resources: ResourceBundle
@FXML
private lateinit var progressIndicatorEntries: ProgressIndicator
@FXML
@Suppress("UnusedPrivateProperty")
private lateinit var labelNoEntriesFound: Label
@FXML
private lateinit var listViewSearchResults: ListView<DictionaryEntryFx>
@FXML
@Suppress("UnusedPrivateMember")
private fun initialize() {
listViewSearchResults.cellFactory = DictionaryEntryCellFactory(resources)
listViewSearchResults.items = model.searchResults
listViewSearchResults
.disableProperty()
.bind(Bindings.or(model.isSearching, Bindings.isEmpty(model.searchResults)))
progressIndicatorEntries.visibleProperty().bind(model.isSearching)
labelNoEntriesFound
.visibleProperty()
.bind(Bindings.and(Bindings.isEmpty(model.searchResults), Bindings.not(model.isSearching)))
listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntryFx? ->
if (newValue == null) {
return@addListener
}
model.selectEntry(newValue)
}
}
}

View File

@ -1,39 +1,12 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.ui.DictionaryEntryFx
import javafx.beans.binding.Bindings
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.scene.control.Label
import javafx.scene.control.ListView
import javafx.scene.control.ProgressIndicator
@Suppress("UnusedPrivateProperty", "UnusedPrivateMember")
class MainController(private val model: Model) { class MainController(private val model: Model) {
@FXML @FXML
private lateinit var progressIndicatorEntries: ProgressIndicator
@FXML
@Suppress("UnusedPrivateProperty")
private lateinit var labelNoEntriesFound: Label
@FXML
private lateinit var listViewSearchResults: ListView<DictionaryEntryFx>
@FXML
@Suppress("UnusedPrivateMember")
private fun initialize() { private fun initialize() {
listViewSearchResults.items = model.searchResults // no-op
listViewSearchResults
.disableProperty()
.bind(Bindings.or(model.isSearching, Bindings.isEmpty(model.searchResults)))
progressIndicatorEntries.visibleProperty().bind(model.isSearching)
listViewSearchResults.selectionModel.selectedItemProperty().addListener { _, _, newValue: DictionaryEntryFx? ->
if (newValue == null) {
return@addListener
}
model.selectEntry(newValue)
}
} }
} }

View File

@ -1,13 +1,19 @@
package com.marvinelsen.willow.ui.controllers package com.marvinelsen.willow.ui.controllers
import com.marvinelsen.willow.Config
import com.marvinelsen.willow.Model import com.marvinelsen.willow.Model
import com.marvinelsen.willow.ui.dialogs.PreferencesDialog
import javafx.application.Platform import javafx.application.Platform
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
import javafx.fxml.FXML import javafx.fxml.FXML
import javafx.scene.control.MenuBar
import javafx.scene.control.MenuItem import javafx.scene.control.MenuItem
@Suppress("UnusedPrivateMember") @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 @FXML
private lateinit var menuItemCopyHeadword: MenuItem private lateinit var menuItemCopyHeadword: MenuItem
@ -20,6 +26,16 @@ class MenuController(private val model: Model) {
menuItemCopyHeadword.disableProperty().bind(Bindings.isNull(model.selectedEntry)) menuItemCopyHeadword.disableProperty().bind(Bindings.isNull(model.selectedEntry))
} }
@FXML
private fun onMenuItemPreferencesAction() {
PreferencesDialog(menuBar.scene.window, config).showAndWait().ifPresent { result ->
when (result) {
PreferencesDialog.Result.CHANGES -> config.save()
PreferencesDialog.Result.NO_CHANGES -> config.load()
}
}
}
@FXML @FXML
private fun onMenuItemQuitAction() { private fun onMenuItemQuitAction() {
Platform.exit() Platform.exit()

View File

@ -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
}

View File

@ -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 PreferencesDialog(owner: Window?, config: Config) : Dialog<PreferencesDialog.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/preferences-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
}
}

View File

@ -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 }
)

View File

@ -1,21 +1,14 @@
package com.marvinelsen.willow.ui.util package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.ui.DictionaryEntryFx
import javafx.scene.input.Clipboard import javafx.scene.input.Clipboard
import javafx.scene.input.ClipboardContent import javafx.scene.input.ClipboardContent
object ClipboardHelper { object ClipboardHelper {
private val systemClipboard = Clipboard.getSystemClipboard() private val systemClipboard = Clipboard.getSystemClipboard()
fun copyHeadword(entry: DictionaryEntryFx) { fun copyString(string: String) {
val clipboardContent = ClipboardContent() val clipboardContent = ClipboardContent()
clipboardContent.putString(entry.traditionalProperty.value) clipboardContent.putString(string)
systemClipboard.setContent(clipboardContent)
}
fun copyPronunciation(entry: DictionaryEntryFx) {
val clipboardContent = ClipboardContent()
clipboardContent.putString(entry.pinyinWithToneMarksProperty.value)
systemClipboard.setContent(clipboardContent) systemClipboard.setContent(clipboardContent)
} }
} }

View File

@ -1,20 +1,20 @@
package com.marvinelsen.willow.ui.util package com.marvinelsen.willow.ui.util
import com.marvinelsen.willow.WillowApplication
import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.DictionaryEntryFx
import javafx.event.EventHandler import javafx.event.EventHandler
import javafx.scene.control.ContextMenu import javafx.scene.control.ContextMenu
import javafx.scene.control.MenuItem import javafx.scene.control.MenuItem
import java.util.ResourceBundle
fun createContextMenuForEntry(entry: DictionaryEntryFx) = ContextMenu().apply { fun createContextMenuForEntry(entry: DictionaryEntryFx, resourceBundle: ResourceBundle) = ContextMenu().apply {
val menuItemCopyHeadword = val menuItemCopyHeadword =
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.headword")).apply { MenuItem(resourceBundle.getString("menubar.edit.copy.headword")).apply {
onAction = EventHandler { ClipboardHelper.copyHeadword(entry) } onAction = EventHandler { ClipboardHelper.copyString(entry.traditionalProperty.value) }
} }
val menuItemCopyPronunciation = val menuItemCopyPronunciation =
MenuItem(WillowApplication.resourceBundle.getString("menubar.edit.copy.pronunciation")).apply { MenuItem(resourceBundle.getString("menubar.edit.copy.pronunciation")).apply {
onAction = EventHandler { ClipboardHelper.copyPronunciation(entry) } onAction = EventHandler { ClipboardHelper.copyString(entry.pinyinWithToneMarksProperty.value) }
} }
items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation) items.addAll(menuItemCopyHeadword, menuItemCopyPronunciation)

View File

@ -37,6 +37,6 @@
-fx-font: 16 "Noto Sans TC"; -fx-font: 16 "Noto Sans TC";
} }
.settings-dialog .content { .preferences-dialog .content {
-fx-padding: 0; -fx-padding: 0;
} }

View File

@ -22,11 +22,7 @@
<ListView fx:id="listviewSentences"/> <ListView fx:id="listviewSentences"/>
</Tab> </Tab>
<Tab id="tabWords" closable="false" disable="false" text="%tab.words"> <Tab id="tabWords" closable="false" disable="false" text="%tab.words">
<ListView fx:id="listViewWords"> <ListView fx:id="listViewWords"/>
<cellFactory>
<DictionaryEntryCellFactory/>
</cellFactory>
</ListView>
</Tab> </Tab>
<Tab closable="false" disable="false" text="%tab.characters"> <Tab closable="false" disable="false" text="%tab.characters">
<ListView fx:id="listViewCharacters"/> <ListView fx:id="listViewCharacters"/>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.StackPane?>
<StackPane xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.marvinelsen.willow.ui.controllers.ListController"
stylesheets="/css/main.css">
<ListView fx:id="listViewSearchResults" disable="true"/>
<Label fx:id="labelNoEntriesFound" text="%list.no_entries_found" textAlignment="CENTER"
visible="false" wrapText="true">
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
</padding>
</Label>
<ProgressIndicator fx:id="progressIndicatorEntries" visible="false"/>
</StackPane>

View File

@ -1,10 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import com.marvinelsen.willow.ui.cells.DictionaryEntryCellFactory?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.SplitPane?> <?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1" <BorderPane xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
@ -20,22 +16,9 @@
</BorderPane.margin> </BorderPane.margin>
<fx:include source="/fxml/search.fxml"/> <fx:include source="/fxml/search.fxml"/>
<SplitPane dividerPositions="0.33" VBox.vgrow="ALWAYS"> <SplitPane dividerPositions="0.33" VBox.vgrow="ALWAYS">
<StackPane> <fx:include source="/fxml/list.fxml"/>
<ListView fx:id="listViewSearchResults" disable="true">
<cellFactory>
<DictionaryEntryCellFactory/>
</cellFactory>
</ListView>
<Label fx:id="labelNoEntriesFound" text="%list.no_entries_found" textAlignment="CENTER"
visible="false" wrapText="true">
<padding>
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0"/>
</padding>
</Label>
<ProgressIndicator fx:id="progressIndicatorEntries" visible="false"/>
</StackPane>
<fx:include source="/fxml/details.fxml"/> <fx:include source="/fxml/details.fxml"/>
</SplitPane> </SplitPane>
</VBox> </VBox>
</center> </center>
</BorderPane> </BorderPane>

View File

@ -4,10 +4,11 @@
<?import javafx.scene.input.KeyCodeCombination?> <?import javafx.scene.input.KeyCodeCombination?>
<?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.BorderPane?>
<MenuBar xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1" <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" fx:controller="com.marvinelsen.willow.ui.controllers.MenuController" BorderPane.alignment="CENTER"
useSystemMenuBar="true"> useSystemMenuBar="true">
<Menu text="%menubar.file"> <Menu text="%menubar.file">
<MenuItem text="%menubar.file.settings"> <MenuItem text="%menubar.file.preferences" onAction="#onMenuItemPreferencesAction">
<accelerator> <accelerator>
<KeyCodeCombination alt="UP" code="COMMA" control="UP" meta="UP" shift="UP" <KeyCodeCombination alt="UP" code="COMMA" control="UP" meta="UP" shift="UP"
shortcut="DOWN"/> shortcut="DOWN"/>

View File

@ -0,0 +1,124 @@
<?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="preferences-dialog" xmlns="http://javafx.com/javafx/23" xmlns:fx="http://javafx.com/fxml/1"
stylesheets="/css/main.css">
<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>

View File

@ -10,7 +10,7 @@ tab.words=Words
tab.characters=Characters tab.characters=Characters
menubar.file=_File menubar.file=_File
menubar.file.quit=_Quit menubar.file.quit=_Quit
menubar.file.settings=_Settings… menubar.file.preferences=_Preferences…
menubar.edit=_Edit menubar.edit=_Edit
menubar.edit.copy.headword=Copy Headword menubar.edit.copy.headword=Copy Headword
menubar.edit.copy.pronunciation=Copy Pronunciation menubar.edit.copy.pronunciation=Copy Pronunciation

View File

@ -10,7 +10,7 @@ tab.words=Wörter
tab.characters=Schriftzeichen tab.characters=Schriftzeichen
menubar.file=_Datei menubar.file=_Datei
menubar.file.quit=_Beenden menubar.file.quit=_Beenden
menubar.file.settings=_Einstellungen… menubar.file.preferences=_Einstellungen…
menubar.edit=_Bearbeiten menubar.edit=_Bearbeiten
menubar.edit.copy.headword=Kopiere Wort menubar.edit.copy.headword=Kopiere Wort
menubar.edit.copy.pronunciation=Kopiere Aussprache menubar.edit.copy.pronunciation=Kopiere Aussprache

View File

@ -10,7 +10,7 @@ tab.words=Words
tab.characters=Characters tab.characters=Characters
menubar.file=_File menubar.file=_File
menubar.file.quit=_Quit menubar.file.quit=_Quit
menubar.file.settings=_Settings… menubar.file.preferences=_Preferences…
menubar.edit=_Edit menubar.edit=_Edit
menubar.edit.copy.headword=Copy Headword menubar.edit.copy.headword=Copy Headword
menubar.edit.copy.pronunciation=Copy Pronunciation menubar.edit.copy.pronunciation=Copy Pronunciation

View File

@ -10,7 +10,7 @@ tab.words=詞
tab.characters= tab.characters=
menubar.file=_檔案 menubar.file=_檔案
menubar.file.quit=_結束 Willow menubar.file.quit=_結束 Willow
menubar.file.settings=_設定… menubar.file.preferences=_設定…
menubar.edit=_編輯 menubar.edit=_編輯
menubar.edit.copy.headword=複製 Wort menubar.edit.copy.headword=複製 Wort
menubar.edit.copy.pronunciation=複製 Aussprache menubar.edit.copy.pronunciation=複製 Aussprache

View File

@ -10,7 +10,7 @@ tab.words=詞
tab.characters= tab.characters=
menubar.file=_檔案 menubar.file=_檔案
menubar.file.quit=_結束 Willow menubar.file.quit=_結束 Willow
menubar.file.settings=_設定… menubar.file.preferences=_設定…
menubar.edit=_編輯 menubar.edit=_編輯
menubar.edit.copy.headword=複製 Wort menubar.edit.copy.headword=複製 Wort
menubar.edit.copy.pronunciation=複製 Aussprache menubar.edit.copy.pronunciation=複製 Aussprache