Jump to content

Leaderboard


Popular Content

Showing most liked content since 03/25/2018 in all areas

  1. 3 points
    lynkfs

    webhooks

    It took me a while to get my head around the concept of webhooks. These have been around for 10 years or more but there is almost no to-the-point info or 'webhook-for-dummies' article on the web available. Webhooks are basically a notification system between disparate systems or servers, where the notifications are based on Http (XmlHttpRequests), so no messaging protocols or middleware software necessary. Like this : In this example there is a service which exposes changes in stock-prices. This service also has an api which allows applications to subscribe to this service (1). Subscription involves providing the url of the application-server. When a stock-price change event occurs, the service POST's a HttpRequest to the url of the server with a payload of the new price (2a) The server responds with a simple OK (2b) and initiates an action towards the client. This action can be maybe sending an email, or just sending a socket-message to the client (3) which allows the client to update its db or user-interface Setting this up in SMS takes a bit of effort as the servers need to have publicly accessible urls. Meaning nodejs servers have to be hosted somewhere (Heroku, Google Cloud). Doable I think.
  2. 3 points
    jarto

    Paint box

    When it comes to saving and sending the data, I suppose something like this works: SaveButton.OnClick:=procedure(Sender: TObject) begin var FImg := TW3Image.Create(Self); FImg.SetBounds(0,0,FCanvas.Width,FCanvas.Height); FImg.LoadFromImageData(FCanvas.Canvas.ToImageData); FImg.ToStream(YourStreamWhereYouWantTheImageDataToBeSaved); end;
  3. 3 points
    Sakis

    SVG position

    I found the answer it is one more line of code in your suggested solution W3Button1.handle.style.setProperty('background-position', '10px 12px'); Thank you very much!!!!!!
  4. 2 points
    lynkfs

    Tensorflow

    Tensorflow.js has arrived - exciting Google just announced tensorflow.js, its machine learning library which previously required access from python (or C# for more limited access), but now can be accessed from js The main page for this : https://js.tensorflow.org/ Just to see how this can be embedded in SMS : the (slightly) altered example on that main page, plunked under a button procedure TForm1.W3Button1Click(Sender: TObject); begin var Script := w3_createHtmlElement('script'); Script.src := 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.6.1'; w3_setElementParentByRef(script, browserapi.document.head); Script.onload := procedure begin asm //import * as tf from '@tensorflow/tfjs'; //var tf = require(['@tensorflow/tfjs']); // Define a model for linear regression. const model = tf.sequential(); model.add(tf.layers.dense({units: 1, inputShape: [1]})); // Prepare the model for training: Specify the loss and the optimizer. model.compile({loss: 'meanSquaredError', optimizer: 'sgd'}); // Generate some synthetic data for training. const xs = tf.tensor2d([1, 2, 3, 4], [4, 1]); const ys = tf.tensor2d([1, 3, 5, 7], [4, 1]); // Train the model using the data. model.fit(xs, ys).then(() => { // Use the model to do inference on a data point the model hasn't seen before: model.predict(tf.tensor2d([5], [1, 1])).print(); window.alert(model.predict(tf.tensor2d([5], [1, 1]))); }); end; end; end; The example above is not the most striking one, the others are better. But at least got it working. PS : a neural network built from first principles was posted at http://www.lynkfs.com/Experiments/mnist/Neural01.sproj a while ago, see in action using a larger character recognition dataset at http://www.lynkit.com.au/native/www/ under 'projects' / 'AI-character recognition'. (Training takes approx 30sec on desktop, 5-6min on mobile)
  5. 2 points
    lennart

    NodeJS and HTTPS

    Hi Dave! Im actually doing this now. Under node, http and https are implemented as separate modules - which are identical. So you are right that you should inherit from TNJCustomServer, but I think you can just copy the whole http-server unit and then make sure it uses https instead. Here are the docs: https://nodejs.org/api/https.html You just have to do a require("https") and import the module. Then define a "class external" for it. If you look at how we import the other modules it should be easy to understand. But - if you can wait for the update Ill try my best to have it ready for you. We were going to wait until the next update (not the one lined up now), but ill see if i can squeeze it into our build for you /Jon
  6. 2 points
    jarto

    Paint box

    When it comes to drawing the signature, here's a quick example based on the featured demo called "Multi Finger Paint": unit Main; interface uses System.Types, System.Colors, SmartCL.System, SmartCL.Controls, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms; type TPaintBoxForm = class(TW3form) private FCanvas: TW3GraphicControl; FMouseDown: boolean; FMouseID: integer; FPrevX: integer; FPrevY: integer; protected procedure InitializeObject; override; procedure FinalizeObject; override; procedure HandleMouseDown(Sender: TObject; button: TMouseButton; Shift: TShiftState; X, Y: integer); procedure HandleMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer); procedure HandleMouseUp(Sender: TObject; button: TMouseButton; Shift: TShiftState; X, Y: integer); procedure Paint(Sender: TObject; canvas: TW3Canvas); procedure PaintLine(X, Y: integer); procedure Resize; override; procedure StyleTagObject; override; end; implementation procedure TPaintBoxForm.FinalizeObject; begin FCanvas.Free; inherited; end; procedure TPaintBoxForm.InitializeObject; begin inherited; FCanvas := TW3GraphicControl.Create(Self); FCanvas.OnPaint := Paint; FCanvas.OnMouseDown := HandleMouseDown; FCanvas.OnMouseMove := HandleMouseMove; FCanvas.OnMouseUp := HandleMouseUp; FCanvas.SimulateMouseEvents := True; FPrevX:=-1; FPrevY:=-1; end; procedure TPaintBoxForm.HandleMouseDown(Sender: TObject; button: TMouseButton; Shift: TShiftState; X, Y: integer); begin FMouseDown := True; PaintLine(X, Y); end; procedure TPaintBoxForm.HandleMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer); begin if FMouseDown then PaintLine(X, Y); end; procedure TPaintBoxForm.HandleMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: integer); begin FMouseDown := False; Inc(FMouseID); FPrevX:=-1; FPrevY:=-1; end; procedure TPaintBoxForm.Paint(Sender: TObject; Canvas: TW3Canvas); begin FCanvas.Canvas.FillStyle := '#FFFFFF'; FCanvas.Canvas.FillRectF(0, 0, Width, Height); end; procedure TPaintBoxForm.PaintLine(X, Y: Integer); begin if (FPrevX>=0) and (FPrevY>=0) then begin FCanvas.Canvas.BeginPath; FCanvas.Canvas.MoveToF(FPrevX,FPrevY); FCanvas.Canvas.LineToF(X,Y); FCanvas.Canvas.StrokeStyle := ColorToWebStr(ClBlack); FCanvas.Canvas.Stroke; end; FPrevX:=X; FPrevY:=Y; end; procedure TPaintBoxForm.Resize; begin FCanvas.SetBounds(0, 0, Width, Height); FCanvas.Invalidate; end; procedure TPaintBoxForm.StyleTagObject; begin inherited; StyleClass := 'TW3CustomForm'; end; end.
  7. 2 points
    jarto

    Scroll form

    There are a few ways you can do this: Easiest is to just set NativeScrolling := True on the form. After that it just scrolls the whole form automatically, if the full contents are not visible. If you don't want the whole form to scroll, you can build all the scrollable contents inside a ScrollBox. Here you have two possibilities: TW3NativeScrollBox or TW3ScrollBox. If you use TW3ScrollBox, make sure to create the contents inside it's content (MyCtrl := TWhatever.Create(ScrollBox.Content) and to call ScrollBox.UpdateControl when content is built and resized. The difference between the "native" solutions and TW3ScrollBox is, that TW3ScrollBox does all the scrolling with code, which lets you control everything better.
  8. 2 points
    jarto

    JSON Post

    Something like this: uses ... SmartCL.Net.Http, System.Codec.Url, System.JSON; type ... TMyJsonCommand = record Command: String; Table: String; Code: String; end; ... procedure SendAndReceiveExample; var JsonToSend: TMyJsonCommand; begin var httpcli := TW3HttpRequest.Create; httpcli.OnDataReady := procedure(Sender: TW3HttpRequest) var JsonData: Variant; begin JsonData := TJson.parse(Sender.ResponseText); WriteLn('Code received: '+JsonData.Code); WriteLn('Name received: '+JsonData.Name); end; httpcli.Open('POST','https://example.com:8082/wherever.it.is'); httpcli.RequestHeaders['Content-type']:='application/x-www-form-urlencoded'; JsonToSend.Command := 'Open'; JsonToSend.Table := 'Client'; JsonToSend.Code := 'XXXX'; httpcli.Send('json='+TURLCodec.Encode(TJson.stringify(JsonToSend))); end; And if you get this to work locally but not on mobile devices, you solve it on your server side by adding the necessary CORS headers.
  9. 2 points
    IElite

    W3webSocket

    uses W3C.DOM, SmartCL.Net.websocket; FSocket.Connect("your url here", []); FSocket.OnOpen:= Procedure (Sender:TW3WebSocket) begin writeln("Socket connected"); Sender.Write("your data here"); end; FSocket.OnClosed:=procedure (Sender:TW3WebSocket) begin Writeln("Socket disconnected"); end; FSocket.OnMessage:= Procedure (Sender: TW3WebSocket; Message: TWebSocketMessageData) begin Writeln("Data received:" + Message.mdText); end; FSocket.OnError:= procedure (Sender: TW3WebSocket; const Error: JEvent) begin writeln("Socket has errors"); end;
  10. 1 point
    DaveK

    NodeJS and HTTPS

    I see - fascinating! And thank you for a detailed and well thought out answer. I have an interest in such things because I myself have written a Pascal like compiler - from scratch. Well, it will eventually be a compiler when it is capable of actually generating object code. At present it only reads in a program and creates a tree out of objects. The constructor of each object reads in the program and creates sub objects that mirror the recursive structure of the program and resolving procedure, function calls and variable references along the way. It uses the shunting yard algorithm for constructing expressions. The tree is executable, and at present I'm using it as a simplified scripting language as a machinery control front end. I say simplified in that it is even more "wordy" than actual Pascal - i.e. many operators that are usually single character symbols can optionally be replaced with words. So a program can almost read like plain English for the benefit of the typical non programmer machine operators that use it... I also built in a feature where it can store its stack in a disc backed file so that it can resume execution after a power or computer failure etc. - that's useful in India where there are plenty of unpredictable power failures. The tree also stores pointers back to the corresponding source which allows the execution to be viewed in realtime etc. At present it only has 4 data types (Boolean, Integer, Real and String), and arrays - no type definitions or records yet. Variables can have read and write functions and procedures associated with them - so reading and writing to variables can have side effects - similar to properties. My eventual plan for that (mainly out of fascination with this sort of thing) is to extend it to output arbitrary assembler or machine code by using itself as a backend generator - i.e. the definition of the target CPU or assembler would itself be written in the language - allowing easy extension to new CPUs and differing architectures etc. I mainly view it as an interesting hobby - certainly nothing that hasn't been done before!
  11. 1 point
    IElite

    W3webSocket

    Mine runs fine! See my code below. If yours does not work, you need to uninstall and re-install. First , Make sure you first save your projects folder (any work that is not part of SMS install) and your license (user.lic) file Next. Uninstall any versions you have installed AND/OR remove any old install locations delete all remaining occurrences of C:\Program Files (x86)\Smart Mobile Studio C:\users\<username>\My Documents\Smart Mobile Projects C:\ProgramData\The Smart Company\Smart Mobile Studio Next Re-install using : http://smartmobilestudio.com/alpha.zip (just unzip folder) Next Run the SmartUpdate.exe unit Form1; interface uses W3C.DOM, System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.Net.websocket, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} FSocket : TW3WebSocket; protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; end; implementation { TForm1 } procedure TForm1.W3Button1Click(Sender: TObject); begin FSocket.Connect("wss://echo.websocket.org",[],nil); end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} FSocket := TW3WebSocket.Create; FSocket.OnOpen:= Procedure (Sender:TW3WebSocket) begin ShowMessage("Socket connected"); Sender.Write("your data here"); end; FSocket.OnClosed:=procedure (Sender:TW3WebSocket) begin ShowMessage("Socket disconnected"); end; FSocket.OnMessage:= Procedure (Sender: TW3WebSocket; Message: TWebSocketMessageData) begin ShowMessage("Data received:" + Message.mdText); end; FSocket.OnError:= procedure (Sender: TW3WebSocket; const Error: JEvent) begin ShowMessage("Socket has errors"); end; end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  12. 1 point
    IElite

    Max length text in editbox

    or W3EditBox1.handle.setAttribute('maxlength', '5');
  13. 1 point
    lynkfs

    lazy loading SEO

    Checking the SEO audits on a webpage constructed with SMS, I noticed I got negative points for not lazy loading images. Lazy loading is to defer loading of images which are initially not visible when the page loads (below the fold), and to only start loading them when users scroll down sufficiently that they (probably will) become visible. There is the Intersection Observer Api which can help with that. See https://developers.google.com/web/updates/2016/04/intersectionobserver and https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API Have a form with an element (say button) and position the button so that it is not visible (top=1200px). Set up an IntersectionObserver and check the intersectionRatio in the asynchronous callback. Scrolling down so that the button becomes visible changes the form's background. procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components NativeScrolling := True; W3Button1.Top := 1200; procedure mycallback(changes: variant); begin If changes[0].intersectionRatio > 0 //between 0 and 1 = partly visible then ThemeBackGround := bsDecorativeInvertBackground; end; var io : variant; asm io = new IntersectionObserver( @mycallback ); end; io.observe(W3Button1.handle); end; Note - does not work in ide but works fine from file or server - for lazy loading images thinking of a TW3Image descendant or class helper which implements a VisibilityChange callback which supplies src parameter ...
  14. 1 point
    IElite

    Where is rtl folder?

    When installing Smart Mobile Studio (SMS) the following is the default path: C:\Program Files (x86)\Smart Mobile Studio Once installed, the following folders are created: C:\Program Files (x86)\Smart Mobile Studio C:\users\<username>\My Documents\Smart Mobile Projects C:\ProgramData\The Smart Company\Smart Mobile Studio NOTE: During the install, no Registry entries are created When running SMS for the first time, the following files are created in the following folders C:\ProgramData\The Smart Company\Smart Mobile Studio 1. User.lic 2. Keyboard _Shortcuts.XML C:\ProgramData\The Smart Company\Smart Mobile Studio\Layouts 1. DefaultLayout.Layout (configuration file) NOTE: During the first run, the following registry entries are created HKEY_CURRENT_USER\Software\Optimale Systemer AS HKEY_CURRENT_USER\Software\Optimale Systemer AS\Smart Mobile Studio HKEY_CURRENT_USER\Software\Optimale Systemer AS\Smart Mobile Studio\FormMain 1. Flags 2. FormVersion 3. MinMaxPos 4. NormPos 5. PixelsPerInch 6. ShowCmd
  15. 1 point
    Ricardo

    Multi form app, menu and navigation

    Thx!
  16. 1 point
    IElite

    Multi form app, menu and navigation

    That is just a TW3Header Just add the image on the headers BackButton and NextButton W3HeaderControl1.BackButton.InnerHtml:= '<img src="res/back.png" >'; W3HeaderControl1.NextButton.InnerHtml:= '<img src="res/menu.png" >'; You can get your button glyphs here: https://fontawesome.com/ To make it so it show through out the entire project https://jonlennartaasenden.wordpress.com/2018/01/03/fixed-header-in-smart-applications/
  17. 1 point
    IElite

    Add components

    You can create your own control be deriving from an existing class https://smartmobilestudio.com/documentation/creating-your-own-controls/ You do not need to add it to the IDE, you can just add the unit to your uses clause in your project's unit that needs it
  18. 1 point
    lynkfs

    Async keyword

    2) Async/Await See previous posts in this thread. Async functions are basically functions returning promises, but in disguise. All Async functions can be re-written using Promises : This ES6 javascript code async function addAsync(x) { const a = await myAsyncProc(10); return x+a; } is equivalent to this ES6 javascript code function addPromise(x){ return new Promise(resolve => { MyAsyncProc(10).then((a) => { resolve(x + a); }) }); } which is equivalent (sort of) to this Smart code function resolve(arg: variant): variant; begin writeln('async/await'); result := 75+arg; end; procedure await(ok,err: procedure(result:variant); arg:variant); begin TW3Dispatch.Execute(procedure() begin ok(resolve(arg)); end, 2000); end; function MyAsync(arg:variant): variant; begin var q : variant; asm @q = new Promise(function (ok,err) { @await(ok,err,@arg) }); end; result := q; end; MyAsync(60).then(procedure(val:variant) begin writeln(val); // 75 + 60 = 135 after 2 seconds MyAsync(val+10).then(procedure(val:variant) begin writeln(val); // 75 + 135 + 10 = 220 after 4 seconds end); end); function "MyAsync" returns a promise "q" which "resolve"s after the asynchronous "await" process ends and is invoked by calling the "MyAsync.then" method (which can be stacked). works, but not liking it very much. 3) TW3Dispatch The polling construct below is used in the rtl in some places and is much more compact. Depends on suitability procedure MyAwait; begin if (completion of asynchronous process = true) then begin // proceed end else //If not, call back in 100ms TW3Dispatch.Execute(MyAwait, 100); end; MyAwait;
  19. 1 point
    lynkfs

    Async keyword

    Some modern libraries return Promises or require Async/Await type of constructions in order to use them. While these javascript constructs are not supported natively by the compiler, sometimes it may be necessary to use them. Hopefully very seldomly I must add. 1) Promise Essentially, a promise is a returned object to attach callbacks to. See https://developers.google.com/web/fundamentals/primers/promises for an indepth article on Promises. The javascript syntax is (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) let myFirstPromise = new Promise((resolve, reject) => { // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed. // In this example, we use setTimeout(...) to simulate async code. // In reality, you will probably be using something like XHR or an HTML5 API. setTimeout(function(){ resolve("Success!"); // Yay! Everything went well! }, 250); }); myFirstPromise.then((successMessage) => { // successMessage is whatever we passed in the resolve(...) function above. // It doesn't have to be a string, but if it is only a succeed message, it probably will be. console.log("Yay! " + successMessage); }); and the closest I could make it in Smart without having to write any javascript code (except for the static 1-liner constructor) //Promise function resolve: variant; begin result := 'successful'; end; procedure async(ok,err: procedure(result:variant)); begin //do something async : readfile, ajax call etc. In this case: timeout TW3Dispatch.Execute(procedure() begin ok(resolve); end, 2000); end; var p : variant; asm @p = new Promise(function (ok,err) { @async(ok,err) }); end; p.then(procedure(val:variant) begin writeln(val); // 'successful' after 2 seconds end); 'p' is the Promise object, 'async' is the method where something asynchronous takes place, 'resolve' (or 'reject') is the method invoked depending on the result of the async operation and the 'p.then' method finally executes after the promise is fulfilled. Just looking at it, the best thing to say about it that it does work, however the whole promise construct strikes me as overly complicated. I would not use it unless really necessary. Nevertheless, some libraries have functions which simply return promises, so need to be able to deal with those. 2) Async/Await See previous posts in this thread. Async functions are basically functions returning promises, but in disguise. All Async functions can be re-written using Promises.
  20. 1 point
    DaveK

    NodeJS and HTTPS

    Hi Lennart, Wow - yes please! I can't believe I've only just come across Smart Pascal. I've thought for a long time that as a language Object Pascal deserves universality. You guys are proving that that's possible - bringing clarity to the web! There's a reason that Wirth derived languages are used in safety critical engineering - you guessed it - clarity (structured text in safety PLCs, Ada in military avionics systems). Because that's what engineers with big responsibilities need! So I for one believe you guys are going in the right direction.
  21. 1 point
    IElite

    SVG position

    So then this will work W3Button1.handle.style.setProperty('background-image', 'url("res/blank.png")'); W3Button1.handle.style.setProperty('background-repeat', 'no-repeat'); W3Button1.handle.style.setProperty('background-size', '100%'); Assuming you are resizing your button first e.g. procedure TForm1.Resize; begin inherited; W3button1.SetBounds(); end;
  22. 1 point
    Igor Savkic

    Confused on variants

    //compiles but gives execution error : Uncaught TypeError: MyList1.Business.Depts[0].Staff.add is not a function MyList1.Business.Depts[0].Staff.add('st4'); //works. Why does 'push' work but 'add' doesn't ? MyList1.Business.Depts[0].Staff.push('st5'); AFAIU you're working here with raw Variant and Staff is JS array object that doesn't have add function defined only push: //lowercase 'staff'. Compiles but gives execution error : Uncaught TypeError: Cannot read property 'push' of undefined MyList1.Business.Depts[0].staff.push('st6'); JS functions and properties are case sensitive so there's no staff defined only Staff.
  23. 1 point
    lennart

    databases and applications

    This is taken into account with the framework we are researching / making now. The objects you use to access data act more as "front-ends" to the mechanisms behind it, and just like the filesystem classes the operations are all async. This has the benefit of decoupling the consumer-part (i.e controls + bindings) from the producer aspect. To the consumer parts the database can be resident - or on the other side of the planet, it cares not where the data originates, only that its made available in a format that it can use. A typical middleware entity would be a node.js server that is connected to X number of databases. The client will use the framework and connect to the node.js endpoint, but neither the controls or bindings will have any clue about the data originating from a server. It will simply see a driver and issue calls to it. When the driver (or class) gets the data it will validate and dispatch it via its internal mechanisms. The nice part about this is that, a datasource can be almost anything. As long as someone implements the 3 core classes, it can be anything from a hardcoded file, local database or remote data server for that matter. I will go into more detail about these classes when we are a little closer, but you are quite right that old-school 1:1 endpoints is useless in the new paradigm
  24. 1 point
    lennart

    Black Screen of Death

    One thing that can be a bit of a challenge when coming from Delphi or FPC, is that Javascript is 100% ASYNC by nature. This means that from the time that you call the constructor, to the time that the element has been successfully created, styled and injected into the DOM - 1000 other things can happen in between. This is true for everything, even loading third party libraries, css files, images; everything is async. There is no such method in our core RTL that you describe (CreateRootElement), this is a part of Chrome itself. So unless you are using a third party library like jquery or something that doesnt synchronize with our RTL - this is too little to go on to properly help you. But when you run your program, click the buttons in the following order, check the "pause on caught exception" (note: there is a deliberate exception when the program starts checking for a feature, but this is handled so just click the blue arrow to continue). Below you see how the callstack looks for an exception i intentially created. From the event being issued, delegated, dispatched and handled. But i doubt this is where your problem stems from. I think this has to do with synchronization and that you are expecting code to execute linear. Hence your code crash because whatever you are trying to access is either not finished yet, fully loaded or in the process or being created. It is a typical thing to experience when coming from Delphi or FPC, because you are used to linear execution. Here are a few pointers on how to synchronize your code and what to override. Because there are 3 substancial differences (InitializeObject, FinalizeObject and ObjectReady. More about those as we move along). When you first look at examples etc. you will notice calls like TW3Dispatch.Execute() which is often used to create small timed delays. A bit like TTimer in Delphi but wrapped into a single method. The same class, TW3Dispatch also has other handy methods. You will find it in System.Time.pas and SmartCL.Time.pas. Smart supports partial classes, which means the content of classes can be finished and/or added to in different units (much like C#, C++ and Java has; with emphasis on Mono C#). This is very effective when building multi target frameworks. The second thing to explore is the use of callbacks when writing code that should run in a purely async environment. This can take some getting used to, but once its in your fingers you will wonder how you ever lived without it (or at least parts of it). So learning to properly synchronize things is the key here. This is why you never override the constructor in Smart, you override InitializeObject() and FinalizeObject() instead which ensures a level of synchronized execution. For example, let's say you are loading a file right? Since JS is async that means whatever Load() routine you use (even direct access to the filesystem under node.js) will finish and exit before any data is available. This can be bewildering to say the least. Until you start to make some observations on how it's best handled. Loading files can be done in many ways, but for browser apps -using the methods in TW3Storage is the easiest (in SmartCL.FileUtils.pas). Here is an example of how you could use callbacks in your own classes to better deal with async IO: // First lets define a callback. Just like events they dont need "of object" postfix // or "reference to" prefixing. Its fairly straight forward stuff. // Note: The procedure we define is actually "TStdCallback" in System.Types.pas type TMyDataClassReadyCB = procedure(Success: boolean); // OK now lets imagine we have a class for loading some text data. It takes text and builds // an object model from this. If the object model is loaded and valid then Resize() will // do layout and show the data. So let's define a loading method that takes our // callback as a parameter: procedure TMyDataClass.LoadDataFromURI(const URI: string; const CB: TMyDataClassReadyCB); begin // We let TW3Storage do the dirtywork. Note that there are several overloaded // methods for loading data in the class. This one loads any file and // returns it as text, but you can also get the data as a TStream (have a peek at the class) TW3Storage.LoadFile(URI, procedure (const FromUrl: string; const TextData: string; const Success: boolean) begin // Loaded ok? Then keep the data if Success then SetRawData(TextData); // Regardless of outcome, always call back! if assigned(CB) then CB(Success); end); end; // When calling the method from the UI, say a button-click, we can be more // flexible with how we handle control states. The callback will fire // regardless of success or failure procedure TForm1.BtnLoadDataOnClick(Sender: TObject) begin // Disable load button and listbox while we are loading. // Also set the cursor to a progress animation BtnLoadData.enabled := false; lstDataView.enabled := false; Cursor := crProgress; // Now use our loading method FMyData.LoadDataFromURI('res/defaultdata.txt', procedure (Success: boolean) begin // Loading is done! Regardless if it failed or not we adjust // the UI's state back to how it was. With one exception, // namely that we let our list remain disabled if no data is present Cursor := crDefault; BtnLoadData.enabled := true; lstDataView.enabled := Success; // Use an inline variable to remember if our list // held any data and was cleared var HadVisibleData := lstDataView.Items.Count > 0; // OK let's build a data-model if we got some data to work with. // If the load failed then clear the list. If the load failed the list // has already been disabled (see above) so we just need a clear() call. // Javascript is laid back, i prefer to be more strict. lstDataView.BeginUpdate(); try // Flush our list if it had content if HadVisibleData then lstDataView.Items.Clear(); // Any data loaded? Build an object model from it if Success then buildDataModel(); finally // Mark the list as needing a Resize() call if we either // cleared old data, loaded new data -or both! if HadVisibleData or Success then lstDataView.AddToComponentState([csResizing]); // End update, it's all good lstDataView.EndUpdate(); end; end); end; One of the root classes for visual controls, TW3TagContainer, has a method you want to investigate. It will save you a lot of time: // Does a check if the child elements are in ready-state function QueryChildrenReady: boolean; virtual; This method will recursively check that all child controls have been created and have their csReady flag set in the ComponentState value-set. I find this to be very handy when writing large and complex visual controls where you need to know 100% that children are ready before you access them. The way JS works is that even though the constructor is finished and the element created --the prototype can still be under construction in the background. It is very rare to encounter this in our new RTL, but it can occur if you expect controls to be ready after a call to Create(). There is also a method to synchronize resize calls. This is quite handy to add in the ObjectReady() method. All visual elements have an ObjectReady() method that is called after the element has been injected into the DOM. This executes just between the time when an element is finished, but before it's visually presented. If you want an immediate resize when all child controls are created and ready, calling ResizeWhenReady() is the ticket. procedure TW3MovableControl.ResizeWhenReady; Next there is type helpers. Handles for example come in many shapes, and most of them (like THandle, TControlHandle, TGradientHandle, TCSSStyleHandle) have a helper class with low-level methods. Sadly these are not always shown in the code suggestion dialog, but that will be improved on in future updates. Now TControlHandle has a method called ReadyExecute(), and as the name implies this sets a callback that fires when the element has been created and been injected into the DOM. But this is low-level so we are not talking about the whole object pascal custom-control, but rather the raw HTML element that the control manages. It should be noted that overriding ObjectReady() is the proper way to do things, but sometimes you have to "bend the rules". While a bit esoteric looking calling a method on a handle, this is perfectly valid: procedure TSomeControl.InitializeObject; begin inherited; ... Handle.ReadyExecute( procedure () begin // Do something at once when the element is injected into the DOM. end); end; I think these will help you synchronize your code when porting from Delphi or FPC. In 99% of cases where people experience problems with porting over code, it comes down to synchronization. The JavaScript Virtual Machine simply doesnt follow conventional behavior and proper use of callback handlers, events and delayed execution is the way things work. The bonus is that you are not boxed into a "fake" linear environment (which could be achieved), communicating with JS libraries and mapping directly to existing JS objects is 1:1 rather than resorting to elaborate interface files. Paradoxical as it may sound, with correct use you can easily produce code that runs faster or en-par with the tools you are used to. We have node.js code in our lab that actually out-performs native, monolithic code. When you add clustering and domain level programming to that - the possebilities are near endless. Have a peek at this demo for example: http://quartexhq.myasustor.com/applications/particles/ We use TW3Dispatch.RequestAnimationFrame() to ensure a steady 60fps rendering, but it runs fine at 120fps on relatively modest PC specs. So its a matter of going with the flow and learn the subtle differences.
  25. 1 point
    lennart

    [2.9.9.79] CSS Theming issues

    This has been raised before. The image border is simply a part of the theme. Each theme will have properties that reflect the platform they represent. If you dont like a style you can call ThemeReset() on the control to get rid of it. // Completely reset theme and styling W3Image1.ThemeReset(); ThemeReset removes the theming styles, but TW3Image can also have some residual settings. In this case, just blank out the StyleClass property completely: W3Image1.ThemeReset(); w3Image1.StyleClass :=''; If you want your own style, I would suggest just using the GlobalStyleSheet() instance and add your CSS there. Having an extra CSS file just for small potatoes is overkill. You can get the global stylesheet (which is a sheet created on demand and used by our effects and other sub systems): // Get global stylesheet var styles := TW3StyleSheet.GlobalStyleSheet(); // Append our css snippet (Note: Only add once, good for unit Initialization section) styles.append(#".MyImageStyle { /* Add your css here } "); // And apply our custom style, perhaps a nice border, no border, gradient background W3Image1.StyleClass := '.MyImageStyle'; That should do the trick!
×