Modern CheckBoxes

by Erick Engelke
September 17, 2024

I was recently asked how to make a modern iOS-styled toggle in EWB.

You can find many examples of this sort of thing on the Web done with CSS and a checkbox and a dash of colour when checked.

The result looks like this:

checkbox

I did not make a true EWB component, so you cannot use the IDE form designer with it.

But I did write the control and the sample application in less than 100 lines of Pascal.

I made it so you just have to define a containing THTMLLabel for each toggle and call AddModernCheckBox() to place the new control in that HTMLLabel.

procedure TForm1.Form1Show(Sender: TObject);
begin
   moderncheckbox1 := AddModernCheckBox( htmllabel1 );
   moderncheckbox2 := AddModernCheckBox( htmllabel2 );

   // set the second one to true
   moderncheckbox2.Checked := True;

   // set the JavaScript-styled event handler
   moderncheckbox1.OnChange := mcbchanged;
   moderncheckbox2.OnChange := mcbchanged;

end;

You can read or write the Checked property to set or return whether the switch is activated.

And you can call a JavaScript-styled Event Hander (which is not identical to typical EWB event handlers in format).

My sample handler shows the state of the control which was just changed. In this sample app I have two of them, so change either for a popup.

function TForm1.mcbchanged( ev : TEvent ):integer;
var
  mcb : TModernCheckBox;
begin
  mcb := TModernCheckBox( ev.Target );
  showmessage('new state of ' + mcb.id + ' : ' + BoolToStr( moderncheckbox1.Checked ) );
end;

You can move the control around the screen by changing the Left, Top properties of the THTMLLabel, or using its Layout properties.

Issues

There are numerous issues.

  1. Much of the code is spent creating the CSS for this toggle switch. That’s quite ugly.

  2. I didn’t spend a lot of time on this, it’s not a high priority to me, but it is cool and some people will find it useful.

  3. You cannot use AutoHeight or AutoSize or it won’t work due to some internals of EWB. The code sets the dimensions in the AddModernCheckbox() function.

  4. Many other typical EWB features are not present, including Dataset, etc.

Here’s the complete code, it just needs two THTMLLables, one for each TModernCheckBox created.

Here’s the complete Form.

unit moderntoggle;

interface

uses WebCore, webdom, WebUI, WebForms, WebCtrls, WebLabels, WebBtns;

type
  TOnChangedFn = function( ev : TEvent  ):integer of object;


  external TModernCheckBox = class( TDOMElement)
  public

     property id : string read write;
     property checked :boolean read write;
     property onchange : TOnChangedFn read write;
  end;

   TForm1 = class(TForm)
      HTMLLabel1: THTMLLabel;
      HTMLLabel2: THTMLLabel;
      procedure Form1Show(Sender: TObject);
   private
      { Private declarations }
      moderncheckbox1 : TModernCheckBox;
      moderncheckbox2 : TModernCheckBox;

      function mcbchanged( ev : TEvent ):integer;
   public
      { Public declarations }
   end;

var
   Form1: TForm1;

implementation

type

var
  moderncheckboxoffset : integer = 0;

function AddModernCheckbox( lab : THTMLlabel ) : TModernCheckBox;
var
   label : string;
begin
   // grab next label
   label := 'ModernCheckBox-'+IntToStr( moderncheckboxoffset );
   inc( moderncheckboxoffset );

   lab.Width := 70;
   lab.Height := 40;
   lab.Content :=
'<style>'+#13+#10+
' .switch{ opacity: 1; }'+#13+#10+
' .switch-btn{ width: 40px; height: 10px; background: #e5e5e5; position: relative; border-radius: 20px; box-shadow: inset 0 3px 10px rgba(0,0,0,.3); }'+#13+#10+
' .switch-btn:before{ content: ""; position: absolute; height: 19px; width: 19px; background: linear-gradient(#FFF, #f2f2f2); '+#13+#10+
'     left: 2px; top: 50%; transition: all 250ms ease-out; cursor: pointer; border-radius: 50%; '+#13+#10+
'     box-shadow: 0 8px 6px -4px rgba(0,0,0,.25); transform: translateY(-50%); }'+#13+#10+
' input[type=checkbox]:checked + .switch-btn { background: #47CB8F; } '+#13+#10+
' input[type=checkbox]:checked + .switch-btn:before { left: 19px; }'+#13+#10+
'</style>'+#13+#10+
'<label class="switch">'+#13+#10+
'     <input id="'+ label + '" style="opacity:0;" type="checkbox"/> '+#13+#10+
'     <div class="switch-btn"></div> '+#13+#10+
'</label>'+#13+#10;

   result := TModernCheckBOx(window.document.getElementById( label ));
end;

function TForm1.mcbchanged( ev : TEvent ):integer;
var
  mcb : TModernCheckBox;
begin
  mcb := TModernCheckBox( ev.Target );
  showmessage('new state of ' + mcb.id + ' : ' + BoolToStr( moderncheckbox1.Checked ) );
end;


procedure TForm1.Form1Show(Sender: TObject);
begin
   moderncheckbox1 := AddModernCheckBox( htmllabel1 );
   moderncheckbox2 := AddModernCheckBox( htmllabel2 );

   // set it true
   moderncheckbox2.Checked := True;

   moderncheckbox1.OnChange := mcbchanged;
   moderncheckbox2.OnChange := mcbchanged;

end;

end.

Erick