Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Dialogs & TaskDialog Cursor Snap to Default Button


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 8 - Last Post: Nov 13, 2017 12:38 PM Last Post By: Hanspeter Widmer
Hanspeter Widmer

Posts: 19
Registered: 10/14/00
Dialogs & TaskDialog Cursor Snap to Default Button  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 7, 2017 10:07 AM
Hello,

there is a well known Windows feature as "Snap to" default button after dialog launch.

All Delphi releases simple ignores this feature, may this has to be added on SHOW as Dave Nottage [TeamB] suggested.

BUT: How about the TaskDialog ... Tokyo simple does not support it (even do not work) and the question rises whether this is/should be done internally by the MS TaskDialog API ??

The settings is available by: SystemParametersInfo(SPI_GETSNAPTODEFBUTTON,...
Hp

Edited by: Hanspeter Widmer on Nov 7, 2017 10:07 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 7, 2017 11:57 AM   in response to: Hanspeter Widmer in response to: Hanspeter Widmer
Hanspeter Widmer wrote:

BUT: How about the TaskDialog ... Tokyo simple does not support it
(even do not work) and the question rises whether this is/should be
done internally by the MS TaskDialog API ??

I would expect it to be, since that is a system-provided dialog. Are
you specifying a "default" button when displaying the TaskDialog?

--
Remy Lebeau (TeamB)
Hanspeter Widmer

Posts: 19
Registered: 10/14/00
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 8, 2017 12:40 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Here my turtle code :D, where simple Button2Click Windows API directly used works as expected...

In other words, some parent or instance related behavior are may missing... or someone else rings a bell :D

procedure TForm1.Button2Click(Sender: TObject);
begin
Winapi.Windows.MessageBox
(Handle,
'Hallo',
'Test', 1);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
MessageDlg
('Hallo',
mtInformation,
[mbYes] + [mbNo],
0,
mbNo);
End;

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 8, 2017 2:04 PM   in response to: Hanspeter Widmer in response to: Hanspeter Widmer
Hanspeter Widmer wrote:

Here my turtle code :D, where simple *Button2Click Windows API
directly used works as expected.*..

MessageBox() and TaskDialog() are two very different APIs (though the
former may or may not just be a wrapper of the latter in modern Windows
versions). Just because the snap works in MessageBox() does not
guarantee it works in TaskDialog(). If you really want to test this
properly, call TaskDialog (or TaskDialogIndirect()) instead of
MessageBox(), and see what happens (which goes back to my earlier
statement that if you call TaskDialogIndirect() directly, you can
explicitly specify the default button).

In VCL, MessageDlg() uses TaskDialogIndirect() on Vista+ if
UseLatestCommonDialogs is true and visual themes/styles are enabled,
otherwise it uses a custom VCL Form instead (which doesn't respect the
snap setting, in fact NOTHING in the VCL does). MessageDlg() has an
overloaded version that has a DefaultButton parameter, which does get
passed to TaskDialogIndirect().

--
Remy Lebeau (TeamB)
Hanspeter Widmer

Posts: 19
Registered: 10/14/00
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 9, 2017 2:26 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
MessageBox() and TaskDialog() are two very different APIs

Right

MessageDlg() has an overloaded version that has a DefaultButton parameter,
which does get passed to TaskDialogIndirect().

Using Tokyo & Win 10 64 bit and compile as 64 bit!

I debugged this and the default button gets loaded within the required structure for the TaskDialogIndirect Win-API

In other words, as I have seen on TaskDialog with snap to default button:

- TextPad & About

On Win 7 / 64 bit & Folder Option with NO snap to default buttons:

- Altering checkbox "Hide protected operation system files (Recommended)

what me buzzles ... or it's a MS issue or it may be a hidden flag for TaskDialog as TDF_SIZE_TO_CONTENT (alters the TaskDialog given string)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 9, 2017 10:40 AM   in response to: Hanspeter Widmer in response to: Hanspeter Widmer
Hanspeter Widmer wrote:

*In other words, as I have seen on TaskDialog with snap to default
button:*

- TextPad & About

On Win 7 / 64 bit & Folder Option with NO snap to default buttons:

- Altering checkbox "Hide protected operation system files
(Recommended)

I don't understand what you are trying to say.

it's a MS issue

Obviously, since it is an MS API displaying an MS dialog.

or it may be a hidden flag for TaskDialog as TDF_SIZE_TO_CONTENT
(alters the TaskDialog given string)

There is no flag (that I am aware of) that effects snap behavior. This
is something that has to be implemented in the dialog's own code, and
it sounds like MS didn't do that.

But it is fairly easy to implement manually. If you call
TaskDialogIndirect() directly, or use the VCL's TTaskDialog component,
then you have access to the HWND of the dialog, and thus can search it
for the HWND of the desired default button, and then move the mouse
cursor on top of it.

Why doesn’t the "Automatically move pointer to the default button in a
dialog box" work for nonstandard dialog boxes, and how do I add it to
my own nonstandard dialog boxes?
https://blogs.msdn.microsoft.com/oldnewthing/20130826-00/?p=3413

For example:

type
  TMyForm = class(TForm)
    Button1: TButton;
    TaskDialog1: TTaskDialog;
    procedure Button1Click(Sender: TObject);
    procedure TaskDialog1DialogConstructed(Sender: TObject);
  protected
    procedure WndProc(var Message: TMessage); override;
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
procedure TMyForm.Button1Click(Sender: TObject);
begin
  //MessageDlg('test', mtInformation, [mbYes, mbRetry], 0, mbRetry);
  TaskDialog1.Execute(Handle);
end;
 
const
  WM_CHECKSNAPDEFBUTTON = WM_APP + 1;
 
procedure TMyForm.TaskDialog1DialogConstructed(Sender: TObject);
begin
  PostMessage(Handle, WM_CHECKSNAPDEFBUTTON, 0, 0);
end;
 
function FindRetryBtn(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
  Buffer: array[0..10] of Char;
begin
  GetClassName(hwnd, Buffer, Length(Buffer));
  if StrComp(Buffer, 'Button') = 0 then
  begin
    GetWindowText(hwnd, Buffer, Length(Buffer));
    if StrComp(Buffer, 'Retry') = 0 then
    begin
      PHandle(lParam)^ := hwnd;
      Result := False;
    end;
  end;
  Result := True;
end;
 
procedure TMyForm.WndProc(var Message: TMessage);
var
  SnapToDefButton: BOOL;
  rcBtn: TRect;
  BtnWnd: HWND;
begin
  if Message.Msg = WM_CHECKSNAPDEFBUTTON then
  begin
    SnapToDefButton := False;
    if SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0,
@SnapToDefButton, 0)
      and SnapToDefButton
      and (TaskDialog1.Handle = GetForegroundWindow()) then
    begin
      BtnWnd := 0;
      EnumChildWindows(TaskDialog1.Handle, @FindRetryBtn,
LPARAM(@BtnWnd));
      GetWindowRect(BtnWnd, rcBtn);
      Mouse.CursorPos := Point(rcBtn.Left + (rcBtn.Width div 2),
rcBtn.Top + (rcBtn.Height div 2));
    end;
  end
  else
    inherited;
end;


object MyForm: TMyForm
  Left = 0
  Top = 0
  Caption = 'MyForm'
  ClientHeight = 243
  ClientWidth = 527
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 232
    Top = 128
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object TaskDialog1: TTaskDialog
    Buttons = <
      item
        Caption = 'OK'
        ModalResult = 1
      end
      item
        Caption = 'Retry'
        Default = True
        ModalResult = 4
      end>
    CommonButtons = []
    Flags = [tfUseHiconMain, tfAllowDialogCancellation]
    RadioButtons = <>
    Text = 'test'
    OnDialogConstructed = TaskDialog1DialogConstructed
    Left = 336
    Top = 88
  end
end


--
Remy Lebeau (TeamB)
Hanspeter Widmer

Posts: 19
Registered: 10/14/00
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 10, 2017 5:57 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy,

thank you for your effort, as it looks like that some App's (none Delphi) has the TaskDialog & Snap To implemented.

I will invest more on this, while would like to alter the Task-Indirect-Callback & Create event (as already did) to have a generic solution, while to fiddle with callbacks within each App's FORM, is the way to go (IMHO).

The only question would be (Within Task-Indirect-Callback), how to fiddle out the default button and position :D

Hp

Edited by: Hanspeter Widmer on Nov 10, 2017 5:57 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 10, 2017 12:15 PM   in response to: Hanspeter Widmer in response to: Hanspeter Widmer
Hanspeter Widmer wrote:

I will invest more on this, while would like to alter the
Task-Indirect-Callback & Create event (as already did) to have a
generic solution, while to fiddle with callbacks within each App's
FORM, is the way to go (IMHO).

The only question would be (Within Task-Indirect-Callback), how to
fiddle out the default button and position :D

I already showed you code that can enumerate the dialog's buttons using
EnumChildWindows(), and to position the mouse cursor over a specific
button given its HWND. Simply tweak the enumeration callback to look
for a button that has the BS_DEFPUSHBUTTON style, instead of a button
with a specific caption text.

--
Remy Lebeau (TeamB)
Hanspeter Widmer

Posts: 19
Registered: 10/14/00
Re: Dialogs & TaskDialog Cursor Snap to Default Button [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 13, 2017 12:38 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy,

Simply tweak the enumeration callback to look
for a button that has the BS_DEFPUSHBUTTON style, instead of a button
with a specific caption text.

OK, with your help I got it and would some thin enhancement for all Delphi Turtles and even for the DEV Team :D

[1] On TTaskMessageDialog.DoOnDialogCreated;

[2] Add the variables

LBtnWnd: HWND;
LSnapToDefButton: BOOL;
LrcBtn: TRect;

[3] Add after SetWindowPos call

if SystemParametersInfo (SPI_GETSNAPTODEFBUTTON, 0, @LSnapToDefButton, 0) then begin
LBtnWnd := 0;
EnumChildWindows(Handle, @FindDefaultBtn, LPARAM(@LBtnWnd));
GetWindowRect (LBtnWnd, LrcBtn);
Mouse.CursorPos := Point(LrcBtn.Left + (LrcBtn.Width div 2), LrcBtn.Top + (LrcBtn.Height div 2));
end

[4] Add the enumeration call before TTaskMessageDialog.DoOnDialogCreated procedure

function FindDefaultBtn (hwnd: HWND; lParam: LPARAM): BOOL; stdcall; var
Buffer: array[0..10] of Char;
begin
GetClassName(hwnd, Buffer, Length(Buffer));

if StrComp(Buffer, 'Button') = 0 then begin

If GetWindowLong(hwnd, GWL_STYLE) And BS_DEFPUSHBUTTON <> 0 Then Begin
PHandle(lParam)^ := hwnd;
Result := False;
end;
end;

Result := True;
end;

Cheers

Hp
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02