/** * Clickable * * This package provides buttons, check boxes, radio buttons, * list boxes, combo boxes, clickable panels and menus. * * It includes the following classes: * Clickable class * Button class * Checkbox class * CircleButton class * RadioButton class * ButtonGroup class * ListBox class * RadioButtonListBox class * ComboBox class * Menu class * ClickablePanel class (see more extensions in meter.pde) * ClickableGroup class * MenuBar class * * Copyright 2013-2014 by James Brink * Revised: Apr. 5, 2014 * Permission is given for noncommercial use of these classes * as long as the copyright notice is retained. *************************************************************/ /* Definitions: * item: A clickable item. That is an item that can * respond to a mouse click and has been * declared as a descendant of an Clickable. * compound item: An item that is composed of or contains other * items. Examples: ButtonGroup, ListBox. * simple item: An item that is not a compound item. Examples: * Button, ClickablePanel. *************************************************************/ /* Note: * Because many of classes contain methods with the same name, it * is difficult to tell which class a particular method belongs * to. To overcome this problem, the class name is included in the * line just before the begining of a method. *************************************************************/ //************************************************************* // Global variables used by both Clickables, ClickableGroups, // ClickableMeters, and ScrollBars. // // (All global variables and methods end with "_". boolean stillSearching_; int soX_ = 0; // scroll offsets for x and y which is used when int soY_ = 0; // there is scrolling /** * The global method sX_() are used by the user program to adjust * the x coordinate for scrolling. locX is the unscrolled x * coordinate. */ int sX_(int locX) { return locX - soX_; } // sX_ /** * The global method sY_() are used by the user program to adjust * the y coordinate for scrolling. locY is the unscrolled y * coordinate. */ int sY_(int locY) { return locY - soY_; } // sY_ /** * Rounds x to 2 decimal places */ float round2_(float x) { return round(100 * x)/100.0; } // round2_ /** * This method is used to determine the appropriate value * for the width and height in simplified constructors. If * vert is true, vv is selected and returned. If it is false, * hh is selected. */ int select_(boolean vert, int vv, int hh) { if (vert) return vv; else return hh; } // select_ //************************************************************* /** * Clickable is the ancestor of all buttons and other items that are * clickable. It provides some common fields and methods. * * Note: Some of the Clickable methods would be considered abstract * by Java programmers but Processing does not use "abstract". * They serve no useful purpose other than establishing the method * name and argument list and allow other methods to call them even * when they do nothing. * * Note: There are two uses of "select". In a compound item like * a ButtonGroup or a ListBox, it indicates an item that has been * selected or chosen. It also can be used for the selected * state of checkbox or radio button. For Buttons, the selected * color is used for both of these things. * * Clicking: * An item is deemed to have been clicked if the mouse is released * while the mouse is over that item. The programmer is responsible * for making sure that every clickable item is checked in the * mouseReleased method. This can be done directly by checking a * each particular item or more simply be including all clickable * items in a ClickableGroup. The following code is appropriate: * void mouseReleased() { * stillSearching_ = true; * myButton.checkForClick(); // repeat for every * // item not in ClickableGroup. * or easier: * myClickGroup.checkForClick(); // checks all items in the * // group - much easier! * } // mouseReleased * The checkForClick() sets the clicked flag on the item clicked. It * will also close any Combobox or menu that is open. It also does * things like toggle checkboxes and color the newly click item as being * selected. * * The programmer using this package is also responsible for * having the draw() method check for clicked items and taking any * required action. It is often useful for draw() to call a * clickable update method. The following code is appropriate: * clickablesFor each item that is not in a ClickableGroup: * stillSearching_ = true; * myButton.isOver(); * if (myButton.hasBeenClicked()) { * ... * } * Every item in Clickable group can be checked with similar code: * myClickGroup.isOver(); * if (myClickGroup.hasBeenClicked()) { * ... * } * isOver() marks individual simple Clickable * items as to whether the mouse cursor is over them so the draw method * knows whether the "over" color should be used for that item. The * hasBeenClicked() method returns true if the item has been clicked * so it can be processed as needed. It also clears the clicked * flag so the processing will not be repeated. * * The programmer can determined which clickable item was clicked * using one of these methods: * int getClickedItemNum() returns the subscript of the clicked item * Clickable getClickedItem() returns the item clicked * String getClickedItemLabel() returns the label of the clicked item * * When the clicked item is a compound item, it must provide * convenient access to the clicked simple item so that it never * necessary to repeat the search for the simple item. All * compound objects must provide a * int getClickedItemNum() * that allows one to access the item using the result as a subscript * into the array used to define the compound object. Note: the * permanent button of a combo box and menu is identified by the number * Clickable.COMBO_BUTTON. * * Responsibilities of Clickable methods: * * checkForClick(): For simple clickable items, the default * method should be adequate. But compound items and * the ClickableGroup's checkForClick() method must check * every simple item that it is responsible for until it finds * the clicked item! In addition, the ClickableGroups * checkForClick must record the item clicked: its subscript * and the item's object. Whenever a clicked item is disovered, * the "clicked" flag must be set. In many cases like * radio buttons and clickable panel, additional actions * should be taken - toggle the button or record the location * of the click. */ abstract class Clickable { public static final int NOTHING_CLICKED = -1; public static final int COMBO_BUTTON = 10000; // Denotes the "always" visible // button of a ComboBox or Menu. public static final int DISABLED_COLOR = #EEEEEE; String label; int x, y, w, h; // usage and meaning varies. Typically left x, top y, // width, and height of simple rectangular items like // Buttons and ClickablePanels. boolean visible; boolean enabled; boolean over; boolean clicked; // The item has been clicked and needs to be processed. boolean selected; // Default false. color[] c, overC; // Arrays holding colors. Size depends on // the situation. "c" is the normal color. "overC" // is used when the mouse if over a simple item. color borderColor; // the color of the border. Default black. Not // always used. int colorState; // Default 0; state = 1 implies selected. Multistate // buttons can have more states. Specifies the color // in the c and overC arrays that will be used. int selectedItemNum; // Number of (sub)item selected from some type of // of group. Primarily for groups and compound // items. 0 for simple items. int clickedItemNum; // Number of the (sub)item most recently clicked. // Primarily for groups and compound items. 0 // for simple items. boolean scrollable; //Determines if the item is scrollable. /** * Construct a Clickable. Sets the given label and initializes * other fields. MUST BE EXTENDED. */ Clickable (String theLabel) { label = theLabel; over = false; clicked = false; enabled = true; visible = true; selected = false; c = new color[1]; overC = new color[1]; c[0] = #EEEEEE; // default colors - just to make sure there is a color overC[0] = #CCCCCC; borderColor = #000000; // default border color. colorState = 0; selectedItemNum = 0; // default for simple item clickedItemNum = 0; // default for simple item scrollable = true; } // constructor Clickable /** * Sets the clickable as being visible true or false. * SHOULD BE OVERRIDEN IN COMPOUND ITEMS. */ // Clickable void setVisible(boolean b) { visible = b; } // setVisible /** * Sets the enabled value for the item. * SHOULD BE OVERRIDEN IN COMPOUND ITEMS. */ // Clickable void setEnabled(boolean b) { enabled = b; } // setEnabled /** * Sets value of x. See individual class descriptions for meaning. */ // Clickable void setX(int xVal) { x = xVal; } // setX /** * Returns x. See individual class descriptions for meaning. */ // Clickable int getX() { return x; } // getX /** * Sets value of y. See individual class descriptions for meaning. */ // Clickable void setY(int yVal) { y = yVal; } // setY /** * Returns y. See individual class descriptions for meaning. */ // Clickable int getY() { return y; } // getY /** * Sets value of w. See individual class descriptions for meaning. */ // Clickable void setW(int wVal) { w = wVal; } // setW /** * Returns w. See individual class descriptions for meaning. */ // Clickable int getW() { return w; } // getW /** * Sets value of h. See individual class descriptions for meaning. */ // Clickable void setH(int hVal) { h = hVal; } // setH /** * Returns h. See individual class descriptions for meaning. */ // Clickable int getH() { return h; } // getH /** * Set the label of a clickable. * Rarily needs to be overriden. */ // Clickable void setLabel(String theLabel) { label = theLabel; } // setLabel /** * Returns the Clickable's label. Trims the label in case * blanks were added. Rarily needs to be overriden. */ // Clickable String getLabel() { return trim(label); } // getLabel /** * When called by the user's mouseReleased() method, it * checks the item to see if it has been clicked. If so, * it sets the item's "clicked" field as true so it can be * processed at a later time and sets clickedItemNum and * possibly selectedItemNum. Global "stillSearching_" indicates if * the clicked item still is being searched for. It is false if * the clicked item has been found. The method takes care of any * special actions that is carried out by the item. For example, * a checkbox is toggled when it has been clicked. IT MUST BE * OVERWRITTEN BY COMPOUND ITEMS OR ITEMS THAT REQUIRE SPECIAL * ACTIONS LIKE CheckBoxes. */ // Clickable boolean checkForClick() { if (!visible || !enabled) return false; if (over) { clicked = true; stillSearching_ = false; return true; } return false; } // checkForClick /** * Returns true if the item has been clicked. It must set * "clicked" to false. The function must be called while * processing items typically in draw or submethod called by * draw. */ // Clickable boolean hasBeenClicked() { boolean c; c = clicked; clicked = false; return c; } // hasBeenClicked /** * Returns the number (subscript) if the clickable has been clicked. * Typically used to determine the clicked item. Rarely needs to be * overridden for simple items as the item number is not used. * IT MUST BE OVERRIDDEN BY COMPOUND ITEMS. */ // Clickable int getClickedItemNum() { boolean c; return 0; // This should be 0 for simple item. } // getClickedItemNum /** * Used to specify the color state if the button has been * "selected". True means use selected colors, false * means use normal colors. Normally used with radio * buttons and checkboxes. Will work even if there is only * one color state. Picks the last state if in a multistate * button. Override as needed. */ // Clickable void setSelected(boolean b) { selected = b; if (selected) colorState = max(0, c.length - 1); else colorState = 0; } // setSelected /** * Returns selected. Rarely needs to be overridden */ // Clickable boolean getSelected() { return selected; } // getSelected /** * Specifies the selectable subitem's subscript in groups or * compound items (like ListBox). * OVERRIDE TO PROVIDE ERROR PROTECTION OR SET SUBITEM * VALUE. */ // Clickable void setSelectedItemNum(int item) { selectedItemNum = item; } // setSelectedItemNum /** * Returns the item selected by this clickable. Rarily needs * to be overridden. */ // Clickable int getSelectedItemNum() { return selectedItemNum; } // getSelectedItemNum /** * Returns the selected item. By default (appriopriate for simple * items) it returns itself. MUST BE OVERRIDDEN BY COMPOUND ITEMS. */ // Clickable Clickable getSelectedItem() { return this; } // getSelectedItem /** * Returns label of selected item. By default, it assumes * its own label. MUST BE OVERRIDDEN BY COMPOUND OBJECTS. */ // Clickable String getSelectedItemLabel() { return label; } /** * Returns the specified item (as determined by the subscript). * MUST BE OVERRIDDEN BY COMPOUND OBJECTS. Some error protection * would be helpful. */ // Clickable Clickable getItem(int b) { return this; } // getItem /** * Sets the colorState. Value is limited by the legal subscripts * of color vector c. Overrides the method in Clickable. * (Normal state is 0, selected is color state 1. Multistate * items could have more states.) */ // Clickable void setColorState(int state) { colorState = max(0, min(c.length - 1, state)); } // setColorState /** * Returns the colorState. Rarily needs to be overriden. */ // Clickable int getColorState() { return colorState; } // getColorState /** * Returns true if the mouse cursor is over the object. * If "stillSearching_" is false, the clicked item has already * been found and false is returned. In compound items, it * must check each of its subitems. * MUST BE OVERRIDDEN. */ // Clickable boolean isOver() { over = false; return over; } // isOver /** * doNotSelect should be true for a Checkbox (and anything else * that is toggled). This is required if the toggled item is in * in ListBox which normally selects an item when it is clicked. * The toggled item should not be selected as that changes its * colorState. OVERRIDE in any toggled item. */ boolean doNotSelect() { return false; } // doNotSelect /** * This routine is designed to hide a dropdown list * in a ComboBox or Menu and anything else that has something * that should be hidden when the user clicks anywhere on * the sketch. It must be called in mouseReleased() * whenever it might be needed. This is done automatically * in a ClickableGroup. The default does nothing. MUST BE * OVERRIDDEN BY ITEMS LIKE ComboBoxes AND Menus THAT SHOULD * HIDE THERE ListBox WHEN THE SKETCH IS CLICKED. */ // Clickable void hideSomething() { } // hide Something /** * Called by draw() to display (draw) the selectable item. * MUST BE OVERRIDDEN. */ // Clickable void display() { text(label, 20, 20); } // display /** * Adjusts the item's x coordinate when the item is scrolled. * It changes nothing if the item is not scrolled. Notice that * it is a little more sophisticated than the global method sX_. * Note: User programs will normally use the global method * sX_(locX). Rarely overridden. */ // Clickable int sX(int locX) { if (scrollable) { return locX - soX_; } else return locX; } // sX /** * Adjusts the item's y coordinate when the item is scrolled. * It changes nothing if the item is not scrolled. Notice that * it is a little more sophisticated than the global method sY_. * Note: User programs will normally use the global method * sY_(locY). Rarely overridden. */ // Clickable int sY(int locY) { if (scrollable) return locY - soY_; else return locY; } // sY /** * Used to specify that this item is or is not scrollable. */ // Clickable void setScrollable(boolean canScroll) { scrollable = canScroll; } // setScrollable /** * Set the color of the button border. Default: black */ // Button void setBorderColor(color theBorderColor) { borderColor = theBorderColor; } // setBorderColor } // class Clickable //************************************************************* /** * Describes a button. Unless overridden, the button shows * as a rectangle and the label is displayed left justified * in the button (assuming the button is wide enough). * * In addition to being a normal button, it is the basis for * other types of buttons. Multiple buttons are the basis * of combination clickable items such as a ListBox. */ class Button extends Clickable { /** * Constructs a button given its upper left corner, * its width and height, its label, its normal color, * and the mouse over color used during mouse overs. * Used in those case where a selected color is not * desired. Uses rectangular variables (x, y, w, and * h) and associated get and put methods in the normal * fashion. */ Button(int leftX, int topY, int theWidth, int theHeight, String theLabel, color stdColor, color overColor) { super(theLabel); commonConstruction(leftX, topY, theWidth, theHeight); // c and overC are dimensioned 1 by "super" which is fine as only // 1 set of colors is provided. c[0] = stdColor; overC[0] = overColor; } // Button constructor - simplist /** * Constructs a button given its upper left corner, * its width and height, its label, its normal color, * and the mouse over color used during mouse overs * and <> * * This constructor is normally used for radio buttons * and checkbox buttons and any other button that can be * selected. */ Button(int leftX, int topY, int theWidth, int theHeight, String theLabel, color stdColor, color overColor, color selected, color overSelected) { super(theLabel); commonConstruction(leftX, topY, theWidth, theHeight); c = new color[2]; overC = new color[2]; c[0] = stdColor; c[1] = selected; overC[0] = overColor; overC[1] = overSelected; } // Button constructor - allows the button to be selected /** * Constructs a button given its upper left corner, * its width and height, its label together with two * color vectors. The first is the set of normal * colors and the second is a set to be used during mouse * overs. The vectors must have same the length. (The vectors * can be the same.) The length of these vectors determines * the number of states. Normally recommended for multi-state * buttons. * Note: numColors is not actually used. It was * included as a workaround for a "bug" in Processing.js. * Note: Use nextState() directly or indirectly in the draw() * method to changes states. */ Button(int leftX, int topY, int theWidth, int theHeight, String theLabel, int numColors, color[] stdColor, color[] overColor) { super(theLabel); commonConstruction(leftX, topY, theWidth, theHeight); c = stdColor; overC = overColor; } // ButtonConstruct - multi state button /** * A private method used by the constructors. */ // Button void commonConstruction(int leftX, int topY, int theWidth, int theHeight) { x = leftX; y = topY; w = theWidth; h = theHeight; } // commonConstruction /** * Draws the button using the colors determined by * colorState. The location and size are determined * by the constructor. If visible is false, then the * button is ignored. If enabled is false, it is drawn * with a special color and the label is not shown. */ // Button void display(){ color curColor; if (!visible) return; stroke(0); // the boundary lines are black if (enabled) { if (over) { curColor = overC[colorState]; } else { curColor = c[colorState]; } } else curColor = DISABLED_COLOR; fill(curColor); stroke(borderColor); rect(sX(x), sY(y), w, h); if (enabled) { // draw label only if enabled fill(0); //Use black lettering text(label, sX(x) + 4, sY(y) + h/2 + 5); } } // display /** * Used by a checkbox button to change states after the button * is clicked. Normally used when the button is clicked. Note: * in two state button like checkboxes, c.length = 2 so, the * button toggles between colorStates 0 and 1. */ // Button int toggle() { if (colorState == 0) { colorState = max(1, c.length - 1); selected = true; } else { colorState = 0; selected = false; } return colorState; } // toggle /** * Used by a multiState button to determine its next * state. Normally used when the button is clicked. * Operates in a round-robin fashion so the highest * state is followed by state 0. */ // Button int nextState() { colorState = (colorState + 1) % c.length; return colorState; } // nextState /** * Determines if the mouse cursor is over the button. * Always sets the "over" properly. Always returns false * if stillSearching_ is false. */ // Button boolean isOver() { if (enabled && visible) { if (stillSearching_) { over = mouseX >= sX(x) && mouseX <= sX(x) + w && mouseY >= sY(y) && mouseY <= sY(y) + h; if (over) { stillSearching_ = false; } } else { over = false; } } else { over = false; } return over; } // isOver } // class Button //************************************************************* /** * Works like a regular button but has a circular shape instead * of the retangular shape. The label is typed to the right of * of the CircleButton. Uses the rectangular variables and methods * but the meaning is somewhat different. x and y are the center * of the circle. w is horizontal radius of and ellipse forming * the circle while h is the vertical radius. */ class CircleButton extends Button { /** * Constucts a Circular button with only 1 state. */ CircleButton(int centerX, int centerY, int radius, String theLabel, color stdColor, color overColor) { super(centerX, centerY, radius, radius, theLabel, stdColor, overColor); } // constructor CircleButton with only 1 state. /** * Constructs a Circular buktton with two states - regular and * selected. */ CircleButton(int centerX, int centerY, int radius, String theLabel, color stdColor, color overColor, color selected, color overSelected) { super(centerX, centerY, radius, radius, theLabel, stdColor, overColor, selected, overSelected); } // constructor CircleButton with selected /** * Constructs a multistate Circle button given its center * and radius, its label, and two color vectors. The first * is the set of normal colors and the second is a set of * mouse over colors to be used during mouse overs. (The * vectors can be the same.) The length of these vectors * (which must be the same), determines the number of states. * Normally recommended for multi-state buttons. (NumColors * is not really used but included as a workaround of a "bug" * in Processing.js.) * Note: Use nextState() directly or indirectly in the draw() * method to changes states. */ CircleButton(int centerX, int centerY, int radius, String theLabel, int numColors, color[] stdColor, color[] overColor) { super(centerX, centerY, radius, radius, theLabel, numColors, stdColor, overColor); c = stdColor; overC = overColor; } // ButtonConstruct - multi state switch /** * Draws the circle button using the colors determined by * colorState. The location and size are determined * by the constructor. If visible is false, then the * button is ignored. If enabled is false, it is drawn * with a special color and the label is not shown. */ // CircleButton void display(){ color curColor; if (!visible) return; stroke(borderColor); if (enabled) { if (over) { curColor = overC[colorState]; } else { curColor = c[colorState]; } } else curColor = DISABLED_COLOR; fill(curColor); ellipse(sX(x), sY(y), 2 * w, 2 * h); if (enabled) { fill(0); // show label to the right of the circle. text(label, sX(x) + w + 6, sY(y) + 5); } } // display /** * Determines if the mouse cursor is over the button. * Always sets the "over" properly. Always returns false * if stillSearching_ is false. */ // CircleButton boolean isOver() { if (enabled && visible) { if (stillSearching_) { over = (mouseX - sX(x)) * (mouseX - sX(x)) + (mouseY - sY(y)) * (mouseY - sY(y)) <= h * h; if (over) stillSearching_ = false; } else { over = false; } } else { over = false; } return over; } // isOver } // class CircleButton //************************************************************* /** * Draws a button that looks like a typical check box. * The label is moved right of box. It automatically * toggles when clicked. The box is black when checked. Uses * rectangular variables (x, y, w, and h) and associated get * and put methods in the normal fashion. */ class Checkbox extends Button { boolean showPanel; /** * Creates a Checkbox without showing a panel around the button. * The user must click the check box. */ Checkbox(int leftX, int topY, String theLabel) { super(leftX, topY, 14, 14, " " + theLabel, #FFFFFF, #E0E0E0, #000000, #707070); showPanel = false; } // constructor for Checkbox /** * Creates a Checkbox inside a button. The user can click anywhere * inside the button. The button uses the same colors whether or * not the Checkbox is selected. Instead a special part of the * display method draws a white or black box depending on whether * the Checkbox is selected. Note: It is probably best to not use * both styles of checkboxes in the same sketch unless this one is * used in menus. */ Checkbox(int leftX, int topY, int theWidth, int theHeight, String theLabel, color stdColor, color overColor) { super(leftX, topY, theWidth, theHeight, " " + theLabel, stdColor, overColor, stdColor, overColor); showPanel = true; } // constructor /** * Returns true if the Checkbox has been clicked. Because it * is a simple Clickable item, clickedItemNum and * slectedItemNum are already set properly, so it just toggles * the color state and selected and sets clicked true. It should * NOT be used as a radio button in a group because it toggles * automatically. */ // Checkbox boolean checkForClick() { if (!visible || !enabled) return false; if (over) { // over is used when the CheckBox is in a menu or ComboBox // isOver is used when it is not. toggle(); return true; } else { return false; } } // checkForClick /** * Display the Checkbox with or without a button. If * showPanel is false, the CheckBox is square with label on the right. * If it true, "super" draws a standard button and box is drawn on * top of it. */ // Checkbox void display() { super.display(); if (showPanel && visible && enabled) { stroke(#000000); if (selected) { fill(#000000); } else fill(#FFFFFF); rect(sX_(x) + 5, sY(y) + 5, 10, 10); } } // display /** * This method tells a ListBox including ComboBoxes and Menus * not to select this item because a Checkbox automatically takes * care of being selected all by itself. */ // CheckBox boolean doNotSelect() { return true; } // doNotSelect } // class Checkbox //************************************************************* /** * Creates a button that looks like a traditional radio * button. The circle is black when checked. It is same as a * CircleButton but has a aimplified setup. Uses the rectangular * variables and methods but the meaning is somewhat different. * x and y are the center of the circle. w is horizontal radius * of and ellipse forming the circle while h is the vertical radius. */ class RadioButton extends CircleButton { RadioButton(int centerX, int centerY, String theLabel) { super (centerX, centerY, 8, theLabel, #FFFFFF, #CCCCCC, #000000, #505050); } // constructor for RadioButton } // class RadioButton //************************************************************* /** * A button group allows handling 2 (or actially 1) or more button * as a group. Serves as the basis for ListBox, ComboBox and Menu. * Group handling includes: * Displaying all buttons in the group * Checking to see if a button in the group has been clicked * Checking to see the cursor is over a button in the group * Marking a button in the group as being selected (and unselected * all the other buttons. Allows using the group as radio * push buttons. * Note: if one doesn't want to show selected buttons, just make * standard colors and the selected colors the same or use buttons * that don't have selected colors. * * A ButtonGroup does not use the rectangular coordinates x, y, w, * and h and the related get and put methods although the buttons * in the group will use them. */ class ButtonGroup extends Clickable { Button[] buttonArray; String selectedLabel; /** * Creates a button group given an array of buttons. Assumes the * first button has been selected. */ ButtonGroup(Button[] aButtonArray) { super("ButtonGroup"); buttonArray = aButtonArray; selectedItemNum = 0; // assumes first button is selected selectedLabel = buttonArray[selectedItemNum].getLabel(); setSelectedItemNum(selectedItemNum); // turns off other buttons.. } // constructor ButtonGroup /** * Sets all the ButtonArray's button's and the entire group * to enabled or disabled. */ // ButtonGroup void setEnabled(boolean b) { int i; enabled = b; for (i = 0; i < buttonArray.length; i++) buttonArray[i].setEnabled(b); } // setEnabled /** * Sets all the ButtonArray's button's and the entire group * to visible or hidden. */ // ButtonGroup void setVisible(boolean b) { int i; visible = b; for (i = 0; i < buttonArray.length; i++) buttonArray[i].setVisible(b); } // setVisible /** * Calling display will draw all the buttons in the group. * Note: each button has its own enabled setting. Buttons are * drawn in the same order as they appear in the supplied * array of buttons. */ // ButtonGroup void display() { int i; if (!visible) return; for (i = 0; i < buttonArray.length; i++) buttonArray[i].display(); } // display /** * If the mouse cursor is over the any of the buttons in * the ButtonGroup, it sets the ButtonGroup's and the * button's "over" to true. Checks all buttons in the * group setting the button's "over" appropiately. */ // ButtonGroup boolean isOver() { int i; boolean bb; over = false; if (enabled && visible) { for (i = 0; i < buttonArray.length; i++) { bb = buttonArray[i].isOver(); // note: the button has set stillSearching_ false if (bb) { over = true; } } } else { over = false; } return over; } // isOver /** * checkForClick() is * called by mouseReleased() method to mark the clicked * button as being "clicked". When a button is clicked, * the method assumes that this is the clicked item * if the cursor is "over" the button. IT MUST ONLY BE * CALLED IN THE mouseReleased() METHOD because it assumes * that some item has been clicked. It also assumes * that every item's "isOver()" has been called by the user * program's mouseReleased() method in order to correctly * determine every item's "over". The item's checkForClick * will carry out necessary actions if it has been clicked. */ // ButtonGroup boolean checkForClick() { int i; if (!visible || !enabled) return false; for (i = 0; i < buttonArray.length; i++) { if (buttonArray[i].over) { buttonArray[i].checkForClick(); clicked = true; selectedItemNum = i; clickedItemNum = i; return true; } } return false; } // checkForClick /** * Check buttons in the group to see if any have been * clicked. If so, returns the subscript of the (first) * item that has been clicked. If not, return false to * indicate no button was clicked. "clicked" is set * false to indicated that the item has been processed. */ // ButtonGroup boolean hasBeenClicked() { if (clicked == false) return false; clicked = false; return buttonArray[clickedItemNum].hasBeenClicked(); // should always be true } // hasBeenClicked /** * Returns the number (subscript) of the (last) button clicked. */ // ButtonGroup int getClickedItemNum() { return clickedItemNum; } // getclickedItemNum /** * Sets all the buttons in the group to colorState 0. * May not be desirable for radio button groups. * Useful for checkboxes, radio buttons (not in a * radio button group), and multistate buttons. */ // ButtonGroup void reset() { int i; for (i = 0; i < buttonArray.length; i++) buttonArray[i].setColorState(0); } // reset /** * The specified button (b) is selected after unselecting all the * other buttons in the group. This action is appropriate for * a radio button type group where only one button is to be selected * at one time. */ // ButtonGroup void setSelectedItemNum(int b) { int i; // unselect all the buttons for (i = 0; i < buttonArray.length; i++) { buttonArray[i].setSelected(false); } // select the selected button selectedItemNum = max(0, min(b, buttonArray.length-1)); buttonArray[selectedItemNum].setSelected(true); selectedLabel = buttonArray[selectedItemNum].getLabel(); } // setSelectedItemNum /** * Returns the number (subscript) of the selected item. */ // ButtonGroup int getSelectedItemNum() { return selectedItemNum; } // getSelectedItemNum /** * Returns the selected button as specifed by selectedItemNum */ // ButtonGroup Button getSelectedItem() { return buttonArray[selectedItemNum]; } // getSelectedItem /** * Returns the label of the selected item; */ // ButtonGroup String getSelectedLabel() { return selectedLabel; } // getSelectedLabel /* * Returns the ith button in the group. The first * button is i = 0. Error checks i. */ // ButtonGroup Button getItem(int i) { i = max(0, min(i, buttonArray.length-1)); return buttonArray[i]; } // getItem /** * Returns the number of buttons in the group. */ // ButtonGroup int getGroupLength() { return buttonArray.length; } // getGroupLength /** * Used to specify the this item is or is not scrollable. * Sets each of the buttons in the group accordingly. */ // ButtonGroup void setScrollable(boolean canScroll) { int i; for (i = 0; i < buttonArray.length; i++) { buttonArray[i].setScrollable(canScroll); } scrollable = canScroll; } // setScrollable } // class ButtonGroup //************************************************************* /** * This is basically a ButtonGroup which is displayed as a unit. * One of the buttons is selected. The standard * display is a set of buttons one above the other. But * a ListBox using one of the nontradional constructors is a * great way to create a radio button group! (But see * RadioButtonListBox for a standardized radio button group.) * * The first constructor does not use the rectangular variables * x, y, w, and h. The second and third constructors use x, y, * w and h and the associated get and put methods for the * optional panel. h = 0 implies the the optional panel is not * used. Note: Changing x, y, and w does not automatically * change the location of the buttons in the ListBox. */ class ListBox extends Clickable { final int ITEM_HEIGHT = 20; Button[] buttonListArray; ButtonGroup buttonListGrp; boolean showSelected; // The selected item is shown using the selected // color when true. color[] c, overC; int clickedItemNum; // The remaining fields are used only with the second and third // nontraditional constructors. See comments about rectangular // x, y, w, and h fields; color panelColor; // color of panel for nontraditional ListBox /** * Creates a list box given a list of labels for the * buttons in the list box. The upper left corner of the box * is specified. */ ListBox(int leftX, int topY, int theWidth, String label, String[] labels, color stdColor, color overColor, color selected, color overSelected) { this(leftX, topY, theWidth, 20, label); // the 20 is not used. int i; int yy; // save parameters c = new color[2]; c[0] = stdColor; c[1] = selected; overC = new color[2]; overC[0] = overColor; overC[1] = overSelected; buttonListArray = new Button[labels.length]; // create the buttons, the button group and set the selected // button. yy = topY; for (i = 0; i < buttonListArray.length; i++) { buttonListArray[i] = new Button(leftX, yy, theWidth, ITEM_HEIGHT, labels[i], stdColor, overColor, selected, overSelected); yy += ITEM_HEIGHT; } buttonListGrp = new ButtonGroup(buttonListArray); buttonListArray[selectedItemNum].setSelected(true); h = 0; // do not draw a panel for the buttons } // constructor ListBox /** * Creates a possibly nontradional ListBox given a ButtonGroup. * The Buttons in the group specify their relative location * (as compared to the leftX and topY parameters) and the * size as well as their colors. (Note: To allow the * buttons to specify their absolute location, just locate * the List box at (0, 0). */ ListBox (int leftX, int topY, String label, ButtonGroup btnGrp) { this(leftX, topY, 0, 0, label); int i; buttonListGrp = btnGrp; buttonListArray = btnGrp.buttonArray; // Move the buttons relative to the location of the // ListBox; for (i = 0; i < buttonListGrp.getGroupLength(); i++) { buttonListGrp.getItem(i).x += leftX; buttonListGrp.getItem(i).y += topY; } buttonListArray[selectedItemNum].setSelected(true); h = 0; // do not draw a panel for the buttons } // nontraditional constructor ListBox /** * Creates a nontradional ListBox given a ButtonGroup. The * Buttons in the group specify their relative location and * size as well as their colors. (Note: To allow the * buttons to specify their absolute location, just locate * the List box at (0, 0). In addition, it draws a panel * that is intended to contain the buttons. Its width, height * and color are additionl parameters. This is button panel * is not Clickable as it does not represent any particular * button. */ ListBox (int leftX, int topY, int theWidth, int theHeight, color buttonPanelColor, String label, ButtonGroup btnGrp) { this(leftX, topY, label, btnGrp); int i; w = theWidth; // width of panel, only in this construction h = theHeight; // height of panel, only in this construction panelColor = buttonPanelColor; } // second nontraditional constructor ListBox with a panel for buttons /** * NOT FOR USE EXCEPT BY OTHER CONSTRUCTORS!! This constructor * just sets some common parameters. It does not set the * ButtonListArray or ButtonListGrp!! So use of it with outside * of a constructor will cause errors! * * Sets the location of the upper left corner, the width, the * height, the label, clickedItemNum, and selectedItemNum. */ ListBox (int leftX, int topY, int theWidth, int theHeight, String label) { super(label); x = leftX; y = topY; w = theWidth; // not used in this construction h = theHeight; // not used in this construction clickedItemNum = NOTHING_CLICKED; selectedItemNum = 0; showSelected = true; } // basic constructor ListBox /** * Sets the enabled value for the ListBox and buttonListGrp. */ // ListBox void setEnabled(boolean b) { enabled = b; buttonListGrp.setEnabled(b); } // setEnabled /** * Sets the visible value for the ListBox and buttonListGrp */ // ListBox void setVisible(boolean b) { visible = b; buttonListGrp.setVisible(b); } // setEnabled /** * Displays the Listbox by displaying its button group. */ // ListBox void display() { if (!visible) return; if (h > 0) { // if request, draw a panel behind the buttons fill(panelColor); rect(sX(x), sY(y), w, h); } buttonListGrp.display(); } // display /** * If the mouse cursor is over the any of the buttons in * the list's ButtonGroup, it sets the list's, the * ButtonGroup's and the button's "over" to true. Always * checks every item in the group. Always returns false if * stillSearching_ is false. */ // ListBox boolean isOver() { if (!visible || !enabled) over = false; else { over = buttonListGrp.isOver(); } return over; } // isOver /** * When called by the user's mouseReleased() method, assuming * that the ListBox is visible and enabled, it * checks its ButtonGroup to see if it has been clicked. If so, * it sets the both the button and its "clicked" field as true so * it can be processed at a later time. It sets clicked, * clickedItemNum, selectedItemNum and the buttonListGrp selected * item num. */ // ListBox boolean checkForClick() { if (!visible || !enabled) return false; clicked = buttonListGrp.checkForClick(); if (clicked) { clickedItemNum = buttonListGrp.getClickedItemNum(); if (showSelected && !buttonListGrp.getItem(clickedItemNum).doNotSelect()) { selectedItemNum = clickedItemNum; buttonListGrp.setSelectedItemNum(clickedItemNum); } } return clicked; } // checkForClick /** * Returns true if the ListBox has been clicked. Uses the * buttonListGrp.hasBeenClicked method to determine if any * its buttons has been clicked. It clears "clicked". */ // ListBox boolean hasBeenClicked() { // make sure the button group does any appropirate processing. clicked = false; return buttonListGrp.hasBeenClicked(); } // hasBeenClicked /** * Returns the subscript of the clicked item. */ // ListBox int getClickedItemNum() { return clickedItemNum; } // getClickedItem /** * Set the selected item num and specifies that item as * selected. */ // ComboBox void setSelectedItemNum(int item) { selectedItemNum = item; buttonListGrp.setSelectedItemNum(item); } // setSelectedItemNum /** * returns the number of the selected item */ // ListBox int getSelectedItemNum() { return selectedItemNum; } // getSelectedItemNum /** * Returns the selected button. (GetSelectedItemNum returns * its subscript.) */ // ListBox Button getSelectedItem() { return buttonListArray[getSelectedItemNum()]; } // getSelectedItem /** * Returns the label of the selected item. */ // ListBox String getSelectedLabel() { return buttonListGrp.getSelectedLabel(); } // getSelectedLabel /** * Returns the specified button in the list. ButtonListGrp * provides error protection if i is out of range */ // ListBox Button getItem(int i) { return buttonListGrp.getItem(i); } // getItem /** * Returns the number of buttons in the list box. */ // ListBox int getGroupLength() { return buttonListGrp.getGroupLength(); } // getGroupLength /** * Used to specify the this item and all its buttons is or is * not scrollable. */ // ListBox void setScrollable(boolean canScroll) { buttonListGrp.setScrollable(canScroll); scrollable = canScroll; } // setScrollable /** * Returns a lot of information about the ListBox and its * Button Group */ // ListBox String toString() { String s, colStr; colStr = "undefined"; if (c != null) colStr = c[0] + " " + overC[0] + " " + c[1] + " " + overC[1]; s = "The label: " + label + "\nx: " + x + " y: " + y + " w: " + w + " h: " + h + "\npanelColor: " + panelColor + "\ncolors: " + colStr + "\nclickedItemNum: " + clickedItemNum + " selectedItemNum: " + selectedItemNum + "\nlength: " + getGroupLength(); if (c != null) { s += "c[0]: " + c[0] + " overC[0]: " + overC[0]; if (c.length > 1) s += " c[1]: " + c[1] + " overC[1]: " + overC[1]; } return s; } // toString } // class ListBox //************************************************************* /** * A RadioButtonListBox is identical to a ListBox except that * it creates RadioButtons instead of standard buttons. It will * put a panel under the buttons if usePanel is true. If you * want a RadioButtonListBox with special buttons, create a * ListBox using the second or third constructor. * * The rectangular fields x, y, w, and h and associated get and * put methods are used for the panel in the normal fashion. * panel. */ class RadioButtonListBox extends ListBox { RadioButtonListBox(int leftX, int topY, int theWidth, String label, String[] labels, Boolean usePanel, color buttonPanelColor) { super(leftX, topY, theWidth, theWidth, label); int RADIUS = 8; // should agree with RadioButton's radius. int i; int yy; panelColor = buttonPanelColor; buttonListArray = new Button[labels.length]; // create the buttons, the button group and set the selected // button. yy = topY; for (i = 0; i < buttonListArray.length; i++) { buttonListArray[i] = new RadioButton(leftX + RADIUS + 2, yy + RADIUS + 2, labels[i]); yy += ITEM_HEIGHT; } buttonListGrp = new ButtonGroup(buttonListArray); buttonListArray[selectedItemNum].setSelected(true); if (usePanel) { h = buttonListArray.length * ITEM_HEIGHT; } else { h = 0; } } // constructor RadioButtonListBox } // class RadioButtonListBox //************************************************************* /** * A ComboBox includes a single button with down arrow and * hideable, pulldown ListBox. One item in the ListBox is * selected and is shown in the single button that is always * visible. The ListBox appears when the single button is * clicked and disappears when any place in the sketch is * clicked. * * The rectagular fields x, y, and w and the associated get' * put methods are used for the combo button. h is * automatically set at 20 and can be changed only at the * users risk. Note: Changing x, y, and w does not * automatically change the location of the buttons in the * ComboBox. */ class ComboBox extends Clickable { final int WIDTH_DOWN_ARROW = 20; final int ITEM_HEIGHT = 20; Button[] comboListArray; Button comboButton; Button comboArrow; ListBox comboListBox; color[] c, overC; boolean listBoxVisible; int pnlHeight = 0; // Used only for the nontraditional construction boolean showSelectedLabel; /** * Creates a ComboBox given the upper left corner, width and * height, the array of labels for the list box, and 4 colors. * If you do not want the selected item to show with the * special colors, just make the 3 and 4 colors the same as * the first two. */ ComboBox(int leftX, int topY, int theWidth, String[] labels, color stdColor, color overColor, color selected, color overSelected) { super("ComboBox"); try { int i; commonInitialization(leftX, topY, theWidth, 0, labels[0], stdColor, overColor, selected, overSelected); // create the ListBox comboListBox = new ListBox(leftX, topY + ITEM_HEIGHT + 2, theWidth - 5, "ComboBox ListBox", labels, stdColor, overColor, selected, overSelected); listBoxVisible = false; comboListBox.setSelectedItemNum(selectedItemNum); } catch (Exception e) { println("Error in constructor for ComboBox: " + e); } } // constructor ComboBox /** * Creates a nontradional ComboBox given a ButtonGroup. The * Buttons in the group specify their *relative* location and * size as well as their colors. "theWidth" determines the * width of the permanent button and arrow. A optional panel * is added to hold the buttons when they are visible, * if panelHeight > 0. This panel is not clickable as it does * not specy any particular one of the buttons. */ ComboBox(int leftX, int topY, int theWidth, int panelHeight, ButtonGroup aBtnGrp, color stdColor, color overColor, color selected, color overSelected) { super("ComboBox"); int i; commonInitialization(leftX, topY, theWidth, panelHeight, aBtnGrp.getSelectedLabel(), stdColor, overColor, selected, overSelected); // create the ListBox comboListArray = aBtnGrp.buttonArray; comboListBox = new ListBox(leftX, topY + ITEM_HEIGHT + 2, "ComboBox ListBox", aBtnGrp); listBoxVisible = false; } // nontraditional constructor ComboBox /** * This constructor does almost nothing and is designed to be used * by Menu and possibly other extensions that will take care of * the construction by themselves. Typically comboArrow should be * set to null so the ComboBox methods will not use to draw it. */ ComboBox(String label) { super(label); } // simplistic constructor for Menu /** * Provides common initialization for the constructors. Not used for * Menus. */ // ComboBox void commonInitialization(int leftX, int topY, int theWidth, int panelHeight, String selectedLabel, color stdColor, color overColor, color selected, color overSelected) { // common initialization x = leftX; y = topY; w = theWidth; pnlHeight = panelHeight; c = new color[2]; c[0] = stdColor; c[1] = selected; overC = new color[2]; overC[0] = overColor; overC[1] = overSelected; selectedItemNum = 0; // picks first element in list // create the permanent button and arrow comboButton = new Button(leftX, topY, w - WIDTH_DOWN_ARROW, ITEM_HEIGHT, selectedLabel, stdColor, overColor); comboArrow = new Button(leftX + w - WIDTH_DOWN_ARROW, topY, WIDTH_DOWN_ARROW, ITEM_HEIGHT, " v", #FFFFFF, #EEEEEE); showSelectedLabel = true; // assume box should show selected label } // commonInitialization /** * Sets the enabled value for the entire comboBox and * its different parts. */ // ComboBox void setEnabled(boolean b) { enabled = b; comboButton.setEnabled(b); comboArrow.setEnabled(b); comboListBox.setEnabled(b); } // setEnabled /** * Sets the visible value for the entire comboBox and * its different parts. */ // ComboBox void setVisible(boolean b) { visible = b; comboButton.setVisible(b); comboArrow.setVisible(b); comboListBox.setVisible(b); } // setVisible /** * Sets the label of the menu item and it displays the label * it the comboButton unless it is a Menu. Automatically * used by Menu. */ // ComboBox void setLabel(String theLabel) { if (comboArrow != null) comboButton.setLabel(theLabel); label = theLabel; } // setLabel /** * Sets a permanent label for the menu items or comboboxes * whose label should not change to match the selection. It * sets the label. Can be used for nontraditional ComboBoxes. */ // ComboBox void setPermanentLabel(String theLabel) { showSelectedLabel = false; comboButton.setLabel(theLabel); label = theLabel; } // setPermanentLabel /** * Display the comboBox. It will include the ListBox * if it is visible. */ // ComboBox void display() { if (!visible) return; if (listBoxVisible && pnlHeight > 0){ fill(c[0]); rect(sX(x), sY(y) + ITEM_HEIGHT + 2, w, pnlHeight); } comboButton.display(); if (comboArrow != null) { // For ComboBox, not for Menu comboArrow.display(); } line(sX(x), sY(y) + 21, sX(x) + w, sY(y) + 21); // An extra line to separate // the permanent box and the list. if (listBoxVisible) { comboListBox.display(); } } // display /** * If the mouse cursor is over the any of the buttons in * the list's ButtonGroup, it sets the list's, the * ButtonGroup's and the button's "over" to true, */ // comboBox boolean isOver() { over = false; if (comboButton.isOver()){ over = true; } // check the arrow if (comboArrow != null && comboArrow.isOver()) { over = true; } // check the listBox if (listBoxVisible && comboListBox.isOver()) { over = true; } return over; } // isOver /** * Hides the ListBox. Used whenever the user clicks on * the sketch unless the mouse cursor is over the ComboBox. * This should be called in user's mouseReleased method * before calling checkForClick(). It is included in the * ClickableGroup's checkForClick method and will be called * automatically. If the list box is not in a ClickableGroup * the programmer is responsible for making sure that it is * called. */ // ComboBox void hideSomething() { if (!over) listBoxVisible = false; } // hideSomething /** * When called by the user's mouseReleased() method, it * checks the item to see if it has been clicked. If so, * it sets the item's "clicked" field as true so it can be * processed at a later time. It determines whether the * permanent button was clicked or rather it was one of * the ListBox buttons. If it was the permanent button, * then clickedItemNum is set to clickable.COMBO_BUTTON. */ // ComboBox boolean checkForClick() { boolean b; if (!visible || !enabled) return false; if (comboButton.over || (comboArrow != null && comboArrow.over)) { // make sure that the button's checkForClick() is processed. b = comboButton.checkForClick(); if (!b && comboArrow != null) b = comboArrow.checkForClick(); clicked = true; clickedItemNum = COMBO_BUTTON; listBoxVisible = !listBoxVisible; comboButton.clicked = true; // in case the arrow was clicked return true; } else { // check the ListBox if (listBoxVisible && comboListBox.over) { comboListBox.checkForClick(); clicked = true; clickedItemNum = comboListBox.getClickedItemNum(); selectedItemNum = clickedItemNum; if (showSelectedLabel) // this is done for ComboBoxes but not Menus comboButton.label = comboListBox.getSelectedLabel(); listBoxVisible = false; return true; } } return false; } // checkForClick /** * Returns true if the ComboBox has been clicked. If so, * calls hasBeenClicked on the list box and it clears the * click. This method must be called by the draw() method * or some submethod called by draw(). */ // ComboBox boolean hasBeenClicked() { clicked = false; if (comboButton.hasBeenClicked() || comboListBox.hasBeenClicked()) return true; else return false; } // hasBeenClicked /** * Returns the subscript of the clicked item. (If it was the * permanent button was clicked, it returns * Clickable.COMBO_BUTTON.) */ // ComboBox int getClickedItemNum() { return clickedItemNum; } // getClickedItem /** * Set the selected item num and indirectly specifies * that item as being selected. */ // ComboBox void setSelectedItemNum(int item) { selectedItemNum = item; comboListBox.setSelectedItemNum(item); if (comboArrow != null) comboButton.label = comboListBox.getSelectedLabel(); } // setSelectedItemNum /** * Returns the number of the selected item */ // ComboBox int getSelectedItemNum() { return selectedItemNum; } // getSelectedItemNum /** * Returns the selected button in the comboListBox. */ // ComboBox Button getSelectedItem() { return comboListBox.getSelectedItem(); } // getSelectedItem /** * Returns the label of the selected item. */ // ComboBox String getSelectedLabel() { return comboButton.getLabel(); } // getSelectedLabel /** * Returns the specified button in the list. If i = * Clickable.COMBO_BUTTON, then it returns the permanent button. * ButtonGroup provides error protection for i. */ // ComboBox Button getItem(int i) { if (i == Clickable.COMBO_BUTTON) return comboButton; else { return comboListBox.getItem(i); } } // getItem /** * Returns the number of number of button in the * list box. */ // ComboBox int getGroupLength() { return comboListBox.getGroupLength(); } // getGroupLength /** * Used to specify the this item and all its components is or is * not scrollable. */ //ComboBox void setScrollable(boolean canScroll) { scrollable = canScroll; comboListBox.setScrollable(canScroll); comboButton.setScrollable(canScroll); if (comboArrow != null) comboArrow.setScrollable(canScroll); } // setScrollable String toString() { String s; s = "Label: " +label + "\nx: " + x + " y: " + y + " w: " + w //+ " h: " + h +" colors: " + c[0] + " " + overC[0] + "\nlistBoxVisible: " +listBoxVisible + " pnlHeight: " + pnlHeight + " clickedItemNum: " + clickedItemNum + " selectedItemNum: " + selectedItemNum + " length: " + getGroupLength() + "\n*** comboListBox: " + comboListBox.toString(); return s; } // toString } // class ComboBox //************************************************************* /** * A Menu includes a permanent button located in the menu bar and * drop-down, hideable ListBox. The supplied label is shown in * menu bar button. The ListBox appears when the menu-bar button * is clicked and disappears when any place in the sketch is * clicked. The MenuBar supplies the x-location and the colors. * By default, there are no special colors for the selectd item * in the menu even though there is one. This means that for a * created by the second constructor, if the supplied buttons have * menu selected colors they will be used. The user program is * responsible for specifying the width of the menu. Menus are * clickable items and should be added to the end of the * ClickableGroup item array. In many ways, a Menu is very similar * to a ComboBox except that by default selected items are not * colored in a special fashion and the label in the menu bar * button remains constant. * * Note: Menus are ordered in the menu bar in the order they are * created! (Not in the order they are listed in the clickable * group. */ class Menu extends ComboBox { /** * Creates a dropdown Menu given the width, the label of * the MenuBar button, the array of labels for the list box, * and the MenuBar. * * The rectangular fields x, y, w, and h are used to locate * the menu button in the MenuBar and (except for w) are * automatically set. They can be changed only at the user's * risk. */ Menu(int theWidth, String label, String[] labels, MenuBar theMenuBar) { super(label); try { commonInitialization(theMenuBar, theWidth, 0, label); // create the ListBox comboListBox = new ListBox(x, y + ITEM_HEIGHT + 2, theWidth, label + "ListBox", labels, c[0], overC[0], c[0], overC[0]); listBoxVisible = false; setScrollable(false); } catch (Exception e) { println("Error in constructor for Menu: " + e); } } // constructor Menu using a label array /** * Creates a dropdown Menu given the width of the MenuBar * button, the label for the MenuBar button, the array of * labels for the list box, the width of the list, and the * MenuBar. One unused parameter "ignore" is required * as a workaround because of "bug" in Processing.js. This * constructor and the first are the same except this allows * the width of the button in the menu bar (theWidth) to be * different than the list of the pulldown list. */ Menu(int theWidth, String label, int listWidth, String[] labels, MenuBar theMenuBar, boolean ignore) { super(label); try { commonInitialization(theMenuBar, theWidth, 0, label); // create the ListBox comboListBox = new ListBox(x, y + ITEM_HEIGHT + 2, listWidth, label + "ListBox", labels, c[0], overC[0], c[0], overC[0]); listBoxVisible = false; } catch (Exception e) { println("Error in constructor for Menu: " + e); } setScrollable(false); } // constructor Menu using a label array /** * Creates a possibly nontradional Menu given a ButtonGroup. * The Buttons in the group specify their *relative* location * and size as well as their colors. "theWidth" determines the * width of the permanent button in the MenuBar. An optional * panel is added to hold the buttons when they are visible, * if panelHeight > 0. This panel is not clickable as it * species none of the buttons. Note: The panel's width (if * used) is determined by "theWidth", not by the buttons. * Important note: The location given in the buttons in the * ButtonGroup is relative to the location of the menu button * in the MenuBar. Normally all the button should be given x * location 0. The y location of the first button should be 0. * The y location of additional buttons should be the y * location of the previous button plus the height of the * previous button. */ Menu(int theWidth, int panelHeight, String label, ButtonGroup aBtnGrp, MenuBar theMenuBar) { super(label); int i; commonInitialization(theMenuBar, theWidth, panelHeight, label); // create the ListBox comboListArray = aBtnGrp.buttonArray; comboListBox = new ListBox(x, y + ITEM_HEIGHT + 2, w, 0,/* ITEM_HEIGHT,*/ c[0], label + "ListBox", aBtnGrp); listBoxVisible = false; comboArrow = null; // not used for menus setScrollable(false); } // nontraditional constructor Menu using ButtonGroup /** * A private method that provides common initialization for the * constructors. It sets the colors and creates and locates the * menu bar button. */ // Menu void commonInitialization(MenuBar mBar, int theWidth, int panelHeight, String theLabel) { // common initialization x = mBar.xLocation(theWidth); y = 0; w = theWidth; pnlHeight = panelHeight; c = new color[1]; c[0] = mBar.getColor(); overC = new color[1]; overC[0] = mBar.getOverColor(); // create the permanent button comboButton = new Button(x, y, w, ITEM_HEIGHT, label, c[0], overC[0]); selectedItemNum = 0; // picks first element in list comboArrow = null; // not used for menus setPermanentLabel(label); } // commonInitialization } // class Menu //************************************************************* /** * This class illustrates that Clickable items do not have to be * based on buttons. In this case, the location of the click is * recorded and can be obtained by getClickX() and getClickY(). * A dummy function drawOnPanel() was added to make it easy to * extend the panel to include some kind of drawing function.) * The items in ClickableMeter.pde are based on this item. Uses * rectangular variables (x, y, w, and associated get and * put methods in the normal fashion. */ class ClickablePanel extends Clickable { int clickX, clickY; // the location of the mouse click boolean invisible; // is the panel invisible? boolean wholeSketch; // if true, panel occupies the entire sketch. /** * Construct a ClickablePanel given its location, width, height, * label (which is not displayed), and color. */ ClickablePanel (int leftX, int topY, int theWidth, int theHeight, String theLabel, color stdColor) { super (theLabel); x = leftX; y = topY; w = theWidth; h = theHeight; c[0] = stdColor; overC[0] = stdColor; mouseX = NOTHING_CLICKED; mouseY = NOTHING_CLICKED; clickX = -1; // Initialize to an impossible value. clickY = -1; invisible = false; wholeSketch = false; } // constructor ClickablePanel /** * Construct an invisible ClickablePanel given its location, width, height, * label (which is not displayed) but without any color. */ ClickablePanel (int leftX, int topY, int theWidth, int theHeight, String theLabel) { this (leftX, topY, theWidth, theHeight, theLabel, #000000); // the color is ignored when drawing the panel invisible = true; wholeSketch = false; } // constructor invisible ClickablePanel /** * Makes the entire sketch an invisible ClickablePanel given only a * label (which is not displayed). */ ClickablePanel (String theLabel){ this (0, 0, 0, 0, theLabel, #000000); // the parameters are ignored invisible = true; wholeSketch = true; } // constructor invisible ClickablePanel /** * Sets the clickable full sketch area to exclude the menu and/or * scrollBars. Assumes that constructor 2 or 3 have been used * to setup an invisible panel. Unfortunately the width and * height are not known until they have been set at the beginning * of the setup() method hence the reduced size can be determined * until after this has been done. Could be used with constructor * but this combination is not normally useful. Use in the * setup() method. */ // ClickablePanel void adjustPanel(boolean hasMenu, boolean hasScrollBars) { // start by setting the panel rectangle to the full sketch size. wholeSketch = false; x = 0; y = 0; w = width; h = height; // reduce the size for 20 pixel menu at the top of screen. if (hasMenu) { y = 20; h -= 20; } // reduce the size for 20 pixel scrollBars on the bottom and // right. if (hasScrollBars) { w -= 20; h -= 20; } } // adjustPanel /** * Draws the panel. This includes calling drawOnPanel(). */ // Clickable Panel void display () { color col; if (!visible) return; if(!invisible) { if (enabled) if (over) // useful for meter extensions col = overC[0]; // overC[0] = c[0] in unextended panels else col = c[0]; else col = DISABLED_COLOR; fill(col); stroke(borderColor); rect(sX(x), sY(y), w, h); } drawOnPanel(); } // display /** * getClickX returns clickX */ // ClickablePanel int getClickX() { if (scrollable) return clickX + soX_; else return clickX; } // getClickX /** * getClickY returns clickY */ // ClickablePanel int getClickY() { if (scrollable) return clickY + soY_; else return clickY; } // getClickY /** * Returns the x distance from the left edge to click. */ // ClickablePanel int getPanelX() { return getClickX() - x; } // getPanelX /** * Returns the y distance from the top edge to the click. */ // ClickablePanel int getPanelY() { return getClickY() - y; } // getPanelY /** * Sets the clickX value but it is constrained to area * of the panel. */ // ClickablePanel void setClickX(int newClickX) { clickX = constrain(newClickX, x, x+w); } // setClickX /** * Sets the clickY value but it is constrained to area * of the panel. */ // ClickablePanel void setClickY(int newClickY) { clickY = constrain(newClickY, y, y+h); } // setClickY /** * Assuming the panel is enabled and visible, it returns * true if the mouse cursor is over the panel. Returns * false if stillSearching_ is false. */ // ClickablePanel boolean isOver() { if (enabled && visible) { if (stillSearching_) { if (!wholeSketch) over = mouseX >= sX(x) && mouseX <= sX(x) + w && mouseY >= sY(y) && mouseY <= sY(y) + h; else over = true; if (over) { stillSearching_ = false; } } else { over = false; } } else { over = false; } return over; } // isOver /** * Determines if the ClickablePanel has been clicked. If so, * it saves the location of the click. */ // ClickablePanel boolean checkForClick() { if (!visible || !enabled) return false; if (over) { if (scrollable) { clickX = mouseX + soX_; clickY = mouseY + soY_; } else { clickX = mouseX; clickY = mouseY; } clicked = true; return true; } else return false; } // checkForClick /** * Sets the color of the panel */ // ClickablePanel void setColor(color stdColor) { c[0] = stdColor; overC[0] = stdColor; } // setColor /** * To DRAW on the panel, EXTEND ClickablePanel AND OVERRIDE * THIS METHOD. By default, the method does nothing. * * This method is called everytime the panel is drawn. It * is possible to draw on this panel without using this * method but using it: * 1. Makes drawing on the panel automatic whenever the * panel is redrawn. * 2. Insures that menus and comboboxes (and any other items) * that overlap the panel can be drawn correctly on top * of the panel and the drawing. * Comment. One can not draw on the panel until after it is * drawn. If the panel is covered by a pulldown list from a * ComboBox or Menu before the drawing is done, the drawing * will cover the pulldown list. Hence the solution is to * draw on the panel immediately after the panel is drawn in * the display() which automatically calls the drawOnPanel() * method as specified in the extension to ClickablePanel. */ // ClickablePanel void drawOnPanel() { } // drawOnPanel } // class ClickablePanel //************************************************************ /** * The SwipePanel was originally intended to allow swiping on * on a touch screen to control the sketch. Unfortunately * normally when using a touch screen, dragging operations * are intercepted by the operating system and not passed on * to the application unless special, sytem dependent technics * are used. It requires uses of mousePressed() and mouseReleased() * methods */ class SwipePanel extends ClickablePanel { int xVal, yVal; // Current values of the swipe controlled variables. int xMax, yMax; // The maximum values of xVal and yVal. int xMin, yMin; // The minimum values of xVal and yVal. int xStart, yStart; // The starting values of xVal and yVal. int minSwipe; // The minimum distance for a drag to be counted as a swipe. boolean incXOnTap; // When true, a tap (click) increments xVal. boolean tapHole; // When true, clicks are ignored by the SwipePanel and // allows Clickable below the panel to process them. boolean processClicks; // Allow clicks near the edge of the swipe panel // to act like swipes. /** * Constructor for a SwipePanel. */ SwipePanel(int leftX, int topY, int theWidth, int theHeight, String theLabel, color stdColor) { super (leftX, topY, theWidth, theHeight, theLabel, stdColor); xMax = 1000; // set default values. yMax = 1000; xMin = 1; yMin = 1; xVal = 1; yVal = 1; xStart = 0; yStart = 0; minSwipe = 10; incXOnTap = false; tapHole = false; processClicks = false; } // constructor swipePanel /** * Called by mousePressed() when the mouse is pressed. It records * the starting mouse coordinates. */ // SwipePanel void mouseDown() { xStart = mouseX; yStart = mouseY; } // mouseDown /** * Assuming the panel is enabled and visible, it returns * true if the mouse cursor is over the panel. However, if * allTapHoles is true, a hole is created in the panel so that * the cursor is not over the panel when one taps - not swipes - * the panel. Returns false if stillSearching_ is false. */ // SwipePanel boolean isOver() { if (enabled && visible) { if (stillSearching_) { if (!wholeSketch) over = mouseX >= sX(x) && mouseX <= sX(x) + w && mouseY >= sY(y) && mouseY <= sY(y) + h; else over = true; if (over && !mousePressed && tapHole && !processClicks) { // ie, the panel was clicked. Create a whole in the panel if // the mouse has not moved very far if (abs(mouseX - xStart) <= minSwipe && abs(mouseY - yStart) <= minSwipe) over = false; } if (over) { stillSearching_ = false; } } else { over = false; } } else { over = false; } return over; } // isOver /** * Determines if the ClickablePanel has been clicked. If so, * it checks to see if the cursor moved far enough to count as * as a swipe. It also checks to make sure the swipe was not * a diagonal movement. If the swipe is to the right, xVal is * is incremented, if it is to the left, xVal is decremented, * if it is upwards, yVal is incremented while if it is * downwards, yVal is decremented. If incXOnTap is true * clicks will increment xVal. */ // SwipePanel boolean checkForClick() { int absDeltaX, absDeltaY; int offsetX, offsetY; float slopeLimit; if (!visible || !enabled) return false; if (over) { slopeLimit = .3; clickX = mouseX; clickY = mouseY; absDeltaX = abs(mouseX - xStart); absDeltaY = abs(mouseY - yStart); if (absDeltaX > minSwipe || absDeltaY > minSwipe) { //process a swipe clicked = true; if (absDeltaX > slopeLimit * (absDeltaY) && absDeltaY > slopeLimit * (absDeltaX)) { // this is neither horizontal or vertical } // ignore swipe else if (absDeltaX > absDeltaY) { // horizontal swipe if (mouseX > xStart) xVal = constrain(xVal+1, xMin, xMax); else xVal = constrain(xVal-1, xMin, xMax); xVal = constrain(xVal, xMin, xMax); } else /* absDeltaX < absDeltaY */ { // vertical swipe if (mouseY > yStart) yVal = constrain(yVal+1, yMin, yMax); else yVal = constrain(yVal-1, yMin, yMax); } return true; } else { if (processClicks) { offsetX = mouseX - x; offsetY = mouseY - y; if (offsetY > float(h)/w * offsetX) { if (offsetY > -float(h)/w * offsetX + h) { yVal = constrain(yVal+1, yMin, yMax); } else { xVal = constrain(xVal-1, xMin, xMax); } } else { if (offsetY > -float(h)/w * offsetX + h) { xVal = constrain(xVal+1, xMin, xMax); } else { yVal = constrain(yVal-1, yMin, yMax); } } return true; } else if (incXOnTap) { clicked = true; clickX = mouseX; clickY = mouseY; xVal = constrain(xVal+1, xMin, xMax); return true; } return false; } } // mouse is over panel clicked = false; return false; } // checkForClick /** * Returns the x value. */ // SwipePanel int getXVal() { return xVal; } // getXVal /** * Returns the y value. */ // SwipePanel int getYVal() { return yVal; } // getYVal /** * Sets the x value subject to the limits of xMin and xMax. * The default is 1. */ // SwipePanel void setXVal(int newXVal) { xVal = constrain(newXVal, xMin, xMax); } // setXVal /** * Sets the y value subject to the limits of yMin and yMax. * The default is 1. */ // SwipePanel void setYVal(int newYVal) { yVal = constrain(newYVal, yMin, yMax); } // setYVal /** * Sets the maximum value of xVal. newXMax > xMin required. * The default is 1000; */ // SwipePanel void setXMax(int newXMax) { if (newXMax > xMin) xMax = newXMax; } // setXMax /** * Sets the minimum value of xVal. newXMin < xMin required. * The default is 1; */ // SwipePanel void setXMin(int newXMin) { if (newXMin < xMax) xMin = newXMin; } // setXMin /** * Sets the maximum value of yVal. newYMax > yMin required. * The default is 1000; */ // SwipePanel void setYMax(int newYMax) { if (newYMax > yMin) yMax = newYMax; } // setYMax /** * Sets the minimum value of yVal. newYMin < yMax required. * The default is 1. */ // SwipePanel void setYMin(int newYMin) { if (newYMin < yMax) yMin = newYMin; } // setXMin /** * Sets minSwipe, the minimum horizontal or vertical movement * needed for swiping. The default is 10; */ // SwipePanel void setMinSwipe(int newMinSwipe) { minSwipe = newMinSwipe; } // setMinSwipe /** * Sets incXOnTap true or false. When it is true, just clicking * will increment x. The default is false. The value of incXOnTap * is ignored if tapHoles is true. */ // SwipePanel void setIncXOnTap(boolean newIncXOnTap) { incXOnTap = newIncXOnTap; } // setIncXOnTap /** * Sets tapHoles true or false. When it is true, the panel has a "hole" * init if the cursor is not moved far enough for a swipe so that a Clickable * item below the panel can be clicked. */ // SwipePanel void setTapHole(boolean newTapHole) { tapHole = newTapHole; } // setTapHoles /** * Sets the processClicks option true or false. When true, clicks (taps) * near the left, right, up, or down edges of the swipe panel as if they * if they were swipes. (Actually the swipe panel is divided into 4 * triangular regions each of which is treated as a directional swipe * when clicked.) */ // SwipePanel void setProcessClicks(boolean newProcessClicks) { processClicks = newProcessClicks; } // setProcessClicks } // class SwipePanel //************************************************************ /** * A group of Clickable items. It is very similar to a ButtonGroup * but is not a Clickable. This allows any Clickable item to be * placed in a ClickableGroup (including combpound items like a * ButtonGroup or ListBox. It is highly recommended at all * clickable items be placed in a ClickableGroup to make greatly * simplify processing mouse clicks. * * IMPORTANT: * The order of the elements is very important especially if the group * includes ComboBoxes or Menus or item that could overlap any other * item. Items should be specified in the order they should be drawn. * Thus any item that could overlap another item, should be specified * after any item it might cover. As a general guide, normal items * should precede ComboBoxes which precede Menus. Clicks are checked * in reverse order so the the item on top (specified later in the * group) is credited with the click should one click on a spot that * covered by multiple items. * * A ClickableGroup does not have the rectangular fields x, y, w, and * h as it has not particular location or size. */ class ClickableGroup { Clickable[] itemArray; boolean clicked; int clickedItemNum; Clickable clickedItem; /** * Creates a group of Clickables. It is very similar to a * ButtonGroup except that it does not inherit from Clickable * and there is no selected item. */ ClickableGroup(Clickable[] anItemArray) { itemArray = anItemArray; clicked = false; clickedItemNum = Clickable.NOTHING_CLICKED; // no selected button clickedItem = null; } // constructor ClickableGroup /** * Calling ClickableGroup.display will draw all the items in * the group. */ // ClickableGroup void display() { int i; for (i = 0; i < itemArray.length; i++) { itemArray[i].display(); } } // display /** * THIS METHOD MUST BE CALLED AND ONLY CALLED IN THE USER'S * mouseReleased() METHOD. When a button is clicked, the method * searches for the item that is under the mouse cursor in * reverse order by using each item's checkForClick() method. * (This order is used so it recognizes the item that is on * top (most recently drawn). It also assumes that every * item's "isOver()" will be called to determine if the item * was the clicked item. When the clicked item is discovered, * it sets clickedItemNum and "clicked". */ // ClickableGroup boolean checkForClick() { int i; stillSearching_ = true; isOver(); for (i = 0; i < itemArray.length; i++) { itemArray[i].hideSomething(); } for (i = itemArray.length - 1; i >= 0; i--) { if (itemArray[i].checkForClick()) { clickedItemNum = i; clickedItem = itemArray[clickedItemNum]; clicked = true; return true; } } return false; } // checkForClick /** * This is called directly or indirectly by the user's draw * method. Sets the clicked field for the ClickableGroup * and the clicked item to false indicating that the item * no longer needs processing. */ // ClickableGroup boolean hasBeenClicked() { boolean cl; cl = clicked; if (clicked) { // turn off clicked item's "clicked" field clickedItem.hasBeenClicked(); } clicked = false; return cl; } // hasBeenClicked /** * isOver returns true if the cursor is over any member of * the ClickableGroup. The clicked item is marked. Searches * entire group in reverse order. */ // ClickableGroup boolean isOver() { int i; boolean b; stillSearching_ = true; b = false; for (i = itemArray.length - 1; i >= 0; i--) { if (itemArray[i].isOver()) { b = true; } } return b; } // isOver /** * getClickedItem returns the subscript of the group item * that has been most recently clicked. If nothing has * been clicked, it returns NONE_CLICKED. */ // ClickableGroup int getClickedItemNum() { return clickedItemNum; } // getClickedItemNum /** * Returns the most recently clicked item or null if none selected. */ // ClickableGroup Clickable getClickedItem() { return clickedItem; } // getClickedItem /** * Returns the label of the most recently clicked item or "*" if * nothing was clicked. */ // ClickableGroup String getClickedItemLabel() { if (clickedItem == null) return "*"; else return clickedItem.getLabel(); } // getClickedItemLabel /** * Returns the number of items in the ClickableGroup. */ // ClickableGroup int groupLength() { return itemArray.length; } // length() } // ClickableGroup //************************************************************ /** * The MenuBar is in charge of locating the menu items and * assigning a uniform color scheme to all the menu items. * Note: The MenuBar is NOT a clickable item and does not * have the rectangular fields x, y, w, and h. */ class MenuBar extends Clickable { int nextX; color menuC, overMenuC; /** * Constructs the MenuBar that is located at the top of * the sketch window. */ // MenuBar MenuBar(color stdColor, color overColor) { super("MenuBar"); nextX = 0; menuC = stdColor; overMenuC = overColor; } // constructor Menu /** * Draws the menu bar across the top of the sketch window. * Menus will be drawn on top of this bar. Hence, the user * program's draw method must call this method before processing * the menu items. */ // MenuBar void display() { fill(menuC); stroke(#000000); rect(0, 0, width, 20); } // display /** * Given the width of the current Menu, it returns the x cordinate * of the Menu. The y coordinate will be 0. */ // MenuBar int xLocation(int itsWidth) { int returnedX; returnedX = nextX; nextX += itsWidth; return returnedX; } // xLocation /** * Returns the standard menu color. */ // MenuBar color getColor() { return menuC; } // getColor /** * Returns the mouse over menu color. */ // MenuBar color getOverColor() { return overMenuC; } // getOverColor } // class MenuBar //*************************************************************