/* * Copyright 2014-2016 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; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics2D; import ccre.log.Logger; import ccre.rconf.RConf; import ccre.rconf.RConf.Entry; import ccre.rconf.RConfable; import ccre.supercanvas.components.channels.RConfComponent; /** * A base component of channel components, such as float, boolean, and event * inputs and outputs. * * @author skeggsc * @param <View> the type of the View enum used for this component. */ public abstract class BaseChannelComponent<View extends Enum<View>> extends DraggableBoxComponent implements RConfable { private static final long serialVersionUID = 6151244350551965041L; /** * The name of this channel as it is displayed on the box. */ protected final String name; /** * The active view of this channel. */ protected View activeView; /** * Create a new BaseChannelComponent. * * @param cx the X coordinate. * @param cy the Y coordinate. * @param name the name of the input. */ public BaseChannelComponent(int cx, int cy, String name) { super(cx, cy); this.name = name; setDefaultView(); } @Override public final void render(Graphics2D g, int screenWidth, int screenHeight, FontMetrics fontMetrics, int mouseX, int mouseY) { halfWidth = Math.max(70, g.getFontMetrics().stringWidth(name) / 2 + 5); halfHeight = 46; if (activeView == null) { setDefaultView(); } if (getPanel().editmode) { Rendering.drawBody(Color.YELLOW, g, this); g.setColor(Color.BLACK); g.drawString(name, centerX - halfWidth + 5, centerY - halfHeight + 1 + g.getFontMetrics().getAscent()); g.setColor(new Color(128, 128, 128, 128)); g.fillOval(centerX - halfWidth + 2, centerY + halfHeight - 10, 8, 8); g.fillOval(centerX - halfWidth + 11, centerY + halfHeight - 10, 8, 8); g.setColor(new Color(255, 0, 0, 128)); g.fillOval(centerX + halfWidth - 10, centerY + halfHeight - 10, 8, 8); } g.setColor(Color.BLACK); channelRender(g, screenWidth, screenHeight, fontMetrics, mouseX, mouseY); } /** * Sets the view mode of the channel to the default view mode. */ protected abstract void setDefaultView(); @Override public boolean onSelect(int x, int y) { if (getPanel().editmode && centerY + halfHeight - 10 <= y && y <= centerY + halfHeight - 2) { if (centerX - halfWidth + 2 <= x && x <= centerX - halfWidth + 10) { getPanel().add(new RConfComponent(x, y, "display config", this)); return true; } else if (centerX - halfWidth + 11 <= x && x <= centerX - halfWidth + 19) { View[] csts = activeView.getDeclaringClass().getEnumConstants(); activeView = csts[(activeView.ordinal() + 1) % csts.length]; return true; } else if (centerX + halfWidth - 10 <= x && x <= centerX + halfWidth - 2) { if (this.onDelete(false)) { getPanel().remove(this); } else { Logger.warning("Component deletion disallowed: " + this); } return true; } } return super.onSelect(x, y); } /** * A helper function for the channel implementations, so that they can just * specify their own RConf entries without having to worry about the default * ones that will be prepended. * * Use as in <code>return rconfBase(... entries ...);</code> * * @param userEntries the RConf entries from the subclass. * @return the list of RConf entries. */ protected Entry[] rconfBase(Entry... userEntries) { View[] cst = activeView.getDeclaringClass().getEnumConstants(); Entry[] out = new Entry[userEntries.length + 1 + cst.length]; out[0] = RConf.title(toString()); for (int i = 0; i < cst.length; i++) { out[1 + i] = RConf.button(cst[i] == activeView ? "[" + cst[i].name() + "]" : cst[i].name()); } System.arraycopy(userEntries, 0, out, cst.length + 1, userEntries.length); return out; } /** * @see #rconfBase(int, byte[]) */ protected static final int BASE_VALID = -1; /** * @see #rconfBase(int, byte[]) */ protected static final int BASE_INVALID = -2; /** * A helper function for the channel implementations, so that they can just * specify their own RConf entries without having to worry about the default * ones. * * If the given field is for a default entry, this function takes care of * doing whatever it means, and returns a negative number: -1 (BASE_VALID) * if the request was processed, or -2 (BASE_INVALID) if the request was * invalid. * * If not, it returns a value equal to the index in the user entries of the * interacted-with component. * * @param field the overall field number. * @param data the data of the command. * @return the index in the user entries array of the interacted-with field, * or a negative number if before that. */ protected int rconfBase(int field, byte[] data) { View[] cst = activeView.getDeclaringClass().getEnumConstants(); field -= 1; if (field < 0) { return BASE_INVALID; } if (field < cst.length) { activeView = cst[field]; return BASE_VALID; } return field - cst.length; } @Override public String toString() { return name; } /** * Called to render the channel-specific part of this component. * * @param g The graphics pen to use to render. * @param screenWidth The width of the screen. * @param screenHeight The height of the screen. * @param fontMetrics The metrics of the (monospaced) font. * @param mouseX The mouse X position. * @param mouseY The mouse Y position. */ protected abstract void channelRender(Graphics2D g, int screenWidth, int screenHeight, FontMetrics fontMetrics, int mouseX, int mouseY); }