TMS WEB Miletus
Miletus enables developers to create desktop applications with TMS WEB Core. Similaly to Electron it provides access to the local file system, shell dialogs, clipboard and much more.
Your first TMS Web Miletus Application
To create a new Miletus application, select the "TMS Web Miletus Application" from the wizard:
It generates a project similar to a TMS Web Application, with extra icon files and build configurations. For each supported platform there is a Debug-Platform
and Build-Platform
configuration. The difference between Debug and Build is the availability of the debugging tools. In Build mode the debugging tools are disabled.
The icon file can be changed through the project options:
You can now develop your application like you would normally do with a TMS Web Application.
Debugging and accessing the Developer Tools
Windows
Debugging on Windows is identical to a TMS Web Application
, it can be done through the
Developer Tools. When the application is deployed in Debug
mode Miletus adds a default
menubar to the application if a TMiletusMainMenu
has not been added to the main form. The
Developer Tools can be accessed with View > Toggle developer tools or via the F12
shortcut.
To force any window to have the Developer Tools opened after the given window is shown, use
the following code in the form's OnCreate
event:
Linux
To be able to debug on Linux, select the Debug-Linux64
build configuration, build your application and copy the resulting application to your target machine.
After these steps, you can debug your application in a similar way as on Windows: Either through the View > Toggle developer tools menu item or by calling the OpenDevTools method.
macOS
To be able to debug on MacOS on the target machine, open up a Safari instance and if you haven't already, enable the Develop menu item: https://support.apple.com/guide/safari/use-thedeveloper-tools-in-the-develop-menu-sfri20948
Select the Debug-MacOS64 build configuration, build your application and copy the resulting application to your target machine.
Sign your application along with the provided .entitlements
file, otherwise the necessary key for
debugging won't be picked up by the binary.
After that you'll be able to debug your running Miletus application by selecting Develop > Your
machine's name > main.html
from your running Safari instance.
Deployment
Set the configuration to the correct Build-Platform
target and Build the application. After that copy the resulting application to the target machine if that differs from the development machine. It's always recommended to sign the application afterwards.
macOS
After deployment if at application launch the message "“YourApp”
cannot be opened because the developer cannot be verified." is displayed, try Right click > Open which gives the option to open the application despite the lack of application signatures.
Depending on how the application is copied to the target machine the necessary read, write and execute permissions might be removed. If the error message "You do not have permission to open the application" is shown try setting the correct permissions:
Starting from Big Sur, on macOS ARM targets it is a requirement to sign the application. Miletus applications come unsigned on all platforms, so on a macOS ARM target they always need to be signed first:
If the code signing fails with the message "resource fork, Finder information, or similar detritus not allowed", remove the extended attributes by running the following command and sign the application afterwards:
Before distribution sign your application with your Developer ID certificate.
Linux or Raspberry Pi with Raspberry Pi OS
Depending on how the application is copied to the target machine the necessary read, write and execute permissions might be removed. If the application cannot be run due to missing permissions try setting them:
On Linux or Raspberry Pi, Miletus is usingGTK3
and the WebKitGTK
browser engine. If your
Linux system or Raspberry Pi does not have WebKitGTK
installed, run the following command:
Custom extensibility
It's possible to extend a Miletus application with custom native functionality through shared libraries.
Loading and unloading a library
The LoadLibrary(ALibraryPath)
and UnloadLibrary(ALibraryPath)
methods can be used to load and unload a library. LoadLibrary
is a TJSPromise
, and its return value can determine if the library could be loaded:
//Mark as async
[asnyc]
procedure WebButton1Click;
//Implementation
procedure TForm1.WebButton1Click(Sender: TObject);
var
b: Boolean;
const
LIBPATH = 'path\to\MyLibrary.dll';
begin
b := Await(Boolean, LoadLibrary(LIBPATH));
if b then
begin
//The library could be loaded, call ProcNoParam procedure
Await(JSValue, ExecProc(LIBPATH, 'ProcNoParam'));
//And finally, unload the library
UnloadLibrary(LIBPATH);
end;
end;
Example of Miletus compatible library from Delphi
unit UMyLibrary;
interface
uses
Classes;
procedure ProcNoParam; cdecl;
procedure ProcParam(AData: PChar); cdecl;
function FuncNoParam: PChar; cdecl;
function FuncParam(AData: PChar): PChar; cdecl;
exports
ProcNoParam,
ProcParam,
FuncNoParam,
FuncParam;
implementation
procedure ProcNoParam; cdecl;
begin
//
end;
procedure ProcParam(AData: PChar); cdecl;
begin
//
end;
function FuncNoParam: PChar; cdecl;
begin
Result := '';
end;
function FuncParam(AData: PChar): PChar; cdecl;
begin
Result := '';
end;
end.
Note: For Raspberry the shared library needs to be created from Lazarus.
Sending custom messages to a Miletus application
It's possible to send custom messages by implementing a RegisterCallback
procedure in the library.
type
TCallback = procedure(AMessageID: Integer; AData: PChar); cdecl;
var
MyCallback: TCallBack;
const
MYID = 123;
procedure RegisterCallback(AFunction: Pointer); cdecl;
begin
@MyCallback := AFunction;
//For Lazarus:
//MyCallback := TCallback(AFunction);
end;
procedure MyProcedure; cdecl;
begin
//Do something and call MyCallback
MyCallback(MYID, '{"Name": "My data", "Value": "This is my JSON formatted
data"}');
end;
To capture these messages, in the Miletus application use the MiletusCommunication.OnCustomMessage
event:
const
MYID = 123;
procedure TForm1.MiletusFormCreate(Sender: TObject);
begin
MiletusCommunication.OnCustomMessage := CustomTextMessage;
end;
procedure TForm1.CustomTextMessage(AMessageID: Integer; AMessage: string);
begin
if AMessageID = MYID then
begin
//Do something with AMessageText
//e.g. Create JSON object
end;
end;
Drag and drop
Miletus provides support for drag and drop functionality. There's a difference between dragging into and dragging out of an application. In both cases the dragging needs be detected by an event.
From desktop to Miletus
Dragging something into the application is a feature that is supported by HTML5.
procedure TForm1.WebMemo1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
f: TJSHTMLFile;
begin
f := TJSDragEvent(TDragSourceObject(Source).Event).dataTransfer.files[0];
//process the TJSHMTLFile futher...
end;
From Miletus to desktop
Dragging something out of an Miletus application is supported, but the file must already exist on the local file system. If the file does not exist, it is up to the developer to create it on the fly based on the contents from the application. If the file is present, then only the following code needs to be called with the path to the existing file:
procedure TForm1.WebMemo1StartDrag(Sender: TObject;
var DragObject: TDragObject);
begin
StartFileDrag('path\to\file');
end;
Helper methods
The following helper methods are available.
Keep in mind
The synchronous methods below only work on Windows. For other platforms or a cross-platform application it's better to use the async promise-based versions.
Property | Description |
---|---|
GetCursorPos: TPoint | Returns the cursor position. |
GetCursorPosP: TJSPromise | Promise based equivalent of GetCursorPos for cross-platform support. The return value of the TJSPromise is a TPoint that contains the on-screen mouse position. |
GetMiletusPath(APathType: Integer; var APath: string) | Retrieves the requested path. Accepted APathType values are: NP_APPDATA , NP_APPPATH , NP_DESKTOP , NP_DOCUMENTS , NP_DOWNLOADS , NP_EXE , NP_HOME , NP_MUSIC , NP_USERDATA , NP_PICTURES , NP_TEMP and NP_VIDEOS |
GetMiletusPathP(APathType: Integer) | Promise based equivalent of GetMiletusPath for cross-platform support. The return value of the TJSPromise is a string that contains the path. |
GetMiletusFilesP(ADirectory: string; AFilter: string) | Promise based function to return the files in a given directory. The AFilter parameter is optional. |
GetMiletusDirectoriesP(ADirectory: string; AFilter: string) | Promise based function to return the subdirectories in a given directory. The AFilter parameter is optional. |
GetOSVersionP: TJSPromise | Promise based function to return the OS information. The return value of the TJSPromise is a TMiletusOSVersion record, which contains the Platform , Architecture , Name, Build , Major and Minor properties of the OS. TMiletusOSVersion.ToString contains the OS version as a formatted string. |
MiletusTerminate | Method to terminate the application from any Miletus window. |
OpenDevTools | Method to open the developer tools. Only works on Windows and Linux . For macOS please refer to the Debugging and accessing the Developer Tools part of the documentation. |
StartFileDrag(APath: string) | Method to start the file dragging at the given APath. |
LoadLibrary(ALibraryPath: string): TJSPromise | Dynamically loads a library by AName . This TJSPromise returns with a Boolean value which indicates if the loading was successful. |
UnloadLibrary(ALibraryPath: string) | Dynamically unloads a library by AName . |
ExecProc(ALibraryPath: string; AProc: string): TJSPromise | Call AProc procedure from ALibraryPath loaded library. |
ExecProc(ALibraryPath: string; AProc: string; AData: string): TJSPromise | Overload of ExecProc(ALibraryPath, AProc) with AData parameter which can be used to send some data over to the library. |
ExecFunc(ALibraryPath: string; AFunc: string): TJSPromise | Call AFunc function from ALibraryPath loaded library. This TJSPromise returns with a string value. |
ExecFunc(ALibraryPath: string; AFunc: string; AData: string): TJSPromise | Overload of ExecFunc(ALibraryPath, AFunc) with AData parameter which can be used to send some data over to the library. This TJSPromise returns with a string value. |