Skip to content

Commit

Permalink
Merge pull request #48 from itsallcode/add-file-cache
Browse files Browse the repository at this point in the history
Autocomplete for comments
  • Loading branch information
kaklakariada authored Nov 29, 2020
2 parents f612816 + 109c5d5 commit 883e676
Show file tree
Hide file tree
Showing 34 changed files with 1,743 additions and 338 deletions.
24 changes: 0 additions & 24 deletions .github/workflows/lint.yml

This file was deleted.

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

See [Release](https://github.com/itsallcode/white-rabbit/releases/tag/v1.1.0) / [Milestone](https://github.com/itsallcode/white-rabbit/milestone/2?closed=1)

### Added

* [#16](https://github.com/itsallcode/white-rabbit/issues/16): Search-as-you-type for comments, select most common project for new activities.

## [1.0.1] 2020-11-25

See [Release](https://github.com/itsallcode/white-rabbit/releases/tag/v1.0.1) / [Milestone](https://github.com/itsallcode/white-rabbit/milestone/3?closed=1)
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ A time recording tool
[![Build](https://github.com/itsallcode/white-rabbit/workflows/Build/badge.svg)](https://github.com/itsallcode/white-rabbit/actions?query=workflow%3ABuild)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=white-rabbit&metric=alert_status)](https://sonarcloud.io/dashboard?id=white-rabbit)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=white-rabbit&metric=coverage)](https://sonarcloud.io/dashboard?id=white-rabbit)
[![deepcode](https://www.deepcode.ai/api/gh/badge?key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwbGF0Zm9ybTEiOiJnaCIsIm93bmVyMSI6Iml0c2FsbGNvZGUiLCJyZXBvMSI6IndoaXRlLXJhYmJpdCIsImluY2x1ZGVMaW50IjpmYWxzZSwiYXV0aG9ySWQiOjE1ODQ3LCJpYXQiOjE2MDExMjk1NTB9.J8l6aFttX7uETXvT1KzG2ai2kER_GJF94SZBOX9FTP0)](https://www.deepcode.ai/app/gh/itsallcode/white-rabbit/_/dashboard?utm_content=gh%2Fitsallcode%2Fwhite-rabbit)

* [Features](#features)
* [Usage](#usage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import org.apache.logging.log4j.Logger;
import org.itsallcode.whiterabbit.jfxui.JavaFxUtil;
import org.itsallcode.whiterabbit.jfxui.table.EditListener;
import org.itsallcode.whiterabbit.jfxui.table.PersistOnFocusLossTextFieldTableCell;
import org.itsallcode.whiterabbit.jfxui.table.converter.DurationStringConverter;
import org.itsallcode.whiterabbit.jfxui.ui.widget.AutoCompleteTextField;
import org.itsallcode.whiterabbit.jfxui.ui.widget.PersistOnFocusLossTextFieldTableCell;
import org.itsallcode.whiterabbit.logic.autocomplete.AutocompleteService;
import org.itsallcode.whiterabbit.logic.model.Activity;
import org.itsallcode.whiterabbit.logic.model.DayRecord;
import org.itsallcode.whiterabbit.logic.service.FormatterService;
Expand Down Expand Up @@ -43,15 +45,17 @@ public class ActivitiesTable
private final EditListener<DayRecord> editListener;
private final FormatterService formatterService;
private final ProjectService projectService;
private final AutocompleteService autocompleteService;

public ActivitiesTable(ReadOnlyProperty<DayRecord> selectedDay, SimpleObjectProperty<Activity> selectedActivity,
EditListener<DayRecord> editListener,
FormatterService formatterService, ProjectService projectService)
EditListener<DayRecord> editListener, FormatterService formatterService, ProjectService projectService,
AutocompleteService autocompleteService)
{
this.selectedActivity = selectedActivity;
this.editListener = editListener;
this.formatterService = formatterService;
this.projectService = projectService;
this.autocompleteService = autocompleteService;
selectedDay.addListener((observable, oldValue, newValue) -> updateTableValues(newValue));
}

Expand Down Expand Up @@ -132,7 +136,8 @@ public TableView<ActivityPropertyAdapter> initTable()
final TableColumn<ActivityPropertyAdapter, Boolean> remainderCol = column("remainder", "Remainder",
cellFactory, data -> data.getValue().remainder);
final TableColumn<ActivityPropertyAdapter, String> commentCol = column("comment", "Comment",
param -> new PersistOnFocusLossTextFieldTableCell<>(new DefaultStringConverter()),
param -> new PersistOnFocusLossTextFieldTableCell<>(new DefaultStringConverter(),
() -> new AutoCompleteTextField(autocompleteService.activityCommentAutocompleter())),
data -> data.getValue().comment);

return asList(projectCol, durationCol, remainderCol, commentCol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import org.eclipse.jdt.annotation.NonNull;
import org.itsallcode.whiterabbit.jfxui.JavaFxUtil;
import org.itsallcode.whiterabbit.jfxui.table.EditListener;
import org.itsallcode.whiterabbit.jfxui.table.PersistOnFocusLossTextFieldTableCell;
import org.itsallcode.whiterabbit.jfxui.table.converter.DurationStringConverter;
import org.itsallcode.whiterabbit.jfxui.ui.widget.AutoCompleteTextField;
import org.itsallcode.whiterabbit.jfxui.ui.widget.PersistOnFocusLossTextFieldTableCell;
import org.itsallcode.whiterabbit.logic.autocomplete.AutocompleteService;
import org.itsallcode.whiterabbit.logic.model.DayRecord;
import org.itsallcode.whiterabbit.logic.model.MonthIndex;
import org.itsallcode.whiterabbit.logic.model.json.DayType;
Expand Down Expand Up @@ -51,14 +53,17 @@ public class DayRecordTable
private final SimpleObjectProperty<DayRecord> selectedDay;
private TableView<DayRecordPropertyAdapter> table;

private final AutocompleteService autocompleteService;

public DayRecordTable(Locale locale, SimpleObjectProperty<DayRecord> selectedDay,
ObjectProperty<MonthIndex> currentMonth, EditListener<DayRecord> editListener,
FormatterService formatterService)
FormatterService formatterService, AutocompleteService autocompleteService)
{
this.editListener = editListener;
this.formatterService = formatterService;
this.locale = locale;
this.selectedDay = selectedDay;
this.autocompleteService = autocompleteService;
fillTableWith31EmptyRows();
currentMonth.addListener((observable, oldValue, newValue) -> updateTableValues(newValue));
}
Expand Down Expand Up @@ -124,7 +129,8 @@ public void selectRow(LocalDate date)
param -> new PersistOnFocusLossTextFieldTableCell<>(durationConverter),
data -> data.getValue().totalOvertime);
final TableColumn<DayRecordPropertyAdapter, String> commentCol = column("comment", "Comment",
param -> new PersistOnFocusLossTextFieldTableCell<>(new DefaultStringConverter()),
param -> new PersistOnFocusLossTextFieldTableCell<>(new DefaultStringConverter(),
() -> new AutoCompleteTextField(autocompleteService.dayCommentAutocompleter())),
data -> data.getValue().comment);

return asList(dateCol, dayTypeCol, beginCol, endCol, breakCol, interruptionCol, workingTimeCol, overTimeCol,
Expand Down
61 changes: 24 additions & 37 deletions jfxui/src/main/java/org/itsallcode/whiterabbit/jfxui/ui/AppUi.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package org.itsallcode.whiterabbit.jfxui.ui;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Locale;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.itsallcode.whiterabbit.jfxui.AppState;
Expand All @@ -24,31 +28,14 @@
import org.itsallcode.whiterabbit.logic.service.AppService;
import org.itsallcode.whiterabbit.logic.service.FormatterService;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Separator;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Locale;

public class AppUi
{
Expand Down Expand Up @@ -113,15 +100,15 @@ public AppUi build()
appService.store(record);
if (record.getDate().equals(state.getSelectedDay().map(DayRecord::getDate).orElse(null)))
{
LOG.debug("Current day {} updated: refresh activieties", record.getDate());
LOG.debug("Current day {} updated: refresh activities", record.getDate());
activitiesTable.refresh();
}
}, appService.formatter());
}, appService.formatter(), appService.autocomplete());

activitiesTable = new ActivitiesTable(state.selectedDay, state.selectedActivity, record -> {
appService.store(record);
activitiesTable.refresh();
}, appService.formatter(), appService.projects());
}, appService.formatter(), appService.projects(), appService.autocomplete());
final BorderPane rootPane = new BorderPane(createMainPane());
rootPane.setTop(createTopContainer());
final Scene scene = new Scene(rootPane, 780, 800);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.itsallcode.whiterabbit.jfxui.ui.widget;

import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.itsallcode.whiterabbit.logic.autocomplete.AutocompleteEntrySupplier;

import javafx.geometry.Side;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

/**
* Based on https://gist.github.com/floralvikings/10290131
*/
@SuppressWarnings("java:S110") // Deep inheritance tree required by API
public class AutoCompleteTextField extends TextField
{
private static final Logger LOG = LogManager.getLogger(AutoCompleteTextField.class);

private static final int MAX_ENTRY_COUNT = 10;

private final AutocompleteEntrySupplier autocompleteEntriesSupplier;
private final ContextMenu entriesPopup;

public AutoCompleteTextField(AutocompleteEntrySupplier autocompleteEntriesSupplier)
{
this.autocompleteEntriesSupplier = autocompleteEntriesSupplier;
entriesPopup = new ContextMenu();
textProperty().addListener((observableValue, oldValue, newValue) -> textUpdated(getText()));

focusedProperty().addListener((observableValue, oldValue, newValue) -> entriesPopup.hide());
}

private void textUpdated(final String currentText)
{
if (currentText.isBlank())
{
entriesPopup.hide();
return;
}
final List<String> searchResult = autocompleteEntriesSupplier.getEntries(currentText);
if (searchResult.isEmpty())
{
entriesPopup.hide();
return;
}
populatePopup(searchResult);
if (!entriesPopup.isShowing())
{
showPopup();
}
}

private void showPopup()
{
if (getScene() == null)
{
LOG.warn("Scene not available for {}", this);
return;
}
entriesPopup.show(this, Side.BOTTOM, 0, 0);
}

private void populatePopup(List<String> searchResult)
{
final int count = Math.min(searchResult.size(), MAX_ENTRY_COUNT);
entriesPopup.getItems().clear();
for (int i = 0; i < count; i++)
{
final String result = searchResult.get(i);
entriesPopup.getItems().add(createMenuItem(result));
}
}

private CustomMenuItem createMenuItem(final String result)
{
final Label entryLabel = new Label(result);
final CustomMenuItem item = new CustomMenuItem(entryLabel, true);
item.setOnAction(actionEvent -> {
setText(result);
entriesPopup.hide();
});
return item;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.itsallcode.whiterabbit.jfxui.table;
package org.itsallcode.whiterabbit.jfxui.ui.widget;

import java.util.Objects;
import java.util.function.Supplier;

import javafx.beans.value.ChangeListener;
import javafx.scene.Node;
Expand All @@ -25,10 +26,18 @@
public class PersistOnFocusLossTextFieldTableCell<S, T> extends TableCell<S, T>
{
private final StringConverter<T> converter;
public final Supplier<TextField> textFieldSupplier;
private TextField textField;

public PersistOnFocusLossTextFieldTableCell(final StringConverter<T> converter)
{
this(converter, TextField::new);
}

public PersistOnFocusLossTextFieldTableCell(final StringConverter<T> converter,
Supplier<TextField> testFieldSupplier)
{
this.textFieldSupplier = testFieldSupplier;
this.converter = Objects.requireNonNull(converter);
}

Expand All @@ -47,17 +56,18 @@ public void startEdit()
{
if (this.textField == null)
{
this.textField = createTextField(this, this.converter);
this.textField = createTextField(this, this.converter, this.textFieldSupplier);
}

startEdit(this, this.converter, this.textField);
}
}

private static <T> TextField createTextField(final PersistOnFocusLossTextFieldTableCell<?, T> cell,
final StringConverter<T> converter)
final StringConverter<T> converter, Supplier<TextField> textFieldSupplier)
{
final TextField textField = new TextField(getItemText(cell, converter));
final TextField textField = textFieldSupplier.get();
textField.setText(getItemText(cell, converter));

textField.setOnAction(event -> {
cell.commitEdit(converter.fromString(textField.getText()));
Expand Down Expand Up @@ -128,7 +138,8 @@ private static <T> void cancelEdit(final Cell<T> cell, final StringConverter<T>
cell.setGraphic(graphic);
}

private static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter, final TextField textField)
private static <T> void updateItem(final Cell<T> cell, final StringConverter<T> converter,
final TextField textField)
{
if (cell.isEmpty())
{
Expand Down
Loading

0 comments on commit 883e676

Please sign in to comment.