Cleaned up view models, tried to get pasting to work.
This commit is contained in:
parent
f8b8740ea8
commit
f7b54dc182
|
@ -16,9 +16,9 @@
|
|||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="cluster_node_icon.svg"
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\ui\images\cluster_node_icon.png"
|
||||
inkscape:export-xdpi="192"
|
||||
inkscape:export-ydpi="192">
|
||||
inkscape:export-filename="A:\Programming\GitHub-andrewlalis\CrystalKeep\src\main\resources\nl\andrewlalis\crystalkeep\ui\images\cluster_node_icon.png"
|
||||
inkscape:export-xdpi="1536"
|
||||
inkscape:export-ydpi="1536">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
|
@ -28,9 +28,9 @@
|
|||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="31.678384"
|
||||
inkscape:cx="9.6523068"
|
||||
inkscape:cy="7.5940447"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="5.0060445"
|
||||
inkscape:cy="7.4646004"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
|
@ -39,7 +39,9 @@
|
|||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
|
@ -48,7 +50,7 @@
|
|||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
|
@ -58,21 +60,19 @@
|
|||
id="layer1"
|
||||
transform="translate(0,-292.76667)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#f072ff;fill-opacity:1;stroke:none;stroke-width:0.07803907;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path817"
|
||||
sodipodi:sides="9"
|
||||
sodipodi:cx="2.1166666"
|
||||
sodipodi:cy="294.93815"
|
||||
sodipodi:r1="1.8178079"
|
||||
sodipodi:r2="1.2179312"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="0.87266463"
|
||||
inkscape:flatsided="false"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 3.6909344,295.84704 -0.7913968,0.0241 -0.1611441,0.77519 -0.6217269,-0.49025 -0.621727,0.49025 -0.1611441,-0.77519 -0.79139678,-0.0241 0.37483979,-0.69741 -0.59076328,-0.52715 0.73543197,-0.2933 -0.11370504,-0.78356 0.75190744,0.24804 0.416557,-0.67333 0.4165569,0.67333 0.7519075,-0.24804 -0.1137051,0.78356 0.735432,0.2933 -0.5907633,0.52715 z"
|
||||
inkscape:transform-center-x="0.06028767"
|
||||
inkscape:transform-center-y="0.030212626" />
|
||||
style="fill:#cf00f8;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 0.99910255,294.74397 1.11756415,-1.93568 1.1175641,1.93568 z"
|
||||
id="path824"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path826"
|
||||
d="m 3.2342308,295.01546 -1.1175641,1.93568 -1.11756415,-1.93568 z"
|
||||
style="fill:#cf00f8;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:#00f8c5;fill-opacity:1;stroke:none;stroke-width:0.14845224px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 2.1114031,294.00949 0.5073106,0.87868 -0.501731,0.86902 -0.5023631,-0.87011 z"
|
||||
id="path828"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.7 KiB |
5
pom.xml
5
pom.xml
|
@ -80,6 +80,11 @@
|
|||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.1stleg</groupId>
|
||||
<artifactId>jnativehook</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module crystalkeep {
|
||||
requires javafx.fxml;
|
||||
requires javafx.controls;
|
||||
requires jnativehook;
|
||||
|
||||
opens nl.andrewlalis.crystalkeep.control;
|
||||
opens nl.andrewlalis.crystalkeep;
|
||||
|
|
|
@ -5,24 +5,10 @@ import javafx.beans.value.ObservableValue;
|
|||
import javafx.scene.control.TreeItem;
|
||||
import javafx.scene.layout.VBox;
|
||||
import nl.andrewlalis.crystalkeep.model.CrystalItem;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
import nl.andrewlalis.crystalkeep.view.ShardTreeItem;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.LoginCredentialsViewModel;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.ShardViewModel;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.TextShardViewModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.ViewModels;
|
||||
|
||||
public class ClusterTreeViewItemSelectionListener implements ChangeListener<TreeItem<CrystalItem>> {
|
||||
private static final Map<Class<? extends Shard>, Class<? extends ShardViewModel<? extends Shard>>> shardPanesMap = new HashMap<>();
|
||||
static {
|
||||
shardPanesMap.put(TextShard.class, TextShardViewModel.class);
|
||||
shardPanesMap.put(LoginCredentialsShard.class, LoginCredentialsViewModel.class);
|
||||
}
|
||||
|
||||
private final VBox shardDetailContainer;
|
||||
|
||||
public ClusterTreeViewItemSelectionListener(VBox shardDetailContainer) {
|
||||
|
@ -34,13 +20,7 @@ public class ClusterTreeViewItemSelectionListener implements ChangeListener<Tree
|
|||
shardDetailContainer.getChildren().clear();
|
||||
if (newValue instanceof ShardTreeItem) {
|
||||
var node = (ShardTreeItem) newValue;
|
||||
var paneClass = shardPanesMap.get(node.getShard().getClass());
|
||||
try {
|
||||
var vm = paneClass.getDeclaredConstructor(node.getShard().getClass()).newInstance(node.getShard());
|
||||
shardDetailContainer.getChildren().add(vm.getContentPane());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ViewModels.get(node.getShard()).ifPresent(vm -> shardDetailContainer.getChildren().add(vm.getContentPane()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,9 +93,4 @@ public class Cluster implements Comparable<Cluster>, CrystalItem {
|
|||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/cluster_node_icon.png";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,4 @@ package nl.andrewlalis.crystalkeep.model;
|
|||
*/
|
||||
public interface CrystalItem {
|
||||
String getName();
|
||||
String getIconPath();
|
||||
}
|
||||
|
|
|
@ -64,9 +64,4 @@ public abstract class Shard implements Comparable<Shard>, CrystalItem {
|
|||
public String toString() {
|
||||
return "Shard: name=\"" + this.name + "\", type=" + this.type + ", createdAt=" + this.createdAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/shard_node_icon.png";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,11 +44,6 @@ public class LoginCredentialsShard extends Shard {
|
|||
return super.toString() + ", username=\"" + this.username + "\", password=\"" + this.password + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/login_credentials_shard_node_icon.png";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<LoginCredentialsShard> {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,11 +34,6 @@ public class TextShard extends Shard {
|
|||
return super.toString() + ", text=\"" + this.text + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/text_shard_node_icon.png";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<TextShard> {
|
||||
@Override
|
||||
public byte[] serialize(TextShard shard) throws IOException {
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package nl.andrewlalis.crystalkeep.util;
|
||||
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* This cache stores pre-loaded images that are used in the application, so that
|
||||
* they only need to be loaded once.
|
||||
*/
|
||||
public class ImageCache {
|
||||
private static final Map<ImageSpec, Image> images = new ConcurrentHashMap<>();
|
||||
|
||||
public static Optional<Image> get(String path, double width, double height) {
|
||||
ImageSpec spec = new ImageSpec(path, width, height);
|
||||
Image img = images.get(spec);
|
||||
if (img == null) {
|
||||
InputStream is = ImageCache.class.getResourceAsStream(path);
|
||||
if (is == null) return Optional.empty();
|
||||
img = new Image(is, spec.width, spec.height, true, true);
|
||||
images.put(spec, img);
|
||||
}
|
||||
return Optional.of(img);
|
||||
}
|
||||
|
||||
private static class ImageSpec {
|
||||
private final String path;
|
||||
private final double width;
|
||||
private final double height;
|
||||
|
||||
public ImageSpec(String path, double width, double height) {
|
||||
this.path = path;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ImageSpec imageSpec = (ImageSpec) o;
|
||||
return Double.compare(imageSpec.width, width) == 0 && Double.compare(imageSpec.height, height) == 0 && path.equals(imageSpec.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(path, width, height);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package nl.andrewlalis.crystalkeep.view;
|
|||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.TreeCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import nl.andrewlalis.crystalkeep.control.AddClusterHandler;
|
||||
import nl.andrewlalis.crystalkeep.control.AddShardHandler;
|
||||
|
@ -11,10 +10,13 @@ import nl.andrewlalis.crystalkeep.control.DeleteItemHandler;
|
|||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.CrystalItem;
|
||||
import nl.andrewlalis.crystalkeep.model.Model;
|
||||
|
||||
import java.io.InputStream;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.util.ImageCache;
|
||||
import nl.andrewlalis.crystalkeep.view.shard_details.ViewModels;
|
||||
|
||||
public class CrystalItemTreeCell extends TreeCell<CrystalItem> {
|
||||
private static final String CLUSTER_ICON = "/nl/andrewlalis/crystalkeep/ui/images/cluster_node_icon.png";
|
||||
|
||||
private final Model model;
|
||||
|
||||
public CrystalItemTreeCell(Model model) {
|
||||
|
@ -36,19 +38,27 @@ public class CrystalItemTreeCell extends TreeCell<CrystalItem> {
|
|||
addClusterItem.setOnAction(new AddClusterHandler(cluster, model));
|
||||
menu.getItems().addAll(addShardItem, addClusterItem);
|
||||
}
|
||||
|
||||
if (this.getTreeItem().getParent() != null && this.getTreeItem().getParent() instanceof ClusterTreeItem) {
|
||||
var deleteItem = new MenuItem("Delete");
|
||||
deleteItem.setOnAction(new DeleteItemHandler(this.getTreeItem(), this.model));
|
||||
menu.getItems().add(deleteItem);
|
||||
}
|
||||
this.setText(item.getName());
|
||||
InputStream is = getClass().getResourceAsStream(item.getIconPath());
|
||||
if (is != null) {
|
||||
ImageView icon = new ImageView(new Image(is));
|
||||
|
||||
String iconPath = CLUSTER_ICON;
|
||||
if (item instanceof Shard) {
|
||||
var vm = ViewModels.get((Shard) item);
|
||||
if (vm.isPresent()) {
|
||||
iconPath = vm.get().getIconPath();
|
||||
}
|
||||
}
|
||||
ImageCache.get(iconPath, 16, 16).ifPresent(img -> {
|
||||
ImageView icon = new ImageView(img);
|
||||
icon.setFitHeight(16);
|
||||
icon.setPreserveRatio(true);
|
||||
this.setGraphic(icon);
|
||||
}
|
||||
});
|
||||
this.setContextMenu(menu);
|
||||
} else {
|
||||
this.setText(null);
|
||||
|
|
|
@ -9,6 +9,9 @@ import javafx.scene.layout.GridPane;
|
|||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Pane;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import org.jnativehook.GlobalScreen;
|
||||
import org.jnativehook.keyboard.NativeKeyAdapter;
|
||||
import org.jnativehook.keyboard.NativeKeyEvent;
|
||||
|
||||
public class LoginCredentialsViewModel extends ShardViewModel<LoginCredentialsShard> {
|
||||
public LoginCredentialsViewModel(LoginCredentialsShard shard) {
|
||||
|
@ -61,12 +64,21 @@ public class LoginCredentialsViewModel extends ShardViewModel<LoginCredentialsSh
|
|||
content.putString(shard.getPassword());
|
||||
Clipboard.getSystemClipboard().setContent(content);
|
||||
});
|
||||
var typePasswordButton = new Button("Auto Type");
|
||||
typePasswordButton.setOnAction(event -> {
|
||||
System.out.println("Not yet implemented.");
|
||||
|
||||
var copyBothButton = new Button("Copy Username and Password");
|
||||
copyBothButton.setOnAction(event -> {
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(shard.getUsername());
|
||||
final Clipboard c = Clipboard.getSystemClipboard();
|
||||
c.setContent(content);
|
||||
var t = new Thread(() -> {
|
||||
while (c.getString().equals(shard.getUsername())) {
|
||||
System.out.println("User hasn't pasted yet");
|
||||
}
|
||||
});
|
||||
typePasswordButton.setDisable(true);
|
||||
passwordActionsPane.getChildren().addAll(showPasswordCheckbox, copyPasswordButton, typePasswordButton);
|
||||
});
|
||||
|
||||
passwordActionsPane.getChildren().addAll(showPasswordCheckbox, copyPasswordButton);
|
||||
|
||||
gp.add(passwordActionsPane, 1, 2);
|
||||
return gp;
|
||||
|
|
|
@ -12,6 +12,12 @@ import nl.andrewlalis.crystalkeep.model.Shard;
|
|||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* The view model for a type of shard. A shard type's view model defines how the
|
||||
* shard is displayed in the application, including the actual UI contents, and
|
||||
* icon.
|
||||
* @param <T> The type of shard.
|
||||
*/
|
||||
public abstract class ShardViewModel<T extends Shard> {
|
||||
protected final T shard;
|
||||
|
||||
|
@ -42,4 +48,8 @@ public abstract class ShardViewModel<T extends Shard> {
|
|||
}
|
||||
|
||||
protected abstract Node getContent(T shard);
|
||||
|
||||
public String getIconPath() {
|
||||
return "/nl/andrewlalis/crystalkeep/ui/images/shard_node_icon.png";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package nl.andrewlalis.crystalkeep.view.shard_details;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Utility class that provides the ability to look up and obtain a new view
|
||||
* model for a particular type of shard.
|
||||
*/
|
||||
public class ViewModels {
|
||||
private static final Map<Class<?>, Class<?>> shardViewModels = new HashMap<>();
|
||||
static {
|
||||
shardViewModels.put(TextShard.class, TextShardViewModel.class);
|
||||
shardViewModels.put(LoginCredentialsShard.class, LoginCredentialsViewModel.class);
|
||||
}
|
||||
|
||||
public static Optional<ShardViewModel<?>> get(Shard shard) {
|
||||
try {
|
||||
Class<?> viewModelClass = shardViewModels.get(shard.getClass());
|
||||
if (viewModelClass != null) {
|
||||
var viewModel = (ShardViewModel<?>) viewModelClass.getDeclaredConstructor(shard.getClass()).newInstance(shard);
|
||||
return Optional.of(viewModel);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 7.2 KiB |
Loading…
Reference in New Issue