/* * 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 ccre.channel.BooleanInput; import ccre.channel.BooleanOutput; import ccre.channel.CancelOutput; import ccre.rconf.RConf; import ccre.rconf.RConf.Entry; import ccre.supercanvas.BaseChannelComponent; import ccre.supercanvas.Rendering; import ccre.supercanvas.SuperCanvasPanel; /** * A component allowing display of booleans. * * @author skeggsc */ public class BooleanDisplayComponent extends BaseChannelComponent<BooleanDisplayComponent.View> implements BooleanOutput { static enum View { RED_GREEN_LIGHT, WARNING_LIGHT, TEXTUAL } private static final long serialVersionUID = -5453098172677583207L; private boolean pressed; private boolean subscribed; private boolean inverted = false; private final BooleanInput inp; private transient CancelOutput unsubscribe; /** * Create a new BooleanDisplayComponent with a BooleanInput to read from. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the input. * @param inp the BooleanInput to read from. */ public BooleanDisplayComponent(int cx, int cy, String name, BooleanInput inp) { super(cx, cy, name); this.inp = inp; } /** * Create a new BooleanDisplayComponent. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the input. */ public BooleanDisplayComponent(int cx, int cy, String name) { this(cx, cy, name, null); } @Override public void channelRender(Graphics2D g, int screenWidth, int screenHeight, FontMetrics fontMetrics, int mouseX, int mouseY) { switch (activeView) { case RED_GREEN_LIGHT: int rad1 = Math.min(halfWidth / 3, halfHeight / 3); g.setColor(pressed ^ inverted ? Color.GREEN : Color.RED); g.fillOval(centerX - rad1, centerY - rad1, rad1 * 2, rad1 * 2); g.setColor(Color.BLACK); g.drawOval(centerX - rad1, centerY - rad1, rad1 * 2, rad1 * 2); break; case WARNING_LIGHT: int rad2 = Math.min(halfWidth / 2, halfHeight / 2); g.setColor(Color.BLACK); g.fillOval(centerX - rad2 - 2, centerY - rad2 - 2, rad2 * 2 + 4, rad2 * 2 + 4); g.setColor(pressed ^ inverted ? (System.currentTimeMillis() / 100) % 2 == 0 ? Color.RED : Color.BLACK : Color.GRAY); g.fillOval(centerX - rad2, centerY - rad2, rad2 * 2, rad2 * 2); if ((pressed ^ inverted) && (System.currentTimeMillis() / 100) % 2 == 0) { g.setColor(Color.WHITE); int rad3 = rad2 / 4; g.fillOval(centerX - rad3, centerY + rad3 * 3 / 2, rad3 * 2, rad3 * 2); g.fillRect(centerX - rad3, centerY - rad2 + rad3, rad3 * 2, rad2 - rad3 / 2); } break; case TEXTUAL: g.setFont(Rendering.labels); if (getPanel().editmode) { g.setColor(Color.BLACK); } else { g.setColor(pressed ^ inverted ? Color.GREEN : Color.RED); } String text = pressed ? "TRUE" : "FALSE"; g.drawString(text, centerX - g.getFontMetrics().stringWidth(text) / 2, centerY + g.getFontMetrics().getAscent() / 2); break; } } @Override public boolean onInteract(int x, int y) { return false; } @Override protected void onChangePanel(SuperCanvasPanel panel) { boolean hasPanel = panel != null; if (inp != null && hasPanel != subscribed) { if (unsubscribe != null) { unsubscribe.cancel(); unsubscribe = null; } if (hasPanel) { unsubscribe = inp.send(this); } subscribed = hasPanel; } } @Override public void set(boolean value) { pressed = value; } @Override protected void setDefaultView() { activeView = View.RED_GREEN_LIGHT; } @Override public Entry[] queryRConf() throws InterruptedException { return rconfBase(RConf.string("invert"), RConf.fieldBoolean(inverted)); } @Override public boolean signalRConf(int field, byte[] data) throws InterruptedException { switch (rconfBase(field, data)) { case 1: inverted = data[0] != 0; return true; case BASE_VALID: return true; default: return false; } } }