The first item is the "Clear" menu. It clears the recent search history. Next comes the recent searches. If you want to call it as "Search History", you can do it in the title field menu item. The title field & clear menu item are visible only when there is at least one entry in the recent searches. The menu can also have custom items, where you can include you own action items. Lets see how to add all these in an SWT app:
Text searchText = new Text(shell, SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
SearchFieldSupport searchFieldSupport = new SearchFieldSupport(searchText);
Menu menu = new Menu(searchText);
MenuItem customItem = new MenuItem(menu, SWT.NONE);
customItem.setText("Custom Action");
customItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
MessageDialog.openInformation(shell, "Search Field", "Custom action is done here");
};
});
MenuItem sep1 = new MenuItem(menu, SWT.SEPARATOR);
MenuItem recentMenuItem = new MenuItem(menu, SWT.NONE);
recentMenuItem.setText("Search History");
SearchFieldSupport.setRecentSearchesTitle(recentMenuItem);
// for the search history
MenuItem recentsMenuItem = new MenuItem(menu, SWT.PUSH);
SearchFieldSupport.setRecentSearches(recentsMenuItem);
MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR);
MenuItem clearMenuItem = new MenuItem(menu, SWT.PUSH);
clearMenuItem.setText("Clear History");
SearchFieldSupport.setClearRecents(clearMenuItem);
searchFieldSupport.setMenu(menu);
Here is the result:
Its almost perfect except that when we clear the search history and see the menu, it looks like this:
Its because the separators are still shown even when there is no recent menu items. The hack is to set the separator as a resentSearches menu item:
MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR); SearchFieldSupport.setRecentSearchesTitle(sep2);
The Search Field also supports a custom menu item which tells that there is no recent search items. This menu item is automatically hidden when there search history is not empty. We can add that also before the second separator:
MenuItem recentsMenuItem = new MenuItem(menu, SWT.PUSH);
SearchFieldSupport.setRecentSearches(recentsMenuItem);
MenuItem noRecentMenuItem = new MenuItem(menu, SWT.NONE);
noRecentMenuItem.setText("No Search History");
SearchFieldSupport.setNoRecentSearches(noRecentMenuItem);
MenuItem sep2 = new MenuItem(menu, SWT.SEPARATOR);
SearchFieldSupport.setRecentSearchesTitle(sep2);
Now we are all set:
The last missing piece of the puzzle, the SearchFieldSupport class:
package org.eclipse.ui.cocoa.ext;
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.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.cocoa.ext.internal.NSSearchField;
/**
*
* @author Prakash G.R.
*
*/
public class SearchFieldSupport implements KeyListener, DisposeListener {
private final NSSearchField nsSearchField;
private final Text text;
public SearchFieldSupport(Text text) {
this.text = text;
text.addKeyListener(this);
text.addDisposeListener(this);
nsSearchField = new NSSearchField(text);
}
public Text getText() {
return text;
}
public String[] getRecentSearchStrings() {
return nsSearchField.getRecentSearches();
}
public void setRecentSearchStrings(String[] recentSearches) {
nsSearchField.setRecentSearches(recentSearches);
}
public void setMenu(Menu menu) {
nsSearchField.setMenu(menu);
}
public static void setNoRecentSearches(MenuItem menuItem) {
NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldNoRecentsMenuItemTag);
}
public static void setClearRecents(MenuItem menuItem) {
NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldClearRecentsMenuItemTag);
}
public static void setRecentSearchesTitle(MenuItem menuItem) {
NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldRecentsTitleMenuItemTag);
}
public static void setRecentSearches(MenuItem menuItem) {
NSSearchField.setTag(menuItem, NSSearchField.NSSearchFieldRecentsMenuItemTag);
}
public void widgetDisposed(DisposeEvent e) {
text.removeKeyListener(this);
}
public void keyPressed(KeyEvent e) {
// do nothing
}
public void keyReleased(KeyEvent e) {
if (e.keyCode == '\r') {
String[] recentSearchStrings = getRecentSearchStrings();
int oldSize = recentSearchStrings.length;
String[] newSearchStrings = new String[recentSearchStrings.length + 1];
System.arraycopy(recentSearchStrings, 0, newSearchStrings, 0, recentSearchStrings.length);
newSearchStrings[oldSize] = text.getText();
setRecentSearchStrings(newSearchStrings);
text.setSelection(0, text.getText().length());
}
}
}
and the custom NSSearchField class:
package org.eclipse.ui.cocoa.ext.internal;
import java.lang.reflect.Field;
import org.eclipse.swt.internal.cocoa.NSArray;
import org.eclipse.swt.internal.cocoa.NSMenu;
import org.eclipse.swt.internal.cocoa.NSMenuItem;
import org.eclipse.swt.internal.cocoa.NSMutableArray;
import org.eclipse.swt.internal.cocoa.NSString;
import org.eclipse.swt.internal.cocoa.OS;
import org.eclipse.swt.internal.cocoa.id;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Text;
/**
* @author Prakash G.R.
*
*/
@SuppressWarnings("restriction")
public class NSSearchField extends org.eclipse.swt.internal.cocoa.NSSearchField {
public static final int NSSearchFieldRecentsTitleMenuItemTag = 1000;
public static final int NSSearchFieldRecentsMenuItemTag = 1001;
public static final int NSSearchFieldClearRecentsMenuItemTag = 1002;
public static final int NSSearchFieldNoRecentsMenuItemTag = 1003;
private static final int /* long */sel_setSearchMenuTemplate = OS.sel_registerName("setSearchMenuTemplate:");
private static final int /* long */sel_setTag = OS.sel_registerName("setTag:");
private static final int /* long */sel_setRecentSearches = OS.sel_registerName("setRecentSearches:");
public NSSearchField(id id) {
super(id);
}
public NSSearchField(Text text) {
super(text.view);
}
public void setMenu(Menu menu) {
try {
Field field = Menu.class.getDeclaredField("nsMenu");
field.setAccessible(true);
NSMenu nsMenu = (NSMenu) field.get(menu);
OS.objc_msgSend(this.id, sel_setSearchMenuTemplate, nsMenu.id);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean setTag(MenuItem menuItem, int tag) {
try {
Field field = MenuItem.class.getDeclaredField("nsItem");
field.setAccessible(true);
NSMenuItem nsMenuItem = (NSMenuItem) field.get(menuItem);
OS.objc_msgSend(nsMenuItem.id, sel_setTag, tag);
// no action for titles
if (tag == NSSearchFieldRecentsTitleMenuItemTag || tag == NSSearchFieldNoRecentsMenuItemTag) {
nsMenuItem.setAction(0);
}
return true;
} catch (Exception e) {
return false;
}
}
public String[] getRecentSearches() {
NSArray recentSearches = super.recentSearches();
String[] recentSearchStrings = new String[recentSearches.count()];
for (int i = 0; i < recentSearchStrings.length; i++) {
recentSearchStrings[i] = (new NSString(recentSearches.objectAtIndex(i))).getString();
}
return recentSearchStrings;
}
public void setRecentSearches(String[] recentSearchStrings) {
NSMutableArray recentSearches = NSMutableArray.arrayWithCapacity(recentSearchStrings.length);
for (String aRecentSearcb : recentSearchStrings) {
NSString nsString = NSString.stringWith(aRecentSearcb);
recentSearches.addObject(nsString);
}
OS.objc_msgSend(this.id, sel_setRecentSearches, recentSearches.id);
}
}
Enjoy!




1 comments: