Extending preference input examples?

Hi, are there any examples anywhere of extending a preference input? I’m looking to:

  1. Convert/validate the text as it’s entered into the input field e.g. convert to uppercase, validate against a regular expression
  2. Set the value of the preference input using a helper dialog e.g. clicking the field (or icon next to the field) opens a dialog which will then be used to update the value of that preference instead

@jweb thank you for the discussion.

  1. Convert/validate the text as it’s entered into the input field e.g. convert to uppercase, validate against a regular expression.

You can take a look at PreferenceNumberInput as an example of an input which is validated (and inspire yourself from it’s implementation):

You can directly modify the input upon validation if your use-case requires it.

  1. Set the value of the preference input using a helper dialog e.g. clicking the field (or icon next to the field) opens a dialog which will then be used to update the value of that preference instead.

I do not see anything that prevents you from doing the following, you can implement a custom dialog which will use the PreferenceService to set the value inputted by the user (which will directly update the settings), and everything should be updated accordingly. We do something similar with the reset preference option as part of the ‘cog icon’ present for updated preferences. The only difference is your dialog will accept the id of the preference you wish to update, and use the service to actually perform the update.

I’ve been trying to add a new preference input type but haven’t been able to get it to work yet:

1.Add a ‘customInput’ property to a preference to flag it to use another input type

export const MyConfigSchema: PreferenceSchema = {
    'type': 'object',
    'properties': {
        'user.testPref': {
            'type': 'string',
            'customInput': 'toUpper'
        },
    }
}

2.Extend SinglePreferenceWrapper getInputType to use a different input based on the customInput flag

@injectable()
export class SinglePreferenceWrapper extends TheiaSinglePreferenceWrapper {

    getInputType = (preferenceDisplayNode: Preference.NodeWithValueInSingleScope): React.ReactNode => {
        const { type, items } = preferenceDisplayNode.preference.data;
        ...
        } if (type === 'string' && preferenceDisplayNode.preference.data.customInput === 'toUpper') {
            return <PreferenceToUpperInput
                preferenceDisplayNode={preferenceDisplayNode}
                setPreference={this.setPreference}
            />;
        ...
}

3.Try to ‘rebind’ the overridden SinglePreferenceWrapper via the PreferencesWidget in the frontend module of my extension

export default new ContainerModule((bind, unbind, isBound, rebind) => {
    rebind(PreferencesWidget)
        .toDynamicValue(({ container }) => createPreferencesWidgetContainer(container).get(PreferencesWidget))
        .inSingletonScope();
}

function createPreferencesWidgetContainer(parent: interfaces.Container): Container {
    const child = createTreeContainer(parent);
    child.bind(PreferenceTreeModel).toSelf();
    child.rebind(TreeModel).toService(PreferenceTreeModel);
    child.unbind(TreeWidget);
    child.bind(PreferencesTreeWidget).toSelf();
    child.rebind(TreeProps).toConstantValue({ ...defaultTreeProps, search: false });
    child.bind(PreferencesEditorWidget).toSelf();
    child.bind(PreferencesDecorator).toSelf();
    child.bind(PreferencesDecoratorService).toSelf();
    child.rebind(TreeDecoratorService).toService(PreferencesDecoratorService);

    // The overridden SinglePreferenceWrapper
    child.bind(SinglePreferenceWrapper).toSelf();
    child.bind(PreferencesSearchbarWidget).toSelf();
    child.bind(PreferencesScopeTabBar).toSelf();
    child.bind(SinglePreferenceDisplayFactory).toSelf();
    child.bind(PreferencesWidget).toSelf();

    return child;
}

This was based on theia/packages/preferences/src/browser/views/preference-widget-bindings. But when trying to compile it I get errors:

../node_modules/@theia/core/src/common/event.ts(333,20): error TS2790: The operand of a 'delete' operator must be optional.
../node_modules/@theia/core/src/common/event.ts(393,24): error TS2790: The operand of a 'delete' operator must be optional.
../node_modules/@theia/core/src/common/messaging/proxy-factory.ts(243,29): error TS2794: Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?

I’m not sure how to rebind the overridden SinglePreferenceWrapper in my frontend module? Or whether I should be taking a different approach to adding a new preference input field?

@jweb if you are still having issues with the implementation would you mind pushing your changes to some public repository (ex: github), it might make it easier for the community to help further.

Sure, I’ve pushed an example of what I’m trying to do in the ‘Custom-Preference-Input’ extension here: https://github.com/jdubya/Theia-Custom-Preference-Input. There are some notes in the frontend module.

The preferences that have been defined are active in settings when launching the IDE. But I’ve not been able to work out how to wire things together so that the PreferenceStringToUpperInput can be used by rebinding my overridden SinglePreferenceWrapper?