/******************************************************************************* * Copyright (c) 2015 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.livexp.ui; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.events.IExpansionListener; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression; import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable; import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult; import org.springsource.ide.eclipse.commons.livexp.core.ValueListener; /** * Section containing an ExpandableComposite that contains another * section which is shown/hidden inside the expandable composite. * <p> * The contained page section is assumed to be a SelectionSource<T> * and so the expandable composite is as well. The 'selection' in * the child are propagated as the selection of the composite, * but only when the composite is in the 'expanded' state. * In non-expanded state, the composite propagates an empty * selection instead. * <p> * Note: This has not been used in many contexts and may not * be re-usable as is in contexts where it hasn't been tested. * The component is somewhat fiddly w.r.t. how parent composite * need to reflow their layout when this element is * expanded/collapsed. * * @author Kris De Volder */ public class ExpandableSection extends WizardPageSection implements Disposable { private IPageSection child; private String title; private LiveVariable<Boolean> expansionState = new LiveVariable<Boolean>(true); private LiveVariable<Boolean> visibleState = new LiveVariable<Boolean>(true); public ExpandableSection(IPageWithSections owner, String title, IPageSection expandableContent) { super(owner); this.title = title; this.child = expandableContent; } @Override public LiveExpression<ValidationResult> getValidator() { return OK_VALIDATOR; } @Override public void createContents(final Composite page) { final ExpandableComposite comp = new ExpandableComposite(page, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(comp); comp.setText(title); comp.setLayout(new FillLayout()); comp.addExpansionListener(new IExpansionListener() { public void expansionStateChanging(ExpansionEvent e) { } @Override public void expansionStateChanged(ExpansionEvent e) { expansionState.setValue(comp.isExpanded()); reflow(owner, comp); } }); expansionState.addListener(new ValueListener<Boolean>() { public void gotValue(LiveExpression<Boolean> exp, Boolean value) { if (value!=null && comp!=null && !comp.isDisposed()) { boolean newState = value; boolean currentState = comp.isExpanded(); if (currentState!=newState) { comp.setExpanded(newState); reflow(owner, comp); } } } }); visibleState.addListener(new ValueListener<Boolean>() { @Override public void gotValue(LiveExpression<Boolean> exp, Boolean value) { if (value!=null && comp!=null && !comp.isDisposed()) { boolean newState = value; comp.setVisible(newState); GridData data = (GridData) comp.getLayoutData(); data.exclude = !newState; reflow(owner, comp); } } }); Composite client = new Composite(comp, SWT.NONE); client.setLayout(new GridLayout()); child.createContents(client); comp.setClient(client); } public LiveVariable<Boolean> getExpansionState() { return expansionState; } @Override public void dispose() { if (child!=null) { if (child instanceof Disposable) { ((Disposable) child).dispose(); } child = null; } } /** * Called after a expandable section was expanded or collapsed. It should * cause the surrounding parent widgets to 'reflow' to adapt to new size. */ protected void reflow(IPageWithSections owner, ExpandableComposite comp) { boolean reflowed = false; if (owner instanceof Reflowable) { reflowed = ((Reflowable) owner).reflow(); } if (!reflowed) { //sortof works in some cases, but may not adjust scrollbars in the page. comp.getParent().layout(true); } } public void setVisible(boolean reveal) { this.visibleState.setValue(reveal); } @Override public String toString() { return "ExpandableSection("+title+")"; } }