1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-04 07:35:24 +02:00

LaunchBar: ui changes in selector

- added ability to filter by typing in selector pop-up
- refactored list part of selection in its own components which is kind
of list viewer
- filter is visible by default if we have more than 7 elements in drop
down
- filter become visible if user start typing, even if it was not visible
originally
- moving storing of history size into the list viewer itself
- hooked up proper focus support and keyboard traversal
- modified CButton so we can re-use it for Edit button 
of selectors and list items
- when filtering is activated history list is no longer available

Change-Id: I2057ad658f5a5c9f8fbce203748d26d7e4b14dd6
This commit is contained in:
Alena Laskavaia 2015-01-13 16:48:24 -05:00
parent bb7f665007
commit edeec7aaaa
9 changed files with 1416 additions and 436 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

View file

@ -99,7 +99,15 @@ public class Activator extends AbstractUIPlugin {
}
public Image getImage(String id) {
return getImageRegistry().get(id);
Image im = getImageRegistry().get(id);
if (im == null) {
ImageDescriptor des = getImageDescriptor(id);
if (des != null) {
im = des.createImage();
getImageRegistry().put(id, im);
}
}
return im;
}
public static ImageDescriptor getImageDescriptor(String path) {

View file

@ -10,25 +10,27 @@
*******************************************************************************/
package org.eclipse.launchbar.ui.internal.controls;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
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.TypedListener;
public class CButton extends Canvas {
private boolean inButton;
private Image hotImage;
private Image coldImage;
public CButton(Composite parent, int style) {
super(parent, style);
addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
@ -47,17 +49,28 @@ public class CButton extends Canvas {
}
}
});
addMouseTrackListener(new MouseTrackAdapter() {
@Override
public void mouseEnter(MouseEvent e) {
inButton = true;
redraw();
setSelected(true);
}
@Override
public void mouseExit(MouseEvent e) {
inButton = false;
redraw();
setSelected(false);
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
setSelected(true);
handleSelection(inButton);
}
@Override
public void mouseDoubleClick(MouseEvent e) {
setSelected(true);
handleDefaultSelection(inButton);
}
});
}
@ -65,14 +78,12 @@ public class CButton extends Canvas {
@Override
public void dispose() {
super.dispose();
if (hotImage != null)
hotImage.dispose();
if (coldImage != null)
coldImage.dispose();
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
int width = 0;
@ -91,7 +102,7 @@ public class CButton extends Canvas {
}
return new Point(width, height);
}
public void setHotImage(Image image) {
this.hotImage = image;
}
@ -100,4 +111,29 @@ public class CButton extends Canvas {
this.coldImage = image;
}
protected void handleSelection(boolean selection) {
// Send event
notifyListeners(SWT.Selection, null);
}
protected void handleDefaultSelection(boolean selection) {
// Send event
notifyListeners(SWT.DefaultSelection, null);
}
public void addSelectionListener(SelectionListener listener) {
checkWidget();
TypedListener typedListener = new TypedListener(listener);
addListener(SWT.Selection, typedListener);
addListener(SWT.DefaultSelection, typedListener);
}
public void setSelected(boolean sel) {
inButton = sel;
redraw();
}
public boolean isSelected() {
return inButton;
}
}

View file

@ -10,52 +10,53 @@
*******************************************************************************/
package org.eclipse.launchbar.ui.internal.controls;
import java.util.Arrays;
import java.util.Comparator;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.launchbar.ui.IHoverProvider;
import org.eclipse.launchbar.ui.internal.Activator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.LineAttributes;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
public abstract class CSelector extends Composite {
private IStructuredContentProvider contentProvider;
private ILabelProvider labelProvider;
private IHoverProvider hoverProvider;
private Comparator<Object> sorter;
private Comparator<?> sorter;
private Comparator<?> sorterTop;
private Object input;
private Composite buttonComposite;
private String toolTipText;
@ -68,19 +69,10 @@ public abstract class CSelector extends Composite {
protected final Color highlightColor;
protected final Color white;
private boolean mouseOver;
private Image editImage;
private boolean inEditButton;
private Image buttonImage;
private Label currentIcon;
private Label currentLabel;
private Shell popup;
private ListItem listItems[];
private int selIndex;
private ScrolledComposite listScrolled;
private final int itemH = 30;
private int scrollBucket;
private final int maxScrollBucket = 7;
private int separatorIndex = -1;
private LaunchBarListViewer listViewer;
private MouseTrackListener mouseTrackListener = new MouseTrackListener() {
@Override
public void mouseEnter(MouseEvent e) {
@ -130,43 +122,91 @@ public abstract class CSelector extends Composite {
@Override
public void mouseUp(MouseEvent event) {
if (popup == null || popup.isDisposed()) {
setFocus();
openPopup();
} else {
closePopup();
}
}
};
protected boolean isFocusAncestor(Control control) {
while (control != null && control != this && !(control instanceof Shell)) {
control = control.getParent();
}
return control == this;
}
private Listener focusOutListener = new Listener() {
private Job closingJob;
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.FocusOut:
Control focusControl = getDisplay().getFocusControl();
if (focusControl != null && focusControl.getShell() == popup) {
Point loc = getDisplay().getCursorLocation();
if (!getBounds().contains(toControl(loc))) {
// Don't do it if we're in the selector, we'll deal with that later
closePopup();
}
case SWT.FocusIn:
if (closingJob != null)
closingJob.cancel();
if (event.widget instanceof Control && isFocusAncestor((Control) event.widget)) {
break; // not closing
}
if (!isPopUpInFocus()) {
closePopup();
}
break;
case SWT.MouseUp:
case SWT.FocusOut:
if (isPopUpInFocus()) {
// we about to loose focus from popup children, but it may go
// to another child, lets schedule a job to wait before we close
if (closingJob != null)
closingJob.cancel();
closingJob = new Job("Closing popup") {
@Override
protected IStatus run(IProgressMonitor monitor) {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
closePopup();
closingJob = null;
return Status.OK_STATUS;
}
};
closingJob.schedule(300);
}
break;
case SWT.MouseUp: {
if (popup != null && !popup.isDisposed()) {
Point loc = getDisplay().getCursorLocation();
if (!popup.getBounds().contains(loc) && !getBounds().contains(toControl(loc))) {
if (!popup.getBounds().contains(loc) && !getBounds().contains(getParent().toControl(loc))) {
closePopup();
}
}
break;
}
}
}
};
private ICellModifier modifier = new ICellModifier() {
@Override
public void modify(Object element, String property, Object value) {
handleEdit(element);
}
@Override
public Object getValue(Object element, String property) {
return null;
}
@Override
public boolean canModify(Object element, String property) {
return isEditable(element);
}
};
public CSelector(Composite parent, int style) {
super(parent, style);
backgroundColor = new Color(getDisplay(), new RGB(249, 249, 249));
outlineColor = new Color(getDisplay(), new RGB(189, 195, 200));
highlightColor = new Color(getDisplay(), new RGB(223, 239, 241));
backgroundColor = getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
outlineColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
highlightColor = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
white = getDisplay().getSystemColor(SWT.COLOR_WHITE);
GridLayout mainButtonLayout = new GridLayout();
setLayout(mainButtonLayout);
@ -186,16 +226,17 @@ public abstract class CSelector extends Composite {
addMouseTrackListener(mouseTrackListener);
}
private boolean isPopUpInFocus() {
Control focusControl = getDisplay().getFocusControl();
if (focusControl != null && focusControl.getShell() == popup) {
return true;
}
return false;
}
@Override
public void dispose() {
super.dispose();
backgroundColor.dispose();
outlineColor.dispose();
highlightColor.dispose();
if (editImage != null)
editImage.dispose();
if (buttonImage != null)
buttonImage.dispose();
if (popup != null)
popup.dispose();
}
@ -272,11 +313,12 @@ public abstract class CSelector extends Composite {
arrow.addMouseListener(mouseListener);
arrow.addMouseTrackListener(mouseTrackListener);
if (editable) {
Control editButton = createEditButton(buttonComposite, element);
final EditButton editButton = new EditButton(buttonComposite, SWT.NONE);
editButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
editButton.setBackground(backgroundColor);
editButton.addMouseListener(new MouseAdapter() {
editButton.addSelectionListener(new SelectionAdapter() {
@Override
public void mouseUp(MouseEvent e) {
public void widgetSelected(SelectionEvent e) {
// Need to run this after the current event storm
// Or we get a disposed error.
getDisplay().asyncExec(new Runnable() {
@ -316,32 +358,25 @@ public abstract class CSelector extends Composite {
}
popup = new Shell(getShell(), SWT.TOOL | SWT.ON_TOP);
popup.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
listScrolled = new ScrolledComposite(popup, SWT.V_SCROLL | SWT.NO_BACKGROUND);
listScrolled.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
listScrolled.setExpandHorizontal(true);
final Composite listComp = new Composite(listScrolled, SWT.NONE);
listScrolled.setContent(listComp);
listComp.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
if (sorter != null)
Arrays.sort(elements, sorter);
listItems = new ListItem[elements.length];
if (elements.length > 0) {
listItems[0] = new ListItem(listComp, SWT.NONE, elements[0], 0);
listItems[0].lazyInit();
final int hHint = Math.max(listItems[0].computeSize(SWT.DEFAULT, SWT.DEFAULT).y, 16);
for (int i = 1; i < elements.length; ++i) {
ListItem item = new ListItem(listComp, SWT.NONE, elements[i], i);
if (i < maxScrollBucket) { // this is how many visible by default
item.lazyInit();
} else {
((GridData) item.getLayoutData()).heightHint = hHint;
listViewer = new LaunchBarListViewer(popup);
initializeListViewer(listViewer);
listViewer.setFilterVisible(elements.length > 7);
listViewer.setInput(input);
listViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (!listViewer.isFinalSelection())
return;
StructuredSelection ss = (StructuredSelection) event.getSelection();
if (!ss.isEmpty()) {
setSelection(ss.getFirstElement());
fireSelectionChanged();
}
listItems[i] = item;
closePopup();
}
createSash(listComp, hHint);
}
Point listCompSize = listComp.computeSize(SWT.DEFAULT, SWT.DEFAULT);
listComp.setSize(listCompSize);
});
if (hasActionArea())
createActionArea(popup);
Rectangle buttonBounds = getBounds();
@ -350,295 +385,53 @@ public abstract class CSelector extends Composite {
popup.setLocation(popupLocation.x, popupLocation.y + 5);
Point size = popup.computeSize(SWT.DEFAULT, SWT.DEFAULT);
Point buttonSize = getSize();
size.x = Math.min(size.x + 16, buttonSize.x * 4 / 3);
size.y = Math.min(size.y, 250);
size.x = Math.max(size.x, buttonSize.x);
size.y = Math.min(size.y, 300);
popup.setSize(size);
popup.setVisible(true);
popup.setFocus();
getDisplay().addFilter(SWT.FocusIn, focusOutListener);
getDisplay().addFilter(SWT.FocusOut, focusOutListener);
getDisplay().addFilter(SWT.MouseUp, focusOutListener);
popup.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
getDisplay().removeFilter(SWT.FocusIn, focusOutListener);
getDisplay().removeFilter(SWT.FocusOut, focusOutListener);
getDisplay().removeFilter(SWT.MouseUp, focusOutListener);
}
});
selIndex = -1;
scrollBucket = 0;
if (hoverProvider != null) {
hoverProvider.dismissHover(selection != null ? selection : null, true);
}
}
private void createSash(final Composite listComp, final int hHint) {
if (separatorIndex<0) return;
final Sash sash = new Sash(listComp, SWT.BORDER | SWT.HORIZONTAL);
sash.setLayoutData(GridDataFactory.fillDefaults().create());
if (separatorIndex < listItems.length)
sash.moveAbove(listItems[separatorIndex]);
else
sash.moveBelow(null);
sash.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
separatorIndex = (e.y + hHint/2) / hHint;
}
});
sash.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
setSeparatorIndex(separatorIndex); // call setter if it was overriden
if (separatorIndex >= 0) {
if (separatorIndex < listItems.length)
sash.moveAbove(listItems[separatorIndex]);
else
sash.moveBelow(null);
listComp.layout();
}
}
@Override
public void mouseDown(MouseEvent e) {
sash.moveAbove(null); // keep on top so user see it when moving
}
@Override
public void mouseDoubleClick(MouseEvent e) {
// ignore
}
});
sash.setToolTipText("Increase/Decrease size of recently used elements pane");
protected void initializeListViewer(LaunchBarListViewer listViewer) {
listViewer.setContentProvider(contentProvider);
listViewer.setLabelProvider(labelProvider);
listViewer.setCellModifier(modifier);
listViewer.setComparator(sorter);
listViewer.setHistoryComparator(sorterTop);
}
private void closePopup() {
arrowTransition.to(arrowMax);
popup.setVisible(false);
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (popup == null || popup.isDisposed())
return;
arrowTransition.to(arrowMax);
popup.setVisible(false);
popup.dispose();
}
});
}
TraverseListener listItemTraverseListener = new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
final ListItem currItem = selIndex >= 0 ? listItems[selIndex] : null;
if (currItem == null && e.keyCode != SWT.ARROW_DOWN) {
return;
}
if (e.detail == SWT.TRAVERSE_ARROW_NEXT || e.detail == SWT.TRAVERSE_TAB_NEXT) {
if (inEditButton || e.keyCode == SWT.ARROW_DOWN) {
int maxIdx = listItems.length - 1;
if (selIndex < maxIdx) {
inEditButton = false;
if (currItem != null)
currItem.setBackground(white);
// move to next item
selIndex++;
if (scrollBucket < maxScrollBucket) {
scrollBucket++;
} else {
// need to scroll the list up 1 item
int sY = listScrolled.getOrigin().y;
listScrolled.setOrigin(0, sY + itemH);
}
listItems[selIndex].setBackground(highlightColor);
} else if (selIndex == maxIdx && maxIdx > maxScrollBucket) {
// level the scroll for any offset at the bottom of the list
listScrolled.setOrigin(0, itemH * (maxIdx - maxScrollBucket + 1));
}
} else if (currItem.editButton != null) {
// move focus on edit button
inEditButton = true;
currItem.editButton.redraw();
}
} else if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
if (!inEditButton || e.keyCode == SWT.ARROW_UP) {
if (selIndex > 0) {
inEditButton = false;
currItem.setBackground(white);
// move to previous item
selIndex--;
if (scrollBucket > 0) {
scrollBucket--;
} else {
// need to scroll the list down 1 item
int sY = listScrolled.getOrigin().y;
listScrolled.setOrigin(0, sY - itemH);
}
listItems[selIndex].setBackground(highlightColor);
} else if (selIndex == 0) {
// level any offset @ beginning
listScrolled.setOrigin(0, 0);
}
} else if (currItem.editButton != null) {
// remove focus from edit button
inEditButton = false;
currItem.editButton.redraw();
}
} else if (e.detail == SWT.TRAVERSE_RETURN) {
if (inEditButton) {
inEditButton = false;
// edit button in list item was pressed
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (CSelector.this.selection != null)
handleEdit(currItem.element);
}
});
} else {
// list item was pressed
popup.dispose();
setSelection(currItem.element);
fireSelectionChanged();
}
} else if (e.detail == SWT.TRAVERSE_ESCAPE) {
popup.dispose();
}
}
};
private class ListItem extends Composite {
protected final Object element;
private Label icon;
private Label label;
protected Control editButton;
private int index;
public ListItem(Composite parent, int style, Object _element, int index) {
super(parent, style);
this.element = _element;
this.index = index;
setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
Point size = getSize();
GC gc = e.gc;
gc.setForeground(outlineColor);
gc.drawLine(0, size.y - 1, size.x, size.y - 1);
if (label == null)
lazyInit();
}
});
// lazyInit();
} // end ListItem(..)
protected void lazyInit() {
Image image = labelProvider.getImage(element);
boolean editable = isEditable(element);
int columns = 1;
if (image != null)
columns++;
if (editable)
columns++;
GridLayout layout = new GridLayout(columns, false);
layout.marginWidth = layout.marginHeight = 7;
setLayout(layout);
MouseListener listItemMouseListener = new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
popup.dispose();
setSelection(element);
fireSelectionChanged();
}
};
MouseTrackListener listItemMouseTrackListener = new MouseTrackAdapter() {
@Override
public void mouseEnter(MouseEvent e) {
setBackground(highlightColor);
int idx = getIndex();
if (idx != selIndex) {
if (selIndex >= 0) {
listItems[selIndex].setBackground(white);
scrollBucket = Math.max(Math.min(scrollBucket + idx - selIndex, maxScrollBucket), 0);
} else { // initially
scrollBucket = Math.min(idx, maxScrollBucket);
}
}
selIndex = idx;
}
@Override
public void mouseExit(MouseEvent e) {
setBackground(white);
}
};
addMouseListener(listItemMouseListener);
// addMouseTrackListener(listItemMouseTrackListener);
if (image != null) {
icon = createImage(this, image);
icon.addMouseListener(listItemMouseListener);
icon.addMouseTrackListener(listItemMouseTrackListener);
}
label = createLabel(this, element);
label.addMouseListener(listItemMouseListener);
label.addMouseTrackListener(listItemMouseTrackListener);
if (editable) {
editButton = createEditButton(this, element);
editButton.setBackground(white);
editButton.addMouseTrackListener(listItemMouseTrackListener);
editButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
// Need to run this after the current event storm
// Or we get a disposed error.
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (CSelector.this.selection != null)
handleEdit(element);
}
});
}
});
editButton.addTraverseListener(listItemTraverseListener);
} else {
addTraverseListener(listItemTraverseListener);
}
setBackground(white);
layout(true);
}
@Override
public void setBackground(Color color) {
super.setBackground(color);
if (icon != null && !icon.isDisposed())
icon.setBackground(color);
if (label != null && !label.isDisposed())
label.setBackground(color);
if (editButton != null && !editButton.isDisposed())
editButton.setBackground(color);
}
public void setImage(Image image) {
if (icon != null && !icon.isDisposed())
icon.setImage(image);
}
public void setText(String text) {
if (!label.isDisposed())
label.setText(text);
}
protected int getIndex() {
return index;
}
} // end ListItem class
private Label createImage(Composite parent, Image image) {
Rectangle bounds = image.getBounds();
boolean disposeImage = false;
if (bounds.height > 16 || bounds.width > 16) {
buttonImage = new Image(getDisplay(), 16, 16);
Image buttonImage = new Image(getDisplay(), 16, 16);
GC gc = new GC(buttonImage);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
@ -646,6 +439,7 @@ public abstract class CSelector extends Composite {
image.getBounds().height, 0, 0, 16, 16);
gc.dispose();
image = buttonImage;
disposeImage = true;
}
Label icon = new Label(parent, SWT.NONE);
icon.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
@ -670,43 +464,6 @@ public abstract class CSelector extends Composite {
return label;
}
private Control createEditButton(Composite parent, Object element) {
if (editImage == null) {
editImage = Activator.getImageDescriptor("icons/config_config.png").createImage();
}
final Canvas editButton = new Canvas(parent, SWT.NONE) {
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
Rectangle bounds = editImage.getBounds();
return new Point(bounds.width, bounds.height);
};
};
editButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
editButton.setToolTipText("Edit");
editButton.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
GC gc = e.gc;
gc.setAlpha(inEditButton ? 255 : 64);
gc.drawImage(editImage, 0, 0);
}
});
editButton.addMouseTrackListener(new MouseTrackAdapter() {
@Override
public void mouseEnter(MouseEvent e) {
inEditButton = true;
editButton.redraw();
}
@Override
public void mouseExit(MouseEvent e) {
inEditButton = false;
editButton.redraw();
}
});
return editButton;
}
public void setContentProvider(IStructuredContentProvider contentProvider) {
this.contentProvider = contentProvider;
}
@ -731,10 +488,24 @@ public abstract class CSelector extends Composite {
return hoverProvider;
}
public void setSorter(Comparator<Object> sorter) {
/**
* Set sorter for the bottom part of the selector
*
* @param sorter
*/
public void setSorter(Comparator<?> sorter) {
this.sorter = sorter;
}
/**
* Set sorter for the "history" part of the selector
*
* @param sorter
*/
public void setHistorySortComparator(Comparator<?> sorter) {
this.sorterTop = sorter;
}
public void setInput(Object input) {
this.input = input;
}
@ -757,15 +528,7 @@ public abstract class CSelector extends Composite {
}
}
if (popup != null && !popup.isDisposed()) {
Object[] elements = contentProvider.getElements(input);
int i;
for (i = 0; i < elements.length; ++i)
if (element == elements[i])
break;
if (i != elements.length) {
listItems[i].setImage(labelProvider.getImage(element));
listItems[i].setText(labelProvider.getText(element));
}
listViewer.update(element, null);
}
}
@ -785,11 +548,4 @@ public abstract class CSelector extends Composite {
// nothing to do here
}
public int getSeparatorIndex() {
return separatorIndex;
}
public void setSeparatorIndex(int separatorIndex) {
this.separatorIndex = separatorIndex;
}
}

View file

@ -10,7 +10,6 @@
*******************************************************************************/
package org.eclipse.launchbar.ui.internal.controls;
import java.util.Arrays;
import java.util.Comparator;
import org.eclipse.core.runtime.CoreException;
@ -26,7 +25,6 @@ import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.launchConfigurations.LaunchGroupExtension;
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
@ -79,27 +77,13 @@ public class ConfigSelector extends CSelector {
@Override
public void dispose() {
}
@Override
public Object[] getElements(Object inputElement) {
ILaunchDescriptor[] descs = uiManager.getManager().getLaunchDescriptors();
if (descs.length > 0) {
int separatorIndex = getSeparatorIndex();
if (descs.length > separatorIndex + 1) {
ILaunchDescriptor[] descsCopy = new ILaunchDescriptor[separatorIndex + descs.length];
System.arraycopy(descs, 0, descsCopy, 0, separatorIndex); // copy first 3 elements
System.arraycopy(descs, 0, descsCopy, separatorIndex, descs.length); // copy all into rest
// sort rest
Arrays.sort(descsCopy, separatorIndex, descsCopy.length, new Comparator<ILaunchDescriptor>() {
@Override
public int compare(ILaunchDescriptor o1, ILaunchDescriptor o2) {
return o1.getName().compareTo(o2.getName());
}
});
return descsCopy;
} else
return descs;
}
return noConfigs;
if (descs.length == 0)
return noConfigs;
return descs;
}
});
@ -141,13 +125,23 @@ public class ConfigSelector extends CSelector {
return defaultProvider.getText(element);
}
});
// no sorter on view, data is sorted by provider
setSorter(null);
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
int separator = store.getInt(Activator.PREF_LAUNCH_HISTORY_SIZE);
if (separator <= 0)
separator = 1;
setSeparatorIndex(separator);
// no sorter for top, data is sorted by provider in historical order
setHistorySortComparator(null);
// alphabetic sorter
setSorter(new Comparator<ILaunchDescriptor>() {
@Override
public int compare(ILaunchDescriptor o1, ILaunchDescriptor o2) {
return o1.getName().compareTo(o2.getName());
}
});
}
@Override
protected void initializeListViewer(LaunchBarListViewer listViewer) {
listViewer.setHistorySupported(true);
listViewer.setHistoryPreferenceName(Activator.PREF_LAUNCH_HISTORY_SIZE);
super.initializeListViewer(listViewer);
}
@Override
@ -162,17 +156,7 @@ public class ConfigSelector extends CSelector {
}
}
}
@Override
public void setSeparatorIndex(int separatorIndex) {
super.setSeparatorIndex(separatorIndex);
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
int separator = store.getInt(Activator.PREF_LAUNCH_HISTORY_SIZE);
if (separator != getSeparatorIndex()) {
store.setValue(Activator.PREF_LAUNCH_HISTORY_SIZE, getSeparatorIndex());
}
}
@Override
public boolean isEditable(Object element) {
return element instanceof ILaunchDescriptor;
@ -233,7 +217,7 @@ public class ConfigSelector extends CSelector {
GridLayout buttonLayout = new GridLayout();
buttonLayout.marginWidth = buttonLayout.marginHeight = 7;
createButton.setLayout(buttonLayout);
createButton.setBackground(white);
createButton.setBackground(backgroundColor);
createButton.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
@ -247,7 +231,7 @@ public class ConfigSelector extends CSelector {
final Label createLabel = new Label(createButton, SWT.None);
createLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
createLabel.setText("Create New Configuration...");
createLabel.setBackground(white);
createLabel.setBackground(backgroundColor);
MouseListener mouseListener = new MouseAdapter() {
public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
@ -281,8 +265,8 @@ public class ConfigSelector extends CSelector {
}
@Override
public void mouseExit(MouseEvent e) {
createButton.setBackground(white);
createLabel.setBackground(white);
createButton.setBackground(backgroundColor);
createLabel.setBackground(backgroundColor);
}
};
createButton.addMouseTrackListener(mouseTrackListener);

View file

@ -0,0 +1,13 @@
package org.eclipse.launchbar.ui.internal.controls;
import org.eclipse.launchbar.ui.internal.Activator;
import org.eclipse.swt.widgets.Composite;
public class EditButton extends CButton {
public EditButton(Composite parent, int style) {
super(parent, style);
setHotImage(Activator.getDefault().getImage("icons/config_config.png"));
setColdImage(Activator.getDefault().getImage("icons/edit_cold.png"));
setToolTipText("Edit");
}
}

View file

@ -0,0 +1,472 @@
package org.eclipse.launchbar.ui.internal.controls;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.progress.WorkbenchJob;
/**
* A simple control that provides a text widget and controls a list viewer
*/
public class FilterControl extends Composite {
/**
* The filter text widget to be used by this tree. This value may be <code>null</code> if there is no filter widget, or if the
* controls have not yet been created.
*/
protected Text filterText;
/**
* The viewer for the filtered tree. This value should never be <code>null</code> after the widget creation methods are
* complete.
*/
protected LaunchBarListViewer listViewer;
protected ViewerFilter patternFilter;
/**
* The text to initially show in the filter text control.
*/
protected String initialText = ""; //$NON-NLS-1$
protected String patternText = null;
/**
* The job used to refresh the tree.
*/
private Job refreshJob;
/**
* The parent composite this control.
*/
protected Composite parent;
/**
* Creates a filter control, to be fully function attachListViewer must be called shortly after
*
* @param parent
*/
public FilterControl(Composite parent) {
super(parent, SWT.NONE);
this.parent = parent;
patternFilter = new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
String text = ((ILabelProvider) listViewer.getLabelProvider()).getText(element);
if (patternText == null)
return true;
String trim = patternText.trim();
if (trim.isEmpty())
return true;
if (text == null)
return false;
if (text.contains(trim))
return true;
if (text.toLowerCase().contains(trim.toLowerCase()))
return true;
return false;
}
};
init();
}
/**
* Create the filtered list.
*/
protected void init() {
createControl(this, SWT.NONE);
createRefreshJob();
setInitialText(WorkbenchMessages.FilteredTree_FilterMessage);
setFont(parent.getFont());
}
/**
* Create the filtered tree's controls. Subclasses should override.
*
* @param parent
* @param treeStyle
*/
protected void createControl(Composite parent, int treeStyle) {
setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
if (parent.getLayout() instanceof GridLayout) {
setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
}
Composite fc = createFilterControls(parent);
fc.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
}
/**
* Create the filter controls. By default, a text and corresponding tool bar button that clears the contents of the text is
* created. Subclasses may override.
*
* @param parent
* parent <code>Composite</code> of the filter controls
* @return the <code>Composite</code> that contains the filter controls
*/
protected Composite createFilterControls(Composite parent) {
createFilterText(parent);
return parent;
}
public Control attachListViewer(LaunchBarListViewer listViewer) {
this.listViewer = listViewer;
// listViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
listViewer.getControl().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
refreshJob.cancel();
}
});
listViewer.addFilter(patternFilter);
return listViewer.getControl();
}
/**
* Create the refresh job for the receiver.
*
*/
private void createRefreshJob() {
refreshJob = doCreateRefreshJob();
refreshJob.setSystem(true);
}
@Override
public void setVisible(boolean visible) {
boolean oldVisible = getVisible();
if (oldVisible == true && visible == false && listViewer != null && filterText.isFocusControl()) {
listViewer.setFocus();
}
if (getLayoutData() instanceof GridData) {
((GridData) getLayoutData()).heightHint = visible ? SWT.DEFAULT : 0;
}
super.setVisible(visible);
}
/**
* Creates a workbench job that will refresh the tree based on the current filter text. Subclasses may override.
*
* @return a workbench job that can be scheduled to refresh the tree
*
* @since 3.4
*/
protected WorkbenchJob doCreateRefreshJob() {
return new WorkbenchJob("Refresh Filter") {//$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (listViewer == null)
return Status.CANCEL_STATUS;
if (listViewer.getControl().isDisposed()) {
return Status.CANCEL_STATUS;
}
updatePatternText();
if (patternText == null) {
return Status.OK_STATUS;
}
Control redrawControl = listViewer.getControl();
try {
// don't want the user to see updates that will be made to
// the tree
// we are setting redraw(false) on the composite to avoid
// dancing scrollbar
redrawControl.setRedraw(false);
listViewer.setHistorySupported(patternText == null || patternText.isEmpty());
listViewer.refresh(true);
updateListSelection(false);
} finally {
redrawControl.setRedraw(true);
}
return Status.OK_STATUS;
}
};
}
/**
* Creates the filter text and adds listeners. This method calls {@link #doCreateFilterText(Composite)} to create the text
* control. Subclasses should override {@link #doCreateFilterText(Composite)} instead of overriding this method.
*
* @param parent
* <code>Composite</code> of the filter text
*/
protected void createFilterText(Composite parent) {
filterText = doCreateFilterText(parent);
filterText.getAccessible().addAccessibleListener(
new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
String filterTextString = filterText.getText();
if (filterTextString.length() == 0
|| filterTextString.equals(initialText)) {
e.result = initialText;
} else {
e.result = NLS.bind(
WorkbenchMessages.FilteredTree_AccessibleListenerFiltered,
new String[] {
filterTextString,
String.valueOf(getFilteredItemsCount()) });
}
}
/**
* Return the number of filtered items
*
* @return int
*/
private int getFilteredItemsCount() {
return listViewer.getItemCount();
}
});
filterText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
}
@Override
public void focusLost(FocusEvent e) {
if (filterText.getText().equals(initialText)) {
setFilterText(""); //$NON-NLS-1$
}
}
});
filterText.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if (filterText.getText().equals(initialText)) {
clearText();
}
}
});
// enter key set focus to tree
filterText.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN) {
e.doit = false;
listViewer.setFocus();
updateListSelection(true);
} else if (e.detail == SWT.TRAVERSE_ARROW_NEXT) {
listViewer.setFocus();
updateListSelection(false);
}
}
});
filterText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
textChanged();
}
});
// if we're using a field with built in cancel we need to listen for
// default selection changes (which tell us the cancel button has been
// pressed)
if ((filterText.getStyle() & SWT.ICON_CANCEL) != 0) {
filterText.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
if (e.detail == SWT.ICON_CANCEL)
clearText();
}
});
}
filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
}
protected void updateListSelection(boolean enter) {
if (listViewer.getItemCount() == 0) {
if (enter)
Display.getCurrent().beep();
} else {
StructuredSelection sel;
// if the initial filter text hasn't changed, do not try to match
if (patternText != null && !patternText.trim().isEmpty()) {
// select item with triggering event, it may close the popup if list used as combo
sel = new StructuredSelection(listViewer.getTopFilteredElement());
} else {
sel = new StructuredSelection(listViewer.getTopElement());
}
if (enter)
listViewer.setDefaultSelection(sel);
else
listViewer.setSelection(sel);
}
}
protected Text doCreateFilterText(Composite parent) {
return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH
| SWT.ICON_CANCEL);
}
/**
* Update the receiver after the text has changed.
*/
protected void textChanged() {
String old = patternText;
updatePatternText();
if (patternText != null && old == null && patternText.isEmpty())
return;// we changing from initial selection to empty string
if (patternText == old)
return;
// cancel currently running job first, to prevent unnecessary redraw
refreshJob.cancel();
refreshJob.schedule(getRefreshJobDelay());
}
/**
* Return the time delay that should be used when scheduling the filter refresh job. Subclasses may override.
*
* @return a time delay in milliseconds before the job should run
*
*/
protected long getRefreshJobDelay() {
return 200;
}
/**
* Set the background for the widgets that support the filter text area.
*
* @param background
* background <code>Color</code> to set
*/
@Override
public void setBackground(Color background) {
super.setBackground(background);
// listComposite.setBackground(background);
// filterText.setBackground(background);
}
/**
* Clears the text in the filter text widget.
*/
protected void clearText() {
setFilterText(""); //$NON-NLS-1$
// textChanged();
}
/**
* Set the text in the filter control.
*
* @param string
*/
protected void setFilterText(String string) {
if (filterText != null) {
filterText.setText(string);
selectAll();
}
}
public Text getFilterText() {
return filterText;
}
/**
* Get the tree viewer of the receiver.
*
* @return the tree viewer
*/
public LaunchBarListViewer getViewer() {
return listViewer;
}
/**
* Get the filter text for the receiver, if it was created. Otherwise return <code>null</code>.
*
* @return the filter Text, or null if it was not created
*/
public Text getFilterControl() {
return filterText;
}
/**
* Convenience method to return the text of the filter control. If the text widget is not created, then null is returned.
*
* @return String in the text, or null if the text does not exist
*/
protected String getFilterString() {
return filterText != null ? filterText.getText() : null;
}
/**
* Set the text that will be shown until the first focus. A default value is provided, so this method only need be called if
* overriding the default initial text is desired.
*
* @param text
* initial text to appear in text field
*/
public void setInitialText(String text) {
initialText = text;
if (filterText != null) {
filterText.setMessage(text);
if (filterText.isFocusControl()) {
setFilterText(initialText);
textChanged();
} else {
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (!filterText.isDisposed() && filterText.isFocusControl()) {
setFilterText(initialText);
textChanged();
}
}
});
}
} else {
setFilterText(initialText);
textChanged();
}
}
/**
* Select all text in the filter text field.
*
*/
protected void selectAll() {
if (filterText != null) {
filterText.selectAll();
}
}
/**
* Get the initial text for the receiver.
*
* @return String
*/
protected String getInitialText() {
return initialText;
}
private void updatePatternText() {
String text = getFilterString();
boolean initial = initialText != null
&& initialText.equals(text);
if (initial) {
patternText = null;
} else if (text != null) {
patternText = text;
}
}
}

View file

@ -0,0 +1,711 @@
package org.eclipse.launchbar.ui.internal.controls;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.launchbar.core.ILaunchDescriptor;
import org.eclipse.launchbar.ui.internal.Activator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
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.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
public class LaunchBarListViewer extends StructuredViewer {
private ScrolledComposite listScrolled;
private Composite listComposite;
private ListItem[] listItems;
private int selIndex;
private int itemH = 30;
private int scrollBucket;
private final int maxScrollBucket = 6;
private int separatorIndex = -1;
private boolean historySupported = true;
private ICellModifier modifier;
private ViewerComparator historyComparator;
private boolean finalSelection = false;
private FilterControl filterControl;
private Sash sash;
private String historyPref;
private static class LaunchBarListViewerComparator extends ViewerComparator {
public LaunchBarListViewerComparator(Comparator<?> comp) {
super(comp);
}
// have to override it because standard ViewerComparator compares by labels only
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
return getComparator().compare(e1, e2);
}
}
private TraverseListener listItemTraverseListener = new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
final ListItem currItem = selIndex >= 0 ? listItems[selIndex] : null;
if (currItem == null && e.keyCode != SWT.ARROW_DOWN) {
return;
}
if (e.detail == SWT.TRAVERSE_ARROW_NEXT || e.detail == SWT.TRAVERSE_TAB_NEXT) {
if (e.keyCode == SWT.ARROW_DOWN) {
int maxIdx = listItems.length - 1;
if (selIndex < maxIdx) {
// move to next item
listItems[selIndex + 1].setSelected(true);
if (scrollBucket < maxScrollBucket) {
scrollBucket++;
} else {
// need to scroll the list up 1 item
int sY = listScrolled.getOrigin().y;
listScrolled.setOrigin(0, sY + itemH);
}
} else if (selIndex == maxIdx && maxIdx > maxScrollBucket) {
// level the scroll for any offset at the bottom of the list
listScrolled.setOrigin(0, itemH * (maxIdx - maxScrollBucket + 1));
}
}
} else if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
if (e.keyCode == SWT.ARROW_UP) {
if (selIndex > 0) {
// move to previous item
if (scrollBucket > 0) {
scrollBucket--;
} else {
// need to scroll the list down 1 item
int sY = listScrolled.getOrigin().y;
listScrolled.setOrigin(0, sY - itemH);
}
listItems[selIndex - 1].setSelected(true);
} else if (selIndex == 0) {
// level any offset @ beginning
listScrolled.setOrigin(0, 0);
}
} else if (currItem.editButton != null) {
// remove focus from edit button
currItem.editButton.setSelected(false);
currItem.editButton.redraw();
}
} else if (e.detail == SWT.TRAVERSE_RETURN) {
setDefaultSelection(new StructuredSelection(currItem.element));
} else if (e.detail == SWT.TRAVERSE_ESCAPE) {
setDefaultSelection(new StructuredSelection());
}
}
};
private class ListItem extends Composite {
protected final Object element;
private Label icon;
private Label label;
protected EditButton editButton;
private int index;
private Color backgroundColor = getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
private Color outlineColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
private Color highlightColor = getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
private ILabelProvider labelProvider;
private ICellModifier modifer;
@Override
public String toString() {
return "[" + index + "] " + labelProvider.getText(element);
}
public ListItem(Composite parent, int style, Object element, int index, ILabelProvider labelProvider,
ICellModifier modifier) {
super(parent, style);
this.element = element;
this.index = index;
this.labelProvider = labelProvider;
this.modifer = modifier;
setData(element);
addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
Point size = getSize();
GC gc = e.gc;
gc.setForeground(outlineColor);
gc.drawLine(0, size.y - 1, size.x, size.y - 1);
if (label == null)
lazyInit();
}
});
// lazyInit();
} // end ListItem(..)
protected void lazyInit() {
Image image = labelProvider.getImage(element);
boolean editable = isEditable(element);
int columns = 1;
if (image != null)
columns++;
if (editable)
columns++;
GridLayout layout = new GridLayout(columns, false);
layout.marginWidth = layout.marginHeight = 7;
setLayout(layout);
MouseListener listItemMouseListener = new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
setDefaultSelection(new StructuredSelection(element));
}
};
MouseTrackListener listItemMouseTrackListener = new MouseTrackAdapter() {
@Override
public void mouseEnter(MouseEvent e) {
setSelected(true);
}
@Override
public void mouseExit(MouseEvent e) {
setSelected(false);
}
};
addMouseListener(listItemMouseListener);
addMouseTrackListener(listItemMouseTrackListener);
if (image != null) {
icon = createImage(this, image);
icon.addMouseListener(listItemMouseListener);
icon.addMouseTrackListener(listItemMouseTrackListener);
}
label = createLabel(this, element);
label.addMouseListener(listItemMouseListener);
label.addMouseTrackListener(listItemMouseTrackListener);
if (editable) {
editButton = new EditButton(this, SWT.NONE);
editButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Need to run this after the current event storm
// Or we get a disposed error.
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (editButton.isSelected())
handleEdit(element);
}
});
}
});
editButton.setBackground(backgroundColor);
editButton.addMouseTrackListener(listItemMouseTrackListener);
editButton.addTraverseListener(listItemTraverseListener);
} else {
// add traverse listnener to control which will have keyboard focus
addTraverseListener(listItemTraverseListener);
}
addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent e) {
// ignore
}
@Override
public void keyPressed(KeyEvent e) {
if (e.character != 0 && !filterControl.isVisible()) {
// enable filter control and send the character there
filterControl.setVisible(true);
filterControl.setFocus();
filterControl.getParent().layout(true);
filterControl.getFilterText().setText(e.character + "");
filterControl.getFilterText().setSelection(1);
}
}
});
setBackground(backgroundColor);
layout(true);
}
public void setSelected(boolean selected) {
if (selected) {
setBackground(highlightColor);
int idx = getIndex();
if (idx != selIndex) {
if (selIndex >= 0) {
listItems[selIndex].setBackground(backgroundColor);
scrollBucket = Math.max(Math.min(scrollBucket + idx - selIndex, maxScrollBucket), 0);
} else { // initially
scrollBucket = Math.min(idx, maxScrollBucket);
}
}
selIndex = idx;
} else {
setBackground(backgroundColor);
}
if (editButton != null) {
editButton.setSelected(selected);
}
}
protected boolean isEditable(Object element) {
if (modifer != null) {
return modifer.canModify(element, null);
}
return false;
}
protected void handleEdit(Object element) {
if (modifer != null) {
modifer.modify(element, null, null);
}
}
@Override
public void setBackground(Color color) {
super.setBackground(color);
if (icon != null && !icon.isDisposed())
icon.setBackground(color);
if (label != null && !label.isDisposed())
label.setBackground(color);
if (editButton != null && !editButton.isDisposed())
editButton.setBackground(color);
}
public void setImage(Image image) {
if (icon != null && !icon.isDisposed())
icon.setImage(image);
}
public void setText(String text) {
if (!label.isDisposed())
label.setText(text);
}
@Override
public boolean setFocus() {
super.setFocus();
return true;
}
protected int getIndex() {
return index;
}
private Label createImage(Composite parent, Image image) {
Rectangle bounds = image.getBounds();
boolean disposeImage = false;
if (bounds.height > 16 || bounds.width > 16) {
Image buttonImage = new Image(getDisplay(), 16, 16);
GC gc = new GC(buttonImage);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
// resize to 16 pixels
gc.drawImage(image, 0, 0, image.getBounds().width,
image.getBounds().height, 0, 0, 16, 16);
gc.dispose();
image = buttonImage;
disposeImage = true;
}
Label icon = new Label(parent, SWT.NONE);
icon.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
icon.setImage(image);
if (disposeImage) {
final Image disposableImage = image;
icon.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
disposableImage.dispose();
}
});
}
return icon;
}
private Label createLabel(Composite parent, Object element) {
Label label = new Label(parent, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();
label.setText(labelProvider.getText(element));
if (labelProvider instanceof IFontProvider) {
label.setFont(((IFontProvider) labelProvider).getFont(element));
}
return label;
}
} // end ListItem class
public LaunchBarListViewer(Composite parent) {
filterControl = new FilterControl(parent);
listScrolled = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.NO_BACKGROUND);
listScrolled.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
listScrolled.setExpandHorizontal(true);
listComposite = new Composite(listScrolled, SWT.NONE);
listScrolled.setContent(listComposite);
listComposite.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
selIndex = -1;
scrollBucket = 0;
filterControl.attachListViewer(this);
historySupported = false;
setHistoryPreferenceName(getHistoryPreferenceName());
}
private void createSash(final Composite listComp) {
if (separatorIndex < 0 || !historySupported)
return;
sash = new Sash(listComp, SWT.BORDER | SWT.HORIZONTAL);
sash.setLayoutData(GridDataFactory.fillDefaults().create());
if (separatorIndex < listItems.length)
sash.moveAbove(listItems[separatorIndex]);
else
sash.moveBelow(null);
sash.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
separatorIndex = (e.y + itemH / 2) / itemH;
}
});
sash.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent e) {
setSeparatorIndex(separatorIndex); // call setter if it was overriden
if (separatorIndex >= 0) {
if (separatorIndex < listItems.length)
sash.moveAbove(listItems[separatorIndex]);
else
sash.moveBelow(null);
listComp.layout();
}
}
@Override
public void mouseDown(MouseEvent e) {
sash.moveAbove(null); // keep on top so user see it when moving
}
@Override
public void mouseDoubleClick(MouseEvent e) {
// ignore
}
});
sash.setToolTipText("Increase/Decrease size of recently used elements pane");
}
@Override
public Control getControl() {
return listScrolled;
}
@Override
protected void inputChanged(Object input, Object oldInput) {
super.inputChanged(input, oldInput);
refreshAll();
}
protected void refreshAll() {
selIndex = -1;
Control[] children = listComposite.getChildren();
for (Control control : children) {
control.dispose();
}
Object[] origElements = getElements();
Object[] elements = filterElements(origElements);
listItems = new ListItem[elements.length];
if (elements.length > 0) {
listItems[0] = createListItem(elements, 0);
itemH = Math.max(listItems[0].computeSize(SWT.DEFAULT, SWT.DEFAULT).y, 16);
for (int i = 1; i < elements.length; ++i) {
listItems[i] = createListItem(elements, i);
}
createSash(listComposite);
}
listComposite.pack(true);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
if (elements.length > maxScrollBucket) {
Rectangle bounds = listItems[maxScrollBucket].getBounds();
gd.heightHint = Math.max(bounds.y + bounds.height, itemH * (maxScrollBucket + 1));
}
listScrolled.setLayoutData(gd);
listScrolled.layout(true);
}
private ListItem createListItem(Object[] elements, int i) {
ListItem item = new ListItem(listComposite, SWT.NONE, elements[i], i, (ILabelProvider) getLabelProvider(), modifier);
GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
item.setLayoutData(gd);
if (i <= maxScrollBucket) { // this is how many visible by default
item.lazyInit();
} else {
gd.heightHint = itemH;
}
return item;
}
@Override
protected Widget doFindInputItem(Object element) {
return doFindItem(element);
}
@Override
protected Widget doFindItem(Object element) {
if (listItems == null)
return null;
for (ListItem listItem : listItems) {
if (listItem.element.equals(element))
return listItem;
}
return null;
}
@Override
protected void doUpdateItem(Widget item, Object element, boolean fullMap) {
if (item instanceof ListItem) {
((ListItem) item).lazyInit();
}
}
@Override
protected List getSelectionFromWidget() {
ArrayList<Object> arrayList = new ArrayList<>();
if (selIndex >= 0)
arrayList.add(listItems[selIndex].element);
return arrayList;
}
@Override
protected void internalRefresh(Object element) {
if (element == null || element == getRoot()) {
refreshAll();
return;
}
ListItem item = (ListItem) doFindItem(element);
ILabelProvider lp = (ILabelProvider) getLabelProvider();
if (lp == null || item == null)
return;
item.setImage(lp.getImage(element));
item.setText(lp.getText(element));
}
private Object[] filterElements(Object[] elements) {
Object[] topElements = elements.clone();
if (getComparator() != null)
getComparator().sort(this, elements);
if (getTopComparator() != null)
getTopComparator().sort(this, topElements);
// only bottom part will be filtered
Object[] result = elements;
if (getFilters() != null) {
for (ViewerFilter f : getFilters()) {
result = f.filter(this, (Object) null, result);
}
}
if (separatorIndex <= 0 || !historySupported)
return result;
if (separatorIndex >= topElements.length) {
return topElements; // all elements will fit in top elements
}
ILaunchDescriptor[] descsCopy = new ILaunchDescriptor[separatorIndex + result.length];
System.arraycopy(topElements, 0, descsCopy, 0, separatorIndex); // copy first N elements
System.arraycopy(result, 0, descsCopy, separatorIndex, result.length); // copy all into rest
return descsCopy;
}
private Object[] getElements() {
IStructuredContentProvider cp = (IStructuredContentProvider) getContentProvider();
if (cp == null)
return new Object[0];
Object[] elements = cp.getElements(getInput());
return elements;
}
@Override
public void reveal(Object element) {
// TODO Auto-generated method stub
}
public void setDefaultSelection(StructuredSelection selection) {
finalSelection = true;
setSelection(selection, true);
}
@Override
protected void setSelectionToWidget(List l, boolean reveal) {
if (l.size() == 0) {
return;
}
Object sel = l.get(0);
Widget wid = doFindItem(sel);
if (wid instanceof ListItem) {
ListItem listItem = (ListItem) wid;
listItem.setSelected(true);
}
}
public int getSeparatorIndex() {
return separatorIndex;
}
public void setSeparatorIndex(int separatorIndex) {
this.separatorIndex = separatorIndex;
if (separatorIndex <= 0)
return;
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
String prefName = getHistoryPreferenceName();
if (prefName != null && store.getInt(prefName) != getSeparatorIndex()) {
store.setValue(prefName, getSeparatorIndex());
}
}
protected String getHistoryPreferenceName() {
return historyPref;
}
public void setCellModifier(ICellModifier modifier) {
this.modifier = modifier;
}
public int getItemCount() {
return listItems.length;
}
/**
* Returns top element (provider element) in the begging on non-history list
*
* @return
*/
public Object getTopFilteredElement() {
if (listItems.length > 0) {
if (separatorIndex <= 0 || separatorIndex >= listItems.length || !historySupported)
return listItems[0].element;
else
return listItems[separatorIndex].element;
}
return null;
}
public Object getTopElement() {
if (listItems.length > 0) {
return listItems[0].element;
}
return null;
}
public ViewerComparator getTopComparator() {
return historyComparator;
}
/**
* ViewerComparator comparator labels of elements by default
*
* @param comp
*/
public void setHistoryComparator(ViewerComparator comp) {
historyComparator = comp;
}
public void setHistoryComparator(Comparator<?> comp) {
historyComparator = comp == null ? null : new LaunchBarListViewerComparator(comp);
}
public void setComparator(Comparator<?> comp) {
setComparator(comp == null ? null : new LaunchBarListViewerComparator(comp));
}
public boolean isHistorySupported() {
return historySupported;
}
public void setHistorySupported(boolean historySupported) {
this.historySupported = historySupported;
}
public void setHistoryPreferenceName(String historyPreferenceName) {
this.historyPref = historyPreferenceName;
if (historyPreferenceName != null) {
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
int separator = store.getInt(historyPreferenceName);
if (separator <= 0)
separator = 1;
setSeparatorIndex(separator);
}
}
/**
* final selection will be set to true when user made a final selection in a list for example when double click on entry or
* pressed enter key
*/
public boolean isFinalSelection() {
return finalSelection;
}
public void setFinalSelection(boolean finalSelection) {
this.finalSelection = finalSelection;
}
public static void main(String[] args) {
Display display = Display.getDefault();
Shell shell = new Shell();
RowLayout rowLayout = new RowLayout();
shell.setLayout(rowLayout);
(new Label(shell, SWT.NULL)).setText("Hello");
LaunchBarListViewer v = new LaunchBarListViewer(shell);
v.setContentProvider(new ArrayContentProvider());
v.setLabelProvider(new LabelProvider());
v.setInput(new String[] { "aaa", "bbb", "ccc" });
shell.pack();
shell.open();
// textUser.forceFocus();
// Set up the event loop.
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
// If no more entries in event queue
display.sleep();
}
}
display.dispose();
}
public void setFocus() {
if (selIndex >= 0 && listItems != null && listItems.length < selIndex)
listItems[selIndex].setFocus();
else
getControl().setFocus();
}
public void setFilterVisible(boolean vis) {
filterControl.setVisible(vis);
}
}

View file

@ -184,7 +184,7 @@ public class TargetSelector extends CSelector {
GridLayout buttonLayout = new GridLayout();
buttonLayout.marginWidth = buttonLayout.marginHeight = 7;
createButton.setLayout(buttonLayout);
createButton.setBackground(white);
createButton.setBackground(backgroundColor);
createButton.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
@ -198,7 +198,7 @@ public class TargetSelector extends CSelector {
final Label createLabel = new Label(createButton, SWT.None);
createLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
createLabel.setText("Create New Target...");
createLabel.setBackground(white);
createLabel.setBackground(backgroundColor);
MouseListener mouseListener = new MouseAdapter() {
public void mouseUp(org.eclipse.swt.events.MouseEvent e) {