Skip to content

Content of file CollapsableGroupSWTRenderer.java

/**
 * Copyright (c) 2011-2016 EclipseSource Muenchen GmbH and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * EclipseSource Munich - initial API and implementation
 */
package org.eclipse.emf.ecp.view.group.swt.internal.collapsable;

import javax.inject.Inject;

import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.core.swt.ContainerSWTRenderer;
import org.eclipse.emf.ecp.view.spi.group.model.VGroup;
import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.swt.core.EMFFormsRendererFactory;
import org.eclipse.emfforms.spi.swt.core.layout.EMFFormsSWTLayoutUtil;
import org.eclipse.emfforms.spi.swt.core.layout.GridDescriptionFactory;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ExpandEvent;
import org.eclipse.swt.events.ExpandListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ExpandBar;
import org.eclipse.swt.widgets.ExpandItem;

/**
 * A Group renderer, which is collapsable.
 *
 * @author Eugen Neufeld
 * @author jfaltermeier
 *
 */
public class CollapsableGroupSWTRenderer extends ContainerSWTRenderer<VGroup> {

	private static final int MARGIN = 5;

	private SWTGridDescription rendererGridDescription;

	private final EMFDataBindingContext dbc;

	/**
	 * Default constructor.
	 *
	 * @param vElement the view model element to be rendered
	 * @param viewContext the view context
	 * @param reportService the {@link ReportService}
	 * @param factory the {@link EMFFormsRendererFactory}
	 */
	@Inject
	public CollapsableGroupSWTRenderer(VGroup vElement, ViewModelContext viewContext, ReportService reportService,
		EMFFormsRendererFactory factory) {
		super(vElement, viewContext, reportService, factory);
		dbc = new EMFDataBindingContext();
	}

	@Override
	public SWTGridDescription getGridDescription(SWTGridDescription gridDescription) {
		if (rendererGridDescription == null) {
			rendererGridDescription = GridDescriptionFactory.INSTANCE.createSimpleGrid(1, 1, this);
			final SWTGridCell swtGridCell = rendererGridDescription.getGrid().get(0);
			swtGridCell.setVerticalFill(false);
			swtGridCell.setVerticalGrab(false);
		}
		return rendererGridDescription;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see org.eclipse.emf.ecp.view.spi.core.swt.ContainerSWTRenderer#renderControl(org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell,
	 *      org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected Control renderControl(SWTGridCell gridCell, final Composite parent) throws NoRendererFoundException,
		NoPropertyDescriptorFoundExeption {
		final CollapsableGroupExpandBar bar = new CollapsableGroupExpandBar(parent, SWT.NONE);
		bar.setBackground(parent.getBackground());

		// First item
		final Composite composite = new Composite(bar, SWT.NONE);
		GridLayoutFactory.fillDefaults().margins(MARGIN, MARGIN).applyTo(composite);
		final ExpandItem item0 = new ExpandItem(bar, SWT.NONE, 0);
		bar.setInitialHeaderHeight(item0.getHeaderHeight());

		final FontDescriptor boldDescriptor = FontDescriptor.createFrom(bar.getFont()).setStyle(SWT.BOLD);
		final Font boldFont = boldDescriptor.createFont(bar.getDisplay());
		bar.setFont(boldFont);

		final ISWTObservableValue target = WidgetProperties.text().observe(item0);
		final IObservableValue modelValue = EMFEditObservables.observeValue(
			AdapterFactoryEditingDomain.getEditingDomainFor(getVElement()), getVElement(),
			VViewPackage.eINSTANCE.getElement_Label());
		dbc.bindValue(target, modelValue);
final Control containerControl = super.renderControl(gridCell, composite); GridDataFactory.fillDefaults().grab(true, false) .minSize(containerControl.computeSize(SWT.DEFAULT, SWT.DEFAULT).x, SWT.DEFAULT).applyTo(containerControl); final int height = computeHeight(composite); item0.setHeight(height); item0.setControl(composite); bar.setItemComposite(composite); bar.addExpandListener(new ExpandListener() { @Override public void itemCollapsed(ExpandEvent e) { final int headerHeight = item0.getHeaderHeight(); item0.setHeight(headerHeight); final Object layoutData = bar.getLayoutData(); updateLayoutData(layoutData, headerHeight + 2 * MARGIN); EMFFormsSWTLayoutUtil.adjustParentSize(bar); getVElement().setCollapsed(true); postCollapsed(); } @Override public void itemExpanded(ExpandEvent e) { item0.setHeight(computeHeight(composite)); final Object layoutData = bar.getLayoutData(); updateLayoutData(layoutData, computeHeight(composite) + item0.getHeaderHeight() + 2 * MARGIN); EMFFormsSWTLayoutUtil.adjustParentSize(bar); getVElement().setCollapsed(false); postExpanded(); } // XXX relayout upon expand/collapse will only work properly when the grid data is adjusted // this might not be the case for non GridData layout datas private void updateLayoutData(final Object layoutData, int height) { if (layoutData instanceof GridData) { final GridData gridData = (GridData) layoutData; gridData.heightHint = height; } } }); item0.setExpanded(!getVElement().isCollapsed()); return bar; } /** * This method gets called after the group has been expanded. Default implementation does nothing. */ protected void postExpanded() { // no op } /** * This method gets called after the group has been collapsed. Default implementation does nothing. */ protected void postCollapsed() { // no op } private int computeHeight(Composite composite) { // XXX +1 because last pixel gets cut off on windows 7 64 return composite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y + 1; } /** * {@link ExpandBar} which takes its item content size into account when computing the size. * * @author Johannes Faltermeier * */ private static final class CollapsableGroupExpandBar extends ExpandBar { private Composite itemComposite; private int headerHeight; CollapsableGroupExpandBar(Composite parent, int style) { super(parent, style); } /** * @param headerHeight the header height */ void setInitialHeaderHeight(int headerHeight) { this.headerHeight = headerHeight; } @Override public Point computeSize(int wHint, int hHint, boolean changed) { return computeSizeForBar(wHint, hHint, changed); } @Override protected void checkSubclass() { /* * we have to override expandbar because the size computation does not take the items control size into * account. For our use case we need this to enable parent scrolled composites to set the min size * correctely. */ } void setItemComposite(Composite itemComposite) { this.itemComposite = itemComposite; } private Point computeSizeForBar(int wHint, int hHint, boolean changed) { final Point sizeComputedByBar = super.computeSize(wHint, hHint, changed); if (itemComposite != null) { final Point itemSize = itemComposite.computeSize(wHint, hHint, changed); if (itemSize.x > sizeComputedByBar.x) { /* else might be true if the expandbar has a really long group text */ sizeComputedByBar.x = itemSize.x; } /* workaround for SWT-Bug 430600 */ if (getItemCount() == 1 && getItem(0).getHeaderHeight() < 0) { if (itemSize.y + headerHeight > sizeComputedByBar.y && getItem(0).getExpanded()) { sizeComputedByBar.y = itemSize.y + headerHeight; } } } sizeComputedByBar.x = sizeComputedByBar.x + 2 * MARGIN; return sizeComputedByBar; } } @Override protected void dispose() { if (dbc != null) { dbc.dispose(); } super.dispose(); } }