Can HtmlDialog yield focus to SketchUp view?

I am working on an extension in which I would like to report various info via an HtmlDialog while the extension runs. When I show() the dialog, it grabs focus from the view and until it closes all keypresses go only to the dialog; they are not seen as events by SketchUp. As a result things like user keyboard shortcuts don’t do anything and can’t be used as a way to activate another Tool.

I know how to relay keydown events from javascript to Ruby via action callbacks, but a) I then have to translate javascript keycodes, which is ok for a small list of explicitly interesting ones but a major task to handle all possible keys with all possible modifiers, and b) I don’t know any way to pass the keys from Ruby to SketchUp so that they can be handled as events there.

If I manually shift the focus back to SketchUp, e.g. by clicking in the view, then keys operate normally in SketchUp. But that’s a peculiar and easily overlooked UX.

So, my question is whether there is either a javascript or Ruby way to either a) tell the dialog to let keypresses bubble on to SketchUp, or b) tell the dialog to give the focus back to the view?

I was in the same situation when working on my Eneroth Railroad System many years ago. In my example I did capture key presses in the dialog window and then called onKeyPress of my custom Ruby tool, but that did not support native SketchUp shortcuts, which introduced an inconsistency that could be more confusing than doing nothing at all.

If the is (platform specific) API for changing focus to the Sketchup window, it can be quite frustrating when a window loses focus seemingly arbitrarily. In older SU versions some windows lost focus when moving the cursor out of them, meaning if you wanted to focus on some text without having the cursor in front of it, the title bar changed color and stuff happened around breaking that focus.

Ideally, pressing a key inside a dialog window, while not having an input focused and not having the keypress being used inside that window, should send the key press up to SketchUp, but if it can’t be achieved flawlessly, I’d say it’s better to do nothing at all and just get used to activating the SU window.

I think John Boundy says* the easiest “hack” is to create another tiny dialog (off screen if able), and then to close it right away. Since it’s a child window of the application, it’s the mother application that regains the focus.

* REF: Cursor doesn't change on Mac when activating a tool from Ruby API

# This Works on MS Windows ..
def test_focus
  UI::HtmlDialog.new({dialog_title: "Test"}).show
  UI.start_timer(0.3,false) {
    dlg = UI::HtmlDialog.new({
      dialog_title: "Focus", resizable: false,
      width: 10, height: 10, left: 0, top: 0
    })
    dlg.show
    dlg.close
  }
end

UI.menu("Plugins").add_item("Test Focus") {
  test_focus()
}

ADD: In practice, the first dialog would wait until it’s content was loaded and then do the fake window trick.

1 Like

I also wonder if a frivolous UI::Notification would return focus to the application window ?

EDIT: NO not on MS Windows. The notifcations don’t take focus.

UI::HtmlDialog.new({dialog_title: "Test"}).show
UI::Notification.new(SketchupExtension.new("Test","Bogus/Filepath"),"Returning focus to main window.").show

Or … UI.refresh_toolbars, … UI.refresh_inspectors … etc.

The HTMLElement.blur() method removes keyboard focus from the current element.

Does the blur() method work when used on the window or other top-level element?

No, (at least I don’t think so,) hence my API request …

I tried blur and couldn’t get it to work.

When I try that, it crashes SketchUp :worried:

I just use this now…

    def de_focus
      path = Sketchup.active_model.path
      Sketchup.open_file(path) unless path.empty?
      # ...other plugin specific code
    end

  #  and use it in a call
      @dlg.add_action_callback('blur_dlg') do |_d, _p|
        de_focus
      end

john

John’s previous example (in the linked topic) did not use a timer block method. (And he’s on Mac.)

I just used a timer (in the above snippet) cause I wanted to simulate the 2nd fake dialog waiting until the 1st dialog’s content was loaded. So in reality we’d do …

  @dlg = UI::HtmlDialog.new({dialog_title: "Test"})

  @dlg.add_action_callback("focus_app") do |_unused|
    focus_sketchup_window()
  end

  @dlg.show

  def focus_sketchup_window()
    dlg = UI::HtmlDialog.new({
      dialog_title: "Focus", resizable: false,
      width: 10, height: 10, left: 0, top: 0
    })
    dlg.set_html('<html></html>')
    dlg.show
    sleep(0.3) # or use a UI timer ?
    dlg.close
  end

… and your dialog’s JS content loaded listener would call:

sketchup.focus_app

You might also be able to use this call from JS in an onMouseLeave listener.

On MS Windows, this doesn’t do anything (from the console) whether the path is empty or not, nor even if the active model has been modified. (We’d expect it to ask to save changes and then revert if the choice was No. But it doesn’t do either. It’s a no-op.)

The documentation is not correct in this regard. But neither does it form a contract to focus the active model document window. … ie, fragility.

Nor does it work with an UI::HtmlDialog on MS Windows

def test_focus
  UI::HtmlDialog.new({dialog_title: "Test"}).show
  Sketchup.open_file(Sketchup.active_model.path)
end

UI.menu("Plugins").add_item("Test Focus") {
  test_focus()
}
1 Like