/** * 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 java.util.Iterator; import java.util.List; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import com.codeaffine.eclipse.swt.widget.scrollable.context.ScrollableControl; import com.codeaffine.eclipse.swt.widget.scrollbar.FlatScrollBar; class TreeVerticalScrollBarUpdater implements ScrollBarUpdater { private final VerticalScrollbarConfigurationBuffer scrollbarConfiguration; private final TreeItemCollector treeItemCollector; private final FlatScrollBar scrollBar; private final Tree tree; TreeVerticalScrollBarUpdater( Tree tree, FlatScrollBar scrollbar ) { this.scrollbarConfiguration = new VerticalScrollbarConfigurationBuffer( new ScrollableControl<>( tree ) ); this.treeItemCollector = new TreeItemCollector( tree ); this.scrollBar = scrollbar; this.tree = tree; } @Override public void update() { if( scrollbarConfiguration.hasChanged() ) { updateScrollbar(); } scrollbarConfiguration.update(); } private void updateScrollbar() { List<TreeItem> visibleItems = treeItemCollector.collectVisibleItems(); scrollBar.setIncrement( SELECTION_RASTER_SMOOTH_FACTOR ); scrollBar.setMaximum( calculateMaximum( visibleItems ) ); scrollBar.setMinimum( 0 ); scrollBar.setPageIncrement( calculateThumb() ); scrollBar.setThumb( calculateThumb() ); scrollBar.setSelection( calculateSelection( visibleItems ) ); } int calculateMaximum( List<TreeItem> visibleItems ) { return SELECTION_RASTER_SMOOTH_FACTOR * visibleItems.size(); } int calculateThumb() { int height = calculateHeight(); int ratio = height / tree.getItemHeight(); return SELECTION_RASTER_SMOOTH_FACTOR * ratio; } int calculateHeight() { int result = tree.getClientArea().height; if( tree.getHeaderVisible() ) { result -= tree.getHeaderHeight(); } return result; } private int calculateSelection( List<TreeItem> visibleItems ) { TreeItem topItem = tree.getTopItem(); int result = calculateSelection( visibleItems.iterator(), topItem ); return SELECTION_RASTER_SMOOTH_FACTOR * cornerCaseWorkaroundForGtk( result, topItem ); } private static int calculateSelection( Iterator<TreeItem> iterator, TreeItem topItem ) { int result = 0; while( checkNext( topItem, iterator ) ){ result++; } return result; } private static boolean checkNext( TreeItem topItem, Iterator<TreeItem> iterator ) { return iterator.hasNext() && iterator.next() != topItem; } // [fappel]: setting topItem does not work reliable on gtk for last selection items // if top item is only half visible. The tree is moved correctly but top item returns the old value. // This recognizes such a situation and increases the flat scrollbar selection anyway. private static int cornerCaseWorkaroundForGtk( int selection, TreeItem topItem ) { return topItem != null && topItem.getBounds().y < 0 ? selection + 1 : selection; } }