Ask Reuben

Color Picker

How can I implement a Color Picker? 

How can I customise a widget using GBC Customisation?

Those at the Cancun WWDC24 event may recall during Jean and Mario Customer Experience presentation, I pointed out that in their screen that allowed end-users to define a color used in their generic reports, currently their users have to type in a color, wouldn’t it be better to have them select a color from a GUI widget?  The reason I pointed that out was for a number of reasons but they included:

  • I knew a color picker was in our list of potential enhancements but had never got to the top because the list of requestors for it was very small
  • I was confident that demand for this functionality was under recorded and if I pointed it out then that we could capture more of that demand.
  • I knew it was possible to implement relatively simply using GBC Customization
  • Once implemented it is a good example of how with Universal Rendering, GUI functionality can be implemented once and be available across our front-ends.

The good news is that in the recent GBC 5.00.10 release, we shipped a Color Picker example in the customization area.  If you follow the instructions you can incorporate it into your own customization.

If you followed those instructions and added it to our customisation, a relatively simple program like the following can be used to demonstrate and test. If you click on the field that has STYLE=”colorpicker”, you get a widget that allows you to select a color.  Instead of displaying the RGB value, the widget is a circle in the selected color.  The screenshot shows the widget that appears and how the selected color is displayed …


BeforeAfter

MAIN
    DEFINE rec RECORD
        field1 STRING,
        field2 STRING,
        field3 STRING
    END RECORD

    DEFINE program_name STRING

    WHENEVER ANY ERROR STOP
    DEFER INTERRUPT
    DEFER QUIT
    OPTIONS FIELD ORDER FORM
    OPTIONS INPUT WRAP

    CONNECT TO ":memory:+driver='dbmsqt'"

    LET program_name = base.Application.getProgramName()

    #CALL ui.Interface.loadStyles(program_name)
    CALL ui.Dialog.setDefaultUnbuffered(TRUE)
    CLOSE WINDOW SCREEN

    OPEN WINDOW w WITH FORM program_name ATTRIBUTES(TEXT = program_name)
    INPUT BY NAME rec.* ATTRIBUTES(WITHOUT DEFAULTS = TRUE)
        BEFORE FIELD field2
            MESSAGE "BEFORE FIELD field2 triggered at ", CURRENT HOUR TO FRACTION(2), " value is :", rec.field2
        ON CHANGE field2
            ERROR "ON CHANGE field2 triggered at ", CURRENT HOUR TO FRACTION(2), " value is :", rec.field2
        ON ACTION dialogtouched INFIELD field2
            MESSAGE "ON ACTION dialogtouched INFIELD field2 triggered at ", CURRENT HOUR TO FRACTION(2), " value is :",
                FGL_DIALOG_GETBUFFER()
        AFTER FIELD field2
            MESSAGE "AFTER FIELD field2 triggered at ", CURRENT HOUR TO FRACTION(2), " value is :", rec.field2
        ON ACTION red
            LET rec.field2 = "#FF0000"
        ON ACTION green
            LET rec.field2 = "#00FF00"
        ON ACTION blue
            LET rec.field2 = "#0000FF"
        ON ACTION yellow
            LET rec.field2 = "#FFFF00"
        ON ACTION magenta
            LET rec.field2 = "#FF00FF"
        ON ACTION cyan
            LET rec.field2 = "#00FFFF"
        ON ACTION black
            LET rec.field2 = "#000000"
        ON ACTION white
            LET rec.field2 = "#FFFFFF"
    END INPUT
END MAIN

LAYOUT
GRID
{
Field 1      [f01         ]
Color Picker [f02         ]
Field 3      [f03         ]


}
END
END

ATTRIBUTES
EDIT f01 = formonly.field1;
#EDIT f02 = formonly.field2 ,STYLE="colorpicker";-- Shipped customization
EDIT f02 = formonly.field2, STYLE="colorpicker_custom"; -- Reubens customization
EDIT f03 = formonly.field3;


INSTRUCTIONS
SCREEN RECORD scr(field1, field2, field3);

Now one interesting point (and it is why I have added the ON ACTION dialogtouched, ON CHANGE, AFTER FIELD blocks) is to note when these events are triggered.  The widget overridden is the EDIT widget so you don’t get the characteristics of other GUI widgets such as RADIOGROUP, COMBOBOX where ON CHANGE is immediately triggered when the user makes a change.  You get the characteristics of an EDIT whereby ON CHANGE is triggered when the user exits the field and the value is different.  You can get an immediate response when a user makes a change in the widget by the use of ON ACTION dialogtouched, I have used the INFIELD clause so that the dialogtouched action is only active when the user is in that particular field, you don’t want it active in all fields.

To see how the widget is customised, have a look at the source in GBC_PROJECT_DIR/customization/colorpicker and read in conjunction with the documentation on Customizing your Application using Widgets. A key line is this one in js/ColorPickerWidget.js …

     * Register our custom widget 'colorpicker' as a replacement of the default Edit widget for all Edit having style='colorpicker'
     */
    cls.WidgetFactory.registerBuilder("Edit.colorpicker", cls.ColorPickerWidget);

This is what determines that any EDIT that has STYLE=”colorpicker” will use this customisation.

Now the neat thing about GBC Customization is that you are not limited to use that customisation.  If you can come up with your own color picker widget you can add that code into your GBC Customisation.  To illustrate this , I have included another Color Picker customisation.  Using the customisation source at the end of this article, you can build another Color Picker widget.  This makes use of input type=”color” and a GUI Color Picker widget that is included in most browsers.  See the w3schools article on input type=”color”.  and run the Try it yourself example on that page.  In the corresponding Mozilla documentation page you will note that his feature is supported on nearly all browsers, at the time of writing only Firefox for Android does not have full support.

Using the earlier example, you can run this customisation with a few changes.

In the .per, uncomment this line  …

 EDIT f02 = formonly.field2, STYLE="colorpicker_custom";

and comment out the previous line so that the STYLE is now colorpicker_custom.

Add to a .4st (and uncomment the line to load a .4st), the following Style and StyleAttribute…


<Style name="Edit.colorpicker_custom" >
    <StyleAttribute name="customWidget" value="color" />
</Style>

In ColorWidget.js, you will note that the registerBuilder line is

cls.WidgetFactory.registerBuilder("Edit[customWidget=color]", cls.ColorWidget);

This says that any EDIT widget that has the StyleAttribute “customWidget” set to the value “color” will use this customisation.

Running the modified example program with this Customisation, you should now get the following, again Before screenshot shows the color being selected and the After screenshot shows how it is rendered in the form.  Again the value behind the scenes is the RGB value. …

BeforeAfter

Which customisation you use is up to you.  The important things to note are …

  • you are not restricted to what we provide
  • using customisation, should work on all front-ends, you are not limited to what is in the Native UI Toolkit
  • when overriding a widget or container, pick the widget or container that is closest to what your widget or container is.
  • the registerBuilder line is what controls if the customisation is used or not.  Personally I prefer the use of customWidget StyleAttribute but you may prefer to use the technique that does not require an entry in a .4st
  • if there are other widgets you feel should be included in our package, make sure you let us know so that demand is captured.  I know one example at the moment is a widget to enter phone numbers.
  • with customisation, make sure you consider how the widget appears:
    • ReadOnly
    • In a Construct
    • In a Table / Tree
    • Dark Mode
  • consider what browser support there is for any Web technologies you are using.

Note: in the code below, remove the #! , this is the location and file name you should use.


#! config.json
{
  "compile": {
    "mode": "cdev",
    "customization": "customization/colorpicker_via_inputtype"
  },
  "customizationSuffix" : "colorpicker_via_inputtype",
  "themes": [{
      "name": "default",
      "title": "Default"
    }
  ]
}

#! js/ColorWidget.js

"use strict";

modulum('ColorWidget', ['EditWidget', 'WidgetFactory'],
  function(context, cls) {

    cls.ColorWidget = context.oo.Class(cls.EditWidget, function($super) {
      return {
        __name: "ColorWidget",

        /* your custom code */
        _inputType: 'color',

setType: function(valType) {
              this._inputElement.setAttribute('type', 'color');
        },

/* TODO figure out how to trip on change */

/* TODO, perhaps use list presets to do versions with TTY and/or HTML named colors see https://stackoverflow.com/questions/74910447/getting-color-input-name-instead-of-hex-datalist-of-colors */
      };
    });
    cls.WidgetFactory.registerBuilder("Edit[customWidget=color]", cls.ColorWidget);
  });

#! js/ColorWidget.tpl.html

<!--
 FOURJS_START_COPYRIGHT(D,2014)
 Property of Four Js*
 (c) Copyright Four Js 2014, 2024. All Rights Reserved.
 * Trademark of Four Js Development Tools Europe Ltd
   in the United States and elsewhere

 This file can be modified by licensees according to the
 product manual.
 FOURJS_END_COPYRIGHT
-->
<div class="mt-field gbc_dataContentPlaceholder">
  <input type="color" class="gbc-label-text-container" autocomplete="new-password"/>
</div>