|
Lines 10-169
Link Here
|
| 10 |
*******************************************************************************/ |
10 |
*******************************************************************************/ |
| 11 |
package org.eclipse.e4.ui.workbench.renderers.swt; |
11 |
package org.eclipse.e4.ui.workbench.renderers.swt; |
| 12 |
|
12 |
|
| 13 |
import java.util.ArrayList; |
|
|
| 14 |
import java.util.HashMap; |
| 15 |
import java.util.List; |
| 16 |
import java.util.Map; |
| 17 |
import javax.annotation.PostConstruct; |
13 |
import javax.annotation.PostConstruct; |
| 18 |
import javax.annotation.PreDestroy; |
14 |
import javax.annotation.PreDestroy; |
| 19 |
import javax.inject.Inject; |
15 |
import javax.inject.Inject; |
| 20 |
import org.eclipse.e4.core.services.events.IEventBroker; |
16 |
import org.eclipse.e4.core.services.events.IEventBroker; |
|
|
17 |
import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; |
| 21 |
import org.eclipse.e4.ui.model.application.ui.MElementContainer; |
18 |
import org.eclipse.e4.ui.model.application.ui.MElementContainer; |
| 22 |
import org.eclipse.e4.ui.model.application.ui.MUIElement; |
19 |
import org.eclipse.e4.ui.model.application.ui.MUIElement; |
|
|
20 |
import org.eclipse.e4.ui.model.application.ui.advanced.MArea; |
| 23 |
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer; |
21 |
import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer; |
| 24 |
import org.eclipse.e4.ui.workbench.UIEvents; |
22 |
import org.eclipse.e4.ui.workbench.UIEvents; |
| 25 |
import org.eclipse.swt.SWT; |
23 |
import org.eclipse.swt.SWT; |
| 26 |
import org.eclipse.swt.custom.SashForm; |
24 |
import org.eclipse.swt.custom.SashForm; |
| 27 |
import org.eclipse.swt.events.ControlEvent; |
25 |
import org.eclipse.swt.graphics.Rectangle; |
| 28 |
import org.eclipse.swt.events.ControlListener; |
|
|
| 29 |
import org.eclipse.swt.events.DisposeEvent; |
| 30 |
import org.eclipse.swt.events.DisposeListener; |
| 31 |
import org.eclipse.swt.widgets.Composite; |
| 32 |
import org.eclipse.swt.widgets.Control; |
26 |
import org.eclipse.swt.widgets.Control; |
| 33 |
import org.eclipse.swt.widgets.Widget; |
|
|
| 34 |
import org.osgi.service.event.Event; |
27 |
import org.osgi.service.event.Event; |
| 35 |
import org.osgi.service.event.EventHandler; |
28 |
import org.osgi.service.event.EventHandler; |
| 36 |
|
29 |
|
| 37 |
public class SashRenderer extends SWTPartRenderer { |
30 |
public class SashRenderer extends SWTPartRenderer { |
| 38 |
|
31 |
|
| 39 |
private ControlListener resizeListener = new ControlListener() { |
|
|
| 40 |
public void controlMoved(ControlEvent e) { |
| 41 |
} |
| 42 |
|
| 43 |
public void controlResized(ControlEvent e) { |
| 44 |
Control ctrl = (Control) e.widget; |
| 45 |
if (ctrl.isDisposed() || !(ctrl.getParent() instanceof SashForm)) |
| 46 |
return; |
| 47 |
|
| 48 |
SashForm sf = (SashForm) ctrl.getParent(); |
| 49 |
if (weightsChanged(sf)) { |
| 50 |
// Cache the new values |
| 51 |
weightsMap.put(sf, sf.getWeights()); |
| 52 |
addSashToUpdate(sf); |
| 53 |
} |
| 54 |
} |
| 55 |
|
| 56 |
// determine if the weights in the SashForm have actually changed |
| 57 |
private boolean weightsChanged(SashForm sf) { |
| 58 |
int[] oldW = (int[]) weightsMap.get(sf); |
| 59 |
int[] newW = sf.getWeights(); |
| 60 |
if (oldW == null || oldW.length != newW.length) |
| 61 |
return true; |
| 62 |
for (int j = 0; j < oldW.length; j++) { |
| 63 |
if (oldW[j] != newW[j]) |
| 64 |
return true; |
| 65 |
} |
| 66 |
return false; |
| 67 |
} |
| 68 |
}; |
| 69 |
|
| 70 |
private class SashUpdateJob implements Runnable { |
| 71 |
public List<SashForm> sashesToUpdate = new ArrayList<SashForm>(); |
| 72 |
|
| 73 |
public void run() { |
| 74 |
clearSashUpdate(); |
| 75 |
while (!sashesToUpdate.isEmpty()) { |
| 76 |
SashForm sf = sashesToUpdate.remove(0); |
| 77 |
if (sf.isDisposed()) |
| 78 |
continue; |
| 79 |
|
| 80 |
// prevent recursive updating |
| 81 |
ignoreWeightUpdates = true; |
| 82 |
synchModelToSash(sf); |
| 83 |
ignoreWeightUpdates = false; |
| 84 |
} |
| 85 |
} |
| 86 |
} |
| 87 |
|
| 88 |
private void addSashToUpdate(SashForm sf) { |
| 89 |
MElementContainer<MUIElement> psc = (MElementContainer<MUIElement>) sf |
| 90 |
.getData(OWNING_ME); |
| 91 |
if (modelUpdateJob != null |
| 92 |
&& modelUpdateJob.sashModelsToUpdate.contains(psc)) { |
| 93 |
return; |
| 94 |
} |
| 95 |
if (sashUpdateJob == null) { |
| 96 |
sashUpdateJob = new SashUpdateJob(); |
| 97 |
sashUpdateJob.sashesToUpdate.add(sf); |
| 98 |
sf.getDisplay().asyncExec(sashUpdateJob); |
| 99 |
} else { |
| 100 |
if (!sashUpdateJob.sashesToUpdate.contains(sf)) |
| 101 |
sashUpdateJob.sashesToUpdate.add(sf); |
| 102 |
} |
| 103 |
} |
| 104 |
|
| 105 |
private class ModelUpdateJob implements Runnable { |
| 106 |
public List<MElementContainer<MUIElement>> sashModelsToUpdate = new ArrayList<MElementContainer<MUIElement>>(); |
| 107 |
|
| 108 |
public void run() { |
| 109 |
clearModelUpdate(); |
| 110 |
while (!sashModelsToUpdate.isEmpty()) { |
| 111 |
MElementContainer<MUIElement> psc = sashModelsToUpdate |
| 112 |
.remove(0); |
| 113 |
|
| 114 |
// prevent recursive updating |
| 115 |
ignoreWeightUpdates = true; |
| 116 |
synchSashToModel(psc); |
| 117 |
ignoreWeightUpdates = false; |
| 118 |
} |
| 119 |
} |
| 120 |
} |
| 121 |
|
| 122 |
private void addModelToUpdate(MElementContainer<MUIElement> pscModel) { |
| 123 |
Control sf = (Control) pscModel.getWidget(); |
| 124 |
if (sf == null || sf.isDisposed()) |
| 125 |
return; |
| 126 |
|
| 127 |
if (modelUpdateJob == null) { |
| 128 |
modelUpdateJob = new ModelUpdateJob(); |
| 129 |
modelUpdateJob.sashModelsToUpdate.add(pscModel); |
| 130 |
sf.getDisplay().asyncExec(modelUpdateJob); |
| 131 |
} else { |
| 132 |
if (!modelUpdateJob.sashModelsToUpdate.contains(pscModel)) |
| 133 |
modelUpdateJob.sashModelsToUpdate.add(pscModel); |
| 134 |
} |
| 135 |
} |
| 136 |
|
| 137 |
@Inject |
32 |
@Inject |
| 138 |
private IEventBroker eventBroker; |
33 |
private IEventBroker eventBroker; |
| 139 |
|
34 |
|
| 140 |
private static final int UNDEFINED_WEIGHT = -1; |
35 |
private static final int UNDEFINED_WEIGHT = -1; |
| 141 |
private static final int DEFAULT_WEIGHT = 100; |
36 |
private static final int DEFAULT_WEIGHT = 100; |
| 142 |
|
37 |
|
| 143 |
private Map weightsMap = new HashMap(); |
|
|
| 144 |
|
| 145 |
private EventHandler sashOrientationHandler; |
38 |
private EventHandler sashOrientationHandler; |
| 146 |
private EventHandler sashWeightHandler; |
39 |
private EventHandler sashWeightHandler; |
| 147 |
|
|
|
| 148 |
ModelUpdateJob modelUpdateJob; |
| 149 |
SashUpdateJob sashUpdateJob; |
| 150 |
|
| 151 |
protected boolean ignoreWeightUpdates = false; |
| 152 |
|
| 153 |
private EventHandler visibilityHandler; |
40 |
private EventHandler visibilityHandler; |
| 154 |
|
41 |
|
| 155 |
public SashRenderer() { |
42 |
public SashRenderer() { |
| 156 |
super(); |
43 |
super(); |
| 157 |
} |
44 |
} |
| 158 |
|
45 |
|
| 159 |
void clearModelUpdate() { |
|
|
| 160 |
modelUpdateJob = null; |
| 161 |
} |
| 162 |
|
| 163 |
void clearSashUpdate() { |
| 164 |
sashUpdateJob = null; |
| 165 |
} |
| 166 |
|
| 167 |
@PostConstruct |
46 |
@PostConstruct |
| 168 |
void postConstruct() { |
47 |
void postConstruct() { |
| 169 |
sashOrientationHandler = new EventHandler() { |
48 |
sashOrientationHandler = new EventHandler() { |
|
Lines 191-199
Link Here
|
| 191 |
|
70 |
|
| 192 |
sashWeightHandler = new EventHandler() { |
71 |
sashWeightHandler = new EventHandler() { |
| 193 |
public void handleEvent(Event event) { |
72 |
public void handleEvent(Event event) { |
| 194 |
if (ignoreWeightUpdates) |
|
|
| 195 |
return; |
| 196 |
|
| 197 |
// Ensure that this event is for a MPartSashContainer |
73 |
// Ensure that this event is for a MPartSashContainer |
| 198 |
MUIElement element = (MUIElement) event |
74 |
MUIElement element = (MUIElement) event |
| 199 |
.getProperty(UIEvents.EventTags.ELEMENT); |
75 |
.getProperty(UIEvents.EventTags.ELEMENT); |
|
Lines 205-211
Link Here
|
| 205 |
MElementContainer<MUIElement> pscModel = (MElementContainer<MUIElement>) parent; |
81 |
MElementContainer<MUIElement> pscModel = (MElementContainer<MUIElement>) parent; |
| 206 |
if (UIEvents.UIElement.CONTAINERDATA.equals(event |
82 |
if (UIEvents.UIElement.CONTAINERDATA.equals(event |
| 207 |
.getProperty(UIEvents.EventTags.ATTNAME))) { |
83 |
.getProperty(UIEvents.EventTags.ATTNAME))) { |
| 208 |
addModelToUpdate(pscModel); |
84 |
forceLayout(pscModel); |
| 209 |
} |
85 |
} |
| 210 |
} |
86 |
} |
| 211 |
}; |
87 |
}; |
|
Lines 223-230
Link Here
|
| 223 |
MElementContainer<MUIElement> pscModel = (MElementContainer<MUIElement>) parent; |
99 |
MElementContainer<MUIElement> pscModel = (MElementContainer<MUIElement>) parent; |
| 224 |
if (UIEvents.UIElement.VISIBLE.equals(event |
100 |
if (UIEvents.UIElement.VISIBLE.equals(event |
| 225 |
.getProperty(UIEvents.EventTags.ATTNAME))) { |
101 |
.getProperty(UIEvents.EventTags.ATTNAME))) { |
| 226 |
if (element.isVisible()) |
102 |
if (element.isVisible()) { |
| 227 |
addModelToUpdate(pscModel); |
103 |
forceLayout(pscModel); |
|
|
104 |
} |
| 228 |
} |
105 |
} |
| 229 |
} |
106 |
} |
| 230 |
}; |
107 |
}; |
|
Lines 233-238
Link Here
|
| 233 |
UIEvents.UIElement.VISIBLE), visibilityHandler); |
110 |
UIEvents.UIElement.VISIBLE), visibilityHandler); |
| 234 |
} |
111 |
} |
| 235 |
|
112 |
|
|
|
113 |
/** |
| 114 |
* @param pscModel |
| 115 |
*/ |
| 116 |
protected void forceLayout(MElementContainer<MUIElement> pscModel) { |
| 117 |
// layout the containing Composite |
| 118 |
while (!(pscModel.getWidget() instanceof Control)) |
| 119 |
pscModel = pscModel.getParent(); |
| 120 |
Control ctrl = (Control) pscModel.getWidget(); |
| 121 |
ctrl.getParent().layout(true, true); |
| 122 |
} |
| 123 |
|
| 236 |
@PreDestroy |
124 |
@PreDestroy |
| 237 |
void preDestroy() { |
125 |
void preDestroy() { |
| 238 |
eventBroker.unsubscribe(sashOrientationHandler); |
126 |
eventBroker.unsubscribe(sashOrientationHandler); |
|
Lines 240-260
Link Here
|
| 240 |
eventBroker.unsubscribe(visibilityHandler); |
128 |
eventBroker.unsubscribe(visibilityHandler); |
| 241 |
} |
129 |
} |
| 242 |
|
130 |
|
| 243 |
public Widget createWidget(MUIElement element, Object parent) { |
131 |
public Object createWidget(MUIElement element, Object parent) { |
| 244 |
if (!(element instanceof MPartSashContainer) |
132 |
Rectangle newRect = new Rectangle(0, 0, 0, 0); |
| 245 |
|| !(parent instanceof Composite)) |
133 |
return newRect; |
| 246 |
return null; |
|
|
| 247 |
|
| 248 |
Widget parentWidget = (Widget) parent; |
| 249 |
|
| 250 |
MPartSashContainer psc = (MPartSashContainer) element; |
| 251 |
int orientation = psc.isHorizontal() ? SWT.HORIZONTAL : SWT.VERTICAL; |
| 252 |
SashForm newSash = new SashForm((Composite) parentWidget, SWT.SMOOTH |
| 253 |
| orientation); |
| 254 |
bindWidget(element, newSash); |
| 255 |
newSash.setVisible(true); |
| 256 |
|
| 257 |
return newSash; |
| 258 |
} |
134 |
} |
| 259 |
|
135 |
|
| 260 |
/* |
136 |
/* |
|
Lines 278-304
Link Here
|
| 278 |
if (weight == UNDEFINED_WEIGHT) { |
154 |
if (weight == UNDEFINED_WEIGHT) { |
| 279 |
element.setContainerData(Integer.toString(DEFAULT_WEIGHT)); |
155 |
element.setContainerData(Integer.toString(DEFAULT_WEIGHT)); |
| 280 |
} |
156 |
} |
|
|
157 |
} |
| 281 |
|
158 |
|
| 282 |
// Ensure the Z-order of the contained controls matches the model order |
159 |
/* |
| 283 |
final MElementContainer<MUIElement> psc = parentElement; |
160 |
* (non-Javadoc) |
| 284 |
for (MUIElement part : psc.getChildren()) { |
161 |
* |
| 285 |
Control partCtrl = (Control) part.getWidget(); |
162 |
* @see |
| 286 |
if (partCtrl != null) { |
163 |
* org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer#getUIContainer |
| 287 |
partCtrl.moveBelow(null); |
164 |
* (org.eclipse.e4.ui.model.application.ui.MUIElement) |
| 288 |
} |
165 |
*/ |
|
|
166 |
@Override |
| 167 |
public Object getUIContainer(MUIElement element) { |
| 168 |
// OK, find the first parent that is *not* a sash container |
| 169 |
MUIElement parentElement = element.getParent(); |
| 170 |
MUIElement prevParent = null; |
| 171 |
while (parentElement instanceof MPartSashContainer |
| 172 |
&& !(parentElement instanceof MArea)) { |
| 173 |
prevParent = parentElement; |
| 174 |
parentElement = parentElement.getParent(); |
| 175 |
} |
| 176 |
|
| 177 |
if (parentElement.getRenderer() instanceof AbstractPartRenderer) { |
| 178 |
AbstractPartRenderer renderer = (AbstractPartRenderer) parentElement |
| 179 |
.getRenderer(); |
| 180 |
return renderer.getUIContainer(prevParent); |
| 289 |
} |
181 |
} |
| 290 |
|
182 |
return null; |
| 291 |
// Set up the size listeners |
|
|
| 292 |
Control newCtrl = (Control) element.getWidget(); |
| 293 |
newCtrl.addControlListener(resizeListener); |
| 294 |
newCtrl.addDisposeListener(new DisposeListener() { |
| 295 |
public void widgetDisposed(DisposeEvent e) { |
| 296 |
((Control) e.widget).removeControlListener(resizeListener); |
| 297 |
} |
| 298 |
}); |
| 299 |
|
| 300 |
// synch this sash after the dust settles |
| 301 |
addModelToUpdate(psc); |
| 302 |
} |
183 |
} |
| 303 |
|
184 |
|
| 304 |
/* |
185 |
/* |
|
Lines 314-338
Link Here
|
| 314 |
MUIElement child) { |
195 |
MUIElement child) { |
| 315 |
super.hideChild(parentElement, child); |
196 |
super.hideChild(parentElement, child); |
| 316 |
|
197 |
|
| 317 |
// synch this sash after the dust settles |
|
|
| 318 |
addModelToUpdate(parentElement); |
| 319 |
} |
| 320 |
|
| 321 |
public void postProcess(MUIElement element) { |
| 322 |
if (!(element instanceof MPartSashContainer)) |
| 323 |
return; |
| 324 |
|
| 325 |
MElementContainer<MUIElement> psc = (MElementContainer<MUIElement>) element; |
| 326 |
final SashForm sashForm = (SashForm) psc.getWidget(); |
| 327 |
|
| 328 |
synchSashToModel(psc); |
| 329 |
|
| 330 |
// Clean up the cache entry on dispose |
| 331 |
sashForm.addDisposeListener(new DisposeListener() { |
| 332 |
public void widgetDisposed(DisposeEvent e) { |
| 333 |
weightsMap.remove(e.widget); |
| 334 |
} |
| 335 |
}); |
| 336 |
} |
198 |
} |
| 337 |
|
199 |
|
| 338 |
/** |
200 |
/** |
|
Lines 353-448
Link Here
|
| 353 |
return UNDEFINED_WEIGHT; |
215 |
return UNDEFINED_WEIGHT; |
| 354 |
} |
216 |
} |
| 355 |
} |
217 |
} |
| 356 |
|
|
|
| 357 |
private static int[] getModelWeights(MElementContainer<MUIElement> psc) { |
| 358 |
int count = 0; |
| 359 |
for (MUIElement element : psc.getChildren()) { |
| 360 |
if (element.getWidget() != null && element.isVisible()) |
| 361 |
count++; |
| 362 |
} |
| 363 |
|
| 364 |
int[] modelWeights = new int[count]; |
| 365 |
int index = 0; |
| 366 |
for (MUIElement element : psc.getChildren()) { |
| 367 |
if (element.getWidget() != null && element.isVisible()) |
| 368 |
modelWeights[index++] = getWeight(element); |
| 369 |
} |
| 370 |
return modelWeights; |
| 371 |
} |
| 372 |
|
| 373 |
private static MUIElement[] getModelElements(SashForm sf) { |
| 374 |
Object me = sf.getData(OWNING_ME); |
| 375 |
if (!(me instanceof MPartSashContainer)) |
| 376 |
return null; |
| 377 |
|
| 378 |
MPartSashContainer psc = (MPartSashContainer) me; |
| 379 |
List<MUIElement> modelElements = new ArrayList<MUIElement>(); |
| 380 |
for (MUIElement element : psc.getChildren()) { |
| 381 |
if (element.getWidget() != null && element.isVisible()) |
| 382 |
modelElements.add(element); |
| 383 |
} |
| 384 |
|
| 385 |
return modelElements.toArray(new MUIElement[modelElements.size()]); |
| 386 |
} |
| 387 |
|
| 388 |
/** |
| 389 |
* @param psc |
| 390 |
*/ |
| 391 |
private void synchSashToModel(MElementContainer<MUIElement> psc) { |
| 392 |
if (!(psc.getWidget() instanceof SashForm)) |
| 393 |
return; |
| 394 |
|
| 395 |
SashForm sf = (SashForm) psc.getWidget(); |
| 396 |
if (sf.isDisposed()) |
| 397 |
return; |
| 398 |
|
| 399 |
int[] curWeights = sf.getWeights(); |
| 400 |
assert (curWeights.length > 0); |
| 401 |
int[] newWeights = getModelWeights(psc); |
| 402 |
|
| 403 |
// Put the new weights in the map first |
| 404 |
if (newWeights.length > 0) |
| 405 |
sf.setWeights(newWeights); |
| 406 |
sf.layout(); |
| 407 |
weightsMap.put(sf, newWeights); |
| 408 |
curWeights = sf.getWeights(); |
| 409 |
} |
| 410 |
|
| 411 |
/** |
| 412 |
* @param psc |
| 413 |
*/ |
| 414 |
private void synchModelToSash(SashForm sf) { |
| 415 |
// Calculate the total amount of 'containerData' weights for the visible |
| 416 |
// components |
| 417 |
MUIElement[] elements = SashRenderer.getModelElements(sf); |
| 418 |
if (elements.length <= 1) |
| 419 |
return; |
| 420 |
|
| 421 |
int totalModelWeight = 0; |
| 422 |
for (MUIElement element : elements) { |
| 423 |
totalModelWeight += getWeight(element); |
| 424 |
} |
| 425 |
|
| 426 |
int[] w = sf.getWeights(); |
| 427 |
int totalSashWeight = 0; |
| 428 |
for (int weight : w) { |
| 429 |
totalSashWeight += weight; |
| 430 |
} |
| 431 |
|
| 432 |
// Ensure that the new containerData weights add up to what they used to |
| 433 |
double ratio = (double) totalModelWeight / totalSashWeight; |
| 434 |
int totalAdded = 0; |
| 435 |
for (int i = 0; i < w.length; i++) { |
| 436 |
int newWeight = (int) (w[i] * ratio); |
| 437 |
|
| 438 |
// The last element will use up all the leftover 'weight' to avoid |
| 439 |
// roundoff errors |
| 440 |
if (i == (w.length - 1)) |
| 441 |
newWeight = totalModelWeight - totalAdded; |
| 442 |
elements[i].setContainerData(Integer.toString(newWeight)); |
| 443 |
totalAdded += newWeight; |
| 444 |
} |
| 445 |
// System.out |
| 446 |
// .println("Model Weights changed: " + totalModelWeight + " " + totalAdded); //$NON-NLS-1$//$NON-NLS-2$ |
| 447 |
} |
| 448 |
} |
218 |
} |