Jump to content

lynkfs

Moderators
  • Content Count

    743
  • Joined

  • Last visited

  • Days Won

    146

Reputation Activity

  1. Like
    lynkfs reacted to IgorSavkic in the future of coding   
    We live in fast advancing times, potential application is limitless, I guess first we'll see it as replacement for human support.
  2. Like
    lynkfs got a reaction from IElite in Integrating Paypal Payment Script   
    do something like this (max control)
      var Script := browserapi.document.createElement('script');   Script.src := 'https://.......your script url ........';   browserapi.document.head.appendChild(Script);   Script.onload := procedure   begin     showmessage('loaded'); ... do your thing ...   end; this can be part of a button click handler
     
     
  3. Like
    lynkfs got a reaction from jarto in Integrating Paypal Payment Script   
    Your code should work, except for the script placement.
    The below code is pretty much the same as yours, just embedded in a button click handler (which you might have done too)
        var W3Button : TW3Button := TW3Button.Create(W3Panel);     W3Button.SetBounds(580,-10+160*(cartitems.length+1),200,40);     W3Button.Caption := 'Make Payment';     W3Button.OnClick := procedure(sender:TObject)     begin       var PayPal : TW3Panel := TW3Panel.Create(W3Panel);       Paypal.SetBounds(140,100 + 160*(cartitems.length+1),400,800); //position below cart        Paypal.StyleClass := 'DIV'; //removes all smart's style info       Paypal.handle.id := 'paypal-button-container';       Paypal.NativeScrolling := true;       var paypalamount : string := StrAfter(currencytotal,'A$');       asm         paypal.Buttons({           createOrder: function(data, actions) {             // This function sets up the details of the transaction, including the amount and line item details.             return actions.order.create({               purchase_units: [{                 amount: {                   value: @paypalamount                 }               }]             });           },           onApprove: function(data, actions) {             // This function captures the funds from the transaction.             return actions.order.capture().then(function(details) {               // This function shows a transaction success message to your buyer.               alert('Transaction completed by ' + details.payer.name.given_name);             });           }         }).render('#paypal-button-container');         //This function displays Smart Payment Buttons on your web page.       end;
    as for the script, put it in the body of your custom template
    <body>     <script src="https://www.paypal.com/sdk/js?client-id=xxx&currency=AUD">
     
  4. Like
    lynkfs got a reaction from Czar in styling   
    Off topic : there is an interesting effect when putting images on top of images.
    Take an image and give it a large width and height. Take the same image (same src) and place it on top of the first image, give it same height but say 25% of width and center it. Both images should have object-fit set to 'cover' and reduce opacity of the first one to about 65%.
    Depending on the image, about 1 in 5 give a passable interesting result doing this, 1 in 50 look amazing.
    Some examples here. 
    (not optimised for size, so slow loading)
     
     
  5. Like
    lynkfs got a reaction from jarto in percentage based positioning   
    Percentage based positioning has the advantage of having a 'responsive' page layout with little effort.
    Responsive in this respect means that page layouts will scale seamlessly on devices which may have slightly different pixel dimensions horizontally and/or vertically.
    So instead of having an editbox on absolute positioning 100,100,400,35 in pixel terms, it is sometimes easier to define all or some of these in percentages. Conveniently 10%-left is interpreted by the browser as starting on 10% of the viewport width, 10%-top starts at 10% of the viewport height etc.
    Mixing pixels and percentages works fine too
    MyEditBox.handle.style.left := '10%'; MyEditBox.handle.style.top := '12px'; //MyEditBox.left := 12; This is all still based on absolute positioning, which is the default in Smart.
    An alternative approach, with the same outcome, is to define dimensions at runtime based on a formula.
    MyEditBox.handle.style.left := 'calc(10vw - 20px)'  resolves in a left positioning of 10% of the viewport-width minus 20 pixels : 28 pixels on a 480 pixels wide phone
    Note that the minus sign needs to be enclosed in spaces or this won't work
    This can be also used to scale f.i. font size depending on device capabilities :
    MyEditBox.handle.style['font-size'] := 'calc(16px + 1vw)'  which has the effect of a font size which grows by 1 pixel for every 100 pixels of viewport width. 
    It can get a bit funny on orientation change
     
    an all-percentage example page with some random text and images, based on these principles, looks like this , which scales nicely from mobile to desktop (resize browser window).
  6. Like
    lynkfs got a reaction from IElite in styling   
    Off topic : there is an interesting effect when putting images on top of images.
    Take an image and give it a large width and height. Take the same image (same src) and place it on top of the first image, give it same height but say 25% of width and center it. Both images should have object-fit set to 'cover' and reduce opacity of the first one to about 65%.
    Depending on the image, about 1 in 5 give a passable interesting result doing this, 1 in 50 look amazing.
    Some examples here. 
    (not optimised for size, so slow loading)
     
     
  7. Like
    lynkfs got a reaction from IgorSavkic in percentage based positioning   
    It becomes even more interesting when combining css variables and calc :
    since .... it has been possible to define css variables, which can be referenced in multiple ways
    they can even be set/get in pascal/js :
     
    get a handle to a stylesheet, or create one on the fly //stylesheet       var style := browserapi.document.createElement("STYLE");       browserapi.document.head.appendChild(style);       var styleSheet := style.sheet; set a css variable ("--variable-width") and insert into the stylesheet css variable names can be any string but have to start with --       //set initial css variable (width)       var s1 := #'       :root {         --variable-width: 500px;       }';       styleSheet.insertRule(s1, 0); retrieve and display css width variable browserapi.window.alert(             browserapi.window.getComputedStyle(browserapi.document.documentElement)             .getPropertyValue('--variable-width')); set css variable to some value           browserapi.document.documentElement.style['--variable-width'] :=             Memo1.handle.getBoundingClientRect().width; use this variable in a 'calc'ulation           Memo1.handle.style['height'] := 'calc(var(--variable-width) * 0.6)';            
    having a 'width' variable is just an example, maybe a bit contrived. Works fine though.
    Usually css variables are used for colors etc,  and those values which need to be set globally for the whole application.
  8. Like
    lynkfs got a reaction from Czar in percentage based positioning   
    Percentage based positioning has the advantage of having a 'responsive' page layout with little effort.
    Responsive in this respect means that page layouts will scale seamlessly on devices which may have slightly different pixel dimensions horizontally and/or vertically.
    So instead of having an editbox on absolute positioning 100,100,400,35 in pixel terms, it is sometimes easier to define all or some of these in percentages. Conveniently 10%-left is interpreted by the browser as starting on 10% of the viewport width, 10%-top starts at 10% of the viewport height etc.
    Mixing pixels and percentages works fine too
    MyEditBox.handle.style.left := '10%'; MyEditBox.handle.style.top := '12px'; //MyEditBox.left := 12; This is all still based on absolute positioning, which is the default in Smart.
    An alternative approach, with the same outcome, is to define dimensions at runtime based on a formula.
    MyEditBox.handle.style.left := 'calc(10vw - 20px)'  resolves in a left positioning of 10% of the viewport-width minus 20 pixels : 28 pixels on a 480 pixels wide phone
    Note that the minus sign needs to be enclosed in spaces or this won't work
    This can be also used to scale f.i. font size depending on device capabilities :
    MyEditBox.handle.style['font-size'] := 'calc(16px + 1vw)'  which has the effect of a font size which grows by 1 pixel for every 100 pixels of viewport width. 
    It can get a bit funny on orientation change
     
    an all-percentage example page with some random text and images, based on these principles, looks like this , which scales nicely from mobile to desktop (resize browser window).
  9. Like
    lynkfs got a reaction from Daniel Eiszele in styling   
    Off topic : there is an interesting effect when putting images on top of images.
    Take an image and give it a large width and height. Take the same image (same src) and place it on top of the first image, give it same height but say 25% of width and center it. Both images should have object-fit set to 'cover' and reduce opacity of the first one to about 65%.
    Depending on the image, about 1 in 5 give a passable interesting result doing this, 1 in 50 look amazing.
    Some examples here. 
    (not optimised for size, so slow loading)
     
     
  10. Thanks
    lynkfs reacted to jarto in mouse position   
    Thank you. This is one of the really old demos where lots of code is in TForm1.InitializeObject. When that is done, you run the risk of everything not being initialized when your code runs.
    Standard fix is to move the code to TForm1.InitializeForm:
    type TForm1 = class(TW3Form) private {$I 'Form1:intf'} FIsCapture: boolean; function MakeMouseEvent(label: TW3Label; name: string): TMouseEvent; function MouseToStr(shift: TShiftState; x, y: integer; scrXY: TPoint): string; protected procedure InitializeForm; override; procedure InitializeObject; override; end; implementation { TForm1} procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.InitializeForm; procedure EndCapture; begin W3pnlSetCapture.Visible := true; W3pnlReleaseCapture.Visible := false; W3Panel2.OnMouseUp := nil; FIsCapture := false; end; begin inherited; OnMouseMove := MakeMouseEvent(W3lblFormCoords, 'Form'); W3Panel1.OnMouseMove := MakeMouseEvent(W3lblPanel1Coords, 'Panel1');  
     
  11. Like
    lynkfs got a reaction from IgorSavkic in Android Back Button (Cordova/ Phonegap)   
    Basically you'll want to add a history entry whenever you change forms. 
    Also make up alias url's for every form. For instance 'Form1' can have an url-alias of 'index.html', 'Form2' an alias of 'bookreviews.html' etc. Every history entry links a Form (from) to an alias (to), see below. 
    There is an old post on this forum (2015), but checking my last project I did this :
    Put an initial history entry on startup and also initiate the main eventhandler for handling backbutton presses
    unit Unit1; interface uses   Pseudo.CreateForms, // auto-generated unit that creates forms during startup   System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms,    SmartCL.Application; type   TApplication  = class(TW3CustomApplication)     procedure ApplicationStarting; reintroduce;   end; implementation uses   Globals; procedure TApplication.ApplicationStarting; begin   browserapi.window.onpopstate := procedure(e:variant) begin CBOnPopState(e); end;   browserapi.window.history.pushState(nil,'Form1','index.html'); end; end. Whenever there is a change in form, add an entry to the history object
    browserapi.window.history.pushState(nil,'Form1','bookreview.html'); //from Form1 to bookreview.html The second parameter (Form1) is the 'from' Smart form-name and the third parameter is the 'to' fake url. Supposing that Form2 in this example handles the bookreviews, then at some stage users will return to Form1, at which time another history entry 'Form2'  --> 'index.html' will be added.
    The handler simply reacts to the history popstate events :
    Procedure CBOnPopState(e: variant); Begin   if (browserapi.window.location.pathname = '/')                    then GoToForm('Form1');   if (browserapi.window.location.pathname = '/index.html')          then GoToForm('Form1');   if (browserapi.window.location.pathname = '/bookreview.html')   then GoToForm('Form2'); ... The above is for browser projects, but should work for native android as well
  12. Thanks
    lynkfs got a reaction from IgorSavkic in drag and drop   
    I've spend hours to implement a simple drag & drop mechanism, and I've given up.
    The mechanism is not that difficult, but the browser implementations drive me nuts.
    The problem is that on different chrome browsers the 'drop' position is off from what it should be. 
    If someone's interested, the code below works flawlessly in the internal ide browser. Executing on win10-latest chrome you'll see a jump in positioning of elements when dropped.
    I've finally figured out, that the jump-amount is based on where the element has been 'grabbed'. It is zero when grabbing top/left, and half the width of the grabbed element when grabbed bottom/right.
    Checking up on stack-overflow, there are heaps of references to this problem. Without a workable solution. And posts dating from 6 years ago until recently. One might expect these type of bugs to be resolved in a timespan that long.
    test-code
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   Var Drag : JW3Panel := JW3Panel.Create(self);   Drag.SetBounds(50,50,100,100);   Drag.handle.style.border := '1px solid silver';   Drag.handle.setAttribute('draggable','true');   Var Drop : JW3Panel := JW3Panel.Create(self);   Drop.SetBounds(50,300,400,200);   Drop.handle.style.border := '1px solid silver';   Drag.handle.ondragstart := procedure(ev: variant)   begin     Drop.handle.ondragover := procedure(ev: variant)     begin       ev.preventDefault();     end;     //payload = image.id, mouse-offsetX and mouse-offsetY     ev.dataTransfer.setData("text", ev.target.id + ';' +                               inttostr(ev.offsetX) + ';' +                             inttostr(ev.offsetY));     ev.dataTransfer.effectAllowed := "copy";     ev.dataTransfer.dropEffect    := "copy";   end;   Drop.handle.ondrop := procedure(ev: variant)   begin     ev.preventDefault();     var data := ev.dataTransfer.getData("text");     //split payload into image.id, mouse-offsetX and mouse-offsetY     var myarray := StrSplit(data,';');     var target : variant := document.getElementById(myarray[0]);     ev.target.appendChild(target);     target.style.left := ev.offsetX - StrToInt(MyArray[1]) + "px";     target.style.top  := ev.offsetY - StrToInt(MyArray[2]) + "px";   end; end; I've ended up with a mouse-event based drag & drop solution rather than using the html5 based d&d api.
  13. Like
    lynkfs got a reaction from IgorSavkic in styling   
    Looking into this idea a bit further.
    So the ultimate styler program needs to do this :
    - read in a theme file
      - discover the visual components in the theme
      - and populate them for use
    - have a canvas where these components can be dropped on
      - and can be manipulated qua size and position
      - and have a preview for how it looks in real life
    Basically this is an exercise in elevating css to its maximum capabilites, without using js (pascal) or html at all in the styling process.
    A first draft of such a program :
    The demo theme file contains components for buttons and images, and thus these are displayed on top.
    These can be dragged onto the canvas and positioned at will. The coordinates are displayed on the right, either in absolute or relative values. 
    The preview icon on the canvas opens up a new live browser window.
       
    So far so good.
    The huge number of atomic css properties per visual component (280) now has been subdivided in 3 chunks :
    A- those css properties to do with visual design : x (left), y (top), width and height
       These are displayed on the right
    B- those css properties which are part of the theme file for this component
       so if for instance the theme file has a line like this: "theme01.buttons.generic.fontWeight: bold", then this will result in a modifier on the screen, where 'bold' can be changed.  <to do>
    C- the remainder of the 280 css properties (280 minus A minus B). 
       Those are either not important, have passable default values or are only seldomly important for styling of the particular component. <to do>
    Using components ultimately translates into html. 
    Taking this example : 
    <img class="img" id="Component38" src="images/buttons.jpg" name="button" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 350px; top: 10px; width: 82px; height: 34px; cursor: pointer;"> Styling through css results in modifying the style attribute (.. style="visibility: etc...)
    Still debating whether the other attributes should be part of the styling process.
    In the demo above for instance I've assigned the 'src' attribute of images to a random unsplash picture, instead of making it directly editable. Its not a css property after all.
    Demo here (alpha version)
     
  14. Like
    lynkfs got a reaction from Czar in ionic   
    Usually I try to steer away from external frameworks. They seldom provide exactly what is needed and are difficult to modify.
    An exception may be the ionic framework. It is an open source ui toolkit with some nice elements in it. The good thing is that their components subtly change depending on which platform they are used on.
    The following code uses 2 Ionic components : a button and an extremely elegant selector
      var iobutton : TIonicButton := TIonicButton.Create(self);   iobutton.setbounds(100,100,100,35);   iobutton.color := 'primary';   iobutton.fill := 'outline';   iobutton.caption := 'mybutton';   iobutton.onclick := procedure(sender:TObject)     begin       showmessage((sender as TIonicButton).caption);     end;   end); and the selector
      var iofab : TIonicFab := TIonicFab.Create(self);   iofab.setbounds(400,200,56,56);   iofab.mainIcon   := 'share';   iofab.topIcon    := 'logo-vimeo';   iofab.bottomIcon := 'logo-facebook';   iofab.leftIcon   := 'logo-instagram';   iofab.rightIcon  := 'logo-twitter';   iofab.right.button.onclick := lambda showmessage('twitter'); end; See demo here
     
    The code for both the button and the selector component in unit2/3 of this project
     
  15. Like
    lynkfs reacted to jarto in Smart Mobile Studio 3.9.1 (1st Alpha release) is available   
    A new update is available:
    IDE:
    Fix character set that was sent from the internal server RTL:
    Bug fix to handling resizes when anchor gaps are locked. Improvements to TW3ScrollBox Scrollbars -property can now be changed in Object Inspector The ScrollBox waits properly until it's subcontrols are ready ScrollBars and indicators did not work properly if ForceParent was true Fix bugs in resizing the scrollbox
  16. Like
    lynkfs got a reaction from Czar in styling   
    Looking into this idea a bit further.
    So the ultimate styler program needs to do this :
    - read in a theme file
      - discover the visual components in the theme
      - and populate them for use
    - have a canvas where these components can be dropped on
      - and can be manipulated qua size and position
      - and have a preview for how it looks in real life
    Basically this is an exercise in elevating css to its maximum capabilites, without using js (pascal) or html at all in the styling process.
    A first draft of such a program :
    The demo theme file contains components for buttons and images, and thus these are displayed on top.
    These can be dragged onto the canvas and positioned at will. The coordinates are displayed on the right, either in absolute or relative values. 
    The preview icon on the canvas opens up a new live browser window.
       
    So far so good.
    The huge number of atomic css properties per visual component (280) now has been subdivided in 3 chunks :
    A- those css properties to do with visual design : x (left), y (top), width and height
       These are displayed on the right
    B- those css properties which are part of the theme file for this component
       so if for instance the theme file has a line like this: "theme01.buttons.generic.fontWeight: bold", then this will result in a modifier on the screen, where 'bold' can be changed.  <to do>
    C- the remainder of the 280 css properties (280 minus A minus B). 
       Those are either not important, have passable default values or are only seldomly important for styling of the particular component. <to do>
    Using components ultimately translates into html. 
    Taking this example : 
    <img class="img" id="Component38" src="images/buttons.jpg" name="button" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 350px; top: 10px; width: 82px; height: 34px; cursor: pointer;"> Styling through css results in modifying the style attribute (.. style="visibility: etc...)
    Still debating whether the other attributes should be part of the styling process.
    In the demo above for instance I've assigned the 'src' attribute of images to a random unsplash picture, instead of making it directly editable. Its not a css property after all.
    Demo here (alpha version)
     
  17. Like
    lynkfs got a reaction from IElite in styling   
    Styling of visual elements.
    There are some 280 separate css properties which can be used to style each and every visual dom element. The following statement produces such a list
    var styles := browserapi.window.getComputedStyle(Panel.handle); (280 entries) If we disregard the properties which have a vendor prefix (-webkit, -moz etc) we still end up with some 230 properties for each visual element. 
    And although many of these have default property values, the complexity is just too great to do styling on this level.
    Each and every css framework (including Smart) tries to reduce the complexity by a number of measures
    group css properties into meaningful categories,  give these groups meaningful names and  possibly also rename individual properties connect groups to visual components making sure that all groups adhere to application wide values (i.e. primaryBlue is set to "#ebf8ff" in all groups) eliminate annoying differences in how browsers interpret universal css do this either static and/or dynamic in code The way the above is implemented varies wildly across frameworks.
    Two examples :
    Smart does its standard styling mainly by grouping classes on the level of components (1), giving them component names like .TW3Button (2,4). Every visual component gets this class on creation with 2 additional classes for background and border (4).
    So the resulting styling of a button gets defined by <button class="TW3Button TW3ButtonBackground TW3ButtonBorder" ...
    The supplied css templates in the Templates directory have a feature which implements application wide setting (5), so f.i. const 'EdgeRounding' will be set to "4px" in all components which use 'EdgeRounding (3)'. 
    There also some settings which standardise browser behaviour (6)
    Furthermore the rtl has methods to manipulate all classes, groups, properties and stylesheets in code (SmartCL.Css.Classes, SmartCL.Css.Stylesheet (7)
    Tailwind CCS. 
    This framework is sort of on the other side of the spectrum. It groups css properties in a large numer of very small groups (1,2,3). These groups are almost atomic in nature, so f.i. the 'm-0' class is specified as { margin: 0 }.
    It basically does not do any of 4,5,6 or 7
    So implementing a button using Tailwind involves many classes :
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" which gives a light blue button with rounded corners, becomes a bit darker on hover, has its caption in bold white and has specific values for top/bottom and left/right padding. 
    Nevertheless people build beautifully styled apps and websites using Tailwind. And it is readily accessible through code (7)

    Thinking about how to merge the best features of the above into a styling design mechanism, I would look at doing this :
    1- define a (limited) number of atomic groups. An atomic group combines and names simple css properties which can be used in more complex structures. Candidates are : colors, borders, backgrounds and fonts to start off with. The font-category f.i could be sub-grouped in 'families', 'sizes', 'weights', 'letterSpacings' and 'lineHeights'.  As an example the 'lineSpacings' subgroup could then be specified as
      "letterSpacings": {     "tighter": "-0.05em",     "tight": "-0.025em",     "normal": "0",     "wide": "0.025em",     "wider": "0.05em",     "widest": "0.1em"   }  
    2- define generic building blocks or component groups, i.e a generic button, using as many of the atomic groupings from 1) as possible
    "button": {      "cursor": "pointer",      "fontSize": "100%",      "lineHeight": "inherit",      "backgroundColor": { "colors" : "primary" },      "border": "none",      "color": "white",      "fontWeight": "bold",      "borderRadius": { "radii" : "default" },      ":hover": {        "backgroundColor": { "colors" : "primaryHover"},     } This uses the atomic 'colors' group, which will have entries like
        "primary": "#2b6cb0",     "primaryHover": "#2c5282" which then will be substituted in the above, so f.i. the 'backgroundColor' entry will become "backgroundColor": "#2b6cb0"
    This way all groups who use 'colors:primary' will have the same colour value, application-wide.
    3- define descendants from the component groups from 2), f.i. a rounded pill button, which only has those properties different from its parent
      "buttons": {
          "pill": {
            "borderRadius": "full",
           }
        }
    So far the above examples boil down to something like this after substitution (2) and child creation (3) 
    theme01.colors.primary: #2b6cb0 theme01.colors.primaryHover: #2c5282 theme01.buttons.generic.cursor: pointer theme01.buttons.generic.fontSize: 100% theme01.buttons.generic.lineHeight: inherit theme01.buttons.generic.backgroundColor: #2b6cb0 theme01.buttons.generic.border: none theme01.buttons.generic.color: white theme01.buttons.generic.fontWeight: bold theme01.buttons.generic.borderRadius: 0.25rem theme01.buttons.generic.hover.backgroundColor: #2c5282 theme01.buttons.generic.pill.borderRadius: 9999px and a class invocation like <button class="buttonPill ..."  (or even TW3Button)

    4- generate this into a regular css template and include the modernize.css entries to harmonize across browsers as well.
    5- integrate this in the visual designer
    Actually the style generation should closely follow the hierarchy in the visual designer. So if there is a rounded-button on a panel on a form on the <body> element, then the css property values should reflect the #body, #form, #panel, #generic-button and #rounded-button properties (and nothing more).
    just an idea, but doable
  18. Like
    lynkfs got a reaction from Daniel Eiszele in Scroll form   
    I'm not a js guru , or maybe
    Anyway, logically it's either the form or the memo which reacts to scrolling/swiping.
    If you want both, you need to be able to distinguish in which situations you want either to happen.
    There are various ways to do this, one of them this : as soon as the form scrolls, disable scrolling on the memo altogether. Then you need to determine under which condition you want to re-enable scrolling the memo. One way would be to have a simple onclick handler
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   self.NativeScrolling := true;   self.onscroll := lambda W3Memo1.NativeScrolling := false; end;   W3Memo1.OnClick := lambda W3Memo1.NativeScrolling := true; end; demo here 
    Instead of having to click the memo, it would be better to do this automatically when form-scrolling has ended. There is no such thing as a scroll-end event, but you could simulate one by using a timer of say 1000 ms. Reset the timer continuously during form scrolling and re-enable memo scrolling after the timer fires.
    This may work for you, but it is hooking into some finely tuned browser actions, so look out for surprises.
     
     
  19. Like
    lynkfs got a reaction from IgorSavkic in styling   
    Styling of visual elements.
    There are some 280 separate css properties which can be used to style each and every visual dom element. The following statement produces such a list
    var styles := browserapi.window.getComputedStyle(Panel.handle); (280 entries) If we disregard the properties which have a vendor prefix (-webkit, -moz etc) we still end up with some 230 properties for each visual element. 
    And although many of these have default property values, the complexity is just too great to do styling on this level.
    Each and every css framework (including Smart) tries to reduce the complexity by a number of measures
    group css properties into meaningful categories,  give these groups meaningful names and  possibly also rename individual properties connect groups to visual components making sure that all groups adhere to application wide values (i.e. primaryBlue is set to "#ebf8ff" in all groups) eliminate annoying differences in how browsers interpret universal css do this either static and/or dynamic in code The way the above is implemented varies wildly across frameworks.
    Two examples :
    Smart does its standard styling mainly by grouping classes on the level of components (1), giving them component names like .TW3Button (2,4). Every visual component gets this class on creation with 2 additional classes for background and border (4).
    So the resulting styling of a button gets defined by <button class="TW3Button TW3ButtonBackground TW3ButtonBorder" ...
    The supplied css templates in the Templates directory have a feature which implements application wide setting (5), so f.i. const 'EdgeRounding' will be set to "4px" in all components which use 'EdgeRounding (3)'. 
    There also some settings which standardise browser behaviour (6)
    Furthermore the rtl has methods to manipulate all classes, groups, properties and stylesheets in code (SmartCL.Css.Classes, SmartCL.Css.Stylesheet (7)
    Tailwind CCS. 
    This framework is sort of on the other side of the spectrum. It groups css properties in a large numer of very small groups (1,2,3). These groups are almost atomic in nature, so f.i. the 'm-0' class is specified as { margin: 0 }.
    It basically does not do any of 4,5,6 or 7
    So implementing a button using Tailwind involves many classes :
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" which gives a light blue button with rounded corners, becomes a bit darker on hover, has its caption in bold white and has specific values for top/bottom and left/right padding. 
    Nevertheless people build beautifully styled apps and websites using Tailwind. And it is readily accessible through code (7)

    Thinking about how to merge the best features of the above into a styling design mechanism, I would look at doing this :
    1- define a (limited) number of atomic groups. An atomic group combines and names simple css properties which can be used in more complex structures. Candidates are : colors, borders, backgrounds and fonts to start off with. The font-category f.i could be sub-grouped in 'families', 'sizes', 'weights', 'letterSpacings' and 'lineHeights'.  As an example the 'lineSpacings' subgroup could then be specified as
      "letterSpacings": {     "tighter": "-0.05em",     "tight": "-0.025em",     "normal": "0",     "wide": "0.025em",     "wider": "0.05em",     "widest": "0.1em"   }  
    2- define generic building blocks or component groups, i.e a generic button, using as many of the atomic groupings from 1) as possible
    "button": {      "cursor": "pointer",      "fontSize": "100%",      "lineHeight": "inherit",      "backgroundColor": { "colors" : "primary" },      "border": "none",      "color": "white",      "fontWeight": "bold",      "borderRadius": { "radii" : "default" },      ":hover": {        "backgroundColor": { "colors" : "primaryHover"},     } This uses the atomic 'colors' group, which will have entries like
        "primary": "#2b6cb0",     "primaryHover": "#2c5282" which then will be substituted in the above, so f.i. the 'backgroundColor' entry will become "backgroundColor": "#2b6cb0"
    This way all groups who use 'colors:primary' will have the same colour value, application-wide.
    3- define descendants from the component groups from 2), f.i. a rounded pill button, which only has those properties different from its parent
      "buttons": {
          "pill": {
            "borderRadius": "full",
           }
        }
    So far the above examples boil down to something like this after substitution (2) and child creation (3) 
    theme01.colors.primary: #2b6cb0 theme01.colors.primaryHover: #2c5282 theme01.buttons.generic.cursor: pointer theme01.buttons.generic.fontSize: 100% theme01.buttons.generic.lineHeight: inherit theme01.buttons.generic.backgroundColor: #2b6cb0 theme01.buttons.generic.border: none theme01.buttons.generic.color: white theme01.buttons.generic.fontWeight: bold theme01.buttons.generic.borderRadius: 0.25rem theme01.buttons.generic.hover.backgroundColor: #2c5282 theme01.buttons.generic.pill.borderRadius: 9999px and a class invocation like <button class="buttonPill ..."  (or even TW3Button)

    4- generate this into a regular css template and include the modernize.css entries to harmonize across browsers as well.
    5- integrate this in the visual designer
    Actually the style generation should closely follow the hierarchy in the visual designer. So if there is a rounded-button on a panel on a form on the <body> element, then the css property values should reflect the #body, #form, #panel, #generic-button and #rounded-button properties (and nothing more).
    just an idea, but doable
  20. Like
    lynkfs got a reaction from Daniel Eiszele in styling   
    Styling of visual elements.
    There are some 280 separate css properties which can be used to style each and every visual dom element. The following statement produces such a list
    var styles := browserapi.window.getComputedStyle(Panel.handle); (280 entries) If we disregard the properties which have a vendor prefix (-webkit, -moz etc) we still end up with some 230 properties for each visual element. 
    And although many of these have default property values, the complexity is just too great to do styling on this level.
    Each and every css framework (including Smart) tries to reduce the complexity by a number of measures
    group css properties into meaningful categories,  give these groups meaningful names and  possibly also rename individual properties connect groups to visual components making sure that all groups adhere to application wide values (i.e. primaryBlue is set to "#ebf8ff" in all groups) eliminate annoying differences in how browsers interpret universal css do this either static and/or dynamic in code The way the above is implemented varies wildly across frameworks.
    Two examples :
    Smart does its standard styling mainly by grouping classes on the level of components (1), giving them component names like .TW3Button (2,4). Every visual component gets this class on creation with 2 additional classes for background and border (4).
    So the resulting styling of a button gets defined by <button class="TW3Button TW3ButtonBackground TW3ButtonBorder" ...
    The supplied css templates in the Templates directory have a feature which implements application wide setting (5), so f.i. const 'EdgeRounding' will be set to "4px" in all components which use 'EdgeRounding (3)'. 
    There also some settings which standardise browser behaviour (6)
    Furthermore the rtl has methods to manipulate all classes, groups, properties and stylesheets in code (SmartCL.Css.Classes, SmartCL.Css.Stylesheet (7)
    Tailwind CCS. 
    This framework is sort of on the other side of the spectrum. It groups css properties in a large numer of very small groups (1,2,3). These groups are almost atomic in nature, so f.i. the 'm-0' class is specified as { margin: 0 }.
    It basically does not do any of 4,5,6 or 7
    So implementing a button using Tailwind involves many classes :
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" which gives a light blue button with rounded corners, becomes a bit darker on hover, has its caption in bold white and has specific values for top/bottom and left/right padding. 
    Nevertheless people build beautifully styled apps and websites using Tailwind. And it is readily accessible through code (7)

    Thinking about how to merge the best features of the above into a styling design mechanism, I would look at doing this :
    1- define a (limited) number of atomic groups. An atomic group combines and names simple css properties which can be used in more complex structures. Candidates are : colors, borders, backgrounds and fonts to start off with. The font-category f.i could be sub-grouped in 'families', 'sizes', 'weights', 'letterSpacings' and 'lineHeights'.  As an example the 'lineSpacings' subgroup could then be specified as
      "letterSpacings": {     "tighter": "-0.05em",     "tight": "-0.025em",     "normal": "0",     "wide": "0.025em",     "wider": "0.05em",     "widest": "0.1em"   }  
    2- define generic building blocks or component groups, i.e a generic button, using as many of the atomic groupings from 1) as possible
    "button": {      "cursor": "pointer",      "fontSize": "100%",      "lineHeight": "inherit",      "backgroundColor": { "colors" : "primary" },      "border": "none",      "color": "white",      "fontWeight": "bold",      "borderRadius": { "radii" : "default" },      ":hover": {        "backgroundColor": { "colors" : "primaryHover"},     } This uses the atomic 'colors' group, which will have entries like
        "primary": "#2b6cb0",     "primaryHover": "#2c5282" which then will be substituted in the above, so f.i. the 'backgroundColor' entry will become "backgroundColor": "#2b6cb0"
    This way all groups who use 'colors:primary' will have the same colour value, application-wide.
    3- define descendants from the component groups from 2), f.i. a rounded pill button, which only has those properties different from its parent
      "buttons": {
          "pill": {
            "borderRadius": "full",
           }
        }
    So far the above examples boil down to something like this after substitution (2) and child creation (3) 
    theme01.colors.primary: #2b6cb0 theme01.colors.primaryHover: #2c5282 theme01.buttons.generic.cursor: pointer theme01.buttons.generic.fontSize: 100% theme01.buttons.generic.lineHeight: inherit theme01.buttons.generic.backgroundColor: #2b6cb0 theme01.buttons.generic.border: none theme01.buttons.generic.color: white theme01.buttons.generic.fontWeight: bold theme01.buttons.generic.borderRadius: 0.25rem theme01.buttons.generic.hover.backgroundColor: #2c5282 theme01.buttons.generic.pill.borderRadius: 9999px and a class invocation like <button class="buttonPill ..."  (or even TW3Button)

    4- generate this into a regular css template and include the modernize.css entries to harmonize across browsers as well.
    5- integrate this in the visual designer
    Actually the style generation should closely follow the hierarchy in the visual designer. So if there is a rounded-button on a panel on a form on the <body> element, then the css property values should reflect the #body, #form, #panel, #generic-button and #rounded-button properties (and nothing more).
    just an idea, but doable
  21. Like
    lynkfs got a reaction from jarto in app   
    I usually don't publish commercial apps on the forum.
    Here is the exception : a retail shopping experience - made with Smart
  22. Like
    lynkfs got a reaction from jarto in proxy   
    I recently came across the javascript proxy object
    A proxy object basically monitors a chosen object, and has getter and setter functions which are invoked when the value of any of its properties changes (set) or are being accessed (get).
    In that respect there are some similarities with the mutationobserver object.
    In the below code snippet a regular EditBox has its text saved in a proxied object, which triggers the setter function
    and a Button which reads that value and thus triggers the getter function
    procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   var Edit1 : TW3EditBox := TW3EditBox.Create(self);   Edit1.SetBounds(32,40,300,32);   var Button1 : TW3Button := TW3Button.Create(self);   Button1.SetBounds(340,40,200,32);   Button1.Caption := 'read';   var Memo1 : TW3Memo := TW3Memo.Create(self);   Memo1.SetBounds(32,90,300,100); //set proxies   var MyProxy  : variant := new JObject;   var ProxyObj : variant := new JObject;   ProxyObj.EditBox1 := 'initialvalue';    //will be overwritten   Edit1.OnInput   := lambda MyProxy.EditBox1 := Edit1.Text; end;      //triggers 'handler.set'   Button1.OnClick := lambda Button1.Caption := MyProxy.EditBox1; end; //triggers 'handler.get'   var handler : variant := new JObject;   handler.get := function(obj, prop: variant): variant   begin     Memo1.Add('someone read ' + prop + ' as ' + obj[prop] + #10);     result := obj[prop];   end;   handler.set := function(obj, prop, value: variant):variant   begin     Memo1.Add('someone set ' + prop + ' to ' + value + #10);     obj[prop] := value;     result := true;   end;   asm     @MyProxy = new Proxy(@ProxyObj,@handler);   end; end;  
    This mechanism could f.i. be used to bind form components with a data source, updating them automatically when data changes or is being accessed
    Demo
     
  23. Like
    lynkfs got a reaction from jarto in gestures   
    Every now and then I read something so refreshing, that it jolts the old braincells out of its rut.
    10/GUI describes a new gui system concept which does away with keyboards and mice, relying on gestures instead
    Fun, and somehow very convincing
    Recently I needed a mechanism to cycle through an image carousel. There are different ways of doing this, but for touch enabled devices I opted for a one-finger swipe gesture.
    W3Image.OnSwipeLeft := procedure(sender: TObject) begin ... end;
    The implementation of OnSwipeLeft below is somewhat project specific, but includes the following
    - it is made a property of all customcomponents descendants (helper construct)
    - it extends TNotifyEvent, so it does not only return the sending object (sender: TObject), but also the event object (event: variant). That way OnSwipeLeft has access to properties of both (and handles all event-types)
    - it checks if the device has touch capabilities and uses the touch-api if that is the case, otherwise it uses pointerevents
    Demo project  (covering all swipe orientations. Verbose code, can be simplified)
    The touch-api supports multi-touch events to a certain maximum. My phone can handle 5 fingers.
    That makes it possible to implement for instance two-finger swipes to slide-in the next form.
    Form.OnFormSwipeRight := lambda Application.GotoForm('Form2', feFromRight); end;  
     
     
  24. Like
    lynkfs reacted to IgorSavkic in gestures   
    Looks interesting, I think it could be implemented as partial class and added to RTL just like with MouseTouch.pas
  25. Like
    lynkfs reacted to jarto in TW3StringGrid is available   
    It's not a good idea to try to hack this by using GetChildById and changing styles. Using BackgroundThemes is one way of doing it. But if you want complete control over how a cell is rendered, make your own column class from TW3StringGridTextColumn and override the GenerateHtml -function in it. That way you can use every trick there is in css.
    For example:
    TW3MyCoronaColumn = class(TW3StringGridTextColumn) public function GenerateHtml(GridLine: TW3RenderedGridLine; ColumnIndex, CurrentLeft: Integer): String; override; end; ... function TW3MyCoronaColumn.GenerateHtml(GridLine: TW3RenderedGridLine; ColumnIndex, CurrentLeft: Integer): String; begin var ItemId:=Grid.Handle.id+'_row_'+IntToStr(GridLine.DataIndex)+'_col_'+IntToStr(ColumnIndex); GridLine.ItemIds.Add(ItemId); result:='html code for the cell'; end; You can have a look at how TW3StringGridTextColumn generates the html as an example.
×
×
  • Create New...