#2 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2019-10-31 10:32:06

FWIW: I added support for TDN_DIALOG_CONSTRUCTED and disabling buttons to my copy of SynTaskDialog. (Besides my previous changes enabling a TComponent wrapper for it.)

#3 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2016-10-24 05:48:31

Dmitro25 wrote:

I think the reason is that when you emulate (under Windows XP) the Width parameter indicates the width of the window in pixels, but when using TaskDialogIndirect cxWidth is in Dialog Units (accoding to documentation).

I just tried it (using aNonNative, I don't have XP :-)) and you seem to be right.
Reading http://stackoverflow.com/questions/6870 … nto-pixels, this might be hard to fix. When we want to set cxWidth we don't have an HWND yet. When we have an HWND (e.g. inside TaskDialogCallbackProc) it's probably too late.

#5 Re: Other components » SynTaskDialog - custom button IDs? » 2016-05-31 07:58:02

I implemented the ideas from my last post. Are you interested in it?

What has been the "Buttons" member variable is now a read/write property "ButtonsString" tied to an array of TTaskDialogButton named "Buttons". I could rename the two to "Buttons" (as before) and "ButtonItems" or similar if you want it to to be completely backward compatible. (Although I like the current names much more. big_smile)

FWIW: The changes also made it quite trivial to write a wrapper component for visual design (similar to the VCL TTaskDialog), so I did that. smile

#6 Re: Other components » SynTaskDialog - custom button IDs? » 2016-05-23 14:06:01

I tried it with VCL's TTaskDialog before my first post and it worked as I wanted.

Looking through SynTaskDialog.pas, I see that it would be a bit tough to add it in it's current state - the contents of TTaskDialog.Buttons would have to evolve into a mini-DSL :-)
I had the idea of internally storing an array of TTASKDIALOG_BUTTON instead of a string (for both emulated and native mode) and then make TTaskDialog.Buttons a read/write property. What do you think?

#7 Other components » SynTaskDialog - custom button IDs? » 2016-05-23 12:34:21

uligerhardt
Replies: 3

Is there a way to manually assign button IDs instead of the automatic 100, 101, 102, ...? To be more concrete: I'd like to have a command link with IDCANCEL so that it acts as Cancel button - especially reacting to the Escape key.

#9 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2016-02-02 14:42:53

Hello Arnaud,

  1. I just noticed the following block in SynTaskDialog.pas:

          {$ifdef HASINLINE}
          Panel.BevelEdges := [beBottom];
          Panel.BevelKind := bkFlat;
          {$endif}

    and am a bit confused. smile
    HASINLINE refers to the keyword inline, right? So how is it connected to the visual appearance of a dialog?

  2. In our about box I want to link to a SynTaskDialog "project page". Currently I use http://blog.synopse.info/post/2011/03/0 … ista,Seven. But the direct link given there points to an obsolete source code archive from 2011. Could you fix that?

Best regards,
Uli

#11 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2015-03-12 10:37:50

Hi Arnaud,

what do you think about adding the following little helper method:

procedure TTaskDialog.AddButton(const ACaption: string; const ACommandLinkHint: string = '');
begin
  if Buttons <> '' then
    Buttons := Buttons + sLineBreak;
  Buttons := Buttons + ACaption;
  if ACommandLinkHint <> '' then
    Buttons := Buttons + '\n' + ACommandLinkHint;
end;

It makes dealing with translated and conditionally included buttons easier (and more readable, IMHO).
One could also include similar methods for Radios and Selection.

#12 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2015-03-12 10:34:05

Arioch wrote:

I wonder if this one  was tried as an interfaced object, with RefCount-controlledFluent Style API (chined method calls)

AFAIK Arnaud prefers using records and old-style objects to classes for saving CPU cycles ;-) So I'm quite sure he didn't try using interfaces with their associated overhead.

#13 Re: Other components » SynTaskDialog.pas for Lazarus » 2015-03-10 16:53:50

reddwarf wrote:

Allow me one more question: what about initializing the TTaskDialog to an empty object? Doesn't it have to be done? What if there is some garbage data from previous calls?

I guess you're right there. I always use TTaskDialogEx like this:

procedure Test;
var
  Task: TTaskDialogEx;
begin
  Task := DefaultTaskDialog;
  //...
end;

, so never had problems from this. You could probably also use:

procedure Test;
var
  Task: TTaskDialog;
begin
  Task := DefaultTaskDialog.Base;
  //...
end;

#14 Re: Other components » SynTaskDialog.pas for Lazarus » 2015-03-10 16:40:18

Hello Arnauld and reddwarf!

FWIW: I just gave this version of the unit a quick test in my VCL app and had one compiler error, probably because I always compile with TYPEDADDRESS ON. The call to TaskDialogIndirect in line 930 yields an error "E2010 Inkompatible Typen: 'PBOOL' und 'Pointer'". I fixed this by introducing a local variable "bVerifyChecked: BOOL" and call TaskDialogIndirect like this:

    bVerifyChecked := VerifyChecked;
    if TaskDialogIndirect(@Config,@result,@RadioRes,@bVerifyChecked)=S_OK then
    begin
      VerifyChecked := bVerifyChecked;
      exit; // error (mostly invalid argument) -> execute the VCL emulation
    end;

After that, I didn't notice any errors.

#15 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-06-20 12:01:04

Hi Arnaud,

I've got one more idea - how about adding

    property Title: string read Base.Title write Base.Title;
    property Inst: string read Base.Inst write Base.Inst;
    property Content: string read Base.Content write Base.Content;
    property Buttons: string read Base.Buttons write Base.Buttons;
    property Radios: string read Base.Radios write Base.Radios;
    property Info: string read Base.Info write Base.Info;
    property InfoExpanded: string read Base.InfoExpanded write Base.InfoExpanded;
    property InfoCollapse: string read Base.InfoCollapse write Base.InfoCollapse;
    property Footer: string read Base.Footer write Base.Footer;
    property Verify: string read Base.Verify write Base.Verify;
    property Selection: string read Base.Selection write Base.Selection;
    property Query: string read Base.Query write Base.Query;
    property RadioRes: integer read Base.RadioRes write Base.RadioRes;
    property SelectionRes: integer read Base.SelectionRes write Base.SelectionRes;
    property VerifyChecked: BOOL read Base.VerifyChecked write Base.VerifyChecked;

inside TTaskDialogEx? One could then write

Task.Inst := 'My instruction';

instead of

Task.Base.Inst := 'My instruction';

where Task is a TTaskDialogEx.

#16 Re: Other components » SynTaskDialog - The problem with resourcestring and packages » 2013-06-14 11:04:11

Cool. I'm gonna make use of that as soon as time permits. :-)

#17 Re: Other components » SynTaskDialog - Why no shortcuts for CommonButtons? » 2013-06-14 10:15:19

Thank you!

One minor nitpick: AFAICS you could make "s" a const parameter now. :-)

#18 Other components » SynTaskDialog - The problem with resourcestring and packages » 2013-06-13 15:12:29

uligerhardt
Replies: 2

Hello again!

Some time ago the problem occurred that array of resourcestrings can't be used in units inside packages. You solved that by using arrays of strings instead, e.g.

const
  TD_BTNS: array[TCommonButton] of string = (
    SMsgDlgOK, SMsgDlgYes, SMsgDlgNo, SMsgDlgCancel, SMsgDlgRetry,
    SCloseButton);

instead of

const
  TD_BTNS: array[TCommonButton] of pointer = (
    @SMsgDlgOK, @SMsgDlgYes, @SMsgDlgNo, @SMsgDlgCancel, @SMsgDlgRetry,
    @SCloseButton);

Unfortunately we have to assign the resourcestrings at runtime (with HookResourceString or similar), so this doesn't work. An alternative solution considered back then was using functions like

function TD_BTNS(cb: TCommonButton): Pointer;
begin
  case cb of
    cbOK: Result := @SMsgDlgOK;
    cbYes: Result := @SMsgDlgYes;
    cbNo: Result := @SMsgDlgNo;
    cbCancel: Result := @SMsgDlgCancel;
    cbRetry: Result := @SMsgDlgRetry;
    cbClose: Result := @SCloseButton;
    else Result := nil;
  end;
end;

which seem to work under these circumstances. What do you think about changing this?

Cheers,
Uli

#19 Re: Other components » SynTaskDialog - Why no shortcuts for CommonButtons? » 2013-06-13 14:14:02

The easiest solution from my perspective would be to just delete the call to StripHotkey in AddButton - or call it controlled by a flag - and have the translators avoid clashing shortcuts. They have to consider them anyway because NonNative might be false and the system dialog shows the CommonButton shortcuts.

#20 Other components » SynTaskDialog - Why no shortcuts for CommonButtons? » 2013-06-13 11:20:55

uligerhardt
Replies: 4

Hi ab,

I recently noticed that emulated SynTaskDialogs suppress the shortcuts for CommonButtons, i.e. the user can't press y to "click" he Yes button.

System provided dialog:
BafYstN.png

Emulated dialog:
06EYClQ.png

For reference please see Windows Explorer's deletion confirmation which has shortcuts:
1TFyn9C.png

I checked the SynTaskDialog source and noticed the explicit call to StripHotkey at the beginninig of AddButton and wondered why it is there?

Best regards,
Uli.

#21 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-04-02 12:01:21

bilm wrote:

Thanks for the explanations.
I think I will use your implementation.
It makes the most sense to me.

A couple of posts back I asked about the possible advantages of
making TTaskDialog a class. Probably better to add a small class 
using TTaskDialogEx to the SynTaskDialog unit.

The reason is in a class you can implement a procedure
to send messages. Here is a very simplified example to
illustrate the idea.

// interface
TTaskDialogX = class
private 
 FtaskDialogX: TTaskDialogEx;
 procedure SendExecuteTD(Handle: HWND);
 procedure MessageHandler(var Msg: TMessage); message TH_MESSAGE;
protected
public
 constructor Create();
 destructor Destroy; override;
 //property TDialogX: read FTaskDialogX; ??
end;

//implementation
procedure TTaskDialogX.SendExecuteTD(Handle: HWND);
var pd: ^FTaskDialogX;
begin  
  SendMessage(Handle, TH_MESSAGE, 0, LPARAM(pd));
end;

procedure TTaskDialogX.MessageHandler(var Msg: TMessage);
var pd: ^FTaskDialogX;
begin  
  pd := Pointer(Message.LParam);
  try
    pd^.Execute(Handle);
  finally
    Dispose(pd);
  end;  
end;

I don't think this is gonna work as TTaskDialogX is just a plain Pascal object without a window handle, so it won't receive messages (AFAIK - feel free to try it nevertheless). You could of course use AllocateHWnd but IMHO this isn't worth the effort.

#22 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-03-24 17:16:32

bilm wrote:

I eliminated FHandle in TMyThread and used Form1.Handle
for PostMessage() in TMyThread.Execute. It works just as well.
Unless you think using FHandle is safer for some reason?

It's just a habit for me to avoid the usage of global variables as far as possible,
and especially so when dealing with threads.

bilm wrote:

I can still use
  Include(pd^.CommonButtons, cbCancel)
but I think
  pd^.CommonButtons := [cbCancel]
is better.

That's a matter of taste here. The first variant changes only one flag
in the set while the second one initializes the whole set. Which one is "better"
than the other depends on the circumstances.

bilm wrote:

But it was adding
  pd^ := DefaultTaskDialog
that did the trick. Except I do not understand, why?

AFAIK pd^ is zero-filled after executing New(pd), so

pd^ := DefaultTaskDialog

should do the same as

pd^.DialogIcon := tiInformation;
pd^.FooterIcon:= tfiWarning;

(or similar, depending on your DefaultTaskDialog). You could experiment with this by commenting out one line and see when it breaks.

Edit: Just checked it - New does not zero-initialize, so pd^ contains garbage, e.g. stuff like a negative Width which isn't good. wink You don't have to assign DefaultTaskDialog, but you have to make sure all fields of pd^ have sensible values.

#23 Re: GDI+ » Draw semitransparent polygons » 2013-03-16 13:28:12

These articles helped me with a similar task: http://itinerantdeveloper.blogspot.de/s … ansparency. I came up with this:

procedure NormalizeRect(var r: TRect);
var
  t: Integer;
begin
  if r.Left > r.Right then
  begin
    t := r.Right;
    r.Right := r.Left;
    r.Left := t;
  end;
  if r.Top > r.Bottom then
  begin
    t := r.Bottom;
    r.Bottom := r.Top;
    r.Top := t;
  end;
end;

// AlphaBlendRect: draws an alphablended rectangle:
procedure AlphaBlendRect(DC: HDC; const ARect: TRect; AColor: TColor; AIntensity: Byte);
var
  Bitmap: TBitmap;
  BlendParams: TBlendFunction;
  rClip, rBlend: TRect;

  function GetBlendColor: TRGBQuad;

    function PreMult(b: Byte): Byte;
    begin
      Result := (b * AIntensity) div $FF;
    end;

  var
    cr: TColorRef;
  begin
    cr := ColorToRGB(AColor);
    Result.rgbBlue := PreMult(GetBValue(cr));
    Result.rgbGreen := PreMult(GetGValue(cr));
    Result.rgbRed := PreMult(GetRValue(cr));
    Result.rgbReserved := AIntensity;
  end;

begin
  GetClipBox(DC, rClip);
  NormalizeRect(rClip);
  rBlend := ARect;
  NormalizeRect(rBlend);

  if not IntersectRect(rBlend, rClip, rBlend) then
    Exit;

  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(1, 1);
    PRGBQuad(Bitmap.ScanLine[0])^ := GetBlendColor;

    BlendParams.BlendOp := AC_SRC_OVER;
    BlendParams.BlendFlags := 0;
    BlendParams.SourceConstantAlpha := $FF;
    BlendParams.AlphaFormat := AC_SRC_ALPHA;

    Windows.AlphaBlend(
      DC, rBlend.Left, rBlend.Top, rBlend.Right - rBlend.Left, rBlend.Bottom - rBlend.Top,
      Bitmap.Canvas.Handle, 0, 0, 1, 1,
      BlendParams);
  finally
    Bitmap.Free;
  end;
end;

// AlphaBlendPolygon: draws an alphablended polygon:
procedure AlphaBlendPolygon(DC: HDC; const APoints: array of TPoint; AColor: TColor; AIntensity: Byte);

  procedure SetClip(APoints: array of TPoint); // pass APoints by value
  var
    rgn: HRGN;
  begin
    LPtoDP(DC, APoints[0], Length(APoints));
    rgn := CreatePolygonRgn(APoints[0], Length(APoints), ALTERNATE);
    try
      ExtSelectClipRgn(DC, rgn, RGN_AND);
    finally
      DeleteObject(rgn);
    end;
  end;

var
  SaveIndex: Integer;
  rClip: TRect;
begin
  SaveIndex := SaveDC(DC);
  try
    SetClip(APoints);
    GetClipBox(DC, rClip);
    AlphaBlendRect(DC, rClip, AColor, AIntensity);
  finally
    RestoreDC(DC, SaveIndex);
  end;
end;

which works quite well for me.

#24 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-03-15 14:28:52

Hi bilm,

I just threw together a mini test program - start a new VCL app, drop a button on Form1 and replace the unit source code with this:

unit MainF;

interface

uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  MyThread,
  StdCtrls;

const
  TH_MESSAGE = WM_USER + 5;

type
  TMyThread = class(TThread)
  private
    FHandle: HWND;
  protected
    procedure Execute; override;
  public
    constructor Create(AHandle: HWND);
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure ThreadMessage(var Message: TMessage); message TH_MESSAGE;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  SynTaskDialog;

{ TMyThread }

constructor TMyThread.Create(AHandle: HWND);
begin
  inherited Create(False);
  FreeOnTerminate := True;
  FHandle := AHandle;
end;

procedure TMyThread.Execute;
var
  pd: ^TTaskDialogEx;
begin
  New(pd);
  try
    pd^ := DefaultTaskDialog;
    pd^.Base.Title := 'Title';
    pd^.Base.Inst := 'Instruction';
    pd^.Base.Buttons := 'AAAAAAAAAA' + sLineBreak + 'BBBBBBBBBBBB';
    pd^.CommonButtons := [cbCancel];
    pd^.ButtonDef := 100;
    pd^.Flags := [tdfUseCommandLinks];
    pd^.DialogIcon := tiQuestion;
    pd^.NonNative := True;
    pd^.EmulateClassicStyle := True;
    PostMessage(FHandle, TH_MESSAGE, 0, LPARAM(pd));
  except
    Dispose(pd);
  end;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  t: TMyThread;
begin
  t := TMyThread.Create(Handle);
end;

procedure TForm1.ThreadMessage(var Message: TMessage);
var
  pd: ^TTaskDialogEx;
begin
  pd := Pointer(Message.LParam);
  try
    pd^.Execute(Handle);
  finally
    Dispose(pd);
  end;
end;

end.

Clicking the button starts a thread which shows a task dialog. Does this work for you?

#25 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-03-14 13:22:08

Just another thought: The StripHotkey routine from Menus.pas probably could replace UnAmp.

#26 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-03-14 13:16:49

Hi Arnaud,

once more about the TBitBtn/TButton issue from long ago. Please have a look at this picture:
2qoNna4.png
and note the different baselines of the "Abbrechen" buttons. The left one is from an app compiled with the latest SynTaskDialog.pas (http://synopse.info/fossil/artifact/d45 … 47564d4bbb), the right one is with my changes - two lines added:

{$ifdef USETMSPACK}
  /// a TMS PopupMenu
  TSynPopupMenu = TAdvPopupMenu;

  TSynButtonParent = TAdvGlowButton;
  TSynTaskDialogButton = TAdvGlowButton; // <== added
{$else}
  /// a generic VCL popup menu
  TSynPopupMenu = TPopupMenu;

  TSynButtonParent = {$ifdef WITHUXTHEME}TBitBtn{$else}TButton{$endif};
  TSynTaskDialogButton = TButton; // <== added
{$endif USETMSPACK}

and two lines changed:

function AddButton(s: string; ModalResult: integer): TSynTaskDialogButton; // <== changed
var WB: integer;
begin
  s := UnAmp(s);
  WB := Form.Canvas.TextWidth(s)+52;
  dec(XB,WB);
  if XB<X shr 1 then begin
    XB := aWidth-WB;
    inc(Y,32);
  end;
  result := TSynTaskDialogButton.Create(Form); // <== changed

I think the changed version looks much better. It also incurs a tiny bit less runtime overhead - just a plain TButton instead of a TBitBtn. ;-) If you agree feel free to adapt my changes.

Best regards,
Uli

#27 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-03-06 10:59:58

Hi bilm!

It's getting difficult to follow this endless stream of code snippets. :-)
I think the best would be if you build a small (!) project showing the wrong behaviour and post it. AFAICS there is no way to attach it on the forum, so you could use some file hoster.

Best regards,
Uli

#28 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-02-28 07:49:22

One simplification you still could make in your code is to make TDparams a pointer to TTaskDialogEx and drop your own TTaskDlg record. Then the only additional parameter needed for calling Execute would be the ParentHandle. And I guess that's the same handle you're posting your message to, so

procedure TForm1.ThreadMessage(var Message: TMessage);
var
  TDparams: ^TTaskDialogEx;    
  // ...
begin
  case Message.WParam of
    TH_SYNTD:
    begin 
      TDparams := Pointer(Message.LParam); 
      try
       TDparams^.Execute(Handle); // <== Self.Handle, i.e. the message receiver
      finally
       Dispose(TDparams); 
      end;         
    end;

    TH_LABEL: // ...

    TH_PROGBAR: // ...
   
    // ... others
  end;
end;

would probably suffice.

I don't think handling the message passing generically in the SynTaskDialog unit is feasible. Which value to use for the message that doesn't collide with application defined messages? Where shall the message be posted to? How should the message handler be implemented? I don't think that making TTaskDialog a class would change much in this respect. You have to pass around a pointer - it's irrelevant whether this pointer points to a record or is a reference to a class instance.

@ab: How about

[delphi]...[/delphi]

BBCode tags? :-)

#30 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-02-25 07:14:53

bilm wrote:

After thread message handler uses it, the local record pointer (TDparams)
in the message handler is disposed in try/finally.

But what about the local record pointer (TDparams) in the thread ?

No problem. Both pointers point to the same record (as there is only one New). So you need only one Dispose, too. After the call to Dispose both pointers point to garbage and shouldn't be used anymore, of course.

#31 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-02-22 09:59:22

At a glance this looks good.

FWIW: I'd get rid of the version checking (IsWinXP, IsVistaOrHigher) und just always show the task dialog from the main thread.

#32 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-02-09 18:50:34

Hello bilm,

I just tend to avoid global variables as much as possible. So I would probably expand the TTDexe record to encompass also Title, Inst, Content, Footer etc. and copy those values to a local TTaskDialog variable inside ThreadMessage:

procedure TMainForm.ThreadMessage(var Message: TMessage);
var
  TDparams: PTTDexe; // pointer for Taskdialog parameters record
  Task: TTaskDialog;
begin
  case Message.WParam of
    TH_WCDTD:
    begin  // In my threads the other params never change.
      TDparams := Pointer(Message.LParam);
      Task.Title := TDparams^.Title;
      // ...
      Task.Execute([TDparams^.CommonButtons], TDparams^.ButtonDef, [], TDparams^.DialogIcon, TDparams^.FooterIcon, 0, 0, 0, False, False);
      Dispose(TDparams); // release allocated memory
    end;
    // other WParam constants for progressbar.stepit/.update, label.update, etc.
    // instead of using Synchronize().
  end;
end;

This seems cleaner to me  - the local variable "lives" only while the dialog is shown, whereas a global variable lingers around unused most of the time. And I think you could omit the critical sections.

Best regards,
Uli

#33 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-02-05 10:59:18

I would try to make Task a local variable in ThreadMessage. Or maybe replace TDparams: PTTDexe by a pointer to a TTaskDialog and kick the separate Task variable. This should avoid the need for synchronizing. (But NB: I'm not a threading expert. :-))

#34 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-01-30 07:09:00

bilm wrote:

I've implemented a thread message handler. I also made a record with a
pointer for all the params of the Task.Execute function and then a
var for the record pointer.

For each occurance in the thread I fill in the Task fields and also the
Task.Execute params using the record pointer var which I send via the PostMessage()
Lparam and dereference in the message handler for the Task.Execute call.

It all works great but I'm just an amateur hobbyist, maybe intermediate level at best.
If you or another more expert programmer has a better way to do this I'd appreciate you sharing it.

I'm no experts in threads, too, but this sounds good to me.

bilm wrote:

>> It specifies the parent window of the task dialog.
I'm still curious about the handle thing. Could you assign
it the handle of the invisible window of a thread?

IIUC you show the task dialog in the context of the main thread, so the parent window probably should be from the main thread too.
Isn't there some kind of "active" window - maybe the one the user used to start the thread?

#35 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2013-01-28 09:23:47

bilm wrote:

The Synopse TaskDialog works great in the main thread but
when I call it in a secondary thread I get an invalid operation error:
  "Canvas does not allow drawing."

I guess that's when you show an emulated task dialog? E.g on a XP system?
That probably won't work because the emulation is a VCL dialog and the VCL is not threadsafe.

bilm wrote:

The handle parameter should be the thread handle, right?

No. Have a look at the declaration - its type is HWND. It specifies the parent window of the task dialog.

#36 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-29 15:41:54

ab wrote:

As far as I tested it, my commit did work as expected under Windows 7 and XP.

Some comments:

  • SynTaskDialog doesn't compile for me - in line 791 EmulateClassicStyle is used instead of aEmulateClassicStyle.

  • Line 726 (the first "Panel.Color := clWhite;") should be deleted, otherwise EmulateClassicStyle also shows a white background.

  • At the moment, with EmulateClassicStyle = True there is a line below the white panel where there should be none IMHO.
    With EmulateClassicStyle = False there are even two lines next to each other. Something like

    Panel := TPanel.Create(Form);
    Panel.Parent := Form;
    Panel.Align := alTop;
    Panel.BorderStyle := bsNone;
    Panel.BevelOuter := bvNone;
    if not aEmulateClassicStyle then begin
      Panel.BevelEdges := [beBottom];
      Panel.BevelKind := bkFlat;
      Panel.Color := clWhite;
      {$ifdef WITHUXTHEME}
      Panel.ParentBackground := false; // clWhite not used otherwise
      {$endif}
    end;
    Par := Panel;

    might be better.

  • Regarding the OK/Cancel/... buttons: Compare this (current version)
    33ygk0l.png
    and this, where I changed AddButton to create and return a plain TButton:
    tyzvr.png
    . Notice the ugly text placement in the OK and Abbrechen (= Cancel) buttons of the first image?
    (NB: Just replacing "TSynButton" with "TButton" in AddButton wouldn't be enough because ButtonClick specifically checks for TSynButton.)

  • Somewhat related: I guess

    Sender is TSynButton

    is more idiomatic in Delphi than

    Sender.InheritsFrom(TSynButton)

Thanks for taking care of my ideas. I threaten to bring some more ideas if I've got some time again.

ab wrote:

Enjoy your holidays!

Thanks, I did up to now. Even watched some French movies although I know next to no French. Luckily there are subtitles :-)

#37 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-27 08:54:45

ab wrote:

About RecreateAsPopup, as far as I understood the VCL implementation of this method (from a quick overview), it is similar to CreateParented...
Perhaps you can confirm it.

But it may definitively be the reason of RecreateAsPopup.
As you stated in this ticket (I did not notice its existence!), we may safely use a plain Create instead. Is it right?
AFAIK it works as expected, and fix the issue.
RecreateAsPopup just does not make any difference with a plain Create + ShowModal.

I can't test right now (I'm on vacation for Hof film festival :-)) but AFAIR CreateParented showed the problematic behaviour under Windows 7 + D2007 and XP + D2006 whereas plain Create (or CreateNew) worked in both cases. RecreateAsPopup probably only makes a difference if the owner/parent form is non-VCL.

#38 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-25 14:58:43

ab wrote:

FYI the RecreateAsPopup method is not available in Delphi 5-6-7 (at least).

So I would rather use the previous implementation, using the CreateParented() constructor.

I see. But CreateParented seems to exist for embedding controls in other controls and not for establishing a hierarchy between top-level windows/forms. This is probably the cause for http://synopse.info/fossil/tktview?name=01395e5932. Can you reproduce that?

#39 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-25 14:52:07

ab wrote:

- What may be the specifications for "EmulateClassicStyle" new parameter?

I'd like a task dialog under Windows XP without themes to look similar to a task dialog under Vista/7 without themes. EmulateClassicStyle tries to achieve that by making the emulation look more like Windows Classic.

For example, this is a task dialog under Windows 7 with Contrast #1 theme:
ogwcau.png
These are emulated dialogs with EmulateClassicStyle = True:
av0iom.png
and EmulateClassicStyle = False:
s60ui1.png

There are quite a few differences: fonts, background color, border sizes etc. And there are still a lot of details missing.

ab wrote:

- What is the purpose of those TSynCommandLinkParent and such types (sounds like redundant code)?

I introduced TSynTaskDialogButton because the OK, Cancel and similar buttons shouldn't be TBitBtns. They don't ever show bitmaps (on a task dialog) and look slightly wrong if one uses TBitBtns (the text is one or two pixels to low, which stands out if the button is only 22 pixels tall).

At the moment TSynCommandLinkParent and TSynCommand are just relicts of my "not so smart move" and could be replaced by TSynButton. One could probably use them to integrate a button class that looks more like a "real" command link.

ab wrote:

- May I receive the refreshed "arrow" icon?

Done.

ab wrote:

- Why did you overwrite the TForm constructor?

In the original code you created a TForm instance and casted it to TTaskDialogForm which it wasn't. In the modified code a real TTaskDialogForm is created and no cast is needed.

If you decide to go with the RecreateAsPopup way and skip the CreateParented, you could also directly call TTaskDialogForm.CreateNew and remove the overridden constructor.

ab wrote:

Using ActiveControl instead of my tricky Form.Tag use is well done.

Thanks. I wasn't sure if it's compatible with D<2006 and TMS.

#40 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-25 13:48:17

ab wrote:
uligerhardt wrote:

That move wasn't too smart, as all of these are used elsewhere in your framework. I've reinstated them and will send you an update later.

I would not have deleted those lines either.
wink

I guessed so. :-)

ab wrote:

I'm waiting for your update.

I sent it this morning to your bouchez-info address. Slow electrons today...

#41 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-24 19:08:26

uligerhardt wrote:

3. BitmapOK (and its accompanying res entry) weren't used, so I removed them.
5. I split TSynButton in two: TSynCommandLink for the command links and TSynButton for the other buttons. ...
6. The DropDown stuff in TSynButton wasn't used, so I commented it out.

That move wasn't too smart, as all of these are used elsewhere in your framework. I've reinstated them and will send you an update later.

#42 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-21 19:02:53

Hello Arnaud,

I have made some modifications to SynTaskDialog.pas and will send them to you per mail as I can't attach it here. You probably won't like all of the changes but feel free to take what you like.

I wrote a little wrapper TTaskDialogEx to make the usage of TTaskDialog.Execute a bit easier if you want to change just one or two of the default arguments. There is a global variable DefaultTaskDialogEx: TTaskDialogEx  which is initialized to the default values from TTaskDialog.Execute.
You could use it like this:

  • Customize DefaultTaskDialogEx in some initialization section, FormCreate or similar, maybe

    DefaultTaskDialogEx.EmulateClassicStyle := True;

    or

    DefaultTaskDialogEx.AlwaysEmulate := True;
  • Use it like

    procedure TForm1.Button3Click(Sender: TObject);
    const
      Icons: array[TMsgDlgType] of SynTaskDialog.TTaskDialogIcon = (
        tiWarning, tiError, tiInformation, tiQuestion, tiNotUsed);
    var
      Task: SynTaskDialog.TTaskDialogEx;
      Res: Integer;
    begin
      Task := DefaultTaskDialogEx;
      
      // Base is a TTaskDialog
      Task.Base.Title := 'My Test App';
      Task.Base.Inst := 'Saving application settings';
      // more Base settings...
    
      Task.CommonButtons := [cbOK, cbCancel];
      Task.ButtonDef := 101;
      Task.Flags := [tdfUseCommandLinks];
      // Notice: no mentioning of DialogIcon, FooterIcon, RadioDef, Width, ...
    
      Res := Task.Execute(Handle);
    end;

All other changes only apply if emulation is active.

  1. New parameter EmulateClassicStyle in TTaskDialog.Execute. If True then the emulation is drawn more like the original Microsoft dialog under Windows Classic. (This is not finished yet - the verification checkbox and footer text aren't handled correctly yet.)

  2. If EmulateClassicStyle = False I've reverted the icon border to your taste (24 instead of 10, see post http://synopse.info/forum/viewtopic.php?pid=2777#p2777).

  3. BitmapOK (and its accompanying res entry) weren't used, so I removed them.

  4. Removed the shadow in the arrow bitmap (see http://synopse.info/forum/viewtopic.php?pid=2785#p2785).

  5. I split TSynButton in two: TSynCommandLink for the command links and TSynButton for the other buttons. The latter ones are now never TBitBtns because this looks ugly for OK buttons etc (wrong text base line) and the Glyph isn't used anyway.

  6. The DropDown stuff in TSynButton wasn't used, so I commented it out.

  7. Fixed the E2201 error by using an array of strings (see http://synopse.info/forum/viewtopic.php?pid=2850#p2850).

  8. By overloading TTaskDialogForm.Create (calling CreateNew) some ugly casting could be removed.

  9. Used ActiveControl instead of the Tag/FormShow trick.

  10. Fixed the TabOrder issue (see http://synopse.info/forum/viewtopic.php?pid=2778#p2778).

Uli

#43 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-20 08:36:05

Hello Arnaud,

if you have an app that uses SynTaskDialog and has runtime packages enabled you get compiler errors

E2201: Need imported data reference ($G) to access 'SMsgDlgOK' from unit 'SynTaskDialog'

for TD_BTNS and TD_ICONS_IDENT. I don't know where this comes from - probably vcl.dpk is compiled with the "wrong {$IMPORTEDDATA ...} directive. There is a (German) post that seems to deal with this problem: http://www.delphipraxis.net/72509-resou … ckage.html and this post: https://forums.embarcadero.com/message. … ageID=6844.

I've found two workarounds: Either

  1. replace the pointer arrays with string arrays like

    TD_ICONS_IDENT: array[TTaskDialogIcon] of string =(
        '', SMsgDlgWarning, SMsgDlgConfirm, SMsgDlgError, SMsgDlgInformation,
        '', SMsgDlgInformation);

    and remove some LoadResString calls or

  2. replace the pointer arrays with functions like

    GetIconIdent(TTaskDialogIcon): Pointer

    .

Best regards,
Uli.

#44 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-14 11:51:00

Once more a PITA. :-)

uligerhardt wrote:

Another thing: I'd suggest you remove the shadow from the arrow bitmap because it looks ugly when you have configured your system to a non-gray clBtnFace. (PNG probably isn't an option.)

Do see what I mean compare these pictures:
Original Windows dialog:
2wgc4dy.png
Emulation, arrow with shadow:
120pgno.png
Emulation, arrow without shadow:
mlo6ti.png

The last screenshot was produced with this BTNARROW (can't append the real bmp, unfortunately):
2ef8he9.png
Looks better, doesn't it?

Best regards,
Uli.

#45 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-13 13:43:12

Thank you, Arnaud,

this almost looks like the original!
Maybe you could insert

Panel.BevelOuter := bvNone;
Panel.BevelEdges := [beBottom];
Panel.BevelKind := bkFlat;

after the

Panel.BorderStyle := bsNone;

line to make the border around the white panel more authentic.

Two more things: If I show a task dialog like this:

var
  Task: TTaskDialog;
  Res: Integer;
begin
  Task.Inst := 'Saving application settings';
  Task.Content := 'This is the content';
  Task.Buttons := 'Registry\nStore settings in registry' + sLineBreak + 'XML\nStore settings in XML file';
  Res := Task.Execute([cbOK, cbCancel], 101, [tdfUseCommandLinks], tiQuestion, tfiInformation, 0, 0, Handle, CheckBoxUseEmulation.Checked);
end;

the "real" dialog has a close button in the upper right corner, while the emulated one hasn't. Is there a way to have the button in the emulation, too?

With the same code, the tab order of the OK and Cancel buttons is reversed. This could fixed like this:

  • Declare

    CurrTabOrder: TTabOrder;

    next to Par and Panel.

  • Put the line

    CurrTabOrder := Panel.TabOrder;

    at the beginning of the

    if (byte(aCommonButtons) <> 0)...

    branch.

  • Set

        result.TabOrder := CurrTabOrder;

    in AddButton.

BTW: I hate to be a PITA, but I like my GUI to be "right". :-)

#46 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-12 20:40:21

Regarding the border width of the emulation: Just compare the border around the icon of an emulated dialog
TaskDialog04.png
with a Windows task dialog, either themed
TaskDialog03.png
or unthemed
taskdialog1.png

(Replacing the 24's after WIN_ICONS[aDialogIcon] with 10's (and 48 with 20) should work. wink)

#47 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-12 16:44:07

ab wrote:

The look depends on the Windows theming - using plain Windows controls has its pros and cons.
I wanted the unit to stay "plain" VCL ready, and the resulting layout is expected to use Windows theming.

The fact that your unit uses standard VCL controls and respects theming is one of the things I like about it.

What I mean is that the emulated task dialogs look like the MS dialogs from a standard Windows Vista/7 Aero. Because of this they look a bit out of place on on XP with Luna and on every classic/unthemed Windows (even on unthemed Vista/7). If the emulation looked more like a Windows Classic task dialog it would blend in better on those platforms.

ab wrote:

The fact that there are some many arguments in Execute() is, as far as I remember, that... there is no obvious reason unless that they will provide default parameters.
We may pass all arguments as TTaskDialog fields, but since it is an object/record, there is no default values to them (when allocated on the stack, it is not filled with zero).

OK, I see.

ab wrote:

You can use the fossil tickets, but for issues.
For suggestions and enhancements, you can better use this forum, and we will discuss here.

OK.

ab wrote:

Thanks for your interest.

Thanks for your component. :-)

#49 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-12 13:14:03

One other question: Is there a reason that there are so many arguments to TTaskDialog.Execute? I think SynTaskDialog would be easier to use if most of these parameters were fields of the TTaskDialog record instead.

And it might be good to replace TTaskDialogForm.FormShow with an override of DoShow.

#50 Re: Other components » Open Source SynTaskDialog unit for XP,Vista,Seven » 2011-10-12 12:18:02

Hello ab!

Some more nitpicking about the look of your emulation. :-)
I'd like to look the emulated dialogs more like the real Windows 7 task dialog under Windows Classic (i.e. unthemed), that is similar to this:
taskdialog1.png.

  • no white panel

  • different fonts

  • narrower borders

  • etc.

I have played a bit with SynTaskDialog.pas and got a look that I like better (even if I'm not done yet). Would you be interested in integrating this in the official unit as an compile time or runtime option?

Another thing: I'd suggest you remove the shadow from the arrow bitmap because it looks ugly when you have configured your system to a non-gray clBtnFace. (PNG probably isn't an option.)

Best regards,
Uli.

Board footer

Powered by FluxBB