-
Content Count
65 -
Joined
-
Last visited
-
Days Won
1
Everything posted by Alexander Halser
-
Cross-platform Application Help for FMX and VCL
Alexander Halser replied to Alexander Halser's topic in Cross-platform
No, it is not derived from CHM. Because CHM is basically Windows only and not cross-platform. Yes. plain HTML5 and based on the regular "sitemap" XML schema. When you create CHM files for a Windows application, you typically use an authoring tool for this and don't craft the HTML in Notepad. Those authoring tools which create CHM files usually create plain HTML5 as well. The example help you see in the demo project was created with Help+Manual, it's plain vanilla HTML5 and Javascript. But not just Help+Manual produces this output, Madcap Flare does, Adobe Robohelp does, you name it. And most authoring tools offer some kind of syntax to use the plain HTML5 output for context-sensitive help. Unfortunately, everyone has their own syntax, there is no established standard. And because of a missing standard, there are no viewer applications, as long as everyone does their own thing. Ziphelp is an attempt and a ready-to-use solution for this dilemma. It is as open as possible, because it is based on the standard sitemap protocol. This is the same sitemap.xml that you use on your website to help Google index it. Ziphelp is basically a legal extension of this protocol. Open for everyone to produce content that includes a Ziphelp sitemap and open for everyone to create an HTML viewer that can read it. To give it a head start, we have created a freely distributable HTML viewer that reads the Ziphelp protocol (https://www.helpandmanual.com/ewriter/). Some people prefer this solution because it's less work for them. But for Delphi developers, I think that a native cross-platform implementation is more appealing. Here, everything is under your control and your application can interactively question the help system if it actually "supports help context number 45327" or if that number is outdated in the UI of your app (and you should clean it up, but at least you don't let your end users run into an 404 error). This interactivity is missing even with CHM files. So, here you go. A free Delphi implementation that supports HTML5 in a zip archive, uncompressed HTML or web-based content. It all works with the same protocol and the same mechanism, on all platforms. You can create this HTML5 help system with our tool Help+Manual, of course. But you don't have to. If you prefer a different authoring tool, go for it. -
Cross-platform Application Help for FMX and VCL
Alexander Halser replied to Alexander Halser's topic in Cross-platform
The project now has been moved to Sourceforge: Delphi Cross-Platform Application Help download | SourceForge.net -
For a new FMX app (Win/Mac only), I need some UI controls that go beyond the simple features that the Delphi built-in controls have to offer. One of them is a color picker component. There are very few color picker tools available for FMX and creating a new one turned out to be more work than anticipated. That's why I put mine on SourceForge. If you require a similar control, feel free to use it as you please. Delphi FMX Color Selector download | SourceForge.net I hope to get some feedback on the component as well, because it currently has 2 flaws (explained below). To use the color picker, reference the unit and call one of the two global methods ColorDropdown and ColorButtonDropdown. The first method is generic and can be used with any control, the second expects a standard TColorButton. I have implemented the component in a TForm that is dynamically created at runtime. No component installation required. This was an important point [for me], because I need a few more very specialized popup controls that are supposed to be placed in this form. Using a simple TForm makes it much easier to design them than doing everything in code. Current issues: One of the obstacles I came across was a drawing bug in TGrid, at least with Delphi 11.0 (maybe fixed in later versions). The grid is/was designed to fit exactly 10 rows and on the first display, it properly shows all 10 rows. Close and reopen the popup control, and the original 10 rows have shrunken to 9 visible rows. I fixed it by making the grid tall enough for 11 rows and filled the whitespace below by moving the opacity slider up. Looks fine and works so far, but if someone has an idea how to fix the visible rows in TGrid, I would appreciate a feedback. The second issue is the color picker. It's implemented as a picture that you can drag & drop anywhere on the parent form to pick a color. This method is safe, but not 100% intuitive. A click-button-then-select-color function would be nicer. So, if anyone wants to spend time on that, I appreciate it. Apart from that, have fun trying it - demo application is included.
-
A popup color selector for Windows and MacOS
Alexander Halser replied to Alexander Halser's topic in FMX
The color selector has been updated to v1.3 on SourceForge. A couple of features added, some bugs fixed. https://sourceforge.net/projects/delphi-fmx-color-selector/files/ -
GDI+ DrawImage output differs by used printer on HighDPI system
Alexander Halser replied to ULIK's topic in Windows API
It really does? I wasn't aware of that. Maybe we have just been lucky and were always using that one DrawImage method, that works. We draw pictures with the GDI+ function below. Bounds is the destination rectangle, already calculated in device pixels. procedure DoGDIPlusDraw(DC: HDC; bmp: TBitmap; bounds: TRect); var gpgraphics: TGPGraphics; gpimage: TGPBitmap; Width, Height: UINT; begin gpgraphics := TGPGraphics.Create(DC); gpimage := TGPBitmap.Create(bmp.Handle, bmp.palette); try Width := gpimage.GetWidth; Height := gpimage.GetHeight; gpgraphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); // 7 gpgraphics.DrawImage(gpimage, bounds, // destination rectangle 0, 0, // upper-left corner of source rectangle Width, // width of source rectangle Height, // height of source rectangle UnitPixel); finally gpimage.Free; gpgraphics.Free; end; end; -
GDI+ DrawImage output differs by used printer on HighDPI system
Alexander Halser replied to ULIK's topic in Windows API
Without trying your example ... When you print an image, the screen DPI doesn't matter at all. You are dealing with printer DPI instead. Nothing else. TGPBitmap loads the PNG image and that image has a defined size in pixels. With TGPBitmap.DrawImage the picture is rendered onto a Windows device, which can be a screen device or a printer device. For GDI+ this is more or less the same (some technical printer details omitted). Fact is, GDI+ does the stretching and resampling to make the image fit into the rectangle you specify. And that target rectangle must be in device pixels. If the device is a printer, get LOGPIXELSX from the printer canvas and relate it to 96 dpi (= Windows standard display resolution at 100%). // get printer device DPI DC := GetDC(Printer.Canvas.Handle); try nLogPx := GetDeviceCaps(DC, LOGPIXELSX); memo1.Lines.Add(Format('Printer Device: %d', [nLogPx])) finally ReleaseHDC(DC); end; rectImage.Create( MulDiv(10, nLogPx, 96), MulDiv(10, nLogPx, 96), MulDiv(100, nLogPx, 96), MulDiv(100, nLogPx, 96) ); (The code example is untested, typed from memory. Point is, get the resolution of the printer canvas, not the screen.) -
How do I know if the click is an actual user click ?
Alexander Halser replied to dormky's topic in VCL
That doesn't apply to checkboxes alone. Radio buttons, radio groups, checked menu items, actions, OnResize events, you name it. Some forms have plenty of details to initialize. We have been using update counters for that, since more than 20 years. Like, every TForm that does some initialization which may auto-trigger events, has an IsUpdating property. The setter increases/decreases the value, the getter returns a boolean. If IsUpdating returns true (> 0), the checkboxes do not execute their OnChange event, manually arranged panels do not update, etc. constructor TForm1.Create(AOwner: TObject); begin inherited; FIsUpdating := 0; IsUpdating := true; //increases FIsUpdating try //do your init here finally IsUpdating := false; end; end; procedure TForm1.SetIsUpdating(value: boolean); begin if value then inc(FIsUpdating) else dec(FIsUpdating); end; function TForm1.GetIsUpdating: boolean; begin result := FIsUpdating > 0; end; A counter is more flexible and error-tolerant than using the csUpdating flag in ComponentState. It can be used to update corresponding controls in an OnChange event as well, without triggering their OnChange. The counter enables nested IsUpdating without worries. The only thing to remember is to consequently use try-finally. -
PDFium lib does all you need. We use it with an internal application that deals with PDF pages. Add or remove pages, split pages into several smaller ones, re-combine smaller pages into one printer page. PDFium can extract text as well.
-
Mouse events not triggered, caused by styled TMainMenu?
Alexander Halser replied to XylemFlow's topic in FMX
tested with polardark_win and polarlight_win styles I confirm that it works for me with the styles included in Delphi and those premium styles available for download when you have an active subscription. That's why I asked the OP where the dark styles origins from. Maybe it is an older style? My argument is that if a simply outdated style compiles just fine and looks good on screen, but generates potential runtime issues that may cause problems I would have never expected and that are difficult to find, then we're in for future problems with FMX styles -
Mouse events not triggered, caused by styled TMainMenu?
Alexander Halser replied to XylemFlow's topic in FMX
ChildForm is not released, StyleBookLight don't contain data Where do you see a problem here? Form2 is auto-created and auto-released, StyleBookLight is not required to produce the problem. I had some similar problem using an old 10.2 style That worries me ... what happens if a project that includes a TStylebook with a previously up-to-date style gets updated to a new Delphi version? Does that mean it compiles just fine but fails at runtime with mysterious errors? How does one test an app for potential problems like the one above? I'd like to know where it comes from before using FMX styles. -
Mouse events not triggered, caused by styled TMainMenu?
Alexander Halser replied to XylemFlow's topic in FMX
Fascinating example... thanks a lot! I am currently experimenting with FMX styles and very much appreciate this. It's got something to do with the dark style - the style itself damages the runtime functionality. I can duplicate the issue with Delphi 11.0, both 32 and 64 bit, it is reproducible with the dark style (only). Switching back to the light style makes it work again. My first guess was that it has to do with TStyleBook. It is apparent that the second form does not pick up the style, so I thought it may have to do with this issue. But this is not the case. So I loaded my own dark skin into your dark stylebook. Guess what? Works like a charm, despite not applying the style to the second window. There is, that's for sure, some flaw in the dark style you are using. Fascinating this is possible at all... I did not think it was. Where does your dark style come from? -
FMX doesn't auto-capture the control the mouse went down on. But you can do this manually, either by TForm.MouseCapture (that's for the form itself) or with TForm.SetCaptured(AnyControl) - this is for a particular control on the form. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin if (Sender <> self) then SetCaptured(TControl(Sender)) else MouseCapture; memo1.lines.add(Sender.Classname + ' mouse down'); end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single); begin ReleaseCapture; memo1.lines.add(Sender.Classname + ' mouse up'); end; Setting the mouse capture is important if you do anything in MouseDown that interacts with the control being clicked. Without capture, the user may click (mouse-down) on Panel1, move it, and release it on Panel2. The MouseUp will come from Panel2 in this case, or no mouse-up at all. By setting the mouse capture, you are guaranteed that you'll receive a MouseUp from that control that is being captured. VCL does this automatically, FMX does not. If you don't need the MouseDown on form level, but just for particular controls, use TControl.AutoCapture instead.
-
I was about to suggest to create a custom inplace editor. But the form that contains the TStringGrid does get KeyDown notifications, even when editing content. Out of curiosity, I wanted to know the internal structure of the grid, when it's editing. procedure TForm2.FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); var ctl: tfmxobject; s: string; begin if key = vkF5 then begin ctl := activecontrol; while ctl <> form2 do begin s := '> ' +ctl.classname + S; ctl := ctl.Parent; end; showmessage(s); end; end; It says: TStringGrid > TGridScrollContent > TDefaultEditor So if you check the OnKeyDown on form level and detect that ActiveControl is a TDefaultEditor with a TStringGrid as grandparent, you should be all set. Aren't you?
-
Firemonkey DLL: loading a TBitmap in initialization fails (solved)
Alexander Halser posted a topic in FMX
I need some FMX functionality in a VCL app. An FMX Windows DLL should handle the task. So far, the (FMX) DLL is working, but... Loading a TBitmap in the initialization of the DLL fails. The host application (actually the DLL) comes up with a general error. Loading the TBitmap at a later point in time (e.g. triggered by the host app) works. Unfortunately, the real DLL is a bit more complex and stripping all the "initialization" from all units is not an option. Static vs. dynamic loading of the DLL by the host VCL app does not make a difference. I've also tried ShareMem (not convinced it would change anything), but to give it a try: same result. Stripped down to the core, the entire DLL code is in the screenshot below. To me, it looks as if the GDI+ or DirectX environment is not loaded, when the DLL initializes. Is this a general limitation or could that be worked around by forcing earlier loading of GDI+/DirectX? -
Firemonkey DLL: loading a TBitmap in initialization fails (solved)
Alexander Halser replied to Alexander Halser's topic in FMX
You are certainly right. But the real DLL is more complex and involves 3rd party components, which happen to load TBitmaps in the initialization. Quite common, I'd say, and I wouldn't have considered it scary. But obviously, it is -
Firemonkey DLL: loading a TBitmap in initialization fails (solved)
Alexander Halser replied to Alexander Halser's topic in FMX
Si Señor! Winapi.GDIOBJ instead of FMX.Graphics does the trick. And saves a few megabytes. Thank you very much for the hint, Uwe! To sum up the solution... If an FMX DLL requires drawing capabilities in the initialization (that requirement might be triggered by 3rd party units), then the VCL host application must load GDI+ before loading the DLL: In the VCL host app, include Winapi.GDIOBJ to init GDI+ Load the FMX DLL dynamically (static linking would cause the DLL to load first, before host can load GDI+) If the host app is FMX and not VCL, it loads GDI+ by default. In this case, point (1) is void, but dynamic loading of the DLL is still required. -
Firemonkey DLL: loading a TBitmap in initialization fails (solved)
Alexander Halser replied to Alexander Halser's topic in FMX
Update, a partial solution, and a question... Observations with an FMX host app + FMX dll: The FMX DLL requires GDI+/DirectX in the intialization section. It creates an FMX.TBitmap and loads an image. To do this, it requires a GDI+/DirectX canvas. Such a DLL cannot be statically linked, fullstop. Not even with an FMX host application, despite the FMX host does initialize GDI+/DirectX. The reason is that statically linked DLLs get loaded before the host app can initialize GDI+/DirectX. Solution for FMX host + FMX dll: link the DLL dynamically, let the host app initialize GDI+/DirectX (as it does by default), then load the DLL. Result: the initialization of the DLL works, because GDI+/DirectX is already available (loaded by the FMX host). Observations with a VCL host app + FMX dll: It's obvious that GDI+/DirectX must be initialized by the host app before the DLL gets loaded. Dynamic DLL loading is therefore a must. The VCL host does not - at least not by default - initialize GDI+/DirectX, because it does not require it. Question: can we force the VCL host to deliberately load GDI+/DirectX, which is required by the DLL? Answer: yes, we can by including FMX.Graphics into the VCL section. Technically, that works (and the compiler does not complain). VCL host app test unit, modified: Note, that the VCL app references FMX.Graphics. This adds about 7mb to the EXE and seems to work. Is this a reliable construct? Or is there a different way to invoke (just) GDI+/DirectX in the host app, without all the FMX.Graphics overhead? unit VCL_hostUnit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, FMX.Graphics; { <--- This works and intializes GDI+/DirectX for the DLL. But will this work reliably? } type TVCLForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TLoadImage = function(Filename: PChar): Boolean; var VCLForm1: TVCLForm1; dllHandle: Cardinal; LoadImageFunc: TLoadImage; implementation {$R *.dfm} function LoadDLL: Boolean; begin dllHandle := LoadLibrary('TestDLL.dll') ; @LoadImageFunc := GetProcAddress(dllHandle, 'LoadImage') ; end; procedure TVCLForm1.Button1Click(Sender: TObject); begin if LoadDLL then LoadImageFunc('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png') else Showmessage('DLL not loaded'); end; initialization dllHandle := 0; finalization if dllHandle <> 0 then FreeLibrary(dllHandle) ; end. -
Firemonkey DLL: loading a TBitmap in initialization fails (solved)
Alexander Halser replied to Alexander Halser's topic in FMX
Hello programmerdelphi2k! I should have been more precise: Delphi 11.0 Win32 (not 64 bit) The DLL is 100% FMX, so TBitmap is an FMX.Graphics.TBitmap The host app is 100% VCL Host and DLL do not exchange bitmaps, nor other complex data structures. They exchange PChars and function results. When the host app starts and the DLL loads, I get the message "Load DLL" and after that: "Exception EAccessViolation in Module TestDLL.dll at 0000842F." Host app (VCL 32 bit, 1 form): unit hostUnit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; function LoadImage(Filename: PChar): Boolean; external 'TestDLL.dll'; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin LoadImage('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png'); end; end. DLL Code: library TestDLL; uses FMX.Forms, FMX.Graphics, FMX.Dialogs; {$R *.res} function LoadImage(Filename: PChar): Boolean; var dummy: FMX.Graphics.TBitmap; begin dummy := FMX.Graphics.TBitmap.Create; dummy.LoadFromFile(Filename); dummy.Free; Showmessage('Image loaded'); end; exports LoadImage; var dummy: FMX.Graphics.TBitmap; begin Showmessage('Load DLL'); dummy := FMX.Graphics.TBitmap.Create; //This line included, the DLL fails to load: // dummy.LoadFromFile('C:\Users\Alexander\Documents\Embarcadero\Studio\Projects\fmx-dll-test\test.png'); dummy.Free; end. HostApp.zip -
Custom sort of a TList with object references (works in 32 bit, does not in 64 bit)
Alexander Halser posted a topic in Algorithms, Data Structures and Class Design
I am currently working on some legacy code that uses simple TLists with object references and custom sort. This has worked and still works perfectly in 32 bit. But in 64 bit output, the list is not sorted (correctly), although it compiles without any errors. The form contains a couple of TShapes with different widths. The code below is supposed to sort and rearrange them by width. In 32 bit, the sort is ok. In 64 bit it's random. In fact, the function SortByWidth is called with item1 and item2 being the same (here: pointing to the same TShape). I am concerned and want to understand what's going on and why this does not work in 64 bit. There are more modern ways to sort a TList or use dictionaries. But this is legacy code that contains sorting functions of this kind in several places. procedure TForm2.BtnSortClick(Sender: TObject); function SortByWidth(const item1, item2: TShape): Integer; begin result := item1.width - item2.width; end; var AList: TList; i: Integer; begin AList := TList.Create; try for i := 0 to ComponentCount-1 do if (Components[i] is TShape) then AList.Add(Components[i]); AList.Sort(@SortByWidth); //what is wrong about this in 64 bit? for i := 0 to AList.Count-1 do TShape(AList[i]).Top := 30 + (40 * i); finally AList.Free; end; end; -
Custom sort of a TList with object references (works in 32 bit, does not in 64 bit)
Alexander Halser replied to Alexander Halser's topic in Algorithms, Data Structures and Class Design
@Uwe Raabe & Günter: Thank you, that does the trick! This saves me a lot of code-rewriting 🙂 function SortShapesByWidth(const item1, item2: TShape): Integer; begin result := item1.width - item2.width; end; procedure TForm2.BtnSortClick(Sender: TObject); var AList: TList; i: Integer; begin AList := TList.Create; try for i := 0 to ComponentCount-1 do if (Components[i] is TShape) then AList.Add(Components[i]); AList.Sort(@SortShapesByWidth); for i := 0 to AList.Count-1 do TShape(AList[i]).Top := 30 + (40 * i); finally AList.Free; end; end; -
The heading is not a typo. I've taken over Mac development from a colleague who recently left, so Delphi/Mac is pretty new to me. I'm running Delphi 10.4 Seattle with all patches and have a new Macbook running Monterey with Xcode 12.5.1. My basic "hello world" test app compiles and deploys successfully. But it doesn't start on the Mac. When started manually, it flickers for a moment, then terminates. PAServer reveals the underlying problem: dyld[xxxx]: Library not loaded: ?^??? Referenced from: /Users/alexander/PAServer/scratch-dir/Alexander-MacProfile/MacTest.app/Contents/MacOS/MacTest Reason: tried: '?^???' (no such file) The app obviously has a reference to "?^???" which is not found. But where does this come from? My current configuration: Delphi 10.4 with all patches MacOS Monterey with Xcode 12.5.1 (after realizing that Xcode 13 is not compatible I managed to install and run 12.5.1) Delphi SDK Manager: MacOSX 11.3 The app compiles and deploys successfully, both in Release and Debug mode. It just doesn't run. I'm pretty much stuck here... Google delivers plenty of results for "library not loaded" but none of them deals with a string called "?^???". PS: When changing the encoding of the (very simply) main unit from ANSI to UTF8, this slightly affects the error message. The UTF8 variant looks for a library called "?^;?[" instead of "?^???". Any hints where to look for this?
-
I am afraid I cannot help much. As described in the referenced thread... (I haven't changed this configuration since it got it working) iOS 15.1 - FMX - Delphi-PRAXiS [en] (delphipraxis.net) Reformatted the Mac, installed maOS Big Sur, an older version of Xcode. Things now work as advertised with Delphi 11. Delphi 11 plus November and January patch Hardware: M1 (2020) Mac running macOS Big Sur Xcode 13.2
-
Best Practice Question: Bidirectional EXE-to-EXE communication
Alexander Halser posted a topic in RTL and Delphi Object Pascal
I've taken over a legacy project from a friend, that needs a structural redesign. The application basically does file analysis and modification. It is currently a GUI app with everything built into the main executable. I would like to split that and am thinking of multiple EXEs. One that resembles the GUI, a console application that executes one particular task. On top of that, it needs a Windows service to execute scheduled tasks automatically, if no user is logged in. What is the best practice to accomplish this? Bi-directional communication is a must and could be done with custom messages and console output. Any other thoughts? Plugin system via DLL? Runtime packages? My thoughts about the general design so far: EXE1 (the GUI app) EXE2 (the non-GUI Windows service) EXE3 (the console app that executes tasks, can be used stand-alone in console mode and must run in multiple instances) EXE1 and EXE2 would both call the console EXE3. However, the tasks being executed can be quite long (from minutes to hours), so bi-directional communication is required. And while a task is being executed, it is not feasible to simply kill the thread in order to stop it. EXE3 must complete or roll back the detail, it's currently working on. Furthermore, EXEs should run without installation (except for the Windows service, that is). If EXE3 was, for instance, a COM server, this wouldn't work, would it? We could of course build everything into every EXE and separate the functionality in the code design. But I don't like the idea in general and multi-threaded execution is easier to handle with separate processes - in particular if the GUI app and the Windows service run at the same time, but must not execute the same task twice. -
Best Practice Question: Bidirectional EXE-to-EXE communication
Alexander Halser replied to Alexander Halser's topic in RTL and Delphi Object Pascal
Thank you for all your answers! I have used memory mapped files in the past, which works just fine in many situations, so this is definitely among the options. I'm also going to explore pipes, which I am not very familiar. Interesting idea! Console output is of course the most simple solution, but I did not think of console input. I like this idea because of its simplicity. Because back in my mind (not yet on the map) is the idea to make the execution part platform-independent from the beginning. If that requires a completely separate GUI part for MacOS/Linux (no plans for iOS/Android here) or can be done in one FMX app, is another story. -
MacOS: How to remove ambiguous Developer ID certificates?
Alexander Halser posted a topic in Cross-platform
I'm trying to get my MacOS app notarized and have applied for an Apple Developer ID certificate, which has been issued, downloaded, etc. Well, in the process of trying to get this thing to work I'm now completely stuck. Delphi tells me that when deploying the app, there are ambiguous certificate matches. Indeed there are: Xcode shows me that there are 2 Developer ID certificates for my company. When logging in to developer.apple.com, there are even three... Please don't ask me how I managed to add them (I don't remember what I did all day today in order to get it working). Question: How do I get rid of the duplicate certificates? There's no "Remove" button anywhere.