// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.editor.selection;
import com.google.collide.client.AppContext;
import com.google.collide.client.util.CssUtils;
import com.google.collide.client.util.Elements;
import com.google.collide.mvp.CompositeView;
import com.google.collide.mvp.UiComponent;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.client.Timer;
import elemental.css.CSSStyleDeclaration;
import elemental.html.Element;
/**
* A presenter and anchor renderer for a cursor.
*/
public class CursorView extends UiComponent<CursorView.View> {
/**
* Static factory method for obtaining an instance of the CursorView.
*/
public static CursorView create(AppContext appContext, boolean isLocal) {
View view = new View(appContext.getResources(), isLocal);
return new CursorView(view, isLocal);
}
public interface Css extends CssResource {
String caret();
String root();
String block();
}
public interface Resources extends ClientBundle {
@Source({"CursorView.css", "com/google/collide/client/editor/constants.css"})
Css workspaceEditorCursorCss();
}
static class View extends CompositeView<ViewEvents> {
private final Css css;
private Element caret;
private View(Resources res, boolean isLocal) {
this.css = res.workspaceEditorCursorCss();
setElement(createElement(isLocal));
}
private Element createElement(boolean isLocal) {
caret = Elements.createDivElement(css.caret());
Element root = Elements.createDivElement(css.root());
root.appendChild(caret);
root.getStyle().setZIndex(isLocal ? 1 : 0);
return root;
}
private boolean isCaretVisible() {
return caret.getStyle().getVisibility().equals(CSSStyleDeclaration.Visibility.VISIBLE);
}
private void setCaretVisible(boolean visible) {
caret.getStyle().setVisibility(
visible ? CSSStyleDeclaration.Visibility.VISIBLE : CSSStyleDeclaration.Visibility.HIDDEN);
}
private void setColor(String color) {
caret.getStyle().setBackgroundColor(color);
}
private void setBlockMode(boolean isBlockMode) {
if (isBlockMode) {
caret.addClassName(css.block());
} else {
caret.removeClassName(css.block());
}
}
}
interface ViewEvents {
// TODO: onHover, so we can show the label
}
private static final int CARET_BLINK_PERIOD_MS = 500;
private final Timer caretBlinker = new Timer() {
@Override
public void run() {
getView().setCaretVisible(!getView().isCaretVisible());
}
};
private final boolean isLocal;
private boolean isVisible = true;
private CursorView(View view, boolean isLocal) {
super(view);
this.isLocal = isLocal;
}
public Element getElement() {
return getView().getElement();
}
public void setVisibility(boolean isVisible) {
this.isVisible = isVisible;
/*
* Use display-based visibility since visibility-based visibility is used
* for blinking
*/
CssUtils.setDisplayVisibility2(getView().getElement(), isVisible);
if (isLocal) {
// Blink the local cursor
if (isVisible) {
caretBlinker.scheduleRepeating(CARET_BLINK_PERIOD_MS);
} else {
caretBlinker.cancel();
}
}
}
public boolean isVisible() {
return isVisible;
}
public void setColor(String color) {
getView().setColor(color);
}
/**
* @param isBlockMode If true, change the cursor into a block that covers the entire
* character.
*/
public void setBlockMode(boolean isBlockMode) {
getView().setBlockMode(isBlockMode);
}
void forceSolidBlinkState() {
if (!isLocal) {
return;
}
getView().setCaretVisible(true);
caretBlinker.scheduleRepeating(CARET_BLINK_PERIOD_MS);
}
}