Jump to content

lynkfs

Moderators
  • Content Count

    743
  • Joined

  • Last visited

  • Days Won

    146

Reputation Activity

  1. Thanks
    lynkfs got a reaction from Czar in console override   
    This mechanism can be used to redefine more built-in functions. Funny.
    The code below redefines 'ShowMessage' to redirect its contents to a Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'ShowMessage : ' + #10;   browserapi.window.alert := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
    or combine the two and redefine 'ShowMessage' to redirect its contents to the 'Writeln' function, which in its turn is redirected to the Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10; //WriteLn redirects to Memo   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end; //ShowMessage redirects to WriteLn   browserapi.window.alert := procedure(text:variant)   begin     writeln(text);   end;   writeln('aaa');   browserapi.console.log('bbb');   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
  2. Thanks
    lynkfs got a reaction from IgorSavkic in console override   
    This mechanism can be used to redefine more built-in functions. Funny.
    The code below redefines 'ShowMessage' to redirect its contents to a Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'ShowMessage : ' + #10;   browserapi.window.alert := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
    or combine the two and redefine 'ShowMessage' to redirect its contents to the 'Writeln' function, which in its turn is redirected to the Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10; //WriteLn redirects to Memo   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end; //ShowMessage redirects to WriteLn   browserapi.window.alert := procedure(text:variant)   begin     writeln(text);   end;   writeln('aaa');   browserapi.console.log('bbb');   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
  3. Like
    lynkfs got a reaction from DavidRM in console override   
    redirecting 'console.log' or 'writeln' output proves to be very simple :
    have a form with a Memo component :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10;   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   writeln('aaa');   browserapi.console.log('bbb'); end; all subsequent calls to writeln and/or console.log are now redirected to the Memo component.
     
  4. Like
    lynkfs got a reaction from IElite in console override   
    This mechanism can be used to redefine more built-in functions. Funny.
    The code below redefines 'ShowMessage' to redirect its contents to a Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'ShowMessage : ' + #10;   browserapi.window.alert := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
    or combine the two and redefine 'ShowMessage' to redirect its contents to the 'Writeln' function, which in its turn is redirected to the Memo :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10; //WriteLn redirects to Memo   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end; //ShowMessage redirects to WriteLn   browserapi.window.alert := procedure(text:variant)   begin     writeln(text);   end;   writeln('aaa');   browserapi.console.log('bbb');   ShowMessage('ccc');   browserapi.window.alert('ddd'); end;  
  5. Thanks
    lynkfs got a reaction from jarto in console override   
    redirecting 'console.log' or 'writeln' output proves to be very simple :
    have a form with a Memo component :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10;   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   writeln('aaa');   browserapi.console.log('bbb'); end; all subsequent calls to writeln and/or console.log are now redirected to the Memo component.
     
  6. Thanks
    lynkfs got a reaction from Czar in console override   
    redirecting 'console.log' or 'writeln' output proves to be very simple :
    have a form with a Memo component :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10;   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   writeln('aaa');   browserapi.console.log('bbb'); end; all subsequent calls to writeln and/or console.log are now redirected to the Memo component.
     
  7. Like
    lynkfs got a reaction from IElite in console override   
    redirecting 'console.log' or 'writeln' output proves to be very simple :
    have a form with a Memo component :
    procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'}   W3Memo1.Text := 'Console.log : ' + #10;   browserapi.console.log := procedure(text:variant)   begin     W3Memo1.Text := W3Memo1.Text + #10 + text;   end;   writeln('aaa');   browserapi.console.log('bbb'); end; all subsequent calls to writeln and/or console.log are now redirected to the Memo component.
     
  8. Like
    lynkfs reacted to jarto in Resize method called 3 times ?   
    I dug deeper into the Layout code and it seems there's a bug in how bottom alignment is done. The code sets the bottom coordinate, which leads to the code to calculate top based on the current component height. However, the current component height at that point is still the default height right after create, so the top coordinate is calculated wrong. Height is set after that, but it does not adjust top accordingly.
    A second resize helps as at that point the bottom component is already of the right height.
  9. Like
    lynkfs got a reaction from IElite in Resize method called 3 times ?   
    if you do this then it works fine
    procedure TForm2.InitializeObject; begin inherited; {$I 'Form2:impl'} .... Handle.ReadyExecute( procedure () begin resize; end); end;  
  10. Like
    lynkfs got a reaction from jarto in component creation   
    Thanks @jarto
    So a good boilerplate for visual components would be something like this
    function TSwitch.MakeElementTagObj: THandle;          //1 begin   result := w3_createHtmlElement('div');              //or any other html element end; procedure TSwitch.StyleTagObject;                     //2 begin   inherited;   {$IFDEF THEME_AUTOSTYLE}   ThemeBorder := btDecorativeBorder; //or whatever is appropriate   {$ENDIF}   ....                                          //optionally add styles end; procedure TSwitch.InitializeObject;               //3 begin   inherited; ... //create sub-components end; procedure TSwitch.ObjectReady;                        //4 begin   inherited;   .... //optionally set some initial properties end; procedure TSwitch.Resize; begin   inherited;   if not (csDestroying in ComponentState) then   begin     // Make sure we have ready state     if (csReady in ComponentState) then     begin ... // layout the controls     end else begin self.Invalidate(); end;   end; end;
     
     
  11. Like
    lynkfs got a reaction from jorn in component creation   
    Question : is this article still the preferred way to develop visual components ?
    I developed some Material Design components some years ago (here and here) but they need to be ported to the latest rtl as they don't compile anymore.
    Hence this question.
    As an example I ported a simple component (switch) using the recipe in Jon's article and it works ok, see below
    Just a bit elaborate.
    unit Switch; /****************************************************************************** LynkFS / 20 - 11 - 2018 The Switch component consists of - a W3Panel as the main container for the Switch - a W3Panel for the Switch - a W3Panel for the sliderline - a W3Panel as the highlighter The component is by default styled according to the css theme of the project. Styling can be modified by overriding the following css classes : The Switch CSS classes in BEM notation are .TSwitch .TSwitch__Panel .TSwitch__Panel__Line .TSwitch__Panel__Line--StateOn .TSwitch__Panel__Line--StateOff .TSwitch__Panel__Knob .TSwitch__Panel__Knob--StateOn .TSwitch__Panel__Knob--StateOff .TSwitch__Panel__HighLight .TSwitch__Panel__HighLight--Active .TSwitch__Panel__HighLight--InActive */ interface uses   W3C.DOM,   System.Types,   System.Time,   System.Colors,   System.Events,   {$IFDEF THEME_AUTOSTYLE}   SmartCL.Theme,   {$ENDIF}   SmartCL.System,   SmartCL.Touch,   SmartCL.Effects,   SmartCL.borders,   SmartCL.Components,   SmartCL.Controls.Panel,   SmartCl.Css.StyleSheet; type   TSwitch = class(TW3CustomControl)   private     Panel0   : TW3Panel;       //background     Panel1   : TW3Panel;       //sliderline     Panel2   : TW3Panel;       //indicator     Panel3   : TW3Panel;       //Switch knob     procedure SetStateOn(StateOn: Boolean);     function  GetStateOn: Boolean;     IsStateOn: Boolean := false;   protected        // see https://jonlennartaasenden.wordpress.com/2018/05/07/     function  MakeElementTagObj: THandle; override;       //1     procedure StyleTagObject; override;                   //2     procedure ObjectReady; override;                      //3     procedure InitializeObject; override;                 //4     procedure Resize; override;                           //5     procedure FinalizeObject; override;     Procedure ShowSwitch; //included in 5     Procedure InsertCSS; //included in 2   public     property StateOn: Boolean read getStateOn write setStateOn;   end; implementation { TSwitch } function TSwitch.MakeElementTagObj: THandle;          //1 begin   result := w3_createHtmlElement('div');              //div = default, so could be omitted end; procedure TSwitch.StyleTagObject;                     //2 begin   inherited;   {$IFDEF THEME_AUTOSTYLE}   ThemeBorder := btDecorativeBorder;   {$ENDIF}   InsertCSS; //insert all styling in stylesheet end; procedure TSwitch.ObjectReady;                        //3 begin   inherited;   //set some initial properties   Panel0.SetBounds(0,0,self.width,self.height);       //background   Panel1.SetBounds(round(self.height/2),              //slider line                    round(self.height/2)-round(self.height*0.2),                    self.Width-self.height,                    round(self.height*0.4));   Panel2.SetBounds(0,0,self.height,self.height);      //highlight circle   var x : integer := round(self.height/5);            //switch knob   Panel3.SetBounds(x,x,Panel2.width-2*x,Panel2.height-2*x);   TW3Dispatch.WaitFor([Panel0, Panel1, Panel2, Panel3], 5,     procedure (Success: boolean)     begin       if Success then       begin         // set some initial properties         If IsStateOn then begin           Panel2.Left := self.Width - Panel2.Width;           Panel3.Left := self.Width - Panel2.Width + round((Panel2.Width-Panel3.Width)/2);         end;         // Do an immediate resize         Resize();       end;     end); end; procedure TSwitch.InitializeObject;              //4 begin   inherited; //creating sub-components   Self.TagStyle.Add('TSwitch');       //superfluous, will be auto generated   Panel0 := TW3Panel.Create(self);   Panel0.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel0.TagStyle.Add('TSwitch__Panel');   Panel1 := TW3Panel.Create(Panel0);   Panel1.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   Panel2 := TW3Panel.Create(Panel0);   Panel2.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');   Panel3 := TW3Panel.Create(Panel0);   Panel3.TagStyle.Clear;              //dont inherit css from TW3Panel   self.SimulateMouseEvents := True;   //touch enable   self.handle.addEventListener("click", procedure()   begin     IsStateOn := not IsStateOn;     If IsStateOn then begin       Panel3.fxMoveBy(self.width - self.height, 0, 0.5);       Panel2.fxMoveBy(self.width - self.height, 0, 0.5);     end else begin       Panel3.fxMoveBy(-self.width + self.height, 0, 0.5);       Panel2.fxMoveBy(-self.width + self.height, 0, 0.5);     end;     Panel2.TagStyle.Clear;            //dont inherit css from TW3Panel     Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--Active');     TW3Dispatch.Execute (Procedure()     begin       Panel2.TagStyle.Clear;          //dont inherit css from TW3Panel       Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');     end, 80);     ReSize;   end); end; procedure TSwitch.Resize; begin   inherited;   if not (csDestroying in ComponentState) then   begin     // Make sure we have ready state     if (csReady in ComponentState) then     begin       // Check that child elements are all assigned       // and that they have their csReady flag set in       // ComponentState. This can be taxing. A more lightweight       // version is TW3Dispatch.Assigned() that doesnt check       // the ready state (see class declaration for more info)       if TW3Dispatch.AssignedAndReady([Panel0, Panel1, Panel2, Panel3]) then       begin         // Finally: layout the controls.         ShowSwitch;       end;     end;   end; end; Procedure TSwitch.ShowSwitch; begin //   Panel1.TagStyle.Clear;   Panel3.TagStyle.Clear;   If IsStateOn then begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOn');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOn');   end else begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOff');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   end; // end; procedure TSwitch.InsertCSS; var   cssProperties : string; begin   //make stylesheet   var styleSheet : TW3StyleSheet := TW3StyleSheet.Create;   styleSheet.Add(".TSwitch", '');   styleSheet.Add(".TSwitch__Panel", '');   cssProperties := 'cursor: pointer; border: 0px; border-radius: 6px;';   styleSheet.Add(".TSwitch__Panel__Line", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__Line--StateOn", cssProperties);   cssProperties := 'background-color: rgba(0,0,0,.26);';   styleSheet.Add(".TSwitch__Panel__Line--StateOff", cssProperties);   cssProperties := 'cursor: pointer; border: 0px; border-radius: 50%;';   styleSheet.Add(".TSwitch__Panel__HighLight", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__HighLight--Active", cssProperties);   cssProperties := 'background-color: transparent;';   styleSheet.Add(".TSwitch__Panel__HighLight--InActive", cssProperties);   cssProperties := 'cursor: pointer; border-radius: 50%; box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);';   styleSheet.Add(".TSwitch__Panel__Knob", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.90);';   styleSheet.Add(".TSwitch__Panel__Knob--StateOn", cssProperties);   cssProperties := 'background-color: white;';   styleSheet.Add(".TSwitch__Panel__Knob--StateOff", cssProperties); /* Alternatively add this to a custom stylesheet : .TSwitch { } .TSwitch__Panel { } .TSwitch__Panel__Line {     cursor: pointer;     border: 0px;   border-radius: 6px; } .TSwitch__Panel__Line--StateOn {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__Line--StateOff {   background-color: rgba(0,0,0,.26); } .TSwitch__Panel__HighLight {     cursor: pointer;     border: 0px;   border-radius: 12px; } .TSwitch__Panel__HighLight--Active {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__HighLight--InActive {   background-color: transparent; } .TSwitch__Panel__Knob {     cursor: pointer;      border-radius: 10px;   box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); } .TSwitch__Panel__Knob--StateOn {   background-color: rgba(164,114,234,.90); } .TSwitch__Panel__Knob--StateOff {   background-color: white; } */ end; procedure TSwitch.FinalizeObject; begin   Panel0.Free;   Panel0 := nil;   inherited; end; procedure TSwitch.SetStateOn(StateOn: Boolean); begin   IsStateOn := StateOn; end; function  TSwitch.GetStateOn: Boolean; begin   Result := IsStateOn; end; end. usage
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   Switch1 := TSwitch.Create(self); //  Switch1.StateOn := true;    //default false   Switch1.SetBounds(50,50,60,35); end;  
     
  12. Like
    lynkfs reacted to jarto in component creation   
    @lynkfs
    You don't need to check csReady in Resize.
    And in resize, do your: if MyChildComponent<>nil then MyChildComponent.SetBounds(....)
  13. Like
    lynkfs reacted to jarto in component creation   
    Let me start by commenting on Jon's blog post. For the most part it's accurate, but he makes a small mistake with the "common mistake":
    ObjectReady() calls ReadySync() which actually does check if the child elements are ready. So, when you override ObjectReady(), the child controls you created in InitializeObject should also be ready. And when ObjectReady() happens, it also triggers the first Resize for the component.
    Let me comment on these three lines:
    Users don't need to write as complex checking routines as ReadySync() is. Let the RTL take care of that. It does it very well. However, do always check your controls in Resize(). Minimum is to check that they are not nil. And if you want to be extra sure, check the ready state too.
    And by far the best way to do this all is pretty simple:
    In your Resize() procedure, always check if your controls are ready If they are not, call Self.Invalidate(). Do not call Resize() directly or via TW3Dispatch as Jon suggests. Think about Invalidate() as a request for a new Resize(). It calls an internal procedure in the RTL called ResizeWhenReady() and it does exactly what it says: It will call Resize() with a short delay and it will check that any new child components you added after the previous resize are also ready. The delay helps to minimize the number of Resize()-calls and it simply makes sense to not let a Resize() happen before your child components are ready.
    I think Jon is explaining how the RTL did work before I got involved. It makes sense to rely on ObjectReady() when you have a simple, static control but when you have a control like TW3ListBox, it does not work at all as the TW3ListBox dynamically creates new child components while you use the control.
  14. Thanks
    lynkfs got a reaction from jarto in component creation   
    I did notice that too. Must be a (unwanted) rtl-warning generated by using TW3StyleSheet.Add(...)
    @jarto  could you have a look at that too ?
    edited :
    line 884 in SmartCL.Css.Stylesheet should be
          xSheet.insertRule(RuleName + ' {' + RuleValues + '}',0);  
  15. Like
    lynkfs got a reaction from IElite in component creation   
    Question : is this article still the preferred way to develop visual components ?
    I developed some Material Design components some years ago (here and here) but they need to be ported to the latest rtl as they don't compile anymore.
    Hence this question.
    As an example I ported a simple component (switch) using the recipe in Jon's article and it works ok, see below
    Just a bit elaborate.
    unit Switch; /****************************************************************************** LynkFS / 20 - 11 - 2018 The Switch component consists of - a W3Panel as the main container for the Switch - a W3Panel for the Switch - a W3Panel for the sliderline - a W3Panel as the highlighter The component is by default styled according to the css theme of the project. Styling can be modified by overriding the following css classes : The Switch CSS classes in BEM notation are .TSwitch .TSwitch__Panel .TSwitch__Panel__Line .TSwitch__Panel__Line--StateOn .TSwitch__Panel__Line--StateOff .TSwitch__Panel__Knob .TSwitch__Panel__Knob--StateOn .TSwitch__Panel__Knob--StateOff .TSwitch__Panel__HighLight .TSwitch__Panel__HighLight--Active .TSwitch__Panel__HighLight--InActive */ interface uses   W3C.DOM,   System.Types,   System.Time,   System.Colors,   System.Events,   {$IFDEF THEME_AUTOSTYLE}   SmartCL.Theme,   {$ENDIF}   SmartCL.System,   SmartCL.Touch,   SmartCL.Effects,   SmartCL.borders,   SmartCL.Components,   SmartCL.Controls.Panel,   SmartCl.Css.StyleSheet; type   TSwitch = class(TW3CustomControl)   private     Panel0   : TW3Panel;       //background     Panel1   : TW3Panel;       //sliderline     Panel2   : TW3Panel;       //indicator     Panel3   : TW3Panel;       //Switch knob     procedure SetStateOn(StateOn: Boolean);     function  GetStateOn: Boolean;     IsStateOn: Boolean := false;   protected        // see https://jonlennartaasenden.wordpress.com/2018/05/07/     function  MakeElementTagObj: THandle; override;       //1     procedure StyleTagObject; override;                   //2     procedure ObjectReady; override;                      //3     procedure InitializeObject; override;                 //4     procedure Resize; override;                           //5     procedure FinalizeObject; override;     Procedure ShowSwitch; //included in 5     Procedure InsertCSS; //included in 2   public     property StateOn: Boolean read getStateOn write setStateOn;   end; implementation { TSwitch } function TSwitch.MakeElementTagObj: THandle;          //1 begin   result := w3_createHtmlElement('div');              //div = default, so could be omitted end; procedure TSwitch.StyleTagObject;                     //2 begin   inherited;   {$IFDEF THEME_AUTOSTYLE}   ThemeBorder := btDecorativeBorder;   {$ENDIF}   InsertCSS; //insert all styling in stylesheet end; procedure TSwitch.ObjectReady;                        //3 begin   inherited;   //set some initial properties   Panel0.SetBounds(0,0,self.width,self.height);       //background   Panel1.SetBounds(round(self.height/2),              //slider line                    round(self.height/2)-round(self.height*0.2),                    self.Width-self.height,                    round(self.height*0.4));   Panel2.SetBounds(0,0,self.height,self.height);      //highlight circle   var x : integer := round(self.height/5);            //switch knob   Panel3.SetBounds(x,x,Panel2.width-2*x,Panel2.height-2*x);   TW3Dispatch.WaitFor([Panel0, Panel1, Panel2, Panel3], 5,     procedure (Success: boolean)     begin       if Success then       begin         // set some initial properties         If IsStateOn then begin           Panel2.Left := self.Width - Panel2.Width;           Panel3.Left := self.Width - Panel2.Width + round((Panel2.Width-Panel3.Width)/2);         end;         // Do an immediate resize         Resize();       end;     end); end; procedure TSwitch.InitializeObject;              //4 begin   inherited; //creating sub-components   Self.TagStyle.Add('TSwitch');       //superfluous, will be auto generated   Panel0 := TW3Panel.Create(self);   Panel0.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel0.TagStyle.Add('TSwitch__Panel');   Panel1 := TW3Panel.Create(Panel0);   Panel1.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   Panel2 := TW3Panel.Create(Panel0);   Panel2.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');   Panel3 := TW3Panel.Create(Panel0);   Panel3.TagStyle.Clear;              //dont inherit css from TW3Panel   self.SimulateMouseEvents := True;   //touch enable   self.handle.addEventListener("click", procedure()   begin     IsStateOn := not IsStateOn;     If IsStateOn then begin       Panel3.fxMoveBy(self.width - self.height, 0, 0.5);       Panel2.fxMoveBy(self.width - self.height, 0, 0.5);     end else begin       Panel3.fxMoveBy(-self.width + self.height, 0, 0.5);       Panel2.fxMoveBy(-self.width + self.height, 0, 0.5);     end;     Panel2.TagStyle.Clear;            //dont inherit css from TW3Panel     Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--Active');     TW3Dispatch.Execute (Procedure()     begin       Panel2.TagStyle.Clear;          //dont inherit css from TW3Panel       Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');     end, 80);     ReSize;   end); end; procedure TSwitch.Resize; begin   inherited;   if not (csDestroying in ComponentState) then   begin     // Make sure we have ready state     if (csReady in ComponentState) then     begin       // Check that child elements are all assigned       // and that they have their csReady flag set in       // ComponentState. This can be taxing. A more lightweight       // version is TW3Dispatch.Assigned() that doesnt check       // the ready state (see class declaration for more info)       if TW3Dispatch.AssignedAndReady([Panel0, Panel1, Panel2, Panel3]) then       begin         // Finally: layout the controls.         ShowSwitch;       end;     end;   end; end; Procedure TSwitch.ShowSwitch; begin //   Panel1.TagStyle.Clear;   Panel3.TagStyle.Clear;   If IsStateOn then begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOn');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOn');   end else begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOff');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   end; // end; procedure TSwitch.InsertCSS; var   cssProperties : string; begin   //make stylesheet   var styleSheet : TW3StyleSheet := TW3StyleSheet.Create;   styleSheet.Add(".TSwitch", '');   styleSheet.Add(".TSwitch__Panel", '');   cssProperties := 'cursor: pointer; border: 0px; border-radius: 6px;';   styleSheet.Add(".TSwitch__Panel__Line", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__Line--StateOn", cssProperties);   cssProperties := 'background-color: rgba(0,0,0,.26);';   styleSheet.Add(".TSwitch__Panel__Line--StateOff", cssProperties);   cssProperties := 'cursor: pointer; border: 0px; border-radius: 50%;';   styleSheet.Add(".TSwitch__Panel__HighLight", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__HighLight--Active", cssProperties);   cssProperties := 'background-color: transparent;';   styleSheet.Add(".TSwitch__Panel__HighLight--InActive", cssProperties);   cssProperties := 'cursor: pointer; border-radius: 50%; box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);';   styleSheet.Add(".TSwitch__Panel__Knob", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.90);';   styleSheet.Add(".TSwitch__Panel__Knob--StateOn", cssProperties);   cssProperties := 'background-color: white;';   styleSheet.Add(".TSwitch__Panel__Knob--StateOff", cssProperties); /* Alternatively add this to a custom stylesheet : .TSwitch { } .TSwitch__Panel { } .TSwitch__Panel__Line {     cursor: pointer;     border: 0px;   border-radius: 6px; } .TSwitch__Panel__Line--StateOn {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__Line--StateOff {   background-color: rgba(0,0,0,.26); } .TSwitch__Panel__HighLight {     cursor: pointer;     border: 0px;   border-radius: 12px; } .TSwitch__Panel__HighLight--Active {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__HighLight--InActive {   background-color: transparent; } .TSwitch__Panel__Knob {     cursor: pointer;      border-radius: 10px;   box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); } .TSwitch__Panel__Knob--StateOn {   background-color: rgba(164,114,234,.90); } .TSwitch__Panel__Knob--StateOff {   background-color: white; } */ end; procedure TSwitch.FinalizeObject; begin   Panel0.Free;   Panel0 := nil;   inherited; end; procedure TSwitch.SetStateOn(StateOn: Boolean); begin   IsStateOn := StateOn; end; function  TSwitch.GetStateOn: Boolean; begin   Result := IsStateOn; end; end. usage
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   Switch1 := TSwitch.Create(self); //  Switch1.StateOn := true;    //default false   Switch1.SetBounds(50,50,60,35); end;  
     
  16. Like
    lynkfs got a reaction from IgorSavkic in component creation   
    Question : is this article still the preferred way to develop visual components ?
    I developed some Material Design components some years ago (here and here) but they need to be ported to the latest rtl as they don't compile anymore.
    Hence this question.
    As an example I ported a simple component (switch) using the recipe in Jon's article and it works ok, see below
    Just a bit elaborate.
    unit Switch; /****************************************************************************** LynkFS / 20 - 11 - 2018 The Switch component consists of - a W3Panel as the main container for the Switch - a W3Panel for the Switch - a W3Panel for the sliderline - a W3Panel as the highlighter The component is by default styled according to the css theme of the project. Styling can be modified by overriding the following css classes : The Switch CSS classes in BEM notation are .TSwitch .TSwitch__Panel .TSwitch__Panel__Line .TSwitch__Panel__Line--StateOn .TSwitch__Panel__Line--StateOff .TSwitch__Panel__Knob .TSwitch__Panel__Knob--StateOn .TSwitch__Panel__Knob--StateOff .TSwitch__Panel__HighLight .TSwitch__Panel__HighLight--Active .TSwitch__Panel__HighLight--InActive */ interface uses   W3C.DOM,   System.Types,   System.Time,   System.Colors,   System.Events,   {$IFDEF THEME_AUTOSTYLE}   SmartCL.Theme,   {$ENDIF}   SmartCL.System,   SmartCL.Touch,   SmartCL.Effects,   SmartCL.borders,   SmartCL.Components,   SmartCL.Controls.Panel,   SmartCl.Css.StyleSheet; type   TSwitch = class(TW3CustomControl)   private     Panel0   : TW3Panel;       //background     Panel1   : TW3Panel;       //sliderline     Panel2   : TW3Panel;       //indicator     Panel3   : TW3Panel;       //Switch knob     procedure SetStateOn(StateOn: Boolean);     function  GetStateOn: Boolean;     IsStateOn: Boolean := false;   protected        // see https://jonlennartaasenden.wordpress.com/2018/05/07/     function  MakeElementTagObj: THandle; override;       //1     procedure StyleTagObject; override;                   //2     procedure ObjectReady; override;                      //3     procedure InitializeObject; override;                 //4     procedure Resize; override;                           //5     procedure FinalizeObject; override;     Procedure ShowSwitch; //included in 5     Procedure InsertCSS; //included in 2   public     property StateOn: Boolean read getStateOn write setStateOn;   end; implementation { TSwitch } function TSwitch.MakeElementTagObj: THandle;          //1 begin   result := w3_createHtmlElement('div');              //div = default, so could be omitted end; procedure TSwitch.StyleTagObject;                     //2 begin   inherited;   {$IFDEF THEME_AUTOSTYLE}   ThemeBorder := btDecorativeBorder;   {$ENDIF}   InsertCSS; //insert all styling in stylesheet end; procedure TSwitch.ObjectReady;                        //3 begin   inherited;   //set some initial properties   Panel0.SetBounds(0,0,self.width,self.height);       //background   Panel1.SetBounds(round(self.height/2),              //slider line                    round(self.height/2)-round(self.height*0.2),                    self.Width-self.height,                    round(self.height*0.4));   Panel2.SetBounds(0,0,self.height,self.height);      //highlight circle   var x : integer := round(self.height/5);            //switch knob   Panel3.SetBounds(x,x,Panel2.width-2*x,Panel2.height-2*x);   TW3Dispatch.WaitFor([Panel0, Panel1, Panel2, Panel3], 5,     procedure (Success: boolean)     begin       if Success then       begin         // set some initial properties         If IsStateOn then begin           Panel2.Left := self.Width - Panel2.Width;           Panel3.Left := self.Width - Panel2.Width + round((Panel2.Width-Panel3.Width)/2);         end;         // Do an immediate resize         Resize();       end;     end); end; procedure TSwitch.InitializeObject;              //4 begin   inherited; //creating sub-components   Self.TagStyle.Add('TSwitch');       //superfluous, will be auto generated   Panel0 := TW3Panel.Create(self);   Panel0.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel0.TagStyle.Add('TSwitch__Panel');   Panel1 := TW3Panel.Create(Panel0);   Panel1.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   Panel2 := TW3Panel.Create(Panel0);   Panel2.TagStyle.Clear;              //dont inherit css from TW3Panel   Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');   Panel3 := TW3Panel.Create(Panel0);   Panel3.TagStyle.Clear;              //dont inherit css from TW3Panel   self.SimulateMouseEvents := True;   //touch enable   self.handle.addEventListener("click", procedure()   begin     IsStateOn := not IsStateOn;     If IsStateOn then begin       Panel3.fxMoveBy(self.width - self.height, 0, 0.5);       Panel2.fxMoveBy(self.width - self.height, 0, 0.5);     end else begin       Panel3.fxMoveBy(-self.width + self.height, 0, 0.5);       Panel2.fxMoveBy(-self.width + self.height, 0, 0.5);     end;     Panel2.TagStyle.Clear;            //dont inherit css from TW3Panel     Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--Active');     TW3Dispatch.Execute (Procedure()     begin       Panel2.TagStyle.Clear;          //dont inherit css from TW3Panel       Panel2.TagStyle.Add('TSwitch__Panel__HighLight TSwitch__Panel__HighLight--InActive');     end, 80);     ReSize;   end); end; procedure TSwitch.Resize; begin   inherited;   if not (csDestroying in ComponentState) then   begin     // Make sure we have ready state     if (csReady in ComponentState) then     begin       // Check that child elements are all assigned       // and that they have their csReady flag set in       // ComponentState. This can be taxing. A more lightweight       // version is TW3Dispatch.Assigned() that doesnt check       // the ready state (see class declaration for more info)       if TW3Dispatch.AssignedAndReady([Panel0, Panel1, Panel2, Panel3]) then       begin         // Finally: layout the controls.         ShowSwitch;       end;     end;   end; end; Procedure TSwitch.ShowSwitch; begin //   Panel1.TagStyle.Clear;   Panel3.TagStyle.Clear;   If IsStateOn then begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOn');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOn');   end else begin     Panel3.TagStyle.Add('TSwitch__Panel__Knob TSwitch__Panel__Knob--StateOff');     Panel1.TagStyle.Add('TSwitch__Panel__Line TSwitch__Panel__Line--StateOff');   end; // end; procedure TSwitch.InsertCSS; var   cssProperties : string; begin   //make stylesheet   var styleSheet : TW3StyleSheet := TW3StyleSheet.Create;   styleSheet.Add(".TSwitch", '');   styleSheet.Add(".TSwitch__Panel", '');   cssProperties := 'cursor: pointer; border: 0px; border-radius: 6px;';   styleSheet.Add(".TSwitch__Panel__Line", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__Line--StateOn", cssProperties);   cssProperties := 'background-color: rgba(0,0,0,.26);';   styleSheet.Add(".TSwitch__Panel__Line--StateOff", cssProperties);   cssProperties := 'cursor: pointer; border: 0px; border-radius: 50%;';   styleSheet.Add(".TSwitch__Panel__HighLight", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.50);';   styleSheet.Add(".TSwitch__Panel__HighLight--Active", cssProperties);   cssProperties := 'background-color: transparent;';   styleSheet.Add(".TSwitch__Panel__HighLight--InActive", cssProperties);   cssProperties := 'cursor: pointer; border-radius: 50%; box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);';   styleSheet.Add(".TSwitch__Panel__Knob", cssProperties);   cssProperties := 'background-color: rgba(164,114,234,.90);';   styleSheet.Add(".TSwitch__Panel__Knob--StateOn", cssProperties);   cssProperties := 'background-color: white;';   styleSheet.Add(".TSwitch__Panel__Knob--StateOff", cssProperties); /* Alternatively add this to a custom stylesheet : .TSwitch { } .TSwitch__Panel { } .TSwitch__Panel__Line {     cursor: pointer;     border: 0px;   border-radius: 6px; } .TSwitch__Panel__Line--StateOn {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__Line--StateOff {   background-color: rgba(0,0,0,.26); } .TSwitch__Panel__HighLight {     cursor: pointer;     border: 0px;   border-radius: 12px; } .TSwitch__Panel__HighLight--Active {   background-color: rgba(164,114,234,.50); } .TSwitch__Panel__HighLight--InActive {   background-color: transparent; } .TSwitch__Panel__Knob {     cursor: pointer;      border-radius: 10px;   box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); } .TSwitch__Panel__Knob--StateOn {   background-color: rgba(164,114,234,.90); } .TSwitch__Panel__Knob--StateOff {   background-color: white; } */ end; procedure TSwitch.FinalizeObject; begin   Panel0.Free;   Panel0 := nil;   inherited; end; procedure TSwitch.SetStateOn(StateOn: Boolean); begin   IsStateOn := StateOn; end; function  TSwitch.GetStateOn: Boolean; begin   Result := IsStateOn; end; end. usage
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   Switch1 := TSwitch.Create(self); //  Switch1.StateOn := true;    //default false   Switch1.SetBounds(50,50,60,35); end;  
     
  17. Like
    lynkfs reacted to jarto in DayOf reporting false value   
    I dug around and found out that I actually can fix those "buggy" functions in the compiler without having to upgrade to a newer version of DWScript. I only have IncMonth to fix any more.
    The root cause of the problem is the way javascript dates work. When you build a date from day, month and year, the result depends on your time zone. I guess it builds the date as UTC and then views it from your location. So, while it's 01.12.2018 at 00:00:00 in London, it's still 30.11.2018 in USA. And when you call DayOf on that, you get 30 instead of 1. This affects about 10 other functions as well, including FormatDateTime and DateTimeToStr and TimeToStr.
    I'll make sure to test this all properly and then release an update. Once it's done, I'm super happy to get rid of these problems.
  18. Thanks
    lynkfs reacted to jarto in Encode Date not working inside control unit   
    Ok, I found a bug in System.DateUtils.DecodeDate. It decodes 43435 as 31.11.2018 while it should be 1.12.2018.
    Inc(QTX,Higher); M := 1; repeat <----- change this to: While True do begin Higher := CNT_DaysInMonthData[IsLeapYear(QTX)][M]; if Lower < Higher then Break; Dec(Lower, Higher); Inc(M); until lower < Higher; <------ change this to: end; That's at lines 320-326
    However, this is not the full story. There still are issues with the compiler's date functons and let me explain here a bit:
    EncodeDate builds a TDateTime from year, month and day, just like in Delphi. TDateTime is a float where the integer part is the days and fraction is for time. So when you do a EncodeDate(2018,12,1), it should return a clean integer: 43435
    In Delphi or Lazarus FloatToStr(EncodeDate(2018,12,1)) gives 43435
    WriteLn(System.DateUtils.EncodeDate(2018,12,1)) gives 43435
    Compiler's own WriteLn(EncodeDate(2018,12,1)) gives in my location 43434.83333333333
    Where you live, it's probably another number. But the bottom line is: It should not be a weird number like that and most definitely, it should not depend on where you run the program.
    Now, FormatDate, DateToStr and other functions that are built into the compiler are tuned so that they actually work with those weird and wrong TDateTimes. So you usually don't notice that something is wrong before you start mixing functions.
    What I can do now is to fix the DecodeDate, but fixing the bug in the compiler itself needs some more work. To avoid getting in trouble with compiler's DateToStr, you might want to use another DateToStr:
    function MyDateToStr(const ADate: TDateTime): String; var d,m,y: Integer; begin System.Dateutils.DecodeDate(ADate,y,m,d); result:=Format('%.2d',[d])+'.'+Format('%.2d',[m])+'.'+Format('%.4d',[y]); end;  
  19. Thanks
    lynkfs got a reaction from IgorSavkic in readable regex (verbalexpressions)   
    I saw that SmartCL.RegEx is moved to System.RegEx. This reminded me of VerbalExpressions, which I came across a while ago
    VerbalExpressions is basically a library which creates regex strings using functional programming
    /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/ The above regular expression string checks for correctly formatted url's, and in 'regular english' would be something like
    the url must start with either http or https followed by :// and optionally www. and then anything but a space This can be replaced by a set of function calls :
    // Create an example of how to test for correctly formed URLs const tester = VerEx() .startOfLine() .then('http') .maybe('s') .then('://') .maybe('www.') .anythingBut(' ') .endOfLine(); // Create an example URL const testMe = 'https://www.google.com'; // Use RegExp object's native test() function if (tester.test(testMe)) { alert('We have a correct URL'); // This output will fire } else { alert('The URL is incorrect'); } which is much more readable
    The library is available for quite a few languages, including javascript, so could be included as a native lib.
    However the below code is based on a c# implementation, which was translated into Delphi, and now translated for use in SMS. Bit of a roundabout way.
    Anyway, here is the code
    unit VerbalExpressions; {* * ---------------------------------------------------------------------------- * "THE VODKA-WARE LICENSE" (Revision 42): * <tim@bandenkrieg.hacked.jp> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a vodka in return. Tim Schumacher * ---------------------------------------------------------------------------- https://www.exceptionnotfound.net/use-verbalexpressions-to-create-readable-regexs-in-c/ https://github.com/enko/DelphiVerbalExpressions updated for use in Smart Pascal by LynkFS *} interface uses   SmartCL.System, System.RegEx; type   VerbalExpressionException = class(Exception);   TVerbalExpression = class     private       FstrSource : string;       FStrPrefix : string;       FstrSuffix : string;       FstrModifier : string;       function Add(astrValue : string) : TVerbalExpression;       function getRegEx : TW3RegEx;     public       function AnyOf(astrValue : string) : TVerbalExpression;       function Any(astrValue : string) : TVerbalExpression;       function Range(astrValue : array of string) : TVerbalExpression;       function RepeatPrevious(astrValue : integer) : TVerbalExpression;       function Anything : TVerbalExpression;       function Sanitize(astrValue : string) : string;       function Multiple(astrValue : string) : TVerbalExpression;       function StartOfLine(aboolEnable : boolean = True) : TVerbalExpression;       function EndOfLine(aboolEnable : boolean = True) : TVerbalExpression;       function _Then(astrValue : string) : TVerbalExpression;       function Find(astrValue : string) : TVerbalExpression;       function Maybe(astrValue : string) : TVerbalExpression;       function AnythingBut(astrValue : string) : TVerbalExpression;       function _Or(astrValue : string) : TVerbalExpression;       function Something : TVerbalExpression;       function SomethingBut(astrValue : string) : TVerbalExpression;       function LineBreak : TVerbalExpression;       function br : TVerbalExpression;       function tab : TVerbalExpression;       function word : TVerbalExpression;       function AddModifier(astrModifier : string) : TVerbalExpression;       function RemoveModifier(astrModifier : string) : TVerbalExpression;       function WithAnyCase(aboolEnable : boolean = true) : TVerbalExpression;       function StopAtFirst(aboolEnable : boolean = true) : TVerbalExpression;       function SearchOneLine(aboolEnable : boolean = true) : TVerbalExpression;       function AsString : string;       function Clear : TVerbalExpression;       function Test(astrValue : string) : boolean;       property RegEx : TW3RegEx read getRegEx;   end; implementation { TVerbalExpression } function TVerbalExpression.Add(astrValue: string): TVerbalExpression; begin   Result := self;   FstrSource := FstrSource + astrValue; end; function TVerbalExpression.AddModifier(astrModifier: string): TVerbalExpression; begin   if (Pos(astrModifier,FstrModifier) = -1) then     FstrModifier := FstrModifier + astrModifier;   Result := self; end; function TVerbalExpression.Any(astrValue: string): TVerbalExpression; begin   Result := AnyOf(astrValue); end; function TVerbalExpression.AnyOf(astrValue: string): TVerbalExpression; begin   Result := Add('['+ astrValue +']'); end; function TVerbalExpression.Anything: TVerbalExpression; begin   Result := Add('(.*)'); end; function TVerbalExpression.AsString: string; begin   Result := FstrSource; end; function TVerbalExpression.br: TVerbalExpression; begin   Result := LineBreak; end; function TVerbalExpression.Clear: TVerbalExpression; begin   Result := self;   FstrSource := '';   FStrPrefix := '';   FstrSuffix := '';   FstrModifier := 'gm'; end; function TVerbalExpression.getRegEx: TW3RegEx; begin   Result := TW3RegEx.Create(FstrSource); end; function TVerbalExpression.LineBreak: TVerbalExpression; begin   Result := Add('(\n|(\r\n))'); end; function TVerbalExpression.Multiple(astrValue: string): TVerbalExpression; begin   astrValue := sanitize(astrValue);   if (not ((Copy(astrValue,Length(astrValue)-1,1) = '+') or (Copy(astrValue,Length(astrValue)-1,1) = '*'))) then     astrValue := astrValue + '+';   Result := Add(astrValue); end; function TVerbalExpression.Range(astrValue: array of string): TVerbalExpression; var   LintCounter: Integer;   LstrValue : string; begin   if (Length(astrValue) mod 2) <> 0 then     raise VerbalExpressionException.Create('Number of args must be even');   LstrValue := '[';   LintCounter := 0;   while LintCounter < Length(astrValue) do begin     LstrValue := LstrValue + astrValue[LintCounter] + '-' + astrValue[LintCounter+1];     Inc(LintCounter,2);   end;   LstrValue := LstrValue + ']';   Result := Add(LstrValue); end; function TVerbalExpression.RepeatPrevious(astrValue: integer): TVerbalExpression; var   LstrValue : string; begin   LStrValue := '{' + inttostr(astrValue) + '}';   Result := Add(LstrValue); end; function TVerbalExpression.RemoveModifier(   astrModifier: string): TVerbalExpression; begin //  FstrModifier := StringReplace(FstrModifier,astrModifier,'',[rfReplaceAll]);           //<============ //replaced with code between //// lines ////   function Replace(Dest, SubStr, Str: string): string;   var     Position: Integer;   begin     Position:=Pos(SubStr, Dest);     Delete(Dest, Position, Length(SubStr));     Insert(Str, Dest, Position);     Result:=Dest;   end;   FstrModifier := Replace(FstrModifier,astrModifier,''); ////   Result := Self; end; function TVerbalExpression.Sanitize(astrValue: string): string; begin //  Result := TW3RegEx.Escape(astrValue);                                                  //<=============== //replaced with code between //// lines //// var myStr := ''; asm var escapeRegExp; (function () {   // Referring to the table here:   // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp   // these characters should be escaped   // \ ^ $ * + ? . ( ) | { } [ ]   // These characters only have special meaning inside of brackets   // they do not need to be escaped, but they MAY be escaped   // without any adverse effects (to the best of my knowledge and casual testing)   // : ! , =   // my test "~!@#$%^&*(){}[]`/=?+\|-_;:'\",<.>".match(/[\#]/g)   var specials = [         // order matters for these           "-"         , "["         , "]"         // order doesn't matter for any of these         , "/"         , "{"         , "}"         , "("         , ")"         , "*"         , "+"         , "?"         , "."         , "\\"         , "^"         , "$"         , "|"       ]       // I choose to escape every character with '\'       // even though only some strictly require it when inside of []     , regex = RegExp('[' + specials.join('\\') + ']', 'g')     ;   escapeRegExp = function (str) {     return str.replace(regex, "\\$&");   }; }());   @myStr = escapeRegExp(@astrValue); end; ////   Result := myStr; end; function TVerbalExpression.SearchOneLine(   aboolEnable: boolean): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('m')   else     Result := RemoveModifier('m'); end; function TVerbalExpression.Something: TVerbalExpression; begin   Result := Add('(.+)'); end; function TVerbalExpression.SomethingBut(astrValue: string): TVerbalExpression; begin   Result := Add('([^'+ Sanitize(astrValue) +']+)'); end; function TVerbalExpression.StartOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin   if aboolEnable then     FStrPrefix := '^'   else     FStrPrefix := '';   Result := self; end; function TVerbalExpression.StopAtFirst(aboolEnable: boolean = true): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('g')   else     Result := RemoveModifier('g'); end; function TVerbalExpression.tab: TVerbalExpression; begin   Result := Add('\t') end; function TVerbalExpression.Test(astrValue: string): boolean; begin //  Result := RegEx.Match(astrValue).Success;                           //<=============== //replaced with code between //// lines ////   Result := RegEx.Test(astrValue); //// end; function TVerbalExpression.WithAnyCase(aboolEnable: boolean = true): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('i')   else     Result := RemoveModifier('i'); end; function TVerbalExpression.word: TVerbalExpression; begin   Result := Add('\w+'); end; function TVerbalExpression.EndOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin   if aboolEnable then     FstrSuffix := '$'   else     FstrSuffix := '';   Result := self; end; function TVerbalExpression.Find(astrValue: string): TVerbalExpression; begin   Result := _Then(astrValue); end; function TVerbalExpression._Or(astrValue: string): TVerbalExpression; begin   if (Pos('(',FstrPrefix) = -1) then begin       FStrPrefix := FStrPrefix + '(';   end;   if (Pos(')',FstrSuffix) = -1) then begin       FstrSuffix := FstrSuffix + ')';   end;   Add(')|(');   if Length(astrValue) > 0 then     Add(Sanitize(astrValue));   Result := self; end; function TVerbalExpression._Then(astrValue : string) : TVerbalExpression; begin   Result := Add('('+ Sanitize(astrValue) +')'); end; function TVerbalExpression.Maybe(astrValue : string) : TVerbalExpression; begin   Result := Add('('+ Sanitize(astrValue) +')?'); end; function TVerbalExpression.AnythingBut(astrValue : string) : TVerbalExpression; begin   Result := Add('([^'+ Sanitize(astrValue) +']*)'); end; end.  
    and usage like this with example check for url, email address and phone nr
     
    unit Form1; interface uses    SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,    SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, VerbalExpressions; type   TForm1 = class(TW3Form)   private     {$I 'Form1:intf'}   protected     procedure InitializeForm; override;     procedure InitializeObject; override;     procedure Resize; override;     urlExp   : TVerbalExpression;     emailExp : TVerbalExpression;     phoneExp : TVerbalExpression;   end; implementation { TForm1 } procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'} /* rules for checking simple URLs: -The URL must start with either "http" or "https". -The URL must then have "://". -The URL can then have anything following "://", as long as it is isn't a space. */   urlExp := TVerbalExpression.Create     .StartOfLine()     ._Then('http')     .Maybe('s')     ._Then('://')     .Maybe('www.')     .anythingBut(' ')     .endOfLine();   writeln('regexp url : ' + urlExp.AsString);   var url := "https://github.com";   if urlExp.Test(url) then writeln(url + ' is valid')                       else writeln(url + ' is invalid'); /* rules for checking email addresses: -The email may start with any text, followed by an '@' symbol. -After the '@', the email may contain any text (except a blank space), followed by a '.' -After the '.', the email address may contain any text (except a blank space). */   emailExp := TVerbalExpression.Create     .StartOfLine()     .Anything()     ._Then("@")     .AnythingBut(" ")     ._Then(".")     .AnythingBut(" ")     .EndOfLine();   writeln('regexp email : ' + emailExp.asString);   var email := "test@example.com";   if emailExp.Test(email) then writeln(email + ' is valid')                           else writeln(email + ' is invalid');   email := "test@example";   if emailExp.Test(email) then writeln(email + ' is valid')                           else writeln(email + ' is invalid'); /* rules for checking (australian) phone nrs: -The phone number may start with "(". -The phone number must then have 2 digits, each of which are in the range 0-9. -The phone number may then have ")". -Following the optional ")", the phone number may also have a space. -Following the optional space, the phone number must have 4 digits, each in the range 0-9. -Following this set of digits, the phone number may optionally include a dash ("-") or a space. -Following the optional dash or space, the phone number must have 4 digits, each in the range 0-9. */   phoneExp := TVerbalExpression.Create     .StartOfLine()     .Maybe("(")     .Range(['0', '9'])     .RepeatPrevious(2)     .Maybe(")")     .Maybe(" ")     .Range(['0', '9'])     .RepeatPrevious(4)     .Maybe(" ")     .Range(['0', '9'])     .RepeatPrevious(4)     .EndOfLine();   writeln('regexp au-phone : ' + phoneExp.asString);   var phone := "(02) 2093 9118";   if phoneExp.Test(phone) then writeln(phone + ' is valid')                           else writeln(phone + ' is invalid');   phone := "022093911";   if phoneExp.Test(phone) then writeln(phone + ' is valid')                           else writeln(phone + ' is invalid'); end;   procedure TForm1.Resize; begin   inherited; end;   initialization   Forms.RegisterForm({$I %FILE%}, TForm1); end.  
  20. Like
    lynkfs got a reaction from jarto in readable regex (verbalexpressions)   
    I saw that SmartCL.RegEx is moved to System.RegEx. This reminded me of VerbalExpressions, which I came across a while ago
    VerbalExpressions is basically a library which creates regex strings using functional programming
    /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/ The above regular expression string checks for correctly formatted url's, and in 'regular english' would be something like
    the url must start with either http or https followed by :// and optionally www. and then anything but a space This can be replaced by a set of function calls :
    // Create an example of how to test for correctly formed URLs const tester = VerEx() .startOfLine() .then('http') .maybe('s') .then('://') .maybe('www.') .anythingBut(' ') .endOfLine(); // Create an example URL const testMe = 'https://www.google.com'; // Use RegExp object's native test() function if (tester.test(testMe)) { alert('We have a correct URL'); // This output will fire } else { alert('The URL is incorrect'); } which is much more readable
    The library is available for quite a few languages, including javascript, so could be included as a native lib.
    However the below code is based on a c# implementation, which was translated into Delphi, and now translated for use in SMS. Bit of a roundabout way.
    Anyway, here is the code
    unit VerbalExpressions; {* * ---------------------------------------------------------------------------- * "THE VODKA-WARE LICENSE" (Revision 42): * <tim@bandenkrieg.hacked.jp> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a vodka in return. Tim Schumacher * ---------------------------------------------------------------------------- https://www.exceptionnotfound.net/use-verbalexpressions-to-create-readable-regexs-in-c/ https://github.com/enko/DelphiVerbalExpressions updated for use in Smart Pascal by LynkFS *} interface uses   SmartCL.System, System.RegEx; type   VerbalExpressionException = class(Exception);   TVerbalExpression = class     private       FstrSource : string;       FStrPrefix : string;       FstrSuffix : string;       FstrModifier : string;       function Add(astrValue : string) : TVerbalExpression;       function getRegEx : TW3RegEx;     public       function AnyOf(astrValue : string) : TVerbalExpression;       function Any(astrValue : string) : TVerbalExpression;       function Range(astrValue : array of string) : TVerbalExpression;       function RepeatPrevious(astrValue : integer) : TVerbalExpression;       function Anything : TVerbalExpression;       function Sanitize(astrValue : string) : string;       function Multiple(astrValue : string) : TVerbalExpression;       function StartOfLine(aboolEnable : boolean = True) : TVerbalExpression;       function EndOfLine(aboolEnable : boolean = True) : TVerbalExpression;       function _Then(astrValue : string) : TVerbalExpression;       function Find(astrValue : string) : TVerbalExpression;       function Maybe(astrValue : string) : TVerbalExpression;       function AnythingBut(astrValue : string) : TVerbalExpression;       function _Or(astrValue : string) : TVerbalExpression;       function Something : TVerbalExpression;       function SomethingBut(astrValue : string) : TVerbalExpression;       function LineBreak : TVerbalExpression;       function br : TVerbalExpression;       function tab : TVerbalExpression;       function word : TVerbalExpression;       function AddModifier(astrModifier : string) : TVerbalExpression;       function RemoveModifier(astrModifier : string) : TVerbalExpression;       function WithAnyCase(aboolEnable : boolean = true) : TVerbalExpression;       function StopAtFirst(aboolEnable : boolean = true) : TVerbalExpression;       function SearchOneLine(aboolEnable : boolean = true) : TVerbalExpression;       function AsString : string;       function Clear : TVerbalExpression;       function Test(astrValue : string) : boolean;       property RegEx : TW3RegEx read getRegEx;   end; implementation { TVerbalExpression } function TVerbalExpression.Add(astrValue: string): TVerbalExpression; begin   Result := self;   FstrSource := FstrSource + astrValue; end; function TVerbalExpression.AddModifier(astrModifier: string): TVerbalExpression; begin   if (Pos(astrModifier,FstrModifier) = -1) then     FstrModifier := FstrModifier + astrModifier;   Result := self; end; function TVerbalExpression.Any(astrValue: string): TVerbalExpression; begin   Result := AnyOf(astrValue); end; function TVerbalExpression.AnyOf(astrValue: string): TVerbalExpression; begin   Result := Add('['+ astrValue +']'); end; function TVerbalExpression.Anything: TVerbalExpression; begin   Result := Add('(.*)'); end; function TVerbalExpression.AsString: string; begin   Result := FstrSource; end; function TVerbalExpression.br: TVerbalExpression; begin   Result := LineBreak; end; function TVerbalExpression.Clear: TVerbalExpression; begin   Result := self;   FstrSource := '';   FStrPrefix := '';   FstrSuffix := '';   FstrModifier := 'gm'; end; function TVerbalExpression.getRegEx: TW3RegEx; begin   Result := TW3RegEx.Create(FstrSource); end; function TVerbalExpression.LineBreak: TVerbalExpression; begin   Result := Add('(\n|(\r\n))'); end; function TVerbalExpression.Multiple(astrValue: string): TVerbalExpression; begin   astrValue := sanitize(astrValue);   if (not ((Copy(astrValue,Length(astrValue)-1,1) = '+') or (Copy(astrValue,Length(astrValue)-1,1) = '*'))) then     astrValue := astrValue + '+';   Result := Add(astrValue); end; function TVerbalExpression.Range(astrValue: array of string): TVerbalExpression; var   LintCounter: Integer;   LstrValue : string; begin   if (Length(astrValue) mod 2) <> 0 then     raise VerbalExpressionException.Create('Number of args must be even');   LstrValue := '[';   LintCounter := 0;   while LintCounter < Length(astrValue) do begin     LstrValue := LstrValue + astrValue[LintCounter] + '-' + astrValue[LintCounter+1];     Inc(LintCounter,2);   end;   LstrValue := LstrValue + ']';   Result := Add(LstrValue); end; function TVerbalExpression.RepeatPrevious(astrValue: integer): TVerbalExpression; var   LstrValue : string; begin   LStrValue := '{' + inttostr(astrValue) + '}';   Result := Add(LstrValue); end; function TVerbalExpression.RemoveModifier(   astrModifier: string): TVerbalExpression; begin //  FstrModifier := StringReplace(FstrModifier,astrModifier,'',[rfReplaceAll]);           //<============ //replaced with code between //// lines ////   function Replace(Dest, SubStr, Str: string): string;   var     Position: Integer;   begin     Position:=Pos(SubStr, Dest);     Delete(Dest, Position, Length(SubStr));     Insert(Str, Dest, Position);     Result:=Dest;   end;   FstrModifier := Replace(FstrModifier,astrModifier,''); ////   Result := Self; end; function TVerbalExpression.Sanitize(astrValue: string): string; begin //  Result := TW3RegEx.Escape(astrValue);                                                  //<=============== //replaced with code between //// lines //// var myStr := ''; asm var escapeRegExp; (function () {   // Referring to the table here:   // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp   // these characters should be escaped   // \ ^ $ * + ? . ( ) | { } [ ]   // These characters only have special meaning inside of brackets   // they do not need to be escaped, but they MAY be escaped   // without any adverse effects (to the best of my knowledge and casual testing)   // : ! , =   // my test "~!@#$%^&*(){}[]`/=?+\|-_;:'\",<.>".match(/[\#]/g)   var specials = [         // order matters for these           "-"         , "["         , "]"         // order doesn't matter for any of these         , "/"         , "{"         , "}"         , "("         , ")"         , "*"         , "+"         , "?"         , "."         , "\\"         , "^"         , "$"         , "|"       ]       // I choose to escape every character with '\'       // even though only some strictly require it when inside of []     , regex = RegExp('[' + specials.join('\\') + ']', 'g')     ;   escapeRegExp = function (str) {     return str.replace(regex, "\\$&");   }; }());   @myStr = escapeRegExp(@astrValue); end; ////   Result := myStr; end; function TVerbalExpression.SearchOneLine(   aboolEnable: boolean): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('m')   else     Result := RemoveModifier('m'); end; function TVerbalExpression.Something: TVerbalExpression; begin   Result := Add('(.+)'); end; function TVerbalExpression.SomethingBut(astrValue: string): TVerbalExpression; begin   Result := Add('([^'+ Sanitize(astrValue) +']+)'); end; function TVerbalExpression.StartOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin   if aboolEnable then     FStrPrefix := '^'   else     FStrPrefix := '';   Result := self; end; function TVerbalExpression.StopAtFirst(aboolEnable: boolean = true): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('g')   else     Result := RemoveModifier('g'); end; function TVerbalExpression.tab: TVerbalExpression; begin   Result := Add('\t') end; function TVerbalExpression.Test(astrValue: string): boolean; begin //  Result := RegEx.Match(astrValue).Success;                           //<=============== //replaced with code between //// lines ////   Result := RegEx.Test(astrValue); //// end; function TVerbalExpression.WithAnyCase(aboolEnable: boolean = true): TVerbalExpression; begin   if aboolEnable then     Result := AddModifier('i')   else     Result := RemoveModifier('i'); end; function TVerbalExpression.word: TVerbalExpression; begin   Result := Add('\w+'); end; function TVerbalExpression.EndOfLine(aboolEnable : boolean = True) : TVerbalExpression; begin   if aboolEnable then     FstrSuffix := '$'   else     FstrSuffix := '';   Result := self; end; function TVerbalExpression.Find(astrValue: string): TVerbalExpression; begin   Result := _Then(astrValue); end; function TVerbalExpression._Or(astrValue: string): TVerbalExpression; begin   if (Pos('(',FstrPrefix) = -1) then begin       FStrPrefix := FStrPrefix + '(';   end;   if (Pos(')',FstrSuffix) = -1) then begin       FstrSuffix := FstrSuffix + ')';   end;   Add(')|(');   if Length(astrValue) > 0 then     Add(Sanitize(astrValue));   Result := self; end; function TVerbalExpression._Then(astrValue : string) : TVerbalExpression; begin   Result := Add('('+ Sanitize(astrValue) +')'); end; function TVerbalExpression.Maybe(astrValue : string) : TVerbalExpression; begin   Result := Add('('+ Sanitize(astrValue) +')?'); end; function TVerbalExpression.AnythingBut(astrValue : string) : TVerbalExpression; begin   Result := Add('([^'+ Sanitize(astrValue) +']*)'); end; end.  
    and usage like this with example check for url, email address and phone nr
     
    unit Form1; interface uses    SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,    SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, VerbalExpressions; type   TForm1 = class(TW3Form)   private     {$I 'Form1:intf'}   protected     procedure InitializeForm; override;     procedure InitializeObject; override;     procedure Resize; override;     urlExp   : TVerbalExpression;     emailExp : TVerbalExpression;     phoneExp : TVerbalExpression;   end; implementation { TForm1 } procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin   inherited;   {$I 'Form1:impl'} /* rules for checking simple URLs: -The URL must start with either "http" or "https". -The URL must then have "://". -The URL can then have anything following "://", as long as it is isn't a space. */   urlExp := TVerbalExpression.Create     .StartOfLine()     ._Then('http')     .Maybe('s')     ._Then('://')     .Maybe('www.')     .anythingBut(' ')     .endOfLine();   writeln('regexp url : ' + urlExp.AsString);   var url := "https://github.com";   if urlExp.Test(url) then writeln(url + ' is valid')                       else writeln(url + ' is invalid'); /* rules for checking email addresses: -The email may start with any text, followed by an '@' symbol. -After the '@', the email may contain any text (except a blank space), followed by a '.' -After the '.', the email address may contain any text (except a blank space). */   emailExp := TVerbalExpression.Create     .StartOfLine()     .Anything()     ._Then("@")     .AnythingBut(" ")     ._Then(".")     .AnythingBut(" ")     .EndOfLine();   writeln('regexp email : ' + emailExp.asString);   var email := "test@example.com";   if emailExp.Test(email) then writeln(email + ' is valid')                           else writeln(email + ' is invalid');   email := "test@example";   if emailExp.Test(email) then writeln(email + ' is valid')                           else writeln(email + ' is invalid'); /* rules for checking (australian) phone nrs: -The phone number may start with "(". -The phone number must then have 2 digits, each of which are in the range 0-9. -The phone number may then have ")". -Following the optional ")", the phone number may also have a space. -Following the optional space, the phone number must have 4 digits, each in the range 0-9. -Following this set of digits, the phone number may optionally include a dash ("-") or a space. -Following the optional dash or space, the phone number must have 4 digits, each in the range 0-9. */   phoneExp := TVerbalExpression.Create     .StartOfLine()     .Maybe("(")     .Range(['0', '9'])     .RepeatPrevious(2)     .Maybe(")")     .Maybe(" ")     .Range(['0', '9'])     .RepeatPrevious(4)     .Maybe(" ")     .Range(['0', '9'])     .RepeatPrevious(4)     .EndOfLine();   writeln('regexp au-phone : ' + phoneExp.asString);   var phone := "(02) 2093 9118";   if phoneExp.Test(phone) then writeln(phone + ' is valid')                           else writeln(phone + ' is invalid');   phone := "022093911";   if phoneExp.Test(phone) then writeln(phone + ' is valid')                           else writeln(phone + ' is invalid'); end;   procedure TForm1.Resize; begin   inherited; end;   initialization   Forms.RegisterForm({$I %FILE%}, TForm1); end.  
  21. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.0.1 is released!   
    Smart Mobile Studio 3.0.1 is released
    This is the first release since 3.0. Biggest new feature is TW3LeafletMap, which lets you use OpenStreetMap. As it does not need API keys (like TW3GoogleMaps), it’s really fast and easy to use:
    – Create a project
    – Add a TW3LeafletMap -control on the form
    – Set AutoCreateMap to true on the map control
    Changes since 3.0
    8.11.2018
    RTL:
    – EventManager:
    – Add procedure AllowDefaultAction, that can be called from OnClick when the default
    action should happen. For example: To let the virtual keyboard to pop up from OnTouch.
    – Bug fixes:
    – Native scrolling was prevented if scrolling was done from an unknown element.
    – Prevent an extra OnClick from being fired on mobile devices.
    – TW3ListView: Bug fix to resizing of items.
    – Bug fixes to GeoLocation. Also update the Geolocation demo.
    – Deprecate PhoneGapAPI’s JGeolocation functions. SmartCL.GeoLocation.pas should be used instead.
    – Fix slider so that OnMouseDown, OnMouseUp and OnMOuseMove can be used.
    – TW3TabControl Tab’s OnShow was sent twice for the first tab
    – SmartCL.RegEx moved to System.RegEx. Also fixed TW3RegEx.Exec to return a proper empty array instead of nil.
    – Bug fix to Chart: TSeriesData.Refresh now also updates the X axis
    – TW3Grid:
    – Added TW3ImageColumn
    – Add Alignment-property to TW3ColumnHeader
    – Added a new OnCellControlCreated-event, which fires when a button, toggle, progress bar or image is created.
    Makes it possible to change properties of the control easily.
    – Added support for OpenStreetMap using the Leaflet library:
    – New control: TW3LeafletMap
    – New featured demo: LeafletMap
    IDE/Compiler:
    – Fixed search path compilation issues
    – Relative and absolute paths are working now
    – Compiler is updated when search path is modified in options
    – $I will look for include file in the project folder first
    – $R supports absolute paths, wildcards, folder name extension and ($Libraries) macro
    – Fix exceptions in Search
    – Upgrade to UPX 3.95
    23.7.2018
    – SmartCL.Effects: Properly handle padding and margins while doing effects.
    22.7.2018
    – Fix to css-files for selected text in TW3Edit and TW3Memo
    Release notes and installers: https://smartmobilestudio.com/2018/11/08/smart-mobile-studio-3-0-1-released/
    Note that you can also use SmartUpdate to keep your portable installation up to date. Instructions on using that are in the link above.
  22. Like
    lynkfs got a reaction from IElite in Resize method called 3 times ?   
    The multiple calls to ReSize have been a 'feature' from the beginning, but I don't know exactly why this happens.
    The Smart Mobile Studio release page (https://smartmobilestudio.com/category/news/) says : 
    28.9.2017 :
    Improvements to Resizing:
    – Minimize number of resizes during form create.
    – Trigger only one resize during device flip.
    so obviously there can be multiple resize events on startup. 
    Not sure what your business case is here, but personally I use ReSize mainly for device flip purposes.
    If your purpose is to DoSomething after an element or a form has been completely initialised in the DOM, you could use ReadyExecute
      Handle.ReadyExecute( procedure ()   begin     DoSomething;   end); or even when it involves doing this on a repaint
      Handle.ReadyExecute( Procedure ()     begin       BrowserAPI.window.requestAnimationFrame( procedure ()       Begin         DoSomething;       end);     end); not in the ReSize proc but in the control setup
     
  23. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.0.1 is released!   
    Couldn't be easier ?
  24. Like
    lynkfs reacted to jarto in Resize method called 3 times ?   
    In versions before 3.0 alpha, there were a lot of resizes. If I remember right, something like closer to 10 before the form was shown. Flipping the device also resulted in two resizes: One, when the width was changed and one when the height was changed. Also, every BeginUpdate/EndUpdate used to cause a resize (regardless if anything was changed) and if you peek at SmartCL.Controls.pas, they are used a lot.
    For 3.0 alpha I did my best to eliminate all that were safe to be eliminated. So, the device flip causes only one resize, for example.
    At the moment the RTL mostly internally calls ResizeWhenReady, which checks to see if the control and all its children are ready: If not, the resize will be delayed and sent later. Also, invalidate does call ResizeWhenReady with a delay. So if you call invalidate for a control 5 times, you will only get one resize.
    I don't know if I can eliminate more resizes safely, but I can have a look.
  25. Like
    lynkfs got a reaction from Czar in Resize method called 3 times ?   
    The multiple calls to ReSize have been a 'feature' from the beginning, but I don't know exactly why this happens.
    The Smart Mobile Studio release page (https://smartmobilestudio.com/category/news/) says : 
    28.9.2017 :
    Improvements to Resizing:
    – Minimize number of resizes during form create.
    – Trigger only one resize during device flip.
    so obviously there can be multiple resize events on startup. 
    Not sure what your business case is here, but personally I use ReSize mainly for device flip purposes.
    If your purpose is to DoSomething after an element or a form has been completely initialised in the DOM, you could use ReadyExecute
      Handle.ReadyExecute( procedure ()   begin     DoSomething;   end); or even when it involves doing this on a repaint
      Handle.ReadyExecute( Procedure ()     begin       BrowserAPI.window.requestAnimationFrame( procedure ()       Begin         DoSomething;       end);     end); not in the ReSize proc but in the control setup
     
×
×
  • Create New...