Package com.peterhi.ui.newbars

Source Code of com.peterhi.ui.newbars.StripBar

package com.peterhi.ui.newbars;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.jdesktop.core.animation.timing.Animator;
import org.jdesktop.core.animation.timing.TimingSource;
import org.jdesktop.swt.animation.timing.sources.SWTTimingSource;

import com.peterhi.ui.UIImageResource;

public final class StripBar extends Canvas implements Listener {

  protected int marginLeft = 5;
  protected int marginRight = 5;
  protected int marginTop = 5;
  protected int marginBottom = 5;
  protected int itemSpacing = 5;
  protected int dragThreshold = 15;
 
  private final List<StripItem> items = new ArrayList<StripItem>();
  private StripBarDragContext dragContext;
 
  public StripBar(Composite parent, int style) {
    super(parent, style);
    addListener(SWT.SetData, this);
    addListener(SWT.Paint, this);
    addListener(SWT.PaintItem, this);
    addListener(SWT.MouseEnter, this);
    addListener(SWT.MouseExit, this);
    addListener(SWT.MouseDown, this);
    addListener(SWT.MouseMove, this);
    addListener(SWT.MouseUp, this);
    addListener(SWT.Dispose, this);
  }

  @Override
  public void handleEvent(Event event) {
    if (equals(event.widget)) {
      if (event.type == SWT.SetData) {
        onSetData(event);
      } else if (event.type == SWT.Paint) {
        onPaint(event);
      } else if (event.type == SWT.PaintItem) {
        onPaintItem(event);
      } else if (event.type == SWT.MouseEnter) {
        onMouseEnter(event);
      } else if (event.type == SWT.MouseExit) {
        onMouseExit(event);
      } else if (event.type == SWT.MouseDown) {
        onMouseDown(event);
      } else if (event.type == SWT.MouseMove) {
        onMouseMove(event);
      } else if (event.type == SWT.MouseUp) {
        onMouseUp(event);
      } else if (event.type == SWT.Dispose) {
        onDispose(event);
      }
    }
  }
 
  @Override
  public Point computeSize(int wHint, int hHint, boolean changed) {
    Point size = computeInternalSize();
   
    Rectangle boundsWithTrim = computeTrim(0, 0, size.x, size.y);
    size.x = boundsWithTrim.width;
    size.y = boundsWithTrim.height;
   
    return size;
  }
 
  public boolean isMultiSelection() {
    int style = getStyle();
    boolean multi = (style & SWT.MULTI) == SWT.MULTI;
    return multi;
  }
 
  public StripItem[] getItems() {
    return items.toArray(new StripItem[items.size()]);
  }
 
  public StripItem getItem(int x, int y) {
    for (StripItem item : items) {
      Rectangle bounds = item.getBounds();
     
      if (bounds.contains(x, y)) {
        return item;
      }
    }
   
    return null;
  }
 
  public StripItem getItem(StripItemState state) {
    StripItem[] items = getItems(state);
   
    if (items.length > 0) {
      return items[0];
    }
   
    return null;
  }
 
  public StripItem[] getItems(StripItemState state) {
    Set<StripItem> itemSet = getItemSet(state);
    return itemSet.toArray(new StripItem[itemSet.size()]);
  }
 
  protected void onSetData(Event event) {
    StripItem item = (StripItem )event.item;
   
    if (event.detail == SWT.INSERT) {
      onAddItem(item, event.index);
    } else if (event.detail == SWT.DEL) {
      onRemoveItem(item);
    }
  }
 
  protected void onPaint(Event event) {
    GC gc = event.gc;
   
    StripItem draggedItem = getItem(StripItemState.DRAGGED);
   
    for (StripItem item : items) {
      if (!item.equals(draggedItem)) {
        firePaintItem(gc, item);
      }
    }
   
    if (draggedItem != null) {
      firePaintItem(gc, draggedItem);
    }
  }
 
  protected void firePaintItem(GC gc, StripItem item) {
    Rectangle bounds = item.getBounds();
    Event redirEvent = new Event();
    redirEvent.time = (int )(System.currentTimeMillis() & 0xffffffffL);
    redirEvent.widget = this;
    redirEvent.item = item;
    redirEvent.gc = gc;
    redirEvent.x = bounds.x;
    redirEvent.y = bounds.y;
    redirEvent.width = bounds.width;
    redirEvent.height = bounds.height;
    notifyListeners(SWT.PaintItem, redirEvent);
  }
 
  protected void onPaintItem(Event event) {
    StripItem item = (StripItem )event.item;
    Event redirEvent = new Event();
    redirEvent.time = (int )(System.currentTimeMillis() & 0xffffffffL);
    redirEvent.widget = item;
    redirEvent.gc = event.gc;
    redirEvent.x = event.x + item.getOffset();
    redirEvent.y = event.y;
    redirEvent.width = event.width;
    redirEvent.height = event.height;
    item.notifyListeners(SWT.Paint, redirEvent);
  }
 
  protected void onMouseEnter(Event event) {
    onFocusItem(getItem(event.x, event.y));
  }

  protected void onMouseExit(Event event) {
    onFocusItem(getItem(event.x, event.y));
  }

  protected void onMouseDown(Event event) {
    if (event.button == 1) {
      if (dragContext == null) {
        StripItem item = getItem(event.x, event.y);
       
        if (item != null) {
          Rectangle bounds = item.getBounds();
         
          dragContext = new StripBarDragContext(this, item);
          dragContext.setMouseDownLocation(event.x, event.y);
          dragContext.setMouseDownOffset(event.x - bounds.x, event.y - bounds.y);
          dragContext.setMouseMoveLocation(event.x, event.y);

          item.setState(StripItemState.PRESSED, false);
        }
      }
    }
  }

  protected void onMouseMove(Event event) {
    onFocusItem(getItem(event.x, event.y));
   
    if (dragContext != null) {
      Point mouseDownDistance = dragContext.getMouseDownDistance(event.x, event.y);
      Point mouseMoveDistance = dragContext.getMouseMoveDistance(event.x, event.y);
     
      if (Math.abs(mouseDownDistance.x) > dragThreshold) {
        StripItem item = dragContext.getItem();
       
        if (item != null) {
          if (item.getState() == StripItemState.PRESSED) {
            item.setState(StripItemState.DRAGGED, false);
            item.setOffset(item.getOffset() + (mouseDownDistance.x - mouseMoveDistance.x), false, null);
          }
        }
      }
     
      StripItem item = dragContext.getItem();
     
      if (item.getState() == StripItemState.DRAGGED) {
        int newOffset = item.getOffset() + mouseMoveDistance.x;
        StripItem[] affectedItems = getAffected(item, mouseMoveDistance.x);
       
        if (mouseMoveDistance.x < 0) {
          for (StripItem affectedItem : affectedItems) {
            int destination = shouldAlignBefore(affectedItem);
            int offset = destination - affectedItem.getBounds().x;
            affectedItem.setOffset(offset, true, null);
          }
        }
       
        if (mouseMoveDistance.x > 0) {
          for (StripItem affectedItem : affectedItems) {
            int destination = shouldAlignAfter(affectedItem);
            int offset = destination - affectedItem.getBounds().x;
            affectedItem.setOffset(offset, true, null);
          }
        }
       
        item.setOffset(newOffset, false, null);
      }
     
      dragContext.setMouseMoveLocation(event.x, event.y);
    }
  }
 
  protected void onMouseUp(Event event) {
    if (event.button == 1) {
      if (dragContext != null) {
        StripItem item = dragContext.getItem();

        if (item.getState() == StripItemState.PRESSED) {
          if (dragContext.getMouseDownState() == StripItemState.SELECTED) {
            if (isMultiSelection()) {
              item.setState(StripItemState.FOCUSED, false);
            } else {
              item.setState(StripItemState.SELECTED, false);
            }
          } else {
            item.setState(StripItemState.SELECTED, false);
          }
         
          if (!isMultiSelection()) {
            Set<StripItem> otherSelectedItems = getItemSet(StripItemState.SELECTED);
            otherSelectedItems.remove(item);
           
            for (StripItem otherSelectedItem : otherSelectedItems) {
              otherSelectedItem.setState(StripItemState.DEFAULT, true);
            }
          }
        } else if (item.getState() == StripItemState.DRAGGED) {
          if (dragContext.getMouseDownState() == StripItemState.SELECTED) {
            item.setState(StripItemState.SELECTED, false);
          } else {
            item.setState(StripItemState.DEFAULT, false);
          }
         
          int after = getAfter(item);
          int offset = after - item.getBounds().x;
         
          item.setOffset(offset, true, new Runnable() {
            @Override
            public void run() {
              reorderItems();
            }
          });
        }
       
        dragContext = null;
      }
    }
  }
 
  private int getAfter(StripItem item) {
    Rectangle bounds = item.getBounds();
    bounds.x += item.getOffset();
   
    for (int i = items.size() - 1; i >= 0; i--) {
      StripItem anItem = items.get(i);
      Rectangle anItemBounds = anItem.getBounds();
      anItemBounds.x += anItem.getOffset();
     
      if (anItemBounds.x < bounds.x) {
        return anItemBounds.x + anItemBounds.width + itemSpacing;
      }
    }
   
    return marginLeft;
  }
 
  protected void reorderItems() {
    Comparator<StripItem> comparator = new Comparator<StripItem>() {
      @Override
      public int compare(StripItem item, StripItem otherItem) {
        return (item.getBounds().x + item.getOffset()) - (otherItem.getBounds().x + otherItem.getOffset());
      }
    };
   
    Collections.sort(items, comparator);
   
    for (StripItem item : items) {
      item.setOffset(0, false, null);
    }
  }
 
  protected void onDispose(Event event) {
    StripItem[] items = getItems();
   
    for (StripItem item : items) {
      item.dispose();
    }
  }
 
  protected void onAddItem(StripItem item, int index) {
    if (index < 0 || index > items.size()) {
      index = items.size();
    }
   
    items.add(index, item);
  }
 
  protected void onRemoveItem(StripItem item) {
    items.remove(item);
  }
 
  protected void onFocusItem(StripItem item) {
    StripItem lastFocusedItem = getItem(StripItemState.FOCUSED);

    if (lastFocusedItem != null) {
      if (!lastFocusedItem.equals(item)) {
        lastFocusedItem.setState(StripItemState.DEFAULT, true);
      }
    }
   
    if (item != null) {
      if (!item.equals(lastFocusedItem)) {
        if (item.getState() != StripItemState.SELECTED) {
          if (getItem(StripItemState.PRESSED) == null) {
            if (getItem(StripItemState.DRAGGED) == null) {
              item.setState(StripItemState.FOCUSED, true);
            }
          }
        }
      }
    }
  }
 
  protected Set<StripItem> getItemSet(StripItemState state) {
    Set<StripItem> itemSet = new HashSet<StripItem>();
   
    for (StripItem item : items) {
      if (item.getState().equals(state)) {
        itemSet.add(item);
      }
    }
   
    return itemSet;
  }
 
  protected Point computeInternalSize() {
    Point size = new Point(0, 0);
   
    for (StripItem item : items) {
      Rectangle bounds = item.getBounds();
      size.x += bounds.width;
      size.y = Math.max(size.y, bounds.height);
    }
   
    size.y += marginTop;
    size.y += marginBottom;
    size.x += marginLeft;
    size.x += marginRight;
    size.x += itemSpacing * (items.size() - 1);
    return size;
  }
 
  private StripItem[] getAffectedItems(StripItem dragItem, int oldOffset, int newOffset) {
    Set<StripItem> affectedItems = new HashSet<StripItem>();
    Rectangle dragItemBounds = dragItem.getBounds();
    dragItemBounds.x += dragItem.getOffset();
   
    for (StripItem otherItem : items) {
      if (otherItem.equals(dragItem)) {
        continue;
      }
     
      Rectangle otherItemBounds = otherItem.getBounds();
      otherItemBounds.x += otherItem.getOffset();
     
      if (newOffset > oldOffset) {
        if (dragItemBounds.x < otherItemBounds.x) {
          if (dragItemBounds.x + dragItemBounds.width > otherItemBounds.x + otherItemBounds.width / 2) {
            affectedItems.add(otherItem);
          }
        }
      }
     
      if (newOffset < oldOffset) {
        if (dragItemBounds.x > otherItemBounds.x) {
          if (dragItemBounds.x < otherItemBounds.x + otherItemBounds.width / 2) {
            affectedItems.add(otherItem);
          }
        }
      }
    }
   
    return affectedItems.toArray(new StripItem[affectedItems.size()]);
  }
 
  private StripItem[] getAffected(StripItem item, int delta) {
    Set<StripItem> affected = new HashSet<StripItem>();
    Rectangle bounds = item.getBounds();
    bounds.x += item.getOffset();
   
    for (StripItem otherStripItem : items) {
      if (otherStripItem.equals(item)) {
        continue;
      }
     
      Rectangle otherBounds = otherStripItem.getBounds();
      otherBounds.x += otherStripItem.getOffset();
     
      if (delta > 0) {
        if (bounds.x < otherBounds.x) {
          if (bounds.x + delta + bounds.width > otherBounds.x + otherBounds.width / 2) {
            affected.add(otherStripItem);
          }
        }
      }
     
      if (delta < 0) {
        if (bounds.x > otherBounds.x) {
          if (bounds.x + delta < otherBounds.x + otherBounds.width / 2) {
            affected.add(otherStripItem);
          }
        }
      }
    }
   
    return affected.toArray(new StripItem[affected.size()]);
  }
 
  private int shouldAlignBefore(StripItem item) {
    Point size = computeInternalSize();
    int right = size.x - marginRight;
   
    for (int i = items.size() - 1; i >= 0; i--) {
      StripItem anItem = items.get(i);
     
      if (anItem.getState() == StripItemState.DRAGGED) {
        continue;
      }
     
      if (anItem.equals(item)) {
        return right - item.getBounds().width;
      }
     
      right -= anItem.getBounds().width;
      right -= itemSpacing;
    }
   
    return 0;
  }
 
  private int shouldAlignAfter(StripItem item) {
    int left = marginLeft;
   
    for (int i = 0; i < items.size(); i++) {
      StripItem anItem = items.get(i);
     
      if (anItem.getState() == StripItemState.DRAGGED) {
        continue;
      }
     
      if (anItem.equals(item)) {
        return left;
      }
     
      left += anItem.getBounds().width;
      left += itemSpacing;
    }
   
    return 0;
  }
 
  public static void main(String[] args) {
    Display display = Display.getDefault();
   
    TimingSource timingSource = new SWTTimingSource(16, TimeUnit.MILLISECONDS, display);
    timingSource.init();
    Animator.setDefaultTimingSource(timingSource);
   
    Shell shell = new Shell(display);
    StripBar bar = new StripBar(shell, SWT.DOUBLE_BUFFERED | SWT.BORDER);

    StripItem item1 = new StripItem(bar, SWT.NONE);
    item1.setText("Item 1");
   
    StripItem item2 = new StripItem(bar, SWT.NONE);
    item2.setImage(UIImageResource.T.addContact16());
   
    StripItem item3 = new StripItem(bar, SWT.NONE);
   
    StripItem item0 = new StripItem(bar, SWT.NONE, 0);
    item0.setText("Item 0");
    item0.setImage(UIImageResource.T.about16());
   
    GridLayout layout = new GridLayout();
    layout.horizontalSpacing = 0;
    layout.verticalSpacing = 0;
    layout.marginWidth = 0;
    layout.marginHeight = 0;
    shell.setLayout(layout);
   
    GridData data = new GridData();
    data.horizontalAlignment = SWT.FILL;
    data.verticalAlignment = SWT.FILL;
    data.grabExcessHorizontalSpace = true;
    bar.setLayoutData(data);
   
    shell.open();
   
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
  }

}
TOP

Related Classes of com.peterhi.ui.newbars.StripBar

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.