Additional API event: onSettingsChanged()

Hello @laurent,

Would it be possible to add an additional event joplin.workspace.onSettingsChange() which is triggered after the settings (options) view returns?

The reason for my request is the following...
I have a function which updates the HTML code of my plugin. updatePanelView() - which is based on your TOC example plugin. Within this function I read a lot of values from settings. What means everytime this function is called, a lot of time is wasted for reading these values. Example:

      // get style values from settings
      const enableDragAndDrop: boolean = await SETTINGS.value('enableDragAndDrop');
      const showCheckboxes: boolean = await SETTINGS.value('showTodoCheckboxes');
      const showCompletedTodos: boolean = await SETTINGS.globalValue('showCompletedTodos');
      const tabHeight: number = await SETTINGS.value('tabHeight');
      const minWidth: number = await SETTINGS.value('minTabWidth');
      const maxWidth: number = await SETTINGS.value('maxTabWidth');
      const font: string = await getSettingOrDefault('fontFamily', SettingDefaults.Font);
      const mainBg: string = await getSettingOrDefault('mainBackground', SettingDefaults.Background);
      const mainFg: string = await getSettingOrDefault('mainForeground', SettingDefaults.Foreground);
      const activeBg: string = await getSettingOrDefault('activeBackground', SettingDefaults.ActiveBackground);
      const activeFg: string = await getSettingOrDefault('activeForeground', SettingDefaults.ActiveForeground);
      const dividerColor: string = await getSettingOrDefault('dividerColor', SettingDefaults.DividerColor);

I know it would be possible to read these values once at startup of the plugin. But then the user has to restart the App everytime he changes one of these settings.

Also I do not know the exact performance of the settings API. But I think if a user has many plugins installed where each of it updates its webview everytime another note is selected might cause some performance issues.

What do you think?

Hmm, indeed that doesn't look very good to have so many ipc calls to read settings. Maybe we could add an onSettingsChanged event, but settings can be changed frequently in some cases (because it contains app state too) and that would mean many unnecessary reading of settings that haven't changed.

Perhaps adding a function to read multiple settings in one call would help? For example joplin.settings.values(names:string[]). And that would return key/value pairs for the provided names. Would that make sense?

Yes, of course, that would also work. I was just a little worried about the performance. However, I then still read out with each updatePanelView() the settings that may not have changed at all.

I was here more about reading the settings only when I expect a change. If the user had the settings view opened and returns to the normal view. Maybe somethink like joplin.workspace.onOptionsViewReturn().

Or how about defining an optional callout function directly at the settings definition, which is called when the value is changed? There I could then simply implement something like that:

await SETTINGS.registerSetting('enableDragAndDrop', {
	value: true,
	type: SettingItemType.Bool,
	section: 'note.tabs.settings',
	public: true,
	label: 'Enable drag & drop of tabs',
	description: 'If disabled, position of tabs can be change via commands or move buttons.',
	onChange: () => {
		// write settings value to global variable and update panel
		enableDragAndDrop = await SETTINGS.value('enableDragAndDrop');
		await updatePanelView();
	}
});

... I'm not sure if it would be correct syntax.

But these are just ideas. Your proposed variant would also be perfectly fine for me.

Edit: updated code example

Actually, I wonder how you handle this at the moment? When a user changes something in the config screen, and go back to the main screen, are you able to update the panel view? I guess it's not currently possible?

Otherwise indeed an event like you suggest might work, maybe onSettingChange would cover most cases.

That's the point... it is currently not possible to handle. Currently I am reading all settings within the updatePanelView() function. This function is triggered from the onNoteSelectionChange() event (and other locations...). So after changing anything in the settings, the user has to select at least another note to update the panel. But with this implementation the settings are also read when the customer simply selects another note.

I would like to separate these two functionalities, like

  • Read setting values and write them to local variables
  • Update the panel using the local variables

Because I think the most time the selected note changes but not the settings.

That makes sense. I'll have a look if there's some way to add this without compromising performances.