/* * Copyright 2016 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.rendering.nui.widgets; import com.google.common.collect.Lists; import org.terasology.input.Keyboard; import org.terasology.input.MouseInput; import org.terasology.rendering.nui.NUIManager; import org.terasology.rendering.nui.contextMenu.ContextMenuUtils; import org.terasology.rendering.nui.contextMenu.MenuTree; import org.terasology.rendering.nui.widgets.treeView.JsonTree; import org.terasology.rendering.nui.widgets.treeView.JsonTreeValue; import org.terasology.rendering.nui.widgets.treeView.Tree; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; /** * A Tree View widget designed to edit {@link JsonTree}s - * tree representations of a JSON object. */ public class JsonEditorTreeView extends UITreeView<JsonTreeValue> { /** * The list of this widget's model states. */ private List<JsonTree> history = Lists.newArrayList(); /** * The current position in the list of this widget's states. */ private int historyPosition; /** * The function used to instantiate a {@link MenuTree} from a given node. */ private Function<JsonTree, MenuTree> contextMenuTreeProducer; public JsonEditorTreeView() { } /** * @return The root node of this widget's model. */ public JsonTree getRoot() { return (JsonTree) getModel().getNode(0).getRoot().copy(); } /** * Adds the current root node of this widget's model to the state history. */ public void addToHistory() { if (historyPosition < history.size() - 1) { history = history.subList(0, historyPosition + 1); } history.add(getRoot()); historyPosition++; } /** * Clears the widget's state history. */ public void clearHistory() { history.clear(); historyPosition = 0; history.add(getRoot()); } /** * Sets the widget's state to the previous item in the history. * * @return true if the widget's state was changed, false otherwise. */ public boolean undo() { if (historyPosition > 0) { historyPosition--; JsonTree node = (JsonTree) history.get(historyPosition).copy(); setTreeViewModel(node, false); return true; } return false; } /** * Sets the widget's state to the next item in the history. * * @return true if the widget's state was changed, false otherwise. */ public boolean redo() { if (historyPosition < history.size() - 1) { historyPosition++; JsonTree node = (JsonTree) history.get(historyPosition).copy(); setTreeViewModel(node, false); return true; } return false; } /** * Sets the widget's state to a copy of a specified {@link JsonTree}. * * @param node The node the widget's state is to be set to. * @param expand Whether the node should be expanded. */ public void setTreeViewModel(JsonTree node, boolean expand) { if (expand) { expandNode(node); } setModel(node.copy()); } /** * Expands a {@link JsonTree} meeting specific conditions; repeats recursively for its' children. * * @param node The node to be expanded. */ private void expandNode(JsonTree node) { // Do not expand OBJECT children of ARRAY parents. Generally concerns widget lists. if (!(node.getValue().getType() == JsonTreeValue.Type.OBJECT && !node.isRoot() && node.getParent().getValue().getType() == JsonTreeValue.Type.ARRAY)) { node.setExpanded(true); } for (Tree child : node.getChildren()) { expandNode((JsonTree) child); } } /** * Copies the specified node to the editor's clipboard, * then deselects it. * * @param node The node to copy. */ public void copyNode(JsonTree node) { copy(node); setSelectedIndex(null); } /** * Pastes the currently copied node as a child of the specified node, * then deselects it. * * @param node The node to paste the copied node to. */ public void pasteNode(JsonTree node) { paste(node); setSelectedIndex(null); } public void deleteNode(JsonTree node) { delete(node); setSelectedIndex(null); } public void setContextMenuTreeProducer(Function<JsonTree, MenuTree> contextMenuTreeProducer) { this.contextMenuTreeProducer = contextMenuTreeProducer; } public void setEditor(Consumer<JsonTree> editorFunction, NUIManager manager) { // Create and display a context menu on RMB. subscribeNodeClick((event, node) -> { if (event.getMouseButton() == MouseInput.MOUSE_RIGHT) { setSelectedIndex(getModel().indexOf(node)); setAlternativeWidget(null); MenuTree menuTree = contextMenuTreeProducer.apply((JsonTree) node); ContextMenuUtils.showContextMenu(manager, event.getMouse().getPosition(), menuTree); } }); // Edit a node on double click. subscribeNodeDoubleClick((event, node) -> { if (event.getMouseButton() == MouseInput.MOUSE_LEFT) { editorFunction.accept((JsonTree) node); } }); // Edit the currently selected node on F2. subscribeKeyEvent(event -> { if (event.isDown() && event.getKey() == Keyboard.Key.F2) { Integer selectedIndex = getSelectedIndex(); if (selectedIndex != null) { editorFunction.accept((JsonTree) getModel().getNode(selectedIndex)); } } }); } }