UI::HtmlDialog.initialize preferences_key

What is the parameter “preferences_key” in the initialize method of the HtmlDialog class used for?
I’m still using SketchUp Make 2017 (it’s free).
Thanks in advance

I use something like:

dialog = UI::HtmlDialog.new(
        {
          :dialog_title => "My Dialog",
          :preferences_key => "com.MyDialog",
          :scrollable => true,
          :resizable => true,
          :width => 300,
          :height => 600,
          :style => UI::HtmlDialog::STYLE_DIALOG
        }

To make the HTML Dialog retain size and position when it is opened/closed in and between sessions.

I don’t know the details but size and position don’t seem to save if that isn’t included.

1 Like

As James told SketchUp store the position/size data’s of the dialog under the key defined by a :preferences_key in a PrivatePreferences.json file.

e.g. the section for the built in Extension Manager looks like:

        "WebCommonDialog_ExtensionManager": {
            "Height": 656,
            "Left": 376,
            "Top": 248,
            "Width": 885
        },

The section for the (bad :wink: ) example from James can be like:

        "WebCommonDialog_com.MyDialog": {
            "Height": 600,
            "Left": 110,
            "Top": 290,
            "Width": 300
        },

That is the reason the preference_key should be with something unique to your extension. Otherwise it can be conflicted to other dev’s or your extension.

Ps. You should be aware what the Operating System is (see your forum profile), otherwise can be difficult to learn to make a code.

Exactly. And since your code (@jorgealfredocarvalho ) should be wrapped within a top-level namespace module, and inside that within a uniquely named extensions submodule, … the best thing to do for a unique dialog preference key is create a local constant within your extension submodule:

  PREF_KEY ||= Module.nesting[0].name.gsub('::','_')

You can then use this for your plugin’s default options and also for the dialog’s settings if the plugin only has one dialog.

If the plugin has more than one dialog, you can expand the key with each dialog’s title, as in …

  @dialog = UI::HtmlDialog.new({
    :dialog_title => "Settings",
    :preferences_key => "#{PREF_KEY}_Settings",
    :scrollable => true,
    :resizable  => true,
    :width  => 300,
    :height => 600,
    :style  => UI::HtmlDialog::STYLE_DIALOG
  })

So, as an example, if your module nesting was …

module Carvalho
  module LocoTool

… then the PREF_KEY would be "Carvalho_LocoTool"

and "#{PREF_KEY}_Settings" would evaluate to "Carvalho_LocoTool_Settings",
… which would be the key "WebCommonDialog_Carvalho_LocoTool_Settings" within
the PrivatePreferences.json file.

2 Likes

UTIs have no meaning for Windows and likely no real benefit within SketchUp’s preference JSON files even on the MacOS platform.

If a coder does not know what a UTI is, or how to form a valid one, or even how it would be useful even on the Mac, a coder should avoid trying to “invent” one. (I have complained about the use of UTIs in API documentation snippets in the past.)

We are all better off just using a string that we know will be unique as I showed above.


REF:

Dear community members: Dan Rathbun. dezmo, and James Doyle / 3DxJFD
Thanks for your preciouse help.
I think I understand the resaon of using this parameter.
Usually my programing is in the desktop environment… I have to evolve.
It’s nice to learn new skills.
Best regard
Jorge Carvalho

Dear dezmo
My computer runs Windows 10 (64 bit). My SketchUp app is only local. (Make 2017). This idea of sessions and other users when running sketchup on the browser “It’s not my thing yet”
Thanks
Jorge Carvalho

DearRathbun
Thanks for your suggestion
Jorge Carvalho

My starting point is that if I do not include:

:preferences_key => "withSomethingHere"

My Html Dialogs don’t restore to their last size and position (in and between sessions).

I’ve seen examples -but don’t know how to set up- the PREF_KEYs. The dialog_title titles the Html Dialog, so I usually put the extension name there. Then for the :preferences_key I’ll use the extension name or the name of the extension ‘main’ (without, .rb). That way it is at least unique.

Usually I set up a ‘_loader.rb’ that my registration file points to. There I establish a PLUGIN_PATH that I use in my ‘main.rb’. Then I use something like:

dialog.set_file(File.join(PLUGIN_PATH, 'html', 'MyWonderful.html'))

Right after dialog creation.

Maybe I should create preferences and point to those as well… I know I’ve seen that but I can’t find an example now :slight_smile:

Thanks!

I just showed how simple it is above, viz:

The reason I use these method calls is that they can be part of a extension template without needing to manually enter the namespace module and extension submodule name into a literal string each time starting a project.


It wont be unique unless you keep the top-level namespace as a prefix.

1 Like

It looks like your suggestion worked perfectly.

For example:

module IDK_Programming
  module Make_Group_HTML

    PREF_KEY ||= Module.nesting[0].name.gsub('::','_')
    PLUGIN_PATH ||= File.dirname(__FILE__)

    def show_html_dialog
      dialog = UI::HtmlDialog.new(
        {
          :dialog_title => "Create Group",
          :preferences_key => PREF_KEY,
          :scrollable => true,
          :resizable => true,
          :width => 300,
          :height => 200,
          :style => UI::HtmlDialog::STYLE_DIALOG
        }
      )

      dialog.set_file(File.join(PLUGIN_PATH, 'html', 'Make_Group.html'))

      dialog.show
    end
  end
end

The result in the PrivatePreferences.json:

        "WebCommonDialog_IDK_Programming_Make_Group_HTML": {
            "Height": 200,
            "Left": 538,
            "Top": 48,
            "Width": 300
        },

I was not able to find the “PREF_KEYs” example I saw (maybe it was constants saved to a json or ruby file). But I did find one of the examples used in the SU htmldialog-examples that I used.

(step07.rb)
def self.create_dialog
    html_file = File.join(__dir__, 'html', 'step07.html')
    # Define pixel perfect dimensions matching our content.
    width = 15 + 128 + 15 + 15 + 128 + 15
    height = 470
    # Add some extra space for older SU versions that don't support inner sizing.
    if Sketchup.version.to_f < 21.1
      width += 20
      height += 40
    end
    options = {
      :dialog_title => "Material",
      :preferences_key => "com.sketchup.example.htmldialog.materialinspector",
      :style => UI::HtmlDialog::STYLE_DIALOG,
      # Set a fixed size now that we know the content size.
      :resizable => false,
      :width => width,
      :height => height,
      :use_content_size => true
    }
    dialog = UI::HtmlDialog.new(options)
    dialog.set_file(html_file)
    dialog.center
    dialog
  end

The relevant line from the tutorial:

:preferences_key => "com.sketchup.example.htmldialog.materialinspector",

That creates this json entry:

"WebCommonDialog_com.sketchup.example.htmldialog.materialinspector": {
            "Height": 582,
            "Left": 872,
            "Top": 108,
            "Width": 358
        },

I guess that’s just another convention. For example if I used:

"com.IDK_Programming.Make_Group_HTML"

That would have been unique. But I’ll probably go with your suggestion in the future.

In reality, it is just another poor example.

Yes, but really the "com." prefix has no meaning, as I said above for Windows, and even most Mac users don’t know how to form a valid UTI.

On Apple systems, UTIs have a purpose. They allow the operating system to treat a resource a certain way. But, these poor example UTIs are just keys into an application specific preference file. They do not tell the OS anything. Ie, the "com.sketchup." prefixes are frivolous.

Think about the preference key that is shown in "step07.rb" … does it have unique prefixes? No.
It is still not unique when combined with a probable common extension name like “Material Inspector”.
But the documentation and examples do not teach new coders how to form a unique extension key(s) for preferences, attribute dictionaries and writing defaults.

It doesn’t matter if the separator is "." or "_" or whatever, as long as it begins with an author’s unique namespace identifier and has the extension name in it to differentiate those settings from the author’s other extensions.

My suggestion comes from the fact that the nested module names have already been chosen for their uniqueness. It then is not necessary to invent another unique string key.


About your example above, …

  • The docs warn to use persistent references for dialog objects.

  • Ruby convention is that module & class constant identifiers are CamelCase, ex: MakeGroupHtml
    Other constants use underscore separators and are all uppercase. Ie … PREF_KEY

A bit more on why Apple UTIs are not so good for SketchUp keys.

We are creating lookup keys that are Strings. In Ruby strings are compared character by character (which can be slow.) So, it makes sense to have the unique parts at the beginning of the String.

Apple UTIs are the opposite, where the common parts of the key are at the beginning and the unique parts at the end.

So, think about the lookup loop that has to go through a list of keys for dialog preferences, attribute dictionaries or extension defaults. The sooner a string comparison can determine that the current key differs, the sooner it can move on to check the next key in the list. The reduction in the number of characters the search loop must compare will translate to faster lookups.

Thanks for the kick in the boo-boo. I need to begin to consistently use ruby conventions.

So, to try to come full circle, this is how I understand what you are saying:

module IDKProgramming
  module MakeGroupHTML
    extend self
    @@loaded = false unless defined?(@@loaded)
    @@dialog = nil 

    PREF_KEY ||= Module.nesting[0].name.gsub('::','_')
    PLUGIN_PATH ||= File.dirname(__FILE__)

    def show_html_dialog
      unless @@dialog
        @@dialog = UI::HtmlDialog.new(
          {
            :dialog_title => "Create Group",
            :preferences_key => PREF_KEY,
            :scrollable => true,
            :resizable => true,
            :width => 300,
            :height => 200,
            :style => UI::HtmlDialog::STYLE_DIALOG
          }
        )

        @@dialog.set_file(File.join(PLUGIN_PATH, 'html', 'make_group.html'))

      #dialog callbacks removed
      end

      @@dialog.show unless @@dialog.visible?
    end        

I will say that it is helpful to me to be able to open numerous instances of the dialog when I’m adjusting .css styling.

For completeness:

"WebCommonDialog_IDKProgramming_MakeGroupHTML": {
            "Height": 213,
            "Left": 206,
            "Top": 160,
            "Width": 310
        },
1 Like