Jump to content


Popular Content

Showing content with the highest reputation since 07/03/2019 in all areas

  1. 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.
  2. 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 🙂
  3. 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.
  4. 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/
  5. 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
  6. 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
  7. 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.
  8. 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.
  9. 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.
  10. 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
  11. 4 points


    I usually don't publish commercial apps on the forum. Here is the exception : a retail shopping experience - made with Smart
  12. 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.
  13. 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.
  14. 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
  15. 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.
  16. 4 points
    Daniel Eiszele

    NodeJS Route Manager

    I am an engineering surveyor, currently working in an Electrical Design office providing "As Built" information to the Civil Construction Industry. The current iteration of this particular program is as an in house REST server, receiving mapping data (in SVG format) for inclusion in a POSTGRES database for later viewing in the browser. In a nutshell, it is a way for our office staff to tell the field staff what they need to do! The front end (my main application), is a hybrid Survey/Cad package written in Delphi; so all this web stuff is quite new to me. Having said that, I think I have all the pieces now to extend the backend significantly and provide functionality I didn't originally plan on. NodeJS really is a great platform and being able to access it with object pascal is icing on the cake. I'll upload my wrappers for POSTGRES and user Authentication too when I get them cleaned up a bit, as they may also be useful as a starting point for others. Note that coding is not my day job, but a means to an end, so the code and documentation may not (please read "will not" ) be great.
  17. 4 points
    With the help of Jarto and a few here I have almost 5000 lines of a Lazarus/Delphi library that implements a mature custom TCP protocol compiling with SMS with just a few {$IFDEF DWSCRIPT}'s required so far. I have about 1000 more line to go before I can give it a full test but I do have a Websocket connected to the library and have sent messages to the library from a Lazarus program and it received and decoded them correctly. I was only working on it for a few hours on and off yesterday so I am amazed and very happy. Hopefully it will all run correctly in the end but at least the compiler thinks it should. The key websites that have helped me get handle on thing are here but so far I have not used them directly, SMS has handled everything in the compile so far but I do have a few dynamic byte array that I need to mimic which will need this information.... https://jonlennartaasenden.wordpress.com/2017/06/04/smart-pascal-memory-and-pointers/ https://jonlennartaasenden.wordpress.com/2015/03/07/system-interop-how-to-for-smart-mobile-studio/ https://www.html5rocks.com/en/tutorials/webgl/typed_arrays/ The ultimate goal is to create phone apps Jim
  18. 4 points
    If everything goes according to plans, before Christmas. I can't tell you a fixed date as there is a little competition going on about which will be released first: The Alpha or my 3rd child 😀
  19. 4 points
    This week has been a bit slow as I've been sick. However, here's a little pic of the Menu Designer
  20. 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. //---------------------------------------------------------------------
  21. 4 points

    SMS and the Ionic Framework

    Hi Thomas, Thank you for an interesting idea. At the moment we are working on the new Visual Designer. Once it's finished, it's way easier to create beautiful and functional PWA apps without having to create all by code. So, the plan is: Finish the Designer and have it support all current Visual components. (90% ready) Add non-visual components to the component palette Release a version Once we have that out, we'll be able to plan our next steps.
  22. 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 🙂
  23. 4 points
  24. 4 points

    file system

    This code works (File system in the browser) create and mount a FileSystem create a directory write a file with some contents read the stats of this file read the contents of this file write another file list the directory contents in a memo Demo procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Script := browserapi.document.createElement('script'); Script.src := 'res/filer.js'; //https://raw.githubusercontent.com/filerjs/filer/develop/dist/filer.js browserapi.document.head.appendChild(Script); Script.onload := procedure begin writeln('filer loaded'); var Filer := browserapi.window.Filer; writeln('instance created'); var fs : variant := new JObject; asm @fs = new (@Filer).FileSystem({ name: "myfilesystem01", flags: [ 'FORMAT' ] }); end; writeln('filesystem created'); //mkdir fs.mkdir('/docs', procedure(err:boolean) begin if (err) then writeln('Unable to create /docs dir'); end); //write file fs.writeFile('/docs/firstDoc.txt', 'Hello World', procedure(err:boolean) begin if (err) then writeln('Unable to write /docs/firstDoc.txt'); end); //stats fs.stat('/docs/firstDoc.txt', procedure(err, stats:variant) begin if (err) then writeln ('Unable to stat /docs/first.txt') else writeln(stats); end); // Read file fs.readFile('/docs/firstDoc.txt', 'utf8', procedure (err:boolean; data:string ) begin if (err) then writeln ('Unable to read /docs/firstDoc.txt') else writeln(data); end); //write second file fs.writeFile('/docs/secondDoc.txt', 'Brave new world', procedure(err:boolean) begin if (err) then writeln('Unable to write /docs/secondDoc.txt'); end); //get reference to shell var sh : variant := new JObject; asm @sh = new (@fs).Shell(); end; // list directory, shallow listing sh.ls('/docs', procedure(err:boolean; entries: array of variant) begin if (err) then writeln ('Unable to read /docs directory') else begin W3Memo1.Text := '/docs' + #10; for var i := 0 to entries.length -1 do begin writeln(entries[i].name); W3Memo1.Text := W3Memo1.Text + ' ' + entries[i].name + #10; end; end; end); end; end; I like this a lot note : works from file or server, not in the ide
  25. 3 points


    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
  26. 3 points
    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
  27. 3 points

    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.
  28. 3 points

    https://peerjs.com/ - is this feasible?

    just as a proof of concept : tutor initiates conversation with student : https://lynkfs.com/Experiments/peer/receive/www and on another machine : https://lynkfs.com/Experiments/peer/send/www (must be on another machine) student sends a text by copying tutor id into the edit box and pressing buttons 1, 2 and 3 this text then arrives at the tutors project The user interface of these projects is really bad, sorry, just proof-of-concept. This demo is basically a simple chap app, but since it is using webrtc (and webrtc handles not only text but also other media), I feel that extending this to a full-fledged channel between parties should be feasible and reasonably straightforward
  29. 3 points

    Parsing unknown JSON (into TW3StringGrid)

    procedure TForm1.AnalyzeJSON(jsontxt: String); var jroot: TJSONObject; begin jroot:=TJSONObject.Create; jroot.FromJson(jsontxt); writeln('Items: '+IntToStr(jroot.Count)); jroot.ForEach(function (Name: string; Data: variant): TEnumState begin var jobj:=TJSONObject.Create(Data); var k:=jobj.Keys; writeln('Keys: '+IntToStr(Length(k))); for var a:=0 to Length(k)-1 do begin var key:=k[a]; Writeln(Format('Key %s has value %s',[key,jobj.Values[key]])); end; result:=esContinue; end); end;
  30. 3 points
    A new update is available. This one contains improvements for aligning TW3CheckBox and TW3Radiobutton, when word wrap is used. Huge thanks to @lynkfs and @Daniel Eiszele for their help with this! RTL: Change horizontal alignment to be done using flexbox styles. This affects mostly TW3CheckBox and TW3RadioButton, but also other controls that are using lbxcontent -styles. Improve right alignment of TW3CheckBox and TW3RadioButton
  31. 3 points
    New update is available. RTL: TW3HttpRequest: Improvements and bug fixes for handling downloading of binary data Bug fixes to TW3CheckBox and TW3RadioButton Word Wrap did not work Right and Center alignments did not work Disable cursor emulation in TStreamReader and TStreamWriter This allows using multiple readers or writers on same stream. If you need every stream reader and writer to have their own cursor, set roEmulateCursor Fix TW3Dataset saving and loading (both streams and JSON) Bug fix to TString.EncodeURL Bug fixes to handling of Opacity IDE: Fix default value for Opacity Designer: TW3CheckBox and TW3RadioButton support now all properties
  32. 3 points

    Waiting for controls to be ready

    Waiting for controls to be ready for action is an interesting topic, which resurfaces from time to time. It's pretty challenging as there is not one simple right way to handle the problem. Try this little test program. It creates a panel which has three labels unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, System.IOUtils, System.Device.Storage, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Device.Storage, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Panel, SmartCL.Controls.Label; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; TTestPanel = class(TW3Panel) protected procedure InitializeObject; override; procedure ObjectReady; override; end; implementation { TForm1 } procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Pan:=TTestPanel.Create(Self); WriteLn('Panel created'); Pan.SetBounds(0,0,200,150); WriteLn('Panel SetBounds done -> Creating Label3'); var Lab3:=TW3Label.Create(Self); Lab3.Caption:='Can you see me three?'; Lab3.SetBounds(10,70,150,30); WriteLn('All done'); end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; { TTestPanel } procedure TTestPanel.InitializeObject; begin inherited; WriteLn('InitializeObject called -> Creating Label'); var Lab:=TW3Label.Create(Self); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); WriteLn('InitializeObject done'); end; procedure TTestPanel.ObjectReady; begin inherited; WriteLn('ObjectReady called -> Creating Label2'); var Lab2:=TW3Label.Create(Self); Lab2.Caption:='Can you see me too?'; Lab2.SetBounds(10,40,150,30); WriteLn('ObjectReady done'); end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end. When you run this, we get the following debug: InitializeObject called -> Creating Label [line #472] InitializeObject done [line #472] Panel created [line #472] Panel SetBounds done -> Creating Label3 [line #472] All done [line #472] ObjectReady called -> Creating Label2 [line #472] ObjectReady done [line #472] All three labels are created but only two are visible. Fire up DevTools and you'll see that the first label (the one created at InitializeObject) does not have a size - even though we did use SetBounds on it. The reason this happens is inside of SetBounds where the code adjusts left, top, width and height according to the object's margins and parent's padding. The SetBounds happens too early, so the computed styles for the element return NaN as values. Now, there is a simple trick to fix this: Change w3_getPropertyAsInt to return 0 if the value is NaN. I'll surely do that but that won't solve the whole problem. If the styles actually had margins or the parent has padding, we will still move the control into wrong position. Another way to prevent this problem is to not create subcontrols inside InitializeObject and only do that in ObjectReady. And yes, this does work most of the time. However, once you start creating more and more complicated apps, the chance of you accessing or changing a not-initialized element increases. There are some ways to wait. You can actually create that 1st label inside InitializeObject lilke this: procedure TTestPanel.InitializeObject; begin inherited; WriteLn('InitializeObject called -> Creating Label'); var Lab:=TW3Label.Create(Self); TW3Dispatch.WaitFor([Lab], procedure begin WriteLn('Wait done!'); Lab.Caption:='Can you see me?'; Lab.SetBounds(10,10,150,30); end); WriteLn('InitializeObject done'); end; Here we create the Label early but wait for it to be ready before setting the propeties. However, code like this is not pretty, which made me think about various ways of solving this in a more beautiful way. When we've discussed this within the team, we've been thinking about using a Promise or a MutationObserver. But before we get there, lets have a look at when an element is actually ready for action: The code in SetBounds actually does properly check the parent's handle: if (Parent.Handle) then begin var PCSHandle:=TW3CustomBrowserAPI.GetComputedStylesFor(Parent.Handle); if (PCSHandle) then begin LLeft+=w3_getPropertyAsInt(PCSHandle,'paddingLeft'); LTop+=w3_getPropertyAsInt(PCSHandle,'paddingTop'); end; end; However, that does not stop paddingLeft from being returned as NaN for the 1st label. Testing it like this works: if (Parent.Handle) and (Parent.Handle.Ready) then Digging deeper, we'll find that TW3Dispatch.WaitFor checks also the display and visibility of the computed styles. One page I found ( https://browsee.io/blog/puppeteer-how-to-check-visibility-of-an-element/ ) even checks element.boxModel So, the challenge is to find a beautiful and simple way for us to wait for the objects to be ready. And as we need to check more thant just the Handle, I wonder if it's even possible to solve this with a Promise or a MutationObserver.
  33. 3 points
    I just pushed a new update to the alpha-channel. It finally fixes the bug we've had that has left SmartMS.exe and CEF4Delphi.exe hanging when the IDE is closed. It was a tricky one to track down. Happy to see it gone 🙂
  34. 3 points

    Debugging a NodeJS -app

    While debugging the httpsserver, I started wondering how NodeJS-apps are debugged. Here's how you do it with Chrome: 1. Open your NodeJS Project, go to the Project Options' Execution-tab and add "--inspect" to Parameters (Notice, that I added --inspect followed by a space before %output%) 2. Compile, Execute 3. Start Chrome and go to chrome://inspect 4. Click on "Open dedicated DevTools for Node" That's it. You can set breakpoints in the Sources-tab, inspect values etc.
  35. 3 points
    Congrats on the new Baby and the new release!
  36. 3 points
    Huge thanks in developing this version also go to Christian Budde, who is one of the original developers of Smart Mobile Studio. Christian helped with updating the DWS and the embedded Chromium component.
  37. 3 points

    License Serial Number invalid

    Just to mention it... I had this problem too. I never received an email with my new information. I was able to get it from clientarea.*, though. -David
  38. 3 points
    Well, I found a better solution: There will be two new properties that relate to the Anchors: AnchorGapRight AnchorGapBottom These are calculated and set by the Designer when Anchors are set. They represent the distance from bottom right corner of the component to the parent's bottom right corner. This is used by the RTL to resize the child component properly when Anchors are used. The great thing is that this makes handling Anchors a lot more simple and elegant. It will also work even if the form size happens to change while the form is being built.
  39. 3 points


    and this works too var html : variant; asm @html = katex.renderToString("c = \\pm\\sqrt{a^3 + b^3}", { throwOnError: false }); end; W3Panel1.InnerHTML := html;
  40. 3 points


    A quick solution : have a form with a panel and a button in the custom template add these lines <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.css" integrity="sha384-bsHo4/LA+lkZv61JspMDQB9QP1TtO4IgOf2yYS+J6VdAYLVyx1c3XKcsHh0Vy8Ws" crossorigin="anonymous"> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.js" integrity="sha384-4z8mjH4yIpuK9dIQGR1JwbrfYsStrNK6MP+2Enhue4eyo0XlBDXOIPc8b6ZU0ajz" crossorigin="anonymous"></script> in the onclick handler of the button add this code procedure TForm1.W3Button1Click(Sender: TObject); begin var katexelement : variant; katexelement := W3Panel1.handle; asm katex.render("c = \\pm\\sqrt{a^2 + b^2}", @katexelement, { throwOnError: false }); end; end; which results in this : This code can be improved a bit. F.i. I like to load the js library under program control, so at least you know when it is ready
  41. 3 points
    Technically, we are not moving away from Delphi. We are just creating one component (the designer) as an html5 app. The rest of the IDE will still be Delphi code. Eventually it could be possible to rewrite the whole IDE as an online application (+server) but that's not something we are attempting to do right now. When it comes to the new designer as an SMS app, we actually don't lose anything. As far as I can see, there are only advantages. There were many disadvantages in having the designer as a Delphi component. Getting the look right even with standard components was very hard. Supporting anything more complicated was impossible. When the Designer is an SMS app, we only have to create the component and it automatically looks right, no matter what theme is used or how complicated the component is or what properties it happens to have. The hard part is only the communication between the IDE and the new designer. That's done using Websockets and it already works well. So I can add a component in the new designer and it shows up in the IDE's component list and the Object Inspector. I can move the component and see the coordinates changing in the Object Inspector. I can change properties and they are reflected in the new Designer. And if I compile and run the app I drew in the new designer, well, it works. I can't tell yet, when I can push out the first alpha. There are still some things that need to be taken care of. For example, we need to finish updating the IDE to use a newer Chrome component. Also, I want to write a few editors to make sure that the new designer has propert support for more complicated controls.
  42. 3 points

    Development updates

    New update available in the development-channel: RTL: Improvements to TW3ScrollBox: AutoUpdate-property. If true, the scrollbox monitors changes in child components and updates itself automatically. ForceParent-property. If true, child components are automatically moved to the Content-component. Bug fixes to ToggleSwitch: Did not work properly on Firefox Value of Checked-property changed only after the animation was completed Parent can now be changed, so components can be moved to different parents just like in Delphi Align: If multiple components have same alignment, decide order based on component coordinates Change w3_getPropertyAsInt to return 0 if value is NaN as NaN messes up any calculations using that value SmartCL.Effects: Animations like fxMove did not work in Firefox The changes to TW3ScrollBox lets you now use the ScrollBox also in the Visual Designer.
  43. 3 points

    Creating a mobile app for Android and iOS

    Took some time, but I was able to create a test apk and install it on my phone. First of all, set up your folders the way I did in https://forums.smartmobilestudio.com/topic/4524-creating-a-mobile-app-for-android-and-ios/?do=findComment&comment=22461 That will let you have one folder for all the Phonegap-related stuff and another folder for your SMS app. Here's my config.xml, which should be in the phonegap-project's folder: <?xml version='1.0' encoding='utf-8'?> <widget id="com.smartmobilestudio.phonegaptest" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0"> <name>PhonegapTest</name> <description> Push sample application that receives push notifications. </description> <author email="youremail@example.com" href="https://www.smartmobilestudio.com"> Smart Mobile Studio </author> <preference name="phonegap-version" value="cli-9.0.0" /> <preference name="android-minSdkVersion" value="19" /> <preference name="android-targetSdkVersion" value="28" /> <content src="index.html" /> <preference name="DisallowOverscroll" value="true" /> <preference name="StatusBarOverlaysWebView" value="false" /> <preference name="StatusBarBackgroundColor" value="#ee6e73" /> <preference name="StatusBarStyle" value="blacktranslucent" /> <plugin name="com.ourcodeworld.plugins.Filebrowser" spec="https://github.com/ourcodeworld/cordova-ourcodeworld-filebrowser.git" /> <plugin name="cordova-plugin-geolocation" /> <plugin name="cordova-plugin-device" /> <plugin name="cordova-plugin-vibration" /> <plugin name="cordova-plugin-file" /> <plugin name="cordova-plugin-statusbar" source="npm" /> <plugin name="phonegap-plugin-push" source="npm" spec="~2.2.3" /> <plugin name="cordova-plugin-console" source="npm" /> <plugin name="cordova-plugin-dialogs" source="npm" /> <plugin name="cordova-plugin-whitelist" source="npm" /> <platform name="android"> <resource-file src="google-services.json" target="app/google-services.json" /> </platform> <platform name="android"> <icon density="ldpi" src="www/res/icon/android/drawable-ldpi-icon.png" /> <icon density="mdpi" src="www/res/icon/android/drawable-mdpi-icon.png" /> <icon density="hdpi" src="www/res/icon/android/drawable-hdpi-icon.png" /> <icon density="xhdpi" src="www/res/icon/android/drawable-xhdpi-icon.png" /> <icon density="xxhdpi" src="www/res/icon/android/drawable-xxhdpi-icon.png" /> <icon density="xxxhdpi" src="www/res/icon/android/drawable-xxxhdpi-icon.png" /> <splash density="land-ldpi" src="www/res/screen/android/drawable-land-ldpi-screen.png" /> <splash density="land-mdpi" src="www/res/screen/android/drawable-land-mdpi-screen.png" /> <splash density="land-hdpi" src="www/res/screen/android/drawable-land-hdpi-screen.png" /> <splash density="land-xhdpi" src="www/res/screen/android/drawable-land-xhdpi-screen.png" /> <splash density="land-xxhdpi" src="www/res/screen/android/drawable-land-xxhdpi-screen.png" /> <splash density="land-xxxhdpi" src="www/res/screen/android/drawable-land-xxxhdpi-screen.png" /> <splash density="port-ldpi" src="www/res/screen/android/drawable-port-ldpi-screen.png" /> <splash density="port-mdpi" src="www/res/screen/android/drawable-port-mdpi-screen.png" /> <splash density="port-hdpi" src="www/res/screen/android/drawable-port-hdpi-screen.png" /> <splash density="port-xhdpi" src="www/res/screen/android/drawable-port-xhdpi-screen.png" /> <splash density="port-xxhdpi" src="www/res/screen/android/drawable-port-xxhdpi-screen.png" /> <splash density="port-xxxhdpi" src="www/res/screen/android/drawable-port-xxxhdpi-screen.png" /> </platform> <platform name="ios"> <icon height="57" platform="ios" src="www/res/icon/ios/icon.png" width="57" /> <icon height="114" platform="ios" src="www/res/icon/ios/icon@2x.png" width="114" /> <icon height="40" platform="ios" src="www/res/icon/ios/icon-40.png" width="40" /> <icon height="80" platform="ios" src="www/res/icon/ios/icon-40@2x.png" width="80" /> <icon height="50" platform="ios" src="www/res/icon/ios/icon-50.png" width="50" /> <icon height="100" platform="ios" src="www/res/icon/ios/icon-50@2x.png" width="100" /> <icon height="60" platform="ios" src="www/res/icon/ios/icon-60.png" width="60" /> <icon height="120" platform="ios" src="www/res/icon/ios/icon-60@2x.png" width="120" /> <icon height="180" platform="ios" src="www/res/icon/ios/icon-60@3x.png" width="180" /> <icon height="72" platform="ios" src="www/res/icon/ios/icon-72.png" width="72" /> <icon height="144" platform="ios" src="www/res/icon/ios/icon-72@2x.png" width="144" /> <icon height="76" platform="ios" src="www/res/icon/ios/icon-76.png" width="76" /> <icon height="152" platform="ios" src="www/res/icon/ios/icon-76@2x.png" width="152" /> <icon height="29" platform="ios" src="www/res/icon/ios/icon-small.png" width="29" /> <icon height="58" platform="ios" src="www/res/icon/ios/icon-small@2x.png" width="58" /> <icon height="87" platform="ios" src="www/res/icon/ios/icon-small@3x.png" width="87" /> <splash height="1136" platform="ios" src="www/res/screen/ios/Default-568h@2x~iphone.png" width="640" /> <splash height="1334" platform="ios" src="www/res/screen/ios/Default-667h.png" width="750" /> <splash height="2208" platform="ios" src="www/res/screen/ios/Default-736h.png" width="1242" /> <splash height="1242" platform="ios" src="www/res/screen/ios/Default-Landscape-736h.png" width="2208" /> <splash height="1536" platform="ios" src="www/res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" /> <splash height="768" platform="ios" src="www/res/screen/ios/Default-Landscape~ipad.png" width="1024" /> <splash height="2048" platform="ios" src="www/res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" /> <splash height="1024" platform="ios" src="www/res/screen/ios/Default-Portrait~ipad.png" width="768" /> <splash height="960" platform="ios" src="www/res/screen/ios/Default@2x~iphone.png" width="640" /> <splash height="480" platform="ios" src="www/res/screen/ios/Default~iphone.png" width="320" /> </platform> <platform name="wp8"> <icon height="99" platform="wp8" src="www/res/icon/wp8/ApplicationIcon.png" width="99" /> <icon height="159" platform="wp8" src="www/res/icon/wp8/Background.png" width="159" /> <splash height="1280" platform="wp8" src="www/res/screen/wp8/screen-portrait.jpg" width="768" /> </platform> <platform name="windows"> <icon height="150" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-100.png" width="150" /> <icon height="30" platform="windows" src="www/res/icon/windows/Square30x30Logo.scale-100.png" width="30" /> <icon height="50" platform="windows" src="www/res/icon/windows/StoreLogo.scale-100.png" width="50" /> <splash height="300" platform="windows" src="www/res/screen/windows/SplashScreen.scale-100.png" width="620" /> <icon height="120" platform="windows" src="www/res/icon/windows/StoreLogo.scale-240.png" width="120" /> <icon height="44" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-100.png" width="44" /> <icon height="106" platform="windows" src="www/res/icon/windows/Square44x44Logo.scale-240.png" width="106" /> <icon height="70" platform="windows" src="www/res/icon/windows/Square70x70Logo.scale-100.png" width="70" /> <icon height="71" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-100.png" width="71" /> <icon height="170" platform="windows" src="www/res/icon/windows/Square71x71Logo.scale-240.png" width="170" /> <icon height="360" platform="windows" src="www/res/icon/windows/Square150x150Logo.scale-240.png" width="360" /> <icon height="310" platform="windows" src="www/res/icon/windows/Square310x310Logo.scale-100.png" width="310" /> <icon height="150" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-100.png" width="310" /> <icon height="360" platform="windows" src="www/res/icon/windows/Wide310x150Logo.scale-240.png" width="744" /> <splash height="1920" platform="windows" src="www/res/screen/windows/SplashScreenPhone.scale-240.png" width="1152" /> </platform> <access origin="*" /> <allow-intent href="http://*/*" /> <allow-intent href="https://*/*" /> <allow-intent href="tel:*" /> <allow-intent href="sms:*" /> <allow-intent href="mailto:*" /> <allow-intent href="geo:*" /> <platform name="android"> <allow-intent href="market:*" /> </platform> <platform name="ios"> <allow-intent href="itms:*" /> <allow-intent href="itms-apps:*" /> </platform> </widget> You also have to register your app with FCM using the advice in the link that @lynkfsjust provided. Then download the google-services.json file and save it in the folder where your config.xml is. Lastly, when you are installing the apk on your phone, you may need to disable Play Protect in the Play Store. Here's the source code I'm currently using for testing. Note, that the parts that use the file dialogs is not pretty at all. You can simply ignore those parts and the FileBrowser-plugin when you make your own implementation. unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, System.json, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button, SmartCL.Controls.Label, SmartCL.Controls.EditBox, PhoneGapApi, SmartCL.Controls.Memo, SmartCL.Controls.Image, System.Device.Storage, SmartCL.Device.Storage, System.IOUtils, System.Streams, System.Writer, SmartCL.Dialogs, SmartCL.Geolocation; type TForm1 = class(TW3Form) private {$I 'Form1:intf'} procedure AddToLog(const Value: String); procedure FileDialogError(data: Variant); procedure InitPushNotifications(Sender: TObject); procedure PhoneGapReady; procedure PushInitialized(data: Variant); procedure PushReceived(data: Variant); procedure PushError(data: Variant); procedure OpenSaveDialog(Sender: TObject); procedure SavePushToken(NewFileName: String); protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation uses PGStripe; { TForm1 } procedure TForm1.AddToLog(const Value: String); begin LogMemo.Add(Value+#13#10); end; procedure TForm1.InitPushNotifications(Sender: TObject); begin var Id := SenderId.Text; PhoneGap.InitPushNotifications(Id, @PushInitialized, @PushReceived, @PushError); end; procedure TForm1.InitializeForm; begin inherited; AddToLog('Waiting for deviceready in PhoneGap'); SenderId.Enabled := False; InitPush.Enabled := False; SaveToken.Enabled := False; PhoneGap.OnReady(PhoneGapReady); end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.PhoneGapReady; begin LogMemo.Clear; AddToLog('uuid: '+PhoneGap.Device.uuid); AddToLog('version: '+PhoneGap.Device.version); AddToLog('model: '+PhoneGap.Device.model); AddToLog('manufacturer: '+PhoneGap.Device.manufacturer); AddToLog('platform: '+PhoneGap.Device.platform); AddToLog('cordova: '+PhoneGap.Device.cordova); AddToLog('serial: '+PhoneGap.Device.serial); //Where are we? var GL:=TW3GeoLocation.Create; GL.OnPosition:=procedure (Sender: TObject; Position: TW3Position) begin AddToLog(''); AddToLog('Got position!'); AddToLog(' Time: '+FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz',Position.Timestamp)); AddToLog(' Lat: '+Variant(Position.Coords.Latitude)); AddToLog(' Lng: '+Variant(Position.Coords.Longitude)); AddToLog(' Alt: '+Variant(Position.Coords.Altitude)); AddToLog(' Acc: '+Variant(Position.Coords.Accuracy)); AddToLog(' AltAcc: '+Variant(Position.Coords.AltitudeAccuracy)); AddToLog(' Head: '+Variant(Position.Coords.Heading)); AddToLog(' Speed: '+Variant(Position.Coords.Speed)); AddToLog(''); end; GL.OnPositionError:=procedure (Sender: TObject; Msg: TW3PositionError) begin AddToLog('Got an error'); AddToLog('Message: '+Msg.ErrorMessage); end; GL.QueryCurrentPosition; SenderId.Enabled := True; InitPush.Enabled := True; InitPush.OnClick := @InitPushNotifications; PhoneGap.Notification.vibrate(1000); end; procedure TForm1.PushInitialized(data: Variant); begin AddToLog('Push init ok'); AddToLog('Token: '+PhoneGap.RegistrationId); SenderId.Enabled := False; InitPush.Enabled := False; SaveToken.Enabled := True; SaveToken.OnClick := @OpenSaveDialog; end; procedure TForm1.PushReceived(data: Variant); begin AddToLog('Push: '+data.message); end; procedure TForm1.PushError(data: Variant); begin AddToLog('Push error: '+data.message); end; procedure TForm1.OpenSaveDialog(Sender: TObject); // Uses https://github.com/ourcodeworld/cordova-ourcodeworld-filebrowser procedure DoSave(data: Variant); begin SavePushToken(data); end; begin asm window.OurCodeWorld.Filebrowser.createFileDialog({ success: function(data) { @DoSave(data); }, error: function(data) { @FileDialogError(data); } }); end; end; procedure TForm1.FileDialogError(data: Variant); begin AddToLog("Error calling createFileDialog: "+data.message); end; procedure TForm1.SavePushToken(NewFileName: String); var NewFilePath: String; procedure fail(err: Variant); begin AddToLog('Error saving push notification token'); end; procedure FileWriterCreated(FileWriter: Variant); begin var TokenToWrite := PhoneGap.RegistrationId; asm FileWriter.onerror = @fail; FileWriter.write(@TokenToWrite); end; end; procedure FileCreated(fileEntry: Variant); begin AddToLog('File created'); asm fileEntry.createWriter(@FileWriterCreated); end; end; procedure onDirectorySuccess(dirEntry: Variant); begin AddToLog('Got directory access'); asm dirEntry.getFile(NewFileName, {create: true, exclusive: false}, @FileCreated, @fail); end; end; begin AddToLog('Saving push notification token'); //Split NewFileName to path and file for var a:=Length(NewFileName) downto 1 do if NewFileName[a]='/' then begin NewFilePath := copy(NewFileName,1,a); NewFileName := copy(NewFileName,a+1,Length(NewFileName)); break; end; asm window.resolveLocalFileSystemURL(@NewFilePath, @onDirectorySuccess); end; end; procedure TForm1.Resize; begin var h:=ClientHeight; if h=0 then exit; var w:=ClientWidth; SaveToken.SetBounds(0,h-40,w,40); dec(h,40); InitPush.SetBounds(0,h-40,w,40); dec(h,40); SenderId.SetBounds(0,h-32,w,32); dec(h,32); LogMemo.SetBounds(0,0,w,h); inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  44. 3 points


    edited Been a while since I had a look at Googles Tensorflow deep learning library (js version) In the meantime this library has expanded quite a bit, most notably it enables using pre-built models for a variety of tasks. This one takes any image and detects up to 90 categories of objects in the image (persons, dogs, traffic lights etc) The library correctly identifies 2 objects of type "person" in the above image (image 620*408 pixels, bbox coordinates x,y,width,height) Usage is incredibly simple : load the libraries, display an image in the browser and issue a detect command var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs'; console.log('init tensorflow'); document.head.appendChild(Script); Script.onload := procedure begin console.log('tensorflow loaded'); var Script := document.createElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd'; console.log('init coco-ssd'); document.head.appendChild(Script); Script.onload := procedure begin console.log('coco-ssd loaded'); var Image1 := TW3Image.Create(Self); Image1.SetBounds(0, 0, 620, 408); Image1.src := 'puydesancy.jpg'; var img := Image1.handle; asm //quick and dirty : cocoSsd.load().then(model => { // detect objects in the image. model.detect(@img).then(predictions => { console.log('Predictions: ', predictions); }); }); end; end; end; (should have checked for image1.onload). Anyway, demo here. Works from server only due to cors constraints, otherwise results in 'tainted image' errors. edited and another random image
  45. 3 points
    Daniel Eiszele

    Learning Smart Mobile Studio

    I would also suggest Jon Lennart Aasenden's blog, https://jonlennartaasenden.wordpress.com/news/ as invaluable for understanding the Smart Mobile studio paradigm. His blog is not solely about Smart Mobile Studio but putting them in as search terms should return a wealth of knowledge on the subject.
  46. 3 points
    "Prompt" is the command that you are after.
  47. 3 points


    Edited. And then there is IPP : Internet Printing Protocol This protocol allows direct access to networked printers, ask for their capabilities, prepare print jobs, manage print queues, execute print jobs and get results. Exactly what I was after. I read through the documentation on the website of "The Printer Working Group", which looks after the standardisation of this protocol. (http://www.pwg.org/ipp/ippguide.html). To actually use this protocol, there are various IPP client libraries available (C, Java, Python). Fortunately there is a node.js variant as well (https://github.com/williamkapke/ipp). Both these links give some examples on how to use IPP in real world situations However the code examples on both sites contain many errors. Amazing. On the plus side, apparently some 98% of all networkable printers have an IPP driver installed by default, and should be adressable this way. To test this all out, I selected a printer on my network. The first thing to find out is the identification of the printer. IPP has its own protocol : ipp(s)://<printer uri> where the printer uri is the printers ip address followed by a path. Example : ipp:// Behind the scenes all traffic is actually routed over http(s), and printer uri's can also be specified as http over port 631. Example : The ip address can usually be found in the OS printer settings somewhere and the path is mostly standardised to be just '/ipp/print' Not too bad. After installing the node.js ipp library (npm install ipp) the following node.js code queries the printers capabilities var ipp = require("ipp"); var printer = ipp.Printer(""); var msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "image/pwg-raster", "requested-attributes": ["printer-description", "job-template", "media-col-database"] } }; printer.execute("Get-Printer-Attributes", msg, function(err, res) { console.log(err); console.log(res); }); This produces quite a list. To print a textfile to this printer, I got this code working var ipp = require("ipp"); var printer = ipp.Printer(""); var fs = require("fs"); fs.readFile("example.txt", function(err, content) { if (err) throw err; var msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "application/octet-stream" }, data: content }; printer.execute("Print-Job", msg, function(err, res) { console.log(err); console.log(res); }); }); Edited : The javascript sources above can be produced by a regular smart project : create a new node project and replace unit1 with unit Unit1; interface uses nodeBasics; //https://forums.smartmobilestudio.com/topic/4652-node-ground-zero/?tab=comments#comment-23115 type TNodeProgram = class(TObject) public constructor Create; virtual; ipp, msg, printer : variant; procedure callback(err, res: variant); procedure Execute; end; implementation constructor TNodeProgram.Create; begin inherited Create; ipp := RequireModule('ipp'); printer := ipp.Printer(""); msg := new JObject; asm @msg = { "operation-attributes-tag": { "requesting-user-name": "John Doe", "document-format": "image/pwg-raster", "requested-attributes": ["printer-description", "job-template", "media-col-database"] } }; end; end; procedure TNodeProgram.callback(err, res: variant); begin console.log(err); console.log(res); end; procedure TNodeProgram.Execute; begin printer.execute("Get-Printer-Attributes", msg, @callback); end; end. note : unfortunately we can't make up anonymous classes with hyphens in the classname, otherwise the msg variable could have been constructed as var msg = class operation-attributes-tag = class requesting-user-name = "John Doe", document-format = "image/pwg-raster", requested-attributes : array[0..2] of string = ["printer-description", "job-template", "media-col-database"]; end; end; Next step is to see if this can be made to work in the browser as well
  48. 2 points
    Well, my wife won the release competition earlier this week, but now the Alpha is also delivered. It's available with SmartUpdate in the ALPHA-channel.
  49. 2 points
    Yes, that was very frustrating in the old designer. The development-channel already contains a version that supports custom properties, but they are not that nice to use as you can't see the result in the designer. In the new one you can just change any property and see what visual changes it makes. Got Undo&redo ready today. Also found a nice solution for the non-visual components. Basically, I just create virtual properties for those if the class itself does not have them. As they are virtual, I get to ignore them when I produce the Form impl-code (the code that creates the controls and sets the properties and events)
  50. 2 points
    Tim Koscielski

    Development updates

    Jarto, I think in the Update list, the date for this update readme lists 2018 and not 2019. You just might want to check. tim
  • Create New...