LCL-GTK3: Improve messageboxes. Issue #37269, patch from Anton Kavalenka.

git-svn-id: trunk@63594 -
This commit is contained in:
juha 2020-07-17 17:42:48 +00:00
parent 09a6fe02b5
commit 08fb55efbb

View File

@ -1076,8 +1076,135 @@ begin
Result := False;
end;
function TGtk3WidgetSet.AskUser(const DialogCaption, DialogMessage: string; DialogType:
LongInt; Buttons: TDialogButtons; HelpCtx: Longint): LongInt;
type
TBtnListfunction=function(ndx:integer):longint of object;
TGtk3DialogFactory = class
btn_def: PGtkButton;
DefaultNdx: Integer;
fButtons: TDialogButtons;
pButtons: PLongint;
fCaption: string;
fDialogType:longint;
Dialog: PGtkMessageDialog;
DialogResult: Integer;
constructor CreateAsk(const DialogCaption, DialogMessage: string;
DialogType: LongInt; Buttons: TDialogButtons; HelpCtx: Longint);
constructor CreatePrompt(const DialogCaption, DialogMessage: string;
DialogType: LongInt; Buttons: PLongInt;
ButtonCount: LongInt; DefaultIndex: LongInt; EscapeResult: LongInt);
class function tr(UseWidgetStr: boolean; const TranslatedStr, WidgetStr: String): string;
destructor Destroy;override;
procedure run;
function btn_coll_info(ndx:integer):longint;
function btn_ptr_info(ndx:integer):longint;
procedure update_widget_list(const func:TBtnListFunction);
procedure CreateButton(const ALabel : String; const AResponse: Integer;
const AImageHint: Integer = -1);
class function ResponseID(const AnID: Integer): Integer;
class function gtk_resp_to_lcl(const gtk_resp:integer):integer;
class function MessageType(ADialogType:longint):TGtkMessageType;
end;
class function TGtk3DialogFactory.ResponseID(const AnID: Integer): Integer;
begin
case AnID of
idButtonOK : Result := GTK_RESPONSE_OK;
idButtonCancel : Result := GTK_RESPONSE_CANCEL;
idButtonHelp : Result := GTK_RESPONSE_HELP;
idButtonYes : Result := GTK_RESPONSE_YES;
idButtonNo : Result := GTK_RESPONSE_NO;
idButtonClose : Result := GTK_RESPONSE_CLOSE;
idButtonAbort : Result := GTK_RESPONSE_REJECT;
idButtonRetry : Result := GTK_RESPONSE_LCL_RETRY;
idButtonIgnore : Result := GTK_RESPONSE_LCL_IGNORE;
idButtonAll : Result := GTK_RESPONSE_LCL_ALL;
idButtonNoToAll : Result := GTK_RESPONSE_LCL_NOTOALL;
idButtonYesToAll : Result := GTK_RESPONSE_LCL_YESTOALL;
else
Result:=AnID;
end;
end;
class function TGtk3DialogFactory.gtk_resp_to_lcl(const gtk_resp:integer):integer;
begin
case gtk_resp of
-5{GTK_RESPONSE_OK}: Result:= mrOK;
-6{GTK_RESPONSE_CANCEL}: Result := mrCancel;
-7{GTK_RESPONSE_CLOSE}: Result:=mrClose;
-8{GTK_RESPONSE_YES}: Result:=mrYes;
-9{GTK_RESPONSE_NO}: Result:=mrNo;
-10{GTK_RESPONSE_APPLY}: Result:=mrAll;
// -11{GTK_RESPONSE_HELP}: Result:=mrhelp;
-1{GTK_RESPONSE_NONE}: Result:=mrNone;
-2{GTK_RESPONSE_REJECT}: Result:=mrAbort;
-3{GTK_RESPONSE_ACCEPT}: Result:=mrAll;
else
Result:=gtk_resp;
end;
end;
procedure TGtk3DialogFactory.CreateButton(
const ALabel : String;
const AResponse: Integer;
const AImageHint: Integer = -1);
var
NewButton: PGtkWidget;
//BitmapHandle, MaskHandle: HBitmap;
// GDIObject: PGDIObject;
//Pixbuf: PGdkPixbuf;
// Mask: PGdkBitmap;
//Img: PGtkWidget;
begin
NewButton := gtk_dialog_add_button(Dialog,
PgChar({Ampersands2Underscore}(ALabel)), AResponse);
gtk_button_set_use_underline(PGtkButton(NewButton), True);
g_object_set_data(PGObject(NewButton), 'modal_result',
{%H-}Pointer(PtrInt(AResponse)));
(*
if AImageHint >= 0 then
begin
if ThemeServices.GetStockImage(AImageHint, BitmapHandle, MaskHandle) then
begin
GDIObject := {%H-}PGDIObject(BitmapHandle);
Mask := nil;
Pixbuf := nil;
if GDIObject^.GDIBitmapType = gbPixbuf then
Pixbuf := GDIObject^.GDIPixbufObject
else
Mask := CreateGdkMaskBitmap(BitmapHandle, MaskHandle);
Img := gtk_image_new;
if Pixbuf <> nil then
gtk_image_set_from_pixbuf(PGtkImage(Img), Pixbuf)
else
gtk_image_set_from_pixmap(PGtkImage(Img), GDIObject^.GDIPixmapObject.Image, Mask);
gtk_button_set_image(PGtkButton(NewButton), Img);
if Mask <> nil then
g_object_unref(Mask);
DeleteObject(BitmapHandle);
DeleteObject(MaskHandle);
end;
end;
*)
end;
class function TGtk3DialogFactory.MessageType(ADialogType:longint):TGtkMessageType;
begin
case ADialogType of
idDialogWarning: Result := GTK_MESSAGE_WARNING;
idDialogError: Result := GTK_MESSAGE_ERROR;
idDialogInfo : Result := GTK_MESSAGE_INFO;
idDialogConfirm : Result := GTK_MESSAGE_QUESTION;
else
Result := GTK_MESSAGE_INFO;
end;
end;
const
ButtonResults : array[mrNone..mrYesToAll] of Longint = (
@ -1085,125 +1212,47 @@ const
idButtonIgnore, idButtonYes,idButtonNo, idButtonAll, idButtonNoToAll,
idButtonYesToAll);
constructor TGtk3DialogFactory.CreateAsk(const DialogCaption,
DialogMessage: string; DialogType: LongInt;
Buttons: TDialogButtons; HelpCtx: Longint);
var
Btn,btn_def: PGtkButton;
BtnId: Longint;
Dialog: PGtkMessageDialog;
ADialogResult: Integer;
GtkDialogType: TGtkMessageType;
Btns: TGtkButtonsType;
BtnIdx: Integer;
DefaultNdx: Integer;
X: Integer;
MainList,ChildList: PGList;
Title: String;
//ActiveWindow: HWND;
BtnResult: LongInt;
n: Integer;
i, BtnIdx, BtnID: Integer;
dbtn:TDialogButton;
procedure CreateButton(const ALabel : String; const AResponse: Integer;
const AImageHint: Integer = -1);
var
NewButton: PGtkWidget;
//BitmapHandle, MaskHandle: HBitmap;
// GDIObject: PGDIObject;
//Pixbuf: PGdkPixbuf;
// Mask: PGdkBitmap;
//Img: PGtkWidget;
begin
NewButton := gtk_dialog_add_button(Dialog,
PgChar({Ampersands2Underscore}(ALabel)), AResponse);
gtk_button_set_use_underline(PGtkButton(NewButton), True);
g_object_set_data(PGObject(NewButton), 'modal_result',
{%H-}Pointer(PtrInt(AResponse)));
(*
if AImageHint >= 0 then
begin
if ThemeServices.GetStockImage(AImageHint, BitmapHandle, MaskHandle) then
begin
GDIObject := {%H-}PGDIObject(BitmapHandle);
Mask := nil;
Pixbuf := nil;
if GDIObject^.GDIBitmapType = gbPixbuf then
Pixbuf := GDIObject^.GDIPixbufObject
else
Mask := CreateGdkMaskBitmap(BitmapHandle, MaskHandle);
Img := gtk_image_new;
if Pixbuf <> nil then
gtk_image_set_from_pixbuf(PGtkImage(Img), Pixbuf)
else
gtk_image_set_from_pixmap(PGtkImage(Img), GDIObject^.GDIPixmapObject.Image, Mask);
gtk_button_set_image(PGtkButton(NewButton), Img);
if Mask <> nil then
g_object_unref(Mask);
DeleteObject(BitmapHandle);
DeleteObject(MaskHandle);
end;
end;
*)
end;
function ResponseID(const AnID: Integer): Integer;
begin
case AnID of
idButtonOK : Result := GTK_RESPONSE_OK;
idButtonCancel : Result := GTK_RESPONSE_CANCEL;
idButtonHelp : Result := GTK_RESPONSE_HELP;
idButtonYes : Result := GTK_RESPONSE_YES;
idButtonNo : Result := GTK_RESPONSE_NO;
idButtonClose : Result := GTK_RESPONSE_CLOSE;
idButtonAbort : Result := GTK_RESPONSE_REJECT;
idButtonRetry : Result := GTK_RESPONSE_LCL_RETRY;
idButtonIgnore : Result := GTK_RESPONSE_LCL_IGNORE;
idButtonAll : Result := GTK_RESPONSE_LCL_ALL;
idButtonNoToAll : Result := GTK_RESPONSE_LCL_NOTOALL;
idButtonYesToAll : Result := GTK_RESPONSE_LCL_YESTOALL;
else
Result:=AnID;
end;
end;
Title: String;
BtnResult: LongInt;
begin
Result := mrNone;
ReleaseCapture;
ADialogResult := mrNone;
case DialogType of
idDialogWarning: GtkDialogType := GTK_MESSAGE_WARNING;
idDialogError: GtkDialogType := GTK_MESSAGE_ERROR;
idDialogInfo : GtkDialogType := GTK_MESSAGE_INFO;
idDialogConfirm : GtkDialogType := GTK_MESSAGE_QUESTION;
else
GtkDialogType := GTK_MESSAGE_INFO;
end;
DialogResult := mrNone;
fDialogType := DialogType;
GtkDialogType := MessageType(fDialogType); // map LCLINTF -> GTK
fButtons:=Buttons;
fCaption:=DialogCaption;
Btns := GTK_BUTTONS_NONE;
DefaultNdx := 0;
for X := 0 to Buttons.Count - 1 do
for i := 0 to Buttons.Count - 1 do
begin
if Buttons[X].Default then
DefaultNdx := X;
if Buttons[i].Default then
DefaultNdx := i;
if (ADialogResult = mrNone) and
(Buttons[X].ModalResult in [mrCancel, mrAbort, mrIgnore,
mrNo, mrNoToAll]) then
ADialogResult := Buttons[X].ModalResult;
if (DialogResult = mrNone) and
(Buttons[i].ModalResult in [mrCancel, mrAbort, mrIgnore, mrNo, mrNoToAll])
then
DialogResult := Buttons[i].ModalResult;
end;
Dialog := gtk_message_dialog_new(nil, GTK_DIALOG_MODAL, GtkDialogType, Btns, nil , []);
gtk_message_dialog_set_markup(PGtkMessageDialog(Dialog), PGChar(DialogMessage));
g_signal_connect_data(Dialog, 'delete-event',
TGCallback(@PromptUserBoxClosed),
@ADialogResult, nil, 0);
@DialogResult, nil, 0);
if Btns = GTK_BUTTONS_NONE then
begin
// gtk2 have reverted buttons eg. No, Yes
// gtk3 have reverted buttons eg. No, Yes
for BtnIdx := Buttons.Count - 1 downto 0 do
begin
dbtn:=Buttons[BtnIdx];
@ -1229,6 +1278,28 @@ begin
end;
end;
update_widget_list(@btn_coll_info);
end;
function TGtk3DialogFactory.btn_coll_info(ndx:integer):longint;
begin
Result:=fButtons[ndx].ModalResult; // get modal result for button
end;
function TGtk3DialogFactory.btn_ptr_info(ndx:integer):longint;
begin
Result:=pButtons[ndx];
end;
procedure TGtk3DialogFactory.update_widget_list(const func:TBtnListFunction);
var
btn:PgtkButton;
BtnIdx,BtnID,BtnRes:integer;
MainList,ChildList: PGList;
begin
MainList := gtk_container_get_children(PGtkContainer(Dialog^.get_action_area));
ChildList:=g_list_last(MainList);
BtnIdx:=0;
@ -1240,20 +1311,19 @@ begin
if g_type_check_instance_is_a(ChildList^.Data, gtk_button_get_type) then
begin
Btn := PGtkButton(ChildList^.Data);
// writeln('btn[',BtnIdx,'] ',Btn^.get_label);
BtnID := -1;
dbtn:=Buttons[BtnIdx];
BtnResult:=dbtn.ModalResult;
if (BtnResult>=Low(ButtonResults)) and (BtnResult<=High(ButtonResults)) then
BtnID := ButtonResults[dbtn.ModalResult]
BtnRes:=func(BtnIdx); // process button
if (BtnRes>=Low(ButtonResults)) and (BtnRes<=High(ButtonResults)) then
BtnID := ButtonResults[BtnRes]
else
BtnID := dbtn.ModalResult;
BtnID := BtnRes;
if BtnID = idButtonCancel then
g_object_set_data(PGObject(Dialog), 'modal_result', Pointer(idButtonCancel));
g_object_set_data(PGObject(Dialog), 'modal_result', Pointer(idButtonCancel));
g_signal_connect_data(Btn, 'clicked',
TGCallback(@PromptUserButtonClicked), @ADialogResult, nil, 0);
TGCallback(@PromptUserButtonClicked), @DialogResult, nil, 0);
if DefaultNdx = BtnIdx then
begin
@ -1275,89 +1345,91 @@ begin
if MainList <> nil then
g_list_free(MainList);
if DialogCaption <> '' then
gtk_window_set_title(PGtkWindow(Dialog), PGChar(DialogCaption))
end;
procedure TGtk3DialogFactory.run;
var
Title:string;
begin
if not Assigned(Dialog) then exit;
if fCaption <> '' then
Title:=fCaption
else
begin
Title := '';
case DialogType of
case fDialogType of
idDialogWarning: Title := rsMtWarning;
idDialogError: Title := rsMtError;
idDialogInfo : Title := rsMtInformation;
idDialogConfirm : Title := rsMtConfirmation;
end;
gtk_window_set_title(PGtkWindow(Dialog), PGChar(Title));
end;
gtk_window_set_title(PGtkWindow(Dialog), PGChar(Title));
gtk_dialog_run(Dialog);
gtk_widget_destroy(Dialog);
Self.DialogResult:=Self.gtk_resp_to_lcl(Self.DialogResult);
end;
Result := ADialogResult;
class function TGtk3DialogFactory.tr(UseWidgetStr: boolean; const TranslatedStr, WidgetStr: String): string;
begin
if UseWidgetStr then
Result:=WidgetStr
else
Result:=TranslatedStr;
end;
destructor TGtk3DialogFactory.Destroy;
begin
if Assigned(Dialog) then
gtk_widget_destroy(Dialog);
end;
function TGtk3WidgetSet.AskUser(const DialogCaption, DialogMessage: string; DialogType:
LongInt; Buttons: TDialogButtons; HelpCtx: Longint): LongInt;
var
fact:TGtk3DialogFactory;
begin
fact:=TGtk3DialogFactory.CreateAsk(DialogCaption,DialogMessage,DialogType,Buttons,HelpCtx);
try
fact.run;
Result := fact.DialogResult;
finally
fact.Free;
end;
end;
function TGtk3WidgetSet.PromptUser(const DialogCaption: string;
const DialogMessage: string; DialogType: LongInt; Buttons: PLongInt;
ButtonCount: LongInt; DefaultIndex: LongInt; EscapeResult: LongInt): LongInt;
var
Btn: PGtkButton;
Dialog: PGtkMessageDialog;
ADialogResult: Integer;
fact:TGtk3DialogFactory;
begin
fact:=TGtk3DialogFactory.CreatePrompt(DialogCaption,DialogMessage,
DialogType,Buttons,ButtonCount,DefaultIndex,EscapeResult);
try
fact.run;
Result:=fact.DialogResult;
finally
fact.Free;
end;
end;
constructor TGtk3DialogFactory.CreatePrompt(const DialogCaption: string;
const DialogMessage: string; DialogType: LongInt; Buttons: PLongInt;
ButtonCount: LongInt; DefaultIndex: LongInt; EscapeResult: LongInt);
var
x,i:integer;
GtkDialogType: TGtkMessageType;
Btns: TGtkButtonsType;
BtnIdx: Integer;
DefaultID: Integer;
X: Integer;
MainList,ChildList: PGList;
Title: String;
n: Integer;
procedure CreateButton(const ALabel : String; const AResponse: Integer);
var
NewButton: PGtkButton;
begin
NewButton := PGtkButton(gtk_dialog_add_button(Dialog,
PgChar(ALabel), AResponse));
gtk_button_set_use_underline(NewButton, True);
end;
function tr(UseWidgetStr: boolean; const TranslatedStr, WidgetStr: String): string;
begin
if UseWidgetStr then
Result:=WidgetStr
else
Result:=TranslatedStr;
end;
function ResponseID(const AnID: Integer): Integer;
begin
case AnID of
idButtonOK : Result := GTK_RESPONSE_OK;
idButtonCancel : Result := GTK_RESPONSE_CANCEL;
idButtonHelp : Result := GTK_RESPONSE_HELP;
idButtonYes : Result := GTK_RESPONSE_YES;
idButtonNo : Result := GTK_RESPONSE_NO;
idButtonClose : Result := GTK_RESPONSE_CLOSE;
idButtonAbort : Result := GTK_RESPONSE_REJECT;
idButtonRetry : Result := GTK_RESPONSE_LCL_RETRY;
idButtonIgnore : Result := GTK_RESPONSE_LCL_IGNORE;
idButtonAll : Result := GTK_RESPONSE_LCL_ALL;
idButtonNoToAll : Result := GTK_RESPONSE_LCL_NOTOALL;
idButtonYesToAll : Result := GTK_RESPONSE_LCL_YESTOALL;
end;
end;
begin
Result := -1;
ReleaseCapture;
ADialogResult := EscapeResult;
case DialogType of
idDialogWarning: GtkDialogType := GTK_MESSAGE_WARNING;
idDialogError: GtkDialogType := GTK_MESSAGE_ERROR;
idDialogInfo : GtkDialogType := GTK_MESSAGE_INFO;
idDialogConfirm : GtkDialogType := GTK_MESSAGE_QUESTION;
else
GtkDialogType := GTK_MESSAGE_INFO;
end;
DialogResult := EscapeResult;
fDialogType := DialogType;
GtkDialogType := MessageType(fDialogType); // map LCLINTF -> GTK
pButtons:=Buttons;
fCaption:=DialogCaption;
Btns := GTK_BUTTONS_NONE;
DefaultId := 0;
@ -1373,13 +1445,13 @@ begin
g_signal_connect_data(GPointer(Dialog), 'delete-event',
TGCallback(@PromptUserBoxClosed),
@ADialogResult, nil, 0);
@DialogResult, nil, 0);
if Btns = GTK_BUTTONS_NONE then
begin
for BtnIdx := ButtonCount-1 downto 0 do
for i := ButtonCount-1 downto 0 do
begin
case Buttons[BtnIdx] of
case Buttons[i] of
idButtonOK : CreateButton(tr(rsmbOK='&OK',rsmbOK, 'gtk-ok'), GTK_RESPONSE_OK);
idButtonCancel : CreateButton(tr(rsmbCancel='Cancel',rsmbCancel,'gtk-cancel'), GTK_RESPONSE_CANCEL);
idButtonHelp : CreateButton(tr(rsmbHelp='&Help',rsmbHelp,'gtk-help'), GTK_RESPONSE_HELP);
@ -1395,67 +1467,7 @@ begin
end;
end;
end;
MainList := gtk_container_get_children(PGtkContainer(Dialog^.get_action_area));
ChildList := MainList;
BtnIdx := 0;
n := 0;
while ChildList <> nil do
begin
if (ChildList^.Data <> nil) then
begin
if g_type_check_instance_is_a(ChildList^.Data, gtk_button_get_type) then
begin
Btn := PGtkButton(ChildList^.Data);
if Buttons[BtnIdx] = idButtonCancel then
g_object_set_data(PGObject(Dialog), 'modal_result', Pointer(idButtonCancel));
X := Buttons[BtnIdx];
g_object_set_data(PGObject(Btn), 'modal_result',
{%H-}Pointer(PtrInt(X)));
g_signal_connect_data(gPointer(Btn), 'clicked',
TGCallback(@PromptUserButtonClicked), @ADialogResult, nil, 0);
if DefaultID = Buttons[BtnIdx] then
begin
gtk_dialog_set_default_response(Dialog, ResponseID(Buttons[BtnIdx]));
X := Buttons[BtnIdx];
g_object_set_data(PGObject(Dialog), 'modal_result',
{%H-}Pointer(PtrInt(X)));
end;
inc(BtnIdx);
end;
end;
inc(n);
ChildList := g_list_nth(ChildList, n);
// ChildList := g_list_next(ChildList);
end;
if MainList <> nil then
g_list_free(MainList);
if DialogCaption <> '' then
gtk_window_set_title(PGtkWindow(Dialog), PGChar(DialogCaption))
else
begin
Title := '';
case DialogType of
idDialogWarning: Title := rsMtWarning;
idDialogError: Title := rsMtError;
idDialogInfo : Title := rsMtInformation;
idDialogConfirm : Title := rsMtConfirmation;
end;
gtk_window_set_title(PGtkWindow(Dialog), PGChar(Title));
end;
// do not allow jump to GtkLabel from button via Tab ?
// if Gtk3IsBox(Dialog^.get_message_area) then
// PGtkBox(Dialog^.get_message_area)^.set_focus_chain(nil);
gtk_dialog_run(Dialog);
gtk_widget_destroy(Dialog);
Result := ADialogResult;
update_widget_list(@btn_ptr_info);
end;
function TGtk3WidgetSet.SetComboMinDropDownSize(Handle: HWND; MinItemsWidth,