import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) /** * A slider to allow a user to select a number in a given range. * * Create a Slider by calling new {@link Slider()} (for the default size) * or new {@link Slider(int, int)} (for a custom size). * Set teh maximum value that the user can select by calling * {@link setMaximumValue(int)}, set the initial value by calling * {@link setValue(int)} and add it to the world by calling the * addObject(Actor, int, int) method on the World. * * You can also set how the slider should display by calling the other methods. * This should be done before adding the slider to the world, but does not need to be * * @author mBread * @version 29-01-2008 */ public class Slider extends Actor { //The background & foregreound images. See updateImage() private GreenfootImage background, foreground; //The height, width of the foreground and height of the foreground private int height, fgWidth = 9, fgHeight = 10; //The maximum value, width in pixels, width of the selectable area, width of the value label, //the padding either side of the selectable area, the x-coordinate of the selectable area //and the x-coordinate of the right-hand side of the selectable area private int maxValue, actualWidth, valueWidth, valueLabelWidth, padding, valueX, valueRight; //The number of major and minor sections private int majorSections, minorSections; //Flags meaning: display the value label, show the value as a percentage, re-draw the images private boolean showValue = false, showPercentage = true, imagesInvalid = true; //How wide each number is in the selectable value private float pixelsPerValue; //The current value is private int value; /** * Create a new slider with the default size. It will be 200 pixels wide by * 20 pixels high. */ public Slider(){ this(200, 20); } /** * Create a new slider. * * @param width The width of the slider in pixels * @param height The height of th slider in pixels */ public Slider(int width, int height){ majorSections = 2; minorSections = 2; maxValue = 100; actualWidth = width; this.height = height; fgHeight = height/2; fgWidth = fgHeight; if(fgWidth % 2 == 0){ fgWidth--; } setImage( new GreenfootImage(actualWidth, height) ); } /** * This method is called by the World class when the slider is added to the * world. You will not need to call this method. * * @param world The world */ public void addedToWorld(World world){ createImages(); updateImage(); imagesInvalid = false; valueX = getX()*getWorld().getCellSize() - actualWidth/2 + padding; valueRight = valueX + valueWidth; } /** * This method is called by Greenfoot, and updates the slider if the user * has dragged the slider. */ public void act(){ if(Greenfoot.mouseDragged(this) || Greenfoot.mousePressed(this)){ MouseInfo mouseInfo = Greenfoot.getMouseInfo(); int x = Math.max(valueX, mouseInfo.getX()*getWorld().getCellSize()); x -= valueX; x = Math.min(x, valueRight); setValue(posToValue(x)); imagesInvalid = true; } if(imagesInvalid){ createImages(); updateImage(); imagesInvalid = false; } } /** * Set the current selected value of the slider. The display will change * to represent this new value. * * @param value The new value */ public void setValue(int value){ if(value < 0){ value = 0; }if(value > maxValue){ value = maxValue; } this.value = value; imagesInvalid = true; } /** * Get the current value. * * @return The current selected value */ public int getValue(){ return value; } /** * Set whether the value should be displayed next to the slider. * * If this method is called with false as the parameter then it will * also set {@link shoePercentage(boolean)} will also be set to false. * * @see showPercentage(boolean) * @param show true if the value should be displayed as text, * false if it should not be */ public void showValue(boolean show){ if(showValue != show){ imagesInvalid = true; } showValue = show; if(!show){ if(showPercentage) imagesInvalid = true; showPercentage = false; } } /** * Check whether the slider is set to display the value. * * @see showValue(boolean) * @see isShowingPercentage() * @return true if the value is being displayed, false if it is not */ public boolean isShowingValue(){ return showValue; } /** * Set whether the value will be displayed next to the slider as a percentage. * * This method will take precedence over {@link showValue(boolean)}. If * both are set to true, the percentage is shown. If both are true and * showPercentage(false) is called then the value will be shown. If both * are true and showValue(false) is called then neither the value nor * percentage is shown. * * @see showValue(boolean) * @see isShowingPercentage() * @param show true if the value should be displayed as a percentage of * the maximum value, false if it should not. */ public void showPercentage(boolean show){ if(showPercentage != show){ imagesInvalid = true; } showPercentage = show; } /** * Check whether the slider is set to display the value as a percentage. * * @see showPercentage(boolean) * @see isShowingValue() * @return true if the percentage is being displayed, false if it is not */ public boolean isShowingPercentage(){ return showPercentage; } /** * Set the number of major sections to be marked. Set the number of sections to * zero to not display any major markers. * * There will be 1 more marker than sections as there is a * marker at both ends as well as between each section * * @see setMinorSections(int) * @param sections The number of major sections to display */ public void setMajorSections(int sections){ majorSections = sections; imagesInvalid = true; } /** * Get the number of major sections that are marked. * * @see setMajorSections(int) * @return The number of major sections that are marked */ public int getMajorSections(){ return majorSections; } /** * Set the number of minor sections to be marked per major secton. Set this * to zero or 1 to not display any minor markers. * * There will be one less minor marker (per major section) than the number * of minor sections as there is not a marker at each end due to the major * markers being there. * * @see setMajorSections(int) * @param sections The number of minor sections to mark per major section */ public void setMinorSections(int sections){ minorSections = sections; imagesInvalid = true; } /** * Get the number of minor sections that are marked. * * @see setMinorSections(int) * @return The number of minor sections that are marked */ public int getMinorSections(){ return minorSections; } /** * Set the maximum value that the user can select. * * @param value The new maximum value */ public void setMaximumValue(int value){ if(value <= 0) return; maxValue = value; pixelsPerValue = valueWidth/(float)maxValue; setValue(value); } /** * Get the current maximum value which the user can select * * @return The maximum value */ public int getMaximumValue(){ return maxValue; } /** * Convert a pixel position (relitive to the left of the selectable area) * to a selected value. */ private int posToValue(int x){ int v = Math.round(x / pixelsPerValue); return v; } /** * Convert a selected value to a pixel position */ private int valueToPos(int v){ int p = Math.round(v * pixelsPerValue); return p; } /** * Create the background & foreground images */ private void createImages(){ if(showPercentage){ valueLabelWidth = 4*10; }else if(showValue){ valueLabelWidth = (int)(Math.floor(Math.log10(maxValue))+1)*10; }else{ valueLabelWidth = 0; } valueWidth = actualWidth - fgWidth - valueLabelWidth; valueWidth -= valueWidth%2; padding = fgWidth/2; pixelsPerValue = valueWidth/(float)maxValue; assert padding >= 0; background = new GreenfootImage(actualWidth, height); foreground = new GreenfootImage(fgWidth, height); background.drawLine(padding, height/2, padding+valueWidth, height/2); if(majorSections > 0){ float pixelsPerMarker = valueWidth/(float)majorSections; float pixelsPerMinor = 0; if(minorSections > 0) pixelsPerMinor = pixelsPerMarker/(float)minorSections; for(int i=0; i<=majorSections; i++){ int x = Math.round(i*pixelsPerMarker + padding); background.drawLine(x, height, x, 2*height/3); for(int m=1; m