/* * Copyright 2014-2015 Cel Skeggs. * * This file is part of the CCRE, the Common Chicken Runtime Engine. * * The CCRE is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with the CCRE. If not, see <http://www.gnu.org/licenses/>. */ package ccre.supercanvas.components.channels; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.io.Serializable; import ccre.channel.CancelOutput; import ccre.channel.EventCell; import ccre.channel.EventInput; import ccre.channel.EventOutput; import ccre.rconf.RConf.Entry; import ccre.supercanvas.BaseChannelComponent; import ccre.supercanvas.Rendering; import ccre.supercanvas.SuperCanvasPanel; /** * A component allowing interaction with events. * * @author skeggsc */ public class EventControlComponent extends BaseChannelComponent<EventControlComponent.View> { static enum View { ISOMETRIC_BUTTON, SQUARE_BUTTON, TEXTUAL } private static final long serialVersionUID = 5604099540525088534L; private transient long countStart; private final EventInput alternateSource; private final EventCell stat = new EventCell(); private transient CancelOutput unsubscribe; /** * Create a new EventControlComponent with a EventOutput to control. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the output. * @param out the EventOutput to control. */ public EventControlComponent(int cx, int cy, String name, EventOutput out) { this(cx, cy, name, null, out); } /** * Create a new EventControlComponent. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the output. */ public EventControlComponent(int cx, int cy, String name) { this(cx, cy, name, null); } /** * Create a new EventControlComponent, with an input channel to represent * the actual value as returned by the remote. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the output. * @param inp the EventInput to control. * @param out the EventOutput to control. */ public EventControlComponent(int cx, int cy, String name, EventInput inp, EventOutput out) { super(cx, cy, name); if (out != null) { stat.send(out); } alternateSource = inp; (inp != null ? inp : stat).send(new CountNotifier()); } private transient int clickWidth; @Override protected boolean containsForInteract(int x, int y) { switch (activeView) { case ISOMETRIC_BUTTON: return x >= centerX - halfHeight / 2 - 10 && x <= centerX + halfHeight / 2 + 10 && y >= centerY - halfHeight / 3 - 10 && y <= centerY + halfHeight / 3 + 20; case SQUARE_BUTTON: int rad = Math.min(halfWidth / 2, halfHeight / 2); return x >= centerX - rad && x <= centerX + rad && y >= centerY - rad - 10 && y <= centerY + rad; case TEXTUAL: return x >= centerX - clickWidth / 2 && x <= centerX + clickWidth / 2 && y >= centerY - 10 && y <= centerY + 20; default: return false; } } @Override public void channelRender(Graphics2D g, int screenWidth, int screenHeight, FontMetrics fontMetrics, int mouseX, int mouseY) { long count = (System.currentTimeMillis() - countStart); switch (activeView) { case ISOMETRIC_BUTTON: g.setColor(Color.ORANGE.darker()); int rel = count < 200 ? 3 : 10; g.fillOval(centerX - halfHeight / 2, 10 + centerY - halfHeight / 3, halfHeight, 2 * halfHeight / 3); g.fillRect(centerX - halfHeight / 2 + 1, 10 + centerY - rel, halfHeight - 1, rel); g.setColor(count < 200 ? Color.GREEN : Color.RED); g.fillOval(centerX - halfHeight / 2, 10 + centerY - halfHeight / 3 - rel, halfHeight, 2 * halfHeight / 3); break; case SQUARE_BUTTON: int rad = Math.min(halfWidth / 2, halfHeight / 2); g.setColor(Color.BLACK); g.fillRect(centerX - rad, centerY - rad, rad * 2, rad * 2); int trad = Math.round(rad * (1 - Math.min(1, Math.max(count / 500.0f, 0)))); g.setColor(Rendering.blend(Color.GREEN, Color.BLACK, count / 500.0f)); g.fillRect(centerX - trad, centerY - trad, trad * 2, trad * 2); break; case TEXTUAL: g.setFont(Rendering.labels); g.setColor(Color.BLACK); String text = count < 500 ? "CLICKED" : "CLICK"; clickWidth = g.getFontMetrics().stringWidth(text); g.drawString(text, centerX - g.getFontMetrics().stringWidth(text) / 2, centerY + g.getFontMetrics().getAscent() / 2); break; } } @Override public boolean onInteract(int x, int y) { stat.safeEvent(); return true; } @Override protected void setDefaultView() { activeView = View.ISOMETRIC_BUTTON; } private final EventOutput fakeOut = new FakeEventOutput(); private boolean isFakeSubscribed = false; @Override protected void onChangePanel(SuperCanvasPanel panel) { boolean hasPanel = panel != null; if (alternateSource != null && hasPanel != isFakeSubscribed) { if (unsubscribe != null) { unsubscribe.cancel(); unsubscribe = null; } if (hasPanel) { unsubscribe = alternateSource.send(fakeOut); } isFakeSubscribed = hasPanel; } } private static final class FakeEventOutput implements EventOutput, Serializable { private static final long serialVersionUID = 1493349644760515921L; @Override public void event() { // Do nothing. This is just so that we can make the remote end send // us data by subscribing. } } private final class CountNotifier implements EventOutput, Serializable { private static final long serialVersionUID = 2028623211384850963L; @Override public void event() { countStart = System.currentTimeMillis(); } } @Override public Entry[] queryRConf() throws InterruptedException { return rconfBase(); } @Override public boolean signalRConf(int field, byte[] data) throws InterruptedException { return rconfBase(field, data) == BASE_VALID; } /** * Provides an input that is fired when this button is pressed. * * @return the EventInput. */ public EventInput asInput() { return stat; } }