Skip to content

Commit

Permalink
Add hotkey for expand/collapse project tree (#3083)
Browse files Browse the repository at this point in the history
* Add hotkey for expand/collapse project tree

* Add unit tests for the actions

* Move project explorer tree expander to dedicate class

* Expose external JS API

* Typo fix

* Add new line
  • Loading branch information
Vladyslav Zhukovskyi committed Nov 17, 2016
1 parent 196e0c6 commit 5fb8ed5
Show file tree
Hide file tree
Showing 9 changed files with 458 additions and 3 deletions.
@@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.data.tree;

import com.google.common.annotations.Beta;

/**
* Component which performs basic tree operation such as expand and collapse.
*
* @author Vlad Zhukovskyi
* @since 5.0.0
*/
@Beta
public interface TreeExpander {

/**
* Perform tree expand in case if {@link #isExpandEnabled()} returns {@code true}.
*/
void expandTree();

/**
* Returns {@code true} in case if tree expand is possible.
*
* @return {@code true} in case if tree expand is possible, otherwise {@code false}
*/
boolean isExpandEnabled();

/**
* Perform tree collapse in case if {@link #isCollapseEnabled()} returns {@code true}.
*/
void collapseTree();

/**
* Returns {@code true} in case if tree collapse is possible.
*
* @return {@code true} in case if tree collapse is possible, otherwise {@code false}
*/
boolean isCollapseEnabled();
}
@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.actions.common;

import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.data.tree.TreeExpander;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* Base tree collapse action which consumes instance of {@link TreeExpander}.
*
* @author Vlad Zhukovskyi
* @see TreeExpander
* @since 5.0.0
*/
public abstract class CollapseTreeAction extends Action {

public abstract TreeExpander getTreeExpander();

public CollapseTreeAction() {
super("Collapse All");
}

@Override
public void actionPerformed(ActionEvent e) {
final TreeExpander treeExpander = getTreeExpander();

checkNotNull(treeExpander);

if (!treeExpander.isCollapseEnabled()) {
return;
}

treeExpander.collapseTree();
}

@Override
public void update(ActionEvent e) {
final TreeExpander treeExpander = getTreeExpander();

e.getPresentation().setEnabledAndVisible(treeExpander.isCollapseEnabled());
}
}
@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.actions.common;

import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.data.tree.TreeExpander;

import static com.google.common.base.Preconditions.checkNotNull;

/**
* Base tree expand action which consumes instance of {@link TreeExpander}.
*
* @author Vlad Zhukovskyi
* @see TreeExpander
* @since 5.0.0
*/
public abstract class ExpandTreeAction extends Action {

public abstract TreeExpander getTreeExpander();

public ExpandTreeAction() {
super("Expand All");
}

@Override
public void actionPerformed(ActionEvent e) {
final TreeExpander treeExpander = getTreeExpander();

checkNotNull(treeExpander);

if (!treeExpander.isExpandEnabled()) {
return;
}

treeExpander.expandTree();
}

@Override
public void update(ActionEvent e) {
final TreeExpander treeExpander = getTreeExpander();

e.getPresentation().setEnabledAndVisible(treeExpander.isExpandEnabled());
}
}
Expand Up @@ -20,7 +20,9 @@
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.api.data.tree.TreeExpander;
import org.eclipse.che.ide.api.data.tree.settings.NodeSettings;
import org.eclipse.che.ide.api.data.tree.settings.SettingsProvider;
import org.eclipse.che.ide.api.mvp.View;
Expand All @@ -37,11 +39,13 @@
import org.eclipse.che.ide.api.selection.Selection;
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
import org.eclipse.che.ide.part.explorer.project.ProjectExplorerView.ActionDelegate;
import org.eclipse.che.ide.project.node.SyntheticNode;
import org.eclipse.che.ide.project.node.SyntheticNodeUpdateEvent;
import org.eclipse.che.ide.resource.Path;
import org.eclipse.che.ide.resources.tree.ResourceNode;
import org.eclipse.che.ide.ui.smartTree.NodeDescriptor;
import org.eclipse.che.ide.ui.smartTree.Tree;
import org.eclipse.che.ide.ui.smartTree.event.BeforeExpandNodeEvent;
import org.eclipse.che.ide.ui.smartTree.event.SelectionChangedEvent;
import org.eclipse.che.ide.ui.smartTree.event.SelectionChangedEvent.SelectionChangedHandler;
import org.eclipse.che.providers.DynaObject;
Expand Down Expand Up @@ -74,6 +78,7 @@ public class ProjectExplorerPresenter extends BasePresenter implements ActionDel
private final SettingsProvider settingsProvider;
private final CoreLocalizationConstant locale;
private final Resources resources;
private final TreeExpander treeExpander;

private static final int PART_SIZE = 500;

Expand All @@ -85,7 +90,8 @@ public ProjectExplorerPresenter(final ProjectExplorerView view,
CoreLocalizationConstant locale,
Resources resources,
final ResourceNode.NodeFactory nodeFactory,
final SettingsProvider settingsProvider) {
final SettingsProvider settingsProvider,
final AppContext appContext) {
this.view = view;
this.nodeFactory = nodeFactory;
this.settingsProvider = settingsProvider;
Expand All @@ -109,6 +115,50 @@ public void onSelectionChanged(SelectionChangedEvent event) {
setSelection(new Selection<>(event.getSelection()));
}
});

view.getTree().addBeforeExpandHandler(new BeforeExpandNodeEvent.BeforeExpandNodeHandler() {
@Override
public void onBeforeExpand(BeforeExpandNodeEvent event) {
final NodeDescriptor nodeDescriptor = view.getTree().getNodeDescriptor(event.getNode());

if (event.getNode() instanceof SyntheticNode && nodeDescriptor != null && nodeDescriptor.isExpandDeep()) {
event.setCancelled(true);
}
}
});

treeExpander = new ProjectExplorerTreeExpander(view.getTree(), appContext);

registerNative();
}

/* Expose Project Explorer's internal API to the world, to allow automated Selenium scripts expand all projects tree. */
private native void registerNative() /*-{
var that = this;
var ProjectExplorer = {};
ProjectExplorer.expandAll = $entry(function() {
that.@org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter::doExpand()();
})
ProjectExplorer.collapseAll = $entry(function() {
that.@org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter::doCollapse()();
})
$wnd.IDE.ProjectExplorer = ProjectExplorer;
}-*/;

private void doExpand() {
if (treeExpander.isExpandEnabled()) {
treeExpander.expandTree();
}
}

private void doCollapse() {
if (treeExpander.isCollapseEnabled()) {
treeExpander.collapseTree();
}
}

@Override
Expand Down
@@ -0,0 +1,80 @@
package org.eclipse.che.ide.part.explorer.project;

import com.google.common.base.Predicate;

import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.api.data.tree.TreeExpander;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.ui.smartTree.Tree;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.any;

/**
* Project explorer tree expander. Takes care about resources loading state, in case if tree has been never expanded before, it requests
* infinite resource tree from the server and then expands project tree on the UI. On the second time it just expands project tree without
* additional resource requests.
*
* @author Vlad Zhukovskyi
* @since 5.0.0
* @see TreeExpander
*/
final class ProjectExplorerTreeExpander implements TreeExpander {

private Tree tree;
private AppContext appContext;

public ProjectExplorerTreeExpander(Tree tree, AppContext appContext) {
this.tree = tree;
this.appContext = appContext;
}

private final boolean[] everExpanded = new boolean[]{false};

@Override
public void expandTree() {
if (everExpanded[0]) {
tree.expandAll();

return;
}

appContext.getWorkspaceRoot().getTree(-1).then(new Operation<Resource[]>() {
@Override
public void apply(Resource[] ignored) throws OperationException {
everExpanded[0] = true;

tree.expandAll();
}
});
}

@Override
public boolean isExpandEnabled() {
return tree.getNodeStorage().getAllItemsCount() != 0;
}

@Override
public void collapseTree() {
tree.collapseAll();
}

@Override
public boolean isCollapseEnabled() {
return any(tree.getRootNodes(), isExpanded());
}

private Predicate<Node> isExpanded() {
return new Predicate<Node>() {
@Override
public boolean apply(@javax.annotation.Nullable Node node) {
checkNotNull(node);

return tree.isExpanded(node);
}
};
}
}
Expand Up @@ -631,10 +631,10 @@ Promise<Resource[]> getRemoteResources(final Container container, final int dept
return promises.resolve(NO_RESOURCES);
}

int depthToReload = depth;
final Optional<Resource[]> descendants = store.getAll(container.getLocation());

int depthToReload = depth;
if (descendants.isPresent()) {
if (depthToReload != -1 && descendants.isPresent()) {
for (Resource resource : descendants.get()) {
if (resource.getLocation().segmentCount() - container.getLocation().segmentCount() > depth) {
depthToReload = resource.getLocation().segmentCount() - container.getLocation().segmentCount();
Expand Down

0 comments on commit 5fb8ed5

Please sign in to comment.