Skip to content

Commit b549e28

Browse files
committed
Draw Combo instead of using native widget
This improves cross-platform issues when the Combo is used, and also height issues. With the prior native Combo, a default platform-specific height was enforced, while now it is free to choose.
1 parent 6bc2a62 commit b549e28

File tree

3 files changed

+187
-122
lines changed

3 files changed

+187
-122
lines changed

org.csstudio.opibuilder/src/main/java/org/csstudio/opibuilder/widgets/editparts/ComboEditPart.java

Lines changed: 131 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,31 @@
99
*******************************************************************************/
1010
package org.csstudio.opibuilder.widgets.editparts;
1111

12-
import static org.csstudio.opibuilder.model.AbstractWidgetModel.PROP_BORDER_STYLE;
13-
import static org.csstudio.opibuilder.model.AbstractWidgetModel.PROP_BORDER_WIDTH;
14-
import static org.csstudio.opibuilder.model.AbstractWidgetModel.PROP_FONT;
15-
import static org.csstudio.opibuilder.model.AbstractWidgetModel.PROP_HEIGHT;
16-
import static org.csstudio.opibuilder.model.AbstractWidgetModel.PROP_WIDTH;
1712
import static org.csstudio.opibuilder.model.IPVWidgetModel.PROP_PVNAME;
1813
import static org.csstudio.opibuilder.model.IPVWidgetModel.PROP_PVVALUE;
1914
import static org.csstudio.opibuilder.widgets.model.ComboModel.PROP_ITEMS;
2015
import static org.csstudio.opibuilder.widgets.model.ComboModel.PROP_ITEMS_FROM_PV;
2116

17+
import java.util.ArrayList;
2218
import java.util.List;
2319

2420
import org.csstudio.opibuilder.editparts.AbstractPVWidgetEditPart;
2521
import org.csstudio.opibuilder.editparts.ExecutionMode;
2622
import org.csstudio.opibuilder.properties.IWidgetPropertyChangeHandler;
2723
import org.csstudio.opibuilder.widgets.figures.ComboFigure;
2824
import org.csstudio.opibuilder.widgets.model.ComboModel;
25+
import org.csstudio.swt.widgets.util.GraphicsUtil;
26+
import org.csstudio.ui.util.CustomMediaFactory;
2927
import org.eclipse.draw2d.IFigure;
30-
import org.eclipse.swt.events.SelectionAdapter;
31-
import org.eclipse.swt.events.SelectionEvent;
32-
import org.eclipse.swt.events.SelectionListener;
33-
import org.eclipse.swt.widgets.Combo;
28+
import org.eclipse.draw2d.MouseEvent;
29+
import org.eclipse.draw2d.MouseListener;
30+
import org.eclipse.draw2d.MouseMotionListener;
31+
import org.eclipse.draw2d.geometry.Point;
32+
import org.eclipse.jface.action.Action;
33+
import org.eclipse.jface.action.MenuManager;
34+
import org.eclipse.swt.graphics.RGB;
35+
import org.eclipse.swt.widgets.Display;
36+
import org.eclipse.ui.PlatformUI;
3437
import org.yamcs.studio.data.IPV;
3538
import org.yamcs.studio.data.IPVListener;
3639
import org.yamcs.studio.data.VTypeHelper;
@@ -42,46 +45,71 @@ public final class ComboEditPart extends AbstractPVWidgetEditPart {
4245

4346
private IPVListener loadItemsFromPVListener;
4447

45-
private Combo combo;
46-
private SelectionListener comboSelectionListener;
48+
private List<String> items = new ArrayList<>();
4749

4850
@Override
4951
protected IFigure doCreateFigure() {
5052
var model = getWidgetModel();
5153
updatePropSheet(model.isItemsFromPV());
52-
var comboFigure = new ComboFigure(this);
53-
combo = comboFigure.getSWTWidget();
54+
var comboFigure = new ComboFigure();
5455

5556
var items = getWidgetModel().getItems();
56-
5757
updateCombo(items);
5858

59-
markAsControlPV(PROP_PVNAME, PROP_PVVALUE);
59+
if (getExecutionMode() == ExecutionMode.RUN_MODE) {
60+
comboFigure.addMouseListener(new MouseListener() {
61+
@Override
62+
public void mouseDoubleClicked(MouseEvent me) {
63+
}
6064

61-
return comboFigure;
62-
}
65+
@Override
66+
public void mousePressed(MouseEvent me) {
67+
if (me.button == 1 && comboFigure.containsPoint(me.getLocation())) {
68+
me.consume();
69+
}
70+
}
6371

64-
private void updateCombo(List<String> items) {
65-
if (items != null && getExecutionMode() == ExecutionMode.RUN_MODE) {
66-
combo.removeAll();
72+
@Override
73+
public void mouseReleased(MouseEvent me) {
74+
// Check location to ignore bogus mouse clicks,
75+
// see https://github.com/ControlSystemStudio/cs-studio/issues/1818
76+
if (me.button == 1 && getExecutionMode().equals(ExecutionMode.RUN_MODE)
77+
&& comboFigure.containsPoint(me.getLocation())) {
78+
var cursorLocation = Display.getCurrent().getCursorLocation();
79+
showMenu(me.getLocation(), cursorLocation.x, cursorLocation.y);
80+
}
81+
}
6782

68-
for (var item : items) {
69-
combo.add(item);
83+
});
84+
}
85+
comboFigure.addMouseMotionListener(new MouseMotionListener.Stub() {
86+
@Override
87+
public void mouseEntered(MouseEvent me) {
88+
if (getExecutionMode().equals(ExecutionMode.RUN_MODE)) {
89+
var backColor = comboFigure.getBackgroundColor();
90+
var darkColor = GraphicsUtil.mixColors(backColor.getRGB(), new RGB(0, 0, 0), 0.9);
91+
comboFigure.setBackgroundColor(CustomMediaFactory.getInstance().getColor(darkColor));
92+
}
7093
}
7194

72-
// write value to pv if pv name is not empty
73-
if (getWidgetModel().getPVName().trim().length() > 0) {
74-
if (comboSelectionListener != null) {
75-
combo.removeSelectionListener(comboSelectionListener);
95+
@Override
96+
public void mouseExited(MouseEvent me) {
97+
if (getExecutionMode().equals(ExecutionMode.RUN_MODE)) {
98+
comboFigure.setBackgroundColor(
99+
CustomMediaFactory.getInstance().getColor(getWidgetModel().getBackgroundColor()));
76100
}
77-
comboSelectionListener = new SelectionAdapter() {
78-
@Override
79-
public void widgetSelected(SelectionEvent e) {
80-
setPVValue(PROP_PVNAME, combo.getText());
81-
}
82-
};
83-
combo.addSelectionListener(comboSelectionListener);
84101
}
102+
});
103+
104+
markAsControlPV(PROP_PVNAME, PROP_PVVALUE);
105+
106+
return comboFigure;
107+
}
108+
109+
private void updateCombo(List<String> items) {
110+
if (items != null && getExecutionMode() == ExecutionMode.RUN_MODE) {
111+
this.items.clear();
112+
this.items.addAll(items);
85113
}
86114
}
87115

@@ -90,6 +118,45 @@ public ComboModel getWidgetModel() {
90118
return (ComboModel) getModel();
91119
}
92120

121+
private ComboFigure getComboFigure() {
122+
return (ComboFigure) getFigure();
123+
}
124+
125+
/**
126+
* Show Menu
127+
*
128+
* @param point
129+
* the location of the mouse-event in the OPI display
130+
* @param absolutX
131+
* The x coordinate of the mouse on the monitor
132+
* @param absolutY
133+
* The y coordinate of the mouse on the monitor
134+
*/
135+
private void showMenu(Point point, int absolutX, int absolutY) {
136+
if (getExecutionMode().equals(ExecutionMode.RUN_MODE)) {
137+
var shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
138+
var menuManager = new MenuManager();
139+
for (var item : items) {
140+
menuManager.add(new SelectItemAction(item));
141+
}
142+
var menu = menuManager.createContextMenu(shell);
143+
144+
/*
145+
* We need to position the menu in absolute monitor coordinates.
146+
* First we calculate the coordinates of the display, then add the
147+
* widget coordinates to these so that the menu opens on the
148+
* bottom left of the widget.
149+
*/
150+
var x = absolutX - point.x;
151+
var y = absolutY - point.y;
152+
x += getWidgetModel().getLocation().x;
153+
y += getWidgetModel().getLocation().y + getWidgetModel().getSize().height;
154+
155+
menu.setLocation(x, y);
156+
menu.setVisible(true);
157+
}
158+
}
159+
93160
@Override
94161
protected void doActivate() {
95162
super.doActivate();
@@ -129,7 +196,6 @@ protected void doDeActivate() {
129196
pv.removeListener(loadItemsFromPVListener);
130197
}
131198
}
132-
// ((ComboFigure)getFigure()).dispose();
133199
}
134200

135201
@Override
@@ -140,14 +206,12 @@ protected void registerPropertyChangeHandlers() {
140206
return false;
141207
});
142208

143-
autoSizeWidget((ComboFigure) getFigure());
144-
145209
setPropertyChangeHandler(PROP_PVVALUE, (oldValue, newValue, refreshableFigure) -> {
146210
if (newValue != null) {
147211
var stringValue = VTypeHelper.getString((VType) newValue);
148212

149213
String matchingItem = null;
150-
for (var item : combo.getItems()) {
214+
for (var item : items) {
151215
if (item.equals(stringValue)) {
152216
matchingItem = item;
153217
break;
@@ -172,9 +236,9 @@ protected void registerPropertyChangeHandlers() {
172236
}
173237

174238
if (matchingItem != null) {
175-
combo.setText(matchingItem);
239+
getComboFigure().setText(matchingItem);
176240
} else {
177-
combo.deselectAll();
241+
getComboFigure().setText(null);
178242
}
179243
}
180244

@@ -185,7 +249,7 @@ protected void registerPropertyChangeHandlers() {
185249
if (newValue != null && newValue instanceof List) {
186250
updateCombo((List<String>) newValue);
187251
if (getWidgetModel().isItemsFromPV()) {
188-
combo.setText(VTypeHelper.getString(getPVValue(PROP_PVNAME)));
252+
getComboFigure().setText(VTypeHelper.getString(getPVValue(PROP_PVNAME)));
189253
}
190254
}
191255
return true;
@@ -197,41 +261,49 @@ protected void registerPropertyChangeHandlers() {
197261
};
198262
getWidgetModel().getProperty(PROP_ITEMS_FROM_PV).addPropertyChangeListener(
199263
evt -> handler.handleChange(evt.getOldValue(), evt.getNewValue(), getFigure()));
200-
201-
// size change handlers--always apply the default height
202-
IWidgetPropertyChangeHandler handle = (oldValue, newValue, figure) -> {
203-
autoSizeWidget((ComboFigure) figure);
204-
return true;
205-
};
206-
setPropertyChangeHandler(PROP_WIDTH, handle);
207-
setPropertyChangeHandler(PROP_HEIGHT, handle);
208-
setPropertyChangeHandler(PROP_BORDER_STYLE, handle);
209-
setPropertyChangeHandler(PROP_BORDER_WIDTH, handle);
210-
setPropertyChangeHandler(PROP_FONT, handle);
211264
}
212265

213266
private void updatePropSheet(boolean itemsFromPV) {
214267
getWidgetModel().setPropertyVisible(PROP_ITEMS, !itemsFromPV);
215268
}
216269

217-
private void autoSizeWidget(ComboFigure comboFigure) {
218-
var d = comboFigure.getAutoSizeDimension();
219-
getWidgetModel().setSize(getWidgetModel().getWidth(), d.height);
220-
}
221-
222270
@Override
223271
public String getValue() {
224-
return combo.getText();
272+
return getComboFigure().getText();
225273
}
226274

227275
@Override
228276
public void setValue(Object value) {
229277
if (value instanceof String) {
230-
combo.setText((String) value);
278+
getComboFigure().setText((String) value);
231279
} else if (value instanceof Number) {
232-
combo.select(((Number) value).intValue());
280+
var idx = ((Number) value).intValue();
281+
if (idx >= 0 && idx <= items.size() - 1) {
282+
var item = items.get(idx);
283+
getComboFigure().setText(item);
284+
}
233285
} else {
234286
super.setValue(value);
235287
}
236288
}
289+
290+
private class SelectItemAction extends Action {
291+
292+
private String item;
293+
294+
SelectItemAction(String item) {
295+
this.item = item;
296+
setText(item);
297+
}
298+
299+
@Override
300+
public void run() {
301+
getComboFigure().setText(item);
302+
303+
// Write value to pv if pv name is not empty
304+
if (getWidgetModel().getPVName().trim().length() > 0) {
305+
setPVValue(PROP_PVNAME, item);
306+
}
307+
}
308+
}
237309
}

0 commit comments

Comments
 (0)