UI 101

edited June 2015 in Questions about Code

Hi there

Building my little demo, I have reached the point where I need to ask the user what he wants to do so here goes my very first steps into building UI interaction into processing

I have a small piece of code that I just wrote and "does the job" but I have the feeling that :

1 I kind of tried to invent the wheel again

2 I'm not going to go very far with my 3 sided wheel

I would very much appreciate your opinions on this

void saveLevel () {
    //when saving, first i need to know about synchronizing the 2 levels
    //then I need a level number
    if (writeOK == false){
        if (p5.numInput != 0){
            numOK = true;
        }
        if (p5.gridSynced  == true){
            syncOK = true;
        }
        if(numOK == false){
            // before that, I need to know if the 2 GRIDs are synchronized
            if (syncOK == false){
                p5.fill(55, 128);
                p5.rect(p5.width/2, p5.height/2, p5.width/2,p5.height/2,17);
                p5.fill(255);
                p5.text("Grids are not synchronised. Do you want to Sync ? : Y/N", p5.width/2-175, p5.height/2);
                p5.askYN = true;
                if (p5.yesNo == 1){
                    level.syncGrids();
                    //p5.println("sync");
                    p5.yesNo = 0;
                    syncOK = true;
                    p5.askYN = false;
                }
                else if (p5.yesNo == 2){
                    //p5.println("save without sync");
                    p5.yesNo = 0;
                    syncOK = true;
                    p5.askYN = false;
                }
            }
            //then ask for the level number
            if (syncOK == true){
                p5.fill(55, 128);
                p5.rect(p5.width/2, p5.height/2, p5.width/2,p5.height/2,17);
                p5.fill(255);
                p5.text("give the level number:", p5.width/2-55, p5.height/2);
                p5.askNum = true;

            }
            // when I have the level number
        } else {
            //save to levels.txt
            p5.text(p5.numInput, p5.width/2-25, p5.height/2);
            writeLevel (p5.numInput);
            p5.askNum = false;
        }
    }
    else {
        // finally ; display the level number that the user did input
        displaySaved(p5.numInput);
    }

}
////////////////////////////////////////////////
void writeLevel (int numInput) {}
////////////////////////////////////////////////

// when saving is done, show it is done
void displaySaved(int numInput){
    if (doOnce == 0) {
        move();
        if (isDead() == false){
            p5.fill(255, 128,0, msgTrans);
            p5.rect(p5.width/2, p5.height/2, p5.width/2,p5.height/2,17);
            p5.fill(255);
            p5.text("Level SAVED AS: " + p5.numInput, p5.width/2-55, p5.height/2);
        }
        else{
            p5.numInput = 0;
            numOK = false;
            syncOK = false;
            writeOK = false;
            msgTrans = 254.0f;
            //job's done
            p5.goSave = false;
        }
    }
}

void move () {
    alpha += speed;
    if (alpha < factor) {
        //the formula I choose being:  - (x² - factor) + factor²
        msgTrans = (-PApplet.sq(alpha)+PApplet.sq(factor)) ;
    } else if (alpha > factor) {
        alpha = 0.0f;
    }
}

boolean isDead() {
    if (msgTrans < 0.1) {
        doOnce = 1;
        return true;
    }else {
        return false;
    }
}
Tagged:

Answers

  • Maybe you could look into libraries such as ControlP5 or G4P?

  • Thank you for answering coloured :) From what I've been looking those libraries seems to help at making buttons for their look and interactions... but I am more wondering about the way to structure the code for this simple "question" and further, more sophisticated interface "windows".

    For example , in this code, I push the "save level" key and pops up a question: - what number do you want to give to this level ? - but BEFORE that I check if the level is "synchronized" and, if not, want to ask you if you want it to be synchronized - meanwhile I did set-up my question in a way that allows me to end it, at any time before saving, by pushing the escape key - finally, when I get my answers, I want to display that the process has been done successfully

    So here it is, a super simple interaction that looks messy to my eyes

    what you think ? :)

  • Well it's a bit hard to comment on your code since you can't run it straight away. Could you post a version that is runnable? It doesn't need to be the whole program.

  • here you go :)

    final int STATEEDIT1 = 1;
    int state = STATEEDIT1;   // current state 
    ///////////////////////////
    //Save / Load /ReaderWriter
    boolean goLoad = false;
    boolean goSave = false;
    //
    boolean syncOK;
    boolean numOK;
    boolean writeOK;
    //////////////////////fading msg
    float msgTrans;
    int doOnce;
    //factor being the magnitude of the fading
    int factor    = 13;
    float speed   = 0.2f;
    float alpha   = 0.0f;
    ///////////////////////
    // INTERFACE
    boolean askYN = false;
    int yesNo      = 0;  //0 = no answer, 1 = yes, 2 = no
    boolean echap  = false;
    boolean askNum = false;
    int numInput;
    //////////////////from level class
    boolean gridSynced = false;
    
    ////////////////////////////////////////////////////////////
    ///////////////////BASIC MAIN//////////////////////////////
    ////////////////////////////////////////////////////////////
    
    void setup() {
      size(400, 300);
      syncOK  = false;
      numOK   = false;
      writeOK = false;
      doOnce  = 0;
    }
    
    void draw() {
      background(255);
      rectMode(CENTER);
      textAlign(CENTER);
      fill(0, 0, 255);
      text("Push 'P' to save", width/2, 15);
    
      if (goSave == true) {
        saveLevel ();
      }
    }
    ////////////////////////////////////////////////////////////
    ///////////////////KEY STROKES//////////////////////////////
    ////////////////////////////////////////////////////////////
    public void keyPressed() {
      if (state == STATEEDIT1 ) {
        keyPressedForSTATEEDIT();
      }
    }
    
    void keyPressedForSTATEEDIT() {
      //print level to file
      if ( key == 'p'|| key == 'P') {
        //readwrite.saveLevel();
        numInput = 0;
        goSave = true;
      }
    
      ///keyboard number reader
      if (askNum == true) {
        if ( (key>='1' && key<='9')) {
          numInput = char2int (key);
        }
      }
      ///YES / NO question // 0 = no answer, 1 = yes, 2 = no
      if (askYN == true) {
        if ( key == 'y'|| key == 'Y') {
          yesNo = 1;
        }
        if ( key == 'n'|| key == 'N') {
          yesNo = 2;
        }
      }
      //Actions for ESCAPE
      if (keyCode == 27) {
        // I like to have ESC to discard a task
        key = 0;
        goSave = false;
        //state = stateMenu;
      }
    }
    //to read user input we have to convert keyboard char into int
    //couldn't get the int() thing to work
    int char2int(char myChar) {
      int buffer = 0;
    
      switch (myChar) {
      case '0':
        buffer = 0;
        break; 
    
      case '1':
        buffer = 1;
        break; 
    
      case '2':
        buffer = 2;
        break; 
    
      case '3':
        buffer = 3;
        break; 
    
      case '4':
        buffer = 4;
        break; 
    
      case '5':
        buffer = 5;
        break; 
    
      case '6':
        buffer = 6;
        break; 
    
      case '7':
        buffer = 7;
        break;
    
      case '8':
        buffer = 8;
        break;
    
      case '9':
        buffer = 9;
        break;
      }
      return (buffer);
    }
    
    
    ////////////////////////////////////////////////////////////
    ///////////////////SAVE INTERFACE///////////////////////////
    ////////////////////////////////////////////////////////////
    
    void saveLevel () {
      //when saving, first i need to know about synchronizing the 2 levels
      //then I need a level number
      if (writeOK == false) {
        if (numInput != 0) {
          numOK = true;
        }
        if (gridSynced  == true) {
          syncOK = true;
        }
        if (numOK == false) {
          // before that, I need to know if the 2 GRIDs are synchronized
          if (syncOK == false) {
            fill(55, 128);
            rect(width/2, height/2, width, height/2, 17);
            fill(255);
            text("Grids are not synchronised.  Do you want to Sync ? : Y/N", width/2, height/2);
            askYN = true;
            if (yesNo == 1) {
              //say we do level.syncGrids();
              //println("sync");
              yesNo = 0;
              syncOK = true;
              askYN = false;
            } else if (yesNo == 2) {
              //println("save without sync");
              yesNo = 0;
              syncOK = true;
              askYN = false;
            }
          }
          //then ask for the level number
          if (syncOK == true) {
            fill(55, 128);
            rect(width/2, height/2, width/2, height/2, 17);
            fill(255);
            text("give the level number:", width/2, height/2);
            askNum = true;
          }
          // when I have the level number
        } else {
          //save to levels.txt
          text(numInput, width/2-25, height/2);
          writeLevel (numInput);
          askNum = false;
        }
      } else {
        // finally ; display the level number that the user did input
        displaySaved(numInput);
      }
    }
    ////////////////////////////////////////////////
    void writeLevel (int numInput) {
      //fake write function
      writeOK = true;
      doOnce = 0;
    }
    /////////////////////////////////////////////////
    
    // when saving is done, show it is done
    void displaySaved(int numInput) {
      if (doOnce == 0) {
        move();
        if (isDead() == false) {
          fill(255, 128, 0, msgTrans);
          rect(width/2, height/2, width/2, height/2, 17);
          fill(255);
          text("Level SAVED AS: " +numInput, width/2, height/2);
        } else {
          numInput = 0;
          numOK = false;
          syncOK = false;
          writeOK = false;
          msgTrans = 254.0f;
          //job's done
          goSave = false;
        }
      }
    }
    
    void move () {
      alpha += speed;
      if (alpha < factor) {
        msgTrans = (-PApplet.sq(alpha)+PApplet.sq(factor)) ;
      } else if (alpha > factor) {
        alpha = 0.0f;
      }
    }
    
    boolean isDead() {
      if (msgTrans < 0.1) {
        doOnce = 1;
        return true;
      } else {
        return false;
      }
    }
    
  • edited June 2015

    One comment is that you should definately switch to OOP. Are you familiar with object oriented programming and classes? If not, definately go follow some tutorials.

    First of all it's really nicely executed and looks great!

    Second, the int char2int(char myChar) should just look like this:

    return (int) myChar-48;

    Yes, it's that simple. Look at this link: http://www.asciitable.com/ , it says what char (when converted to an int) corresponds to what number. The numbers start at position 48 so you need to subtract the char with 48 to get the actual number it represents.

    Back to OOP. I suggest you make a parent "GuiElement" class and extend it (make a child class) for all the actual elements you need. That way you can still have a large variety of elements but have them work more or less the same. You could make a class for user input, yes/no boxes, feedback etc. so you don't need to do it again and again. They could have an internal state like WAITFORUSERINPUT which only exists within the class so the main program doesn't get clogged with global variables.

  • edited June 2015

    wah ! it feels really good to read a nice comment about my job early in the morning :) specially since I would never even think of being good at programming

    I'm going to try that (int) char thing again and, about the OOP, I did "un-OOP" my code to share it here because it is spread across various classes already. You can see the various "p5." things in the first bits I pasted in my first post... I did follow a great MOOC on OOP and I kind of "slice" my code into classes yet.( I have still to master the hierarchy principle because I still don't use the parent/ child feature. )

    back to my saveLevel () function:

    Is it suppose to look like that ?

    the 2 first if() feel to me like some patch on bad logic... are they ?

                 if (p5.numInput != 0){
                    numOK = true;
                }
                if (p5.gridSynced  == true){
                    syncOK = true;
                }
    

    thanks again for the time you take to help (y)

  • edited June 2015 Answer ✓

    I wouldn't know if it's bad logic... if it works it works, even if there's a better way to do it. As you get more practice when you code, this will improve. Just the other day I wrote something in 50 lines while a year ago I did more or less the same thing in 400 lines... just by using a little recursion trick and ternary operators and all kinds of little tricks I picked up over time.

    As for a class scheme maybe something like this?

    Class: Gui
    Fields: int x, y, sizex, sizey
    Methods: update(), display()
    
    Class: GuiMessage extends Gui
    Fields: String message, GuiButton ok
    Methods: update(), display(), buttonClicked()
    
    Class: GuiNumericInput extends Gui
    Fields: int value, GuiNumberbox input
    Methods: update(), display(), setValue(), ...
    
    Class: GuiElement
    Fields: int x, y
    Methods: update(), display()
    
    Class: GuiButton extends GuiElement
    Fields: String text, boolean clicked
    Methods: update(), display(), notifyClicked()
    
    Class: GuiNumberbox extends GuiElement
    Fields: float value
    Methods: update(), display(), changeValue()
    
    ...
    
  • ok got it I will dig this class scheme thanks again !!

Sign In or Register to comment.