Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Reputation Activity

  1. Like
    Czar reacted to jarto in Development updates   
    New update available in the development-channel:
    Bug fix to browser specific CSS. Fixes scrolling in Firefox. EventManager: Prevent extra OnClick when using SetCapture Bug fix to handling movement events while using EventManager's MouseCapture Bug fix to scrolling the earth in Tabs, Scrolling and ListBox demo
  2. Like
    Czar reacted to jarto in New Visual Designer for Smart Mobile Studio   
    Technically, we are not moving away from Delphi. We are just creating one component (the designer) as an html5 app. The rest of the IDE will still be Delphi code. Eventually it could be possible to rewrite the whole IDE as an online application (+server) but that's not something we are attempting to do right now.
    When it comes to the new designer as an SMS app, we actually don't lose anything. As far as I can see, there are only advantages. There were many disadvantages in having the designer as a Delphi component. Getting the look right even with standard components was very hard. Supporting anything more complicated was impossible. When the Designer is an SMS app, we only have to create the component and it automatically looks right, no matter what theme is used or how complicated the component is or what properties it happens to have.
    The hard part is only the communication between the IDE and the new designer. That's done using Websockets and it already works well. So I can add a component in the new designer and it shows up in the IDE's component list and the Object Inspector. I can move the component and see the coordinates changing in the Object Inspector. I can change properties and they are reflected in the new Designer. And if I compile and run the app I drew in the new designer, well, it works.
    I can't tell yet, when I can push out the first alpha. There are still some things that need to be taken care of. For example, we need to finish updating the IDE to use a newer Chrome component. Also, I want to write a few editors to make sure that the new designer has propert support for more complicated controls.
  3. Like
    Czar reacted to jarto in New Visual Designer for Smart Mobile Studio   
    The new designer does automatically support Align and Anchor, which makes creating responsive apps easy. On top of that, you'll be able to tweak the form for different screen sizes.
  4. Like
    Czar reacted to jarto in New Visual Designer for Smart Mobile Studio   
    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.
  5. Like
    Czar reacted to jarto in Development updates   
    New update available in the development-channel:
    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.
  6. Like
    Czar reacted to jarto in 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.  
  7. Like
    Czar reacted to jarto in Creating a mobile app for Android and iOS   
    I've been having a look at this today. Google Play changed their requirements for generated packages. Seems like Phonegap Build does not yet have an updated push notification plugin - or the old one is not compatible with Cordova 9.0.0, which is required by Google - or the way push notifications are handled in Android these days have changed.
    These are not specifically problems with Smart Mobile Studio itself as the same issues do happen with any html5-app. I hope I can crack this or that someone else can contribute.
    On the plus side, push notifications seem to work on phonegap's mobile test client, which is helpful.
  8. Like
    Czar reacted to jarto in Creating a mobile app for Android and iOS   
    Yes, the push notifications are a great way for alerting users.
    I'll have a look to see if anything needs to be updated and upload the demo again.
  9. Like
    Czar reacted to jarto in Creating a mobile app for Android and iOS   
    After writing that, I noticed that newer versions of iTunes do not install ipa-packages any more. To get around that, there are some ways: https://codeburst.io/latest-itunes-12-7-removed-the-apps-option-how-to-install-ipa-on-the-device-3c7d4a2bc788
    I myself decided to use an older version of iTunes, that you can download from that page. That let me install the PhonegapTest.ipa, which I created in Phonegap build. And guess what?
    The app was installed in my iPad It started and initialized Phonegap properly I clicked on "Init push notifications", which registered itself properly and returned a Token What did not work:
    Saving the token as the plugin I had used for selecting the folder is strictly Android only However, there are many other plugins that you can use and there's no problem defining different plugins for Android and iOS in the config.xml -file.
    But basically, your Smart Mobile Studio app is now ready to receive push notifications on both Android and iOS.
  10. Like
    Czar reacted to lynkfs in kodi grid   
    @lennart posted a reference to a scrollable grid in Delphi Developer.
    See post here.
    Since he produced this grid in Smart Pascal, it might be of interest to forum members.
    This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns. 
    The code he posted doesn't work in my (latest) version of Smart, but with a few tweaks here and there I got it going. 
    At least for strings that is.  Icons/images are not fully implemented in the source he posted, but I'll take another look. Won't be too difficult.
    I like the marching ants selection.
    See demo.
    kodi := TWbIconView.Create(self);   kodi.setbounds(10,10,500,500); //by way of demo : just split any sentence into words and use these as variable length cell items   var s : string := #'This grid basically takes items of irregular dimensions and determines how to best fit those items in rows/columns.';   var a : array of string := StrSplit(s,' ');   for var i := 0 to a.length-1 do begin     kodi.AddText(a[i]); end;
  11. Like
    Czar reacted to lynkfs in tensorflow   
    Been a while since I had a look at Googles Tensorflow deep learning library (js version)
    In the meantime this library has expanded quite a bit, most notably it enables using pre-built models for a variety of tasks.
    This one takes any image and detects up to 90 categories of objects in the image (persons, dogs, traffic lights etc)

    The library correctly identifies 2 objects of type "person" in the above image (image 620*408 pixels, bbox coordinates x,y,width,height)
    Usage is incredibly simple : load the libraries, display an image in the browser and issue a detect command
      var Script := document.createElement('script');   Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs';   console.log('init tensorflow');   document.head.appendChild(Script);   Script.onload := procedure   begin     console.log('tensorflow loaded');     var Script := document.createElement('script');     Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd';     console.log('init coco-ssd');     document.head.appendChild(Script);     Script.onload := procedure     begin       console.log('coco-ssd loaded');       var Image1 := TW3Image.Create(Self);       Image1.SetBounds(0, 0, 620, 408);       Image1.src := 'puydesancy.jpg'; var img := Image1.handle;       asm //quick and dirty :         cocoSsd.load().then(model => {         // detect objects in the image.           model.detect(@img).then(predictions => {           console.log('Predictions: ', predictions);           });         });       end;     end;   end; (should have checked for image1.onload). Anyway, demo here. Works from server only due to cors constraints, otherwise results in 'tainted image' errors.
    and another random image

  12. Like
    Czar reacted to jarto in TryStrToInt bug   
    Sorry for taking a long time to answer this. We're working on upgrading the compiler to use the newest DWS. While doing that, I made a quick test. Unfortunately this bug is still there. So we need to dig deeper.
  13. Like
    Czar reacted to lynkfs in 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
  14. Like
    Czar reacted to jarto in 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.
  15. Like
    Czar reacted to lynkfs in 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
  16. Like
    Czar reacted to lynkfs in file system   
    The original html5 spec had a file api which allowed to read and write files clientside (the File Directories and System Api). This was only fully implemented by Chrome at one point, but even then Chrome needed to be started with a switch (-allow-file-access-from-files).
    Today clientside storage is limited to cookies, local/session storage, indexedDB and the Cache API.
    While I understand the reasons for limiting file handling on the client, it is sometimes annoying. Fortunately some pretty creative solutions have emerged.
    One of them is Filer. This is a nodejs fs filesystem look-a-like which uses indexedDB for storage, and runs in the browser. Basically it allows for a complete filesystem clientside.  Not bad at all.
    To continue stacking : Filer is stacked on top of indexedDB. Stack nohost on top of Filer and you have a web fileserver (based on service workers) in the browser. Amazing.
    Will certainly try this out.
  17. Like
    Czar reacted to Daniel Eiszele in Getting user's input from input box   
    "Prompt" is the command that you are after.
  18. Like
    Czar got a reaction from tristan in components lose name - little IDE bug   
    Components on the visual form lose their name property if they are cut and paste. I use this quite often as I will use a panel to organise related editboxes, and labels.
    1. drop a label on the visual forum and call it lbl1
    2. now drop a panel on the form.
    3. We will cut the label and paste it inside form.
    select the lbl + ctrl X. And then select the panel and ctrl + v to paste the label into the form.
    4 . This works fine but the label is now call w3label1
    It would be nice if the component retained the name property.
  19. Like
    Czar reacted to tristan in Bugs and Features   
    Thanks for all the effort in making Smart Mobile Studio a powerful tool for pascal web development. After having a license for over a year I am finally making some effort at building a real life application. 
    I just want to provide some feedback; I did try a quick search in the forum to avoid duplication.
    I believe I am testing the latest development version; however I do find it difficult to know with any certainty.
    Release notes in SmartUpdate have incorrect date (I think). Can you make it easier to identify the version available? SmartUpdate fails with DEVELOPMENT not available unless re-run with /changechannel /showhidden parameters IDE
    Import existing form. I could not find an intuitive way to import a form created from another project. Copy form into new project folder, add external, not added to Form section Switch comment (CTRL + /) will not include the last line selected 
    TabControl AnimateTabs with Forms in tabs, will cause form contents to disappear (in particular memo and stringrids) (See TabForms demo with TMemo on form3) Turning off AnimateTabs will resolve the issue.
  20. Like
    Czar reacted to tristan in TW3StringGrid is available   
    For anyone needing something similar
    unit SmartCL.Controls.StringGrid.ImageColumn; interface uses W3C.DOM, System.Types, System.Types.Graphics, System.Types.Convert, System.Time, System.Dictionaries, SmartCL.Application, SmartCL.Components, SmartCL.System, SmartCL.Theme, SmartCL.Css.Classes, SmartCL.Scroll, SmartCL.Controls.Label, SmartCL.Controls.StringGrid, SmartCL.Controls.Image; type TW3StringGridImageColumn = class(TW3StringGridColumn) protected function GetClasses: String; override; public constructor Create; override; function GenerateHtml(GridLine: TW3RenderedGridLine; ColumnIndex, CurrentLeft: Integer): String; override; procedure SetColumnEvents(Sender: TW3CustomStringGrid; GridLine: TW3RenderedGridLine; ColumnIndex: Integer); override; end; implementation constructor TW3StringGridImageColumn.Create; begin FBaseClass := 'TW3Image'; AlignText := taCenter; VAlign := tvCenter; BorderType := btLightBorderRight; BackgroundType := bsNone; end; function TW3StringGridImageColumn.GenerateHtml(GridLine: TW3RenderedGridLine; ColumnIndex, CurrentLeft: Integer): String; begin var ItemId:=Grid.Handle.id+'_row_'+IntToStr(GridLine.DataIndex)+'_btncol_'+IntToStr(ColumnIndex); GridLine.ItemIds.Add(ItemId); result:='<img id="'+ItemId+'" class="'+GetClasses+'" style="visibility: visible; display: inline-block; overflow: hidden; left: '+IntToStr(CurrentLeft)+'px; position: absolute; width: '+IntToStr(Width)+'px; height: 100%;border-radius: 0px;" src="'+TString.EncodeTags(Grid.Cells[GridLine.DataIndex,ColumnIndex])+'"</img>'; end; function TW3StringGridImageColumn.GetClasses: String; begin result:=inherited GetClasses+' TW3ButtonBackground'; end; procedure TW3StringGridImageColumn.SetColumnEvents(Sender: TW3CustomStringGrid; GridLine: TW3RenderedGridLine; ColumnIndex: Integer); // Note: This is used by the combo box too var cHandle: THandle; begin try cHandle := GridLine.FixedCols.Handle.GetChildById(GridLine.ItemIds[ColumnIndex]); if not (cHandle) then cHandle := GridLine.ScrollableCols.Handle.GetChildById(GridLine.ItemIds[ColumnIndex]); // cHandle['oninput'] := @Sender.HandleDataChanged; except end; end; end.  
  21. Like
    Czar reacted to jarto in Development updates   
    New update is available in the development-channel:
    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.
  22. Like
    Czar reacted to lynkfs in 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. 
    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. 
    On the plus side, apparently some 98% of all networkable printers have an IPP driver installed by default, and should be adressable this way.
    To test this all out, I selected a printer on my network.
    The first thing to find out is the identification of the printer.
    IPP has its own protocol : ipp(s)://<printer uri> where the printer uri is the printers ip address followed by a path. 
    Example : ipp://
    Behind the scenes all traffic is actually routed over http(s), and printer uri's can also be specified as http over port 631. 
    Example :
    The ip address can usually be found in the OS printer settings somewhere and the path is mostly standardised to be just '/ipp/print'  
    Not too bad. 
    After installing the node.js ipp library (npm install ipp) the following node.js code queries the printers capabilities
    var ipp = require("ipp"); var printer = ipp.Printer(""); var msg = {   "operation-attributes-tag": {     "requesting-user-name": "John Doe",     "document-format": "image/pwg-raster",     "requested-attributes": ["printer-description", "job-template", "media-col-database"]   } }; printer.execute("Get-Printer-Attributes", msg, function(err, res) {         console.log(err);         console.log(res); }); This produces quite a list.
    To print a textfile to this printer, I got this code working
    var ipp = require("ipp"); var printer = ipp.Printer(""); var fs = require("fs"); fs.readFile("example.txt", function(err, content) {               if (err) throw err;   var msg = {     "operation-attributes-tag": {       "requesting-user-name": "John Doe",       "document-format": "application/octet-stream"              },     data: content   };   printer.execute("Print-Job", msg, function(err, res) {         console.log(err);         console.log(res);   }); });  
    Edited :
    The javascript sources above can be produced by a regular smart project : create a new node project and replace unit1 with
    unit Unit1; interface uses nodeBasics; //https://forums.smartmobilestudio.com/topic/4652-node-ground-zero/?tab=comments#comment-23115 type   TNodeProgram = class(TObject)   public     constructor Create; virtual;     ipp, msg, printer : variant;     procedure   callback(err, res: variant);     procedure   Execute;   end; implementation constructor TNodeProgram.Create; begin   inherited Create;   ipp := RequireModule('ipp');   printer := ipp.Printer("");   msg := new JObject;   asm   @msg = {     "operation-attributes-tag": {       "requesting-user-name": "John Doe",       "document-format": "image/pwg-raster",       "requested-attributes": ["printer-description", "job-template", "media-col-database"]     }   };   end; end; procedure TNodeProgram.callback(err, res: variant); begin   console.log(err);   console.log(res); end; procedure TNodeProgram.Execute; begin   printer.execute("Get-Printer-Attributes", msg, @callback); end; end.  
    note : unfortunately we can't make up anonymous classes with hyphens in the classname, otherwise the msg variable could have been constructed as
    var msg = class   operation-attributes-tag = class     requesting-user-name = "John Doe",     document-format = "image/pwg-raster",     requested-attributes : array[0..2] of string =       ["printer-description", "job-template", "media-col-database"];   end; end;  
    Next step is to see if this can be made to work in the browser as well 
  23. Like
    Czar reacted to lynkfs in printing   
    Printers used to be dumb peripherals. Not so with the latest receipt printers (epson).
    They have actually an apache webserver built in, and the printer can act as the hub for displays, scanners and other (dumb) peripherals.

    Connecting them to Smart looks like not too difficult.
  24. Sad
    Czar reacted to lynkfs in reserved project name   
    I accidentally gave a project a reserved name ('implementation').
    Not a good idea: compiles then crashes
  25. Like
    Czar reacted to lynkfs in 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

  • Create New...