All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kie.uberfire.client.tables.ResizableMovableHeader Maven / Gradle / Ivy

The newest version!
package org.kie.uberfire.client.tables;

import com.github.gwtbootstrap.client.ui.DataGrid;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import org.uberfire.commons.validation.PortablePreconditions;

import static com.google.gwt.dom.client.Style.Unit.*;
import java.util.ArrayList;
import java.util.List;

/**
 * A column header that supports resizing and moving
 * See https://github.com/gchatelet/GwtResizableDraggableColumns/blob/master/src/fr/mikrosimage/gwt/client/ResizableHeader.java
 * @param 
 */
public abstract class ResizableMovableHeader extends Header {

    private static final Style.Cursor MOVE_CURSOR = Cursor.MOVE;
    private static final String MOVE_COLOR = "gray";
    private static final int MOVE_HANDLE_WIDTH = 32;

    private static final Style.Cursor RESIZE_CURSOR = Cursor.COL_RESIZE;
    private static final String RESIZE_COLOR = "gray";
    private static final int RESIZE_HANDLE_WIDTH = 8;

    private static final double GHOST_OPACITY = .3;

    private static final int MINIMUM_COLUMN_WIDTH = 30;

    private final Document document = Document.get();

    private final String title;
    private final DataGrid table;
    private final ColumnPicker columnPicker;
    private final Column column;

    private final Element tableElement;
    private HeaderHelper current;
    private List columnChangedHandlers = new ArrayList();
    

    public ResizableMovableHeader( final String title,
                                   final DataGrid table, 
                                   final ColumnPicker columnPicker,
                                   final Column column ) {
        super( new HeaderCell() );      
        this.title = PortablePreconditions.checkNotNull( "title",
                                                         title );
        this.table = PortablePreconditions.checkNotNull( "table",
                                                         table );
        this.columnPicker = PortablePreconditions.checkNotNull( "columnPicker",
                                                                columnPicker );
        this.column = PortablePreconditions.checkNotNull( "column",
                                                          column );
        this.tableElement = table.getElement();
    }

    @Override
    public String getValue() {
        return title;
    }

    @Override
    public void onBrowserEvent( final Context context,
                                final Element target,
                                final NativeEvent event ) {
        if ( current == null ) {
            current = new HeaderHelper( target,
                                        event );
        }
    }

    interface IDragCallback {

        void dragFinished();
    }

    private static NativeEvent getEventAndPreventPropagation( final NativePreviewEvent event ) {
        final NativeEvent nativeEvent = event.getNativeEvent();
        nativeEvent.preventDefault();
        nativeEvent.stopPropagation();
        return nativeEvent;
    }

    private static void setLine( final Style style,
                                 final int width,
                                 final int top,
                                 final int height,
                                 final String color ) {
        style.setPosition( Position.ABSOLUTE );
        style.setTop( top,
                      PX );
        style.setHeight( height,
                         PX );
        style.setWidth( width,
                        PX );
        style.setBackgroundColor( color );
        style.setZIndex( Integer.MAX_VALUE );
    }

    private class HeaderHelper implements NativePreviewHandler,
                                          IDragCallback {

        private final HandlerRegistration handler = Event.addNativePreviewHandler( this );
        private final Element source;
        private final Element handles;
        private final Element moveHandle;
        private final Element resizeHandle;
        private boolean dragging;

        public HeaderHelper( final Element target,
                             final NativeEvent event ) {
            event.preventDefault();
            event.stopPropagation();
            this.source = target;
            this.handles = document.createDivElement();

            final int leftBound = target.getOffsetLeft() + target.getOffsetWidth();
            this.moveHandle = createSpanElement( MOVE_CURSOR,
                                                 leftBound - RESIZE_HANDLE_WIDTH - MOVE_HANDLE_WIDTH,
                                                 MOVE_HANDLE_WIDTH );
            this.resizeHandle = createSpanElement( RESIZE_CURSOR,
                                                   leftBound - RESIZE_HANDLE_WIDTH,
                                                   RESIZE_HANDLE_WIDTH );
            handles.appendChild( moveHandle );
            handles.appendChild( resizeHandle );
            source.appendChild( handles );
        }

        private SpanElement createSpanElement( final Cursor cursor,
                                               final double left,
                                               final double width ) {
            final SpanElement span = document.createSpanElement();
            span.setAttribute( "title",
                               title );
            final Style style = span.getStyle();
            style.setCursor( cursor );
            style.setPosition( Position.ABSOLUTE );
            style.setBottom( 0,
                             PX );
            style.setHeight( source.getOffsetHeight(),
                             PX );
            style.setTop( source.getOffsetTop(),
                          PX );
            style.setWidth( width,
                            PX );
            style.setLeft( left,
                           PX );
            return span;
        }

        @Override
        public void onPreviewNativeEvent( final NativePreviewEvent event ) {
            final NativeEvent natEvent = event.getNativeEvent();
            final Element element = natEvent.getEventTarget().cast();
            final String eventType = natEvent.getType();
            if ( !( element == moveHandle || element == resizeHandle ) ) {
                if ( "mousedown".equals( eventType ) ) {
                    //No need to do anything, the event will be passed on to the column sort handler
                } else if ( !dragging && "mouseover".equals( eventType ) ) {
                    cleanUp();
                }
                return;
            }
            final NativeEvent nativeEvent = getEventAndPreventPropagation( event );
            if ( "mousedown".equals( eventType ) ) {
                if ( element == resizeHandle ) {
                    moveHandle.removeFromParent();
                    new ColumnResizeHelper( this,
                                            source,
                                            nativeEvent );
                } else {
                    new ColumnMoverHelper( this,
                                           source,
                                           nativeEvent );
                }
                dragging = true;
            }
        }

        private void cleanUp() {
            handler.removeHandler();
            handles.removeFromParent();
            current = null;
        }

        public void dragFinished() {
            dragging = false;
            cleanUp();
        }
    }

    private class ColumnResizeHelper implements NativePreviewHandler {

        private final HandlerRegistration handler = Event.addNativePreviewHandler( this );
        private final DivElement resizeLine = document.createDivElement();
        private final Style resizeLineStyle = resizeLine.getStyle();
        private final Element header;
        private final IDragCallback dragCallback;

        private ColumnResizeHelper( final IDragCallback dragCallback,
                                    final Element header,
                                    final NativeEvent event ) {
            this.dragCallback = dragCallback;
            this.header = header;
            setLine( resizeLineStyle,
                     2,
                     0,
                     getTableBodyHeight(),
                     RESIZE_COLOR );
            moveLine( event.getClientX() );
            tableElement.appendChild( resizeLine );
        }

        @Override
        public void onPreviewNativeEvent( final NativePreviewEvent event ) {
            final NativeEvent nativeEvent = getEventAndPreventPropagation( event );
            final int clientX = nativeEvent.getClientX();
            final String eventType = nativeEvent.getType();
            if ( "mousemove".equals( eventType ) ) {
                moveLine( clientX );
            } else if ( "mouseup".equals( eventType ) ) {
                handler.removeHandler();
                resizeLine.removeFromParent();
                dragCallback.dragFinished();
                columnResized( Math.max( clientX - header.getAbsoluteLeft(),
                                         MINIMUM_COLUMN_WIDTH ) );
            }
        }

        private void moveLine( final int clientX ) {
            final int xPos = clientX - table.getAbsoluteLeft();
            resizeLineStyle.setLeft( xPos,
                                     PX );
        }
    }

    private class ColumnMoverHelper implements NativePreviewHandler {

        private static final int ghostLineWidth = 4;
        private final HandlerRegistration handler = Event.addNativePreviewHandler( this );
        private final DivElement ghostLine = document.createDivElement();
        private final Style ghostLineStyle = ghostLine.getStyle();
        private final DivElement ghostColumn = document.createDivElement();
        private final Style ghostColumnStyle = ghostColumn.getStyle();
        private final int columnWidth;
        private final int[] columnXPositions;
        private final IDragCallback dragCallback;
        private int fromIndex = -1;
        private int toIndex;

        private ColumnMoverHelper( final IDragCallback dragCallback,
                                   final Element target,
                                   final NativeEvent event ) {
            final int clientX = event.getClientX();
            final Element tr = target.getParentElement();
            final int columns = tr.getChildCount();

            this.dragCallback = dragCallback;
            this.columnWidth = target.getOffsetWidth();
            this.columnXPositions = new int[ columns + 1 ];
            this.columnXPositions[ 0 ] = tr.getAbsoluteLeft();
            for ( int i = 0; i < columns; ++i ) {
                final int xPos = columnXPositions[ i ] + ( (Element) tr.getChild( i ) ).getOffsetWidth();
                if ( xPos > clientX && fromIndex == -1 ) {
                    fromIndex = i;
                }
                columnXPositions[ i + 1 ] = xPos;
            }
            toIndex = fromIndex;
            final int bodyHeight = getTableBodyHeight();
            setLine( ghostColumnStyle,
                     columnWidth,
                     0,
                     bodyHeight,
                     MOVE_COLOR );
            setLine( ghostLineStyle,
                     ghostLineWidth,
                     0,
                     bodyHeight,
                     RESIZE_COLOR );
            ghostColumnStyle.setOpacity( GHOST_OPACITY );
            moveColumn( clientX );
            tableElement.appendChild( ghostColumn );
            tableElement.appendChild( ghostLine );
        }

        @Override
        public void onPreviewNativeEvent( final NativePreviewEvent event ) {
            final NativeEvent nativeEvent = getEventAndPreventPropagation( event );
            final String eventType = nativeEvent.getType();
            if ( "mousemove".equals( eventType ) ) {
                moveColumn( nativeEvent.getClientX() );
            } else if ( "mouseup".equals( eventType ) ) {
                handler.removeHandler();
                ghostColumn.removeFromParent();
                ghostLine.removeFromParent();
                if ( fromIndex != toIndex ) {
                    columnMoved( fromIndex,
                                 toIndex );
                }
                dragCallback.dragFinished();
            }
        }

        private void moveColumn( final int clientX ) {
            final int pointer = clientX - columnWidth / 2;
            ghostColumnStyle.setLeft( pointer - table.getAbsoluteLeft(),
                                      PX );
            for ( int i = 0; i < columnXPositions.length - 1; ++i ) {
                if ( clientX < columnXPositions[ i + 1 ] ) {
                    final int adjustedIndex = i > fromIndex ? i + 1 : i;
                    int lineXPos = columnXPositions[ adjustedIndex ] - table.getAbsoluteLeft();
                    if ( adjustedIndex == columnXPositions.length - 1 ) {
                        lineXPos -= ghostLineWidth;
                    } else if ( adjustedIndex > 0 ) {
                        lineXPos -= ghostLineWidth / 2;
                    }
                    ghostLineStyle.setLeft( lineXPos,
                                            PX );
                    toIndex = i;
                    break;
                }
            }
        }
    }

    private static class HeaderCell extends AbstractCell {

        public HeaderCell() {
            super( "mousemove" );
        }

        @Override
        public void render( final Context context,
                            final String value,
                            final SafeHtmlBuilder sb ) {
            sb.append( SafeHtmlUtils.fromString( value ) );
        }
    }

    protected void columnResized( final int newWidth ) {
        table.setColumnWidth( column,
                              newWidth + "px" );
        for(ColumnChangedHandler handler : columnChangedHandlers){
          handler.afterColumnChanged();
        }
    }

    protected void columnMoved( final int fromIndex,
                                final int beforeIndex ) {
        columnPicker.columnMoved( fromIndex,
                                  beforeIndex );
        table.removeColumn( fromIndex );
        table.insertColumn( beforeIndex,
                            column,
                            this );
        for(ColumnChangedHandler handler : columnChangedHandlers){
          handler.afterColumnChanged();
        }
    }

    protected abstract int getTableBodyHeight();
    
    public void addColumnChangedHandler(ColumnChangedHandler handler){
      if(handler != null){
        columnChangedHandlers.add(handler);
      }
    }
  

};





© 2015 - 2025 Weber Informatics LLC | Privacy Policy