0

I am building a JavaFX todo list and am not sure how to continue. The right click popup menu works fine but I am not sure how to edit/change the contents of the ListView other than just removing them.

LocalEvent e = a string somehow?

I am trying to do 4 total things in a right click popup menu in Javafx:

  1. Done is to place a check mark next to and strikeout the item.
  2. Nest is to create a nested list from a list item (no idea at all how).
  3. Edit is to make the list item editable and save the chages.
  4. Remove works :)

I have done this by adding the following to the fxml file:

<JFXListView fx:id="eventList" editable="true" layoutX="24.0" layoutY="106.0" prefHeight="354.0" prefWidth="939.0">

<contextMenu>
        <ContextMenu>
          <items>
            <MenuItem fx:id="popUp" mnemonicParsing="false" onAction="#Done" text="Done" />
              <MenuItem fx:id="popUp3" mnemonicParsing="false" onAction="#Remove" text="Remove" />
              <MenuItem fx:id="popUp1" mnemonicParsing="false" onAction="#Nest" text="Nest" />
              <MenuItem fx:id="popUp2" mnemonicParsing="false" onAction="#Edit" text="Edit" />
          </items>
        </ContextMenu>
     </contextMenu></JFXListView>`

Here is my Controller.java file:

package application;

import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXTextField;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DatePicker;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseEvent;

public class Controller implements Initializable{

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        datePicker.setValue(LocalDate.now());
        eventList.setExpanded(true);
        eventList.depthProperty().set(1);   
    }

    @FXML
    private MenuItem popUp;

    @FXML
    private JFXTextField textBox;

    @FXML
    private JFXListView<LocalEvent> eventList;
    ObservableList<LocalEvent> list = FXCollections.observableArrayList();

    @FXML
    private JFXButton AddButton;

    @FXML
    private DatePicker datePicker;


    @FXML
    void Submit(ActionEvent event) {
        list.add(new LocalEvent(datePicker.getValue(), textBox.getText()));
        eventList.setItems(list);
        datePicker.setValue(LocalDate.now());
        textBox.setText("");

    }

    @FXML
    public void onEnter(ActionEvent event){
        list.add(new LocalEvent(datePicker.getValue(), textBox.getText()));
        eventList.setItems(list);
        datePicker.setValue(LocalDate.now());
        textBox.setText("");    
    }

    @FXML
    void Done(ActionEvent event) {
        int index = eventList.getSelectionModel().getSelectedIndex();
        String str = list.get(index).toString();
        str = "[✓] " +  str;
        LocalEvent e = null;    // <- how to put a string in here?
        list.set(index, e);
        eventList.setItems(list);
        //eventList.setItems(list.set(index, element));
    }

    @FXML 
    void Remove(ActionEvent event) {
        // remove selected task
        list.remove(eventList.getSelectionModel().getSelectedIndex());
    }

    @FXML
    void Nest(ActionEvent event) {
        System.out.println("How the hell do I do that? lol");
        // check for nested level
        // create a nested list item

    }

    @FXML
    void Edit(ActionEvent e) {
        System.out.println("Edit selection");
        eventList.setEditable(true);
        int index = eventList.getSelectionModel().getSelectedIndex(); 
        eventList.scrollTo(index);
        eventList.layout();
        eventList.edit(index); 
        eventList.layout();
    }
}

LocalEvent is a java class file as follows:

package application;

import java.time.LocalDate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import javafx.beans.InvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

public class LocalEvent implements ObservableList<LocalEvent> {
    private String description;
    private LocalDate date;

    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public LocalDate getDate() {
        return date;
    }
    public void setDate(LocalDate date) {
        this.date = date;
    }

    public LocalEvent(LocalDate date, String description) {
        this.setDate(date);
        this.setDescription(description);
    }

    @Override
    public String toString() {
        return "At " + this.getDate() + ": " + this.getDescription();
    }
    @Override
    public int size() {
        // TODO Auto-generated method stub
        return 0;
    }
    @Override
    public boolean isEmpty() {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean contains(Object o) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public Iterator<LocalEvent> iterator() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public Object[] toArray() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public <T> T[] toArray(T[] a) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public boolean add(LocalEvent e) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean remove(Object o) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean containsAll(Collection<?> c) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean addAll(Collection<? extends LocalEvent> c) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean addAll(int index, Collection<? extends LocalEvent> c) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean removeAll(Collection<?> c) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean retainAll(Collection<?> c) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public void clear() {
        // TODO Auto-generated method stub

    }
    @Override
    public LocalEvent get(int index) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public LocalEvent set(int index, LocalEvent element) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void add(int index, LocalEvent element) {
        // TODO Auto-generated method stub

    }
    @Override
    public LocalEvent remove(int index) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public int indexOf(Object o) {
        // TODO Auto-generated method stub
        return 0;
    }
    @Override
    public int lastIndexOf(Object o) {
        // TODO Auto-generated method stub
        return 0;
    }
    @Override
    public ListIterator<LocalEvent> listIterator() {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public ListIterator<LocalEvent> listIterator(int index) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public List<LocalEvent> subList(int fromIndex, int toIndex) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public void addListener(InvalidationListener listener) {
        // TODO Auto-generated method stub

    }
    @Override
    public void removeListener(InvalidationListener listener) {
        // TODO Auto-generated method stub

    }
    @Override
    public void addListener(ListChangeListener<? super LocalEvent> listener) {
        // TODO Auto-generated method stub

    }
    @Override
    public void removeListener(ListChangeListener<? super LocalEvent> listener) {
        // TODO Auto-generated method stub

    }
    @Override
    public boolean addAll(LocalEvent... elements) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean setAll(LocalEvent... elements) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean setAll(Collection<? extends LocalEvent> col) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean removeAll(LocalEvent... elements) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean retainAll(LocalEvent... elements) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public void remove(int from, int to) {
        // TODO Auto-generated method stub

    }
}

Any help at all appreciated.

user1803551
  • 12,965
  • 5
  • 47
  • 74
  • What is LocalEvent? I don't see it in the API. – Shoikana Jul 24 '18 at 18:58
  • Updated my question to have LocalEvent in it – JuliaTheMad Jul 24 '18 at 19:21
  • Why would you create a class implementing `ObservableList` and then create an `ObservableList` of that class? `LocalEvent` shouldn't implement that, and all the `setItems()` can be removed as long you have done that once in the `initialize()` method, which you probably don't have at the moment. – Jai Jul 25 '18 at 01:29

1 Answers1

0

The first thing that stands out was mentioned in the comments: your LocalEvent shouldn't implements ObservableList, it's just a data holder. You probably will want it to also hold a boolean to see if it's completed:

public class LocalEvent {

    private String description;
    private LocalDate date;
    private boolean completed = false;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public LocalDate getDate() {
        return date;
    }

    public void setDate(LocalDate date) {
        this.date = date;
    }

    public void setCompleted(boolean completed) {
        this.completed= completed;
    }

    public LocalEvent(LocalDate date, String description) {
        this.setDate(date);
        this.setDescription(description);
    }

    @Override
    public String toString() {
        String base = "At " + this.getDate() + ": " + this.getDescription();
        return completed ? "[✓] " + base : base;
    }
}

I don't know what the jfoenix controls are, but I'll assume they are close enough to JavaFX standard controls. The ListView should be bound to the list of events:

ObservableList<LocalEvent> list = FXCollections.observableArrayList();
ListView<LocalEvent> eventList = new ListView<>(list);

(I would also rename the lists to something that makes more sense.)

Now any change to list will be reflected in eventList so you don't need to touch eventList. This allows your done method to look like:

@FXML
void done(ActionEvent event) {
    int index = eventList.getSelectionModel().getSelectedIndex();
    LocalEvent localEvent = list.get(index);
    localEvent.setCompleted(true);
    list.set(index, localEvent);
}

Note that we needed to reset the item in the list because it doesn't know that a field of the LocalEvent instance has changed. You can make it that the fields also report changes by using an extractor, see here and many other answers here about it.

Editing depends on the cell used by the ListView, I would suggest reading about it in the docs and other questions here. Note that you probably want to be able to edit the description and date separately and not the whole toString value, so you'll have to provide a mechanism for that like bringing back the DatePicker.

Nesting depends on what you want it to look like. You might need to define your own cell factory, but consider just adding an indentation, via "\t"s, which will make it looks nested. If you really need a nesting in the data model, then each LocalEvent will have to hold its own ObservableList<LocalEvent> nestedEvents.

Also, method names should start with a lowercase.

user1803551
  • 12,965
  • 5
  • 47
  • 74