Skip to content

Commit 2b7cef7

Browse files
committed
Added a fluent builder for Actions.
1 parent 5648d2d commit 2b7cef7

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* Copyright 2015 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.common.swt.jface;
17+
18+
import java.util.Arrays;
19+
import java.util.Locale;
20+
21+
import org.eclipse.jface.action.Action;
22+
import org.eclipse.jface.action.IAction;
23+
import org.eclipse.jface.resource.ImageDescriptor;
24+
import org.eclipse.swt.SWT;
25+
26+
import com.google.common.base.Preconditions;
27+
import com.google.common.util.concurrent.Runnables;
28+
29+
import com.diffplug.common.swt.SwtMisc;
30+
31+
/** Provides a fluent interface for constructing actions. */
32+
public class Actions {
33+
/** Style enum for various kinds of IAction. */
34+
public enum Style {
35+
// @formatter:off
36+
PUSH(IAction.AS_PUSH_BUTTON),
37+
CHECK(IAction.AS_CHECK_BOX),
38+
RADIO(IAction.AS_RADIO_BUTTON),
39+
MENU(IAction.AS_DROP_DOWN_MENU);
40+
// @formatter:on
41+
42+
public final int jfaceStyle;
43+
44+
private Style(int jfaceStyle) {
45+
this.jfaceStyle = jfaceStyle;
46+
}
47+
48+
/** Returns the Style of the given IAction. */
49+
public static Style of(IAction action) {
50+
return Arrays.asList(Style.values()).stream()
51+
.filter(style -> style.jfaceStyle == action.getStyle())
52+
.findFirst().get();
53+
}
54+
}
55+
56+
/** Concise method for creating a push action. */
57+
public static IAction create(String text, Runnable action) {
58+
return builder().setText(text).setRunnable(action).build();
59+
}
60+
61+
/** Concise method for creating a push action. */
62+
public static IAction create(String text, ImageDescriptor image, Runnable action) {
63+
return builder().setText(text).setImage(image).setRunnable(action).build();
64+
}
65+
66+
/** Concise method for creating a push action. */
67+
public static IAction create(String text, ImageDescriptor image, int accelerator, Runnable action) {
68+
return builder().setText(text).setImage(image).setAccelerator(accelerator).setRunnable(action).build();
69+
}
70+
71+
/** Defaults to a push style with no text, accelerator, or action. */
72+
public static Actions builder() {
73+
return new Actions();
74+
}
75+
76+
/** Action builder that starts with all default values. */
77+
private Actions() {}
78+
79+
private Style style = Style.PUSH;
80+
private String text = "";
81+
private String tooltip = "";
82+
private Runnable run = Runnables.doNothing();
83+
private int accelerator = SWT.NONE;
84+
private ImageDescriptor img = null;
85+
86+
/** Copies all behavior from the given action. */
87+
public static Actions builderCopy(IAction action) {
88+
return new Actions(action);
89+
}
90+
91+
private Actions(IAction action) {
92+
this.text = action.getText();
93+
this.style = Style.of(action);
94+
this.run = action::run;
95+
this.img = action.getImageDescriptor();
96+
this.accelerator = action.getAccelerator();
97+
this.tooltip = action.getToolTipText();
98+
99+
if (accelerator != SWT.NONE) {
100+
// the toolTip might have had an accelerator added,
101+
// which we'll want to strip so it doesn't get doubled
102+
String hint = getAcceleratorHint(accelerator);
103+
if (tooltip.endsWith(hint)) {
104+
tooltip = tooltip.substring(0, tooltip.length() - hint.length());
105+
}
106+
}
107+
}
108+
109+
/** Sets the text and tooltip. */
110+
public Actions setText(String text) {
111+
this.text = text;
112+
this.tooltip = text;
113+
return this;
114+
}
115+
116+
/** Sets the tooltip. */
117+
public Actions setTooltip(String tooltip) {
118+
this.tooltip = tooltip;
119+
return this;
120+
}
121+
122+
/** Sets the style. */
123+
public Actions setStyle(Style style) {
124+
this.style = style;
125+
return this;
126+
}
127+
128+
/** Sets the runnable. */
129+
public Actions setRunnable(Runnable run) {
130+
this.run = run;
131+
return this;
132+
}
133+
134+
/** Sets the keyboard accelerator. */
135+
public Actions setAccelerator(int accelerator) {
136+
this.accelerator = accelerator;
137+
return this;
138+
}
139+
140+
/** Sets the runnable. */
141+
public Actions setImage(ImageDescriptor img) {
142+
this.img = img;
143+
return this;
144+
}
145+
146+
/** Returns an action with the specified properties. */
147+
public IAction build() {
148+
ActionImp action = new ActionImp(text, style.jfaceStyle, run);
149+
action.setImageDescriptor(img);
150+
action.setAccelerator(accelerator);
151+
setToolTipAccelAware(action, tooltip);
152+
return action;
153+
}
154+
155+
/** A trivial Action class which delegates its run() method to a Runnable. */
156+
private static class ActionImp extends Action {
157+
private final Runnable run;
158+
159+
private ActionImp(String text, int style, Runnable run) {
160+
super(text, style);
161+
this.run = run;
162+
}
163+
164+
@Override
165+
public void run() {
166+
run.run();
167+
}
168+
}
169+
170+
/** Sets the tooltip text for the given action while remaing aware of its accelerator. */
171+
public static void setToolTipAccelAware(IAction action, String tooltip) {
172+
if (action.getAccelerator() == SWT.NONE) {
173+
action.setToolTipText(tooltip);
174+
} else {
175+
action.setToolTipText(tooltip + getAcceleratorHint(action.getAccelerator()));
176+
}
177+
}
178+
179+
/** Returns a human-readable hint about the accelerator. Must not be passed NO_ACCELERATOR. */
180+
private static String getAcceleratorHint(int accelerator) {
181+
Preconditions.checkArgument(accelerator != SWT.NONE);
182+
return " [" + Actions.getAcceleratorString(accelerator) + "]";
183+
}
184+
185+
/** Returns the given key accelerator as a string (including shift, control, command, etc). */
186+
public static String getAcceleratorString(int accelerator) {
187+
if (accelerator == SWT.NONE) {
188+
return "<none>";
189+
}
190+
191+
StringBuilder builder = new StringBuilder(16);
192+
193+
if (SwtMisc.flagIsSet(SWT.CTRL, accelerator)) {
194+
builder.append("Ctrl ");
195+
accelerator -= SWT.CTRL;
196+
}
197+
198+
if (SwtMisc.flagIsSet(SWT.COMMAND, accelerator)) {
199+
builder.append(UC_CMD + " ");
200+
accelerator -= SWT.COMMAND;
201+
}
202+
203+
if (SwtMisc.flagIsSet(SWT.ALT, accelerator)) {
204+
builder.append("Alt ");
205+
accelerator -= SWT.ALT;
206+
}
207+
208+
if (SwtMisc.flagIsSet(SWT.SHIFT, accelerator)) {
209+
builder.append("Shift ");
210+
accelerator -= SWT.SHIFT;
211+
}
212+
213+
final String end;
214+
215+
if (SWT.F1 <= accelerator && accelerator <= SWT.F20) {
216+
int num = 1 + accelerator - SWT.F1;
217+
end = "F" + Integer.toString(num);
218+
} else {
219+
switch (accelerator) {
220+
case SWT.ARROW_UP:
221+
end = UC_ARROW_UP;
222+
break;
223+
case SWT.ARROW_DOWN:
224+
end = UC_ARROW_DOWN;
225+
break;
226+
case SWT.ARROW_LEFT:
227+
end = UC_ARROW_LEFT;
228+
break;
229+
case SWT.ARROW_RIGHT:
230+
end = UC_ARROW_RIGHT;
231+
break;
232+
case SWT.ESC:
233+
end = "Esc";
234+
break;
235+
default:
236+
end = Character.toString((char) accelerator).toUpperCase(Locale.getDefault());
237+
break;
238+
}
239+
}
240+
builder.append(end);
241+
242+
return builder.toString();
243+
}
244+
245+
/** Unicode for arrows. */
246+
public static final String UC_ARROW_UP = "\u2191";
247+
public static final String UC_ARROW_DOWN = "\u2193";
248+
public static final String UC_ARROW_LEFT = "\u2190";
249+
public static final String UC_ARROW_RIGHT = "\u2192";
250+
/** Unicode for the Mac cmd icon. */
251+
public static final String UC_CMD = "\u2318";
252+
253+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2015 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.common.swt.jface;
17+
18+
import org.eclipse.jface.action.IAction;
19+
import org.eclipse.swt.SWT;
20+
import org.junit.Assert;
21+
import org.junit.Test;
22+
23+
import com.diffplug.common.base.Box;
24+
25+
public class ActionsTest {
26+
@Test
27+
public void testCopy() {
28+
// we'll set this variable to show that it's running as expected
29+
Box.Nullable<String> toSet = Box.Nullable.ofNull();
30+
31+
// create an action
32+
IAction action = Actions.builder()
33+
.setText("Name")
34+
.setTooltip("Tooltip")
35+
.setAccelerator(SWT.SHIFT | 'a')
36+
.setRunnable(() -> toSet.set("WasRun")).build();
37+
38+
// make sure it's doing what we expect
39+
Assert.assertEquals("Name", action.getText());
40+
Assert.assertEquals("Tooltip [Shift A]", action.getToolTipText());
41+
Assert.assertEquals(SWT.SHIFT | 'a', action.getAccelerator());
42+
Assert.assertEquals(null, toSet.get());
43+
action.run();
44+
Assert.assertEquals("WasRun", toSet.get());
45+
46+
// copy that action
47+
IAction copy = Actions.builderCopy(action).setAccelerator(SWT.NONE)
48+
.setRunnable(() -> toSet.set("CopyWasRun")).build();
49+
50+
Assert.assertEquals(SWT.NONE, copy.getAccelerator());
51+
// test that the tooltip was stripped correctly in the copy
52+
Assert.assertEquals("Tooltip", copy.getToolTipText());
53+
// make sure that the runnable took
54+
copy.run();
55+
Assert.assertEquals("CopyWasRun", toSet.get());
56+
// but didn't screw up the other one
57+
action.run();
58+
Assert.assertEquals("WasRun", toSet.get());
59+
}
60+
61+
@Test
62+
public void testAcceleratorString() {
63+
testCase("X", 'x');
64+
testCase("1", '1');
65+
66+
testCase("F5", SWT.F5);
67+
testCase("F12", SWT.F12);
68+
69+
testCase(Actions.UC_ARROW_LEFT, SWT.ARROW_LEFT);
70+
testCase(Actions.UC_ARROW_RIGHT, SWT.ARROW_RIGHT);
71+
testCase(Actions.UC_ARROW_UP, SWT.ARROW_UP);
72+
testCase(Actions.UC_ARROW_DOWN, SWT.ARROW_DOWN);
73+
74+
testCase("<none>", SWT.NONE);
75+
testCase("Esc", SWT.ESC);
76+
77+
testCase("Ctrl X", SWT.CONTROL | 'x');
78+
testCase("Shift 1", SWT.SHIFT | '1');
79+
testCase(Actions.UC_CMD + " .", SWT.COMMAND | '.');
80+
testCase("Alt =", SWT.ALT | '=');
81+
82+
testCase("Ctrl Shift X", SWT.CONTROL | SWT.SHIFT | 'x');
83+
testCase("Alt Shift 1", SWT.ALT | SWT.SHIFT | '1');
84+
}
85+
86+
private void testCase(String expected, int accelerator) {
87+
Assert.assertEquals(expected, Actions.getAcceleratorString(accelerator));
88+
}
89+
}

0 commit comments

Comments
 (0)