Jump to content


Popular Content

Showing content with the highest reputation since 08/04/2014 in Posts

  1. 7 points

    TW3StringGrid is available

    A new update is available in the development-channel of SmartUpdate. It contains a completely new Grid: TW3StringGrid. It's a completely new design, which was made to be very fast and able to handle lots of data. This is achieved through dynamic drawing: Only the visible grid lines are rendered.ο»Ώ There is a new StringGrid-demo in the Featured Demos. It downloads a json file and populates the grid with data from it. TW3StringGrid supports six different column types: Text (column class TW3StringGridTextColumn) Numeric (column class TW3StringGridNumericColumn) EditBox (column class TW3StringGridEditColumn) ComboBox (column class TW3StringGridComboColumn) Checkmark (column class TW3StringGridCheckmarkColumn) Button (column class TW3StringGridButtonColumn) Columns are created by calling: MyNewColumn:=Grid.AddColumn(ColumnClass); If no ColumnClass is given, a Text column is created. Column properties: Caption Width BorderType Default is btLightBorderRight. Set to btNone if you don't want any vertical borders. Backgroundtype Default is btNone to let the line color through. AlignText (The same way as in TW3Label) taLeft (default) taCenter taRight SelectOptions (for TW3StringGridComboColumn only) This is an array of selectable values. First value (index 0) is the value for no selection. For example: SelectOptions:=['','First','Second','Third']; RowBorderType: Default is btLightBorderBottom. Set to btNone if you don't want any horizontal borders. RowBackgroundType: Default is bsListItemBackground. Set to bsDecorativeListItemBackground (or test other backgrounds) to change the background style for even rows. RowOddBorderType: Default is btLightBorderBottom. Set to btNone if you don't want any horizontal borders. Affects odd rows. RowOddBackgroundType: Default is bsListItemBackground. Set to bsDecorativeListItemBackground (or test other backgrounds) to change the background style for odd rows. Grid properties: RowCount: Set number of rows to show FixedColumns: How many columns should be fixed (aka not scrollable horizontally) LineHeight MultiSelect Events: OnCellClick: Is triggered when the row is clicked. OnCellChanged: Is triggered when cell content changes through editing (for example: through editing) OnDrawGridLineTheme: Can be used to set custom backgrounds and borders for a row. (for example, set background to bsErrorBackground for lines that should be highlighted to the user) Methods: InvalidateGrid: Triggers a repaint of the grid. Sort Sorting TW3StringGrid supports sorting based on one of multiple columns. Clicking on the column headers sets or reverses sort order. You can also control sorting in code: procedure AddSortColumn(Index: Integer; SortOrder: TW3SortOrder = soNormal); overload; procedure AddSortColumn(Col: TW3StringGridColumn; SortOrder: TW3SortOrder = soNormal); overload; procedure SetSortColumn(Index: Integer; SortOrder: TW3SortOrder = soNormal); overload; procedure SetSortColumn(Col: TW3StringGridColumn; SortOrder: TW3SortOrder = soNormal); overload; procedure ToggleSortColumn(Index: Integer); For example: Grid.SortOptions.AddSortColumn(3,soReverse); //Set primary sort column Grid.SortOptions.AddSortColumn(1); //Add secondary sort columns Grid.Sort; Working with sorted data When the Grid is sorted, it only affects how data is shown. Sorting does not change Grid data itself at all. When you work with a sorted grid events and indexes (for example SelectedIndex) always refer to the index in the grid data itself and NOT the visible line number. TW3StringGrid uses these events: TW3StringGridColumnEvent = procedure(const Sender: TW3StringGridColumn; const Row: Integer); TW3StringGridEvent = procedure(const Sender: TObject; const Row, Col: Integer); If you have a Grid where "Australia" is on line 2, SelectedIndex:=2 selects that row regardless of how the grid is sorted. Clicking on that row or changing data on that row also returns Row=2 regardless of how the grid is sorted.
  2. 7 points
    Updated 5/2019 to add: Now online here: Paintball Net --- Back in the mid-90's, my brother created what he called an "action MUD". He called it "Paintball Net". PBN used ANSI text terminal commands to create a combined roll-and-scroll and animated text experience. ! (exclamation points) were trees, _ (underscores) were grass, ^ (up carets) were mountains, and more. Your "avatar" was a "Y" (uppercase y) and enemies were * (asterisks). You used MUD-like commands to move around the world and target your paintball gun and buy and sell and more. I wish I had a screenshot of what the first version looked like, but you'll just have to imagine. πŸ˜ƒ He asked me to create the GUI for the game. That was 1996, and I had just purchased Delphi 2 and wrapped up my first "learn Delphi" project. This sounded like a good next step, and "PBTerm" was born. I *do* have screenshots of that. Oh, yeah. Love those 16-color Windows graphics. πŸ˜ƒ During the years it was online, the game evolved and upgraded to look more like this: We were never going to win any awards, but we had a very devoted player community. πŸ˜ƒ Paintball Net was online from the summer of 1996 through the summer of 2000, when we took it offline to focus on other projects. The game was never huge, but we had thousands of players come through over that period. And since then, every year at least a few of the players have reached out and asked/begged/pleaded/demanded if we were going to put it back online. There really wasn't much chance of the original version going back online. The original server, written in ANSI C for Linux had proven rather fragile, and was a big part of why we took the game offline. It was taking hours every day just to keep it up and running, in addition to time spent managing the community of players. On top of that, a hard drive incident in 2008 had cost me the source code of a number of core third-party/modified components for PBTerm. This past summer, though, I realized I might be able to make the game live again using Smart Mobile Studio. I would do a straight port of the ANSI C server to Smart Pascal using Node.JS and WebSocket, and I would rebuild the PBTerm client as a browser-based client. I'm not going to say it was *easy*, but it has been a lot of fun. ANSI C converts to Pascal without a lot of painful gyrations. And Node.JS seems a LOT more flexible, stable, and powerful than the TCP sockets approach we were using before. Also, game development is a lot easier when you already own all the graphic and audio resources. πŸ˜ƒ This is the server "in action"... Recreating the client has been more complicated. 20+ years of Delphi habits had to be adapted to the new reality of SMS and a browser-based UI. But that's coming together too. It might be obvious, but I'm not targeting this game at mobile. Paintball Net needed a mouse and keyboard in 1996, it's gonna need a mouse and keyboard in 2018. Also, I've made as few modifications to the gameplay as possible. I really wanted to bring back the original as much as I could. Today I got the handful of sound effects integrated, which was easier than I expected. I'm planning to start testing soon. I just need to line up a server to use and find a few volunteers. I'm excited. This would never have happened without Smart Mobile Studio. So I figured I would share. Merry Xmas! -David
  3. 6 points
    Hi all, as you have probably noticed, we've been a bit quiet lately. That's because we've been hard at work on exciting new changes and features to Smart Mobile Studio. I'd like to share with you, what we're doing to the Visual Designer. We're rewriting the Visual Designer from scratch. This new one is completely WYSIWYG as it's actually a Smart Mobile Studio app itself, running inside the IDE in Embedded Chrome. This lets us accurately show what components look like, instead of the dull old boxes in the old designer. You can set Align, Anchors, Angle and any other property and they are accurately reflected in the component in the Designer - like the blue panel (Align Top) or the rotated List box. The new designer also lets us support non-visual components, which will add a number of new components into our component palette. TW3Timer was the first one to be added and it already works just like in Delphi. We'll also be able to expand the concept into a full featured Multi Device Designer, so you could design forms for multiple devices, multiple screen sizes and multiple themes. The Designer also has support for Wizards and Component specific designers, so you'll be able to design popup menus in a Menu Designer and all the tabs in the Tab Control.
  4. 6 points
    You know the feeling when everything makes sense and everything is easy and you get a lot done? That's where I've been today. I cleaned up the IDE code and removed the old designer and the live preview-code, which never worked. Then Snap to grid and export to code and XML. The old designer's popup menu had many functions, but apparently half of them were never finished. So, while there is an undo there, it never worked. Same thing with aligns. Now I'm working on cut/copy/paste. While testing, I noticed that the old designer does not check for duplicate names, if you paste a component with child components. Also, you can't paste into another child component, like another panel. Weird thing is also, that the clipboard is cleaned after every paste. And even weirder is, that the old IDE is not using a global clipboard. Wonder why? So I ended up fixing those bugs in the old designer, as I get to use most of the underlying functionality with the new designer as well. Also wrote a static TW3URL-class for nice handling of url parameters. Life is so good in the zone πŸ™‚
  5. 6 points

    Development updates

    New update is available in the development-channel: IDE: Anchors (and other sets) can be set in the Object Inspector Improvements to generated Form implementation code: Wait until created components are ready before setting properties and creating children Set Anchors last Prevent conflicting keyboard shortcuts from being saved Use better default project options (no manifests, do not embed Javascript) Add new units to units defaults and improve formatting RTL: Add xml-js to Libraries. New ECMA.Promise unit from api docs System.JSON: Support for adding/setting JSON arrays Add TJSONObject.Delete Set form size to 100% before calling InitializeObject Add missing units to SmartCL.Controls Setting control's angle did not work in Firefox Don't raise an exception while freeing a form which is not registered Themes: default styles were not applied for all elements in Android and iOS Fixes a bug where textarea's size is too big in TW3Memo Note, that while the anchors can be set in the Object Inspector, it does not result in visible changes in the Visual Designer.
  6. 6 points

    menu component

    Probably not the most inspiring topic, but just sharing a menu component I needed for some project. There are quite a few css based menu components around, but I wanted to have a pure smart one. This one has an unlimited number of submenus and is instantiated as a hamburger menu on a toolbar Hamburger := TCHMenu.Create(self); //nodes : id,parent,description,procedure Hamburger.Add('root','','Hamburger menu'); //root Hamburger.Add( 'projects','root','Projects'); Hamburger.Add( 'project0','projects','New project'); Hamburger.Add( 'project1','projects','Open project'); Hamburger.Add( 'project2','projects','Delete project'); Hamburger.Add( 'designers','root','Designer'); Hamburger.Add( 'designer0','designers','Domain info',clickproc); //<== execproc Hamburger.Add( 'designer1','designers','Data development'); ... Hamburger.Add( 'designer5','designers','Goal Metrics'); //some test cases below Hamburger.Add( 'test','project0','test3'); // test level 3 Hamburger.Add( 'designer5','designers','Goal Metrics'); //test on double entries Hamburger.Add( 'test0','root','test',procedure begin writeln('clickproc'); end); //test on no children Hamburger.Add( 'designer6','designers','extra test'); //test on out of order demo and project files on the .../Menu subdir
  7. 6 points

    Smart Mobile Studio 3.0 is released!

    One year of hard work, lots of changes, new features and bug fixes. But here it is! Release announcement: https://smartmobilestudio.com/2018/07/20/smart-mobile-studio-3-0-released/ Most of you have already been using beta-versions, that are really, really close to this release. The last bit was to fix the cors-problem in the Images and to add the Smart Desktop source code to the Featured Demos. We've also set up a live demo of the Desktop to showcase what Smart Mobile Studio and JavaScript can do: desktop.smartmobilestudio.com Big thanks to everybody here, who have helped by testing, suggesting improvements and reporting bugs.
  8. 5 points
    We are proud to release Smart Mobile Studio 3.9.1. This is the first Alpha release of the upcoming Smart Mobile Studio 4.0. The biggest improvement is the new WYSIWYG Designer, which takes developing visual applications in SMS to a new level. The compiler was updated with a new version of DWS (Delphi Web Script) and the IDE uses an updated Chromium component. On top of this, there are new components and improvements to the RTL....... Please read the release notes here: https://smartmobilestudio.com/2019/12/20/smart-mobile-studio-3-9-1-released/
  9. 5 points
    Daniel Eiszele

    NodeJS Route Manager

    The attached uRouter.pas file is something I've been playing with for the last couple of days. It is basically a route manager (think ExpressJS) for Smart Mobile Studio. It isn't fully baked at this point in time and I probably won't progress it far past this point as it already meets my needs from a REST Service perspective. Feel free to use it as a base for your own efforts though. It works in both http and https environments. Just create a global var on your NodeProgram object to hold it and instantiate it after the server object is created with the following call: FRouter := TRouteManager.Create(FServer); Do not manually set the request handler as the constructor sets that up for you. To create routes, use the following syntax: FRouter.get('/', procedure (const Request: TNJHttpRequest; const Response: TNJHttpResponse; data : TRequestData; data : TRequestData) begin Response.end('You Selected the Root Directory'); end); FRouter.post('/', procedure (const Request: TNJHttpRequest; const Response: TNJHttpResponse; data : TRequestData; data : TRequestData) begin Response.end('You Selected a post request'); end); FRouter.put('/', procedure (const Request: TNJHttpRequest; const Response: TNJHttpResponse; data : TRequestData; data : TRequestData) begin Response.end('You Selected a put request'); end); FRouter.delete('/', procedure (const Request: TNJHttpRequest; const Response: TNJHttpResponse; data : TRequestData; data : TRequestData) begin Response.end('You Selected a delete request'); end); To access data when it's available, you may manually retrieve it from the request object or use one of two functions on the data / TRequestdata object as per below. data.BodyAsBuffer(procedure (_Buffer : JNodeBuffer) begin Response.End('Here is the buffer: ' + _Buffer.toString('utf8'); end); or data.BodyToFile('C:\temp\aFile.pdf',procedure(err : JError) begin if assigned(err) then begin Response.end('Something went wrong'); end else begin Response.end('File has been saved'); end end); Similarly, url parameters can be read off the TRequestdata object as follows. Response.end('You requested id: ' + data.Params.ReadString('id')); Let me know what you think uRouter.pas
  10. 5 points
    2nd Alpha release of the upcoming version 4.0 is now available. You can install it with SmartUpdate from the ALPHA-channel. If you have installed the first alpha release, you can simply run SmartUpdate in the same folder. Designer: If a component is added by just clicking, it's created at 96x32 size Prevent deletion of form IDE: Disable adding of units to uses-section when a form is opened Required units will still be added when a component is added to the form Add a message if the IDE's internal server can not start Bug fix to sorting of forum posts on the Welcome Page Small bug fix to Switch Comment
  11. 5 points
    Things are looking good. Today I finished fixing timing-related bugs in the IDE. Those were a bit tricky as creating and freeing Embedded Chrome in Delphi is not the simplest thing in the world. The rest of the day went deep inside the RTL. There were some timing issues with TW3CheckBox. If it's Resize was done while the parent was not visible, the resize went south. I ended up solving that by creating a new version of TW3CheckBox, which uses only CSS for resizing. It made the control a lot simpler, faster and bullet proof. My work list is getting seriously short now, which means that the Alpha-version is really close.
  12. 5 points

    New Visual Designer for Smart Mobile Studio

    Let me also write about the progress I'm making with the designer. I got it running inside embedded Chrome in the IDE last night. I'm very excited with everything you can do with this new designer and what it lets us do in the future. Let's start with a small geeky detail. Look at those dots in the form. They are the standard 8px grid that Delphi and Lazarus also have. Two for-loops plotting dots on an html5-canvas? No, no, no, this is made with pure CSS and you can use this same code in your own apps as well: DesignerForm.Handle.style['background'] := 'linear-gradient(90deg, #EFEBE7 7px, transparent 1%), linear-gradient(#EFEBE7 7px, transparent 1%), #000000'; DesignerForm.Handle.style['background-size'] := '8px 8px'; DesignerForm.Handle.style['background-position'] := '1px 1px'; What is lovely is that you can see what every property does. Like you can see in the image above, I changed the ThemeBorder for the label to flat. So, you can change the theme border and background for every control and see what it looks like. Set AlphaBlend to True and adjust Opacity and you'll see the result. All the other properties are also supported: Color, Angle, BorderRadius, you name it. And it's really cool that you can change functional properties and see what the control did. Fill in the Items of a ListBox and you can see how they are drawn. This one will also unlock the secrets of TW3EditBox. Look at the picture below. The slider down there is just a TW3EditBox with itRange as InputType. The panel with the angle has three sub panels and are using Align-properties to position themselves. Inside the middle panel is a TW3ListBox where the background Opacity is adjusted. Hence, you see the blue color from the background getting through. If the background had been an image, you'd see it as well. I've been testing and drawing forms for a while now and it's really great fun. I hope I can get this finished soon so I can share the fun with you guys too.
  13. 5 points

    HTML programming

    A bit of a weird topic name for a product which elevates Object Pascal using javascript. I could have named it 'Web Components' (as I did a while ago) or maybe better 'Api Programming'. There are a number of organisations which look after the web api's. W3C is the most known one, but there are others looking after specific parts (WhatWG, Khronos etc.). What they have in common is that they publish the web api's they're looking after in a specific formal format (WebIDL). Smart has transformed these specifications in corresponding pascal classes, contained in the APIs directory in the RTL. These classes basically map directly onto functions and data available in the browser itself, and using them is sort of similar to for instance incorporating Microsoft Office functions in a Delphi program (although technically very different from using COM). The RTL uses some of these api's under the hood, this post is about a mini-rtl if you like consisting of just 1 component. I use this approach a lot for non-form based projects, single-page-apps (or multiple) and websites. Demo of a semi-static SPA The inheritance chain of the DOM looks like - EventTarget - Node - Element - HTMLElement with all the various html elements underneath : HTMLDivElement for a <div>, HTMLAnchorElement for a <a> etc. The code below wraps the HTMLElement, but allows its constructor to create any of the specialised sub-elements. unit JElement; interface uses W3C.HTML5, W3C.DOM, W3C.CSS; type THTMLElement = class private FHTMLElement: JHTMLElement; public constructor Create(element: String; parent: THTMLElement; className: String; innerHTML : String); end; implementation { THTMLElement } constructor THTMLElement.Create(element: String; parent: THTMLElement; className: String; innerHTML : String); begin // cache element and set innerHTML and className FHTMLElement := JHTMLElement(Document.createElement(element)); FHTMLElement.innerHTML := innerHTML; FHTMLElement.className := ClassName; If parent = nil then document.body.appendChild(FHTMLElement) else parent.FHTMLElement.appendchild(FHTMLElement); end; HTMLElements have as specifiers 'attributes' and 'properties'. Properties are any of the values in the 'style' attribute : in the html expression style="left=10px; color=white;" href="#" both 'left' and 'color' are properties, and both 'style' and 'href' are attributes. So we need to add the setters for these parameters : Procedure THTMLElement.SetProperty(s1: String; S2: String); begin var FElementStyle := JElementCSSInlineStyle(FHTMLElement).style; FElementStyle.setProperty(S1, S2); end; Procedure THTMLElement.SetAttribute(S1: String; S2: String); begin FHTMLElement.setAttribute(S1, S2); end; The first part of the demo app is an image, which fills the complete viewport on any device and orientation, a slightly darker tinted bar on top, and a button in the bar linking to an email form further down. This header can be constructed as per below : unit JHeader; interface uses JHTMLElement, smartcl.system; type THeader = class(THTMLElement) public constructor Create(props: Variant); end; implementation { THeader } constructor THeader.Create(props: Variant); begin //hero image var hdr0 := THTMLElement.Create('div', nil, 'div'); hdr0.setProperty('width', '100%'); hdr0.setProperty('height', '100%'); hdr0.setProperty('min-height', '100vh'); hdr0.setProperty('background-image','url('+props['header-img']+')'); hdr0.setProperty('background-size','cover'); //top bar 100px var hdr11 := THTMLElement.Create('div', hdr0, 'div'); hdr11.setProperty('width', '100%'); hdr11.setProperty('height', '100px'); hdr11.setProperty('background-color','rgb(21,21,21,0.2)'); //contact button var hdr21 := THTMLElement.Create('a', hdr11, 'div', 'Contact'); hdr21.SetBounds(20,30,75,35); hdr21.setProperty('color', 'white'); hdr21.SetAttribute('type', 'button'); hdr21.setAttribute('href', '#contact'); hdr21.SetProperty('background-color', 'rgb(21,21,21,0.2)'); which can be instantiated as in (with a variable for the image) //THeader props['header-img'] := 'assets/img/blueprint.jpg'; THeader.Create(props); and which on execution time resolves in <div class="div" style="width: 100%; height: 100%; min-height: 100vh; background-image: url(; background-size: cover;"> <div class="div" style="width: 100%; height: 100px;"> <a class="div" type="button" href="#contact" style="visibility: visible; display: inline-block; position: absolute; overflow: auto; left: 20px; top: 30px; width: 75px; height: 35px; color: white;"> Contact </a> </div> </div> Hence the title of this post : HTML programming. The advantage of this approach is that once there are a number of these multi-purpose constructs like THeader available, making up another webpage is as easy as providing values for constructor parameters and stringing them together. At some stage I created the constructs listed here They need to be updated and streamlined a bit.
  14. 5 points


    I recently revisited the console object, available in all browsers, and discovered some newbies I didn't know about. The methods and properties below may be sometimes useful for debugging purposes. The console object is tied to the global window object, so access either by browserapi.window.console.log('whatever'); or define a reference to the window object : var window external 'window': variant; and call like window.console.dirxml(self); Some console methods are wrapped in SmartCL.System (like writeln ea) and SmartCL.Console The console.dirxml() above gives an xml representation of any object, which is way better than the 'object Object' result of a console.log(some object) instruction For performance testing, console also offers native timers console.time() console.timeLog() console.timeEnd() The above respectively initiate, report and destroy a named timer. The maximum nr of concurrent timers is 10,000 (!) To see how this works I created a stock new visual project with a single form and no visual controls. A timer was inserted at the beginning of the generated js projectfile, at the initialization of the form and at the ApplicationStarting, InitializeForm, InitializeObject and Resize events. Results : console.timeLog top of main.js : 0.0009765625 ms Initialization 10.574951171875 ms applicationstarting 17.234130859375 ms InitializeObject 21.23095703125 ms ReSize 62.385986328125 ms ReSize 101.25 ms InitializeForm 225.994140625 ms ReSize 226.757080078125 ms Wondering about the multiple ReSize calls and the relatively large time lag between InitializeObject and InitializeForm. There is another feature built into browsers re performance : the window.performance object with its own api's The most useful methods of this object are performance.now(), which gives a high res timestamp, and performance.toJSON() which gives a json representation of the attributes of the performance object Below the 'console.timer' and 'performance.now' results of a minimum project with 1 form and no visual controls in the native framework : console.timeLog performance.now() top of main.js : 0.001953125 ms / 118.28000005334616 timestamps Initialization 2.16186523437 ms / 120.43999996967614 InitializeForm 3.61083984375 ms / 121.92000006325543 InitializeObject 4.32080078125 ms / 122.65000003390014 and some values for the attributes of the performance object itself (console.log(window.JSON.stringify(window.performance.toJSON()));) timeOrigin : 1564383720413.3972 timestamps navigationStart: 1564383720413 redirectStart: 0 redirectEnd: 0 fetchStart: 1564383720414 domainLookupStart: 1564383720418 domainLookupEnd: 1564383720418 connectStart: 1564383720418 connectEnd: 1564383720418 secureConnectionStart: 0 requestStart: 1564383720418 responseStart: 1564383720423 responseEnd: 1564383720426 unloadEventStart: 1564383720432 unloadEventEnd: 1564383720432 domLoading: 1564383720435 domInteractive: 0 domContentLoadedEventStart:0 domContentLoadedEventEnd: 0 domComplete: 0 loadEventStart: 0 loadEventEnd: 0
  15. 5 points

    UI layout

  16. 5 points

    async / await keywords

    look this following procedure: procedure main; begin Sleep(5000); console.log('A'); Sleep(3000); console.log('B'); end; when we run this code, it will immediately output 'A' and 'B' - JS the asyncronous nature. ...but I want our program to log 'A' after 5 seconds then log 'B' after 3 seconds... that why we, we need the async / await native keywords in SMS. In a "synchronous" manner. Await and Async are just syntactic sugar for working with promises. It would be cool if we can declare a method like: procedure main; async; The compiler can check whether the encompassing function has async() in then and emit expectd JS, for instance: (async function(){ })(); Yeah, we need to to wrap it inside an async function and add "await" before the method "Sleep" to work in synchrounous manner.ther await keyword native in SMS would be nice. await Sleep(5000); I found out an ugly workaround for the "Immediately Invoked Function Expression (IIFE) - async function" to work at SMS. { global functions } procedure async(fn: Variant = nil); external '(async function(){' ; procedure &end; external '})'; function await(promise: Variant): JPromise; external 'await '; function Sleep(ms: Integer): JPromise; begin Result := JPromise.Create( procedure(resolve,reject : TCallBack) begin window.setTimeout( procedure() begin console.log('Done waiting'); resolve(ms); end, ms); end); procedure main; begin async; await( Sleep(5000) ); console.log('A after 5s'); await (Sleep(3000)); console.log('B after 3s'); &end; end; end; the compiler will emit expected JS at least: function main() { (async function(){(null); await (Sleep$1(5000)); console.log("A after 5s"); await (Sleep$1(3000)); console.log("B after 3s"); })(); }; but it would super awesome if we could // define procedure as async function procedure main: async; procedure main; begin await Sleep(5000); console.log('A after 5s'); await Sleep(3000); console.log('B after 3s'); end;
  17. 5 points

    Development updates

    New update available: RTL: TW3ListMenu: Add Items-property, so items can created in Object Inspector. Add OnSelected-event. IDE: Bug fix to renaming of units Show form source instead of designer when form is selected from Project Manager
  18. 5 points

    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.
  19. 5 points

    Font Demo

    in project options add a webfont (see https://jonlennartaasenden.wordpress.com/2017/10/04/webfonts-in-smart-mobile-studio/) W3Memo1.Font.Name := 'Tangerine'; W3Memo1.Font.Style := [fsItalic]; W3Memo1.Font.Size := 48; W3Memo1.Font.Weight := 'bold'; In this case I added Font 'Tangerine' (see also https://developers.google.com/fonts/docs/getting_started)
  20. 5 points
    Daniel Eiszele

    Google Maps API

    It took quite a bit of trial and error due to some case sensitivity errors but I finally have a working google map which utilises Google's javascript API. I know this has been touched on in other topics on the forum but none of them had a full blown working example; so I include this here for those who come after! Just create a new Visual Components project and replace everything in the form1 unit with the below code. Note that you will also need to obtain an API Key from Google (https://developers.google.com/maps/documentation/javascript/) and replace the dummy one in the code below. Note that I have only included a few partial classes and objects as far as the maps API is concerned but hopefully this is enough to get newcomers started. As an aside, the reference section on the above linked URL is very helpful in getting things up and running. unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Elements, W3C.HTML5, W3C.DOM, SmartCL.Require, System.JSON; type JMap = partial Class external; JLatLngLiteral = record Property lat : Double; Property lng : Double; end; JMapOptions = record property Zoom : Integer; property Center : JLatLngLiteral; end; JMarkerOptions = record property position : JLatLngLiteral; property map : JMap; property title : String; end; JMarker = partial class external 'google.maps.Marker' Public Constructor Create(options:JMarkerOptions); external 'Marker'; end; JMap = partial class external 'google.maps.Map' public Constructor Create(mapDiv:JElement; options : JMapOptions); external 'Map'; end; TForm1 = class(TW3Form) private {$I 'Form1:intf'} FMap : JMap; FMapDIV : TW3DIVHtmlElement; protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; procedure InitMap; end; implementation { TForm1 } procedure TForm1.InitializeForm; begin inherited; end; procedure TForm1.InitializeObject; var APIKey : String; begin inherited; {$I 'Form1:impl'} FMapDIV := TW3DIVHtmlElement.Create(Self); FMapDiv.SetBounds(5,5,450,400); //Enter a valid API Key here. It can be obtained from //https://developers.google.com/maps/documentation/javascript/ APIKey := 'YOUR API KEY HERE'; Require(['https://maps.googleapis.com/maps/api/js?key=' + APIKey],procedure() begin InitMap; end); end; procedure TForm1.Resize; begin inherited; end; procedure TForm1.InitMap; var LUluru : JLatLngLiteral; LMapOptions : JMapOptions; LMarkerOptions : JMarkerOptions; LMarker : JMarker; LMapElement : JELement; begin LUluru.lat := -25.3444; LUluru.lng := 131.0369; LMapOptions.Zoom := 13; LMapOptions.Center := LUluru; LMapElement := Document.getElementById(FMapDIV.Handle.id); FMap := JMap.Create(LMapElement,LMapOptions); LMarkerOptions.position := LUluru; LMarkerOptions.map := FMap; LMarkerOptions.title := 'Woo Hoo!'; LMarker := JMarker.Create(LMarkerOptions); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  21. 5 points

    Database Examples

    Sorry for the delay we are working non-stop on the next update, so its hectic! First, let me explain a bit where we are, so we can pick the right storage mechanism: The past months we have focused solely on the visual aspect of the RTL. We had to start somewhere and since the visual aspect is what most people care about first, that seemed like the best place. So as you have no doubt seen, we have gone through the RTL with a fine tooth comb, removing things that didnt work, rewriting controls from scratch and testing each part to make sure that everything works. But that is just 1/3 of the path we are on. We are now moving into the non-visual aspects of the RTL. Here we have things like databases, non-visual controls (TComponent in Delphi and Lazarus), filesystems and ultimately node.js - which by nature is UI less and runs on the server (or the shell). We have also brushed up Phonegap and removed the wrongs we found there. The Phonegap API has not stood still and have evolved, so it was due for an overhaul. So that is a quick "status". We are completing 1/3 of the planned system, which covers the visual aspects. DB typically falls under "non-visual", with bindings back to visual later. DB for browsers, what is what? Modern browsers actually have 3 DB engines built into them: WebSQL IndexDB Microsoft Access (*) (*) Only supported by Microsoft and relies on COM (so not for mobile). It must be mentioned that WebSQL, which is the only really good option here, has been deprecated. But it wont vanish tomorrow. The W3C (world wide web consortium) is a bit like the vatican; they move in decades and code will linger in the codebase for ages before it changes. So I wouldnt worry too much about using WebSQL. Secondly, there are shim's for everything these days. A "shim" is a JS re-implementation of something. What a shim does is check if a feature is supported, and if it's not then the shim installs itself and delivers the missing functionality. In other words, you can safely use websql and just download a shim if suddenly google or firefox lacks it. Here is a nice shim for websql: https://github.com/agershun/WebSQLShim Then there is IndexDB. This is a no-sql database and is designed to store JS structures (or objects) in a typical name-value pair style. I think JS people like this a lot since they can easily stuff raw JS objects into it, using a string key. And while it does have its uses, its not a table/row/column solutions. And finally there is access. Which is a pure Microsoft solution, only runs in Edge or Internet Explorer and requires the onslaught of COM. This might have changed in the later versions of edge but either way - this is Microsoft only. So I doubt that is interesting. Thinking outside the box, Smart approach Facing this typical JavaScript mess (sorry but it's true, the browser wars were not kind to JS) I decided we needed to become independent. Browsers change all the time and JS have little standards to speak of compared to Delphi, C# or C++; So coming from Delphi JS will be hell to work with unless you do a long and serious study. Our role is in many ways to "tame" JS and force it to behave inside object pascal's rules. So we needed some structure, something that only change when we allow it! So that people can write programs without being afraid that it will vanish in six months. So we created two solutions: TW3Dataset for simple, single table work SQLite compiled from C to JavaScript Note: Some bugs sadly crept into TW3Dataset, but these have been fixed. So the next update will remedy the situation profoundly. The second option, SQLite, is awesome. It is a pure, 1:1 compile from the original C code, so it contains everything WebSQL has "and then some". The reason it havent been wrapped more rigidly is because it's destined to become a part of our "DataSnap" like API later. Like mentioned above we are on a journey here, and we have to deal with things in the right order. So each DB engine will be isolated in a "driver" like class and then register with a common API. That way people can use classes like TDatabase, TQuery, TStatement and similar high-level components to access any database they like. IndexDB is the oddball in all of this, so we might write a shim/proxy to leverage that. Picking an engine OK, with a situation rapport out of the way, lets pick a database. Our options are thus: WebSQL SQLite TW3Dataset Since TW3Dataset needs the update, I have excluded that. SQLite is awesome but could perhaps need more solid wrappers. So for this I will use WebSQL since that has more clearly defined classes. Picking a storage point Browsers only have cache storage. This is basically a sandboxed file that is saved in the cache folder. Under phonegap this is likewise sandboxed and stored in the "yourname.app/cache" folder if im not mistaken (not sure about that one, please check the Phonegap docs if you need the absolute path). But you dont need a path to load or save into the cache. The browser maps your data into the file-region automatically. Also worth mentioning: When you run in the browser you have a limit of 10-15 megabytes (depends on the browser) for cache storage. When you link the project with Phonegap this limit goes away and you have the same storage rights as native applications. So you dont need to worry about running out of space if you plan to link with phonegap. For pure browser work, you are not expected to make huge databases - but rather cache info before shipping that to a server. A bit like what you would use TClientDataset for under Delphi. But ok, let's use normal cache storage for now. WebSQL is excellent because this will store itself automatically! You dont need to do anything in particular to load or save it. It all boils down to you creating or dropping the database. DB example Right! So fire up Smart and create a new visual project. Our first business is to create the database object and I typically put that into the application class. That way it can be accessed by any form in the program. A nice helper function is also good to have. Here is how it looks so far: unit Unit1; interface uses Pseudo.CreateForms, System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms, SmartCL.Application, SmartCL.DbSql, Form1; type TApplication = class(TW3CustomApplication) private FDatabase: TW3WebSQLDatabase; public procedure ApplicationStarting; override; published Property Database: TW3WebSQLDatabase read FDatabase; end; // global easy-access function to DB layer function Datastore: TW3WebSQLDatabase; implementation function Datastore: TW3WebSQLDatabase; begin Result := TApplication(Application).database; end; procedure TApplication.ApplicationStarting; begin //setup database engine FDatabase:=TW3WebSQLDatabase.Create; FDatabase.DBName:={$I 'app:name'}; FDatabase.DBDescription:='Database for ' + {$I 'app:name'}; FDatabase.DBSize:=(1024 * 1024) * 4; inherited; end; end. This creates a websql database that can hold 4 megabytes of data. Which in browser terms is huge (I mean, you are not doing your taxes here are you). Then there is the main form and actually making something happen. So drop a TW3Button on the form and set the title to "Create Database". We then need some code to create a table and insert some records. For brewity here is the whole code for Form1: unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.DbSql, SmartCL.Controls.Button; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation { TForm1 } uses unit1; procedure TForm1.W3Button1Click(Sender: TObject); var Transaction: TW3WebSQLCustomTransaction; begin // activate database try if not Datastore.Active then Datastore.Active := True; except on e: exception do begin writeln("Failed to activate database: "); writeln(e.message); exit; end; end; // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin // Create our table try Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get new write transaction if Datastore.GetWriteTransaction(Transaction) then begin try // Populate the table with some records for var x := 1 to 10 do begin var MyData := 'John Doe #' + x.ToString(); Transaction.Execute("insert into customers (name) values (?);",[MyData]); end; finally if Transaction.LastFailed then WriteLn(Transaction.LastError); Transaction.Free; end; end else raise Exception.Create('Failed to create write transaction error'); // Get a new read transaction if DataStore.GetReadTransaction(Transaction) then begin // Setup the OnSuccess event handler. Transaction.OnSuccess := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); if Reader.Dataset <> nil then begin var Dataset := Reader.Dataset; for var x := 0 to Dataset.rows.Length - 1 do begin var Row := Dataset.rows.item(x); WriteLn(Row['name']); end; end; // All done, kill the transaction Reader.free; end; // Setup the OnFailed event handler TransAction.OnFailed := procedure (Sender: TObject) begin var Reader := TW3WebSQLReadTransaction(Sender); writelnF("Read transaction failed: %s", [reader.LastError]); // Kill the transaction Reader.free; end; // OK lets execute the SQL Query! try Transaction.Execute("select * from customers;",[]); finally if Transaction.LastFailed then WriteLn(Transaction.LastError); end; end; end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end. Now you are going to notice something -- namely that the database is persistent (!) So if you run this example 10 times, it will actually create 10 new records every time. The key here is the SQL that creates the database: Transaction.Execute("create table if not exists customers (id integer primary key asc, name string);",[]); This basically says "Create this database UNLESS it already exists". Manual storage with SQLite I am bit pressed for time right now. I will be away from the office for about a month pending surgery. I will be busy with forum duty etc. as much as I can, but sadly a secondary example with SQLite will have to wait a little. But I hope this one has given you an easy path to store things in the cache. Also remember: You can look at the data using the Browser Devtools I will do an article on SQLite when time allows
  22. 5 points

    New forum software version

    So far working fine for me - an improvement over the previous forum. BTW I am loving the activity of users and admins in the forum. Hopefully it will continue to grow.
  23. 4 points


    I usually don't publish commercial apps on the forum. Here is the exception : a retail shopping experience - made with Smart
  24. 4 points
    New update is available. This is probably the last alpha release. RTL: Add TabOrder-property Make TW3Label, TW3Display and TW3DisplayView not focusable Set TabOrder for these if you want to override this TW3CheckBox and TW3RadioButton: Value can be changed with keyboard Allow changing of HRange and VRange in TW3SwipeController Improvements to DateTimeToLocal and LocalDateTimeToUTC IDE: Fix exception in code that monitors changes to RTL files. Prevent exception during startup if IDE's internal port is already in use.
  25. 4 points

    Database development

    First picture is from a manually constructed dataset. Second one is from sqlite-database. Doesn't look beautiful, but you can see data-aware versions of TW3Memo, TW3Label and TW3StringGrid. All connected to the same Data source.
  26. 4 points

    Performance due to compiler optimisation

    The other thing I noticed when comparing the same project generated by SMS3.0.0 and the latest alpha is a marked speed increase. I did some tests on the training phase of a neural network, with these results : SMS 3.0.0 : Training took 30810 milliseconds. SMS 3.1.9 : Training took 18937 milliseconds. SMS 3.0.0 : Training took 32611 milliseconds. SMS 3.1.9 : Training took 47272 milliseconds. SMS 3.0.0 : Training took 34233 milliseconds. SMS 3.1.9 : Training took 16627 milliseconds. SMS 3.0.0 : Training took 33177 milliseconds. SMS 3.1.9 : Training took 18275 milliseconds. SMS 3.0.0 : Training took 30762 milliseconds. SMS 3.1.9 : Training took 16734 milliseconds. SMS 3.0.0 : Training took ... milliseconds. SMS 3.1.9 : Training took 16062 milliseconds. Except for 1 outlier, the speed difference of the latest alpha is about on average 1.6 - 1.8 times faster This can only be due to compiler optimisation Impressive
  27. 4 points

    css styling

    Well, I've been thinking about how to support this as it'd be great to be able to use standard stylesheets from other frameworks. That way we could have access to a lot of ready made stylesheets. The one I had a look at earlier was Bulma. At the moment we do have background- and borderstyles like for example bsContainerBackground or btFlatBorder. What I'd like to test is to add a number of new styles and then create a mapping layer between them and the other frameworks. For example: Add a background like bsErrorBackground When a Bulma stylesheet is used, and bsErrorBackground is set, the mapping layer would translate that to Bulma's style: "is-problem" In the same way, bsContainerBackground could be translated to Bulma's "box" style etc.
  28. 4 points


    //------ typescript definition ------- /** Documentation: https://katex.org/docs/options.html */ export interface KatexOptions { /** * If `true`, math will be rendered in display mode * (math in display style and center math on page) * * If `false`, math will be rendered in inline mode * @default false */ displayMode?: boolean; /** * If `true`, KaTeX will throw a `ParseError` when * it encounters an unsupported command or invalid LaTex * * If `false`, KaTeX will render unsupported commands as * text, and render invalid LaTeX as its source code with * hover text giving the error, in color given by errorColor * @default true */ throwOnError?: boolean; /** * A Color string given in format `#XXX` or `#XXXXXX` */ errorColor?: string; /** * A collection of custom macros. * * See `src/macros.js` for its usage */ macros?: any; /** * If `true`, `\color` will work like LaTeX's `\textcolor` * and takes 2 arguments * * If `false`, `\color` will work like LaTeX's `\color` * and takes 1 argument * * In both cases, `\textcolor` works as in LaTeX * * @default false */ colorIsTextColor?: boolean; /** * All user-specified sizes will be caped to `maxSize` ems * * If set to Infinity, users can make elements and space * arbitrarily large * * @default Infinity */ maxSize?: number; /** * Limit the number of macro expansions to specified number * * If set to `Infinity`, marco expander will try to fully expand * as in LaTex * * @default 1000 */ maxExpand?: number; /** * Allowed protocols in `\href` * * Use `_relative` to allow relative urls * * Use `*` to allow all protocols */ allowedProtocols?: string[]; /** * If `false` or `"ignore"`, allow features that make * writing in LaTex convenient but not supported by LaTex * * If `true` or `"error"`, throw an error for such transgressions * * If `"warn"`, warn about behavior via `console.warn` * * @default "warn" */ strict?: boolean | string | Function; } export class ParseError implements Error { constructor(message: string, lexer: any, position: number); name: string; message: string; position: number; } /** * Renders a TeX expression into the specified DOM element * @param tex A TeX expression * @param element The DOM element to render into * @param options KaTeX options */ export function render(tex: string, element: HTMLElement, options?: KatexOptions): void; /** * Renders a TeX expression into an HTML string * @param tex A TeX expression * @param options KaTeX options */ export function renderToString(tex: string, options?: KatexOptions): string; //---------------- // ====>- pascal external class definition <==== unit katex; interface uses System.Types, W3C.HTML5, W3C.DOM; type TJSKatexOptions = class external 'Object' displayMode: Boolean; // nullable throwOnError: Boolean; // nullable errorColor: String; // nullable macros: Variant; // nullable colorIsTextColor: Boolean; // nullable maxSize: Float; // nullable maxExpand: Float; // nullable allowedProtocols: TStrArray; // nullable &strict: Variant; {Boolean or String or JFunction} // nullable end; type TJSKatex = class external 'katex' public class procedure render(tex: String; element: JHTMLElement); overload; external 'render'; class procedure render(tex: String; element: JHTMLElement; options: TJSKatexOptions); overload; external 'render'; class function renderToString(tex: String): String; overload; external 'renderToString'; class function renderToString(tex: String; options: TJSKatexOptions): String; overload;external 'renderToString'; end; type TJSParseError = class external 'katex.ParseError' constructor Create(const msg: String; lexer: Variant; position: Float); name: String; &message: String; position: Float; end; implementation end. //---------------------------------------------------------------------
  29. 4 points

    New Visual Designer for Smart Mobile Studio

    Time for my (almost) daily update on how the work with the new Designer is progressing. Today I started changing TW3TabControl, so I could use it in the Designer. New tabs can be added by selecting "New tab" in Designer's popup-menu. Then you can change properties in the Property Inspector and add child components into the tabs. This was one of my original big goals with the new designer and I'm really happy to see it become reality πŸ™‚
  30. 4 points
  31. 4 points

    Responsive Design

    Responsive design in essence consists of 2 measures : style all components as best as possible depending on screen size (larger buttons on mobile, proportional font sizing etc) tweak the layout of forms so that it works best on any given screen size 1) Screen size dependent styling can be done in code and/or is done as other frameworks do by including media queries in the stylesheet. Media queries basically activate different parts of a stylesheet depending on the actual screen size. Smart doesn't implement this in its shipped stylesheets, but there is no reason it can't be done. Just a bit of work. 2) Tweaking the form layout in Smart is possible by doing it in code depending on screensize and orientation - a bit of a pain really using the Layout Manager using some css grid framework or ui kit. For website development I've used the Frow framework quite a bit, but there are many others. using anchors and the CSS grid (which is basically the topic of this post) The new anchors are fantastic, but don't really help in responsive design. Browser window resizing doesn't proportionally resize anchored components. That is unless they are dimensioned depending on screen size beforehand : var W3Panel0 : TW3Panel := TW3Panel.Create(self); W3Panel0.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel0.Top := trunc(browserapi.window.innerHeight*0.05); W3Panel0.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel0.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel0.Anchors := [akLeft, akRight, akTop]; // var W3Panel1 : TW3Panel := TW3Panel.Create(self); W3Panel1.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel1.Top := trunc(browserapi.window.innerHeight*0.15); W3Panel1.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel1.Height := trunc(browserapi.window.innerHeight*0.70); W3Panel1.Anchors := [akLeft, akRight, akTop, akBottom]; W3Panel1.NativeScrolling := true; // var W3Panel2 : TW3Panel := TW3Panel.Create(self); W3Panel2.Left := trunc(browserapi.window.innerWidth*0.05); W3Panel2.Top := trunc(browserapi.window.innerHeight*0.85); W3Panel2.Width := trunc(browserapi.window.innerWidth*0.9); W3Panel2.Height := trunc(browserapi.window.innerHeight*0.10); W3Panel2.Anchors := [akLeft, akRight, akBottom]; As an example the code above produces a header (always on top), a footer (always on the bottom) and a scrollable body inbetween with some proportional margins. The good thing is it works well on all screen sizes, and even on orientation changes, without having to code anything else. Instead of using a grid system with media queries, or doing it in code, it is also possible to use the built-in css grid to get a responsive design. Much easier, see article. If I just plonk the css and html of the first example in that article into the structure above, this is how it ends up looking : Demo Resize browser window to see this in effect (or see it in the responsinator) Not the end-all of all possible responsive design options, but it may fit the bill sometimes. Note : the built-in chrome browser doesn't understand the css grid, so execute from file or server.
  32. 4 points

    UI layout

    For me the most frustrating aspect of using SMS is layouts. Putting together anything beyond a trivial UI takes ages and is very frustrating to achieve. The Tlayout is not intuitive, it has weird and unexplainable side effects and takes a long time to get it right. Once you have it working then generally it is fine. We really need to have a more intuitive system to allow easy to create and maintain UI. I believe simple anchor (left, top, right, bottom)m, Align and "alignWithMargin" like Delphi has would be a great bonus to SMS. I have no idea what would be involved, or how much work it would be, but I would love to see this being developed.
  33. 4 points

    UI layout

    The development-channel contains a new update that adds Align and Anchor-support in the RTL. We still have some work to do to get the Visual Designer and Object Inspector to support these properly. While we work on that, you can still use these by setting anchors in code. Align can actually be set in the Object Inspector, but do note that setting the alignment does not make any visual changes in the Visual Designer. I'm going to be travelling for a bit more than a week.. During that time I'll be able to participate in this forum but can't make any changes or bug fixes. If you happen to find situations when Anchors and Align does not work properly, please post examples. I'll be happy to have a look when I return from my trip.
  34. 4 points

    scrolling big numbers

    Got challenged the other day by trying to scroll large numbers of rows in a listbox. Browsers are very good at scrolling, but when row numbers become large and the scroll context becomes complex, scroll behaviour deteriorates. A simple un-optimised scrolling div is, rule of thumb, able to scroll comfortably up to a couple of hundred rows. Depending on browser and complexity. In the native framework I tweaked that a bit by setting all rows outside the visible viewport to 'display:none'. That extends comfortable scrolling somewhat, say to numbers in the low thousands. A better way is to completely separate display from content. Meaning that the content needs to be held in some kind of memory structure, not in visual components, and that scrolling is redefined to re-using a small set of visual rows confined to the scroll-window. A structure to make that happen would be a <div> like component (TW3Panel) on a form with NativeScrolling set to true. Set up 2 child-panels on this component, where the first child-panel has dimensions equal to the maximum scrollable area, and a second childpanel dimensioned equal to this parent. The first child-panel is the scroller, which will always be completely empty. The second child-panel contains the visible rows, which will be refreshed on scroll-events. To keep this panel always in view, set the position attribute to '-webkit-sticky'. This demo shows that Chrome can readily handle 500,000 rows, even on mobile. FF a bit less. The good thing is that the number of rows doesn't really matter, scrolling behaviour stays the same regardless. Would be interesting to see if performance gets even better by pushing scrolling to the GPU, pretty sure Chrome does that by default. Infinite scrolling will be easy to implement using this structure too. Code procedure TForm1.W3Button1Click(Sender: TObject); begin //data var column1 := TVariant.CreateArray; for var i := 0 to 500000 do begin column1.push(inttostr(i)); end; var column2 := TVariant.CreateArray; for var i := 1 to 500001 do begin column2.push(inttostr(i)); end; //column2.sort(); //sorting works too //scrolling setup var rowHeight := 30; W3Panel.NativeScrolling := true; //Parent panel (in visual designer : width 408, height 266) var Panel1 : TW3Panel := TW3Panel.Create(W3Panel); //scroller (always empty!) Panel1.SetBounds (0,0,380,column1.length*rowHeight); //but with large height Panel1.BorderRadius := 0; //set up viewport var Panel2 : TW3Panel := TW3Panel.Create(W3Panel); //viewport, only the visual rows Panel2.SetBounds(0,0,380,262); //dimensions same as grid-parent Panel2.BorderRadius := 0; Panel2.handle.style.position := '-webkit-sticky'; Panel2.handle.style.position := '-moz-sticky'; Panel2.handle.style.position := '-ms-sticky'; Panel2.handle.style.position := '-o-sticky'; Panel2.handle.style.position := 'sticky'; Panel2.handle.style.top := '0px'; //set up columns Var CPanel1 : TW3Panel := TW3Panel.Create(Panel2); //column 1 CPanel1.SetBounds(-2,-2,200,264); CPanel1.BorderRadius := 0; Var CPanel2 : TW3Panel := TW3Panel.Create(Panel2); //column 2 CPanel2.SetBounds(200,-2,200,264); CPanel2.BorderRadius := 0; //initial fill viewport prior to first onscroll event for var j := 0 to 8 do begin //only first couple of rows var x : TW3Panel := TW3Panel.Create(CPanel1); //column 1 x.SetBounds(-2,j*rowHeight-2,200,rowHeight); x.BorderRadius := 0; x.innerHTML := column1[j]; x.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end; // var y : TW3Panel := TW3Panel.Create(CPanel2); //column 2 y.SetBounds(-2,j*rowHeight-2,190,rowHeight); y.BorderRadius := 0; y.innerHTML := column2[j]; y.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end; end; //fill viewport while scrolling var c1 := CPanel1.handle.children; var c2 := CPanel2.handle.children; W3Panel.onscroll := procedure(sender:tobject) begin var d : integer := trunc(W3Panel.handle.scrollTop/rowHeight); for var k := 0 to 8 do begin c1[k].innerHTML := column1[d+k]; c2[k].innerHTML := column2[d+k]; end; //fall back : ide chrome browser and iOS don't handle 'sticky' very well, //in that case set viewport position manually if Panel2.handle.style.position <> 'sticky' then if not w3_getIsSafari then Panel2.top := W3Panel.handle.scrollTop; end; end; infinity scroll : add to end of W3Panel.onscroll procedure //infinity scroll : if W3Panel.handle.scrollHeight - W3Panel.handle.scrollTop = W3Panel.handle.clientHeight then W3Panel.handle.scrollTop := 0;
  35. 4 points

    Update embedded Chromium

    Hi guys, I've been a busy for a while working on updating the Embedded Chrome we use in the IDE. The one we've used so far has been from Henri Gourvest. Unfortunately it's not been updated for 3 years, but there's a new fork by Salvador Diaz Fau, which is actively updated: https://github.com/salvadordf/CEF4Delphi
  36. 4 points

    Quantum computing with Smart

    Is it possible to do a bit of quantum computing using SMS ? The answer looks to be yes, using IBM's quantum computer resources which are partly made available for developers Basics first : IBM has released it's SDK to program these quantum computers using a visual composer, Python, a proprietary assembler QASM format and some run and execute utilities. There is however a second way of using its quantum resources, using the entry-points of is Q-experience REST server. Most if not all of the visual composer functions can be done by ajax-calls ! To demonstrate : the first thing is to acquire an api-token. (Create an account on https://quantumexperience.ng.bluemix.net/qx/experience. Log in and navigate to the Composer. Username > My Account, and then Advanced on the upper right. Then generate API Token. Api Tokens are personal and are not supposed to be shared.) The next thing is to generate a session token (access token). This can be done in code. This screenshot shows a couple of these REST server calls and its results : The first button (get access) generates a session token The second button queries which quantum computers are available, how many qubits can be used, if it is online and what type of system it is. Current results show 3 quantum systems - 2 real ones and 1 simulator The third button (current Temp) gives some processor info of the selected system. In this case the current temperature in Kelvin : the Melbourne processor is cooled to just above the absolute minimum. Next : the next button to implement will be to submit some code to one of the available processors for processing. Below is the project code for these REST calls unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Net.Http, ECMA.Json, SmartCL.Controls.Button, SmartCL.Controls.Label, HTMLTableElement, SmartCL.Controls.ScrollBox, SmartCL.Controls.Panel, SmartCL.Controls.ComboBox; type TForm1 = class(TW3Form) procedure W3Button3Click(Sender: TObject); procedure W3Button2Click(Sender: TObject); procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; DBRows : integer := 0; FHttp : TW3HttpRequest; smscursor: variant; procedure GetAccessToken(Sender: TW3HttpRequest); procedure ListAvailableSystems(Sender: TW3HttpRequest); procedure GetTempKelvin(Sender: TW3HttpRequest); AccessToken : string; 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'} end; { Button1 } procedure TForm1.W3Button1Click(Sender: TObject); begin //Get Access Token var FHttp := TW3HttpRequest.Create; FHttp.OnDataReady := GetAccessToken; FHttp.open("POST",'https://quantumexperience.ng.bluemix.net/api/users/loginWithToken'); FHttp.RequestHeaders.Add("Content-type","application/json"); var api : variant := new JObject; api.apiToken := TString.encodeURIComponent('a59d26ebf........1d6d6d4a28c430b5336aba'); FHttp.send(json.stringify(api)); end; procedure TForm1.GetAccessToken(Sender: TW3HttpRequest); begin smscursor := JSON.parse(Sender.ResponseText); AccessToken := smscursor.id; W3Label1.Caption:= 'Access Token : ' + AccessToken; end; { Button2 } procedure TForm1.W3Button2Click(Sender: TObject); begin //List available Q systems var FHttp := TW3HttpRequest.Create; FHttp.OnDataReady := ListAvailableSystems; FHttp.open("GET",'https://quantumexperience.ng.bluemix.net/api/Backends?access_token=' + AccessToken); FHttp.RequestHeaders.Add("Content-type","application/json"); FHttp.send; end; procedure TForm1.ListAvailableSystems(Sender: TW3HttpRequest); begin var W3TableElement1 : TW3TableElement := TW3TableElement.Create(W3Panel1); //add 5 columns to the grid W3TableElement1.AddColumn('Name',200); //title, width W3TableElement1.AddColumn('Description',200); W3TableElement1.AddColumn('Qubits',50); W3TableElement1.AddColumn('System type',150); W3TableElement1.AddColumn('Status',50); smscursor := JSON.parse(Sender.ResponseText); W3ComboBox1.Clear; for var i := 0 to smscursor.length -1 do begin W3TableElement1.AddCell(i+1,1,smscursor[i].name); W3TableElement1.AddCell(i+1,2,smscursor[i].description); W3TableElement1.AddCell(i+1,3,smscursor[i].nQubits); if smscursor[i].simulator = true then W3TableElement1.AddCell(i+1,4,'quantum simulator') else W3TableElement1.AddCell(i+1,4,'quantum system'); W3TableElement1.AddCell(i+1,5,smscursor[i].status); W3ComboBox1.add(smscursor[i].name); end; W3Panel1.NativeScrolling := true; W3ComboBox1.OnChanged := procedure (Sender: TObject) begin writeln(W3ComboBox1.Items[W3ComboBox1.SelectedIndex]); end; end; { Button3 } procedure TForm1.W3Button3Click(Sender: TObject); begin //Get Temp var FHttp := TW3HttpRequest.Create; FHttp.OnDataReady := GetTempKelvin; //'https://quantumexperience.ng.bluemix.net/api/Backends/NAME/parameters?access_token=ACCESS-TOKEN'; FHttp.open("GET",'https://quantumexperience.ng.bluemix.net/api/Backends/' + W3ComboBox1.Items[W3ComboBox1.SelectedIndex] + '/parameters?access_token=' + AccessToken); FHttp.RequestHeaders.Add("Content-type","application/json"); FHttp.send; end; procedure TForm1.GetTempKelvin(Sender: TW3HttpRequest); begin If Sender.ResponseText = '{}' then begin W3Label3.Caption := 'Simulators are not cooled to (close to) absolute minimum'; end else begin smscursor := JSON.parse(Sender.ResponseText); if smscursor.fridgeParameters.Temperature.value = '' then W3Label3.Caption := 'Processor does not support Temp reading' else W3Label3.Caption:= 'Current Temp : ' + smscursor.fridgeParameters.Temperature.value + ' Kelvin'; end; end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end. In the end hopefully it will be possible to recreate the classic Battleships game using quantum computing, as described here
  37. 4 points


    Was looking for an easy to make wysiwyg editor. This one is based on 'document.designMode'. Mozilla: "When an HTML document has been switched to designMode, its document object exposes an execCommand method to run commands that manipulate the current editable region" IFrame's have an innate document element which can be used for that purpose. Have a form with an IFrameHtmlElement and 2 buttons ('bold' and 'italic') : procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components W3IframeHtmlElement1.handle.contentDocument.designMode := 'on'; W3IframeHtmlElement1.handle.contentDocument.body.innerHTML := 'this is some text'; W3IframeHtmlElement1.handle.focus(); end; procedure TForm1.W3Button1Click(Sender: TObject); //bold begin W3IframeHtmlElement1.handle.contentDocument.execCommand('bold', false, null); W3IframeHtmlElement1.handle.focus(); end; procedure TForm1.W3Button2Click(Sender: TObject); //italic begin W3IframeHtmlElement1.handle.contentDocument.execCommand('italic', false, null); W3IframeHtmlElement1.handle.focus(); end; Besides 'bold' and 'italic' execCommand supports a host of other edit commands as well. see https://codepen.io/chrisdavidmills/full/gzYjag/
  38. 4 points

    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;
  39. 4 points

    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.
  40. 4 points

    Development updates

    17.11.2018 RTL: Changes to handling of Cursor: Style definitions moved from basic html elements to control styles GetCursor and SetCursor can now be overridden Bug fixes to how many controls handle cursor. Especially TW3Label. Themes: Add missing styles TW3CheckBox, TW3CheckMark, TW3RadioButton, TW3RadioToggle and TW3RadioGroup Two new backgrounds: TW3DecorativeListItemBackground and TW3TransparentBackground RTL optimizations to creation of controls, GetBoundsRect, SetBounds, MoveTo and SetSize. Bug fix to SmartCL.Graphics.pas: Changing of canvas font, size and styles did not work. Bug fix to System.DateUtils.DecodeDate. IDE: Delete key works now Search dialog and other dialogs. Bug fix: Internal Browser Window showed only a white screen if Execute was clicked while it was open. Compiler: Now(), EncodeDate() and EncodeTime() returns now the same values as Delphi and FPC All time/date -functions fixed to work with the new TDateTime-values
  41. 4 points


    I'm sad to see Jon leave his baby like this, but I do respect his decision. What I can say is, that SMS is not dying here. I've been waiting a few weeks already to push a new update, but have not been able to get any answers from Jon, if he'd also want to include some changes. I believe things look worse to you guys than they really are. A lot of RTL work and especially bug fixes was done by me during the last year and almost all the IDE improvements and bug fixes by Primoz. Jon did pretty much all the NodeJS work, so we'll miss him most there. If I look at this optimistically, Jon's donating his shares to two young and talented Delphi programmers is a very nice thing and may give them a possibility to spend more time on SMS. But hey, hang on there and lets hope that this divorce will not be too ugly.
  42. 4 points
    I'd rather do it like Delphi and Lazarus: Keycode as a var parameter and setting it to #0 would cancel. However, that would break backwards compatibility of SMS code just as any other new parameters would do. If you guys don't see it as a big problem, we could do it in a future version. Let me know what your thoughts are.
  43. 4 points

    Scroll bar bug

    This was a weird bug that required changes in SynEdit code itself. May be a Delphi bug even. But I got it fixed and it will be in 3.0
  44. 4 points
    @rshotbolt Here are some resources that might help you out. I like the first one as it explains the precedence and how each overrides the others. Playing with the design 03.06.2013 https://smartmobilestudio.com/2013/06/03/playing-with-the-design/ Smart Mobile Studio and CSS: part 1 October 9, 2017 https://jonlennartaasenden.wordpress.com/2017/10/09/smart-mobile-studio-and-css-part-1/ Smart Mobile Studio and CSS: part 2 https://jonlennartaasenden.wordpress.com/2017/10/11/smart-mobile-studio-and-css-part-2/ Smart Mobile Studio and CSS: part 3 https://jonlennartaasenden.wordpress.com/2017/10/12/smart-mobile-studio-and-css-part-3/ Smart Mobile Studio and CSS: part 4 https://jonlennartaasenden.wordpress.com/2017/10/18/smart-mobile-studio-and-css-part-4/ Themes and styles https://smartmobilestudio.com/documentation/getting-started/themes-and-styles/ Working with controls, the boxing model 05.01.2012 https://smartmobilestudio.com/2012/01/05/working-with-controls-the-boxing-model/ Hidden stylesheets with Smart Pascal August 13, 2014 https://jonlennartaasenden.wordpress.com/2014/08/13/hidden-stylesheets-with-smart-pascal/ HTML5 Attributes, learn how to trigger conditional styling with Smart Mobile Studio November 8, 2017 https://jonlennartaasenden.wordpress.com/2017/11/08/html5-attributes-learn-how-to-trigger-conditional-styling-with-smart-mobile-studio/
  45. 4 points

    databases and applications

    This is taken into account with the framework we are researching / making now. The objects you use to access data act more as "front-ends" to the mechanisms behind it, and just like the filesystem classes the operations are all async. This has the benefit of decoupling the consumer-part (i.e controls + bindings) from the producer aspect. To the consumer parts the database can be resident - or on the other side of the planet, it cares not where the data originates, only that its made available in a format that it can use. A typical middleware entity would be a node.js server that is connected to X number of databases. The client will use the framework and connect to the node.js endpoint, but neither the controls or bindings will have any clue about the data originating from a server. It will simply see a driver and issue calls to it. When the driver (or class) gets the data it will validate and dispatch it via its internal mechanisms. The nice part about this is that, a datasource can be almost anything. As long as someone implements the 3 core classes, it can be anything from a hardcoded file, local database or remote data server for that matter. I will go into more detail about these classes when we are a little closer, but you are quite right that old-school 1:1 endpoints is useless in the new paradigm
  46. 4 points
    A new update is available (in both alpha- and beta-channels on SmartUpdate) aand it's a big one: IDE: - Added TStrings-support to Object Inspector, which allows editing of: - TW3ListBox.Items - TW3ComboBox.Items - TW3RadioGroup.Items - TW3TabControl.Tabs - NodeJS background executions are stopped when project is closed. - Bug fixes to OmniXML - Updated Datasnap proxy generator dll to latest - Fixed problem with Datasnap access interface importer RTL: - New control: TW3GoogleMaps - TW3TabControl can use Forms as Tab contents (Check TabForms -demo) - Updated node.js headers to version 8.10.0 LTS compatible with stable v9.8.0) - Isolated node.js EventEmitter in separate unit (NodeJS.Events) as per specification (node.js v6.9.1) - Implemented System.IOUtils with storage independent TPath class - Implemented a synchronous node.js filesystem API, TW3NodeSyncFileAPI class in SmartNJ.System - Implemented abstract directory parser (TW3DirectoryParser in System.IOUtils) - Implemented standard system file and folder functiction in SmartNJ.System - Upgraded our virtual, BTree based in-memory filesystem to use TPath - Changed TW3VirtualFileSystem to store data as TByteArray rather than variant - Implemented directory parser for Linux, Unix and Windows - Implemented abstract storage device driver (System.Device.Storage) - Implemented storage device driver for browsers (SmartCL.Device.Storage) - Implemented storage device driver for node.js (SmartNJ.Device.Storage) - Implemented TW3DirectoryWatch class for node.js (SmartNJ.Device.Storage) - Implemented TApplication object for node.js, exposing traditional properties and process info - Updated our message api (SmartCL.Messages) to support javascript messagechannel ports - Added full support for socket.io clients (SmartCL.Net.Socketio) - Updated TW3Memo control to initialize states for autocorrect, autocapitalize etc. - Added GetIsRunningInBrowser function to system.types - TStreamReader and TStreamWriter moved to System.Reader and System.Writer units. This deprecates the System.StreamReader and System.StreamWriter units. - Implemented new string parser functions for recognizing intrinsic types (TDataType in System.Types.Convert) - Updated TW3DatasetFilter to use new parser framework - Fixed string-to-intrinsic-type (TryStrToInt, TryStrToBoolean etc) functions - Full re-implementation of our parser framework (System.Text.Parser) - Partial class TBinaryData, under node has functions for consuming and emitting data as a node buffer - Fixed bug in our node.js http server (SmartNJ.Server.HTTP) where the response object would not release if an error occured, causing the server to drop the connection. - Cleaned up multiple declarations for JBuffer and JNodeBuffer, now isolated in unit nodejs.core - Changed default creation flags for node.js files to R+W as opposed to R+W+E (only affects Linux) - Fixed bug where TNJHttpRequest.GetHeaders function returned null - Removed use of JError under node where applicable, now use TJSErrorObject from system.types unit - Updated bytecode assembler and virtual machine project to latest - Removed TFileNameHelper class from SmartCl.Legacy unit, this is now handled by TPath in System.IOUtils - Updated codec manager (system.codec unit) - Implemented RC4 encryption codec and binding (System.Codec.RC4) - Updated Base64.js to latest revision (libraries/base64.js) which is added by the linker on demand - Full re-implementation of base64 codec (System.Codec.base64 unit) - Changed TString.ToBase64() function to use new codec - TW3GroupBox header fixed, now uses TW3Label rather than mapping the unreliable <legend> tag - Improved AutoCreateForm routine, this now performs better checking before construction - TW3Grid control now uses TW3ScrollControl as a container, adding momentum scrolling and better touch handling - Fixed important problem with TString helper class, function missing @ for result caused exception Demos: - Full reimplementation of our websocket server, replacing websocket.io with the standard ws package - Updated all node.js demos to use our high-level classes - Updated WebSQL demo to use correct names - Updated "Binary data" demo to use TBinaryData class (System.Memory.Buffer unit) - Fishfacts demo updated to use new functionality - Fixed problems with advanced demos (biotopia in particular) to work with latest RTL
  47. 4 points

    Google Maps API

    the google map, defaults to the roadmap view, so I added the MapTypeId to the code i posted above just add the following to the interface section const HYBRID = 'hybrid'; ROADMAP = 'roadmap'; SATELLITE = 'satellite'; TERRAIN = 'terrain'; JMapOptions = record property Zoom : Integer; property Center : JLatLngLiteral; property mapTypeId: String; end; change the following procedure to include the MapType param procedure InitMap(AAPIKey, ATitle: String; AMapType: String; ALat, ALong: Double; AControl: TW3CustomControl); and then in the implementation section, modify the require to include the mapTypeId option Require(['https://maps.googleapis.com/maps/api/js?key=' + AAPIKey],procedure() begin ... LMapOptions.mapTypeId:= AMapType; ... end); In my case, i needed my map to default to the TERRAIN view so, i used the TERRAIN const InitMap('myApiKey', Mountain, TERRAIN, Latitude, Longitude, FMap); e.g.
  48. 4 points

    Pagespeed ranking 98/100

    Googles PageSpeed tool (https://developers.google.com/speed/pagespeed/insights/) checks any website or webapp on various speedfactors, and returns a rating both for desktop and mobile. Most of these factors are easy to implement. Just tick the relevant minifying, packing, obfuscation boxes in the "Compiler-Code generation" section of the Project Options. The PageSpeed tool also flags un-optimised images and even optimises them to download. The more difficult option is the rule about "prioritising visible content". This is obviously difficult to do in regular SMS projects. Both the CSS and JS files generated by the compiler don't make any distinction between 'above' or 'below' the fold. To circumvent this, the following works for me : Get the generated HTML code for the first form. Easiest is execute the project in the IDE, go to the "Console section" in DevTools and type 'copy(document.body.innerHTML);' This copies the generated HTML to the clipboard. Insert this code in the HTML template in the top of the <body> section Insert the keyword 'async' in the <script> section where the main.js file is loaded This basically quickly renders the first page in HTML only and the user will see it pretty quickly. It won't do anything until the js file has loaded but that's generally ok. CSS files, unless really really big, usually don't pose a problem as they can fit into the initial congestion window (typically 14.6kB compressed) Doing this gives me a 98/100 rating both for mobile and desktop. The remaining 2% is about 'leveraging browser caching' which doesn't bother me too much. Haven't checked yet what the sms manifest file options may do to this rule.
  49. 4 points

    simple game

    Hi, there is simple game i've written for my children. Hope you enjoy this game too probably a code is not mastery but it works guessing game for children.7z
  50. 4 points

    simple game

    Either that or support this great product
  • Create New...