View Issue Details

IDProjectCategoryView StatusLast Update
0035667LazarusWidgetsetpublic2019-11-04 11:33
ReporterAnton KavalenkaAssigned ToZeljan Rikalo 
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Product Version2.0.3 (SVN)Product Build 
Target VersionFixed in Version 
Summary0035667: gtk3: Make toolbar buttons appear
DescriptionImplementation of native GTK3 toolbar.
TagsNo tags attached.
Fixed in Revision62164
LazTarget-
WidgetsetGTK 3
Attached Files
  • gtk3widgets.diff (1,674 bytes)
    Index: gtk3widgets.pas
    ===================================================================
    --- gtk3widgets.pas	(revision 61310)
    +++ gtk3widgets.pas	(working copy)
    @@ -1667,6 +1667,7 @@
       Result:=TGtkPaned.new(ornt[TPairSplitter(Self.LCLObject).SplitterType]);
     end;
     
    +
     { TGtk3Widget }
     
     function TGtk3Widget.GtkEventMouseEnterLeave(Sender: PGtkWidget; Event: PGdkEvent): Boolean;
    @@ -3951,14 +3952,50 @@
     
     function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
     var
    +  i:integer;
       AToolBar: TToolBar;
    +  btn:TToolButton;
    +  gtb:PGtkToolItem;
    +  wmenu:PGtkWIdget;
     begin
       AToolBar := TToolBar(LCLObject);
       FHasPaint := False;
       FWidgetType := [wtWidget, wtContainer];
       Result:=PGtkWidget(TGtkToolbar.new);
    +
    +  // allocate appropriate number of tool items
    +  for i:=0 to AToolbar.ButtonCount-1 do
    +  begin
    +    btn:=AToolBar.Buttons[i];
    +    if btn is TToolButton then
    +    begin
    +      case btn.Style of
    +  	  tbsSeparator:
    +    	  gtb:=TGtkSeparatorToolItem.new();
    +      tbsDropDown:
    +        begin
    +        	gtb:=TGtkMenuToolButton.new(nil,PgChar(btn.Caption));
    +          if Assigned(btn.DropdownMenu) then
    +          begin
    +          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
    +          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
    +          end;
    +        end;
    +      tbsCheck:
    +        begin
    +          gtb:=TGtkToggleToolButton.new();
    +          PGtkToolButton(gtb)^.set_label(PgChar(btn.Caption));
    +        end
    +  	  else
    +    	  gtb:=TGtkToolButton.new(nil,PgChar(btn.Caption));
    + 		  end;
    +      PGtkToolBar(Result)^.add(gtb);
    +    end;
    +  end;
    +
     end;
     
    +
     { TGtk3Page }
     
     procedure TGtk3Page.setText(AValue: String);
    
    gtk3widgets.diff (1,674 bytes)
  • gtk3widgets-2.diff (5,789 bytes)
    Index: gtk3widgets.pas
    ===================================================================
    --- gtk3widgets.pas	(revision 62087)
    +++ gtk3widgets.pas	(working copy)
    @@ -24,7 +24,7 @@
     uses
       Classes, SysUtils, types, math,
       // LCL
    -  Controls, StdCtrls, ExtCtrls, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
    +  Controls, StdCtrls, ExtCtrls, Buttons, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
       Spin, CheckLst, PairSplitter, LCLType, LCLProc, LMessages, LCLMessageGlue, LCLIntf,
       // GTK3
       LazGtk3, LazGdk3, LazGObject2, LazGLib2, LazCairo1, LazPango1, LazGdkPixbuf2,
    @@ -469,7 +469,11 @@
       { TGtk3ToolBar }
     
       TGtk3ToolBar = class(TGtk3Container)
    +  private
    +    fBmpList:TList;
    +    procedure ClearGlyphs;
       protected
    +    destructor Destroy;override;
         function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
       end;
     
    @@ -657,6 +661,7 @@
         FMargin: Integer;
         FLayout: Integer;
         FSpacing: Integer;
    +    FImage: TBitmap;
         function getLayout: Integer;
         function getMargin: Integer;
         procedure SetLayout(AValue: Integer);
    @@ -663,15 +668,18 @@
         procedure SetMargin(AValue: Integer);
         procedure SetSpacing(AValue: Integer);
       protected
    +    procedure SetImage(AImage:TBitmap);
         function getText: String; override;
         procedure setText(const AValue: String); override;
         function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
       public
    +    destructor Destroy;override;
         function IsWidgetOk: Boolean; override;
         procedure SetDefault(const ADefault: Boolean);
         property Layout: Integer read getLayout write SetLayout;
         property Margin: Integer read getMargin write SetMargin;
         property Spacing: Integer read FSpacing write SetSpacing;
    +    property Image:TBitmap read fImage write SetImage;
       end;
     
       { TGtk3ToggleButton }
    @@ -817,7 +825,7 @@
     
     implementation
     
    -uses gtk3int;
    +uses gtk3int,imglist;
     
     const
       GDK_DEFAULT_EVENTS_MASK: TGdkEventMask =
    @@ -3949,14 +3957,108 @@
     
     { TGtk3ToolBar }
     
    +procedure TGtk3ToolBar.ClearGlyphs;
    +var i:integer;
    +begin
    +  if Assigned(fBmpList) then
    +  for i:=fBmpList.Count-1 downto 0 do
    +    TObject(fBmpList[i]).Free;
    +end;
    +
    +destructor TGtk3ToolBar.Destroy;
    +begin
    +  ClearGlyphs;
    +  fBmpList.Free;
    +  inherited Destroy;
    +end;
    +
    +
    +procedure tool_button_clicked(widget: PGtkWidget; data: gPointer); cdecl;
    +begin
    +  if TObject(data) is TToolButton then
    +  TToolButton(data).Click;
    +end;
    +
    +
     function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
     var
    +  i:integer;
       AToolBar: TToolBar;
    +  btn:TToolButton;
    +  gtb:PGtkToolItem;
    +  wmenu,wicon:PGtkWidget;
    +  pb:PGdkPixBuf;
    +  bmp:TBitmap;
    +  resolution:TCustomImageListResolution;
    +  bs:string;
     begin
       AToolBar := TToolBar(LCLObject);
       FHasPaint := False;
       FWidgetType := [wtWidget, wtContainer];
       Result:=PGtkWidget(TGtkToolbar.new);
    +
    +  if not Assigned(fBmpList) then
    +    fBmpList:=TList.Create;
    +
    +  ClearGlyphs;
    +
    +  // allocate appropriate number of tool items
    +  for i:=0 to AToolbar.ButtonCount-1 do
    +  begin
    +    btn:=AToolBar.Buttons[i];
    +    bs:=StringReplace(btn.Caption,'&','_',[rfReplaceAll]);
    +    wicon:=nil;
    +    if btn is TToolButton then
    +    begin
    +      if (btn.ImageIndex>=0) and
    +          assigned(AToolbar.Images) and
    +          not (btn.Style in [tbsSeparator,tbsDivider]) then
    +      begin
    +        bmp:=TBitmap.Create; { this carries gdk pixmap }
    +        resolution:=Atoolbar.Images.Resolution[16];
    +        resolution.GetBitmap(btn.ImageIndex,bmp);
    +        if not bmp.Empty then
    +        begin
    +          //wicon := TGtkImage.new;
    +          pb:=TGtk3Image(bmp.Handle).Handle;
    +          wicon := gtk_image_new_from_pixbuf(pb);
    +        end
    +        else
    +          wicon := nil;
    +        fBmpList.Add(bmp);
    +      end;
    +
    +      case btn.Style of
    +  	  tbsSeparator:
    +    	  gtb:=TGtkSeparatorToolItem.new();
    +      tbsDropDown:
    +        begin
    +        	gtb:=TGtkMenuToolButton.new(wicon,PgChar(bs));
    +          if Assigned(btn.DropdownMenu) then
    +          begin
    +          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
    +          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
    +          end;
    +        end;
    +      tbsCheck:
    +        begin
    +          gtb:=TGtkToggleToolButton.new();
    +          PGtkToolButton(gtb)^.set_label(PgChar(bs));
    +        end
    +  	  else
    +    	  gtb:=TGtkToolButton.new(wicon,PgChar(bs));
    + 		  end;
    +      gtb^.set_tooltip_text(PgChar(btn.Hint));
    +      PgtkToolButton(gtb)^.set_use_underline(true);
    +      PGtkToolBar(Result)^.add(gtb);
    +
    +      if not (btn.Style in [tbsSeparator,tbsDivider]) then
    +      g_signal_connect_data(gtb,'clicked',
    +        TGCallback(@tool_button_clicked), btn, nil, 0);
    +
    +    end;
    +  end;
    +
     end;
     
     { TGtk3Page }
    @@ -6245,6 +6347,13 @@
       end;
     end;
     
    +procedure TGtk3Button.SetImage(AImage: TBitmap);
    +begin
    +  if Assigned(fImage) then
    +    fImage.free;
    +  fImage:=AImage;
    +end;
    +
     function TGtk3Button.getText: String;
     begin
       if IsWidgetOK then
    @@ -6256,17 +6365,31 @@
     procedure TGtk3Button.setText(const AValue: String);
     begin
       if IsWidgetOk then
    -    PGtkButton(FWidget)^.set_label(PgChar(AValue));
    +  begin
    +    PGtkButton(FWidget)^.set_label(PgChar(StringReplace(AValue,'&','_',[rfReplaceAll])));
    +  end;
     end;
     
     function TGtk3Button.CreateWidget(const Params: TCreateParams): PGtkWidget;
    +var
    +  img:PGtkImage;
    +  btn:PGtkButton absolute Result;
     begin
       Result := PGtkWidget(TGtkButton.new);
    +
    +  btn^.set_use_underline(true);
    +
       FMargin := -1;
       FLayout := GTK_POS_LEFT;
       FSpacing := 2; // default gtk3 spacing is 2
     end;
     
    +destructor TGtk3Button.Destroy;
    +begin
    +  SetImage(nil);
    +  inherited Destroy;
    +end;
    +
     function TGtk3Button.IsWidgetOk: Boolean;
     begin
       Result := (FWidget <> nil) and Gtk3IsButton(FWidget);
    
    gtk3widgets-2.diff (5,789 bytes)
  • laztest105.zip (5,568 bytes)
  • gtk3widgets-3.diff (7,529 bytes)
    Index: gtk3widgets.pas
    ===================================================================
    --- gtk3widgets.pas	(revision 62145)
    +++ gtk3widgets.pas	(working copy)
    @@ -24,8 +24,9 @@
     uses
       Classes, SysUtils, types, math,
       // LCL
    -  Controls, StdCtrls, ExtCtrls, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
    +  Controls, StdCtrls, ExtCtrls, Buttons, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
       Spin, CheckLst, PairSplitter, LCLType, LCLProc, LMessages, LCLMessageGlue, LCLIntf,
    +  graphtype,
       // GTK3
       LazGtk3, LazGdk3, LazGObject2, LazGLib2, LazCairo1, LazPango1, LazGdkPixbuf2,
       gtk3objects, gtk3procs, gtk3private, Gtk3CellRenderer;
    @@ -355,6 +356,7 @@
         function getText: String; override;
         function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
         procedure DestroyWidget; override;
    +    function getClientOffset:TPoint;override;
       public
         function getClientRect: TRect; override;
       end;
    @@ -469,7 +471,12 @@
       { TGtk3ToolBar }
     
       TGtk3ToolBar = class(TGtk3Container)
    -  protected
    +  private
    +    fBmpList:TList;
    +    procedure ButtonClicked(data: gPointer); cdecl;
    +    procedure ClearGlyphs;
    +  public
    +    destructor Destroy;override;
         function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
       end;
     
    @@ -657,6 +664,7 @@
         FMargin: Integer;
         FLayout: Integer;
         FSpacing: Integer;
    +    FImage: TBitmap;
         function getLayout: Integer;
         function getMargin: Integer;
         procedure SetLayout(AValue: Integer);
    @@ -663,15 +671,18 @@
         procedure SetMargin(AValue: Integer);
         procedure SetSpacing(AValue: Integer);
       protected
    +    procedure SetImage(AImage:TBitmap);
         function getText: String; override;
         procedure setText(const AValue: String); override;
         function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
       public
    +    destructor Destroy;override;
         function IsWidgetOk: Boolean; override;
         procedure SetDefault(const ADefault: Boolean);
         property Layout: Integer read getLayout write SetLayout;
         property Margin: Integer read getMargin write SetMargin;
         property Spacing: Integer read FSpacing write SetSpacing;
    +    property Image:TBitmap read fImage write SetImage;
       end;
     
       { TGtk3ToggleButton }
    @@ -817,7 +828,7 @@
     
     implementation
     
    -uses gtk3int;
    +uses gtk3int,imglist;
     
     const
       GDK_DEFAULT_EVENTS_MASK: TGdkEventMask =
    @@ -3926,7 +3937,7 @@
       AClass: PGTypeClass;
     begin
       inherited InitializeWidget;
    -  //TODO: move hookers check variable code into Gtk3WidgetSet.
    +  //TODO: move hook check variable code into Gtk3WidgetSet.
       if not AProgressClassHookInitialized then
       begin
         AProgressClassHookInitialized := True;
    @@ -3949,14 +3960,110 @@
     
     { TGtk3ToolBar }
     
    +procedure TGtk3ToolBar.ClearGlyphs;
    +var i:integer;
    +begin
    +  if Assigned(fBmpList) then
    +  for i:=fBmpList.Count-1 downto 0 do
    +    TObject(fBmpList[i]).Free;
    +end;
    +
    +destructor TGtk3ToolBar.Destroy;
    +begin
    +  ClearGlyphs;
    +  fBmpList.Free;
    +  inherited Destroy;
    +end;
    +
    +procedure TGtk3ToolBar.ButtonClicked(data: gPointer);cdecl;
    +begin
    +  if TObject(data) is TToolButton then
    +  TToolButton(data).Click;
    +end;
    +
     function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
     var
    +  i:integer;
       AToolBar: TToolBar;
    +  btn:TToolButton;
    +  gtb:PGtkToolItem;
    +  wmenu,wicon:PGtkWidget;
    +  pb:PGdkPixBuf;
    +  bmp:TBitmap;
    +  resolution:TCustomImageListResolution;
    +  raw:TRawImage;
    +  bs:string;
     begin
       AToolBar := TToolBar(LCLObject);
       FHasPaint := False;
       FWidgetType := [wtWidget, wtContainer];
       Result:=PGtkWidget(TGtkToolbar.new);
    +
    +  if not Assigned(fBmpList) then
    +    fBmpList:=TList.Create;
    +
    +  ClearGlyphs;
    +
    +  // allocate appropriate number of tool items
    +  for i:=0 to AToolbar.ButtonCount-1 do
    +  begin
    +    btn:=AToolBar.Buttons[i];
    +    bs:=StringReplace(btn.Caption,'&','_',[rfReplaceAll]);
    +    wicon:=nil;
    +    if btn is TToolButton then
    +    begin
    +      if (btn.ImageIndex>=0) and
    +          assigned(AToolbar.Images) and
    +          not (btn.Style in [tbsSeparator,tbsDivider]) then
    +      begin
    +        if Assigned(AToolBar.Images) and (btn.ImageIndex>=0) then
    +        begin
    +          bmp:=TBitmap.Create; { this carries gdk pixmap }
    +          resolution:=Atoolbar.Images.Resolution[Atoolbar.Images.Width];
    +          resolution.GetRawImage(btn.ImageIndex,raw);
    +          { convince the bitmap it has actually another format }
    +          bmp.BeginUpdate();
    +          raw.Description.Init_BPP32_R8G8B8A8_BIO_TTB(resolution.Width,resolution.Height);
    +          bmp.LoadFromRawImage(raw,false);
    +          bmp.EndUpdate();
    +          pb:=TGtk3Image(bmp.Handle).Handle;
    +          wicon := TGtkImage.new_from_pixbuf(pb);
    +          fBmpList.Add(bmp);
    +        end
    +        else
    +          wicon := nil;
    +      end;
    +
    +      case btn.Style of
    +  	  tbsSeparator:
    +    	  gtb:=TGtkSeparatorToolItem.new();
    +      tbsDropDown:
    +        begin
    +        	gtb:=TGtkMenuToolButton.new(wicon,PgChar(bs));
    +          if Assigned(btn.DropdownMenu) then
    +          begin
    +          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
    +          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
    +          end;
    +        end;
    +      tbsCheck:
    +        begin
    +          gtb:=TGtkToggleToolButton.new();
    +          PGtkToolButton(gtb)^.set_label(PgChar(bs));
    +        end
    +  	  else
    +    	  gtb:=TGtkToolButton.new(wicon,PgChar(bs));
    + 		  end;
    +      gtb^.set_tooltip_text(PgChar(btn.Hint));
    +      PgtkToolButton(gtb)^.set_use_underline(true);
    +      PGtkToolBar(Result)^.add(gtb);
    +
    +      if not (btn.Style in [tbsSeparator,tbsDivider]) then
    +      g_signal_connect_data(gtb,'clicked',
    +        TGCallback(@TGtk3Toolbar.ButtonClicked), btn, nil, 0);
    +    end;
    +  end;
    +
     end;
     
     { TGtk3Page }
    @@ -3979,6 +4086,7 @@
     begin
       FWidgetType := FWidgetType + [wtContainer];
       FPageLabel:= TGtkLabel.new(PChar(Params.Caption));
    +  Self.FHasPaint:=true;
       // ref it to save it in case TabVisble is set to false
       FPageLabel^.ref;
       Result := TGtkHBox.new(GTK_ORIENTATION_HORIZONTAL, 0);
    @@ -3995,6 +4103,20 @@
       FPageLabel^.unref;
     end;
     
    +function TGtk3Page.getClientOffset: TPoint;
    +var
    +  Allocation: TGtkAllocation;
    +  R: TRect;
    +begin
    +  Self.Widget^.get_allocation(@Allocation);
    +  Result.X := -Allocation.X;
    +  Result.Y := -Allocation.Y;
    +
    +  R := getClientBounds;
    +  Result := Point(Result.x + R.Left, Result.y + R.Top);
    +end;
    +
    +
     function TGtk3Page.getClientRect: TRect;
     var
       AParent: PGtkWidget;
    @@ -6252,6 +6374,13 @@
       end;
     end;
     
    +procedure TGtk3Button.SetImage(AImage: TBitmap);
    +begin
    +  if Assigned(fImage) then
    +    fImage.free;
    +  fImage:=AImage;
    +end;
    +
     function TGtk3Button.getText: String;
     begin
       if IsWidgetOK then
    @@ -6263,17 +6392,31 @@
     procedure TGtk3Button.setText(const AValue: String);
     begin
       if IsWidgetOk then
    -    PGtkButton(FWidget)^.set_label(PgChar(AValue));
    +  begin
    +    PGtkButton(FWidget)^.set_label(PgChar(StringReplace(AValue,'&','_',[rfReplaceAll])));
    +  end;
     end;
     
     function TGtk3Button.CreateWidget(const Params: TCreateParams): PGtkWidget;
    +var
    +  img:PGtkImage;
    +  btn:PGtkButton absolute Result;
     begin
       Result := PGtkWidget(TGtkButton.new);
    +
    +  btn^.set_use_underline(true);
    +
       FMargin := -1;
       FLayout := GTK_POS_LEFT;
       FSpacing := 2; // default gtk3 spacing is 2
     end;
     
    +destructor TGtk3Button.Destroy;
    +begin
    +  SetImage(nil);
    +  inherited Destroy;
    +end;
    +
     function TGtk3Button.IsWidgetOk: Boolean;
     begin
       Result := (FWidget <> nil) and Gtk3IsButton(FWidget);
    
    gtk3widgets-3.diff (7,529 bytes)

Relationships

related to 0035668 new lcl: Feature like Widgetset-native toolbar 
related to 0035699 closedZeljan Rikalo gtk3: Implement proper LogFont getting from pango layount 

Activities

Anton Kavalenka

2019-06-02 14:22

reporter  

gtk3widgets.diff (1,674 bytes)
Index: gtk3widgets.pas
===================================================================
--- gtk3widgets.pas	(revision 61310)
+++ gtk3widgets.pas	(working copy)
@@ -1667,6 +1667,7 @@
   Result:=TGtkPaned.new(ornt[TPairSplitter(Self.LCLObject).SplitterType]);
 end;
 
+
 { TGtk3Widget }
 
 function TGtk3Widget.GtkEventMouseEnterLeave(Sender: PGtkWidget; Event: PGdkEvent): Boolean;
@@ -3951,14 +3952,50 @@
 
 function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
 var
+  i:integer;
   AToolBar: TToolBar;
+  btn:TToolButton;
+  gtb:PGtkToolItem;
+  wmenu:PGtkWIdget;
 begin
   AToolBar := TToolBar(LCLObject);
   FHasPaint := False;
   FWidgetType := [wtWidget, wtContainer];
   Result:=PGtkWidget(TGtkToolbar.new);
+
+  // allocate appropriate number of tool items
+  for i:=0 to AToolbar.ButtonCount-1 do
+  begin
+    btn:=AToolBar.Buttons[i];
+    if btn is TToolButton then
+    begin
+      case btn.Style of
+  	  tbsSeparator:
+    	  gtb:=TGtkSeparatorToolItem.new();
+      tbsDropDown:
+        begin
+        	gtb:=TGtkMenuToolButton.new(nil,PgChar(btn.Caption));
+          if Assigned(btn.DropdownMenu) then
+          begin
+          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
+          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
+          end;
+        end;
+      tbsCheck:
+        begin
+          gtb:=TGtkToggleToolButton.new();
+          PGtkToolButton(gtb)^.set_label(PgChar(btn.Caption));
+        end
+  	  else
+    	  gtb:=TGtkToolButton.new(nil,PgChar(btn.Caption));
+ 		  end;
+      PGtkToolBar(Result)^.add(gtb);
+    end;
+  end;
+
 end;
 
+
 { TGtk3Page }
 
 procedure TGtk3Page.setText(AValue: String);
gtk3widgets.diff (1,674 bytes)

Anton Kavalenka

2019-06-02 14:32

reporter  

Anton Kavalenka

2019-10-19 16:55

reporter   ~0118701

Make GTK3 toolbar click-responsible and show button icons.

gtk3widgets-2.diff (5,789 bytes)
Index: gtk3widgets.pas
===================================================================
--- gtk3widgets.pas	(revision 62087)
+++ gtk3widgets.pas	(working copy)
@@ -24,7 +24,7 @@
 uses
   Classes, SysUtils, types, math,
   // LCL
-  Controls, StdCtrls, ExtCtrls, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
+  Controls, StdCtrls, ExtCtrls, Buttons, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
   Spin, CheckLst, PairSplitter, LCLType, LCLProc, LMessages, LCLMessageGlue, LCLIntf,
   // GTK3
   LazGtk3, LazGdk3, LazGObject2, LazGLib2, LazCairo1, LazPango1, LazGdkPixbuf2,
@@ -469,7 +469,11 @@
   { TGtk3ToolBar }
 
   TGtk3ToolBar = class(TGtk3Container)
+  private
+    fBmpList:TList;
+    procedure ClearGlyphs;
   protected
+    destructor Destroy;override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
   end;
 
@@ -657,6 +661,7 @@
     FMargin: Integer;
     FLayout: Integer;
     FSpacing: Integer;
+    FImage: TBitmap;
     function getLayout: Integer;
     function getMargin: Integer;
     procedure SetLayout(AValue: Integer);
@@ -663,15 +668,18 @@
     procedure SetMargin(AValue: Integer);
     procedure SetSpacing(AValue: Integer);
   protected
+    procedure SetImage(AImage:TBitmap);
     function getText: String; override;
     procedure setText(const AValue: String); override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
   public
+    destructor Destroy;override;
     function IsWidgetOk: Boolean; override;
     procedure SetDefault(const ADefault: Boolean);
     property Layout: Integer read getLayout write SetLayout;
     property Margin: Integer read getMargin write SetMargin;
     property Spacing: Integer read FSpacing write SetSpacing;
+    property Image:TBitmap read fImage write SetImage;
   end;
 
   { TGtk3ToggleButton }
@@ -817,7 +825,7 @@
 
 implementation
 
-uses gtk3int;
+uses gtk3int,imglist;
 
 const
   GDK_DEFAULT_EVENTS_MASK: TGdkEventMask =
@@ -3949,14 +3957,108 @@
 
 { TGtk3ToolBar }
 
+procedure TGtk3ToolBar.ClearGlyphs;
+var i:integer;
+begin
+  if Assigned(fBmpList) then
+  for i:=fBmpList.Count-1 downto 0 do
+    TObject(fBmpList[i]).Free;
+end;
+
+destructor TGtk3ToolBar.Destroy;
+begin
+  ClearGlyphs;
+  fBmpList.Free;
+  inherited Destroy;
+end;
+
+
+procedure tool_button_clicked(widget: PGtkWidget; data: gPointer); cdecl;
+begin
+  if TObject(data) is TToolButton then
+  TToolButton(data).Click;
+end;
+
+
 function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
 var
+  i:integer;
   AToolBar: TToolBar;
+  btn:TToolButton;
+  gtb:PGtkToolItem;
+  wmenu,wicon:PGtkWidget;
+  pb:PGdkPixBuf;
+  bmp:TBitmap;
+  resolution:TCustomImageListResolution;
+  bs:string;
 begin
   AToolBar := TToolBar(LCLObject);
   FHasPaint := False;
   FWidgetType := [wtWidget, wtContainer];
   Result:=PGtkWidget(TGtkToolbar.new);
+
+  if not Assigned(fBmpList) then
+    fBmpList:=TList.Create;
+
+  ClearGlyphs;
+
+  // allocate appropriate number of tool items
+  for i:=0 to AToolbar.ButtonCount-1 do
+  begin
+    btn:=AToolBar.Buttons[i];
+    bs:=StringReplace(btn.Caption,'&','_',[rfReplaceAll]);
+    wicon:=nil;
+    if btn is TToolButton then
+    begin
+      if (btn.ImageIndex>=0) and
+          assigned(AToolbar.Images) and
+          not (btn.Style in [tbsSeparator,tbsDivider]) then
+      begin
+        bmp:=TBitmap.Create; { this carries gdk pixmap }
+        resolution:=Atoolbar.Images.Resolution[16];
+        resolution.GetBitmap(btn.ImageIndex,bmp);
+        if not bmp.Empty then
+        begin
+          //wicon := TGtkImage.new;
+          pb:=TGtk3Image(bmp.Handle).Handle;
+          wicon := gtk_image_new_from_pixbuf(pb);
+        end
+        else
+          wicon := nil;
+        fBmpList.Add(bmp);
+      end;
+
+      case btn.Style of
+  	  tbsSeparator:
+    	  gtb:=TGtkSeparatorToolItem.new();
+      tbsDropDown:
+        begin
+        	gtb:=TGtkMenuToolButton.new(wicon,PgChar(bs));
+          if Assigned(btn.DropdownMenu) then
+          begin
+          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
+          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
+          end;
+        end;
+      tbsCheck:
+        begin
+          gtb:=TGtkToggleToolButton.new();
+          PGtkToolButton(gtb)^.set_label(PgChar(bs));
+        end
+  	  else
+    	  gtb:=TGtkToolButton.new(wicon,PgChar(bs));
+ 		  end;
+      gtb^.set_tooltip_text(PgChar(btn.Hint));
+      PgtkToolButton(gtb)^.set_use_underline(true);
+      PGtkToolBar(Result)^.add(gtb);
+
+      if not (btn.Style in [tbsSeparator,tbsDivider]) then
+      g_signal_connect_data(gtb,'clicked',
+        TGCallback(@tool_button_clicked), btn, nil, 0);
+
+    end;
+  end;
+
 end;
 
 { TGtk3Page }
@@ -6245,6 +6347,13 @@
   end;
 end;
 
+procedure TGtk3Button.SetImage(AImage: TBitmap);
+begin
+  if Assigned(fImage) then
+    fImage.free;
+  fImage:=AImage;
+end;
+
 function TGtk3Button.getText: String;
 begin
   if IsWidgetOK then
@@ -6256,17 +6365,31 @@
 procedure TGtk3Button.setText(const AValue: String);
 begin
   if IsWidgetOk then
-    PGtkButton(FWidget)^.set_label(PgChar(AValue));
+  begin
+    PGtkButton(FWidget)^.set_label(PgChar(StringReplace(AValue,'&','_',[rfReplaceAll])));
+  end;
 end;
 
 function TGtk3Button.CreateWidget(const Params: TCreateParams): PGtkWidget;
+var
+  img:PGtkImage;
+  btn:PGtkButton absolute Result;
 begin
   Result := PGtkWidget(TGtkButton.new);
+
+  btn^.set_use_underline(true);
+
   FMargin := -1;
   FLayout := GTK_POS_LEFT;
   FSpacing := 2; // default gtk3 spacing is 2
 end;
 
+destructor TGtk3Button.Destroy;
+begin
+  SetImage(nil);
+  inherited Destroy;
+end;
+
 function TGtk3Button.IsWidgetOk: Boolean;
 begin
   Result := (FWidget <> nil) and Gtk3IsButton(FWidget);
gtk3widgets-2.diff (5,789 bytes)
laztest105.zip (5,568 bytes)

Zeljan Rikalo

2019-10-19 18:35

developer   ~0118708

@Anton, only gtk3widget-2.diff should be applied or both ?

Anton Kavalenka

2019-10-19 20:04

reporter   ~0118713

only 2-nd
mantis automatically adds numeric suffix when uploaded files have same names

Zeljan Rikalo

2019-10-19 20:06

developer   ~0118714

Please look my comment about widgetset native implementation of TToolBar at related issue.

Alexey Tor.

2019-10-19 21:09

reporter   ~0118717

Tested this patch with the 0035699 bug- it helped, pls apply.

Anton Kavalenka

2019-10-29 16:04

reporter   ~0118915

Make toolbar buttons appear in proper colours (swap BGRA -> RGBA)
And accidentally make TGtk3Page paint TGraphicControl descendants inside

gtk3widgets-3.diff (7,529 bytes)
Index: gtk3widgets.pas
===================================================================
--- gtk3widgets.pas	(revision 62145)
+++ gtk3widgets.pas	(working copy)
@@ -24,8 +24,9 @@
 uses
   Classes, SysUtils, types, math,
   // LCL
-  Controls, StdCtrls, ExtCtrls, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
+  Controls, StdCtrls, ExtCtrls, Buttons, ComCtrls, Graphics, Dialogs, Forms, Menus, ExtDlgs,
   Spin, CheckLst, PairSplitter, LCLType, LCLProc, LMessages, LCLMessageGlue, LCLIntf,
+  graphtype,
   // GTK3
   LazGtk3, LazGdk3, LazGObject2, LazGLib2, LazCairo1, LazPango1, LazGdkPixbuf2,
   gtk3objects, gtk3procs, gtk3private, Gtk3CellRenderer;
@@ -355,6 +356,7 @@
     function getText: String; override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
     procedure DestroyWidget; override;
+    function getClientOffset:TPoint;override;
   public
     function getClientRect: TRect; override;
   end;
@@ -469,7 +471,12 @@
   { TGtk3ToolBar }
 
   TGtk3ToolBar = class(TGtk3Container)
-  protected
+  private
+    fBmpList:TList;
+    procedure ButtonClicked(data: gPointer); cdecl;
+    procedure ClearGlyphs;
+  public
+    destructor Destroy;override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
   end;
 
@@ -657,6 +664,7 @@
     FMargin: Integer;
     FLayout: Integer;
     FSpacing: Integer;
+    FImage: TBitmap;
     function getLayout: Integer;
     function getMargin: Integer;
     procedure SetLayout(AValue: Integer);
@@ -663,15 +671,18 @@
     procedure SetMargin(AValue: Integer);
     procedure SetSpacing(AValue: Integer);
   protected
+    procedure SetImage(AImage:TBitmap);
     function getText: String; override;
     procedure setText(const AValue: String); override;
     function CreateWidget(const Params: TCreateParams):PGtkWidget; override;
   public
+    destructor Destroy;override;
     function IsWidgetOk: Boolean; override;
     procedure SetDefault(const ADefault: Boolean);
     property Layout: Integer read getLayout write SetLayout;
     property Margin: Integer read getMargin write SetMargin;
     property Spacing: Integer read FSpacing write SetSpacing;
+    property Image:TBitmap read fImage write SetImage;
   end;
 
   { TGtk3ToggleButton }
@@ -817,7 +828,7 @@
 
 implementation
 
-uses gtk3int;
+uses gtk3int,imglist;
 
 const
   GDK_DEFAULT_EVENTS_MASK: TGdkEventMask =
@@ -3926,7 +3937,7 @@
   AClass: PGTypeClass;
 begin
   inherited InitializeWidget;
-  //TODO: move hookers check variable code into Gtk3WidgetSet.
+  //TODO: move hook check variable code into Gtk3WidgetSet.
   if not AProgressClassHookInitialized then
   begin
     AProgressClassHookInitialized := True;
@@ -3949,14 +3960,110 @@
 
 { TGtk3ToolBar }
 
+procedure TGtk3ToolBar.ClearGlyphs;
+var i:integer;
+begin
+  if Assigned(fBmpList) then
+  for i:=fBmpList.Count-1 downto 0 do
+    TObject(fBmpList[i]).Free;
+end;
+
+destructor TGtk3ToolBar.Destroy;
+begin
+  ClearGlyphs;
+  fBmpList.Free;
+  inherited Destroy;
+end;
+
+procedure TGtk3ToolBar.ButtonClicked(data: gPointer);cdecl;
+begin
+  if TObject(data) is TToolButton then
+  TToolButton(data).Click;
+end;
+
 function TGtk3ToolBar.CreateWidget(const Params: TCreateParams): PGtkWidget;
 var
+  i:integer;
   AToolBar: TToolBar;
+  btn:TToolButton;
+  gtb:PGtkToolItem;
+  wmenu,wicon:PGtkWidget;
+  pb:PGdkPixBuf;
+  bmp:TBitmap;
+  resolution:TCustomImageListResolution;
+  raw:TRawImage;
+  bs:string;
 begin
   AToolBar := TToolBar(LCLObject);
   FHasPaint := False;
   FWidgetType := [wtWidget, wtContainer];
   Result:=PGtkWidget(TGtkToolbar.new);
+
+  if not Assigned(fBmpList) then
+    fBmpList:=TList.Create;
+
+  ClearGlyphs;
+
+  // allocate appropriate number of tool items
+  for i:=0 to AToolbar.ButtonCount-1 do
+  begin
+    btn:=AToolBar.Buttons[i];
+    bs:=StringReplace(btn.Caption,'&','_',[rfReplaceAll]);
+    wicon:=nil;
+    if btn is TToolButton then
+    begin
+      if (btn.ImageIndex>=0) and
+          assigned(AToolbar.Images) and
+          not (btn.Style in [tbsSeparator,tbsDivider]) then
+      begin
+        if Assigned(AToolBar.Images) and (btn.ImageIndex>=0) then
+        begin
+          bmp:=TBitmap.Create; { this carries gdk pixmap }
+          resolution:=Atoolbar.Images.Resolution[Atoolbar.Images.Width];
+          resolution.GetRawImage(btn.ImageIndex,raw);
+          { convince the bitmap it has actually another format }
+          bmp.BeginUpdate();
+          raw.Description.Init_BPP32_R8G8B8A8_BIO_TTB(resolution.Width,resolution.Height);
+          bmp.LoadFromRawImage(raw,false);
+          bmp.EndUpdate();
+          pb:=TGtk3Image(bmp.Handle).Handle;
+          wicon := TGtkImage.new_from_pixbuf(pb);
+          fBmpList.Add(bmp);
+        end
+        else
+          wicon := nil;
+      end;
+
+      case btn.Style of
+  	  tbsSeparator:
+    	  gtb:=TGtkSeparatorToolItem.new();
+      tbsDropDown:
+        begin
+        	gtb:=TGtkMenuToolButton.new(wicon,PgChar(bs));
+          if Assigned(btn.DropdownMenu) then
+          begin
+          	wmenu:=TGtk3Menu(btn.DropdownMenu.Handle).Widget;
+          	PGtkMenuToolButton(gtb)^.set_menu(wmenu);
+          end;
+        end;
+      tbsCheck:
+        begin
+          gtb:=TGtkToggleToolButton.new();
+          PGtkToolButton(gtb)^.set_label(PgChar(bs));
+        end
+  	  else
+    	  gtb:=TGtkToolButton.new(wicon,PgChar(bs));
+ 		  end;
+      gtb^.set_tooltip_text(PgChar(btn.Hint));
+      PgtkToolButton(gtb)^.set_use_underline(true);
+      PGtkToolBar(Result)^.add(gtb);
+
+      if not (btn.Style in [tbsSeparator,tbsDivider]) then
+      g_signal_connect_data(gtb,'clicked',
+        TGCallback(@TGtk3Toolbar.ButtonClicked), btn, nil, 0);
+    end;
+  end;
+
 end;
 
 { TGtk3Page }
@@ -3979,6 +4086,7 @@
 begin
   FWidgetType := FWidgetType + [wtContainer];
   FPageLabel:= TGtkLabel.new(PChar(Params.Caption));
+  Self.FHasPaint:=true;
   // ref it to save it in case TabVisble is set to false
   FPageLabel^.ref;
   Result := TGtkHBox.new(GTK_ORIENTATION_HORIZONTAL, 0);
@@ -3995,6 +4103,20 @@
   FPageLabel^.unref;
 end;
 
+function TGtk3Page.getClientOffset: TPoint;
+var
+  Allocation: TGtkAllocation;
+  R: TRect;
+begin
+  Self.Widget^.get_allocation(@Allocation);
+  Result.X := -Allocation.X;
+  Result.Y := -Allocation.Y;
+
+  R := getClientBounds;
+  Result := Point(Result.x + R.Left, Result.y + R.Top);
+end;
+
+
 function TGtk3Page.getClientRect: TRect;
 var
   AParent: PGtkWidget;
@@ -6252,6 +6374,13 @@
   end;
 end;
 
+procedure TGtk3Button.SetImage(AImage: TBitmap);
+begin
+  if Assigned(fImage) then
+    fImage.free;
+  fImage:=AImage;
+end;
+
 function TGtk3Button.getText: String;
 begin
   if IsWidgetOK then
@@ -6263,17 +6392,31 @@
 procedure TGtk3Button.setText(const AValue: String);
 begin
   if IsWidgetOk then
-    PGtkButton(FWidget)^.set_label(PgChar(AValue));
+  begin
+    PGtkButton(FWidget)^.set_label(PgChar(StringReplace(AValue,'&','_',[rfReplaceAll])));
+  end;
 end;
 
 function TGtk3Button.CreateWidget(const Params: TCreateParams): PGtkWidget;
+var
+  img:PGtkImage;
+  btn:PGtkButton absolute Result;
 begin
   Result := PGtkWidget(TGtkButton.new);
+
+  btn^.set_use_underline(true);
+
   FMargin := -1;
   FLayout := GTK_POS_LEFT;
   FSpacing := 2; // default gtk3 spacing is 2
 end;
 
+destructor TGtk3Button.Destroy;
+begin
+  SetImage(nil);
+  inherited Destroy;
+end;
+
 function TGtk3Button.IsWidgetOk: Boolean;
 begin
   Result := (FWidget <> nil) and Gtk3IsButton(FWidget);
gtk3widgets-3.diff (7,529 bytes)

Zeljan Rikalo

2019-11-03 16:09

developer   ~0119014

Please test and close if ok. Thanks for the patch.

Issue History

Date Modified Username Field Change
2019-06-02 14:22 Anton Kavalenka New Issue
2019-06-02 14:22 Anton Kavalenka File Added: gtk3widgets.diff
2019-06-02 14:32 Anton Kavalenka File Added: Здымак экрана, 2019-06-02 15-20-01.png
2019-06-03 16:11 Dmitry Boyarintsev Relationship added related to 0035668
2019-10-19 16:55 Anton Kavalenka File Added: gtk3widgets-2.diff
2019-10-19 16:55 Anton Kavalenka File Added: laztest105.zip
2019-10-19 16:55 Anton Kavalenka File Added: Здымак экрана, 2019-10-19 17-54-53.png
2019-10-19 16:55 Anton Kavalenka Note Added: 0118701
2019-10-19 18:35 Zeljan Rikalo Assigned To => Zeljan Rikalo
2019-10-19 18:35 Zeljan Rikalo Status new => assigned
2019-10-19 18:35 Zeljan Rikalo Note Added: 0118708
2019-10-19 20:04 Anton Kavalenka Note Added: 0118713
2019-10-19 20:06 Zeljan Rikalo Status assigned => feedback
2019-10-19 20:06 Zeljan Rikalo LazTarget => -
2019-10-19 20:06 Zeljan Rikalo Note Added: 0118714
2019-10-19 21:09 Alexey Tor. Note Added: 0118717
2019-10-24 23:00 Juha Manninen Relationship added related to 0035699
2019-10-29 16:04 Anton Kavalenka File Added: gtk3widgets-3.diff
2019-10-29 16:04 Anton Kavalenka File Added: Здымак экрана, 2019-10-29 18-03-01.png
2019-10-29 16:04 Anton Kavalenka Note Added: 0118915
2019-10-29 16:04 Anton Kavalenka Status feedback => assigned
2019-11-03 16:09 Zeljan Rikalo Status assigned => resolved
2019-11-03 16:09 Zeljan Rikalo Resolution open => fixed
2019-11-03 16:09 Zeljan Rikalo Fixed in Revision => 62164
2019-11-03 16:09 Zeljan Rikalo Widgetset GTK 3 => GTK 3
2019-11-03 16:09 Zeljan Rikalo Note Added: 0119014
2019-11-04 11:33 Anton Kavalenka Status resolved => closed