From 4f91adb822609465febb163866429cb4e08f2eb4 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sun, 28 Dec 2014 16:46:14 +0100 Subject: [PATCH] - allow reordering of directories via drag'n'drop --- .../ui/controls/DirectoryListCell.java | 3 +- .../ui/controls/DraggableListCell.java | 132 ++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java b/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java index a55c5d1a4..8b2f760af 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.controls; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.control.ContentDisplay; -import javafx.scene.control.ListCell; import javafx.scene.control.Tooltip; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; @@ -11,7 +10,7 @@ import javafx.scene.shape.Circle; import org.cryptomator.ui.model.Directory; -public class DirectoryListCell extends ListCell implements ChangeListener { +public class DirectoryListCell extends DraggableListCell implements ChangeListener { // fill: #FD4943, stroke: #E1443F private static final Color RED_FILL = Color.rgb(253, 73, 67); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java b/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java new file mode 100644 index 000000000..19468dc96 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/controls/DraggableListCell.java @@ -0,0 +1,132 @@ +package org.cryptomator.ui.controls; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javafx.geometry.Insets; +import javafx.scene.SnapshotParameters; +import javafx.scene.control.ListCell; +import javafx.scene.image.Image; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.DragEvent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.Border; +import javafx.scene.layout.BorderImage; +import javafx.scene.layout.BorderStroke; +import javafx.scene.layout.BorderStrokeStyle; +import javafx.scene.layout.BorderWidths; +import javafx.scene.layout.CornerRadii; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; + +class DraggableListCell extends ListCell { + + private static final double DROP_LINE_WIDTH = 4.0; + private static final Paint DROP_LINE_COLOR = Color.gray(0.0, 0.6); + + private final List defaultBorderStrokes; + private final List defaultBorderImages; + + public DraggableListCell() { + setOnDragDetected(this::onDragDetected); + setOnDragOver(this::onDragOver); + setOnDragEntered(this::onDragEntered); + setOnDragExited(this::onDragExited); + setOnDragDropped(this::onDragDropped); + setOnDragDone(DragEvent::consume); + this.defaultBorderStrokes = this.getBorder() == null ? Collections.emptyList() : this.getBorder().getStrokes(); + this.defaultBorderImages = this.getBorder() == null ? Collections.emptyList() : this.getBorder().getImages(); + } + + private Border createDropPositionBorder(double verticalCursorPosition) { + boolean isUpperHalf = verticalCursorPosition < this.getHeight() / 2.0; + final double topBorder = isUpperHalf ? DROP_LINE_WIDTH : 0.0; + final double bottomBorder = !isUpperHalf ? DROP_LINE_WIDTH : 0.0; + final BorderWidths borderWidths = new BorderWidths(topBorder, 0.0, bottomBorder, 0.0); + final BorderStroke dragStroke = new BorderStroke(DROP_LINE_COLOR, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, borderWidths, Insets.EMPTY); + final List strokes = new ArrayList(defaultBorderStrokes); + strokes.add(0, dragStroke); + return new Border(strokes, defaultBorderImages); + } + + private void onDragDetected(MouseEvent event) { + if (getItem() == null) { + return; + } + + final ClipboardContent content = new ClipboardContent(); + content.putString(Integer.toString(getIndex())); + final Image snapshot = this.snapshot(new SnapshotParameters(), null); + final Dragboard dragboard = startDragAndDrop(TransferMode.MOVE); + dragboard.setDragView(snapshot); + dragboard.setContent(content); + + event.consume(); + } + + private void onDragOver(DragEvent event) { + if (getItem() == null) { + return; + } + + if (event.getGestureSource() instanceof DraggableListCell && event.getGestureSource() != this && event.getDragboard().hasString()) { + event.acceptTransferModes(TransferMode.MOVE); + setBorder(createDropPositionBorder(event.getY())); + } + + event.consume(); + } + + private void onDragEntered(DragEvent event) { + if (getItem() == null) { + return; + } + + if (event.getGestureSource() instanceof DraggableListCell && event.getGestureSource() != this && event.getDragboard().hasString()) { + setBorder(createDropPositionBorder(event.getY())); + } + } + + private void onDragExited(DragEvent event) { + if (getItem() == null) { + return; + } + + if (event.getGestureSource() instanceof DraggableListCell && event.getGestureSource() != this && event.getDragboard().hasString()) { + setBorder(new Border(defaultBorderStrokes, defaultBorderImages)); + } + } + + private void onDragDropped(DragEvent event) { + if (getItem() == null) { + return; + } + + if (event.getGestureSource() instanceof DraggableListCell && event.getDragboard().hasString()) { + final List list = getListView().getItems(); + try { + // where to insert what? + final int draggedIdx = Integer.parseInt(event.getDragboard().getString()); + final T currentItem = this.getItem(); + final T draggedItem = list.remove(draggedIdx); + final int currentItemIdx = list.indexOf(currentItem); + + // insert before or after currentItem? + boolean insertBefore = event.getY() < this.getHeight() / 2.0; + final int insertPosition = insertBefore ? currentItemIdx : currentItemIdx + 1; + + // insert! + getListView().getItems().add(insertPosition, draggedItem); + getListView().getSelectionModel().select(insertPosition); + event.setDropCompleted(true); + } catch (NumberFormatException e) { + event.setDropCompleted(false); + } + } + + event.consume(); + } +}