package com.project.website.canvas.client.canvastools.base;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HumanInputEvent;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.project.shared.client.events.SimpleEvent;
import com.project.shared.client.events.SimpleEvent.Handler;
import com.project.shared.client.handlers.RegistrationsManager;
import com.project.shared.client.utils.DocumentUtils;
import com.project.shared.client.utils.ElementUtils;
import com.project.shared.client.utils.SchedulerUtils;
import com.project.shared.client.utils.widgets.WidgetUtils;
import com.project.shared.data.Point2D;
import com.project.website.canvas.client.canvastools.base.eventargs.LoadStartedEventArgs;
import com.project.website.canvas.client.canvastools.base.interfaces.CanvasTool;
import com.project.website.canvas.client.canvastools.base.interfaces.CanvasToolFrame;
import com.project.website.canvas.client.resources.CanvasResources;
import com.project.website.canvas.client.shared.widgets.FloatingToolbar;
import com.project.website.canvas.shared.data.ElementData;
public class CanvasToolFrameImpl extends Composite implements CanvasToolFrame {
private static CanvasToolFrameImplUiBinder uiBinder = GWT.create(CanvasToolFrameImplUiBinder.class);
interface CanvasToolFrameImplUiBinder extends UiBinder<Widget, CanvasToolFrameImpl> {
}
@UiField
FocusPanel focusPanel;
@UiField
HTMLPanel frameHeader;
@UiField
HTMLPanel toolPanel;
@UiField
Anchor closeLink;
@UiField
Anchor moveBackLink;
@UiField
Anchor moveFrontLink;
@UiField
FlowPanel framePanel;
@UiField
FlowPanel buttonsPanel;
@UiField
HTMLPanel resizePanel;
@UiField
HTMLPanel rotatePanel;
@UiField
HTMLPanel loadingPanel;
@UiField
HTMLPanel toolArea;
@UiField
FlowPanel dragOverlayPanel;
protected final CanvasTool<?> tool;
private FloatingToolbar floatingToolbar = null;
protected final SimpleEvent<Void> closeRequest = new SimpleEvent<Void>();
protected final SimpleEvent<Void> moveBackRequest = new SimpleEvent<Void>();
protected final SimpleEvent<Void> moveFrontRequest = new SimpleEvent<Void>();
protected final SimpleEvent<Void> moveStartRequest = new SimpleEvent<Void>();
protected final SimpleEvent<Void> resizeStartRequest = new SimpleEvent<Void>();
protected final SimpleEvent<Void> rotateStartRequest = new SimpleEvent<Void>();
private final RegistrationsManager frameRegs = new RegistrationsManager();
private final RegistrationsManager toolRegs = new RegistrationsManager();
protected Integer _rotation = null;
private int draggingStackDepth = 0;
private boolean _viewMode = false;
private boolean _isActive = false;
private final ScheduledCommand onTransformCommand = new ScheduledCommand() {
@Override public void execute() {
handleOnTransform();
}
};
public CanvasToolFrameImpl(CanvasTool<?> canvasTool) {
initWidget(uiBinder.createAndBindUi(this));
//WidgetUtils.stopClickPropagation(this);
this.tool = canvasTool;
this.toolPanel.add(canvasTool);
WidgetUtils.stopClickPropagation(this.closeLink.asWidget());
WidgetUtils.stopMouseMovePropagation(this.closeLink.asWidget());
WidgetUtils.stopClickPropagation(this.moveBackLink.asWidget());
WidgetUtils.stopMouseMovePropagation(this.moveBackLink.asWidget());
WidgetUtils.stopClickPropagation(this.moveFrontLink.asWidget());
WidgetUtils.stopMouseMovePropagation(this.moveFrontLink.asWidget());
WidgetUtils.stopMouseMovePropagation(this.rotatePanel);
WidgetUtils.stopMouseMovePropagation(this.resizePanel);
this.loadingPanel.setVisible(false);
this.rotatePanel.setVisible(tool.canRotate());
this.resizePanel.setVisible(tool.getResizeMode() != ResizeMode.NONE);
this.preventTouchScroll();
}
@Override
protected void onLoad()
{
super.onLoad();
ElementUtils.setTextSelectionEnabled(this.dragOverlayPanel.getElement(), false);
ElementUtils.setTextSelectionEnabled(this.frameHeader.getElement(), false);
ElementUtils.setTextSelectionEnabled(this.buttonsPanel.getElement(), false);
ElementUtils.setTextSelectionEnabled(this.rotatePanel.getElement(), false);
ElementUtils.setTextSelectionEnabled(this.resizePanel.getElement(), false);
this.initToolbar();
this.reRegisterFrameHandlers();
this.registerTransformHandlers();
}
@Override
protected void onUnload()
{
this.frameRegs.clear();
this.toolRegs.clear();
if (null != this.floatingToolbar) {
this.floatingToolbar.setEditedWidget(null);
this.floatingToolbar.removeFromParent();
}
super.onUnload();
}
private void initToolbar()
{
IsWidget toolbar = this.tool.getToolbar();
if (null == toolbar) {
return;
}
this.floatingToolbar = new FloatingToolbar();
this.floatingToolbar.setEditedWidget(this);
this.floatingToolbar.add(toolbar);
if (this.isAttached()) {
RootPanel.get().add(this.floatingToolbar);
}
}
private void reRegisterFrameHandlers() {
final CanvasToolFrameImpl that = this;
frameRegs.clear();
frameRegs.add(this.toolPanel.addDomHandler(new KeyDownHandler(){
@Override public void onKeyDown(KeyDownEvent event) {
//Stop propogation of KeyDown events from the toolframe so that the worksheet
//won't get any keydown that was already handled by the tool.
event.stopPropagation();
}}, KeyDownEvent.getType()));
frameRegs.add(this.closeLink.addClickHandler(new ClickHandler() {
@Override public void onClick(ClickEvent event) {
that.closeRequest.dispatch(null);
}}));
frameRegs.add(this.moveBackLink.addClickHandler(new ClickHandler() {
@Override public void onClick(ClickEvent event) {
that.moveBackRequest.dispatch(null);
}}));
frameRegs.add(this.moveFrontLink.addClickHandler(new ClickHandler() {
@Override public void onClick(ClickEvent event) {
that.moveFrontRequest.dispatch(null);
}}));
frameRegs.add(WidgetUtils.addMovementStartHandler(this.resizePanel, new SimpleEvent.Handler<HumanInputEvent<?>>() {
@Override public void onFire(HumanInputEvent<?> arg) {
that.resizeStartRequest.dispatch(null);
arg.stopPropagation();
}}));
frameRegs.add(WidgetUtils.addMovementStartHandler(this.rotatePanel, new SimpleEvent.Handler<HumanInputEvent<?>>() {
@Override public void onFire(HumanInputEvent<?> arg) {
that.rotateStartRequest.dispatch(null);
arg.stopPropagation();
}}));
frameRegs.add(WidgetUtils.addMovementStartHandler(this.dragOverlayPanel, new SimpleEvent.Handler<HumanInputEvent<?>>() {
@Override public void onFire(HumanInputEvent<?> arg) {
moveStartRequest.dispatch(null);
}}));
frameRegs.add(WidgetUtils.addMovementStartHandler(this.frameHeader, new SimpleEvent.Handler<HumanInputEvent<?>>() {
@Override public void onFire(HumanInputEvent<?> arg) {
moveStartRequest.dispatch(null);
}}));
frameRegs.add(tool.getToolEvents().addSelfMoveRequestEventHandler(new Handler<Point2D>() {
@Override public void onFire(Point2D offset) {
that.toolSelfMoveRequest(offset);
}}));
frameRegs.add(WidgetUtils.addMovementStartHandler(this, new SimpleEvent.Handler<HumanInputEvent<?>>() {
@Override public void onFire(HumanInputEvent<?> arg) {
that.onToolFrameSelected();
}}));
}
private void onToolFrameSelected()
{
if (false == DocumentUtils.isActiveElementTree(this.getElement())) {
focusPanel.setFocus(true); // take away focus from any others
}
}
protected void registerTransformHandlers() {
this.toolRegs.add(this.tool.getToolEvents().addMoveStartEventHandler(
new SimpleEvent.Handler<MouseEvent<?>>() {
@Override
public void onFire(MouseEvent<?> arg) {
moveStartRequest.dispatch(null);
}
}));
toolRegs.add(tool.getToolEvents().addLoadStartedEventHandler(new Handler<LoadStartedEventArgs>() {
@Override
public void onFire(LoadStartedEventArgs arg) {
toolLoadStarted(arg);
}
}));
toolRegs.add(tool.getToolEvents().addLoadEndedEventHandler(new Handler<Void>() {
@Override
public void onFire(Void arg) {
toolLoadEnded();
}
}));
}
@Override
public void setViewMode(boolean inViewMode) {
if (inViewMode == this._viewMode) {
return;
}
this._viewMode = inViewMode;
if (this._viewMode) {
frameRegs.clear();
this.setFloatingToolbarVisible(false);
}
else {
this.reRegisterFrameHandlers();
}
this.tool.setViewMode(this._viewMode);
}
@Override
public CanvasTool<? extends ElementData> getTool() {
return this.tool;
}
@Override
public HandlerRegistration addCloseRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.closeRequest.addHandler(handler);
}
@Override
public HandlerRegistration addMoveStartRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.moveStartRequest.addHandler(handler);
}
@Override
public HandlerRegistration addMoveBackRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.moveBackRequest.addHandler(handler);
}
@Override
public HandlerRegistration addMoveFrontRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.moveFrontRequest.addHandler(handler);
}
@Override
public HandlerRegistration addResizeStartRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.resizeStartRequest.addHandler(handler);
}
@Override
public HandlerRegistration addRotateStartRequestHandler(SimpleEvent.Handler<Void> handler) {
return this.rotateStartRequest.addHandler(handler);
}
@Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler)
{
return this.addDomHandler(handler, MouseDownEvent.getType());
}
@Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler)
{
return this.addDomHandler(handler, MouseUpEvent.getType());
}
@Override
public Point2D getToolSize() {
// Client size is not what we want, but we use it as a fallback (it includes padding, we want without)
return ElementUtils.getElementClientSize(this.tool.asWidget().getElement());
}
@Override
public Point2D getToolOffsetInFrame()
{
/*Element toolPanelElement = this.toolPanel.getElement();
Point2D offset = ElementUtils.getElementOffsetPosition(toolPanelElement);
Rectangle paddingRect = ElementUtils.tryGetPaddingRectangle(toolPanelElement);
if (null != paddingRect) {
offset = offset.plus(paddingRect.getCorners().topLeft);
}
return offset;*/
return ElementUtils.getElementAbsolutePosition(this.toolPanel.getElement())
.minus(ElementUtils.getElementAbsolutePosition(this.getElement()));
}
// public void setToolPosition(Point2D pos)
// {
// Point2D toolPosition = new Point2D(
// this.toolPanel.getAbsoluteLeft(), this.toolPanel.getAbsoluteLeft());
// Point2D framePosition = new Point2D(
// this.getAbsoluteLeft(), this.getAbsoluteLeft());
// Point2D actualPosition = pos.minus(toolPosition.minus(framePosition));
//
// ElementUtils.setElementPosition(this.getElement(), actualPosition);
// }
/**
* Note: make sure the size here does NOT include padding/margin/border of the tool, otherwise
* getToolSize and setToolSize will not be compatible (will be using different values.)
*/
@Override
public Point2D setToolSize(Point2D size) {
Point2D newSize = this.setResolvedToolSize(size);
this.onResize();
return newSize;
}
private Point2D setResolvedToolSize(Point2D desiredSize)
{
Element toolElement = this.tool.asWidget().getElement();
switch (this.tool.getResizeMode())
{
case BOTH:
ElementUtils.setElementSize(toolElement, desiredSize);
return desiredSize;
case WIDTH_ONLY:
toolElement.getStyle().setWidth(desiredSize.getX(), Unit.PX);
return new Point2D(desiredSize);
case HEIGHT_ONLY:
toolElement.getStyle().setHeight(desiredSize.getY(), Unit.PX);
return new Point2D(this.getToolSize().getX(), desiredSize.getY());
case RELATIVE:
int uniformSize = (desiredSize.getX() + desiredSize.getY()) / 2;
Point2D newSize = new Point2D(uniformSize, uniformSize);
ElementUtils.setElementSize(toolElement, newSize);
return newSize;
case NONE:
default:
return this.getToolSize();
}
}
@Override
public int getTabIndex() {
return this.focusPanel.getTabIndex();
}
@Override
public void setAccessKey(char key) {
this.focusPanel.setAccessKey(key);
}
@Override
public void setFocus(boolean focused) {
setFloatingToolbarVisible(focused);
this.focusPanel.setFocus(focused);
}
@Override
public void setTabIndex(int index) {
this.focusPanel.setTabIndex(index);
}
@Override
public HandlerRegistration addBlurHandler(BlurHandler handler) {
RegistrationsManager regs = new RegistrationsManager();
regs.add(tool.getToolEvents().addBlurHandler(handler));
regs.add(this.focusPanel.addBlurHandler(handler));
return regs.asSingleRegistration();
}
@Override
public HandlerRegistration addFocusHandler(FocusHandler handler) {
RegistrationsManager regs = new RegistrationsManager();
regs.add(tool.getToolEvents().addFocusHandler(handler));
regs.add(this.focusPanel.addFocusHandler(handler));
return regs.asSingleRegistration();
}
/**
* Notifies the CanvasToolFrame that it is being dragged / not being dragged.
* The dragged state is actually a stack, so that if several different mechanisms
* want the frame to think it's being dragged, it will prevent one of them from
* turning off the drag state by mistake while the frame is still being considered dragged
* by another mechanism.
* This is used for knowing whether we should pass setActive commands in to the CanvasTool.
* @param isDragging
*/
@Override
public void setDragging(boolean isDragging)
{
this.draggingStackDepth += isDragging ? 1 : -1;
this.draggingStackDepth = Math.max(this.draggingStackDepth, 0);
if (this.draggingStackDepth > 0) {
// Hide the floating toolbar while dragging
this.setFloatingToolbarVisible(false);
this.addStyleName(CanvasResources.INSTANCE.main().drag());
this.addStyleName(CanvasResources.INSTANCE.main().toolFrameDragged());
}
else {
this.removeStyleName(CanvasResources.INSTANCE.main().toolFrameDragged());
this.removeStyleName(CanvasResources.INSTANCE.main().drag());
this.setFloatingToolbarVisible(true);
this.updateToolActive();
// heuristic - we assume we have just finished moving.
this.onTransformed();
}
}
/**
* Wraps CanvasTool.setActive so that if the tool frame is being dragged,
* it will not be set active until the operation ends.
* This is REQUIRED: because if the tool steals focus when it becomes active,
* the drag manager in the worksheet may receive a stop event immediately.
* @param isActive
*/
@Override
public void setActive(boolean isActive)
{
this._isActive = isActive;
this.updateToolActive();
}
private void toolLoadStarted(LoadStartedEventArgs args)
{
this.loadingPanel.removeStyleName(CanvasResources.INSTANCE.main().loadingFillerDim());
if (args.dimBackground)
{
this.loadingPanel.addStyleName(CanvasResources.INSTANCE.main().loadingFillerDim());
}
this.loadingPanel.setVisible(true);
}
private void toolLoadEnded()
{
this.loadingPanel.setVisible(false);
}
private void toolSelfMoveRequest(Point2D offset) {
// TODO find a more global way of handling view mode.
if (this._viewMode) {
return;
}
Point2D newPos = ElementUtils.getElementOffsetPosition(getElement()).plus(offset);
ElementUtils.setElementCSSPosition(getElement(), newPos);
this.onTransformed();
}
private void updateToolActive()
{
if (0 >= this.draggingStackDepth) {
this.tool.setActive(this._isActive);
setFloatingToolbarVisible(this._isActive);
if (this._isActive) {
this.addStyleName(CanvasResources.INSTANCE.main().activeToolFrame());
}
else {
this.removeStyleName(CanvasResources.INSTANCE.main().activeToolFrame());
}
}
}
private void setFloatingToolbarVisible(boolean floatingToolbarVisible)
{
if (null != this.floatingToolbar) {
floatingToolbarVisible &= (false == this._viewMode);
this.floatingToolbar.setEditedWidget(floatingToolbarVisible ? this : null);
}
}
@Override
public void onTransformed()
{
SchedulerUtils.OneTimeScheduler.get().scheduleDeferredOnce(onTransformCommand);
}
private void onResize()
{
this.onTransformed();
this.tool.onResize();
}
private void handleOnTransform()
{
if (null != this.floatingToolbar) {
this.floatingToolbar.updatePosition();
}
}
private void preventTouchScroll()
{
Widget widget = this;
widget.addDomHandler(new TouchStartHandler(){
@Override public void onTouchStart(TouchStartEvent event) {
event.preventDefault();
}}, TouchStartEvent.getType());
widget.addDomHandler(new TouchEndHandler(){
@Override public void onTouchEnd(TouchEndEvent event) {
event.preventDefault();
}}, TouchEndEvent.getType());
widget.addDomHandler(new TouchMoveHandler(){
@Override public void onTouchMove(TouchMoveEvent event) {
event.preventDefault();
}}, TouchMoveEvent.getType());
}
}