/******************************************************************************* * Copyright (c) 2012-2017 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.part.editor.multipart; import com.google.common.collect.BiMap; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.SimpleLayoutPanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; import com.google.gwt.user.client.ui.Widget; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import org.eclipse.che.ide.api.constraints.Direction; import org.eclipse.che.ide.api.parts.EditorMultiPartStackState; import org.eclipse.che.ide.api.parts.EditorPartStack; import org.eclipse.che.ide.api.theme.Style; import static org.eclipse.che.ide.api.constraints.Direction.HORIZONTALLY; import static org.eclipse.che.ide.api.constraints.Direction.VERTICALLY; /** * @author Roman Nikitenko */ public class SplitEditorPartViewImpl implements SplitEditorPartView { private final static int SPLITTER_SIZE = 5; private final static String VERTICAL_DRAGGER_CLASS = "gwt-SplitLayoutPanel-VDragger"; private final static String HORIZONTAL_DRAGGER_CLASS = "gwt-SplitLayoutPanel-HDragger"; private SplitEditorPartView parent; private SplitEditorPartView specimenView; private SplitEditorPartView replicaView; private final IsWidget specimenWidget; private final SimpleLayoutPanel rootPanel; private SplitLayoutPanel splitLayoutPanel; private Direction direction; @AssistedInject public SplitEditorPartViewImpl(@Assisted IsWidget specimenWidget) { this.specimenWidget = specimenWidget; rootPanel = new SimpleLayoutPanel(); rootPanel.add(specimenWidget); } @AssistedInject public SplitEditorPartViewImpl(@Assisted IsWidget specimen, @Assisted SplitEditorPartView parent) { this(specimen); this.parent = parent; } @Override public SplitEditorPartView getSpecimen() { return specimenView; } @Override public SplitEditorPartView getReplica() { return replicaView; } @Override public void split(IsWidget replicaWidget, Direction direction, double size) { this.direction = direction; splitLayoutPanel = new SplitLayoutPanel(SPLITTER_SIZE); specimenView = new SplitEditorPartViewImpl(specimenWidget, this); replicaView = new SplitEditorPartViewImpl(replicaWidget, this); if (direction == VERTICALLY) { splitVertically(size); } else if (direction == HORIZONTALLY) { splitHorizontally(size); } splitLayoutPanel.add(replicaView); rootPanel.remove(specimenWidget); rootPanel.add(splitLayoutPanel); tuneSplitter(splitLayoutPanel); } private void splitVertically(double size) { double newSize = size == -1 ? rootPanel.getOffsetWidth() / 2 : size; splitLayoutPanel.addWest(specimenView, newSize); } private void splitHorizontally(double size) { double newSize = size == -1 ? rootPanel.getOffsetHeight() / 2 : size; splitLayoutPanel.addNorth(specimenView, newSize); } @Override public void removeChild(SplitEditorPartView child) { splitLayoutPanel.remove(child); if (child == replicaView && specimenView != null) { splitLayoutPanel.add(specimenView); } if (child == specimenView) { specimenView = null; } else if (child == replicaView) { replicaView = null; } if (parent != null && isEmpty()) { parent.removeChild(this); } if (isEmpty()) { rootPanel.removeFromParent(); } splitLayoutPanel.forceLayout(); } @Override public void removeFromParent() { if (parent != null) { parent.removeChild(this); } else { rootPanel.removeFromParent(); } } @Override public EditorMultiPartStackState getState(BiMap<SplitEditorPartView, EditorPartStack> splitEditorParts) { if (splitLayoutPanel == null) { return new EditorMultiPartStackState(splitEditorParts.get(this)); } else if(specimenView == null && replicaView != null){ return replicaView.getState(splitEditorParts); } else if(replicaView == null && specimenView != null){ return specimenView.getState(splitEditorParts); } if(specimenView != null){ return new EditorMultiPartStackState(direction, splitLayoutPanel.getWidgetSize(specimenView.asWidget()), specimenView.getState(splitEditorParts), replicaView.getState(splitEditorParts)); } throw new IllegalStateException("Can't create state, specimenView and replicaView are both null."); } private boolean isEmpty() { return specimenView == null && replicaView == null; } /** * Improves splitter visibility. */ private void tuneSplitter(SplitLayoutPanel splitLayoutPanel) { NodeList<Node> nodes = splitLayoutPanel.getElement().getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.getItem(i); if (node.hasChildNodes()) { Element el = node.getFirstChild().cast(); String className = el.getClassName(); if (HORIZONTAL_DRAGGER_CLASS.equals(className)) { tuneVerticalSplitter(el); } else if (VERTICAL_DRAGGER_CLASS.equals(className)) { tuneHorizontalSplitter(el); } } } } /** * Tunes splitter. Makes it wider and adds double border to seem rich. * * @param el * element to tune */ private void tuneVerticalSplitter(Element el) { /** Add Z-Index to move the splitter on the top and make content visible */ el.getParentElement().getStyle().setProperty("zIndex", "1000"); el.getParentElement().getStyle().setProperty("overflow", "visible"); /** Tune splitter catch panel */ el.getStyle().setProperty("boxSizing", "border-box"); el.getStyle().setProperty("width", "5px"); el.getStyle().setProperty("overflow", "hidden"); el.getStyle().setProperty("marginLeft", "-3px"); el.getStyle().setProperty("backgroundColor", "transparent"); /** Add small border */ DivElement smallBorder = Document.get().createDivElement(); smallBorder.getStyle().setProperty("position", "absolute"); smallBorder.getStyle().setProperty("width", "1px"); smallBorder.getStyle().setProperty("height", "100%"); smallBorder.getStyle().setProperty("left", "3px"); smallBorder.getStyle().setProperty("top", "0px"); smallBorder.getStyle().setProperty("backgroundColor", Style.getSplitterSmallBorderColor()); el.appendChild(smallBorder); } /** * Tunes bottom splitter. Makes it tiny but with a transparent area for easy resizing. * * @param el * element to tune */ private void tuneHorizontalSplitter(Element el) { /** Add Z-Index to move the splitter on the top and make content visible */ el.getParentElement().getStyle().setProperty("zIndex", "1000"); el.getParentElement().getStyle().setProperty("overflow", "visible"); el.getStyle().setProperty("height", "3px"); el.getStyle().setProperty("marginTop", "-2px"); el.getStyle().setProperty("backgroundColor", "transparent"); /** Add small border */ DivElement delimiter = Document.get().createDivElement(); delimiter.getStyle().setProperty("position", "absolute"); delimiter.getStyle().setProperty("width", "100%"); delimiter.getStyle().setProperty("height", "1px"); delimiter.getStyle().setProperty("left", "0px"); delimiter.getStyle().setProperty("backgroundColor", Style.getSplitterSmallBorderColor()); delimiter.getStyle().setProperty("top", "2px"); el.appendChild(delimiter); } @Override public Widget asWidget() { return rootPanel; } }