Windows Vista introduced new kind of "common dialogs" to ask user about their choices, Task Dialogs. A good brief article wih examples is at
https://specials.rejbrand.se/TTaskDialog/Being more generic and feature-full iteration of MessageDlg/CreateMessageDialog
https://lazarus-ccr.sourceforge.io/docs/lcl/dialogs/createmessagedialog.htmlEspecially after Lazarus expanded MessageDlg to include random-amount of developer-captioned button - those dialogs tend to become non-proportional, ultra-wide while thin.
Windows TaksDialog tends to be veritcally-stacked instead, usually making a more harmonious window shape.
mORMot project developed pre-Vista TaskDialog implementation using stock VCL components, later it was adapted to us LCL components and donated to LCL/Lazarus.
https://wiki.freepascal.org/TTaskDialogThat Lazarus dialog however lacks a lot of Windows features, which is understandable given emphasis on cross-platform and least-common-base reuquiments of LCL.
Simple example: compare https://lazarus-ccr.sourceforge.io/docs/lcl/dialogs/ttaskdialog-5.html with https://docwiki.embarcadero.com/Libraries/Sydney/en/Vcl.Dialogs.TTaskDialog_Events
This became especially apparent in debugger interface thread:
https://forum.lazarus.freepascal.org/index.php/topic,60624.0.htmlSo, i guess, some reflections of how TTaskDialog could be enhanced are to be collected somewhere, rather than pollute debugger's thread.
Microsoft decided that in "Vista age" every user knows what is hyperlink in the internet, and i think they do, They probably also thought, that if explanation is required for common problem - the chance is that number of explanations for different terms are. IOW, one single Help button is not many enough.
This or other reasoning, there is explicitly no HELP buttons in TaskDialog API. More so, eveyr button, even with ModalResult set to zero, would close the dialog.
Martin pointed to a hack making a non-closing button:
procedure TForm1.dlgDWARFButtonClicked(Sender: TObject;
AModalResult: TModalResult; var ACanClose: Boolean);
begin
ACanClose := AModalResult <> 0; // or any other MR assigned to non-closing buttons
end;
This seems to work indeed, but still has drawbacks:
1. This violates MS intentions, that are materialized in hundred of TaskDialog use cases in dozens of programs. Users would NOT expect that Help button is "harmless".
Even if they did - violating UI guidelines is questionable choice always.
But as a temptorary hack it works.
2. Such a button would have no visual cue it is fundamentally different from other, "final" buttons. It is okay when "horizontal bottom bar of buttons" mode is used, as in traditional dialogs. People got used that Help button there is a non-closing kind. But if the verical arrangement of button is selected (AKA Command buttons mode, it would probably be confusing)
3. The "Command buttons" have two texts in them, Large "caption" and smaller "hint" text. In Delphi.
Such an "extended caption" could explain to suer the Help button is not going to close the dialog. Bad design, but at least user is forewarned.
Since LCL dialog is rooted in pure LCL for "Least common denominator", though, it does not have command button hint at all.
To backport it - 2-captioned buttons have to be added to core LCL.
4. Microsoft made a dubious choice about hyperlinks - the texts in the dialog explicitly support the <A> tag and none of other HTML features.
https://learn.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-taskdialogconfigThis solution seems practical, but also a very ad-hoc-ish. Non-elegant.
This precludes "normal" connecting help systems to the LCL dialog. The dialog needs "on hyperlink clicked" event - whic hcan not be implemented while the dialog does not know what hyperlink is.
One can implement a custom one-tag-HTML parser and x-platform rendred rendered for TTaskDialog, but seems a weird one-use-only solution.
Once can also ascend some basic HTML parser and rendered into LCL code. Examples:
However...
a) this would create the reverse problems, UNIX devs would start using all of implemented HTML features, ruining uniformity of the dialogs, and then suddenly becoming incompatible with Windows (see attached pic below)
b) LCL Core team would probably not be very happy to include a Chrome-grade modern parser into the core and have mantainance burder, not to make LCL dependent on Chromium. There is a commercial library Delphi HTML controls, and it seems working good, but it was their intention to use HTML renderer as their foundation. LCL is not that, though, and hardly wishes to be HTML-rewritten.
c) LCL Core team would probably not be happy to have some very-limited-subset rendered enshrined, and then see repeteative complains "your HTML sucks, obsolete, need to be extended".
Potentially it would be posisble just to add the 3rd imlpementation to TTaskDialog, based entirely on THTMLFrame or partially on TJvHTLabel, and let users who need full functionality include it in their projects. But then TaskDialog would be exposing API that is just ignored on core implementation.
The current code raises some eyesbrows.
if (WidgetSet.GetLCLCapability(lcNativeTaskDialog) = LCL_CAPABILITY_YES) and
.....
// D:\fpcupdeluxe\lazarus\lcl\include\interfacebase.inc
function TWidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
begin
case ACapability of
lcCanDrawOutsideOnPaint,
lcNeedMininimizeAppWithMainForm,
lcApplicationTitle,
lcFormIcon,
lcModalWindow,
lcReceivesLMClearCutCopyPasteReliably,
lcSendsUTF8KeyPress,
lcEmulatedMDI,
lcNativeTaskDialog,
lcAccelleratorKeys: Result := LCL_CAPABILITY_YES;
else
Result := LCL_CAPABILITY_NO;
end;
end;
/// if TRUE, the Delphi emulation code will always be used
NonNative: boolean;
...
// use our native (naive?) Delphi implementation
Dialog.Emulated := true;
Dialog.Form := TEmulatedTaskDialog.CreateNew(Application);
1. I wonder if some standard layer could be added to override `GetLCLCapability` in runtime, for testing purposes. I could patch LCL and rcecompile, but this does not feel good, and it won't provide for changing behavior in runtime.
2. The use of term "native" is the comments is perverse here. Everywhere else "Native" means native to the platform. Buttons rendered by Windows GDI or MacOX Cocoa in SWT and VCL, as opposed to custom-rendered by Java Swing and FMX. It adds a cognitive burden... Especially since the nameing of the variables/parameters is conventional.
3. The
GetLCLCapability asserts every platform to have this, Vista-only API. Which infers it is demanded for every toolkit but Windows to remove that flag.
Yet, better, function
TWin32WidgetSet.GetLCLCapability in
lazarus\lcl\interfaces\win32\win32object.inc does not override it. But hey, only Vista+ had API, XP- does not, the dialog was precisely developed for Windows 2000 and XP !!! The TaskDialog itself explicitly queries OS API, if the interface function is present or not. This flag makes no sense in LCL Capabilities!
function TQtWidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
begin
case ACapability of
....
lcNativeTaskDialog: Result := {$ifdef MSWINDOWS} LCL_CAPABILITY_NO {$else} LCL_CAPABILITY_YES {$endif};
Qt, LOL, it is not correct, you have to check Windows version!!!
And why do you say Windows does not have native API and non-Windows do - when it is exactly the opposite???
And GTK2 and GTK3 and what over toolkits should do it too.
And Cocoa, i am looking at you, why don't you opt-out and pretend having Windows-specific API ???
And then it has
{$IFDEF MSWINDOWS}
if WidgetSet.GetLCLCapability(lcNativeTaskDialog) = LCL_CAPABILITY_NO then
ARadioOffset := 1
else
ARadioOffset := 0;
{$ELSE}
ARadioOffset := 1;
{$ENDIF}
equivalent code:
const ARadioOffset = {$IFDEF MSWINDOWS} 0 {$ELSE} 1 {$ENDIF};
There is probably a lot more to the TaskDialog. For example Windows/Delphi have some OnNavigate event which is nowhere documented.