View Issue Details

IDProjectCategoryView StatusLast Update
0037802LazarusWidgetsetpublic2020-09-24 12:08
ReporterAnton Kavalenka Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version2.1 (SVN) 
Summary0037802: gtk3: Implement TextHint for TEdit
DescriptionGTK3 has strange textHint behavior. The TextHint is show when GtkEntry (editor) has no focus.
TagsNo tags attached.
Fixed in Revisionr63911
LazTarget-
WidgetsetGTK 3
Attached Files

Relationships

related to 0037277 closedJuha Manninen GTK2: TCustomEdit.NumbersOnly not implemented 
related to 0036942 closedJuha Manninen GTK3: Anchor autosizing 

Activities

Anton Kavalenka

2020-09-23 17:21

reporter  

gtk3texthint.diff (7,512 bytes)   
Index: lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas	(working copy)
@@ -760,19 +760,18 @@
   GTK_INPUT_HINT_INHIBIT_OSK: TGtkInputHints = 128;
 
 type
-  TGtkInputPurpose = Integer;
-const
+  TGtkInputPurpose = (
   { GtkInputPurpose }
-  GTK_INPUT_PURPOSE_FREE_FORM: TGtkInputPurpose = 0;
-  GTK_INPUT_PURPOSE_ALPHA: TGtkInputPurpose = 1;
-  GTK_INPUT_PURPOSE_DIGITS: TGtkInputPurpose = 2;
-  GTK_INPUT_PURPOSE_NUMBER: TGtkInputPurpose = 3;
-  GTK_INPUT_PURPOSE_PHONE: TGtkInputPurpose = 4;
-  GTK_INPUT_PURPOSE_URL: TGtkInputPurpose = 5;
-  GTK_INPUT_PURPOSE_EMAIL: TGtkInputPurpose = 6;
-  GTK_INPUT_PURPOSE_NAME: TGtkInputPurpose = 7;
-  GTK_INPUT_PURPOSE_PASSWORD: TGtkInputPurpose = 8;
-  GTK_INPUT_PURPOSE_PIN: TGtkInputPurpose = 9;
+  GTK_INPUT_PURPOSE_FREE_FORM = 0,
+  GTK_INPUT_PURPOSE_ALPHA = 1,
+  GTK_INPUT_PURPOSE_DIGITS = 2,
+  GTK_INPUT_PURPOSE_NUMBER = 3,
+  GTK_INPUT_PURPOSE_PHONE = 4,
+  GTK_INPUT_PURPOSE_URL = 5,
+  GTK_INPUT_PURPOSE_EMAIL = 6,
+  GTK_INPUT_PURPOSE_NAME = 7,
+  GTK_INPUT_PURPOSE_PASSWORD = 8,
+  GTK_INPUT_PURPOSE_PIN = 9);
 
 type
   TGtkImageType = Integer;
Index: lcl/interfaces/gtk3/gtk3int.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3int.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3int.pas	(working copy)
@@ -124,6 +124,7 @@
     procedure DCRedraw(CanvasHandle: HDC); override;
     procedure DCSetAntialiasing(CanvasHandle: HDC; AEnabled: Boolean); override;
     procedure SetDesigning(AComponent: TComponent); override;
+    function  GetLCLCapability(ACapability: TLCLCapability): PtrUInt; override;
 
     function CreateTimer(Interval: integer; TimerFunc: TWSTimerProc): THandle; override;
     function DestroyTimer(TimerHandle: THandle): boolean; override;
@@ -211,6 +212,16 @@
   Result := HWND(Gtk3WidgetFromGtkWidget(AWidget));
 end;
 
+function TGtk3WidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
+begin
+  case ACapability of
+  lcTextHint: Result := LCL_CAPABILITY_YES;
+  else
+    Result := inherited GetLCLCapability(ACapability);
+  end;
+end;
+
+
 {$i gtk3object.inc}
 {$i gtk3winapi.inc}
 {$i gtk3lclintf.inc}
Index: lcl/interfaces/gtk3/gtk3widgets.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3widgets.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3widgets.pas	(working copy)
@@ -217,8 +217,12 @@
     procedure SetEchoMode(AVisible: Boolean);
     procedure SetMaxLength(AMaxLength: Integer);
     procedure SetPasswordChar(APasswordChar: Char);
+    procedure SetNumbersOnly(ANumbersOnly:boolean);
+    procedure SetTextHint(const AHint:string);
+    function GetTextHint:string;
     function IsWidgetOk: Boolean; override;
     property Alignment: TAlignment read GetAlignment write SetAlignment;
+    property TextHint:string read GetTextHint write SetTextHint;
   end;
 
   { TGtk3SpinEdit }
@@ -2649,6 +2653,7 @@
   ARect: TGdkRectangle;
   ARgba: TGdkRGBA;
   i: TGtkStateType;
+  mh,nh,mw,nw:gint;
 begin
   FFocusableByMouse := False;
   FCentralWidget := nil;
@@ -2671,6 +2676,13 @@
       height := LCLObject.Height;
     end;
     FWidget^.set_allocation(@ARect);
+
+    fWidget^.get_preferred_height(@mh,@nh);
+    fWidget^.get_preferred_width(@mw,@nw);
+
+    LCLObject.Constraints.MinHeight:=mh;
+    LCLObject.Constraints.MinWidth:=mw;
+
   end;
   LCLIntf.SetProp(HWND(Self),'lclwidget',Self);
 
@@ -3412,6 +3424,10 @@
 procedure TGtk3Entry.InitializeWidget;
 begin
   inherited InitializeWidget;
+
+  Self.SetTextHint(TCustomEdit(Self.LCLObject).TextHint);
+  Self.SetNumbersOnly(TCustomEdit(Self.LCLObject).NumbersOnly);
+
   g_signal_connect_data(PGtkEntry(FWidget), 'changed', TGCallback(@Gtk3EntryChanged), Self, nil, 0);
   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'deleted-text', TGCallback(@Gtk3EntryDeletedText), Self, nil, 0);
   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'inserted-text', TGCallback(@Gtk3EntryInsertedText), Self, nil, 0);
@@ -3430,6 +3446,30 @@
   end;
 end;
 
+procedure TGtk3Entry.SetNumbersOnly(ANumbersOnly:boolean);
+const
+  ips:array[boolean]of TGtkInputPurpose=(GTK_INPUT_PURPOSE_FREE_FORM,GTK_INPUT_PURPOSE_NUMBER);
+begin
+  // this is not enough for numeric input - it is just a hint for GUI
+  if IsWidgetOK then
+    PGtkEntry(FWidget)^.set_input_purpose(ips[ANumbersOnly]);
+end;
+
+procedure TGtk3Entry.SetTextHint(const AHint: string);
+begin
+  if IsWidgetOK then
+    PGtkEntry(FWidget)^.set_placeholder_text(PgChar(AHint));
+end;
+
+function TGtk3Entry.GetTextHint:string;
+
+begin
+  if IsWidgetOK then
+    Result:=PGtkEntry(FWidget)^.get_placeholder_text()
+  else
+    Result:='';
+end;
+
 procedure TGtk3Entry.SetEchoMode(AVisible: Boolean);
 begin
   if IsWidgetOK then
@@ -6562,6 +6602,8 @@
   g_signal_connect_data(btn,'clicked',
         TGCallback(@TGtk3Button.ButtonClicked), LCLObject, nil, 0);
 
+  LCLObject.ControlStyle:=LCLObject.ControlStyle+[csClickEvents];
+
   FMargin := -1;
   FLayout := GTK_POS_LEFT;
   FSpacing := 2; // default gtk3 spacing is 2
Index: lcl/interfaces/gtk3/gtk3wsstdctrls.pp
===================================================================
--- lcl/interfaces/gtk3/gtk3wsstdctrls.pp	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3wsstdctrls.pp	(working copy)
@@ -164,10 +164,12 @@
     class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
     class procedure SetHideSelection(const ACustomEdit: TCustomEdit; NewHideSelection: Boolean); override;
     class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
+    class procedure SetNumbersOnly(const ACustomEdit: TCustomEdit; NewNumbersOnly: Boolean); override;
     class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char); override;
     class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
     class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
     class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
+    class procedure SetTextHint(const ACustomEdit: TCustomEdit; const ATextHint: string); override;
 
     class procedure Cut(const ACustomEdit: TCustomEdit); override;
     class procedure Copy(const ACustomEdit: TCustomEdit); override;
@@ -767,6 +769,14 @@
   TGtk3Entry(ACustomEdit.Handle).SetMaxLength(NewLength);
 end;
 
+class procedure TGtk3WSCustomEdit.SetNumbersOnly(
+  const ACustomEdit: TCustomEdit; NewNumbersOnly: Boolean);
+begin
+  if not WSCheckHandleAllocated(ACustomEdit, 'NumbersOnly') then
+    Exit;
+  TGtk3Entry(ACustomEdit.Handle).SetNumbersOnly(NewNumbersOnly);
+end;
+
 class procedure TGtk3WSCustomEdit.SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char);
 begin
   if not WSCheckHandleAllocated(ACustomEdit, 'SetPasswordChar') then
@@ -799,6 +809,14 @@
   TGtk3Editable(ACustomEdit.Handle).EndUpdate;
 end;
 
+class procedure TGtk3WSCustomEdit.SetTextHint(const ACustomEdit: TCustomEdit;
+  const ATextHint: string);
+begin
+  if not WSCheckHandleAllocated(ACustomEdit, 'SetTextHint') then
+    Exit;
+  TGtk3Entry(ACustomEdit.Handle).SetTextHint(ATextHint);
+end;
+
 class procedure TGtk3WSCustomEdit.Cut(const ACustomEdit: TCustomEdit);
 begin
   ACustomEdit.CopyToClipboard;
gtk3texthint.diff (7,512 bytes)   

Anton Kavalenka

2020-09-23 18:07

reporter   ~0125781

Second patch additionally implements NumberOnly for TEdit
gtk3numberonly.diff (8,674 bytes)   
Index: lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3bindings/lazgtk3.pas	(working copy)
@@ -760,19 +760,18 @@
   GTK_INPUT_HINT_INHIBIT_OSK: TGtkInputHints = 128;
 
 type
-  TGtkInputPurpose = Integer;
-const
+  TGtkInputPurpose = (
   { GtkInputPurpose }
-  GTK_INPUT_PURPOSE_FREE_FORM: TGtkInputPurpose = 0;
-  GTK_INPUT_PURPOSE_ALPHA: TGtkInputPurpose = 1;
-  GTK_INPUT_PURPOSE_DIGITS: TGtkInputPurpose = 2;
-  GTK_INPUT_PURPOSE_NUMBER: TGtkInputPurpose = 3;
-  GTK_INPUT_PURPOSE_PHONE: TGtkInputPurpose = 4;
-  GTK_INPUT_PURPOSE_URL: TGtkInputPurpose = 5;
-  GTK_INPUT_PURPOSE_EMAIL: TGtkInputPurpose = 6;
-  GTK_INPUT_PURPOSE_NAME: TGtkInputPurpose = 7;
-  GTK_INPUT_PURPOSE_PASSWORD: TGtkInputPurpose = 8;
-  GTK_INPUT_PURPOSE_PIN: TGtkInputPurpose = 9;
+  GTK_INPUT_PURPOSE_FREE_FORM = 0,
+  GTK_INPUT_PURPOSE_ALPHA = 1,
+  GTK_INPUT_PURPOSE_DIGITS = 2,
+  GTK_INPUT_PURPOSE_NUMBER = 3,
+  GTK_INPUT_PURPOSE_PHONE = 4,
+  GTK_INPUT_PURPOSE_URL = 5,
+  GTK_INPUT_PURPOSE_EMAIL = 6,
+  GTK_INPUT_PURPOSE_NAME = 7,
+  GTK_INPUT_PURPOSE_PASSWORD = 8,
+  GTK_INPUT_PURPOSE_PIN = 9);
 
 type
   TGtkImageType = Integer;
Index: lcl/interfaces/gtk3/gtk3int.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3int.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3int.pas	(working copy)
@@ -124,6 +124,7 @@
     procedure DCRedraw(CanvasHandle: HDC); override;
     procedure DCSetAntialiasing(CanvasHandle: HDC; AEnabled: Boolean); override;
     procedure SetDesigning(AComponent: TComponent); override;
+    function  GetLCLCapability(ACapability: TLCLCapability): PtrUInt; override;
 
     function CreateTimer(Interval: integer; TimerFunc: TWSTimerProc): THandle; override;
     function DestroyTimer(TimerHandle: THandle): boolean; override;
@@ -211,6 +212,16 @@
   Result := HWND(Gtk3WidgetFromGtkWidget(AWidget));
 end;
 
+function TGtk3WidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
+begin
+  case ACapability of
+  lcTextHint: Result := LCL_CAPABILITY_YES;
+  else
+    Result := inherited GetLCLCapability(ACapability);
+  end;
+end;
+
+
 {$i gtk3object.inc}
 {$i gtk3winapi.inc}
 {$i gtk3lclintf.inc}
Index: lcl/interfaces/gtk3/gtk3widgets.pas
===================================================================
--- lcl/interfaces/gtk3/gtk3widgets.pas	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3widgets.pas	(working copy)
@@ -209,6 +209,7 @@
     procedure SetAlignment(AValue: TAlignment);
   protected
     function EatArrowKeys(const AKey: Word): Boolean; override;
+    procedure InsertText(const atext:pchar;len:gint;var pos:gint;edt:TGtk3Entry);cdecl;
     function getText: String; override;
     procedure setText(const AValue: String); override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
@@ -217,8 +218,12 @@
     procedure SetEchoMode(AVisible: Boolean);
     procedure SetMaxLength(AMaxLength: Integer);
     procedure SetPasswordChar(APasswordChar: Char);
+    procedure SetNumbersOnly(ANumbersOnly:boolean);
+    procedure SetTextHint(const AHint:string);
+    function GetTextHint:string;
     function IsWidgetOk: Boolean; override;
     property Alignment: TAlignment read GetAlignment write SetAlignment;
+    property TextHint:string read GetTextHint write SetTextHint;
   end;
 
   { TGtk3SpinEdit }
@@ -2649,6 +2654,7 @@
   ARect: TGdkRectangle;
   ARgba: TGdkRGBA;
   i: TGtkStateType;
+  mh,nh,mw,nw:gint;
 begin
   FFocusableByMouse := False;
   FCentralWidget := nil;
@@ -2671,6 +2677,13 @@
       height := LCLObject.Height;
     end;
     FWidget^.set_allocation(@ARect);
+
+    fWidget^.get_preferred_height(@mh,@nh);
+    fWidget^.get_preferred_width(@mw,@nw);
+
+    LCLObject.Constraints.MinHeight:=mh;
+    LCLObject.Constraints.MinWidth:=mw;
+
   end;
   LCLIntf.SetProp(HWND(Self),'lclwidget',Self);
 
@@ -3387,6 +3400,23 @@
   Result := AKey in [VK_UP, VK_DOWN];
 end;
 
+procedure TGtk3Entry.InsertText(const atext:pchar;len:gint;var pos:gint;edt:TGtk3Entry);cdecl;
+var
+  i:integer;
+begin
+  if TCustomEdit(edt.LCLObject).NumbersOnly then
+  begin
+    for i := 0 to len-1 do
+    begin
+       if not (atext[i] in ['0'..'9']) then
+       begin
+         g_signal_stop_emission_by_name(Self, 'insert-text');
+         exit;
+       end;
+    end;
+  end;
+end;
+
 function TGtk3Entry.getText: String;
 begin
   if IsValidHandle and IsWidgetOk then
@@ -3412,7 +3442,13 @@
 procedure TGtk3Entry.InitializeWidget;
 begin
   inherited InitializeWidget;
-  g_signal_connect_data(PGtkEntry(FWidget), 'changed', TGCallback(@Gtk3EntryChanged), Self, nil, 0);
+
+  Self.SetTextHint(TCustomEdit(Self.LCLObject).TextHint);
+  Self.SetNumbersOnly(TCustomEdit(Self.LCLObject).NumbersOnly);
+
+  g_signal_connect_data(FWidget, 'changed', TGCallback(@Gtk3EntryChanged), Self, nil, 0);
+  g_signal_connect_data(FWidget, 'insert-text', TGCallback(@TGtk3Entry.InsertText), Self, nil, 0);
+
   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'deleted-text', TGCallback(@Gtk3EntryDeletedText), Self, nil, 0);
   //g_signal_connect_data(PGtkEntry(FWidget)^.get_buffer, 'inserted-text', TGCallback(@Gtk3EntryInsertedText), Self, nil, 0);
 end;
@@ -3430,6 +3466,30 @@
   end;
 end;
 
+procedure TGtk3Entry.SetNumbersOnly(ANumbersOnly:boolean);
+const
+  ips:array[boolean]of TGtkInputPurpose=(GTK_INPUT_PURPOSE_FREE_FORM,GTK_INPUT_PURPOSE_NUMBER);
+begin
+  // this is not enough for numeric input - it is just a hint for GUI
+  if IsWidgetOK then
+    PGtkEntry(FWidget)^.set_input_purpose(ips[ANumbersOnly]);
+end;
+
+procedure TGtk3Entry.SetTextHint(const AHint: string);
+begin
+  if IsWidgetOK then
+    PGtkEntry(FWidget)^.set_placeholder_text(PgChar(AHint));
+end;
+
+function TGtk3Entry.GetTextHint:string;
+
+begin
+  if IsWidgetOK then
+    Result:=PGtkEntry(FWidget)^.get_placeholder_text()
+  else
+    Result:='';
+end;
+
 procedure TGtk3Entry.SetEchoMode(AVisible: Boolean);
 begin
   if IsWidgetOK then
@@ -6562,6 +6622,8 @@
   g_signal_connect_data(btn,'clicked',
         TGCallback(@TGtk3Button.ButtonClicked), LCLObject, nil, 0);
 
+  LCLObject.ControlStyle:=LCLObject.ControlStyle+[csClickEvents];
+
   FMargin := -1;
   FLayout := GTK_POS_LEFT;
   FSpacing := 2; // default gtk3 spacing is 2
Index: lcl/interfaces/gtk3/gtk3wsstdctrls.pp
===================================================================
--- lcl/interfaces/gtk3/gtk3wsstdctrls.pp	(revision 63907)
+++ lcl/interfaces/gtk3/gtk3wsstdctrls.pp	(working copy)
@@ -164,10 +164,12 @@
     class procedure SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); override;
     class procedure SetHideSelection(const ACustomEdit: TCustomEdit; NewHideSelection: Boolean); override;
     class procedure SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
+    class procedure SetNumbersOnly(const ACustomEdit: TCustomEdit; NewNumbersOnly: Boolean); override;
     class procedure SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char); override;
     class procedure SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); override;
     class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override;
     class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override;
+    class procedure SetTextHint(const ACustomEdit: TCustomEdit; const ATextHint: string); override;
 
     class procedure Cut(const ACustomEdit: TCustomEdit); override;
     class procedure Copy(const ACustomEdit: TCustomEdit); override;
@@ -767,6 +769,14 @@
   TGtk3Entry(ACustomEdit.Handle).SetMaxLength(NewLength);
 end;
 
+class procedure TGtk3WSCustomEdit.SetNumbersOnly(
+  const ACustomEdit: TCustomEdit; NewNumbersOnly: Boolean);
+begin
+  if not WSCheckHandleAllocated(ACustomEdit, 'NumbersOnly') then
+    Exit;
+  TGtk3Entry(ACustomEdit.Handle).SetNumbersOnly(NewNumbersOnly);
+end;
+
 class procedure TGtk3WSCustomEdit.SetPasswordChar(const ACustomEdit: TCustomEdit; NewChar: char);
 begin
   if not WSCheckHandleAllocated(ACustomEdit, 'SetPasswordChar') then
@@ -799,6 +809,14 @@
   TGtk3Editable(ACustomEdit.Handle).EndUpdate;
 end;
 
+class procedure TGtk3WSCustomEdit.SetTextHint(const ACustomEdit: TCustomEdit;
+  const ATextHint: string);
+begin
+  if not WSCheckHandleAllocated(ACustomEdit, 'SetTextHint') then
+    Exit;
+  TGtk3Entry(ACustomEdit.Handle).SetTextHint(ATextHint);
+end;
+
 class procedure TGtk3WSCustomEdit.Cut(const ACustomEdit: TCustomEdit);
 begin
   ACustomEdit.CopyToClipboard;
gtk3numberonly.diff (8,674 bytes)   

CudaText man

2020-09-23 18:46

reporter   ~0125784

Good, thanks again Anton.

Juha Manninen

2020-09-23 21:22

developer   ~0125788

Applied, thanks.

Issue History

Date Modified Username Field Change
2020-09-23 17:21 Anton Kavalenka New Issue
2020-09-23 17:21 Anton Kavalenka File Added: gtk3texthint.diff
2020-09-23 18:07 Anton Kavalenka Note Added: 0125781
2020-09-23 18:07 Anton Kavalenka File Added: gtk3numberonly.diff
2020-09-23 18:46 CudaText man Note Added: 0125784
2020-09-23 21:11 Juha Manninen Relationship added related to 0037277
2020-09-23 21:21 Juha Manninen Assigned To => Juha Manninen
2020-09-23 21:21 Juha Manninen Status new => assigned
2020-09-23 21:22 Juha Manninen Status assigned => resolved
2020-09-23 21:22 Juha Manninen Resolution open => fixed
2020-09-23 21:22 Juha Manninen Fixed in Revision => r63911
2020-09-23 21:22 Juha Manninen LazTarget => -
2020-09-23 21:22 Juha Manninen Widgetset GTK 3 => GTK 3
2020-09-23 21:22 Juha Manninen Note Added: 0125788
2020-09-24 11:33 Juha Manninen Relationship added related to 0036942
2020-09-24 12:08 Anton Kavalenka Status resolved => closed