blob: d9e67b46d0dde9007ed6160f835f0ede6b0ceec0 [file] [log] [blame]
/**
* 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();
}
}