1 /******************************************************************************* 2 * Copyright (c) 2009, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 441150, 441120 14 *******************************************************************************/ 15 package org.eclipse.e4.ui.workbench.renderers.swt; 16 17 import javax.inject.Inject; 18 import org.eclipse.core.runtime.Platform; 19 import org.eclipse.e4.core.di.annotations.Optional; 20 import org.eclipse.e4.ui.di.UIEventTopic; 21 import org.eclipse.e4.ui.model.application.ui.MElementContainer; 22 import org.eclipse.e4.ui.model.application.ui.MUIElement; 23 import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainer; 24 import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement; 25 import org.eclipse.e4.ui.workbench.UIEvents; 26 import org.eclipse.swt.SWT; 27 import org.eclipse.swt.graphics.Rectangle; 28 import org.eclipse.swt.widgets.Composite; 29 import org.eclipse.swt.widgets.Layout; 30 import org.osgi.service.event.Event; 31 32 /** 33 * Default SWT renderer responsible for a MPartSashContainer. See 34 * {@link WorkbenchRendererFactory} 35 */ 36 public class SashRenderer extends SWTPartRenderer { 37 38 private static final int DEFAULT_WEIGHT = 5000; 39 40 private int processedContent = 0; 41 42 @SuppressWarnings("unchecked") 43 @Inject 44 @Optional subscribeTopicOrientationChanged(@IEventTopicUIEvents.GenericTile.TOPIC_HORIZONTAL) Event event)45 private void subscribeTopicOrientationChanged(@UIEventTopic(UIEvents.GenericTile.TOPIC_HORIZONTAL) Event event) { 46 // Ensure that this event is for a MPartSashContainer 47 MUIElement element = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT); 48 if (element.getRenderer() != SashRenderer.this) { 49 return; 50 } 51 forceLayout((MElementContainer<MUIElement>) element); 52 } 53 54 @Inject 55 @Optional subscribeTopicSashWeightChanged(@IEventTopicUIEvents.UIElement.TOPIC_CONTAINERDATA) Event event)56 private void subscribeTopicSashWeightChanged(@UIEventTopic(UIEvents.UIElement.TOPIC_CONTAINERDATA) Event event) { 57 // Ensure that this event is for a child of a MPartSashContainer 58 MUIElement element = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT); 59 MElementContainer<MUIElement> parent = element.getParent(); 60 if (parent.getRenderer() != SashRenderer.this) { 61 return; 62 } 63 forceLayout(parent); 64 } 65 66 /** 67 * @param pscModel 68 */ forceLayout(MElementContainer<MUIElement> pscModel)69 protected void forceLayout(MElementContainer<MUIElement> pscModel) { 70 if (processedContent != 0) { 71 return; 72 } 73 // layout the containing Composite 74 while (!(pscModel.getWidget() instanceof Composite)) { 75 pscModel = pscModel.getParent(); 76 } 77 78 Composite s = (Composite) pscModel.getWidget(); 79 80 // FIXME SWT Win requires a synchronous layout call to update the UI 81 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=558392 82 // once this is fixed, the requestLayout call should be sufficient 83 if (Platform.getOS().equals(Platform.OS_WIN32)) { 84 Layout layout = s.getLayout(); 85 if (layout instanceof SashLayout) { 86 if (((SashLayout) layout).layoutUpdateInProgress) { 87 return; 88 } 89 } 90 s.layout(true, true); 91 } else { 92 s.requestLayout(); 93 } 94 } 95 96 @Override createWidget(final MUIElement element, Object parent)97 public Object createWidget(final MUIElement element, Object parent) { 98 MUIElement elementParent = element.getParent(); 99 if (elementParent == null && element.getCurSharedRef() != null) 100 elementParent = element.getCurSharedRef(); 101 102 if (elementParent != null && elementParent.getRenderer() == this) { 103 Rectangle newRect = new Rectangle(0, 0, 0, 0); 104 105 // If my layout's container gets disposed 'unbind' the sash elements 106 if (parent instanceof Composite) { 107 ((Composite) parent).addDisposeListener(e -> { 108 element.setWidget(null); 109 element.setRenderer(null); 110 }); 111 } 112 return newRect; 113 } 114 115 // Check to see if this is a new PSC added 'above' the previous 'root' 116 // sash 117 Composite sashComposite = null; 118 MPartSashContainer psc = (MPartSashContainer) element; 119 for (MPartSashContainerElement psce : psc.getChildren()) { 120 if (psce instanceof MPartSashContainer && psce.getWidget() instanceof Composite) { 121 // 'Adopt' the previous root's layout / composite 122 sashComposite = (Composite) psce.getWidget(); 123 bindWidget(psce, new Rectangle(0, 0, 0, 0)); 124 } 125 } 126 // This is a 'root' sash container, create a composite 127 if (sashComposite == null) { 128 sashComposite = new Composite((Composite) parent, SWT.NONE); 129 } 130 sashComposite.setLayout(new SashLayout(sashComposite, element)); 131 132 return sashComposite; 133 } 134 135 @Override childRendered(MElementContainer<MUIElement> parentElement, MUIElement element)136 public void childRendered(MElementContainer<MUIElement> parentElement, MUIElement element) { 137 super.childRendered(parentElement, element); 138 139 // Ensure that the element's 'containerInfo' is initialized 140 ensureLayoutWeight(element); 141 forceLayout(parentElement); 142 } 143 144 @Override processContents(MElementContainer<MUIElement> container)145 public void processContents(MElementContainer<MUIElement> container) { 146 try { 147 processedContent++; 148 super.processContents(container); 149 } finally { 150 processedContent--; 151 if (processedContent == 0) { 152 forceLayout(container); 153 } 154 } 155 } 156 157 @Override hideChild(MElementContainer<MUIElement> parentElement, MUIElement child)158 public void hideChild(MElementContainer<MUIElement> parentElement, MUIElement child) { 159 super.hideChild(parentElement, child); 160 161 forceLayout(parentElement); 162 } 163 164 @Override getUIContainer(MUIElement element)165 public Object getUIContainer(MUIElement element) { 166 // OK, find the 'root' of the sash container 167 MUIElement parentElement = element.getParent(); 168 while (parentElement.getRenderer() == this && !(parentElement.getWidget() instanceof Composite)) { 169 parentElement = parentElement.getParent(); 170 } 171 172 if (parentElement.getWidget() instanceof Composite) { 173 return parentElement.getWidget(); 174 } 175 return null; 176 } 177 178 /* 179 * Container data is used by the SashLayout to determine the size of the control 180 */ ensureLayoutWeight(MUIElement element)181 private static void ensureLayoutWeight(MUIElement element) { 182 int weight = DEFAULT_WEIGHT; 183 184 String info = element.getContainerData(); 185 if (info != null && info.length() > 0) { 186 try { 187 int value = Integer.parseInt(info); 188 weight = value; 189 } catch (NumberFormatException e) { 190 // continue to use the default value 191 } 192 } 193 element.setContainerData(Integer.toString(weight)); 194 } 195 } 196