Package mindnotes.client.ui

Source Code of mindnotes.client.ui.NodeLayout

package mindnotes.client.ui;

import mindnotes.shared.model.NodeLocation;

/**
* NodeLayout is responsible for computing desired positions of nodes in a node
* tree based on their relations.
*
*
*
*
*
*
* @author dominik
*
*/
public class NodeLayout {

  public static class LayoutPosition {
    public LayoutTreeElement parent;
    public int index;
    public NodeLocation location;

    public LayoutPosition() {

    }

    public LayoutPosition(LayoutTreeElement parent, int index,
        NodeLocation location) {
      this.parent = parent;
      this.index = index;
      this.location = location;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + index;
      result = prime * result
          + ((location == null) ? 0 : location.hashCode());
      result = prime * result
          + ((parent == null) ? 0 : parent.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      LayoutPosition other = (LayoutPosition) obj;
      if (index != other.index)
        return false;
      if (location != other.location)
        return false;
      if (parent == null) {
        if (other.parent != null)
          return false;
      } else if (!parent.equals(other.parent))
        return false;
      return true;
    }

  }

  /**
   * Some margins for the layout; TODO make this configurable someday
   */
  private static final int HORIZONTAL_MARGIN = 48;
  private static final int VERTICAL_MARGIN = 32;

  /**
   * Layout works from bottom to top; layout mechanism for each element works
   * as a function:
   *
   * F: {Element Bounds of this element, Branch Bounds of every child} ->
   * {relative position of every child, Branch Bounds of this element}
   *
   * The doLayout() method is called recursively for every child; childless
   * elements return data immediately.
   *
   * @see LayoutTreeElement for definitions of branch bounds and element
   *      bounds.
   *
   * @param element
   */
  public static void doLayout(LayoutTreeElement element) {
    if (element.isLayoutValid()) {
      return; // nothing changed, nothing to do;
    }
    // there are two groups of elements: one on the left, one on the right

    // go through the list of children and find out:
    // maximum width on the left and right
    // total height on left and right

    Box elementBounds = element.getElementBounds();

    int wLeft = 0, wRight = 0, hLeft = 0, hRight = 0;
    for (LayoutTreeElement child : element.getLayoutChildren()) {
      // recursive call to let the child layout itself
      doLayout(child);
      Box b = child.getBranchBounds();
      if (child.getLocation() == NodeLocation.LEFT) {
        // right now, the child will be vertically shifted from the
        // desired location, because we don't know the height
        // of all elements
        child.setOffset(
            elementBounds.x - HORIZONTAL_MARGIN - b.x - b.w, hLeft
                - child.getBranchBounds().y);
        wLeft = Math.max(wLeft, b.w);
        hLeft += b.h + VERTICAL_MARGIN;

      }
      if (child.getLocation() == NodeLocation.RIGHT) {
        // see comment above
        child.setOffset(elementBounds.x + elementBounds.w
            + HORIZONTAL_MARGIN - child.getBranchBounds().x, hRight
            - child.getBranchBounds().y);
        wRight = Math.max(wRight, b.w);
        hRight += b.h + VERTICAL_MARGIN;
      }
    }
    if (hRight > 0)
      hRight -= VERTICAL_MARGIN;
    if (hLeft > 0)
      hLeft -= VERTICAL_MARGIN;

    // now, while traversing, we'll adjust the vertical position of
    // children.
    // the offset is set so that vertical center of parent element is at the
    // same height as center of all children brought together;
    int shiftLeft = elementBounds.y + (elementBounds.h / 2) - hLeft / 2;
    int shiftRight = elementBounds.y + (elementBounds.h / 2) - hRight / 2;

    for (LayoutTreeElement child : element.getLayoutChildren()) {
      if (child.getLocation() == NodeLocation.LEFT) {
        child.setOffset(child.getOffsetX(), child.getOffsetY()
            + shiftLeft);
      }
      if (child.getLocation() == NodeLocation.RIGHT) {
        child.setOffset(child.getOffsetX(), child.getOffsetY()
            + shiftRight);
      }
    }

    // determine this element's branch bounding box

    int eycenter = elementBounds.y + elementBounds.h / 2;
    int top = Math.min(eycenter - Math.max(hLeft, hRight) / 2,
        elementBounds.y);
    int bottom = Math.max(eycenter + Math.max(hLeft, hRight) / 2,
        elementBounds.y + elementBounds.h);
    int left = elementBounds.x
        - (wLeft == 0 ? 0 : HORIZONTAL_MARGIN + wLeft);
    int right = elementBounds.x + elementBounds.w
        + (wRight == 0 ? 0 : HORIZONTAL_MARGIN + wRight);

    Box bounds = new Box(left, top, right - left, bottom - top);
    element.setBranchBounds(bounds);
    element.setLayoutValid(true);

  }

  public static LayoutPosition findClosestInsertPosition(
      LayoutTreeElement root, int px, int py) {

    Box b = root.getElementBounds();

    boolean right = px >= (b.x + b.w / 2);

    // mouse location
    NodeLocation ml = right ? NodeLocation.RIGHT : NodeLocation.LEFT;
    boolean up = py < (b.y + b.h / 2);

    boolean isRoot = root.getLocation() == NodeLocation.ROOT;

    if (root.getLayoutChildren().isEmpty()) {
      if (isRoot) {

        return new LayoutPosition(root, 0, ml);
      } else {

        if (ml == root.getLocation()) {
          // we are deeper into the leaves - propose adding a child
          return new LayoutPosition(root, 0, ml);
        } else {
          // we are facing the parent - propose adding a sibling
          int index = root.getLayoutParent().getLayoutChildren()
              .indexOf(root);
          return new LayoutPosition(root.getLayoutParent(), index
              + (up ? 0 : 1), root.getLocation());
        }
      }
    } else {
      if (ml == root.getLocation() || isRoot) {

        // we are facing the parent - ask child to decide
        LayoutTreeElement lastChild = null;

        for (LayoutTreeElement child : root.getLayoutChildren()) {
          if (isRoot && ml != child.getLocation())
            continue;
          Box cb = child.getBranchBounds();

          if (py - child.getOffsetY() < cb.y + cb.h) {
            return findClosestInsertPosition(child,
                px - child.getOffsetX(),
                py - child.getOffsetY());
          }
          lastChild = child;
        }

        if (lastChild != null) {

          return findClosestInsertPosition(lastChild,
              px - lastChild.getOffsetX(),
              py - lastChild.getOffsetY());
        } else {

          // we are on childless side of root node; suggest adding
          return new LayoutPosition(root, 0, ml);
        }

      } else {

        // we are facing the parent - propose adding a sibling
        int index = root.getLayoutParent().getLayoutChildren()
            .indexOf(root);
        return new LayoutPosition(root.getLayoutParent(), index
            + (up ? 0 : 1), root.getLocation());
      }
    }
  }
}
TOP

Related Classes of mindnotes.client.ui.NodeLayout

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.