/** * Copyright (c) 2014 - 2017 Frank Appel * 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: * Frank Appel - initial API and implementation */ package com.codeaffine.eclipse.swt.widget.scrollable; import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.PARENT; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import com.codeaffine.eclipse.swt.util.ControlReflectionUtil; import com.codeaffine.eclipse.swt.widget.scrollable.context.ScrollableControl; class ItemHeightMeasurementEnabler { static final int UNSET = -1; private final Listener ownerDrawInUseWatchDog; private final ScrollableControl<?> scrollable; private final ScrollableRedrawState redrawState; private final Composite adapter; private final Listener prepare; private final Listener restore; int intermediateHeightBuffer; boolean skipEnsureRestore; boolean onMeasurement; int height; ItemHeightMeasurementEnabler( ScrollableControl<?> scrollable, Composite adapter ) { this.scrollable = scrollable; this.adapter = adapter; this.height = scrollable.getItemHeight(); this.redrawState = new ScrollableRedrawState( scrollable ); this.ownerDrawInUseWatchDog = evt -> registerListenerOnMeasurementEvent( evt ); this.prepare = evt -> prepareScrollableToAllowProperHeightMeasurement( evt ); this.restore = evt -> restoreScrollableAfterMeasurement(); this.intermediateHeightBuffer = UNSET; registerMeasurementWatchDog( scrollable ); } private void registerListenerOnMeasurementEvent( Event event ) { if( scrollable.isSameAs( event.widget ) && scrollable.isOwnerDrawn() ) { scrollable.addListener( SWT.MeasureItem, prepare ); scrollable.addListener( SWT.EraseItem, restore ); scrollable.getDisplay().removeFilter( SWT.MeasureItem, this.ownerDrawInUseWatchDog ); scrollable.getDisplay().timerExec( 50, () -> { ensureRestore(); } ); } } private void registerMeasurementWatchDog( ScrollableControl<?> scrollable ) { Display display = scrollable.getDisplay(); display.addFilter( SWT.MeasureItem, ownerDrawInUseWatchDog ); scrollable.addListener( SWT.Dispose, evt -> display.removeFilter( SWT.MeasureItem, ownerDrawInUseWatchDog ) ); } private void prepareScrollableToAllowProperHeightMeasurement( Event event ) { if( needPreparations( event ) ) { reparentScrollable( false, adapter.getParent() ); shiftHeightStateBuffer( event ); onMeasurement = true; } } private void shiftHeightStateBuffer( Event event ) { if( event.height == intermediateHeightBuffer ) { height = intermediateHeightBuffer; } if( intermediateHeightBuffer == UNSET ) { intermediateHeightBuffer = event.height; } } private void ensureRestore() { if( onMeasurement && !skipEnsureRestore ) { height = intermediateHeightBuffer; restoreScrollableAfterMeasurement(); } } private void restoreScrollableAfterMeasurement() { if( onMeasurement ) { skipEnsureRestore = true; reparentScrollable( true, adapter ); reAdjustParentReferenceOfScrollableAfterMeasurement(); onMeasurement = false; adapter.getParent().layout(); } } private void reparentScrollable( boolean redraw, Composite parent ) { redrawState.update( redraw ); scrollable.setParent( parent ); } private void reAdjustParentReferenceOfScrollableAfterMeasurement() { if( height == intermediateHeightBuffer ) { new ControlReflectionUtil().setField( scrollable.getControl(), PARENT, adapter.getParent() ); scrollable.removeListener( SWT.MeasureItem, prepare ); scrollable.removeListener( SWT.EraseItem, restore ); } } private boolean needPreparations( Event event ) { return event.height != height; } }