Most Recent Post | Index List
Wednesday, February 20, 2019A few months ago we have started our experimental work with Electron. After the working proof of concept, our goal was to wrap as many Electron API for the Delphi developers as possible. But of course it's not just the Electron API that can be used, you can easily transfer your already existing code to make it a desktop application! We have already given a small teaser about this in a previous blog post.
Right now we would like to take the opportunity to give a bit more insight on the new upcoming TMS Web Electron Application support.
You are probably already familiar with creating a TMS Web Application or a progressive web application. The procedure is identical, and all you have to do to create a new Electron application is to select the TMS Web Electron Application from the wizard:
You can now develop your application like you would normally do with a TMS Web Application.
Upon pressing F9 in Debug mode, the project will compile as usual, then the Electron engine starts up and launches the application.
When it comes to Build mode, there are little differences and platform limitations. Pressing F9 in Windows 32/64-bit Build mode will package the application and launch it. Pressing F9 in Linux 32/64-bit Build mode will create the packaged application for Linux, which then can be moved onto a Linux machine and launched. While macOS 32/64-bit Build mode is added, unfortunately currently it's not possible to create a packaged macOS application from a Windows machine. But the good news is that a packaged macOS 64-bit application still can be made by copying the source files to a prepared Mac machine, and running a single command. Not to mention that if you would like to distribute a macOS application, then it's recommended to sign it, which can only be done on a Mac.
Creating a photo editor application
Electron provides a way for TMS Web Applications to create cross-platform (Windows/macOS/Linux) desktop applications and to use native operating system calls and directly access the local file system. So we would like to show the power of this by creating a basic photo editor application.
To keep this simple for now, we are only going to allow to apply filters on a selected photo through the CSS filter property, which is already an easier and faster approach than writing it for example in VCL since image filters like blur, contrast, gray-scale ... are not out of the box available in VCL . As a first step we add every component we will need in our photo editor.
Of course we cannot edit an image if we don't have an image. To tackle this problem, we are going to use TWebImageControl, TElectronMainMenu and TElectronOpenDialog. With TElectronMainMenu we can add a menu bar with menu items such as 'Open' or 'Save as...'. In the OnClick event of the Open menu item, we can then execute the TElectronOpenDialog and use the retrieved file name to load the image from the local file system.
With the image in place, we still need something on which we can apply the filter. Luckily, a TJSHTMLElement is just what we are looking for because it has a style property. Let's retrieve our TWebImageControl's TJSHTMLElement at form creation.
procedure TForm1.Open1Click(Sender: TObject); begin if ElectronOpenDialog1.Execute then begin WebImageControl1.Picture.LoadFromFile(ElectronOpenDialog1.FileName); end; end;
Now we have everything to apply the CSS filters. Instead of adding and removing filters as they change, a simpler solution is to store all the filters in their own string properties. When a filter trackbar changes, we update the given string property, and concatenate it with the rest of the filter strings. As a last step we apply this single filter string to our image.
procedure TForm1.WebFormCreate(Sender: TObject); begin FImgEl := TJSHTMLElement(document.getElementById(WebImageControl1.ElementID)); end;
With this approach, resetting the filters is also as easy as setting the single filter string to the basic values and resetting the trackbars into their original position.
procedure TForm1.BrightnessTBChange(Sender: TObject); begin FBrightnessFilter := ' brightness(' + IntToStr(BrightnessTB.Position) + '%)'; CreateFilter; end; procedure TForm1.CreateFilter; begin FFilter := FBlurFilter + FBrightnessFilter + FContrastFilter + FGrayscaleFilter + FHueRotateFilter + FInvertFilter + FOpacityFilter + FSaturateFilter + FSepiaFilter; FImgEl.style.setProperty('filter', FFilter); end;
This was not complex so far, and our application is already almost complete, but the saving feature is still missing. For that, we are going to use TElectronSaveDialog, TElectronBinaryDataStream and TJSHTMLCanvasElement.
We can add a 'Save as...' menu item to our menu bar just as we did with the 'Open' menu item, then in the OnClick event we are going to write the logic to the image saving.
By default if you are trying to save a picture that has a filter applied, it's going to save the original image without the filter. To make things worse, the generally accepted approach is not supported by every browser. Fortunately Chromium supports it, and it allows us to create our application without any drawbacks.
procedure TForm1.Save1Click(Sender: TObject); begin if ElectronSaveDialog1.Execute then begin //save image end; end;
Now if you take a look at what we have so far, sooner or later you will notice that the 'Save as...' menu item can be clicked even if no image is loaded to our application, which is not something we want. There's a quick fix for that by introducing a flag.
procedure TForm1.Save1Click(Sender: TObject); var canvas, el: TJSHTMLCanvasElement; ctx: TJSCanvasRenderingContext2D; bd: TElectronBinaryDataStream; w, h: Integer; begin if ElectronSaveDialog1.Execute then begin //1: get image as TJSHTMLCanvasElement el := TJSHTMLCanvasElement(document.getElementById(WebImageControl1.ElementID)); asm w = el.naturalWidth; h = el.naturalHeight; end; //2: create the canvas element canvas := TJSHTMLCanvasElement(document.createElement('canvas')); canvas.width := w; canvas.height := h; ctx := canvas.getContextAs2DContext('2d'); asm ctx.fillStyle = "rgba(255, 255, 255, 0.0)"; //set a transparent background ctx.filter = el.style.filter; //3: add the filter end; ctx.fillRect(0, 0, w, h); //4: render our image ctx.drawImage(TJSObject(el), 0, 0, w, h); //5: save to file bd := TElectronBinaryDataStream.Create; bd.Base64 := canvas.toDataURL; bd.SaveToFile(ElectronSaveDialog1.FileName); end; end;
After modifying our 'Save as...' menu item by setting it to be disabled by default, we can do the following to enable it after the first image has been opened:
You might be wondering why the TElectronMainMenu.EndUpdate call is necessary. It's a shortcoming of Electron, where you cannot modify the menu dynamically. Each time the menu is modified, it needs to be reassigned to the window. To make this procedure less problematic to the Delphi developers, calling EndUpdate will recreate the menu bar and reassign it to the window.
procedure TForm1.WebFormCreate(Sender: TObject); begin FImgEmpty := True; //flag FImgEl := TJSHTMLElement(document.getElementById(WebImageControl1.ElementID)); end; procedure TForm1.Open1Click(Sender: TObject); begin if ElectronOpenDialog1.Execute then begin WebImageControl1.Picture.LoadFromFile(ElectronOpenDialog1.FileName); if FImgEmpty then begin Save1.Enabled := True; ElectronMainMenu1.EndUpdate; FImgEmpty := False; end; end; end;
With this last small addition, we can say that we have created a basic and simple cross platform photo editor application that is capable of:
- opening a local file system image via a native open dialog
- applying filters using CSS
- and saving images to the local file system via a native save dialog.
And of course it's running on the 3 major operating systems: Windows, macOS and Linux using a single source code-base!
Building cross platform Electron application support is going to be the part of the TMS WEB Core v1.2 Padua release, and soon we are going to provide the first BETA to TMS ALL-ACCESS users! Don't miss out and get started today with TMS WEB Core! You can download the trial version that is generally available, go ahead with the standalone version you purchased or with TMS WEB Core and additional tools that are all included in TMS ALL-ACCESS. Our team looks forward to all your comments, feedback, feature-requests to help steering the development of TMS WEB Core to the next stages!
This blog post has received 4 comments.
Most Recent Post | Index List