Wait for HtmlDialog to close before executing next line

How do I wait for my html window to close, or at least the submit button to have been clicked, before collecting my javascript parameters from the #HtmlDialog?

So I’m currently trying to use:

module DialogBox
  extend self
  def dialogtest_setup
    # html dialog is setup and defined in here
  end

  def show_dialog
    @dialog.show
    # some code here to wait for the box to be closed/submit button to be clicked
      @dialog.add_action_callback("say") { |action_context, param1, param2|
        @param1, @param2 = param1, param2
      }
  end

  def puts_params
    puts @param1
    puts @param2
  end
end

testbox = DialogBox
testbox.dialogtest_setup
testbox.show_dialog
testbox.puts_params

But this returns previous values :roll_eyes:

Any help appreciated!!

OK, I changed the code to make it work as follows:

module DialogBox
  extend self
  def dialogtest_setup
    # html dialog is setup and defined in here
  end

  def show_dialog
    @dialog.show
    @dialog.add_action_callback("say") { |action_context, param1, param2|
      @param1, @param2 = param1, param2
      puts @param1
      puts @param2
    }
  end
end

testbox = DialogBox
testbox.dialogtest_setup
testbox.show_dialog

This works for me, because I will just call the necessary code block within the add_action_callback. However, this may not work for some people…any suggestions on how to wait outside of the module?

Using the callbacks to ensure the dialog has done its thing is the idiomatic way to do it. With async communication there isn’t any better way around it, you have to use chained callbacks like this.

1 Like

One option, and the one I’m using, is to use dialog.show_modal instead of dialog.show. The user will be unable to interact with sketchup’s main application until the dialog is closed.

This works well for me because I do not want the user to be able to manipulate the model or what’s selected while the dialog is open as I’m using what’s selected to manipulate information to present to the user.

1 Like

It works for you, but with a bit of explanation of what the “solution” is, it may be helpful for others.

  • Your approach is to put the code (“next line” or a method call to that code) in place of puts @param1.
    That means that the execution (as envisioned in the thread title) becomes instead of

      → show_dialog()
      ↵
      → next_method()
      ↵
    

    the call to the next method is nested:

      → show_dialog()
          ↳ next_method()
          ↵
      ↵
    
  • It may be that someone does not want the method show_dialog to “know” what to execute next, because then the same method can be reused for more situations. An alternative (or extension of the idea) is to pass next_method as a parameter (a reference to the method or a Ruby code block), a “callback”.

    def show_dialog(&callback)
      # …
      @dialog.add_action_callback("say") { |action_context, param1, param2|
        # …
        callback.call(param1, param2)
      }
    end
    
    show_dialog{ |param1, param2|
      puts(param1)
    }
    

    In that case, show_dialog must know about the callback, it must accept it as a parameter even if it is not used. Another alternative is to return an object that represents “when the method has completed successfully”, and only this object knows and executed callbacks:

    promise = show_dialog()
    promise.then{ |param1, param2|
      puts(param1)
    }
    

    I am heavily using this pattern in for example the Console+ and other plugins.

  • In any case it is important not to forget that while HtmlDialogs are async, Ruby in SketchUp is single-threaded, so any solution cannot require concurrency with threading.

3 Likes