Is there a way to add JMenuBar (File, Edit...) to my program?

Hey
I wanted to ask if there is a way to add an File, Edit, Sketchbook… to a Processing Sketch like the PDE also has?
Screenshot 2021-08-21 191636

Thanks in advance :smile:

1 Like

Please elaborate

Do you have an editor and want to save what the user typed?

Yes, for example, or that you can select a certain tool…

Do you mean

  • a Menu like the File Menu with different commands OR
  • a mouse button?

Please elaborate

A Menu like the processing IDE has.

but then your own commands for the menu entries not the stuff from the processing IDE right?

Check out section GUI here : https://processing.org/reference/libraries/#gui

not sure about Menus

1 Like

Yes my own commands. GUI buttons is not what I mean :frowning:

just tried to post a code but it was too long for the forum
you have to join both posts - or split them to multiple tabs at the ******* lines


// ********************************************************************************
//    
// ********************************************************************************


// ********************************************************************************
// tab: MenuAdvanced8.pde main file (file name is folder name)

/* 
 All tabs beginning with "class..." belong to the library. 
 All other tabs are part of the demo. 
 
 features 
 * different positions: top, left, right, bottom
 * Headlines for groups of buttons
 * help view
 * easy handling 
 * groups of buttons 
 * radio button with circle 
 * check box with rectangle and cross
 * ID und y-position is given by the order you define the buttons (with menu.addButton)
 * drop down menus or side menus (all buttons visible)
 */

import java.util.Map;

// text 
final String helpText = 
  "Demo for advanced menu with different types of buttons (toggle side with l,r,b,t)."; 

// the core object (from lib)
Menu menu;

// those 3 int/boolean switches are modified by buttons (example switches without function)
int mode;  // switch 1

int viewMode=0;   // switch 2 

boolean boolTest = true;   // switch 3

// status bar text 
String statusBarText        = "Demo for advanced menu with different types of buttons";
String statusBarTextDefault = statusBarText; 

// states 
final int NORMAL = 0; 
final int HELP   = 1; 
int state = NORMAL;

// ---------------------------------------------------------------------
// CORE functions 

void settings() {
  size(1600, 660, P2D);
}

void setup() {
  background(255);

  menu=new Menu();

  // ------------------------------

  menu.addButton( "Modes", Button.IS_HEADLINE, "Modes_Group");  //  HEADLINE 
  menu.addButton( "Normal", Button.IS_RADIO_BUTTON, "Modes_Group");
  menu.addButton( "Select", Button.IS_RADIO_BUTTON, "Modes_Group");
  menu.addButton( "Drag", Button.IS_RADIO_BUTTON, "Modes_Group");

  menu.addButton( "Views", Button.IS_HEADLINE, "Views_Group");  //  HEADLINE 
  menu.addButton( "Top Down", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "From North", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "From East", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "From South", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "From West", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "Peasy Cam", Button.IS_RADIO_BUTTON, "Views_Group" );
  menu.addButton( "Rotate", Button.IS_RADIO_BUTTON, "Views_Group" );

  menu.addButton( "Camera View", Button.IS_HEADLINE, "Camera View");  //  HEADLINE
  menu.addButton( "View is on", Button.IS_CHECKBOX, "Camera View");
  // during adding buttons user can retrieve the last ID 
  menu.printlnCurrentButtonID();

  menu.addButton( "General", Button.IS_HEADLINE, "General");  //  HEADLINE
  menu.addButton( "Export", Button.IS_COMMAND_BUTTON, "General" );
  // during adding buttons user can retrieve the last ID 
  menu.printlnCurrentButtonID();
  menu.addButton( "Help", Button.IS_COMMAND_BUTTON, "General");
  // during adding buttons user can retrieve the last ID 
  menu.printlnCurrentButtonID();

  // ------------------------------

  // add help texts to the groups
  menu.addGroupHelp ( "Modes_Group", "Here you can choose modes" );
  menu.addGroupHelp ( "Views_Group", "Here you can choose views" );
  menu.addGroupHelp ( "Camera View", "Here you can toggle the view for camera" );
  menu.addGroupHelp ( "General", "Here you can do general things" ); 

  // these buttons are already highlighted (they represent values: mode, viewMode, boolTest)
  menu.buttons[1].isActive=true;
  menu.buttons[5].isActive=true;
  menu.buttons[13].isActive=true;

  // set position; some positions (top and bottom) don't have enough space for all entries 
  // menu.setLeft();
  // menu.setRight(); // the default 
  // menu.setTop();
  // menu.setBottom();

  // set menu as drop down menu 
  menu.setDropDown();
  //
}// func 

void draw() {

  // state based program 

  switch(state) {

  case NORMAL:
    drawForNormal();
    break; 

  case HELP:
    drawForHelp();
    break;

  default:
    println("Error #111: unknown state: "
      +state);
    exit();
    break; 
    //
  }//switch
  //
}//draw
//

// ********************************************************************************
// tab: classButton.pde


// Helper class for the Menu class.
// Used only by the Menu class.

class Button {

  // visible text
  String btnText; 

  // type: Headline or command button or ............. 
  static final int IS_HEADLINE       = 0; // constants: unique numbers 
  static final int IS_COMMAND_BUTTON = 1;
  static final int IS_RADIO_BUTTON   = 2; 
  static final int IS_CHECKBOX       = 3;
  // type
  int type = IS_COMMAND_BUTTON; 

  // which group does the button belong to?  
  String groupName = "";
  int id_Within_Group = 0; // for radio buttons: a group has one and only one active member.  

  // pos and size
  int x, y; // The x- and y-coordinates
  int sizeWidth, sizeHeight; // size (width and height) (w/h)

  // flags 
  boolean over    = false; // True when the mouse is over
  boolean pressed = false; // True when the mouse is over and pressed

  // index 
  int index; // index 

  // is it ON ? 
  boolean isActive=false;  // for check box and radio button 

  // -------------------------------------------------------------------------

  // constructor 
  Button(String text_, 
    int type_, 
    String groupName_, 
    int xp_, int yp_, 
    int sizeWidth_, int sizeHeight_, 
    int index_) {

    btnText=text_;

    type=type_; 

    groupName=groupName_;

    x = xp_;
    y = yp_;

    sizeWidth = sizeWidth_;
    sizeHeight = sizeHeight_; 

    index=index_;
  } // constructor 

  // Updates the over field every frame
  void update() {
    if ((mouseX >= x) && (mouseX <= x + sizeWidth) &&
      (mouseY >= y) && (mouseY <= y + sizeHeight)) {
      over = true;
    } else {
      over = false;
    }
  }

  boolean press() {
    if (over) {
      pressed = true;
      return true;
    } else {
      pressed = false; 
      return false;
    }
  }

  void release() {
    pressed = false; // Set to false when the mouse is released
  }

  void displayForDropDownMenu(Group currGroup) {
    //
    switch(type) {

    case IS_HEADLINE: 
      // Headline
      // display like normal button/command button  
      fill(111);
      noStroke(); 
      rect(x, y, 
        sizeWidth, sizeHeight);

      fill(255);  // white
      textAlign(LEFT);
      text(btnText, x+6, y+16);
      break;

    default:
      if (currGroup.dropDownIsOpen) {
        // 
        display();
      }//if
      break;
    }//switch
  }//method 

  void display() {
    //
    float dist = 2;
    switch(type) {

    case IS_HEADLINE: 
      // Headline
      fill(0);  // white
      textAlign(LEFT);
      text(btnText, x+0, y+16);
      break;

    case IS_COMMAND_BUTTON:
      // normal button 
      fill(111);
      noStroke(); 
      rect(x, y, 
        sizeWidth, sizeHeight);

      fill(255);  // white
      textAlign(LEFT);
      text(btnText, x+6, y+16);
      break;

    case IS_RADIO_BUTTON:
      //
      fill(111);
      noStroke(); 
      rect(x, y, 
        sizeWidth, sizeHeight);

      noFill(); 
      stroke(0);
      dist = 2; 
      strokeWeight(1);//reset
      ellipseMode(CENTER);  // Set ellipseMode to CENTER
      ellipse(x+dist+8, y+dist+9, 
        9, 9);  
      noStroke(); 
      if (isActive) {
        fill(0); 
        ellipseMode(CENTER);  // Set ellipseMode 
        ellipse(x+dist+8, y+dist+9, 
          4, 4);  
        strokeWeight(2);
        stroke(0, 225, 0);//green
        noFill();
      }

      dist = 1; 
      rect(x+dist, y+dist, 
        sizeWidth-dist-dist, sizeHeight-dist-dist);
      strokeWeight(1);//reset

      fill(255);  // white
      textAlign(LEFT);
      text(btnText, x+19, y+16);
      break; 

    case IS_CHECKBOX:
      // yes/no boolean
      //
      fill(111);
      noStroke();
      rect(x, y, 
        sizeWidth, sizeHeight);

      noFill(); 
      stroke(0);
      dist = 2; 
      strokeWeight(1);//reset
      // rect with a CHECKBOX 
      PVector upperLeftCorner=new PVector(x+dist+2, y+dist+2+2);
      rect(upperLeftCorner.x, upperLeftCorner.y, 
        9, 9);

      noStroke(); 
      if (isActive) {
        fill(0); 
        stroke(0); 
        // line \
        line(upperLeftCorner.x, upperLeftCorner.y, 
          upperLeftCorner.x+9, upperLeftCorner.y+9);  
        // line /
        line(upperLeftCorner.x+9, upperLeftCorner.y, 
          upperLeftCorner.x, upperLeftCorner.y+9);  
        strokeWeight(2);
        stroke(0, 225, 0);//green
        noFill();
      }// if

      dist = 1; 
      rect(x+dist, y+dist, 
        sizeWidth-dist-dist, sizeHeight-dist-dist);
      strokeWeight(1);//reset

      fill(255);  // white
      textAlign(LEFT);
      text(btnText, x+19, y+16);
      break;

    default:
      println("Error 121");
      exit();
      break;
    }//switch
  }//method

  void displayHelpText() {
    // Help View 
    if (type==IS_HEADLINE) {
      // Headline
      fill(0);  // 
      textAlign(LEFT);
      text(btnText, x+6, y+16);
    } else {
      // normal button 
      noFill(); 
      stroke(111);
      rect(x, y, sizeWidth, sizeHeight);
      strokeWeight(1);//reset
      fill(111);
      textAlign(LEFT);
      text(btnText, x+6, y+16);
    }//else
  }
  //
}//class
//

// ********************************************************************************
// tab: classGroup.pde


// Helper class for the Menu class.
// A group of buttons.
// Used only by the Menu class.

class Group {

  String name="";

  String helpText=""; 

  String headline="";
  boolean hasHeadline = false; 

  boolean isRadioButtonGroup = false; 
  int groupReturnValue = 0;
  int id_Within_Group_Counter = 0;

  boolean dropDownIsOpen=false; 

  float minXvalue; 
  float maxXvalue; 

  float minYvalue; 
  float maxYvalue;

  ArrayList<Integer> indexOfButtons = new ArrayList(); 

  // ------------------------------------------------------------------

  // constr 
  Group( String name_, int minYvalue_ ) {
    //
    minYvalue = minYvalue_;
    name      = name_;
  }// constr 
  //
}//class
2 Likes

// ********************************************************************************
// tab: classMenu.pde


// The core class for the demo: Menu.

class Menu {

  // The class' variables

  // buttons
  Button[] buttons;

  // consts for positions 
  final int POS_RIGHT   = 0; 
  final int POS_LEFT    = 1; 
  final int POS_TOP     = 2; 
  final int POS_BOTTOM  = 3; 
  int menuPosition = POS_RIGHT; // current 

  // on mouse pressed, data gets returned. Must all be negative (since command button returns its ID which is positive). 
  static final int menuReturnIgnore      = -1; 
  static final int menuReturnRadioButton = -2;
  static final int menuReturnCheckBox    = -3;

  // for init buttons 
  int iForNewButtons=0; 

  // register of groups (buttons are organized in groups)
  HashMap<String, Group> hmGroups = new HashMap();

  int groupsCounter=0; 

  boolean flagDropDown=false;

  // ---------------------------------------------------------------------

  //constr
  Menu () {
    buttons = new Button[0];
  } //constr

  void addButton(String text, int type, String groupName) {
    // parameters:
    //  * text is the text 
    //  * type is either headline, commandBtn, radio button, check box, see constants in class Button
    //  * free name of the group 

    int unit=29; // distY

    // we manage the group names
    Group group;
    // Is the group new? 
    if (hmGroups.containsKey(groupName)) {
      //No, old group - update values 
      group = hmGroups.get(groupName); 
      group.maxYvalue = (iForNewButtons*unit+20) + 19;
      // when entry (button) is a headline, we manage this 
      if (type==Button.IS_HEADLINE) {
        group.headline=text; 
        if ( group.hasHeadline ) {
          println("Warning: Double assigning of headline to a group "
            + text
            +" in group "+groupName);
        } else {
          group.hasHeadline = true;
        }//else
      }//if
    } else
    {
      //Yes, new group - make group and put it in hm
      groupsCounter++; 
      group=new Group(groupName, (iForNewButtons*unit+20) );
      // when entry (button) is a headline, we manage this 
      if (type==Button.IS_HEADLINE) {
        group.headline=text; 
        group.hasHeadline = true;
      }//if
      hmGroups.put(groupName, group);
    }//else 

    // make button 
    Button newButton = new Button( text, type, groupName, 
      width-134, iForNewButtons*unit+20, 
      104, unit-6, 
      iForNewButtons ); 

    // If it's a radio button some adjustments are necessary 
    if (type==Button.IS_RADIO_BUTTON) {
      group.isRadioButtonGroup=true; 
      newButton.id_Within_Group = group.id_Within_Group_Counter;
      group.id_Within_Group_Counter++;
    }//if
    group.indexOfButtons.add(iForNewButtons);

    // add button to array 
    buttons = (Button[]) append (buttons, newButton);
    iForNewButtons++;
  } // method 

  void addGroupHelp(String groupName, String helpText) {
    // adds one help text to a group
    Group currentGroup = hmGroups.get ( groupName );
    if (currentGroup!=null) {
      currentGroup.helpText=helpText;
    } else {
      // error 
      println("Error 331: addGroupHelp: unknown groupname: "+groupName);
      exit();
    }//else
  }// method

  // ----------------------------------------------------------------------

  void display() {
    // show Menu
    if (flagDropDown) {
      // drop down menu
      for (Button but : buttons) {
        but.update();
        Group currGroup=hmGroups.get(but.groupName);
        but.displayForDropDownMenu(currGroup);
      }//for
    } else {
      // no drop down menu
      for (Button but : buttons) {
        but.update();
        but.display();
      }//for
    }//else
  }//method

  int getValueFromGroup( String groupNameLocal ) {
    return 
      hmGroups.get(groupNameLocal).groupReturnValue;
  }

  PVector getHelpBoxPos() {
    // Just a small function for the help screen, that returns a free spot,
    // where a general help text can be placed by the programmer.
    // This is not the position of the help texts of the menus.

    if (flagDropDown) {
      return new PVector(800, 330);
    }

    // Non drop down menus 
    switch(menuPosition) {
    case POS_RIGHT:
      return new PVector(100, 330);
    case POS_LEFT:
      return new PVector(800, 330);
    case POS_TOP:
      return new PVector(100, 330);
    case POS_BOTTOM:
      return new PVector(600, 330);
    default:
      // error
      println("Error 107 : "+menuPosition);
      exit();
      break;
    }//switch
    return new PVector(600, 330);
  }//method

  // ------------------------------------------------------------

  void displayHelp() {
    // show help 
    if (flagDropDown) 
      displayHelpForDropDown();
    else 
    displayHelpForNonDropDown();
  } // method

  void displayHelpForNonDropDown() {
    // helper for displayHelp() 
    if (menuPosition==POS_LEFT || menuPosition==POS_RIGHT) {
      showTexts1();
    } //if
    else {
      showTexts2();
    }//else 

    // show Menu as HELP buttons 
    fill(0); 
    for (Button but : buttons) {
      but.displayHelpText();
    }//for

    // back to default
    textAlign(LEFT);
  }//method 

  void displayHelpForDropDown() {
    // helper for displayHelp()
    // close all 
    closeAllMenus();
    // show help texts for groups 
    showTexts3();
  }//method 

  void showTexts1() {
    // version for menuPosition POS_RIGHT and POS_LEFT
    int xValue=0;
    // for the meaning of the variables see method forkHorizontal 
    PVector pvA=new PVector(); 
    PVector pvB=new PVector(); 
    PVector pvC; 
    switch(menuPosition) {
    case POS_RIGHT:
      xValue=1170;
      pvA.x = xValue+10;
      pvB.x = buttons[0].x - 7;
      textAlign(RIGHT);
      break;   
    case POS_LEFT:
      xValue=buttons[0].x+buttons[0].sizeWidth+93;
      pvA.x=xValue-10;
      pvB.x= buttons[0].x + buttons[0].sizeWidth + 8;
      textAlign(LEFT);
      break;
    default:
      println("Error 108");
      exit();
      break;
    }//switch 

    // GROUPS 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue(); 
      text( currentGroup.helpText, 
        xValue, (currentGroup.minYvalue+currentGroup.maxYvalue)/2 +2);
      stroke(255, 0, 0);
      // for the meaning of the variables see method forkHorizontal  
      pvB.y=currentGroup.minYvalue;
      pvC=new PVector( pvB.x, currentGroup.maxYvalue );
      forkHorizontal(pvA, pvB, pvC);
    }//for 
    textAlign(LEFT);
  }//method 

  void showTexts2() {
    // version for menuPosition TOP and BOTTOM
    int yValue=0; 
    // for the meaning of the variables see method forkVertical  
    PVector pvA=new PVector(); 
    PVector pvB=new PVector(); 
    PVector pvC; 

    switch(menuPosition) {
    case POS_TOP:
      yValue=170;
      pvA.y = yValue-10;
      pvB.y = buttons[0].x + buttons[0].sizeHeight + 7;
      break;   
    case POS_BOTTOM:
      yValue=height-180;
      pvA.y = yValue+30;
      pvB.y = buttons[0].y - 7;
      break;
    default:
      println("Error 109");
      exit();
      break;
    }//switch 

    // loop over all GROUPS 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue(); 
      textAlign(CENTER);
      text( currentGroup.helpText, 
        (currentGroup.minXvalue+currentGroup.maxXvalue)/2, yValue, 
        180, 900 );
      stroke(255, 0, 0);
      // for the meaning of the variables see method forkVertical  
      pvB.x=currentGroup.minXvalue;
      pvC=new PVector( currentGroup.maxXvalue+buttons[0].sizeWidth, pvB.y);
      forkVertical(pvA, pvB, pvC);
    }//for

    textAlign(LEFT);
  }//method 

  void showTexts3() {  
    // version for drop down menus 
    int counter = 0; 
    // loop over all groups 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue(); 
      // loop over all buttons in group
      for (int ind : currentGroup.indexOfButtons) {
        if (buttons[ind].type == Button.IS_HEADLINE) {
          buttons[ind].displayForDropDownMenu(currentGroup);
        }//if
      }//for
      float xValue = buttons[currentGroup.indexOfButtons.get(0)].x + 
        buttons[currentGroup.indexOfButtons.get(0)].sizeWidth / 2;
      fill(255, 0, 0); 
      text( currentGroup.helpText, 
        xValue+50+4, (groupsCounter-counter) * 70 +4);
      stroke(255, 0, 0);
      // for the meaning of the variables see method rightAngle  
      PVector pvA=new PVector(xValue, 30) ;
      PVector pvB=new PVector(xValue+50, (groupsCounter-counter) * 70 );
      rightAngle( pvA, pvB );
      counter++;
    }//for 
    textAlign(LEFT);
  }//method 

  // -------------------------------------------
  // set positions and drop down mode 

  void setLeft() {
    menuPosition = POS_LEFT; 
    for (Button but : buttons) {
      but.x=4;
    }
  }

  void setRight() {
    menuPosition = POS_RIGHT; 
    for (Button but : buttons) {
      but.x=width-134;
    }
  }

  void setTop() {
    menuPosition = POS_TOP; 
    int i=0; 
    for (Button but : buttons) {
      but.x=i*(buttons[1].sizeWidth+4)+5; 
      but.y=5; 
      i++;
    }//for
    getSomeDataForGroups();
  }

  void setBottom() {
    menuPosition = POS_BOTTOM; 
    int i=0; 
    for (Button but : buttons) {
      but.x=i*(buttons[1].sizeWidth+4)+5; 
      but.y=height-25; 
      i++;
    }//for
    getSomeDataForGroups();
  }

  void setDropDown() {
    // changes menu to a drop down menu  
    flagDropDown=true;
    setTop();
    // x-position, y-position
    int xVal=20;
    int yVal=32;
    float textWidthHeadline=0; 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue();
      // loop over all buttons in group
      for (int ind : currentGroup.indexOfButtons) {
        if (buttons[ind].type == Button.IS_HEADLINE) {
          // make headline button (the menu header from which the menu drops down) 
          buttons[ind].x=xVal; 
          buttons[ind].sizeWidth=int(textWidth(buttons[ind].btnText)+14); 
          textWidthHeadline = textWidth(buttons[ind].btnText)+17;
        }//if
        else {
          // make buttons that drop down
          buttons[ind].x=xVal+3; // x-position little right of menu
          buttons[ind].y=yVal;   // y-position 
          yVal+=23; // next line
        }//else
      }//for
      // prepare for next menu: 
      // increase x-position, reset y-position 
      xVal += textWidthHeadline;
      yVal=32;
    } //for
  } //method

  // -------------------------------------------

  void getSomeDataForGroups() {
    //  setTop() and setBottom() use this function to set minXvalue and maxXvalue for the groups 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue();
      // pre-init 
      currentGroup.minXvalue=10000;
      currentGroup.maxXvalue=-1000;
      // loop over all buttons in grouo 
      for (int ind : currentGroup.indexOfButtons) {
        if (buttons[ind].x<currentGroup.minXvalue)
          currentGroup.minXvalue=buttons[ind].x;
        if (buttons[ind].x>currentGroup.maxXvalue)
          currentGroup.maxXvalue=buttons[ind].x;
      }//for
    }//for
  }//method 

  // -------------------------------------------

  int checkMouseOnMenu() {
    // one of the core functions of the class. 
    // Handling mouse pressed.
    int result=menuReturnIgnore; // no button found.
    if (flagDropDown) {
      // drop down menu --- 
      // step 1 : check headlines
      if (checkMouseOnMenuHeadlines())
        return result;
      // step 2 : check menu content of open menu   
      result=checkMouseOnMenuForDropDown();
    } else 
    {
      // not drop down menus ---
      result=checkMouseOnMenuForNonDropDownMenu();
    }//else 
    return result;
  }//method    

  boolean checkMouseOnMenuHeadlines() {
    // helper for checkMouseOnMenu() 
    if (!flagDropDown) 
      return false; 

    for (Button but : buttons) {
      but.press(); 
      if (but.pressed&&but.type==Button.IS_HEADLINE) {
        //drop down menu 
        Group currGroup=hmGroups.get(but.groupName);
        if (currGroup.dropDownIsOpen) {
          // close an open menu            
          currGroup.dropDownIsOpen=false;
        } else {
          // close all 
          closeAllMenus(); 
          // open this menu 
          currGroup.dropDownIsOpen=true;
        }//else 
        but.pressed=false; 
        return true;
      }//if
    }//for 
    return false;
  }// method 

  int checkMouseOnMenuForDropDown() {
    // helper for checkMouseOnMenu() 

    if (!flagDropDown) 
      return menuReturnIgnore; 

    int result = menuReturnIgnore; // no button found.

    // loop over all groups 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue();

      if (currentGroup.dropDownIsOpen) {

        // loop over all buttons in grouo 
        for (int ind : currentGroup.indexOfButtons) {
          Button but = buttons[ind];

          but.press(); 
          if (but.pressed) {
            // some analysis depending on type
            switch(but.type) {

            case Button.IS_HEADLINE : 
              if (flagDropDown) {
                //drop down menu 
                Group currGroup=hmGroups.get(but.groupName);
                if (currGroup.dropDownIsOpen) {
                  // close an open menu            
                  currGroup.dropDownIsOpen=false;
                } else {
                  // close all 
                  closeAllMenus(); 
                  // open this menu 
                  currGroup.dropDownIsOpen=true;
                }//else 
                but.pressed=false; 
                // return menuReturnIgnore; // just leave
                result =  menuReturnIgnore;
              } else {
                // NOT drop down menu 
                // ignore 
                return menuReturnIgnore; // just leave
              } // else and end of case
              break; 

            case Button.IS_COMMAND_BUTTON : 
              // returning positive value
              return but.index; // result. Other buttons won't be pressed, just leave

            case Button.IS_RADIO_BUTTON : 
              Group currentGroup1 = hmGroups.get ( but.groupName ); 
              currentGroup1.groupReturnValue = but.id_Within_Group; 
              setInactive(currentGroup1); 
              but.isActive=true; 
              return menuReturnRadioButton; 

            case Button.IS_CHECKBOX : 
              // toggle 
              but.isActive = 
                ! but.isActive; 
              return menuReturnCheckBox; 

            default : 
              // error 
              println("Error 263: type: "
                + but.type 
                + ", button text: "
                + but.btnText); 
              exit(); // terminate sketch  
              return menuReturnIgnore; // Error. Just leave.
              //
            }//switch 
            //
          } else {
            // but.isActive=false;
          }//else
        }//for
      }
    }
    return result;
  } //method 

  int checkMouseOnMenuForNonDropDownMenu() {
    // helper for checkMouseOnMenu() 

    // not suited for drop down menu
    if (flagDropDown) 
      return menuReturnIgnore;

    int result = menuReturnIgnore; // no button found.
    for (Button but : buttons) {
      but.press(); 
      if (but.pressed) {
        // some analysis depending on type
        switch(but.type) {

        case Button.IS_HEADLINE :  
          // ignore 
          return menuReturnIgnore; // just leave

        case Button.IS_COMMAND_BUTTON : 
          // returning positive value
          return but.index; // result. Other buttons won't be pressed, just leave

        case Button.IS_RADIO_BUTTON : 
          Group currentGroup = hmGroups.get ( but.groupName ); 
          currentGroup.groupReturnValue = but.id_Within_Group; 
          setInactive(currentGroup); 
          but.isActive=true; 
          return menuReturnRadioButton; 

        case Button.IS_CHECKBOX : 
          // toggle 
          but.isActive = 
            ! but.isActive; 
          return menuReturnCheckBox; 

        default : 
          // error 
          println("Error 263: type: "
            + but.type 
            + ", button text: "
            + but.btnText); 
          exit(); // terminate sketch  
          return menuReturnIgnore; // Error. Just leave.
          //
        }//switch 
        //
      }//if
    }//for
    return result;
  } //method 

  // -------------------------------------------
  // minor tools 

  void forkHorizontal (PVector pvA, PVector pvB, PVector pvC) {
    // 
    //  draws :
    //                (E)_________ B 
    //                |
    //     A ---------|(D)
    //                |
    //                (F)--------- C

    // (The x-values of B and C should be the same)  

    // pvA's y-value gets overwritten btw.
    pvA.y =  (pvB.y + pvC.y) / 2 ;

    PVector pvD = new PVector ( (pvA.x + pvB.x) / 2, (pvB.y + pvC.y) / 2 );

    PVector pvE = new PVector ( pvD.x, pvB.y ); 
    PVector pvF = new PVector ( pvD.x, pvC.y );  

    linePV ( pvA, pvD ) ; 
    linePV ( pvE, pvF ) ;

    linePV ( pvE, pvB ) ;
    linePV ( pvF, pvC ) ;
  }//method 

  void forkVertical (PVector pvA, PVector pvB, PVector pvC) {
    // 
    //  draws :
    //             A
    //             |
    //             |
    //       E-----D----F
    //       |          |
    //       |          |
    //       |          |
    //       B          C


    // (The y-values of B and C should be the same)  

    // pvA's x-value gets overwritten btw.
    pvA.x =  (pvB.x + pvC.x) / 2 ;

    PVector pvD = new PVector (  (pvB.x + pvC.x) / 2, (pvA.y + pvB.y) / 2 );

    PVector pvE = new PVector ( pvB.x, pvD.y); 
    PVector pvF = new PVector ( pvC.x, pvD.y);  

    linePV ( pvA, pvD ) ; 
    linePV ( pvE, pvF ) ;

    linePV ( pvE, pvB ) ;
    linePV ( pvF, pvC ) ;
  }//method 

  void rightAngle( PVector pvA, PVector pvB  ) {
    //draws:
    //
    //  A
    //  |
    //  |
    //  |
    //  (C)---------B
    // 
    PVector pvC = new PVector (pvA.x, pvB.y); 
    linePV( pvA, pvC );
    linePV( pvC, pvB );
  }

  void linePV(PVector pv1, PVector pv2) {
    line(pv1.x, pv1.y, 
      pv2.x, pv2.y);
  }//method

  boolean closeAllMenus() {
    // close all menus.
    // Returns failure (false) when 
    // we are not in Drop Down Mode
    // or when no menu was open.
    // When a menu was closed, success (true) is returned by the variable result. 

    // When we are not in Drop Down Mode return failure 
    if (!flagDropDown)
      return false; // return failure

    boolean result=false; 
    for (Map.Entry<String, Group> hmEntry : hmGroups.entrySet()) {
      Group currentGroup = hmEntry.getValue();
      if (currentGroup.dropDownIsOpen) {
        result=true;
      }
      currentGroup.dropDownIsOpen=false;
    }//for
    return result;
  }//method 

  void setInactive( Group currGroup ) {
    // for radio buttons: set entire group inactive
    for (int ind : currGroup.indexOfButtons) {
      buttons[ind].isActive=false;
    }
  }//method

  void printlnCurrentButtonID() {
    // during adding buttons user can retrieve the last ID 
    println(iForNewButtons-1
      +": "
      +buttons[iForNewButtons-1].btnText);
  }
  //
}//class
//

// ********************************************************************************
// tab: inputsKeyboard.pde


// INPUT FUNCTIONS keyboard 

void keyPressed() {

  statusBarText=statusBarTextDefault;

  if (state==HELP) {
    if (key==ESC) {
      key=0; // kill ESC
      state=NORMAL;
    }//if
    return;
  }//if

  // Normal 
  if (state==NORMAL) {
    // menu position 
    if (key=='l')menu.setLeft();
    else if (key=='r')menu.setRight();
    else if (key=='t')menu.setTop();
    else if (key=='b') menu.setBottom();
    else if (key=='d') menu.setDropDown();
    else if (key==ESC) {
      // when a menu is open, close it (and return true)
      if (menu.closeAllMenus()) 
        key=0; // kill Esc when a menu was closed
    }//else if
    return;
  }//if
}//func 
//

// ********************************************************************************
// tab: inputsMouse.pde


// INPUTS Mouse 

void mousePressed() {

  // state Help 
  if (state==HELP) {
    state=NORMAL; 
    return;
  }//if

  // state Normal 
  if (state==NORMAL) {
    // the menu
    if (mouseButton==LEFT) {
      int menuReturn = menu.checkMouseOnMenu(); 
      doCommand(menuReturn);
    } //if
  } //if
} // func 
//

// ********************************************************************************
// tab: toolsDoCommand.pde


// Tools for buttons 

void doCommand(int menuReturn) {

  statusBarText = statusBarTextDefault;

  switch(menuReturn) {

    // menuReturn is not necessary a button ID
    // but can be Menu.menuReturnIgnore, Menu.menuReturnRadioButton,
    // Menu.menuReturnCheckBox which are all negative.
    // Positive values are IDs and are used for command buttons. 

    //--------------------------
    // step 1 : three negative vars 

  case Menu.menuReturnIgnore:
    //ignore (headlines, errors etc.)
    break; 

  case Menu.menuReturnRadioButton:
    // radio button:
    // to make things easier, we just update all variables (in this case 2) that are connected to radio buttons / groups.
    // Programmer using the lib needs to know which int switch is connected to which group of radio buttons
    mode     = menu.getValueFromGroup( "Modes_Group" );
    viewMode = menu.getValueFromGroup( "Views_Group" ); 
    break; 

  case Menu.menuReturnCheckBox:
    // Check Box Button:
    // to make things easier, we just update all variables (in this case 1) that are connected to check box buttons.
    // you need to know IDs of buttons.
    // cam view ON/OFF     
    boolTest = menu.buttons[13].isActive;
    break; 

    // -------------------------------------------
    // step 2 : just IDs of buttons for command buttons  

  case 15:
    // Export
    statusBarText="Export (nothing happening, just a demo)";
    break;

  case 16: 
    //Help
    state = HELP; 
    break;

  default:
    // Error situation - doesn't need to be changed by user of library 
    statusBarText="Error";
    println ("Error 170 : in doCommand: "
      +menuReturn); 
    exit();
    return; // leave here
  }//switch
}//func 
//

// ********************************************************************************
// tab: toolsStates.pde


// states 

void drawForNormal() {

  background(255); 

  // distinguish between modes (it's not a state)
  fill(0);
  text("Mode is "+mode  
    +"\nView Mode is "+viewMode
    +"\nCamera View (boolean) is "+boolTest, 160, 300);

  // in ALL modes: ---------- 

  // show status
  if (menu.menuPosition != menu.POS_BOTTOM) {
    fill(0);
    text(statusBarText, 5, height-4);
    stroke(0);
    line(0, height-21, 
      width, height-21);
  }

  // show text help 
  fill(0) ;
  text(helpText, 160, 107);

  menu.display(); 
  //
}// func 

void drawForHelp() {

  background(255);

  PVector pos = menu.getHelpBoxPos();
  fill(0, 255, 0); 
  text("Help Screen - "
    +"\n\nPress ESCAPE to leave this help screen. ", 
    pos.x, pos.y, 300, 700);

  fill(255, 0, 0); 
  menu.displayHelp(); 
  //
} // func 
//

// End of joined file. *************************************************
2 Likes

This is also a possibility, thank you for your efforts :smile:

I just found how to create a menu:

I’ve revised that a bit:

Main:

import javax.swing.filechooser.FileSystemView;
import javax.swing.JFileChooser;
Menu_bar mp;

void setup() {
  size(600, 600);
  buildMenuBar();
}

void draw() {
  background(0);
  fill(255);
  ellipse(mouseX, mouseY, 30, 30);
}

void buildMenuBar() {
  mp = new Menu_bar(this, getClass().getSimpleName(), width, height);
}

Menu_bar:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

public class Menu_bar implements ActionListener {
  JFrame frame;

  public Menu_bar(PApplet app, String name, int width, int height) {
    System.setProperty("apple.laf.useScreenMenuBar", "true");
    frame = (JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)app.getSurface().getNative()).getFrame();
    frame.setTitle(name);

    // Creates a menubar for a JFrame
    JMenuBar menu_bar = new JMenuBar();
    // Add the menubar to the frame
    frame.setJMenuBar(menu_bar);
    // Define and add two drop down menu to the menubar
    JMenu import_menu = new JMenu("File");
    JMenu text_menu = new JMenu("Edit");
    JMenu shape_menu = new JMenu("Sketch");
    JMenu image_menu = new JMenu("Tools");
    JMenu video_menu = new JMenu("Help");

    menu_bar.add(import_menu);
    menu_bar.add(text_menu);
    menu_bar.add(shape_menu);
    menu_bar.add(image_menu);
    menu_bar.add(video_menu);

    // Create and add simple menu item to one of the drop down menu
    JMenuItem new_file = new JMenuItem("Save");
    JMenuItem new_folder = new JMenuItem("Save As...");
    JMenuItem action_exit = new JMenuItem("Exit");

    new_file.addActionListener(this);
    new_folder.addActionListener(this);
    action_exit.addActionListener(this);

    import_menu.add(new_file);
    import_menu.add(new_folder);
    import_menu.addSeparator();
    import_menu.add(action_exit);

    frame.setVisible(true);
  }
  @Override
    public void actionPerformed(ActionEvent e) {
    JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory());
    jfc.setDialogTitle("Choose destination.");
    jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

    String ae = e.getActionCommand();

    if (ae.equals("Save")) {
      println("Save");
    } else if (ae.equals("Save As...")) {
      println("Save As...");
    } else if (ae.equals("Exit")) {
      println("Exit");
      System.exit(0);
    }
  }
}

4 Likes