Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 04/14/2019 in all areas

  1. 3 points
    jarto

    UI layout

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

    Sorting arrays and lists

    This is a tutorial for sorting arrays and lists in Smart Mobile Studio. String arrays and TStringList are pretty easy to sort. You only need to call Sort: var strarr: array of String; ... strarr.Sort; var strlist: TStringList; strlist:=TStringList.Create; ... strlist.Sort; But how do you sort arrays of records or arrays of classes? For example: TSortData = record Id: Integer; Data: String; end; You can't simply call Sort as your application does not know how you want to sort the records. You solve this by using a comparison function: function CompareSortData(Item1, Item2: TSortData): Integer; begin result:=CompareStr(Item1.Data, Item2.Data); end; . . . var objarr: array of TSortData; ... objarr.Sort(@CompareSortData); The comparison function should return a negative number if Item1 is smaller, positive number if Item1 is bigger and 0 if they are equal. In Smart Mobile Studio you can also use anonymous methods: var objarr: array of TSortData; ... objarr.Sort( function(Item1, Item2: TSortData): Integer begin result:=CompareStr(Item1.Data,Item2.Data); end); Comparison functions can also be used for TList and even for TStringList. For example, you may want to have greater control over how names are sorted (eliminating prefixes like von): function EliminatePrefix(Name: String): String; const Prefixes: array[0..1] of String = ['von ','af ']; begin result:=Name; for var a:=0 to High(Prefixes) do begin if pos(Prefixes[a],result)=1 then begin Delete(result,1,Length(Prefixes[a])); exit; end; end; end; . . . var StrList:=TStringList.Create; StrList.Add('Perry'); StrList.Add('Miller'); StrList.Add('von Essen'); StrList.Add('Stockton'); StrList.Add('af Trolle'); StrList.Sort( function(Name1,Name2: String): Integer begin result:=CompareStr(EliminatePrefix(Name1),EliminatePrefix(Name2)); end); for var a:=0 to StrList.Count-1 do WriteLn(StrList[a]);
  4. 2 points
    jarto

    UI layout

    ' Working on this now. Align in the RTL is at pretty good shape already.
  5. 2 points
    lynkfs

    css styling

    Thanks There are however a couple of minor / major (?) caveats doing it like this : I made up some more metro components, and it becomes apparent that it is unavoidable to use more modifier classes: TagStyle.Add('primary image-button outline icon-left rounded etc). this type of styling is not supported by the visual designer in the ide, so styling can be done in code only. Even though only one-liners (TagStayle.Add( ) (most rtl components (but not all) have a component, a border and a background theme class baked in. Not all of these can be reused, so sometimes the border and/or background classes may end up being empty) probably the second bullet (visual designer) is the most important one for future enhancements
  6. 2 points
    lynkfs

    css styling

    Easy to use... Suppose we want to develop a Metro theme To take buttons as an example, the official metro style has a number of predefined buttons : To replicate the above, and do it in such a way that : the component code (TW3Button) does not need any change the standard theming classes still apply (TW3Button TW3ButtonBorder TW3ButtonBackground), but with different content of course while allowing for these different predefined component versions is to do something like this procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components var Button1 : TW3Button := TW3Button.Create(self); Button1.SetBounds(20,20,83,40); Button1.Caption := 'Default'; var Button2 : TW3Button := TW3Button.Create(self); Button2.SetBounds(110,20,83,40); Button2.Caption := 'Primary'; Button2.TagStyle.Add('primary'); var Button3 : TW3Button := TW3Button.Create(self); Button3.SetBounds(200,20,83,40); Button3.Caption := 'Secondary'; Button3.TagStyle.Add('secondary'); var Button4 : TW3Button := TW3Button.Create(self); Button4.SetBounds(290,20,83,40); Button4.Caption := 'Success'; Button4.TagStyle.Add('success'); end; result : with the metro css file for the above looking like this /* ////////////////////////////////////////// // TW3Button (default) ////////////////////////////////////////// */ .TW3Button { font-weight: 400; text-align: center; white-space: nowrap; vertical-align: middle; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border: 1px solid transparent; padding: 0 0.75rem; -webkit-transition: all 0.15s ease-in-out; transition: all 0.15s ease-in-out; background-color: #ebebeb; color: #1d1d1d; cursor: pointer; outline: none; -ms-touch-action: manipulation; touch-action: manipulation; -webkit-appearance: button; /* font-size: 0.875rem; line-height: 34px; height: 36px; */ } .TW3Button:focus, .TW3Button:active { text-decoration: none; -webkit-box-shadow: 0 0 0 3px rgba(228, 228, 228, 0.45); box-shadow: 0 0 0 3px rgba(228, 228, 228, 0.45); } .TW3Button:hover { background-color: rgba(29, 29, 29, 0.1); } .TW3ButtonBackground { } .TW3ButtonBorder { } /* ////////////////////////////////////////// // TW3Button Primary ////////////////////////////////////////// */ .TW3Button.primary { outline-color: #75b5fd; background-color: #0366d6; color: #ffffff; } .TW3Button.primary:hover { color: #ffffff; background-color: #024ea4; border-color: #023671; } .TW3Button.primary:focus, .TW3Button.primary:active { -webkit-box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.45); box-shadow: 0 0 0 3px rgba(3, 102, 214, 0.45); } /* ////////////////////////////////////////// // TW3Button Secondary ////////////////////////////////////////// */ .TW3Button.secondary { outline-color: #b7c6cd; background-color: #607d8b; color: #ffffff; } .TW3Button.secondary:hover { color: #ffffff; background-color: #4b626d; border-color: #36474f; } .TW3Button.secondary:focus, .TW3Button.secondary:active { -webkit-box-shadow: 0 0 0 3px rgba(96, 125, 139, 0.45); box-shadow: 0 0 0 3px rgba(96, 125, 139, 0.45); } /* ////////////////////////////////////////// // TW3Button Success ////////////////////////////////////////// */ .TW3Button.success { outline-color: #adeb6e; background-color: #60a917; color: #ffffff; } .TW3Button.success:hover { color: #ffffff; background-color: #477c11; border-color: #2d4f0b; } .TW3Button.success:focus, .TW3Button.success:active { -webkit-box-shadow: 0 0 0 3px rgba(96, 169, 23, 0.45); box-shadow: 0 0 0 3px rgba(96, 169, 23, 0.45); } see demo (check click and hover states) What do you think ? By the way, this css file is made up by screenscraping, which is actually not too bad / timeconsuming. Alternatively it could also be composed out of relevant Tailwind functions, results would be very similar.
  7. 2 points
    jarto

    Development updates

    A new update is available. RTL: Improvements to TW3StringGrid: New column types: TW3StringGridNumericColumn TW3StringGridButtonColumn Added sorting New event: OnCellChanged TStringList and TObjectList: Add Sort() with CompareFunction. Fix CSS browser prefix for Firefox Themes: Use normal size and style text in buttons.
  8. 1 point
    Czar

    UI layout

  9. 1 point
    Czar

    UI layout

    Being able to make more complex UI that works on all devices will make life much much easier Looking forward to finished product.
  10. 1 point
    DavidRM

    UI layout

    Now add some TW3Labels to your layout demo and experience Real Pain. 😉
  11. 1 point
    lynkfs

    css styling

    Browsers are certainly talented as multi-language processors. They have to understand html, css, javascript, svg and lately web-assembly as a fifth language. XML and Json are format specifiers, but could be borderline categorised as languages as well. Smart elevates javascript to great heights, but sort of leaves the other ones at the periphery. Especially css. Css can be used for a) component styling including colours, fonts etc, b) visual layout of pages/forms and reacting responsively to screen size and orientation and c) interactivity in various forms. Some of this overlaps with the js domain. Smart basically uses css for a) but not for b) or c). Looking at various css frameworks out there, I would categorise these as on one end of the scale there are many UI kit frameworks where the emphasis is on components - buttons, accordions, cards etc. Sometimes as css stand-alone files, sometimes in conjunction with js. Most of these also have functionality for layout purposes, usually based on flexbox or internal grid structures. on the other end of the scale there are frameworks which forego the component focus, but instead provide a large number of elementary styling functions, which can be applied to any component. and many which occupy a space in between This post is about the open source Tailwind framework, belonging in the second category above, just to see how that would work in the Smart environment. Tailwind comes in 2 flavours : a static framework which can be accessed locally or from a cdn, and a more dynamic version. The latter can be tailored dynamically for any specific project and basically needs to be part of the build toolchain. To keep it simple I used the static version. Change the res/app.css reference to the tailwind css file (either just replace it or change the reference in the index.html file). Now any component can be made to use this styling. Because this is not more (or less) than a list of individual styling functions, the use boils down to defining what you want. Tell a story as in "give me a button with a green background, which turns to blue on hover, has rounded corners and white text and a shadow effect which makes it seem floating". In code : W3Button1.handle.className := "bg-green-500 hover:bg-blue-700 text-white rounded shadow-2xl"; The second button in the demo project (see below) has a different story (className sequence), which gives it a totally different look and feel. I kind of like this approach, it works very well with any component, thus also with the built in Smart components. It is a bit of a drag specifying these className sequences by hand, so it would be nice if this would be supported in the IDE. There are after all only a small number of categories of these elementary styling bits : backgrounds, typography, borders, spacing, sizing, svg, interactivity and effects. And a relatively limited list of functions per category (bg-green-500 means a green background, 500 is the fifth shade in the green colour scheme. So there will be a bg-green-600, a bg-blue-200 etc). Doable I think. Apart from these styling functions, Tailwind also has a number of layout functions. Smart has its own layout mechanism, however the Tailwind functions can be used as well. See the 'card' component in the demo. Demo and source
  12. 1 point
    Czar

    Development updates

    I like the lighter default theme for buttons. Thanks for the update.
  13. 1 point
    jarto

    TW3StringGrid is available

    I just updated the first post here with new events, column classes and sort documentation.
  14. 1 point
    DavidRM

    RTL Search

    I would find a built-in search of the RTL quite handy. Sorta like the "Find in Files...", where it brings up a list of hits, but searching the current SMS RTL folders and files instead. Could even just be a checkbox option in "Find in Files..." This would be a step in the direction of "better documentation". 😃 Or, at least, easier to review what's already available. Currently, I do this using Notepad++, but that doesn't let me open the results in the SMS IDE. Being in the IDE would provide access to "Find Declaration" and similar features. -David
×