Package com.peterhi.ui

Source Code of com.peterhi.ui.SplitPane

package com.peterhi.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;


final class SplitPane extends Canvas implements Listener {

  private Rectangle cachedOldClientArea;
  private int gap = 10;
 
  public SplitPane(Composite parent, int style) {
    super(parent, style);
    addListener(SWT.Resize, this);
  }
 
  public TabPane[] getTabPanes() {
    List<TabPane> paneList = new ArrayList<TabPane>();
    Control[] controls = getChildren();

    for (Control control : controls) {
      if (control instanceof TabPane) {
        TabPane pane = (TabPane )control;
        paneList.add(pane);
      }
    }
   
    RectComparator compare = RectComparator.getHorizontal();
    Collections.sort(paneList, compare);
    TabPane[] panes = paneList.toArray(new TabPane[paneList.size()]);
    return panes;
  }
 
  public View[] getViews() {
    List<View> viewList = new ArrayList<View>();
    TabPane[] tabViewContainers = getTabPanes();
   
    for (TabPane tabViewContainer : tabViewContainers) {
      View[] tabbedViews = tabViewContainer.getViews();
     
      for (View tabbedView : tabbedViews) {
        viewList.add(tabbedView);
      }
    }
   
    // TODO: add other non-tab views when they are implemented
   
    return viewList.toArray(new View[viewList.size()]);
  }

  public Window getWindow() {
    return (Window )getShell();
  }
 
  public int getGap() {
    return gap;
  }
 
  public void setGap(int value) {
    if (value < 0) {
      throw new IllegalArgumentException();
    }
   
    if (value == 0) {
      throw new IllegalArgumentException();
    }
   
    if (value % 2 != 0) {
      throw new IllegalArgumentException();
    }
   
    gap = value;
    layout();
  }

  @Override
  public void handleEvent(Event event) {
    if (equals(event.widget)) {
      if (event.type == SWT.Resize) {
        onResize(event);
      }
    }
  }
 
  @Override
  public void setLayout(Layout layout) {
  }
 
  @Override
  public Point computeSize(int widthHint, int heightHint, boolean changed) {
    Point size = super.computeSize(widthHint, heightHint, changed);
    View[] views = getViews();
   
    if (views != null) {
      Rectangle clientArea = getClientArea();
     
      if (widthHint == SWT.DEFAULT) {
        widthHint = clientArea.width;
      }
     
      if (heightHint == SWT.DEFAULT) {
        heightHint = clientArea.height;
      }
     
      int insets = gap / 2;
      float xFactor = 0.0f;
      float yFactor = 0.0f;
     
      for (View view : views) {
        Composite parent = view.getParent();
        Rectangle parentBounds = parent.getBounds();
        Point viewMinimumSize = view.getMinimumSize();
       
        if (parentBounds.x > clientArea.x) {
          parentBounds.x -= insets;
          parentBounds.width += insets;
          viewMinimumSize.x += insets;
        }
       
        if (parentBounds.y > clientArea.y) {
          parentBounds.y -= insets;
          parentBounds.height += insets;
          viewMinimumSize.y += insets;
        }
       
        if (parentBounds.x + parentBounds.width < clientArea.x + clientArea.width) {
          parentBounds.width += insets;
          viewMinimumSize.x += insets;
        }
       
        if (parentBounds.y + parentBounds.height < clientArea.y + clientArea.height) {
          parentBounds.height += insets;
          viewMinimumSize.y += insets;
        }
       
        Rectangle parentTrim = parent.computeTrim(0, 0, 0, 0);
        viewMinimumSize.x += parentTrim.width;
        viewMinimumSize.y += parentTrim.height;
       
        float curXFactor = ((float )parentBounds.width) / ((float )viewMinimumSize.x);
        float curYFactor = ((float )parentBounds.height) / ((float )viewMinimumSize.y);
       
        if (xFactor == 0.0f || xFactor > curXFactor) {
          xFactor = curXFactor;
        }
       
        if (yFactor == 0.0f || yFactor > curYFactor) {
          yFactor = curYFactor;
        }
      }
     
      if (xFactor > 0.0f) {
        size.x = Math.round(((float )widthHint) / xFactor);
      }
     
      if (yFactor > 0.0f) {
        size.y = Math.round(((float )heightHint) / yFactor);
      }
    }
   
    return size;
  }
 
  @Override
  public void layout(boolean changed) {
    super.layout(changed);
   
    Rectangle newClientArea = getClientArea();
   
    if (newClientArea.width <= 0 || newClientArea.height <= 0) {
      return;
    }
   
    TabPane[] tabViewContainers = getTabPanes();
    SortedMap<TabPane, SortedSet<TabPane>> xFollowingTabViewContainerMap = generateFollowingTabViewContainerMap(SWT.HORIZONTAL, tabViewContainers);
    SortedMap<TabPane, SortedSet<TabPane>> yFollowingTabViewContainerMap = generateFollowingTabViewContainerMap(SWT.VERTICAL, tabViewContainers);
    SortedSet<TabPane> xTrailingTabViewContainerSet = generateTrailingTabViewContainerSet(SWT.HORIZONTAL, tabViewContainers, cachedOldClientArea);
    SortedSet<TabPane> yTrailingTabViewContainerSet = generateTrailingTabViewContainerSet(SWT.VERTICAL, tabViewContainers, cachedOldClientArea);
    Map<TabPane, Rectangle> cachedBoundsMap = generateCachedBoundsMap(tabViewContainers);
    inflateCachedBounds(cachedBoundsMap, cachedOldClientArea, gap / 2);
    scaleCachedBounds(tabViewContainers, cachedBoundsMap, cachedOldClientArea, newClientArea);
    TabPane initialTabViewContainer = getUpperLeftTabViewContainer();
    lineUpTabViewContainers(initialTabViewContainer, cachedBoundsMap, xFollowingTabViewContainerMap, yFollowingTabViewContainerMap);
    truncateTrailingViewContainers(cachedBoundsMap, newClientArea, xTrailingTabViewContainerSet, yTrailingTabViewContainerSet);
    inflateCachedBounds(cachedBoundsMap, newClientArea, -gap / 2);
    applyCachedBounds(cachedBoundsMap);
   
    Window window = getWindow();
    Point size = computeSize(SWT.DEFAULT, SWT.DEFAULT);
    Rectangle trim = window.computeTrim(0, 0, size.x, size.y);
    window.setMinimumSize(trim.width, trim.height);
  }
 
  /*public Point computeMinimumSize() {
    Point minimumSize = new Point(0, 0);
    View[] views = getViews();
   
    if (views != null) {
      Rectangle clientArea = getClientArea();
      int insets = viewSpacing / 2;
      float xFactor = 0.0f;
      float yFactor = 0.0f;
     
      for (View view : views) {
        Composite parent = view.getParent();
        Rectangle parentBounds = parent.getBounds();
        Point viewMinimumSize = view.getMinimumSize();
       
        if (parentBounds.x > clientArea.x) {
          parentBounds.x -= insets;
          parentBounds.width += insets;
          viewMinimumSize.x += insets;
        }
       
        if (parentBounds.y > clientArea.y) {
          parentBounds.y -= insets;
          parentBounds.height += insets;
          viewMinimumSize.y += insets;
        }
       
        if (parentBounds.x + parentBounds.width < clientArea.x + clientArea.width) {
          parentBounds.width += insets;
          viewMinimumSize.x += insets;
        }
       
        if (parentBounds.y + parentBounds.height < clientArea.y + clientArea.height) {
          parentBounds.height += insets;
          viewMinimumSize.y += insets;
        }
       
        Rectangle parentTrim = parent.computeTrim(0, 0, 0, 0);
        viewMinimumSize.x += parentTrim.width;
        viewMinimumSize.y += parentTrim.height;
       
        float curXFactor = ((float )parentBounds.width) / ((float )viewMinimumSize.x);
        float curYFactor = ((float )parentBounds.height) / ((float )viewMinimumSize.y);
       
        if (xFactor == 0.0f || xFactor > curXFactor) {
          xFactor = curXFactor;
        }
       
        if (yFactor == 0.0f || yFactor > curYFactor) {
          yFactor = curYFactor;
        }
      }
     
      if (xFactor > 0.0f) {
        minimumSize.x = Math.round(((float )clientArea.width) / xFactor);
      }
     
      if (yFactor > 0.0f) {
        minimumSize.y = Math.round(((float )clientArea.height) / yFactor);
      }
    }
   
    return minimumSize;
  }
 
  private void updateMinimumSize() {
    View[] views = getViews();
   
    if (views == null || views.length == 0) {
      return;
    }
   
    Window window = (Window )getShell();
    Rectangle clientArea = getClientArea();
   
    Point trim = new Point(0, 0);
    trim.x = getShell().getBounds().width - getClientArea().width;
    trim.y = getShell().getBounds().height - getClientArea().height;
   
    float xFactor = 0.0f;
    float yFactor = 0.0f;
   
    for (View view : views) {
      TabViewContainer tab = (TabViewContainer )view.getParent();
      Rectangle tabBounds = tab.getBounds();
      Rectangle tabTrim = tab.computeTrim(0, 0, 0, 0);
     
      Point min = view.getMinimumSize();
      min.x += tabTrim.width;
      min.y += tabTrim.height;
     
      if (tabBounds.x != clientArea.x) {
        tabBounds.x -= viewSpacing / 2;
        tabBounds.width += viewSpacing / 2;
       
        min.x += viewSpacing / 2;
      }
     
      if (tabBounds.y != clientArea.y) {
        tabBounds.y -= viewSpacing / 2;
        tabBounds.height += viewSpacing / 2;
       
        min.y += viewSpacing / 2;
      }
     
      if (tabBounds.x + tabBounds.width != clientArea.x + clientArea.width) {
        tabBounds.width += viewSpacing / 2;
       
        min.x += viewSpacing / 2;
      }
     
      if (tabBounds.y + tabBounds.height != clientArea.y + clientArea.height) {
        tabBounds.height += viewSpacing / 2;
       
        min.y += viewSpacing / 2;
      }
     
      float curXFactor = (float )(tabBounds.width) / (float )min.x;
      float curYFactor = (float )(tabBounds.height) / (float )min.y;
     
      if (xFactor == 0.0f) {
        xFactor = curXFactor;
      } else if (curXFactor < xFactor) {
        xFactor = curXFactor;
      }
     
      if (yFactor == 0.0f) {
        yFactor = curYFactor;
      } else if (curYFactor < yFactor) {
        yFactor = curYFactor;
      }
    }
   
    Point size = window.getSize();
   
    if (xFactor > 0.0f) {
      size.x -= trim.x;
      size.x = Math.round((float )size.x / xFactor);
      size.x += trim.x;
    }
   
    if (yFactor > 0.0f) {
      size.y -= trim.y;
      size.y = Math.round((float )size.y / yFactor);
      size.y += trim.y;
    }
   
    window.setMinimumSize(size);
  }*/

  protected void onResize(Event event) {
    if (cachedOldClientArea == null) {
      cachedOldClientArea = getClientArea();
    }
   
    layout();
   
    cachedOldClientArea = getClientArea();
  }
 
  private Map<TabPane, Rectangle> generateCachedBoundsMap(TabPane[] tabViewContainers) {
    Map<TabPane, Rectangle> cachedBoundsMap = new HashMap<TabPane, Rectangle>();
   
    for (TabPane tabViewContainer : tabViewContainers) {
      Rectangle containerBounds = tabViewContainer.getBounds();
      cachedBoundsMap.put(tabViewContainer, containerBounds);
    }
   
    return cachedBoundsMap;
  }
 
  private void inflateCachedBounds(Map<TabPane, Rectangle> cachedBoundsMap, Rectangle clientArea, int amount) {
    int cLeft = clientArea.x;
    int cTop = clientArea.y;
    int cRight = cLeft + clientArea.width;
    int cBottom = cTop + clientArea.height;
   
    for (Map.Entry<TabPane, Rectangle> entry : cachedBoundsMap.entrySet()) {
      TabPane tabContainer = entry.getKey();
      Rectangle containerBounds = entry.getValue();
     
      int left = containerBounds.x;
      int top = containerBounds.y;
      int right = left + containerBounds.width;
      int bottom = top + containerBounds.height;
     
      if (left > cLeft) {
        containerBounds.x -= amount;
        containerBounds.width += amount;
      }
     
      if (top > cTop) {
        containerBounds.y -= amount;
        containerBounds.height += amount;
      }
     
      if (right < cRight) {
        containerBounds.width += amount;
      }
     
      if (bottom < cBottom) {
        containerBounds.height += amount;
      }
     
      updateCachedBounds(cachedBoundsMap, tabContainer, containerBounds);
    }
  }
 
  private SortedMap<TabPane, SortedSet<TabPane>> generateFollowingTabViewContainerMap(
    int orientation, TabPane[] tabViewContainers) {
   
    if (tabViewContainers == null) {
      throw new NullPointerException();
    }
   
    if (orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL) {
      throw new IllegalArgumentException();
    }
   
    RectComparator primary = RectComparator.getInstance(orientation);
    Arrays.sort(tabViewContainers, primary);
   
    RectComparator secondary;
   
    if (orientation == SWT.HORIZONTAL) {
      secondary = RectComparator.getVertical();
    } else if (orientation == SWT.VERTICAL) {
      secondary = RectComparator.getHorizontal();
    } else {
      throw new IllegalStateException();
    }
   
    SortedMap<TabPane, SortedSet<TabPane>> followingTabViewContainerMap =
      new TreeMap<TabPane, SortedSet<TabPane>>(secondary);
   
    for (TabPane leadingTabViewContainer : tabViewContainers) {
      SortedSet<TabPane> followingTabViewContainerSet = followingTabViewContainerMap.get(leadingTabViewContainer);
     
      if (followingTabViewContainerSet == null) {
        followingTabViewContainerSet = new TreeSet<TabPane>(secondary);
        followingTabViewContainerMap.put(leadingTabViewContainer, followingTabViewContainerSet);
      }
     
      Rectangle lBounds = leadingTabViewContainer.getBounds();
      int lLeft = lBounds.x;
      int lTop = lBounds.y;
      int lRight = lLeft + lBounds.width;
      int lBottom = lTop + lBounds.height;
     
      for (TabPane possibleFollowingTabViewContainer : tabViewContainers) {
        if (leadingTabViewContainer.equals(possibleFollowingTabViewContainer)) {
          continue;
        }
       
        Rectangle fBounds = possibleFollowingTabViewContainer.getBounds();
        int fLeft = fBounds.x;
        int fTop = fBounds.y;
        int fRight = fLeft + fBounds.width;
        int fBottom = fTop + fBounds.height;
       
        if (orientation == SWT.HORIZONTAL) {
          if (lTop > fBottom || lBottom < fTop) {
            continue;
          }
         
          if (lRight + gap != fLeft) {
            continue;
          }
         
          followingTabViewContainerSet.add(possibleFollowingTabViewContainer);
        } else if (orientation == SWT.VERTICAL) {
          if (lLeft > fRight || lRight < fLeft) {
            continue;
          }
         
          if (lBottom + gap != fTop) {
            continue;
          }
         
          followingTabViewContainerSet.add(possibleFollowingTabViewContainer);
        } else {
          throw new IllegalStateException();
        }
      }
    }
   
    return followingTabViewContainerMap;
  }
 
  private SortedSet<TabPane> generateTrailingTabViewContainerSet(
    int orientation, TabPane[] tabViewContainers, Rectangle clientArea) {
   
    if (tabViewContainers == null) {
      throw new NullPointerException();
    }
   
    if (orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL) {
      throw new IllegalArgumentException();
    }
   
    RectComparator primaryComparator = RectComparator.getInstance(orientation);
    Arrays.sort(tabViewContainers, primaryComparator);
   
    RectComparator secondaryComparator;
   
    if (orientation == SWT.HORIZONTAL) {
      secondaryComparator = RectComparator.getVertical();
    } else if (orientation == SWT.VERTICAL) {
      secondaryComparator = RectComparator.getHorizontal();
    } else {
      throw new IllegalStateException();
    }
   
    SortedSet<TabPane> trailingTabViewContainerSet = new TreeSet<TabPane>(secondaryComparator);

    int cLeft = clientArea.x;
    int cTop = clientArea.y;
    int cRight = cLeft + clientArea.width;
    int cBottom = cTop + clientArea.height;
   
    for (TabPane possibleTrailingTabViewContainer : tabViewContainers) {
      Rectangle tBounds = possibleTrailingTabViewContainer.getBounds();
      int tLeft = tBounds.x;
      int tTop = tBounds.y;
      int tRight = tLeft + tBounds.width;
      int tBottom = tTop + tBounds.height;
     
      if (orientation == SWT.HORIZONTAL) {
        if (tRight == cRight) {
          trailingTabViewContainerSet.add(possibleTrailingTabViewContainer);
        }
      } else if (orientation == SWT.VERTICAL) {
        if (tBottom == cBottom) {
          trailingTabViewContainerSet.add(possibleTrailingTabViewContainer);
        }
      } else {
        throw new IllegalStateException();
      }
    }
   
    return trailingTabViewContainerSet;
  }
 
  private void scaleCachedBounds(TabPane[] tabViewContainers, Map<TabPane, Rectangle> cachedBoundsMap, Rectangle oldClientArea, Rectangle newClientArea) {
    float xScaleFactor = (float )newClientArea.width / (float )oldClientArea.width;
    float yScaleFactor = (float )newClientArea.height / (float )oldClientArea.height;
   
    for (TabPane tabViewContainer : tabViewContainers) {
      Rectangle cachedBounds = cachedBoundsMap.get(tabViewContainer);
      float scaledWidth = (float )cachedBounds.width * xScaleFactor;
      float scaledHeight = (float )cachedBounds.height * yScaleFactor;
      cachedBounds.width = Math.round(scaledWidth);
      cachedBounds.height = Math.round(scaledHeight);
      updateCachedBounds(cachedBoundsMap, tabViewContainer, cachedBounds);
    }
  }
 
  private TabPane getUpperLeftTabViewContainer() {
    TabPane[] tabViewContainers = getTabPanes();
   
    for (TabPane tabViewContainer : tabViewContainers) {
      Rectangle countainerBounds = tabViewContainer.getBounds();
     
      if (countainerBounds.x == 0 && countainerBounds.y == 0) {
        return tabViewContainer;
      }
    }
   
    return null;
  }
 
  private void lineUpTabViewContainers(TabPane currentTabViewContainer, Map<TabPane, Rectangle> cachedBoundsMap,
    SortedMap<TabPane, SortedSet<TabPane>> xFollowingTabViewContainerMap,
    SortedMap<TabPane, SortedSet<TabPane>> yFollowingTabViewContainerMap) {
   
    {
      SortedSet<TabPane> xCurrentTabViewContainerFollowerSet = xFollowingTabViewContainerMap.get(currentTabViewContainer);
     
      if (xCurrentTabViewContainerFollowerSet != null) {
        Rectangle cBounds = cachedBoundsMap.get(currentTabViewContainer);
       
        for (TabPane xCurrentTabViewContainerFollower : xCurrentTabViewContainerFollowerSet) {
          Rectangle fBounds = cachedBoundsMap.get(xCurrentTabViewContainerFollower);
          fBounds.x = cBounds.x + cBounds.width;
          updateCachedBounds(cachedBoundsMap, xCurrentTabViewContainerFollower, fBounds);
          lineUpTabViewContainers(xCurrentTabViewContainerFollower, cachedBoundsMap, xFollowingTabViewContainerMap, yFollowingTabViewContainerMap);
        }
      }
    }
   
    {
      SortedSet<TabPane> yCurrentTabViewContainerFollowerSet = yFollowingTabViewContainerMap.get(currentTabViewContainer);
     
      if (yCurrentTabViewContainerFollowerSet != null) {
        Rectangle cBounds = cachedBoundsMap.get(currentTabViewContainer);
       
        for (TabPane yCurrentTabViewContainerFollower : yCurrentTabViewContainerFollowerSet) {
          Rectangle fBounds = cachedBoundsMap.get(yCurrentTabViewContainerFollower);
          fBounds.y = cBounds.y + cBounds.height;
          updateCachedBounds(cachedBoundsMap, yCurrentTabViewContainerFollower, fBounds);
          lineUpTabViewContainers(yCurrentTabViewContainerFollower, cachedBoundsMap, xFollowingTabViewContainerMap, yFollowingTabViewContainerMap);
        }
      }
    }
  }
 
  private void truncateTrailingViewContainers(Map<TabPane, Rectangle> cachedBoundsMap, Rectangle clientArea,
    SortedSet<TabPane> xTrailingTabViewContainerSet, SortedSet<TabPane> yTrailingTabViewContainerSet) {
    int cLeft = clientArea.x;
    int cTop = clientArea.y;
    int cRight = cLeft + clientArea.width;
    int cBottom = cTop + clientArea.height;
   
    for (TabPane xTrailingTabViewContainer : xTrailingTabViewContainerSet) {
      Rectangle tBounds = cachedBoundsMap.get(xTrailingTabViewContainer);
      int tLeft = tBounds.x;
      int tRight = tLeft + tBounds.width;
      tBounds.width -= tRight - cRight;
      updateCachedBounds(cachedBoundsMap, xTrailingTabViewContainer, tBounds);
    }
   
    for (TabPane yTrailingTabViewContainer : yTrailingTabViewContainerSet) {
      Rectangle tBounds = cachedBoundsMap.get(yTrailingTabViewContainer);
      int tTop = tBounds.y;
      int tBottom = tTop + tBounds.height;
      tBounds.height -= tBottom - cBottom;
      updateCachedBounds(cachedBoundsMap, yTrailingTabViewContainer, tBounds);
    }
  }
 
  private void applyCachedBounds(Map<TabPane, Rectangle> cachedBoundsMap) {
    for (Map.Entry<TabPane, Rectangle> entry : cachedBoundsMap.entrySet()) {
      TabPane tab = entry.getKey();
      Rectangle bounds = entry.getValue();
      tab.setBounds(bounds);
    }
  }
 
  private void updateCachedBounds(Map<TabPane, Rectangle> cachedBoundsMap, TabPane tabViewContainer, Rectangle bounds) {
    cachedBoundsMap.put(tabViewContainer, bounds);
  }
}
TOP

Related Classes of com.peterhi.ui.SplitPane

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.