class Gui { ProjectList projectList; Project currentProject; Environment currentModel; View currentView; Stylesheet styles; Toolbar projectsToolbar, modelsToolbar=null, viewsToolbar=null, controller=null; // these should be allowed to remain null if appropriate SliderList paramSliders; Slider speedSlider; boolean isPlaying, savingScreenshot = false; float desiredSpeed, realSpeed, dt; String errorMsg; float archiveSaveTime = -1000, screenshotSaveTime = -1000; Gui(ProjectList projectList, Stylesheet styles) { this.styles = styles; this.projectList = projectList; projectsToolbar = buildProjectsToolbar(); selectProject(projectList.currentProject.shortname); } void selectProject(String key) { currentProject = (Project) projectList.get(key); projectList.currentProject = currentProject; projectsToolbar.deselect(); projectsToolbar.select(key); if (currentProject.modelsToolbar == null) currentProject.modelsToolbar = buildModelsToolbar(currentProject); modelsToolbar = currentProject.modelsToolbar; selectModel(currentProject.currentModel.shortname); } void selectModel(String key) { currentModel = (Environment) currentProject.get(key); currentProject.currentModel = currentModel; if (currentModel.awakened == false) { currentModel.wakeup(); isPlaying = true; } modelsToolbar.deselect(); modelsToolbar.select(key); if (currentModel.viewsToolbar == null) currentModel.viewsToolbar = buildViewsToolbar(currentModel); viewsToolbar = currentModel.viewsToolbar; if (currentModel.controller == null) currentModel.controller = buildController(currentModel); controller = currentModel.controller; speedSlider = (Slider)controller.getElement("speed"); if (currentModel.paramSliders == null) currentModel.paramSliders = buildParamSliders(currentModel); paramSliders = currentModel.paramSliders; selectView(currentModel.currentView.shortname); } void selectView(String key) { currentView = (View) currentModel.views.get(key); currentModel.currentView = currentView; if (currentView == null) currentView = new View(); if (currentView.awakened == false) currentView.wakeup(this); viewsToolbar.deselect(); viewsToolbar.select(key); if (currentView.toolbar == null || currentView.toolbar.hidden) { viewsToolbar.setTop(defaultViewsToolbarTop()); } else { if (!viewsToolbar.hidden) viewsToolbar.setTop(defaultViewsToolbarTop() - styles.guiHeight - styles.guiMargin); currentView.toolbar.setTop(defaultViewsToolbarTop()); } } void update() { boolean hit = false; String target = ""; background(styles.bkgndColor); hit = projectsToolbar.update(); if (hit) selectProject(projectsToolbar.lastUpdated); hit = modelsToolbar.update(); if (hit) selectModel(modelsToolbar.lastUpdated); hit = viewsToolbar.update(); if (hit) selectView(viewsToolbar.lastUpdated); pushStyle(); stroke(255); strokeWeight(1.5); line(0, modelsToolbar.bottom(), width, modelsToolbar.bottom()); line(0, viewsToolbar.top(), width, viewsToolbar.top()); popStyle(); displayTitleText(); currentView.update(); // just gui controls and background stuff--not the main drawing. the View is repsonsible for handling the results of any clicks in itself hit = paramSliders.update(); if (hit) { currentModel.updatePublicParams(); currentModel.updatePrivateParams(); } hit = controller.update(); if (hit) { target = controller.lastUpdated; if (target.equals("REW")) { currentModel.rewind(); } else if (target.equals("PLAY") || (target.equals("PAUSE"))) { isPlaying = !isPlaying; } else if (target.equals("SHOW\nARROWS") || target.equals("HIDE\nARROWS")) { currentModel.toggleArrows(); } else if (target.equals("MORE\nDETAIL") || target.equals("LESS\nDETAIL")) { currentModel.toggleDetails(); } else if (target.equals("SAVE\nDATA")) { currentModel.saveArchive(); archiveSaveTime = millis(); } else if (target.equals("SAVE\nIMAGE")) { savingScreenshot = true; beginRecord(PDF, currentModel.shortname + millis() +".pdf"); screenshotSaveTime = millis(); } else if (target.equals("RESET\nPARAMS")) { currentModel.resetParams(); } else { // if (debug) println("I wonder what " + target + " means."); } } if (isPlaying) { desiredSpeed = speedSlider.getVal(); dt = desiredSpeed / max(20, frameRate); realSpeed = dt * frameRate; speedSlider.writeOnRight(speedSlider.val2string(realSpeed) + " " + currentModel.time.units + "/sec (t = " + round(currentModel.time.current[0]) + " " + currentModel.time.units + ")"); if (realSpeed < 0.9 * desiredSpeed) speedSlider.drawMarker(realSpeed, transparency(styles.orangeColor, 0.5)); errorMsg = currentModel.integrate(dt); // *** the real action *** // might want to do something with that error message } else { speedSlider.writeOnRight("stopped (t = " + round(currentModel.time.current[0]) + " " + currentModel.time.units + ")"); } displayCredits(); currentView.draw(); // *** the other real action *** if (savingScreenshot) endRecord(); if (screenshotSaveTime + 1000 > millis()) writeHuge("image saved"); if (archiveSaveTime + 1000 > millis()) writeHuge("archived"); // notification if an archive was saved recently } void offerMousePress() { boolean captured = currentView.offerMousePress(); if (!captured && controller != null) captured = controller.offerMousePress(); if (!captured && paramSliders != null) captured = paramSliders.offerMousePress(); if (!captured && viewsToolbar != null) captured = viewsToolbar.offerMousePress(); if (!captured && modelsToolbar != null) captured = modelsToolbar.offerMousePress(); if (!captured && projectsToolbar != null) captured = projectsToolbar.offerMousePress(); } // building toolbars ------------------------------------------------------------------------------- Toolbar buildProjectsToolbar() { Toolbar bar = new Toolbar(0, 0, width, styles); Iterator i = projectList.values().iterator(); bar.addTextLabel("projects"); while (i.hasNext()) { String name = ((Project) i.next()).shortname; bar.addTextButton(name); } bar.addSpacer(); return bar; } Toolbar buildModelsToolbar(Project p) { // float x0 = projectsToolbar.unoccLeft; // float y0 = projectsToolbar.top(); float x0 = 0; float y0 = projectsToolbar.bottom(); Toolbar bar = new Toolbar(x0, y0, width, styles); bar.addTextLabel("model cases"); Iterator i = p.values().iterator(); while (i.hasNext()) { String name = ((Environment) i.next()).shortname; bar.addTextButton(name); } return bar; } Toolbar buildController(Environment env) { float y0 = modelsToolbar.bottom() + 0.75*styles.guiMargin; Toolbar bar = new Toolbar(0, y0, width, styles); IconButton btn; bar.addIconButton("REW", "icons/rewind.png"); btn = bar.addIconButton("PAUSE", "icons/pause.png"); btn.addState("PLAY", "icons/play.png"); speedSlider = bar.addSlider("speed", 4*styles.guiHeight, 0.2, 20); speedSlider.logScale = true; speedSlider.showValOnRight = false; // will do this manually, in order to display real speed instead of desired speed speedSlider.showNameOnLeft = false; bar.addSpacer(); bar.addSpacer(); bar.addIconButton("SAVE\nDATA", "icons/write.png"); // bar.addIconButton("SAVE\nIMAGE", "icons/write.png"); // disabled for now because of text-drawing issues in the PDF library bar.addSpacer(); bar.addIconButton("RESET\nPARAMS", "icons/rewind-circle.png"); return bar; } SliderList buildParamSliders(Environment env) { float y0 = controller.bottom() + countLines(env.name) * 1.1 * styles.largeFontSize + (countLines(env.commentary) + 1) * 1.1 * styles.normalFontSize; SliderList sl = new SliderList(width - 150, y0, 4*styles.guiHeight, styles); for (int i=0; i 0) p.slider.quantize(p.quantization); p.slider.highlight = p.highlight; p.slider.setVal(p.current); } } return sl; } float defaultViewsToolbarTop() { return height-styles.guiHeight; } Toolbar buildViewsToolbar(Environment env) { Toolbar bar = new Toolbar(0, defaultViewsToolbarTop(), width, styles); bar.addTextLabel("views"); Iterator i = env.views.values().iterator(); while (i.hasNext()) { String name = ((View) i.next()).shortname; bar.addTextButton(name); } if (bar.elements.length <= 2) bar.hidden = true; // don't show the toolbar if it's just the label + one button return bar; } // other display elements ------------------------------------------------------------------------- void displayTitleText() { pushStyle(); styles.setFont(styles.largeFontSize); float leading = 1.1 * styles.largeFontSize; textLeading(leading); textAlign(RIGHT); fill(styles.labelColor); float x0 = width - styles.guiMargin; float y0 = controller.y0 + 0.5*controller.height + 0.5*(textAscent()-textDescent()); text(currentModel.name, x0, y0); y0 += countLines(currentModel.name) * leading; styles.setFont(styles.normalFontSize); leading = 1.1 * styles.normalFontSize; y0 += leading; textLeading(leading); text(currentModel.commentary, x0, y0); } void displayCredits() { String S = versionString + "\n" + creditsString; styles.setFont(styles.smallFontSize); fill(styles.labelColor); textAlign(RIGHT); textLeading(styles.smallFontSize); text(S, width-textWidth(" "), height - styles.smallFontSize - textDescent() - 1); } void writeHuge(String S) { styles.setFont(72); textAlign(CENTER); fill(transparency(styles.labelColor,0.7)); stroke(styles.labelColor); strokeWeight(1); text(S, width/2, height/2); } Axes availableViewSpace() { // an Axes that spans the onscreen space that Views can lay claim to, // with the square that spans the minimum dimension mapped onto -1..1, -1..1 // call this in wakeup(), not constructors--the toolbars may not get built until a model is selected float x0 = styles.guiMargin; float y0 = controller.bottom() + styles.guiMargin; // how much space to leave for the top toolbars float x1 = paramSliders.left() - 80; float y1 = viewsToolbar.top(); Axes ax = new Axes(x0, y0, x1-x0, y1-y0); ax.setYRange(-1, 1); float asp = abs((ax.right()-ax.left()) / (ax.bottom()-ax.top())); ax.setXRange(-asp, asp); return ax; } }