Merge remote-tracking branch 'origin/master'

This commit is contained in:
law39 2020-05-01 14:48:20 +01:00
commit 1a8d956a7d
7 changed files with 428 additions and 353 deletions

View file

@ -26,7 +26,7 @@ import uk.ac.aber.cs221.group20.json.DictionaryEntry;
* @author Henry Dugmore [hjd3]
* @author Kain Bryan-Jones [kab74]
* @author Luke Wybar [law39]
* @author Marcin Jakob [maj83]
* @author Marcin Jakobik [maj83]
* @author Oscar Pocock [osp1]
* @author Tom Perry [top19]
* @author Waylen Watts [ncw]
@ -40,6 +40,8 @@ public class AddWordController extends SharedCodeController {
// Instance variables. //
// /////////////////// //
@FXML
private TextField welsh;
@FXML
@ -47,10 +49,23 @@ public class AddWordController extends SharedCodeController {
@FXML
private ComboBox<String> wordType;
// //////// //
@FXML
private ComboBox<String> specialChar1;
@FXML
private ComboBox<String> specialChar2;
@FXML
private ComboBox<String> specialChar3;
@FXML
private ComboBox<String> specialChar4;
// //////// //
// Methods. //
// //////// //
/**
* Gets the value from the welsh text field
*
@ -162,6 +177,7 @@ public class AddWordController extends SharedCodeController {
}
/**
* Method that adds ch to the welsh text field and runs when the user clicks the ch button on the add word screen
*
@ -236,93 +252,18 @@ public class AddWordController extends SharedCodeController {
welsh.appendText("th");
}
public void addCharâ(ActionEvent actionEvent) {
welsh.appendText("â");
}
public void addCharê(ActionEvent actionEvent) {
welsh.appendText("ê");
}
public void addCharî(ActionEvent actionEvent) {
welsh.appendText("î");
}
public void addCharô(ActionEvent actionEvent) {
welsh.appendText("ô");
}
public void addCharû(ActionEvent actionEvent) {
welsh.appendText("û");
}
public void addCharŵ(ActionEvent actionEvent) {
welsh.appendText("ŵ");
}
public void addCharŷ(ActionEvent actionEvent) {
welsh.appendText("ŷ");
public void specialChar1(ActionEvent actionEvent){
welsh.appendText(specialChar1.getValue());
}
// äëïöüẅÿ
public void addCharä(ActionEvent actionEvent) {
welsh.appendText("ä");
public void specialChar2(ActionEvent actionEvent) {
welsh.appendText(specialChar2.getValue());
}
public void addCharë(ActionEvent actionEvent) {
welsh.appendText("ë");
public void specialChar3(ActionEvent actionEvent) {
welsh.appendText(specialChar3.getValue());
}
public void addCharï(ActionEvent actionEvent) {
welsh.appendText("ï");
}
public void addCharö(ActionEvent actionEvent) {
welsh.appendText("ö");
}
public void addCharü(ActionEvent actionEvent) {
welsh.appendText("ü");
}
public void addCharẅ(ActionEvent actionEvent) {
welsh.appendText("");
}
public void addCharÿ(ActionEvent actionEvent) {
welsh.appendText("ÿ");
}
// áéíóúẃý
public void addChará(ActionEvent actionEvent) {
welsh.appendText("á");
}
public void addCharé(ActionEvent actionEvent) {
welsh.appendText("é");
}
public void addCharí(ActionEvent actionEvent) {
welsh.appendText("í");
}
public void addCharó(ActionEvent actionEvent) {
welsh.appendText("ó");
}
public void addCharú(ActionEvent actionEvent) {
welsh.appendText("ú");
}
public void addCharẃ(ActionEvent actionEvent) {
welsh.appendText("");
}
public void addCharý(ActionEvent actionEvent) {
welsh.appendText("ý");
}
// àèìòùẁỳ
public void addCharà(ActionEvent actionEvent) {
welsh.appendText("à");
}
public void addCharè(ActionEvent actionEvent) {
welsh.appendText("è");
}
public void addCharì(ActionEvent actionEvent) { welsh.appendText("ì"); }
public void addCharò(ActionEvent actionEvent) {
welsh.appendText("ò");
}
public void addCharù(ActionEvent actionEvent) {
welsh.appendText("ù");
}
public void addCharẁ(ActionEvent actionEvent) {
welsh.appendText("");
}
public void addCharỳ(ActionEvent actionEvent) {
welsh.appendText("");
public void specialChar4(ActionEvent actionEvent) {
welsh.appendText(specialChar4.getValue());
}
}

View file

@ -46,6 +46,10 @@ import java.util.Comparator;
*/
public class DictionaryController extends SharedCodeController {
// /////////////////// //
// Instance variables. //
// /////////////////// //
@FXML
private ImageView alphaSort;
@FXML
@ -61,6 +65,10 @@ public class DictionaryController extends SharedCodeController {
public ObservableList<DictionaryEntry> list = FXCollections.observableArrayList();
// //////// //
// Methods. //
// //////// //
/**
* Method to switch the language used to sort the dictionary list.
* <p>
@ -138,6 +146,8 @@ public class DictionaryController extends SharedCodeController {
*/
public void initialize() {
setup();
// Hide TableViewHeader
table.widthProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number t1) {
@ -151,6 +161,8 @@ public class DictionaryController extends SharedCodeController {
}
}
});
// Compare words while ignoring the "to"
english.setComparator(new Comparator<String>() {
@Override
public int compare(String s, String t1) {
@ -166,7 +178,7 @@ public class DictionaryController extends SharedCodeController {
}
});
currentPageIcon.setImage(new Image(getClass().getResourceAsStream("/assets/icons/white_icons/50px/read-50.png")));
currentPageIcon.setImage(new Image(getClass().getResourceAsStream("/assets/icons/white_icons/50px/read-50.png")));
currentPageText.setText("Dictionary");
alphaSort.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/sort-alpha-up-50.png"));
@ -190,6 +202,8 @@ public class DictionaryController extends SharedCodeController {
}
}
};
// Unsets practice word when they are clicked on
row.setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getClickCount() == 1 && (!row.isEmpty())) {
if (row.getItem().isPracticeWord()) {
@ -201,11 +215,9 @@ public class DictionaryController extends SharedCodeController {
}
}
Application.practiceList.removeAll(toRemove);
// row.getItem().setPracticeWord(false);
} else if (!row.getItem().isPracticeWord()) {
Application.dictionary.get(list.indexOf(row.getItem())).setPracticeWord(true);
Application.practiceList.add(row.getItem());
// row.getItem().setPracticeWord(true);
}
table.getSelectionModel().clearSelection();
}
@ -214,6 +226,7 @@ public class DictionaryController extends SharedCodeController {
}
);
// Render Welsh nouns as "[masculine noun] {nm}" and "[feminine noun] {nm}" respectively.
welsh.setCellValueFactory(dictionaryEntryStringCellDataFeatures -> {
if (dictionaryEntryStringCellDataFeatures.getValue().getWordType().equals(DictionaryEntry.wordTypeEnum.nm)) {
return new SimpleStringProperty(dictionaryEntryStringCellDataFeatures.getValue().getWelsh() + " {nm}");
@ -224,6 +237,7 @@ public class DictionaryController extends SharedCodeController {
}
});
// Render English verbs as "to [verb]"
english.setCellValueFactory(dictionaryEntryStringCellDataFeatures -> {
if (dictionaryEntryStringCellDataFeatures.getValue().getWordType().equals(DictionaryEntry.wordTypeEnum.verb)) {
return new SimpleStringProperty("to " + dictionaryEntryStringCellDataFeatures.getValue().getEnglish());
@ -232,31 +246,41 @@ public class DictionaryController extends SharedCodeController {
}
});
FilteredList<DictionaryEntry> filteredList = new FilteredList<>(list, p -> true); // Wrap list in a FilteredList
// Wrap list in a FilteredList
FilteredList<DictionaryEntry> filteredList = new FilteredList<>(list, p -> true);
searchBox.textProperty().addListener((observable, oldSearchTerm, newSearchTerm) -> {
filteredList.setPredicate(dictionaryEntry -> { // returns true on a filter match, false if no match
boolean result = false;
table.refresh(); // This fixes the table highlighting issue
if (newSearchTerm == null || newSearchTerm.isEmpty()) { // If filter text is empty, display all dictionary entries
// returns true on a filter match, false if no match
filteredList.setPredicate(dictionaryEntry -> {
boolean result = false;
// This fixes the table highlighting issue
table.refresh();
// If filter text is empty, display all dictionary entries
if (newSearchTerm == null || newSearchTerm.isEmpty()) {
result = true;
} else {
// need all same case for compare.
final String lowerCaseSearchFilter = newSearchTerm.toLowerCase();
if (isSortedByEnglish) {
if (dictionaryEntry.getEnglish().toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches English
// Filter matches English
result = true;
}
else if(lowerCaseSearchFilter.startsWith("to ")){
if (dictionaryEntry.getWordType().equals(DictionaryEntry.wordTypeEnum.verb) && ("to " + dictionaryEntry.getEnglish()).toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches ['to' + a word] or [a word] if word is a verb
// Filter matches ['to' + a word] or [a word] if word is a verb
result = true;
}
}
}
else {
if (dictionaryEntry.getWelsh().toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches Welsh
// Filter matches Welsh
result = true;
}
}
}
@ -264,15 +288,21 @@ public class DictionaryController extends SharedCodeController {
});
});
SortedList<DictionaryEntry> sortedList = new SortedList<>(filteredList); //Wrap the filtered list in a SortedList
sortedList.comparatorProperty().bind(table.comparatorProperty()); //Bind the sorted list comparator to the table comparator
//Wrap the filtered list in a SortedList
SortedList<DictionaryEntry> sortedList = new SortedList<>(filteredList);
//Bind the sorted list comparator to the table comparator
sortedList.comparatorProperty().bind(table.comparatorProperty());
table.setItems(sortedList);
// Change Table sorting based on boolean value
if(isSortedByEnglish){
table.getSortOrder().add(english);
langSort.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/sort-lang-eng-50.png"));
} else{
table.getSortOrder().add(welsh);
langSort.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/sort-lang-welsh-50.png"));
}
}

View file

@ -42,7 +42,8 @@ public class FlashcardController extends SharedCodeController {
// /////////////////// //
int index = 0;
Node card; // Node that will be flipped using RotateTransition.
// Node that will be flipped using RotateTransition.
Node card;
@FXML
private Text counter;
@ -70,22 +71,25 @@ public class FlashcardController extends SharedCodeController {
*/
@FXML
private void initialize() {
setup(); // Call method from SharedCodeController to setup the menu screens images.
// Call method from SharedCodeController to setup the menu screens images.
setup();
currentPageIcon.setImage(new Image("file:src/main/resources/assets/icons/white_icons/50px/flashcard-50.png"));
currentPageText.setText("Flashcard");
flashcardIcon.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/flashcard-50.png"));
flashcardsText.setFill(Color.BLACK);
if(isSortedByEnglish){ // If the current language ordering is by english, display the english word first.
// If the current language ordering is by english, display the english word first.
if(isSortedByEnglish){
testWord.setText(Application.practiceList.getFirst().getEnglish());
wordType.setText("English");
} else{ // Else display the word definition first.
} else{
// Else display the word definition first.
testWord.setText(Application.practiceList.getFirst().getWelsh());
wordType.setText("Welsh");
}
updateCounter(); // Update the on screen counter and setup the flashcards images.
// Update the on screen counter and setup the flashcards images.
updateCounter();
flashcard.setImage(new Image("file:src/main/resources/assets/flashcard/FlashCard.png"));
leftArrow.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/left-50.png"));
rightArrow.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/right-50.png"));
@ -99,8 +103,10 @@ public class FlashcardController extends SharedCodeController {
@FXML
private void handleFlashcardClick() {
card = flashcard;
RotateTransition rotator = RotateCard(card); // Call method to create the RotateTransition.
rotator.play(); // Play the rotate transition.
// Call method to create the RotateTransition.
RotateTransition rotator = RotateCard(card);
// Play the rotate transition.
rotator.play();
}
/**
@ -116,10 +122,12 @@ public class FlashcardController extends SharedCodeController {
index--;
updateCounter();
if (isSortedByEnglish) { // If the current language ordering is by english, display the english word first.
// If the current language ordering is by english, display the english word first.
if (isSortedByEnglish) {
testWord.setText(Application.practiceList.get(index).getEnglish());
wordType.setText("English");
} else { // Else display the word definition first.
} else {
// Else display the word definition first.
testWord.setText(Application.practiceList.get(index).getWelsh());
wordType.setText("Welsh");
}
@ -139,10 +147,12 @@ public class FlashcardController extends SharedCodeController {
index++;
updateCounter();
if (isSortedByEnglish) { // If the current language ordering is by english, display the english word first.
// If the current language ordering is by english, display the english word first.
if (isSortedByEnglish) {
testWord.setText(Application.practiceList.get(index).getEnglish());
wordType.setText("English");
} else { // Else display the word definition first.
} else {
// Else display the word definition first.
testWord.setText(Application.practiceList.get(index).getWelsh());
wordType.setText("Welsh");
}
@ -182,12 +192,15 @@ public class FlashcardController extends SharedCodeController {
rotate.setToAngle(180);
rotate.setInterpolator(Interpolator.LINEAR);
rotate.setCycleCount(1);
rotate.setOnFinished(event -> { // Once the transition is completed, update the text on the flashcard.
// Once the transition is completed, update the text on the flashcard.
rotate.setOnFinished(event -> {
if (wordType.getText().equals("Welsh")) { // If the word currently on the flashcard is welsh, display the english translation.
// If the word currently on the flashcard is welsh, display the english translation.
if (wordType.getText().equals("Welsh")) {
testWord.setText(Application.practiceList.get(index).getEnglish());
wordType.setText("English");
} else { // Else display the welsh translation.
} else {
// Else display the welsh translation.
testWord.setText(Application.practiceList.get(index).getWelsh());
wordType.setText("Welsh");
}

View file

@ -42,6 +42,10 @@ import java.util.Comparator;
*/
public class PracticeListController extends SharedCodeController {
// /////////////////// //
// Instance variables. //
// /////////////////// //
@FXML
private ImageView alphaSort;
@FXML
@ -57,6 +61,10 @@ public class PracticeListController extends SharedCodeController {
public ObservableList<DictionaryEntry> list = FXCollections.observableArrayList();
// //////// //
// Methods. //
// //////// //
/**
* Method to switch the language used to sort the dictionary list.
* <p>
@ -132,6 +140,8 @@ public class PracticeListController extends SharedCodeController {
*/
public void initialize() {
setup();
// Hide TableViewHeader
table.widthProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number t1) {
@ -146,6 +156,7 @@ public class PracticeListController extends SharedCodeController {
}
});
// Compare words while ignoring the "to"
english.setComparator(new Comparator<String>() {
@Override
public int compare(String s, String t1) {
@ -172,20 +183,19 @@ public class PracticeListController extends SharedCodeController {
list.addAll(Application.practiceList);
table.setPlaceholder(new Label("No practice words found. Please try adding a practice word from the 'Dictionary' page."));
table.setRowFactory(tv -> {
TableRow<DictionaryEntry> row = new TableRow<DictionaryEntry>() {
@Override
protected void updateItem(DictionaryEntry dictionaryEntry, boolean b) {
super.updateItem(dictionaryEntry, b);
if (!isEmpty()) {
// if (dictionaryEntry.isPracticeWord()) {
// setStyle("-fx-background-color: gray;");
// } else {
setStyle(" ");
// }
}
}
};
// Remove practice word when they are clicked on
row.setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getClickCount() == 1 && (!row.isEmpty())) {
for (DictionaryEntry entry : Application.dictionary) {
@ -212,6 +222,8 @@ public class PracticeListController extends SharedCodeController {
return row;
}
);
// Render Welsh nouns as "[masculine noun] {nm}" and "[feminine noun] {nm}" respectively.
welsh.setCellValueFactory(dictionaryEntryStringCellDataFeatures -> {
if (dictionaryEntryStringCellDataFeatures.getValue().getWordType().equals(DictionaryEntry.wordTypeEnum.nm)) {
return new SimpleStringProperty(dictionaryEntryStringCellDataFeatures.getValue().getWelsh() + " {nm}");
@ -222,6 +234,7 @@ public class PracticeListController extends SharedCodeController {
}
});
// Render English verbs as "to [verb]"
english.setCellValueFactory(dictionaryEntryStringCellDataFeatures -> {
if (dictionaryEntryStringCellDataFeatures.getValue().getWordType().equals(DictionaryEntry.wordTypeEnum.verb)) {
return new SimpleStringProperty("to " + dictionaryEntryStringCellDataFeatures.getValue().getEnglish());
@ -230,29 +243,42 @@ public class PracticeListController extends SharedCodeController {
}
});
FilteredList<DictionaryEntry> filteredList = new FilteredList<>(list, p -> true); // Wrap list in a FilteredList
// Wrap list in a FilteredList
FilteredList<DictionaryEntry> filteredList = new FilteredList<>(list, p -> true);
searchBox.textProperty().addListener((observable, oldSearchTerm, newSearchTerm) -> {
filteredList.setPredicate(dictionaryEntry -> { // returns true on a filter match, false if no match
boolean result = false;
table.refresh(); // This fixes the table highlighting issue
if (newSearchTerm == null || newSearchTerm.isEmpty()) { // If filter text is empty, display all dictionary entries
// Returns true on a filter match, false if no match
filteredList.setPredicate(dictionaryEntry -> {
boolean result = false;
// This fixes the table highlighting issue
table.refresh();
// If filter text is empty, display all dictionary entries
if (newSearchTerm == null || newSearchTerm.isEmpty()) {
result = true;
} else {
// need all same case for compare.
final String lowerCaseSearchFilter = newSearchTerm.toLowerCase();
if (isSortedByEnglish) {
if (dictionaryEntry.getEnglish().toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches English
// Filter matches English
result = true;
} else if (lowerCaseSearchFilter.startsWith("to ")) {
if (dictionaryEntry.getWordType().equals(DictionaryEntry.wordTypeEnum.verb) && ("to " + dictionaryEntry.getEnglish()).toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches ['to' + a word] or [a word] if word is a verb
// Filter matches ['to' + a word] or [a word] if word is a verb
result = true;
}
}
} else {
if (dictionaryEntry.getWelsh().toLowerCase().startsWith(lowerCaseSearchFilter)) {
result = true; // Filter matches Welsh
// Filter matches Welsh
result = true;
}
}
}
@ -260,15 +286,21 @@ public class PracticeListController extends SharedCodeController {
});
});
SortedList<DictionaryEntry> sortedList = new SortedList<>(filteredList); //Wrap the filtered list in a SortedList
sortedList.comparatorProperty().bind(table.comparatorProperty()); //Bind the sorted list comparator to the table comparator
// Wrap the filtered list in a SortedList
SortedList<DictionaryEntry> sortedList = new SortedList<>(filteredList);
// Bind the sorted list comparator to the table comparator
sortedList.comparatorProperty().bind(table.comparatorProperty());
table.setItems(sortedList);
// Change Table sorting based on boolean value
if (isSortedByEnglish) {
table.getSortOrder().add(english);
langSort.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/sort-lang-eng-50.png"));
} else {
table.getSortOrder().add(welsh);
langSort.setImage(new Image("file:src/main/resources/assets/icons/black_icons/50px/sort-lang-welsh-50.png"));
}
}

View file

@ -1,3 +1,9 @@
/**
* @(#) FlashcardController.java 0,1 2020/05/07
* <p>
* Copyright (c) 2020 Aberystwyth University.
* All rights reserved.
*/
package uk.ac.aber.cs221.group20.javafx;
import javafx.fxml.FXML;
@ -12,8 +18,9 @@ import uk.ac.aber.cs221.group20.json.DictionaryEntry;
* Abstract class that contains all the shared FXML elements between the
* different controller classes including the sliding menu and the test score counter, to reduce code
* duplication. This will be extended by all the controller classes.
*
* @author Tom Perry [top19]
* @Version 0.1 Initial development.
* @Version 0.2 Documentation.
* @see Application
* @see DictionaryEntry
* @see ScreenSwitch
@ -25,7 +32,9 @@ abstract public class SharedCodeController {
// Class variables. //
// //////////////// //
// Static variable that tracks whether the words are currently sorted by english or welsh.
static boolean isSortedByEnglish = true;
// Static variable that tracks the current size of the menu's sideBar to keep its size consistent when switching scenes.
static int sideBarWidth = 50;
// /////////////////// //
@ -71,9 +80,11 @@ abstract public class SharedCodeController {
* Method that sets up the program's menu in each of the controllers, intialising the icons and text.
*/
public void setup() {
// Call method to setup the icons and set the menus side bars width to the current value of 'sideBar'.
initializeIcons();
sideBar.setWidth(sideBarWidth);
// If the menus width isnt the default value, the menu is determined to be expanded and the text is initialised.
if (sideBarWidth != 50)
initializeMenuText();
}
@ -102,7 +113,7 @@ abstract public class SharedCodeController {
}
/**
* Method that disables the menus text when the menu is collapsed.
* Method that disables the menus text when the menu is collapsed by setting their text to nothing.
*/
private void disableMenuText() {
dictionaryText.setText("");
@ -113,41 +124,49 @@ abstract public class SharedCodeController {
}
/**
* Event that expands the menu whenever the menu's 'expandMenuIcon' icon is clicked.
* Event that collapses or expands the menu whenever the 'expandMenuIcon' is clicked by the user. The method determines the menus current state by looking at the value of
* 'sideBarWidth' and uses to decide whether the menu needs to expand to 230 and initialise the menu text or collapse to 50, disabling menu text.
*/
@FXML
private void expandMenuClick() {
if(sideBar.getWidth() == 50) { // If sideBar is currently collapsed, expand it and display menu text.
// If sideBar is currently collapsed, expand it and display menu text.
if(sideBar.getWidth() == 50) {
sideBar.setWidth(sideBarWidth = 230);
initializeMenuText(); // Display menu
// Display menu
initializeMenuText();
} else {
sideBar.setWidth(sideBarWidth = 50); // Else collapse the menu and disable its text.
// Else collapse the menu and disable its text.
sideBar.setWidth(sideBarWidth = 50);
disableMenuText();
}
}
/**
* Event to switch scenes to 'dictionary.fxml' when the menu's 'dictionaryIcon' icon is clicked.
*
* @see ScreenSwitch
*/
@FXML
private void dictionaryIconClick() {
// Use 'ScreenSwitch' to switch to the 'dictionaryScene'.
ScreenSwitch.swap(ScreenSwitch.SceneType.dictionaryScene);
}
/**
* Event to switch scenes to 'practicelist.fxml' when the menu's 'practiceListIcon' icon is clicked.
*
* @see ScreenSwitch
*/
@FXML
private void practiceListIconClick() {
ScreenSwitch.swap(ScreenSwitch.SceneType.practiceListScene);
// Use 'ScreenSwitch' to switch to the 'practiceListScene'.
ScreenSwitch.swap(ScreenSwitch.SceneType.practiceListScene);
}
/**
* Event to switch scenes to 'flashcard.fxml' when the menu's 'practiceListIcon' icon is clicked. This method checks to see if practiceList is empty before switching in order
* to avoid NullPointerException's in the flashcard scene.
*
* @see ScreenSwitch
* @see Application
* @see DictionaryEntry
@ -155,36 +174,40 @@ abstract public class SharedCodeController {
*/
@FXML
private void flashcardIconClick() {
if(Application.practiceList.size() == 0) { // Check to see if there are any practice words before switching scene, throwing an alert notifying them that they can't switch scenes.
// Check to see if there are any practice words before switching scene, throwing an alert notifying them that they can't switch scenes.
if(Application.practiceList.size() == 0) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Unable to use Flashcard");
alert.setContentText("The practice list is currently empty, please add some practice words to use the Flashcard feature.");
alert.showAndWait();
} else{
ScreenSwitch.swap(ScreenSwitch.SceneType.flashcardScene); // Switch to flashcard scene if the program has practice words.
// Switch to flashcard scene if the program has practice words.
ScreenSwitch.swap(ScreenSwitch.SceneType.flashcardScene);
}
}
/**
* Event to generate an assessment using AssessmentGenerator when the menu's 'studyIcon' icon is clicked.
*
* @see AssessmentGenerator
* @see Application
* @see DictionaryEntry
*/
@FXML
private void studyIconClick() {
// Generate a new assessment using the programs practice list.
AssessmentGenerator.generateAssessment(Application.practiceList);
}
/**
* Event to switch scenes to 'addword.fxml' when the menu's 'addwordIcon' icon is clicked.
*
* @see ScreenSwitch
*/
@FXML
private void addWordIconClick(){
// Use 'ScreenSwitch' to switch to the 'addWordScene'.
ScreenSwitch.swap(ScreenSwitch.SceneType.addWordScene);
}

View file

@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
/**
* Class that contains methods which will be used to test that the JSON package classes are
* correctly loading and saving to and from the JSON file.
*
* @author Tom Perry [top19]
* @version 0.1 Initial development.
* @see JsonProcessing
@ -25,67 +26,85 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
*/
public class JSONTest {
static LinkedList<DictionaryEntry> testList;
LinkedList<DictionaryEntry> loadedList;
static File testFile;
static JsonProcessing processor = new JsonProcessing();
// //////////////// //
// Class variables. //
// //////////////// //
/**
* Setup method that is run before all of the tests, setting up a test list of DictionaryEntry's that it saved to a JSON test file.
* @see JsonProcessing
* @see DictionaryEntry
*/
@BeforeAll
public static void setupTest() {
static LinkedList<DictionaryEntry> testList;
LinkedList<DictionaryEntry> loadedList;
static File testFile;
static JsonProcessing processor = new JsonProcessing();
// Populate a test list with DictionaryEntrys that is to be used for the loading/saving tests.
testList = new LinkedList<>(Arrays.asList(new DictionaryEntry("abbey","abaty", DictionaryEntry.wordTypeEnum.other), new DictionaryEntry("about to", "ar fin", DictionaryEntry.wordTypeEnum.other),
new DictionaryEntry("above","uwchben", DictionaryEntry.wordTypeEnum.other), new DictionaryEntry("abroad","dramor", DictionaryEntry.wordTypeEnum.other),
new DictionaryEntry("abstract","haniaethol", DictionaryEntry.wordTypeEnum.other)));
// ////////////// //
// Class methods. //
// ////////////// //
// Create a JSON test file in the test package.
testFile = new File("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json");
/**
* Setup method that is run before all of the tests, setting up a test list of DictionaryEntry's that it saved to a JSON test file.
*
* @see JsonProcessing
* @see DictionaryEntry
*/
@BeforeAll
public static void setupTest() {
// Save the testList to the testFile.
processor.writeOutJson("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json", testList);
}
// Populate a test list with DictionaryEntrys that is to be used for the loading/saving tests.
testList = new LinkedList<>(Arrays.asList(new DictionaryEntry("abbey","abaty", DictionaryEntry.wordTypeEnum.other), new DictionaryEntry("about to", "ar fin", DictionaryEntry.wordTypeEnum.other),
new DictionaryEntry("above","uwchben", DictionaryEntry.wordTypeEnum.other), new DictionaryEntry("abroad","dramor", DictionaryEntry.wordTypeEnum.other),
new DictionaryEntry("abstract","haniaethol", DictionaryEntry.wordTypeEnum.other)));
/**
* JUnit test to check that the JSON file has been correctly loaded.
* @see JsonProcessing
* @see DictionaryEntry
*/
@Test
public void testLoad(){
// Create a JSON test file in the test package.
testFile = new File("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json");
// Load the DictionaryEntry's from testFile and check if the loaded list matches the test list.
loadedList = processor.readInJson(testFile);
assertArrayEquals(testList.toArray(),loadedList.toArray());
}
// Save the testList to the testFile.
processor.writeOutJson("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json", testList);
}
/**
* JUnit test to check that any changes to the list of definitions are
* updated and saved to the JSON file accordingly.
* @see JsonProcessing
* @see DictionaryEntry
*/
@Test public void testSave(){
// Add an additional word to the testList and save it to jsontest.
testList.add(new DictionaryEntry("beer", "cwrw", DictionaryEntry.wordTypeEnum.nm));
processor.writeOutJson("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json", testList);
/**
* Method that is ran after the JUnit tests have finished to remove the JSON test file from program.
*/
@AfterAll
public static void deleteFile() {
testFile.delete();
}
// Load the DictionaryEntry's back from the file and check that they match the testList.
loadedList = processor.readInJson(testFile);
assertArrayEquals(testList.toArray(), loadedList.toArray());
}
// //////// //
// Methods. //
// //////// //
/**
* JUnit test to check that the JSON file has been correctly loaded. This works by loading the test file and check
* its contents match the list that was saved to it.
*
* @see JsonProcessing
* @see DictionaryEntry
*/
@Test
public void testLoad(){
// Load the DictionaryEntry's from testFile and check if the loaded list matches the test list.
loadedList = processor.readInJson(testFile);
assertArrayEquals(testList.toArray(),loadedList.toArray());
}
/**
* JUnit test to check that changes to the list of definitions are
* updated and saved to the JSON file accordingly. This is done by adding a new item to the JSON test list and
* saving it to the file before reloading it to check the loaded list matches the updated test list.
*
* @see JsonProcessing
* @see DictionaryEntry
*/
@Test public void testSave(){
// Add an additional word to the testList and save it to jsontest.
testList.add(new DictionaryEntry("beer", "cwrw", DictionaryEntry.wordTypeEnum.nm));
processor.writeOutJson("src/main/java/uk/ac/aber/cs22120/group20/test/jsontest.json", testList);
// Load the DictionaryEntry's back from the file and check that they match the testList.
loadedList = processor.readInJson(testFile);
assertArrayEquals(testList.toArray(), loadedList.toArray());
}
/**
* Method that is ran after the JUnit tests have finished to remove the JSON test file from program.
*/
@AfterAll
public static void deleteFile() {
testFile.delete();
}
}

View file

@ -1,5 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
@ -15,186 +13,205 @@
<?import javafx.scene.shape.Rectangle?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<?import javafx.collections.FXCollections?>
<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<BorderPane minHeight="550" minWidth="500" stylesheets="" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="uk.ac.aber.cs221.group20.javafx.AddWordController">
<BorderPane minHeight="550" minWidth="500" stylesheets="" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="uk.ac.aber.cs221.group20.javafx.AddWordController">
<left>
<left>
<StackPane fx:id="outerBar">
<Rectangle fx:id="sideBar" fill="dimgray" height="${outerBar.height}" width="50" />
<StackPane fx:id="outerBar">
<Rectangle fx:id="sideBar" fill="dimgray" height="${outerBar.height}" width="50" />
<VBox spacing="300">
<VBox spacing="300">
<VBox alignment="TOP_CENTER" maxHeight="${outerBar.height}">
<StackPane onMouseClicked="#dictionaryIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="dictionaryText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="dictionaryIcon" />
</HBox>
</StackPane>
<VBox alignment="TOP_CENTER" maxHeight="${outerBar.height}">
<StackPane onMouseClicked="#dictionaryIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="dictionaryText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="dictionaryIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#practiceListIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="practiceListTest" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="practiceListIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#practiceListIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="practiceListTest" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="practiceListIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#flashcardIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="flashcardsText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="flashcardIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#flashcardIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="flashcardsText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="flashcardIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#studyIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="studyText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="studyIcon" />
</HBox>
</StackPane>
<StackPane onMouseClicked="#studyIconClick">
<Rectangle fill="dimgray" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="studyText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="studyIcon" />
</HBox>
</StackPane>
</VBox>
<StackPane alignment="BOTTOM_CENTER" onMouseClicked="#addWordIconClick">
<Rectangle fill="white" height="60" width="${sideBar.width}" />
<HBox alignment="CENTER_RIGHT">
<Text fx:id="addDefinitionText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
<ImageView fx:id="addDefinitionIcon" />
</HBox>
</StackPane>
</VBox>
</StackPane>
</left>
<StackPane alignment="BOTTOM_CENTER" onMouseClicked="#addWordIconClick">
<Rectangle fill="white" height="60" width="${sideBar.width}" />
<top>
<StackPane fx:id="topBar">
<Rectangle fx:id="parentRectangle" fill="dimgray" height="50" width="${topBar.width}" />
<HBox alignment="CENTER_LEFT" prefWidth="${topBar.width}" spacing="7">
<StackPane onMouseClicked="#expandMenuClick">
<Rectangle fill="dimgray" height="50" width="55" />
<ImageView fx:id="expandMenuIcon" />
</StackPane>
<ImageView fx:id="currentPageIcon" />
<Text fx:id="currentPageText" fill="white">
<font>
<HBox alignment="CENTER_RIGHT">
<Text fx:id="addDefinitionText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
</HBox>
</font>
</Text>
<ImageView fx:id="addDefinitionIcon" />
</HBox>
</StackPane>
</top>
</StackPane>
</VBox>
</StackPane>
<center>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="506.0" prefWidth="702.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
</left>
<top>
<StackPane fx:id="topBar">
<Rectangle fx:id="parentRectangle" fill="dimgray" height="50" width="${topBar.width}" />
<HBox alignment="CENTER_LEFT" prefWidth="${topBar.width}" spacing="7">
<StackPane onMouseClicked="#expandMenuClick">
<Rectangle fill="dimgray" height="50" width="55" />
<ImageView fx:id="expandMenuIcon" />
</StackPane>
<ImageView fx:id="currentPageIcon" />
<Text fx:id="currentPageText" fill="white">
<font>
<Font name="System Bold" size="25" />
</font>
</Text>
</HBox>
</StackPane>
</top>
<center>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="506.0" prefWidth="702.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="84.33331298828125" minWidth="10.0" prefWidth="10.666646321614593" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="273.3333536783854" minWidth="10.0" prefWidth="273.3333536783854" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="87.3333740234375" minWidth="7.6666259765625" prefWidth="7.6666259765625" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="50.66668701171875" minHeight="10.0" prefHeight="50.66668701171875" vgrow="SOMETIMES" />
<RowConstraints maxHeight="46.33333333333332" minHeight="10.0" prefHeight="46.33333333333332" vgrow="SOMETIMES" />
<RowConstraints maxHeight="37.666646321614564" minHeight="10.0" prefHeight="37.666646321614564" vgrow="SOMETIMES" />
<RowConstraints maxHeight="34.3333740234375" minHeight="10.0" prefHeight="34.3333740234375" vgrow="SOMETIMES" />
<RowConstraints maxHeight="123.99998474121094" minHeight="10.0" prefHeight="76.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="110.0" minHeight="10.0" prefHeight="31.66668701171872" vgrow="SOMETIMES" />
<RowConstraints maxHeight="133.0" minHeight="10.0" prefHeight="28.999979654947936" vgrow="SOMETIMES" />
<RowConstraints maxHeight="129.33331298828125" minHeight="10.0" prefHeight="129.33331298828125" vgrow="SOMETIMES" />
</rowConstraints>
<Label alignment="CENTER" prefHeight="17.0" prefWidth="105.0" text="English" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label alignment="CENTER" prefHeight="17.0" prefWidth="105.0" text="Welsh" GridPane.columnIndex="5" GridPane.rowIndex="2" />
<ComboBox fx:id="wordType" prefHeight="25.0" prefWidth="275.0" promptText="Type" GridPane.columnIndex="3" GridPane.rowIndex="3" />
<Button mnemonicParsing="false" onAction="#addButtonClick" prefHeight="25.0" prefWidth="274.0" text="Add Word" GridPane.columnIndex="3" GridPane.rowIndex="4" />
<TextField fx:id="english" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<TextField fx:id="welsh" GridPane.columnIndex="5" GridPane.rowIndex="3" />
<GridPane GridPane.columnIndex="3" GridPane.rowIndex="5">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="84.33331298828125" minWidth="10.0" prefWidth="10.666646321614593" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="273.3333536783854" minWidth="10.0" prefWidth="273.3333536783854" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="87.3333740234375" minWidth="7.6666259765625" prefWidth="7.6666259765625" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="50.66668701171875" minHeight="10.0" prefHeight="50.66668701171875" vgrow="SOMETIMES" />
<RowConstraints maxHeight="46.33333333333332" minHeight="10.0" prefHeight="46.33333333333332" vgrow="SOMETIMES" />
<RowConstraints maxHeight="37.666646321614564" minHeight="10.0" prefHeight="37.666646321614564" vgrow="SOMETIMES" />
<RowConstraints maxHeight="34.3333740234375" minHeight="10.0" prefHeight="34.3333740234375" vgrow="SOMETIMES" />
<RowConstraints maxHeight="123.99998474121094" minHeight="10.0" prefHeight="76.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="110.0" minHeight="10.0" prefHeight="31.66668701171872" vgrow="SOMETIMES" />
<RowConstraints maxHeight="133.0" minHeight="10.0" prefHeight="28.999979654947936" vgrow="SOMETIMES" />
<RowConstraints maxHeight="129.33331298828125" minHeight="10.0" prefHeight="129.33331298828125" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<Label alignment="CENTER" prefHeight="17.0" prefWidth="105.0" text="English" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label alignment="CENTER" prefHeight="17.0" prefWidth="105.0" text="Welsh" GridPane.columnIndex="5" GridPane.rowIndex="2" />
<ComboBox fx:id="wordType" prefHeight="25.0" prefWidth="275.0" promptText="Type" GridPane.columnIndex="3" GridPane.rowIndex="3" />
<Button mnemonicParsing="false" onAction="#addButtonClick" prefHeight="25.0" prefWidth="274.0" text="Add Word" GridPane.columnIndex="3" GridPane.rowIndex="4" />
<TextField fx:id="english" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<TextField fx:id="welsh" GridPane.columnIndex="5" GridPane.rowIndex="3" />
<GridPane alignment="CENTER" prefHeight="236.0" prefWidth="328.0" GridPane.columnIndex="3" GridPane.rowIndex="6" vgap="5">
<columnConstraints>
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="100.0" valignment="CENTER" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button onAction="#addCharâ" mnemonicParsing="false" text="â" />
<Button onAction="#addCharê" mnemonicParsing="false" text="ê" GridPane.columnIndex="1" />
<Button onAction="#addCharî" mnemonicParsing="false" text="î" GridPane.columnIndex="2" />
<Button onAction="#addCharô" mnemonicParsing="false" text="ô" GridPane.columnIndex="3" />
<Button onAction="#addCharû" mnemonicParsing="false" text="û" GridPane.columnIndex="4" />
<Button onAction="#addCharŵ" mnemonicParsing="false" text="ŵ" GridPane.columnIndex="5" />
<Button onAction="#addCharŷ" mnemonicParsing="false" text="ŷ" GridPane.columnIndex="6" />
<Button onAction="#addCharä" mnemonicParsing="false" text="ä" GridPane.rowIndex="1" />
<Button onAction="#addCharë" mnemonicParsing="false" text="ë" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Button onAction="#addCharï" mnemonicParsing="false" text="ï" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Button onAction="#addCharö" mnemonicParsing="false" text="ö" GridPane.columnIndex="3" GridPane.rowIndex="1" />
<Button onAction="#addCharü" mnemonicParsing="false" text="ü" GridPane.columnIndex="4" GridPane.rowIndex="1" />
<Button onAction="#addCharẅ" mnemonicParsing="false" text="ẅ" GridPane.columnIndex="5" GridPane.rowIndex="1" />
<Button onAction="#addCharÿ" mnemonicParsing="false" text="ÿ" GridPane.columnIndex="6" GridPane.rowIndex="1" />
<Button onAction="#addChará" mnemonicParsing="false" text="á" GridPane.rowIndex="2" />
<Button onAction="#addCharé" mnemonicParsing="false" text="é" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Button onAction="#addCharí" mnemonicParsing="false" text="í" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<Button onAction="#addCharó" mnemonicParsing="false" text="ó" GridPane.columnIndex="3" GridPane.rowIndex="2" />
<Button onAction="#addCharú" mnemonicParsing="false" text="ú" GridPane.columnIndex="4" GridPane.rowIndex="2" />
<Button onAction="#addCharẃ" mnemonicParsing="false" text="ẃ" GridPane.columnIndex="5" GridPane.rowIndex="2" />
<Button onAction="#addCharý" mnemonicParsing="false" text="ý" GridPane.columnIndex="6" GridPane.rowIndex="2" />
<Button onAction="#addCharà" mnemonicParsing="false" text="à" GridPane.rowIndex="3" />
<Button onAction="#addCharè" mnemonicParsing="false" text="è" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Button onAction="#addCharì" mnemonicParsing="false" text="ì" GridPane.columnIndex="2" GridPane.rowIndex="3" />
<Button onAction="#addCharò" mnemonicParsing="false" text="ò" GridPane.columnIndex="3" GridPane.rowIndex="3" />
<Button onAction="#addCharù" mnemonicParsing="false" text="ù" GridPane.columnIndex="4" GridPane.rowIndex="3" />
<Button onAction="#addCharẁ" mnemonicParsing="false" text="ẁ" GridPane.columnIndex="5" GridPane.rowIndex="3" />
<Button onAction="#addCharỳ" mnemonicParsing="false" text="ỳ" GridPane.columnIndex="6" GridPane.rowIndex="3" />
</children>
<padding>
<Insets left="25.0" right="25.0" />
</padding>
</GridPane>
<children>
<ComboBox fx:id="specialChar1" prefWidth="56.0" promptText="â" onAction="#specialChar1">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="â" />
<String fx:value="ê" />
<String fx:value="î" />
<String fx:value="ô" />
<String fx:value="û" />
<String fx:value="ŵ" />
<String fx:value="ŷ" />
</FXCollections>
</items>
</GridPane>
</ComboBox>
<ComboBox fx:id="specialChar2" prefWidth="56.0" GridPane.columnIndex="2" promptText="ä" onAction="#specialChar2">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="ä" />
<String fx:value="ë" />
<String fx:value="ï" />
<String fx:value="ö" />
<String fx:value="ü" />
<String fx:value="ẅ" />
<String fx:value="ÿ" />
</FXCollections>
</items>
</ComboBox>
<ComboBox fx:id="specialChar3" prefWidth="56.0" GridPane.columnIndex="1" promptText="á" onAction="#specialChar3">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="á" />
<String fx:value="é" />
<String fx:value="í" />
<String fx:value="ó" />
<String fx:value="ú" />
<String fx:value="ẃ" />
<String fx:value="ý" />
</FXCollections>
</items>
</ComboBox>
<ComboBox fx:id="specialChar4" prefWidth="56.0" GridPane.columnIndex="3" promptText="à" onAction="#specialChar4">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="à" />
<String fx:value="è" />
<String fx:value="ì" />
<String fx:value="ò" />
<String fx:value="ù" />
<String fx:value="ẁ" />
<String fx:value="ỳ" />
</FXCollections>
</items>
</ComboBox>
</children>
</GridPane>
</GridPane>
</center>
</center>
</BorderPane>