Skip to content

Commit

Permalink
Improve drag and drop (#5306)
Browse files Browse the repository at this point in the history
* Add indicator for dropping files > open

* Improve drag and drop in groups and main table

* Update JabRef_en.properties
  • Loading branch information
tobiasdiez authored Sep 13, 2019
1 parent a27a430 commit 381f0f3
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 138 deletions.
3 changes: 2 additions & 1 deletion .idea/runConfigurations/JabRef_Main.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 16 additions & 3 deletions src/main/java/org/jabref/gui/Base.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
/* Highlights */
-jr-blue: #0abde3;
-jr-light-blue: #48dbfb;
-jr-purple: #f368e0;
-jr-purple: #7559C2;
-jr-light-purple: #ff9ff3;
-jr-green: #10ac84;
-jr-light-green: #1dd1a1;
Expand All @@ -53,8 +53,6 @@
-jr-menu-item-foreground: -fx-dark-text-color;
-jr-menu-forground-active: -fx-dark-text-color;

-jr-drag-target: -jr-accent;

-jr-head-fg: -fx-text-inner-color;

/* All icons/text on toolbars */
Expand Down Expand Up @@ -96,6 +94,10 @@

-jr-search-text: -fx-text-base-color;

/* For drag and drop actions */
-jr-drag-target: -jr-purple;
-jr-drag-target-hover: derive(-jr-purple, 80%);

/*
Here are redefinitions of the default properties of modena. They should in principle all be derived from the
above colors. Goal should be to make as few as possible direct color-changes to elements and only do this for
Expand Down Expand Up @@ -491,6 +493,17 @@
-fx-border-radius: 0;
}

.tab-pane > .tab-header-area > .headers-region > .tab.drop {
-fx-border-color: -jr-drag-target;
-fx-background-color: -jr-drag-target-hover;
-fx-border-width: 3 1 1 1;
}

.tab-pane > .tab-header-area > .headers-region > .tab.drop .tab-label {
-fx-fill: -jr-drag-target;
-fx-text-fill: -jr-drag-target;
}

.tab-pane > .tab-header-area > .tab-header-background {
-fx-background-color: -jr-background-alt;
}
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/jabref/gui/DragAndDropDataFormats.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import javafx.scene.input.DataFormat;

import org.jabref.logic.citationstyle.PreviewLayout;
import org.jabref.model.entry.BibEntry;

/**
* Contains all the different {@link DataFormat}s that may occur in JabRef.
Expand All @@ -15,7 +14,6 @@ public class DragAndDropDataFormats {
public static final DataFormat GROUP = new DataFormat("dnd/org.jabref.model.groups.GroupTreeNode");
public static final DataFormat LINKED_FILE = new DataFormat("dnd/org.jabref.model.entry.LinkedFile");
public static final DataFormat ENTRIES = new DataFormat("dnd/org.jabref.model.entry.BibEntries");
@SuppressWarnings("unchecked") public static final Class<List<BibEntry>> BIBENTRY_LIST_CLASS = (Class<List<BibEntry>>) (Class<?>) List.class;
public static final DataFormat PREVIEWLAYOUTS = new DataFormat("dnd/org.jabref.logic.citationstyle.PreviewLayouts");
@SuppressWarnings("unchecked") public static final Class<List<PreviewLayout>> PREVIEWLAYOUT_LIST_CLASS = (Class<List<PreviewLayout>>) (Class<?>) List.class;
}
26 changes: 26 additions & 0 deletions src/main/java/org/jabref/gui/DragAndDropHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.jabref.gui;

import java.io.File;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import javafx.scene.input.Dragboard;

import org.jabref.logic.util.io.FileUtil;

public class DragAndDropHelper {

public static boolean hasBibFiles(Dragboard dragboard) {
return !getBibFiles(dragboard).isEmpty();
}

public static List<Path> getBibFiles(Dragboard dragboard) {
if (!dragboard.hasFiles()) {
return Collections.emptyList();
} else {
return dragboard.getFiles().stream().map(File::toPath).filter(FileUtil::isBibFile).collect(Collectors.toList());
}
}
}
77 changes: 53 additions & 24 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.TimerTask;
import java.util.stream.Collectors;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
Expand All @@ -35,7 +34,7 @@
import javafx.scene.control.TextInputControl;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
import javafx.scene.input.DataFormat;
import javafx.scene.control.skin.TabPaneSkin;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
Expand Down Expand Up @@ -175,28 +174,7 @@ public void init() {

initKeyBindings();

tabbedPane.setOnDragOver(event -> {
if (event.getDragboard().hasFiles()) {
event.acceptTransferModes(TransferMode.COPY, TransferMode.MOVE, TransferMode.LINK);
}
});

tabbedPane.setOnDragDropped(event -> {
boolean success = false;

if (event.getDragboard().hasContent(DataFormat.FILES)) {
List<Path> files = event.getDragboard().getFiles().stream().map(File::toPath).filter(FileUtil::isBibFile).collect(Collectors.toList());
success = true;

for (Path file : files) {
ParserResult pr = OpenDatabase.loadDatabase(file.toString(), Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
addParserResult(pr, true);
}
}

event.setDropCompleted(success);
event.consume();
});
initDragAndDrop();

//setBounds(GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds());
//WindowLocation pw = new WindowLocation(this, JabRefPreferences.POS_X, JabRefPreferences.POS_Y, JabRefPreferences.SIZE_X,
Expand Down Expand Up @@ -246,6 +224,57 @@ public void init() {
initShowTrackingNotification();
}

public void initDragAndDrop() {
Tab dndIndicator = new Tab(Localization.lang("Open files..."), null);
dndIndicator.getStyleClass().add("drop");

EasyBind.subscribe(tabbedPane.skinProperty(), skin -> {
if (!(skin instanceof TabPaneSkin)) {
return;
}

// We need to get the tab header, the following is a ugly workaround
Node tabHeaderArea = ((TabPaneSkin) this.tabbedPane.getSkin())
.getChildren()
.stream()
.filter(node -> node.getStyleClass().contains("tab-header-area"))
.findFirst()
.orElseThrow();

tabHeaderArea.setOnDragOver(event -> {
if (DragAndDropHelper.hasBibFiles(event.getDragboard())) {
event.acceptTransferModes(TransferMode.ANY);
if (!tabbedPane.getTabs().contains(dndIndicator)) {
tabbedPane.getTabs().add(dndIndicator);
}
event.consume();
} else {
tabbedPane.getTabs().remove(dndIndicator);
}
});

tabHeaderArea.setOnDragExited(event -> tabbedPane.getTabs().remove(dndIndicator));

tabHeaderArea.setOnDragDropped(event -> {
tabbedPane.getTabs().remove(dndIndicator);

boolean success = false;

List<Path> bibFiles = DragAndDropHelper.getBibFiles(event.getDragboard());
if (!bibFiles.isEmpty()) {
for (Path file : bibFiles) {
ParserResult pr = OpenDatabase.loadDatabase(file.toString(), Globals.prefs.getImportFormatPreferences(), Globals.getFileUpdateMonitor());
addParserResult(pr, true);
}
success = true;
}

event.setDropCompleted(success);
event.consume();
});
});
}

private void initKeyBindings() {
addEventFilter(KeyEvent.KEY_PRESSED, event -> {
Optional<KeyBinding> keyBinding = Globals.getKeyPrefs().mapToKeyBinding(event);
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/org/jabref/gui/externalfiles/ImportHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public void importAsNewEntries(List<Path> files) {
entriesToAdd = Collections.singletonList(createEmptyEntryWithLink(file));
}
}
} else if (FileUtil.isBibFile(file)) {
entriesToAdd = contentImporter.importFromBibFile(file, fileUpdateMonitor);
} else {
entriesToAdd = Collections.singletonList(createEmptyEntryWithLink(file));
}
Expand All @@ -105,11 +107,6 @@ private BibEntry createEmptyEntryWithLink(Path file) {
return entry;
}

public void importEntriesFromBibFiles(Path bibFile) {
List<BibEntry> entriesToImport = contentImporter.importFromBibFile(bibFile, fileUpdateMonitor);
importEntries(entriesToImport);
}

public void importEntries(List<BibEntry> entries) {
//TODO: Add undo/redo
//ce.addEdit(new UndoableInsertEntry(panel.getDatabase(), entry));
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/jabref/gui/groups/GroupNodeViewModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.CustomLocalDragboard;
import org.jabref.gui.util.DroppingMouseLocation;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.groups.DefaultGroupsFactory;
import org.jabref.logic.layout.format.LatexToUnicodeFormatter;
Expand Down Expand Up @@ -262,8 +263,7 @@ public Optional<GroupNodeViewModel> getChildByPath(String pathToSource) {
public boolean acceptableDrop(Dragboard dragboard) {
// TODO: we should also check isNodeDescendant
boolean canDropOtherGroup = dragboard.hasContent(DragAndDropDataFormats.GROUP);
boolean canDropEntries = localDragBoard.hasType(DragAndDropDataFormats.BIBENTRY_LIST_CLASS)
&& (groupNode.getGroup() instanceof GroupEntryChanger);
boolean canDropEntries = localDragBoard.hasBibEntries() && (groupNode.getGroup() instanceof GroupEntryChanger);
return canDropOtherGroup || canDropEntries;
}

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/jabref/gui/groups/GroupTree.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@

.tree-table-row-cell:dragOver-center {
-fx-border-color: -jr-drag-target;
-fx-border-width: 2 2 2 2;
-fx-padding: -2 -2 -2 -2;
-fx-border-width: 1 1 1 1;
-fx-padding: -1 -1 -1 -1;
-fx-background-color: -jr-drag-target-hover;
}

.tree-table-row-cell:dragOver-top {
Expand Down
48 changes: 9 additions & 39 deletions src/main/java/org/jabref/gui/groups/GroupTreeView.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
Expand All @@ -40,6 +39,7 @@
import org.jabref.gui.GUIGlobals;
import org.jabref.gui.StateManager;
import org.jabref.gui.util.BindingsHelper;
import org.jabref.gui.util.ControlHelper;
import org.jabref.gui.util.CustomLocalDragboard;
import org.jabref.gui.util.RecursiveTreeItem;
import org.jabref.gui.util.TaskExecutor;
Expand Down Expand Up @@ -74,12 +74,6 @@ public class GroupTreeView {

private DragExpansionHandler dragExpansionHandler;

private static void removePseudoClasses(TreeTableRow<GroupNodeViewModel> row, PseudoClass... pseudoClasses) {
for (PseudoClass pseudoClass : pseudoClasses) {
row.pseudoClassStateChanged(pseudoClass, false);
}
}

@FXML
public void initialize() {
this.localDragboard = GUIGlobals.localDragboard;
Expand Down Expand Up @@ -169,10 +163,6 @@ public void initialize() {
PseudoClass rootPseudoClass = PseudoClass.getPseudoClass("root");
PseudoClass subElementPseudoClass = PseudoClass.getPseudoClass("sub");

// Pseudo-classes for drag and drop
PseudoClass dragOverBottom = PseudoClass.getPseudoClass("dragOver-bottom");
PseudoClass dragOverCenter = PseudoClass.getPseudoClass("dragOver-center");
PseudoClass dragOverTop = PseudoClass.getPseudoClass("dragOver-top");
groupTree.setRowFactory(treeTable -> {
TreeTableRow<GroupNodeViewModel> row = new TreeTableRow<>();
row.treeItemProperty().addListener((ov, oldTreeItem, newTreeItem) -> {
Expand Down Expand Up @@ -226,23 +216,16 @@ public void initialize() {
//expand node and all children on drag over
dragExpansionHandler.expandGroup(row.getTreeItem());

removePseudoClasses(row, dragOverBottom, dragOverCenter, dragOverTop);
switch (getDroppingMouseLocation(row, event)) {
case BOTTOM:
row.pseudoClassStateChanged(dragOverBottom, true);
break;
case CENTER:
row.pseudoClassStateChanged(dragOverCenter, true);
break;
case TOP:
row.pseudoClassStateChanged(dragOverTop, true);
break;
if (localDragboard.hasBibEntries()) {
ControlHelper.setDroppingPseudoClasses(row);
} else {
ControlHelper.setDroppingPseudoClasses(row, event);
}
}
event.consume();
});
row.setOnDragExited(event -> {
removePseudoClasses(row, dragOverBottom, dragOverCenter, dragOverTop);
ControlHelper.removeDroppingPseudoClasses(row);
});

row.setOnDragDropped(event -> {
Expand All @@ -255,13 +238,13 @@ public void initialize() {
Optional<GroupNodeViewModel> source = viewModel.rootGroupProperty().get()
.getChildByPath(pathToSource);
if (source.isPresent()) {
source.get().draggedOn(row.getItem(), getDroppingMouseLocation(row, event));
source.get().draggedOn(row.getItem(), ControlHelper.getDroppingMouseLocation(row, event));
success = true;
}
}
}

if (localDragboard.hasType(DragAndDropDataFormats.BIBENTRY_LIST_CLASS)) {
if (localDragboard.hasBibEntries()) {
List<BibEntry> entries = localDragboard.getBibEntries();
row.getItem().addEntriesToGroup(entries);
success = true;
Expand All @@ -281,7 +264,7 @@ private void updateSelection(List<TreeItem<GroupNodeViewModel>> newSelectedGroup
if ((newSelectedGroups == null) || newSelectedGroups.isEmpty()) {
viewModel.selectedGroupsProperty().clear();
} else {
List<GroupNodeViewModel> list = newSelectedGroups.stream().filter(model -> model != null && !(model.getValue().getGroupNode().getGroup() instanceof AllEntriesGroup)).map(TreeItem<GroupNodeViewModel>::getValue).collect(Collectors.toList());
List<GroupNodeViewModel> list = newSelectedGroups.stream().filter(model -> model != null && !(model.getValue().getGroupNode().getGroup() instanceof AllEntriesGroup)).map(TreeItem::getValue).collect(Collectors.toList());
viewModel.selectedGroupsProperty().setAll(list);
}
}
Expand Down Expand Up @@ -378,19 +361,6 @@ private void setupClearButtonField(CustomTextField customTextField) {
}
}

/**
* Determines where the mouse is in the given row.
*/
private DroppingMouseLocation getDroppingMouseLocation(TreeTableRow<GroupNodeViewModel> row, DragEvent event) {
if ((row.getHeight() * 0.25) > event.getY()) {
return DroppingMouseLocation.TOP;
} else if ((row.getHeight() * 0.75) < event.getY()) {
return DroppingMouseLocation.BOTTOM;
} else {
return DroppingMouseLocation.CENTER;
}
}

private class DragExpansionHandler {
private static final long DRAG_TIME_BEFORE_EXPANDING_MS = 1000;
private TreeItem<GroupNodeViewModel> draggedItem;
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/org/jabref/gui/maintable/MainTable.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@
-fx-fill: -jr-gray-2;
}

.table-row-cell:dragOver-bottom {
-fx-border-color: -jr-drag-target;
-fx-border-width: 0 0 2 0;
-fx-padding: 0 0 -2 0;
}

.table-row-cell:dragOver-center {
-fx-border-color: -jr-drag-target;
-fx-border-width: 1 1 1 1;
-fx-padding: -1 -1 -1 -1;
-fx-background-color: -jr-drag-target-hover;
}

.table-row-cell:dragOver-top {
-fx-border-color: -jr-drag-target;
-fx-border-width: 2 0 0 0;
-fx-padding: -2 0 0 0;
}

.rating > .container {
-fx-spacing: 2;
}
Expand Down
Loading

0 comments on commit 381f0f3

Please sign in to comment.