Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 01/13/2019 in all areas

  1. 7 points
    jarto

    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. 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.
  3. 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 🙂
  4. 6 points
    jarto

    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.
  5. 6 points
    lynkfs

    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
  6. 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/
  7. 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
  8. 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
  9. 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.
  10. 5 points
    jarto

    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.
  11. 5 points
    lynkfs

    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(http://127.0.0.1:8090/assets/img/blueprint.jpg); 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.
  12. 5 points
    lynkfs

    console

    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
  13. 5 points
    jarto

    UI layout

  14. 5 points
    warleyalex

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

    app

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

    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.
  18. 4 points
    lynkfs

    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
  19. 4 points
    jarto

    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.
  20. 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.
  21. 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
  22. 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 😀
  23. 4 points
    This week has been a bit slow as I've been sick. However, here's a little pic of the Menu Designer
  24. 4 points
    warleyalex

    KaTex

    //------ 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. //---------------------------------------------------------------------
  25. 4 points
    jarto

    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.
  26. 4 points
    jarto

    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 🙂
  27. 4 points
  28. 4 points
    lynkfs

    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.
  29. 4 points
    lynkfs

    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
  30. 4 points
    Czar

    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.
  31. 4 points
    jarto

    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.
  32. 4 points
    lynkfs

    JQWidget integration

    I got asked to have a look at how to integrate jqxWidgets in SMS There are a heap of ready-made widgets under the name of jQuery UI Widgets, ranging from grids to calendars to tooltips and many more. The example here is a jqxButton with an image in the left top corner. The vanilla html file for this looks like <!DOCTYPE html> <html lang="en"> <head> <meta name="keywords" content="jQuery Button, CheckBox, Toggle Button, Repeat Button, Radio Button, Link Button, Button" /> <meta name="description" content="The jqxButton widget allows you to display a button on the Web page." /> <title id='Description'>The jqxButton widget allows you to display a button on the Web page.</title> <link rel="stylesheet" href="../../jqwidgets/styles/jqx.base.css" type="text/css" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1 minimum-scale=1" /> <script type="text/javascript" src="https://www.jqwidgets.com/jquery-widgets-demo/scripts/jquery-1.11.1.min.js"></script> <script type="text/javascript" src="https://www.jqwidgets.com/jquery-widgets-demo/jqwidgets/jqxcore.js"></script> <script type="text/javascript" src="https://www.jqwidgets.com/jquery-widgets-demo/jqwidgets/jqxbuttons.js"></script> <script type="text/javascript"> $(document).ready(function () { // Create jqxButton widgets. $("#jqxButton").jqxButton({ width: 120, height: 40 }); // Subscribe to Click events. $("#jqxButton").on('click', function () { $("#events").find('span').remove(); $("#events").append('<span>Button Clicked</span'); }); }); </script> </head> <body class='default'> <div> <input type="button" value="Button" id='jqxButton' /> </div> <div style='font-size: 12px; font-family: Verdana; margin-top: 10px;'> <div>Events:</div> <div id='events'> </div> </div> </body> </html> which requires a number of external js and css files. These can be local or reside on a cdn server somewhere. Make sure they are served from either http or https, same as the platform the app is executed on. The widgets are created first, immediately after the document is dom-ready, and they will be rendered on a placeholder element with the same element.id as the created widgets. This can be simplified considerably in SMS : All functions can be recoded using this function tied to the ubiquitous jQuery "$" directive : function jqxQuery(aTagObj: string): Variant; external '$'; which results in this code : var props : variant := new JObject; props.height := 40; props.width := 120; props.imgSrc := "https://www.jqwidgets.com/jquery-widgets-demo/images/twitter.png"; props.imgPosition := "topLeft"; jqxQuery( "#jqxButton1" ).jqxButton(props); jqxQuery( "#jqxButton1" ).on('click', procedure() begin browserapi.window.alert('clicked button1'); end); and the placeholder element jqxButton1 := TW3Panel.Create(self); jqxButton1.SetBounds(30,20,126,46); jqxButton1.handle.style.border := '1px solid grey'; jqxButton1.handle.innerHTML := '<input type="button" value="Button" id="jqxButton1" />'; Sort of a rough and tumble approach, but it does work. A better way is to encapsulate these widgets in a dedicated component : unit jqxButton; { **************************************************************************** } { } { Smart Mobile Studio } { } { **************************************************************************** } interface {$I 'Smart.inc'} uses System.Types, {$IFDEF THEME_AUTOSTYLE} SmartCL.Theme, {$ENDIF} SmartCL.System, SmartCL.Components; type TJQXButton = class(TW3CustomControl) protected function MakeElementTagObj: THandle; override; procedure InitializeObject; override; published property caption: string; // read GetCaption write SetCaption; end; function jqxQuery(aTagObj: string): Variant; external '$'; implementation //############################################################################# // TJQXButton //############################################################################# procedure TJQXButton.InitializeObject; begin inherited; Caption := 'Button'; //default Handle.ReadyExecute( procedure () begin var Script := browserapi.document.createElement('script'); Script.src := 'https://www.jqwidgets.com/jquery-widgets-demo/jqwidgets/jqxcore.js'; browserapi.document.head.appendChild(Script); Script.onload := procedure begin writeln('jqxcore loaded'); var props : variant := new JObject; props.height := self.height-2; props.width := self.width-2; props.imgSrc := "https://www.jqwidgets.com/jquery-widgets-demo/images/twitter.png"; props.imgPosition := "topLeft"; jqxQuery( "#jqx" + self.handle.id ).jqxButton(props); end; handle.innerHTML := '<input type="button" value="' + Caption + '" id="jqx' + self.handle.id + '" />'; end); end; function TJQXButton.MakeElementTagObj: THandle; begin result := w3_createHtmlElement('div'); end; end. which can be instantiated as simple as jqxButton2 := TJQXButton.Create(self); jqxButton2.SetBounds(230,20,130,50); jqxButton2.Caption := 'Button2'; jqxButton2.OnClick := procedure(Sender:TObject) begin browserapi.window.alert('clicked button2'); end; Project demo here and source code here
  33. 4 points
    lynkfs

    JQWidget integration

    and here is the Grid - JQWidgets implementation working sms demo here Notes : there are 2 different formats of defining the grid, one using jQuery explicitly and one as a web-component. The latter format has a bug somewhere and comes up consistently with a runtime error. The former format works fine. the 'export to Excel' button actually fires up Excel if installed and run locally (sms demo is grid only but take my word)
  34. 4 points
    jarto

    Beware of box shadows!

    I have been working on a new TW3StringGrid for Smart Mobile Studio. The original plan was to include it in 3.0, but there was a problem: It was slow on iOS. So, ever since the release, I've tried to crack this problem. How can Javascript that works beautifully on every other platform and browser be absolutely horrible on iOS? It was extremely fast on desktop browsers and Android tablets. On iOS it took seconds to draw the grid and scrolling it was close to unusable. After countless of hours of testing and trying, I finally found the reason: Box shadows in CSS. For example: .TW3ContainerBorder { border-radius: <?pas=EdgeRounding?>; border-top: 1px solid rgba(250, 250, 250, 0.7); border-left: 1px solid rgba(250, 250, 250, 0.7); border-right: 1px solid rgba(240, 240, 240, 0.5); border-bottom: 1px solid rgba(240, 240, 240, 0.5); -webkit-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -moz-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -ms-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; -o-box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; box-shadow: 0px 0px 1px 1px <?pas=GetRGBA(clDlgShadowBaseColor, 0.3)?>; } .TW3FlatBorder { border-radius: <?pas=EdgeRounding?>; border-top: 1px solid rgba(44, 44, 44, 0.8); border-left: 1px solid rgba(44, 44, 44, 0.8); border-right: 1px solid rgba(44, 44, 44, 0.8); border-bottom: 1px solid rgba(44, 44, 44, 0.8); } I had used a TW3ContainerBorder for every cell in the grid. Replacing them with a flat border improved performance dramatically on iOS. Something like an order of magnitude faster. So if you've been having performance problems with iOS, that's most likely the reason.
  35. 4 points
    lynkfs

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

    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
  37. 4 points
    jarto

    Development updates

    New update available: RTL: New controls: TW3SpinButton, TW3ArrowUpButton, TW3ArrowDownButton, TW3ArrowLeftButton and TW3ArrowRightButton. Add 1px margin to TW3ButtonBorder to prevent the border from being clipped. Bug fixes to Tween.Effects IDE: Bug fixes and changes to renaming of forms: Renaming in Project Manager will not change form class any more. Form class name can be renamed in Object Inspector. Improvements to Project Statistics: Only count Total time when Smart Mobile Studio IDE has focus. Only Count Design time when changes are made in Form Designer. Add clear button. Component Palette: Added new controls New icons for many existing controls
  38. 4 points
    lynkfs

    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
  39. 3 points
    jarto

    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.
  40. 3 points
    DavidRM

    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
  41. 3 points
    lynkfs

    KaTex

    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
  42. 3 points
    jarto

    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
    jarto

    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
    lynkfs

    printing

    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://192.168.1.233/ipp/print 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 : http://192.168.1.233:631/ipp/print 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("http://192.168.1.233:631/ipp/print"); 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("http://192.168.1.233:631/ipp/print"); 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("http://192.168.1.233:631/ipp/print"); 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
  45. 3 points
    lynkfs

    fetching files

    There are afaik 3 ways to read external files directly from the client : using ajax / xmlhttprequest using the filereader using the fetch api 1: get xmlhttprequest var FHttp := TW3HttpRequest.Create; FHttp.OnDataReady := lambda writeln(FHttp.responsetext); end; FHttp.open('GET','res/textfile.txt'); FHttp.send(); 2: file input W3EditBox1.InputType := itFile; W3EditBox1.Handle.ReadyExecute( procedure () begin W3EditBox1.OnChanged := procedure(sender:TObject) begin var reader: variant; asm @reader = new FileReader(); end; reader.onload := lambda writeln(reader.result); end; reader.readAsText(W3EditBox1.handle.files[0]); end; end); 3: fetch api var window external 'window': variant; window.fetch('res/textfile.txt') .then(function(response:variant):variant begin result := response.text(); end) .then(procedure(myText:variant) begin writeln(myText); end); these fetch calls return a promise and the promise returns a response The fetch api is a mechanism which can replace the usual ajax calls. This api is primarily designed to act on network events. (see this post), but works for simple file fetching as well 4: Variations of the above, see f.i. this one using webworkers var FileReader : variant := new JObject; asm @FileReader = new Worker('filereader.js'); end; FileReader.onmessage := procedure(e: variant) begin writeln(e.data); end; FileReader.postMessage('res/textfile.txt'); //read file through webworker (filereader.js) demo / project (demo output is to console : ctrl+shift+i)
  46. 3 points
    lynkfs

    window component

    Just in case anyone has a need for a window component. (multiple windows, movable, resizable, bringtofront, minimise, close, maximise) project
  47. 3 points
    jarto

    Development updates

    New update available in the development-channel: Anchors and Align-support to the RTL. Bug fix to BoundsRect Please note that anchors can only be set in code at the moment. The next step is to add support for setting the anchors and align in the IDE's designer as well. Currently Align actually can be set in the Object Inspector, but the designer does not show the result by aligning components.
  48. 3 points
    DavidRM

    Node.JS WebSocket Client Socket

    This is me sharing again. The NodeJS WebSocket server socket is implemented in SMS. But I didn't find an implementation of the WebSocket client socket. Sometimes an SMS NodeJS server needs to connect to another server. So I kinda hacked this together using the server socket as a guide. unit UPBNCommonNJWebSocket; interface uses System.Types, System.Types.Convert, System.Time, System.Streams, System.Reader, System.Writer, System.Device.Storage, System.Objects, SmartNJ.System, SmartNJ.Streams, SmartNJ.Device.Storage, SmartNJ.Application, NodeJS.Core, NodeJS.WebSocket, SmartNJ.Server.WebSocket; type // Forward declarations TNJWebSocket = class; TNJWebSocketOpenEvent = procedure (Sender: TNJWebSocket); TNJWebSocketCloseEvent = procedure (Sender: TNJWebSocket; Code: integer; const Reason: string); TNJWebSocketErrorEvent = procedure (Sender: TNJWebSocket; Error: TJSErrorObject); TNJWebSocketMessageEvent = procedure (Sender: TNJWebSocket; Message: TNJWebsocketMessage); TNJWebSocket = class(TW3ErrorObject) private FSocket: JWsSocket; public property WSSocket: JWsSocket read FSocket; function SocketState: JWsReadyState; function Connected: boolean; function URL: string; function Protocol: string; procedure Connect(URL: string; Protocols: array of string); overload; procedure Connect(URL: string); overload; procedure Disconnect; overload; procedure Send(const Data: variant); overload; procedure Send(const Text: string); overload; procedure Send(const Data: TStream); overload; property TagData: variant; constructor Create; override; destructor Destroy; override; procedure Ping; published property OnOpen: TNJWebSocketOpenEvent; property OnClosed: TNJWebSocketCloseEvent; property OnMessage: TNJWebSocketMessageEvent; property OnError: TNJWebSocketErrorEvent; end; implementation constructor TNJWebSocket.Create; begin inherited Create; // We dont want to throw exceptions whenever SetLastError() is called ErrorOptions.ThrowExceptions := false; end; destructor TNJWebSocket.Destroy; begin FSocket := nil; inherited; end; procedure TNJWebSocket.Ping; begin if FSocket <> nil then asm (@FSocket).ping(function() {}); end; end; function TNJWebSocket.Protocol: string; begin if FSocket <> nil then Result := FSocket.protocol; end; function TNJWebSocket.URL:String; begin if FSocket <> nil then Result:=FSocket.url; end; function TNJWebSocket.SocketState: JWsReadyState; begin if FSocket <> nil then Result := JWsReadyState( integer( FSocket.readyState) ) else Result := rsClosed; end; function TNJWebSocket.Connected: boolean; begin Result := SocketState = rsOpen; end; procedure TNJWebSocket.Connect(URL: string); begin Connect(Url, []); end; procedure TNJWebSocket.Connect(URL: string; Protocols: Array of string); begin ClearLastError(); (* disconnect socket if already connected *) if connected then disconnect(); (* Allocate new socket *) var WebSocket = WebSocketAPI; asm (@self.FSocket) = new (@WebSocket)(@URL, @Protocols); end; // initialize standard socket events FSocket.on("open", procedure begin if assigned(OnOpen) then OnOpen(self); end); FSocket.on("error", procedure (error: variant) begin SetLastError("internal websocket error"); if assigned(OnError) then OnError(self, TJSErrorObject(error)); end); FSocket.on("message", procedure (message: variant) var ResData: TNJWebsocketMessage; begin if message.IsUint8Array then begin ResData.wiType := mtBinary; ResData.wiBuffer := JBuffer(message); end else begin ResData.wiType := mtText; ResData.wiText := message; end; if assigned(OnMessage) then OnMessage(self, ResData); end); Variant(FSocket).on("close", procedure (code: integer; reason: string) begin if assigned(OnClosed) then OnClosed(self, code, reason); end); end; procedure TNJWebSocket.Disconnect; begin ClearLastError(); if Connected then begin try FSocket.close(); finally FSocket := nil; end; end; end; procedure TNJWebSocket.Send(const Data: variant); begin FSocket.send(data); end; procedure TNJWebSocket.Send(const Text: string); begin FSocket.send(Text); end; procedure TNJWebSocket.Send(const Data: TStream); begin if Data <> nil then begin if Data.position < Data.Size then begin // Get bytes from stream var Bytes := Data.Read(Data.Size - Data.Position); // Convert to typed-array var TypedArray := TDataType.BytesToTypedArray(bytes); // Send as binary FSocket.Send(TypedArray); end; end; end; end. It might be a bit simplified, and it doesn't really follow the SMS component name convention, but it does what I need. And I figured I would share. -David
  49. 3 points
    jarto

    UI layout

    Yes, we need anchors and aligns. Now that I'm about to get TW3StringGrid finished, I'll have a look at Anchors and Align.
  50. 3 points
    to match/map the JSON fieldname, you have to declare the record definition is type TFishRecord = record Category: String; external 'Category'; Common_Name: String; external 'Common_Name'; Length_Cm: String; external 'Length_Cm'; Length_In: String; external 'Length_In'; Notes: String; external 'Notes'; Species_Name: String; external 'Species_Name'; Species_No: String; external 'Species_No'; end; to emit this JS Object, for instance: { Category : "Snapper", Common_Name : "Red Emperor", Length_Cm : "60", Length_In : "23.6220472440945", Notes : "Called seaperch in Australia.", Species_Name : "Lutjanus sebae", Species_No : "90030" }
×
×
  • Create New...