/** * 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.context; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Scrollable; import org.eclipse.swt.widgets.Tree; class BoundsReconciliation { private final ScrollableControl<? extends Scrollable> scrollable; private final Composite adapter; private Rectangle oldScrollableBounds; private Rectangle newScrollableBounds; private boolean treeEvent; private int suspendCount; private boolean moved; BoundsReconciliation( Composite adapter, ScrollableControl<? extends Scrollable> scrollable ) { this.adapter = adapter; this.scrollable = scrollable; registerListeners( scrollable ); updateBoundsBuffer(); } void run() { if( mustReconcile() ) { reconcile(); } updateBoundsBuffer(); clearTreeEvent(); } boolean isSuspended() { return suspendCount > 0; } void suspend() { suspendCount++; } void resume() { suspendCount--; } void runSuspended( Runnable runnable ) { suspend(); try { runnable.run(); } finally { updateBoundsBuffer(); resume(); } } private void treeExpanded() { treeEvent = true; } private void treeCollapsed() { treeEvent = true; } private void controlMoved() { if( !isSuspended() ) { newScrollableBounds = scrollable.getBounds(); flagScrollableAsMoved(); if( mustWorkaroundScrollableWithBorderInitializations() ) { oldScrollableBounds = scrollable.getBounds(); } } } private boolean mustWorkaroundScrollableWithBorderInitializations() { return newScrollableBounds.x == newScrollableBounds.y && newScrollableBounds.x == -scrollable.getBorderWidth() && scrollable.hasStyle( SWT.BORDER ); } private void controlResized() { if( !isSuspended() ) { newScrollableBounds = scrollable.getBounds(); } } private boolean mustReconcile() { return scrollableBoundsHaveBeenChanged() && !changeByTreeEvent(); } private boolean scrollableBoundsHaveBeenChanged() { return !oldScrollableBounds.equals( newScrollableBounds ); } private boolean changeByTreeEvent() { return treeEvent; } private void reconcile() { if( hasScrollableBeenMoved() ) { unflagScrollableAsMoved(); newScrollableBounds = computeScrollableBoundsWithLocationDelta(); } adapter.setBounds( newScrollableBounds ); } private Rectangle computeScrollableBoundsWithLocationDelta() { return new Rectangle( newScrollableBounds.x - oldScrollableBounds.x, newScrollableBounds.y - oldScrollableBounds.y, newScrollableBounds.width, newScrollableBounds.height ); } private void updateBoundsBuffer() { oldScrollableBounds = scrollable.getBounds(); newScrollableBounds = scrollable.getBounds(); } private boolean clearTreeEvent() { return treeEvent = false; } private void registerListeners( ScrollableControl<? extends Scrollable> scrollable ) { scrollable.addListener( SWT.Move, evt -> controlMoved() ); scrollable.addListener( SWT.Resize, evt -> controlResized() ); if( scrollable.isInstanceof( Tree.class ) ) { scrollable.addListener( SWT.Collapse, evt -> treeCollapsed() ); scrollable.addListener( SWT.Expand, evt -> treeExpanded() ); } } private void flagScrollableAsMoved() { moved = true; } private void unflagScrollableAsMoved() { moved = false; } private boolean hasScrollableBeenMoved() { return moved; } }