package ch.unifr.pai.twice.widgets.client; /* * Copyright 2013 Oliver Schmid * 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. */ import java.util.HashMap; import java.util.Map; import ch.unifr.pai.twice.comm.serverPush.client.RemoteWidget; import ch.unifr.pai.twice.utils.device.client.UUID; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.canvas.dom.client.Context2d.TextAlign; import com.google.gwt.canvas.dom.client.Context2d.TextBaseline; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style.BorderStyle; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasValue; import com.google.gwt.user.client.ui.TextBox; public class MultiFocusTextBox extends Composite implements HasValue<String>{ private final Map<String, Cursor> cursors = new HashMap<String, Cursor>(); FlowPanel p = new FlowPanel(); private String value; AbsolutePanel multiFocus = new AbsolutePanel(); private final int cursorSpeed = 700; String[] colors = new String[]{"red", "blue"}; Cursor blueCursor = new Cursor("blue", UUID.createNew()); Cursor redCursor = new Cursor("red", UUID.createNew()); private final Context2d context; private TextBox textBox = new TextBox(); private final Canvas c; private Timer blinkTimer; private boolean cursorsVisible; public MultiFocusTextBox() { blinkTimer = new Timer(){ @Override public void run() { for(Cursor c : cursors.values()){ c.setVisible(cursorsVisible); } cursorsVisible = !cursorsVisible; } }; blinkTimer.scheduleRepeating(cursorSpeed); p.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); c = Canvas.createIfSupported(); c.getElement().getStyle().setBorderWidth(0, Unit.PX); c.getElement().getStyle().setProperty("outline", "none"); c.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { //TODO if it is a new device, create a new cursor with the repositionCursor(null, event.getRelativeX(c.getCanvasElement()), event.getRelativeY(c.getCanvasElement())); } }); multiFocus.insert(c, 0, 0, 0); initWidget(multiFocus); getElement().getStyle().setBorderStyle(BorderStyle.SOLID); getElement().getStyle().setBorderWidth(1, Unit.PX); c.getElement().getStyle().setMargin(5, Unit.PX); context = c.getContext2d(); context.setTextAlign(TextAlign.LEFT); context.setTextBaseline(TextBaseline.TOP); context.setFont("13px sans-serif;"); // TODO Auto-generated constructor stub // multiFocus.setVisible(false); multiFocus.setWidth("161px"); multiFocus.setHeight("28px"); } private void repositionCursor(String deviceId, int x, int y){ blueCursor.setPosition(findChar(x)); } private int findChar(int x){ StringBuilder b = new StringBuilder(); for(char c : value.toCharArray()){ b.append(c); double textWidth = context.measureText(b.toString()).getWidth(); if(x<textWidth){ double charWidth = context.measureText(String.valueOf(c)).getWidth(); if(textWidth-(charWidth/2.0)>x) return Math.max(b.length()-1, 0); else return b.length(); } } return b.length(); } private void registerCursor(String uuid, Cursor c){ cursors.put(uuid, c); } private void unregisterCursor(String uuid){ cursors.remove(uuid); } protected class Cursor extends HTML{ int x; int y; final String uuid; int position; HandlerRegistration reg; private Cursor(String color, String uuid){ this.uuid = uuid; getElement().getStyle().setBackgroundColor(color); setWidth("1px"); setHeight("18px"); } private void hide(){ multiFocus.remove(this); unregisterCursor(uuid); if(reg!=null){ reg.removeHandler(); reg = null; } } private void show(){ registerCursor(uuid, this); multiFocus.add(this); multiFocus.setWidgetPosition(this, x+5, y+5); reg = Event.addNativePreviewHandler(new NativePreviewHandler() { @Override public void onPreviewNativeEvent(NativePreviewEvent event) { if(event.getTypeInt() == Event.ONMOUSEUP && !c.getElement().isOrHasChild(Element.as(event.getNativeEvent().getEventTarget()))){ //TODO enable after testing // hide(); } } }); } public void setPosition(int position){ hide(); this.position = position; this.x = (int)Math.max(0, context.measureText(position<value.length() ? value.substring(0, position) : value).getWidth()); show(); } public int getPosition(){ return position; } } @Override public HandlerRegistration addValueChangeHandler( ValueChangeHandler<String> handler) { return addHandler(handler, ValueChangeEvent.getType()); } @Override public String getValue() { return value; } protected Cursor getOrCreateCursor(String uuid){ Cursor c = cursors.get(uuid); if(c==null){ c = new Cursor("blue", uuid); registerCursor(uuid, c); } return c; } protected Map<String, Cursor> getCursors(){ return cursors; } private void processInput(String uuid, int pos, char c){ if(pos<=value.length()){ setValue(value.substring(0, pos)+c+((pos==value.length())? "" : value.substring(pos))); } for(Cursor cursor : cursors.values()){ if(pos<=cursor.getPosition()){ cursor.setPosition(cursor.getPosition()+1); } } } @Override public void setValue(String value) { this.value = value; textBox.setValue(value); context.clearRect(0, 0, c.getOffsetWidth(), c.getOffsetHeight()); context.fillText(value, 0, 0); } @Override public void setValue(String value, boolean fireEvents) { setValue(value); //TODO } }