Editable textbox in mobile phone

URL of experiment: painpred [PsychoPy]

Description of the problem:

  • OS: Android
  • browser: Google Chrome

In order to get typed response from participants, I added a textbox from Builder window. It went well locally. Then I compile it to js code and run it on Pavlovia. It also went well in iPad (both Safari and Chrome). But when I tried it on mobile phone, the keyboard start to pop up continuously each frame.

I paste some of the javascript code which related to this textbox object here.

var PID_box;
/*******/
PID_box = new visual.TextBox({
    win: psychoJS.window,
    name: 'PID_box',
    text: '',
    font: 'Open Sans',
    pos: [0, 0], letterHeight: 0.05,
    //size change based on multiple devices (I am sure this part works) 
    size: [0.5*longside,0.1*longside],  units: undefined,
    color: 'white', colorSpace: 'rgb',
    fillColor: [0.0039, 0.0039, 0.0039], borderColor: [(- 1.0), 1.0, 1.0],
    languageStyle: 'LTR',
    bold: false, italic: false,
    opacity: 1,
    padding: undefined,
    //alignment: 'center',
    editable: true,
    multiline: true,
    anchor: 'center',
    depth: -4.0 
  });
/*****/
function addPIDRoutineBegin(snapshot) {
  return async function () {
    TrialHandler.fromSnapshot(snapshot); // ensure that .thisN vals are up to date
    
    //--- Prepare to start Routine 'addPID' ---
    t = 0;
    addPIDClock.reset(); // clock
    frameN = -1;
    continueRoutine = true; // until we're told otherwise
    // update component parameters for each repeat
    PID_box.setText('');
    PID_box.refresh();
    PID_box.setText('');
    // setup some python lists for storing info about the mouse_PID
    // current position of the mouse:
    mouse_PID.x = [];
    mouse_PID.y = [];
    mouse_PID.leftButton = [];
    mouse_PID.midButton = [];
    mouse_PID.rightButton = [];
    mouse_PID.time = [];
    mouse_PID.clicked_name = [];
    gotValidClick = false; // until a click is received
    // keep track of which components have finished
    addPIDComponents = [];
    addPIDComponents.push(PID_instruction);
    addPIDComponents.push(PID_box);
    addPIDComponents.push(next_button_PID);
    addPIDComponents.push(mouse_PID);
    
    for (const thisComponent of addPIDComponents)
      if ('status' in thisComponent)
        thisComponent.status = PsychoJS.Status.NOT_STARTED;
    return Scheduler.Event.NEXT;
  }
}


var prevButtonState;
var _mouseButtons;
var _mouseXYs;
function addPIDRoutineEachFrame() {
  return async function () {
    //--- Loop for each frame of Routine 'addPID' ---
    // get current time
    t = addPIDClock.getTime();
    frameN = frameN + 1;// number of completed frames (so 0 is the first frame)
    // update/draw components on each frame
    
    // *PID_instruction* updates
    if (t >= 0 && PID_instruction.status === PsychoJS.Status.NOT_STARTED) {
      // keep track of start time/frame for later
      PID_instruction.tStart = t;  // (not accounting for frame time here)
      PID_instruction.frameNStart = frameN;  // exact frame index
      
      PID_instruction.setAutoDraw(true);
    }

    
    // *PID_box* updates
    if (t >= 0.0 && PID_box.status === PsychoJS.Status.NOT_STARTED) {
      // keep track of start time/frame for later
      PID_box.tStart = t;  // (not accounting for frame time here)
      PID_box.frameNStart = frameN;  // exact frame index
      
      PID_box.setAutoDraw(true);
    }

    
    // *next_button_PID* updates
    if (t >= 0.0 && next_button_PID.status === PsychoJS.Status.NOT_STARTED) {
      // keep track of start time/frame for later
      next_button_PID.tStart = t;  // (not accounting for frame time here)
      next_button_PID.frameNStart = frameN;  // exact frame index
      
      next_button_PID.setAutoDraw(true);
    }

    // *mouse_PID* updates
    if (t >= 0 && mouse_PID.status === PsychoJS.Status.NOT_STARTED) {
      // keep track of start time/frame for later
      mouse_PID.tStart = t;  // (not accounting for frame time here)
      mouse_PID.frameNStart = frameN;  // exact frame index
      
      mouse_PID.status = PsychoJS.Status.STARTED;
      mouse_PID.mouseClock.reset();
      prevButtonState = mouse_PID.getPressed();  // if button is down already this ISN'T a new click
      }
    if (mouse_PID.status === PsychoJS.Status.STARTED) {  // only update if started and not finished!
      _mouseButtons = mouse_PID.getPressed();
      if (!_mouseButtons.every( (e,i,) => (e == prevButtonState[i]) )) { // button state changed?
        prevButtonState = _mouseButtons;
        if (_mouseButtons.reduce( (e, acc) => (e+acc) ) > 0) { // state changed to a new click
          // check if the mouse was inside our 'clickable' objects
          gotValidClick = false;
          for (const obj of [next_button_PID]) {
            if (obj.contains(mouse_PID)) {
              gotValidClick = true;
              mouse_PID.clicked_name.push(obj.name)
            }
          }
          _mouseXYs = mouse_PID.getPos();
          mouse_PID.x.push(_mouseXYs[0]);
          mouse_PID.y.push(_mouseXYs[1]);
          mouse_PID.leftButton.push(_mouseButtons[0]);
          mouse_PID.midButton.push(_mouseButtons[1]);
          mouse_PID.rightButton.push(_mouseButtons[2]);
          mouse_PID.time.push(mouse_PID.mouseClock.getTime());
          if (gotValidClick === true) { // abort routine on response
            continueRoutine = false;
          }
        }
      }
    }
    // check for quit (typically the Esc key)
    if (psychoJS.experiment.experimentEnded || psychoJS.eventManager.getKeys({keyList:['escape']}).length > 0) {
      return quitPsychoJS('The [Escape] key was pressed. Goodbye!', false);
    }
    
    // check if the Routine should terminate
    if (!continueRoutine) {  // a component has requested a forced-end of Routine
      return Scheduler.Event.NEXT;
    }
    
    continueRoutine = false;  // reverts to True if at least one component still running
    for (const thisComponent of addPIDComponents)
      if ('status' in thisComponent && thisComponent.status !== PsychoJS.Status.FINISHED) {
        continueRoutine = true;
        break;
      }
    
    // refresh the screen if continuing
    if (continueRoutine) {
      return Scheduler.Event.FLIP_REPEAT;
    } else {
      return Scheduler.Event.NEXT;
    }
  };
}


function addPIDRoutineEnd(snapshot) {
  return async function () {
    //--- Ending Routine 'addPID' ---
    for (const thisComponent of addPIDComponents) {
      if (typeof thisComponent.setAutoDraw === 'function') {
        thisComponent.setAutoDraw(false);
      }
    }
    psychoJS.experiment.addData('PID_box.text',PID_box.text)
    // store data for psychoJS.experiment (ExperimentHandler)
    if (mouse_PID.x) {  psychoJS.experiment.addData('mouse_PID.x', mouse_PID.x[0])};
    if (mouse_PID.y) {  psychoJS.experiment.addData('mouse_PID.y', mouse_PID.y[0])};
    if (mouse_PID.leftButton) {  psychoJS.experiment.addData('mouse_PID.leftButton', mouse_PID.leftButton[0])};
    if (mouse_PID.midButton) {  psychoJS.experiment.addData('mouse_PID.midButton', mouse_PID.midButton[0])};
    if (mouse_PID.rightButton) {  psychoJS.experiment.addData('mouse_PID.rightButton', mouse_PID.rightButton[0])};
    if (mouse_PID.time) {  psychoJS.experiment.addData('mouse_PID.time', mouse_PID.time[0])};
    if (mouse_PID.clicked_name) {  psychoJS.experiment.addData('mouse_PID.clicked_name', mouse_PID.clicked_name[0])};
    
    // the Routine "addPID" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset();
    
    // Routines running outside a loop should always advance the datafile row
    if (currentLoop === psychoJS.experiment) {
      psychoJS.experiment.nextEntry(snapshot);
    }
    return Scheduler.Event.NEXT;
  }
}
1 Like

Actually only appear in Android system. It goes well in iPhone

I am also facing the same issue, can someone from pyschopy please take a look at this? I am even happy to schedule a call to discuss it in detail.

Thanks!

1 Like