Forum > LCL

TTaskDialog musings

(1/7) > >>

Arioch:
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.html

Especially 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/TTaskDialog

That 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.html

So, 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:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.dlgDWARFButtonClicked(Sender: TObject;  AModalResult: TModalResult; var ACanClose: Boolean);begin  ACanClose := AModalResult <> 0; // or any other MR assigned to non-closing buttonsend;   
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)


* https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=60624.0;attach=50837;image
* https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=60624.0;attach=50866;image
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.


* https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=60624.0;attach=50862;image
* Repeat - https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=60624.0;attach=50876;image
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-taskdialogconfig

This 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:

* JediVCL HTML button from CCR - D:\fpcupdeluxe\ccr\jvcl\run\JvCtrls\jvhtcontrols.pas and D:\fpcupdeluxe\ccr\jvcl\run\JvNet\jvhtmlparser.pas
* THTMLFrame
* Google shows https://github.com/jackdp/DzHTMLText2
* and also https://laptrinhx.com/delphi-and-lazarus-html-label-component-4127787442/
* Martin points at "one for the IDE hints ...IPro... package"
* et centera
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.


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---  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!


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---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

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---      {$IFDEF MSWINDOWS}      if WidgetSet.GetLCLCapability(lcNativeTaskDialog) = LCL_CAPABILITY_NO then        ARadioOffset := 1      else        ARadioOffset := 0;      {$ELSE}      ARadioOffset := 1;      {$ENDIF} 
equivalent code:


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---         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.

Arioch:
LCL implementation puts command buttons above radiobuttons, native Windows implementaiton does the opposite.

Compare attached pic with https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=60624.0;attach=50866;image

Also, the HELP button has to be default, and it is on Windows (albeit the highlight is thinly faint), but not in the LCL-only dialog

Arioch:
Wrong implementation of [tfUseCommandLinks,tfUseCommandLinksNoIcon] set of flags.

tfUseCommandLinks says "use big buttons with green arrows"
tfUseCommandLinksNoIcon says "use big buttons but no green arrows"

When both flags are set, Windows still draws arrows, but LCL does not.

wp:

--- Quote from: Arioch on September 20, 2022, 05:41:04 pm ---LCL implementation puts command buttons above radiobuttons, native Windows implementaiton does the opposite.

--- End quote ---
Can't confirm what you are saying about command buttons above radiobuttons:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TForm1.Button12Click(Sender: TObject);begin  with TTaskDialog.Create(Self) do    try      Caption := 'My Application';      Title := 'The Title';      Text := 'This is the text of the TaskDialog.';      Flags := Flags + [tfUseCommandLinks];      with TTaskDialogButtonItem(Buttons.Add) do      begin        Caption := 'Command Item 1';        ModalResult := mrYes;      end;      with TTaskDialogButtonItem(Buttons.Add) do      begin        Caption := 'Command Item 2';        ModalResult := mrNo;      end;      with RadioButtons.Add do        Caption := 'This is one option';      with RadioButtons.Add do        Caption := 'This is another option';      with RadioButtons.Add do        Caption := 'This is a third option';      CommonButtons := [tcbYes, tcbNo];      MainIcon := tdiQuestion;  //tdiNone; // There is no tdiQuestion  -- wp: this comment is for Delphi only      if Execute then        if ModalResult = mrYes then          beep;    finally      Free;    end;end; Same as with Delphi.

Martin_fr:

--- Quote from: wp on September 20, 2022, 07:15:45 pm ---Can't confirm what you are saying about command buttons above radiobuttons:
Same as with Delphi.

--- End quote ---

Depends on native Windows:

Flags := [tfUseHiconMain,tfAllowDialogCancellation,tfUseCommandLinks]

vs

Flags := [tfAllowDialogCancellation,tfUseCommandLinks]


similar tfUseCommandLinksNoIcon

Navigation

[0] Message Index

[#] Next page

Go to full version