Wednesday, March 26, 2014

Getting Started with GtkAda - Drawing

Many widgets, like buttons, do all their drawing themselves. You just tell them the label you want to see, and they figure out what font to use, draw the button outline and focus rectangle, etc. Sometimes, it is necessary to do some custom drawing. In that case, a Gtk_Drawing_Area might be the right widget to use. It offers a canvas on which you can draw by connecting to the "draw" signal.

The contents of a widget often need to be partially or fully redrawn, e.g. when another window is moved and uncovers part of the widget, or when tie window containing it is resized. It is also possible to explicitly cause part or all of the widget to be redrawn, by calling Gtk.Widget.Queue_Draw() or its variants. GtkAda takes care of most of the details by providing a ready-to-use cairo context to the ::draw signal handler.

The following example shows a ::draw signal handler. It is a bit more complicated than the previous examples, since it also demonstrates input event handling by means of ::button-press and ::motion-notify handlers.

src/draw.adb
with Gtk.Window;       use Gtk.Window;
with Gtk.Frame;        use Gtk.Frame;
with Gtk.Button;       use Gtk.Button;
with Gtk.Drawing_Area; use Gtk.Drawing_Area;
with Gtkada.Handlers;  use Gtkada.Handlers;
with Gdk.Event;        use Gdk.Event;
with draw_cb; use draw_cb;

with Gtk.Main;
with Gtk.Enums;

procedure Draw is
   Win   : Gtk_Window;
   Frame : Gtk_Frame;
   Da    : Gtk_Drawing_Area;
begin
   --  Initialize GtkAda.
   Gtk.Main.Init;

   -- create a top level window
   Gtk_New (Win);
   Win.Set_Title ("Drawing Area");
   -- set the border width of the window
   Win.Set_Border_Width (8);
   -- connect the "destroy" signal
   Win.On_Destroy (main_quit'Access);

   -- create a frame
   Gtk_New (Frame);
   Frame.Set_Shadow_Type (Gtk.Enums.Shadow_In);
   Win.Add (Frame);

   Gtk_New (Da);
   -- set a minimum size
   Da.Set_Size_Request (100, 100);
   Frame.Add (Da);

   -- Signals used to handle the backing surface
   Da.On_Draw (draw_cb.draw_cb'Access);
   Da.On_Configure_Event (configure_event_cb'Access);
   -- Event signals
   Da.On_Motion_Notify_Event (motion_notify_event_cb'Access);
   Da.On_Button_Press_Event (button_press_event_cb'Access);

   -- Ask to receive events the drawing area doesn't normally
   -- subscribe to. In particular, we need to ask for the
   -- button press and motion notify events that want to handle.
   Da.Set_Events (Da.Get_Events or Button_Press_Mask or Pointer_Motion_Mask);

   -- Now that we are done packing our widgets, we show them all
   -- in one go, by calling Win.Show_All.
   -- This call recursively calls Show on all widgets
   -- that are contained in the window, directly or indirectly.
   Win.Show_All;

   -- All GTK applications must have a Gtk.Main.Main. Control ends here
   -- and waits for an event to occur (like a key press or a mouse event),
   -- until Gtk.Main.Main_Quit is called.
   Gtk.Main.Main;
end Draw;
src/draw_cb.ads:
with Gtk.Widget;  use Gtk.Widget;
with Gtk.Button;  use Gtk.Button;
with Glib.Object;

with Gdk.Event;
with Cairo;

package draw_cb is
   procedure main_quit (Self : access Gtk_Widget_Record'Class);

   function draw_cb
     (Self : access Gtk_Widget_Record'Class;
      Cr   : Cairo.Cairo_Context)
      return Boolean;

   -- Create a new surface of the appropriate size to store our scribbles
   function configure_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Configure)
      return  Boolean;

   -- Handle motion events by continuing to draw if button 1 is
   -- still held down. The ::motion-notify signal handler receives
   -- a GdkEventMotion struct which contains this information.
   function motion_notify_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Motion)
      return  Boolean;

   -- Handle button press events by either drawing a rectangle
   -- or clearing the surface, depending on which button was pressed.
   -- The ::button-press signal handler receives a GdkEventButton
   -- struct which contains this information.
   function button_press_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Button)
      return  Boolean;
end draw_cb;
src/draw_cb.adb:
with Glib;       use Glib;

with Gdk.Types;  use Gdk.Types;
with Gdk.Window;
with Gtk.Main;

package body draw_cb is

   -- Button defintions from gtk-3.0/gdk/gdkevents.h
   -- The primary button. This is typically the left button, or the
   -- right button in a left-handed setup.
   Gdk_Button_Primary : constant := 1;
   --  The secondary button. This is typically the right mouse button, or the
   -- left button in a left-handed setup.
   Gdk_Button_Secondary : constant := 3;

   surface : Cairo.Cairo_Surface;
   use type Cairo.Cairo_Surface;

   procedure main_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      if surface /= Cairo.Null_Surface then
         Cairo.Surface_Destroy (surface);
      end if;

      Gtk.Main.Main_Quit;
   end main_quit;

   procedure Clear_Surface is
      Cr : Cairo.Cairo_Context;
   begin
      Cr := Cairo.Create (surface);
      Cairo.Set_Source_Rgb (Cr, 1.0, 1.0, 1.0);
      Cairo.Paint (Cr);
      Cairo.Destroy (Cr);
   end Clear_Surface;

   -- Redraw the screen from the surface. Note that the ::draw
   -- signal receives a ready-to-be-used cairo_t that is already
   -- clipped to only draw the exposed areas of the widget
   function draw_cb
     (Self : access Gtk_Widget_Record'Class;
      Cr   : Cairo.Cairo_Context)
      return Boolean
   is
   begin
      Cairo.Set_Source_Surface (Cr, surface, 0.0, 0.0);
      Cairo.Paint (Cr);
      return False;
   end draw_cb;

   function configure_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Configure)
      return  Boolean
   is
   begin
      if surface /= Cairo.Null_Surface then
         Cairo.Surface_Destroy (surface);
      end if;
      surface :=
         Gdk.Window.Create_Similar_Surface
           (Self.Get_Window,
            Cairo.Cairo_Content_Color,
            Self.Get_Allocated_Width,
            Self.Get_Allocated_Height);
      -- Initialize the surface to white
      Clear_Surface;

      -- We've handled the configure event, no need for further processing.
      return True;
   end configure_event_cb;

   -- Draw a rectangle on the surface at the given position
   procedure draw_brush
     (Self : access Gtk_Widget_Record'Class;
      x    : Gdouble;
      y    : Gdouble)
   is
      Cr : Cairo.Cairo_Context;
   begin

      -- Paint to the surface, where we store our state
      Cr := Cairo.Create (surface);
      Cairo.Rectangle (Cr, x - 3.0, y - 3.0, 6.0, 6.0);
      Cairo.Fill (Cr);
      Cairo.Destroy (Cr);

      -- Now invalidate the affected region of the drawing area.
      Self.Queue_Draw_Area (Gint (x - 3.0), Gint (y - 3.0), 6, 6);
   end draw_brush;

   function motion_notify_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Motion)
      return  Boolean
   is
   begin
      -- paranoia check, in case we haven't gotten a configure event
      if surface = Cairo.Null_Surface then
         return False;
      end if;

      if (Event.State and Gdk.Types.Button1_Mask) > 0 then
         draw_brush (Self, Event.X, Event.Y);
      end if;

      -- We've handled it, stop processing
      return True;
   end motion_notify_event_cb;

   function button_press_event_cb
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event_Button)
      return  Boolean
   is
   begin
      -- paranoia check, in case we haven't gotten a configure event
      if surface = Cairo.Null_Surface then
         return False;
      end if;

      if Event.Button = Gdk_Button_Primary then
         draw_brush (Self, Event.X, Event.Y);
      elsif Event.Button = Gdk_Button_Secondary then
         Clear_Surface;
         Self.Queue_Draw;
      end if;
      -- We've handled the event, stop processing
      return True;
   end button_press_event_cb;
end draw_cb;
draw.gpr:
with "gtkada";

project Draw is

   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("draw.adb");

   --  Enable Ada 2005.
   package Compiler is
      for Default_Switches ("ada") use ("-gnat05");
   end Compiler;

end Draw;
To compile:
gprbuild -P draw

This program is more complicated and it shows a problem with the current GtkAda (3.8.3) binding.

There is no button definition for GDK_BUTTON_PRIMARY and GDK_BUTTON_SECONDARY, etc., which have to be defined in package draw_cb ourselves by taking the definition from the gtk-3.0/gdk/gdkevents.h. As of 2013-03-26, the SVN source tree has been updated after I reported the bug, it now contains the button definitions in gdk-event.ads as Button_Primary, Button_Secondary and Button_MIddle, if you are using the SVN source after the day, you may want to use that definition directly.

The cairo surface variable is defined in the draw_cb package body, it has the similar effect of the C static definition that only procedures and functions in the package body could see it.

We use use type Cairo.Cairo_Surface; in the draw_cb package body, so that /= and = could be used directly on the type without exposing other Cairo definitions.

Saturday, March 22, 2014

Getting started with GtkAda - Building user interfaces

When constructing a more complicated user interface, with dozens or hundreds of widgets, doing all the setup work in Ada code is cumbersome, and making changes becomes next to impossible.

Thankfully, GtkAda supports the separation of user interface layout from your business logic, by using UI descriptions in an XML format that can be parsed by the Gtk_Builder class.

This example is exactly like the grid example, but using the Gtk_Builder class.

src/builder.adb:
with Gtk.Builder; use Gtk.Builder;
with Gtk.Window; use Gtk.Window;
with Gtk.Button; use Gtk.Button;
with Gtkada.Handlers;  use Gtkada.Handlers;

with builder_cb; use builder_cb;
with Glib; use Glib;
with Glib.Error; use Glib.Error;
with Gtk.Main;

procedure Builder is
   Win   : Gtk_Window;
   Button : Gtk_Button;
   builder : Gtk_Builder;
   ret : GUint;
   error : aliased GError;
begin
   --  Initialize GtkAda.
   Gtk.Main.Init;

   -- construct a Gtk_Builder instance and load our UI description
   Gtk_New (Builder);
   ret := Builder.Add_From_File ("builder.ui", error'Access);

   -- connect signal handlers to the constructed widgets
   Win := Gtk_Window (Builder.Get_Object ("window"));
   -- connect the "destroy" signal
   Win.On_Destroy (main_quit'Access);

   button := Gtk_Button (Builder.Get_Object ("button1"));
   button.On_Clicked (button_clicked'Access);

   button := Gtk_Button (Builder.Get_Object ("button2"));
   button.On_Clicked (button_clicked'Access);

   button := Gtk_Button (Builder.Get_Object ("quit"));
   -- connect the "clicked" signal of the button to destroy function
   Widget_Callback.Object_Connect
     (Button,
      "clicked",
      Widget_Callback.To_Marshaller (button_quit'Access),
      Win);
   -- All GTK applications must have a Gtk.Main.Main. Control ends here
   -- and waits for an event to occur (like a key press or a mouse event),
   -- until Gtk.Main.Main_Quit is called.
   Gtk.Main.Main;
end Builder;
src/builder_cb.ads:
with Gtk.Widget;  use Gtk.Widget;
with Gtk.Button; use Gtk.Button;

package builder_cb is
   procedure main_quit (Self : access Gtk_Widget_Record'Class);

   procedure button_clicked (Self : access Gtk_Button_Record'Class);
   procedure button_quit (Self : access Gtk_Widget_Record'Class);
end builder_cb;
src/builder_cb.adb:
with Ada.Text_IO; use Ada.Text_IO;
with Gtk.Main;

package body builder_cb is

   procedure main_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Gtk.Main.Main_Quit;
   end main_quit;

   procedure button_clicked (Self : access Gtk_Button_Record'Class) is
   begin
      Put_Line ("Hello World!");
   end button_clicked;

   procedure button_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Put_Line ("buttion_quit is called");
      Destroy (Self);
   end button_quit;
end builder_cb;
builder.gpr:
with "gtkada";

project Builder is

   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("builder.adb");

   -- Enable Ada 2005.
   package Compiler is
     for Default_Switches ("ada") use ("-gnat05");
   end Compiler;
end Builder
builder.ui:
<interface> 
  <object id="window" class="GtkWindow"> 
    <property name="visible">True</property> 
    <property name="title">Grid</property> 
    <property name="border-width">10</property> 
    <child> 
      <object id="grid" class="GtkGrid"> 
        <property name="visible">True</property> 
        <child> 
          <object id="button1" class="GtkButton"> 
            <property name="visible">True</property> 
            <property name="label">Button 1</property> 
          </object> 
          <packing> 
            <property name="left-attach">0</property> 
            <property name="top-attach">0</property> 
          </packing> 
        </child> 
        <child> 
          <object id="button2" class="GtkButton"> 
            <property name="visible">True</property> 
            <property name="label">Button 2</property> 
          </object> 
          <packing> 
            <property name="left-attach">1</property> 
            <property name="top-attach">0</property> 
          </packing> 
        </child> 
        <child> 
          <object id="quit" class="GtkButton"> 
            <property name="visible">True</property> 
            <property name="label">Quit</property> 
          </object> 
          <packing> 
            <property name="left-attach">0</property> 
            <property name="top-attach">1</property> 
            <property name="width">2</property> 
          </packing> 
        </child> 
      </object> 
      <packing> 
      </packing> 
    </child> 
  </object> 
</interface>
To compile:
gprbuild -P builder

Note that Gtk_Builder can also be used to construct objects that are not widgets, such as tree models, adjustments, etc. That is the reason the method we use here is called Get_Object and returns a Glib.Object.GObject instead of a Gtk_Widget. That is why we need to cast the return value of Get_Object to the corresponding type to pass the compilation.

Normally, you would pass a full path to Add_From_File to make the execution of your program independent of the current directory. A common location to install UI descriptions and similar data is /usr/share/appname.

It is also possible to embed the UI description in the source code as a string and use Add_From_String to load it. But keeping the UI description in a separate file has several advantages: It is then possible to make minor adjustments to the UI without recompiling your program, and, more importantly, graphical UI editors such as glade can load the file and allow you to create and modify your UI by point-and-click.

There is one thing in this example differ from the C version. In the C example, gtk_builder_add_from_file() is called with a NULL pointer for the error parameter, in GtkAda version, the Error is a must, otherwise, you will get constraint error:

raised CONSTRAINT_ERROR : gtk-builder.adb:151 access check failed

Friday, March 21, 2014

Getting Started with GtkAda - Packing

When creating an application, you'll want to put more than one widget inside a window. Our first helloworld example only used one widget so we could simply use Add call to "pack" the widget into the window. But when you want to put more than one widget into a window, it it becomes important to control how each widget is positioned and sized. This is where packing comes in.

GtkAda comes with a large variety of layout containers whose purpose it is to control the layout of the child widgets that are added to them. You could see the Gtk+ Layout Containers for an overview. GtkAda has all the containers and normally has the form of Gtk_<Container_Name>

The following example shows how the Gtk_Grid container lets you arrange several buttons. It follows the same directory structure in the helloworld example.

src/grid.adb:
with Gtk.Main;
with Gtk.Window;      use Gtk.Window;
with Gtk.Grid;        use Gtk.Grid;
with Gtk.Button;      use Gtk.Button;
with Gtkada.Handlers; use Gtkada.Handlers;

with grid_cb; use grid_cb;

procedure Grid is
   Win    : Gtk_Window;
   Grid   : Gtk_Grid;
   Button : Gtk_Button;
begin
   --  Initialize GtkAda.
   Gtk.Main.Init;

   -- create a top level window
   Gtk_New (Win);
   Win.Set_Title ("Grid");
   -- set the border width of the window
   Win.Set_Border_Width (10);
   -- connect the "destroy" signal
   Win.On_Destroy (main_quit'Access);

   -- Here we construct the container that is going pack our buttons
   Gtk_New (Grid);

   -- Packed the container in the Window
   Win.Add (Grid);

   -- create a button with label
   Gtk_New (Button, "Button 1");
   -- connect the click signal
   Button.On_Clicked (button_clicked'Access);

   -- Place the first button in the grid cell (0, 0), and make it fill
   -- just 1 cell horizontally and vertically (ie no spanning)
   Grid.Attach (Button, 0, 0, 1, 1);

   -- create another button with label
   Gtk_New (Button, "Button 2");
   Button.On_Clicked (button_clicked'Access);

   -- Place the second button in the grid cell (1, 0), and make it fill
   -- just 1 cell horizontally and vertically (ie no spanning)
   Grid.Attach (Button, 1, 0, 1, 1);

   -- create the quit button
   Gtk_New (Button, "Quit");
   -- connect the "clicked" signal of the button to destroy function
   Widget_Callback.Object_Connect
     (Button,
      "clicked",
      Widget_Callback.To_Marshaller (button_quit'Access),
      Win);
   -- Place the Quit button in the grid cell (0, 1), and make it
   -- span 2 columns.
   Grid.Attach (Button, 0, 1, 2, 1);

   -- Now that we are done packing our widgets, we show them all
   -- in one go, by calling Win.Show_All.
   -- This call recursively calls Show on all widgets
   -- that are contained in the window, directly or indirectly.
   Win.Show_All;

   -- All GTK applications must have a Gtk.Main.Main. Control ends here
   -- and waits for an event to occur (like a key press or a mouse event),
   -- until Gtk.Main.Main_Quit is called.
   Gtk.Main.Main;
end Grid;
src/grid_cb.ads:
with Gtk.Widget;  use Gtk.Widget;
with Gtk.Button;  use Gtk.Button;
with Glib.Object;

with Gdk.Event;

package grid_cb is
   function main_del
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event)
      return  Boolean;
   procedure main_quit (Self : access Gtk_Widget_Record'Class);
   procedure button_clicked (Self : access Gtk_Button_Record'Class);
   procedure button_quit (Self : access Gtk_Widget_Record'Class);
end grid_cb;
src/grid_cb.adb:
with Ada.Text_IO; use Ada.Text_IO;

with Gtk.Main;

package body grid_cb is
   -- If you return false in the "delete_event" signal handler,
   -- GTK will emit the "destroy" signal. Returning true means
   -- you don't want the window to be destroyed.
   --
   -- This is useful for popping up 'are you sure you want to quit?'
   -- type dialogs.
   function main_del
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event)
      return  Boolean
   is
   begin
      Put_Line ("Delete event encounter.");
      return True;
   end main_del;

   procedure main_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Gtk.Main.Main_Quit;
   end main_quit;

   procedure button_clicked (Self : access Gtk_Button_Record'Class) is
   begin
      Put_Line ("Hello clicked");
   end button_clicked;

   procedure button_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Put_Line ("buttion_quit is called");
      Destroy (Self);
   end button_quit;

end grid_cb;
grid.gpr
with "gtkada";

project Grid is

   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("grid.adb");

   --  Enable Ada 2005.
   package Compiler is
      for Default_Switches ("ada") use ("-gnat05");
   end Compiler;

end Grid
To compile the program:
gprbuild -P grid

You may notice the callback functions are exactly the same as helloworld, you could reuse it by just rename the packages or simple using the same hello_cb package and in grid.adb with and use it with hello_cb; use hello_cb;, then you don't need to change anything on the call backs.

Thursday, March 20, 2014

Getting Started with GtkAda

There is not much resources on how to program with GtkAda. GtkAda User’s Guide provides some information but not enough to start from scratch. I tried to follow the Getting Started with GTK+ but writing it with GtkAda.

I will assume you have gnat gpl 2013 and GtkAda installed. I use the svn version of GtkAda (3.8.3) in stead of the GtkAda 2013 GPL 3.4.2.

Basics

We'll start with the simplest program possible. This program will create an empty window.
The source files are under the following directory structure:
example-top-dir
  |-- obj
  |-- src
  `-- hello.gpr
src/hello.adb:
with Gtk.Main;
with Gtk.Window;      use Gtk.Window;

with Main_quit;

procedure Hello is
   Win   : Gtk_Window;
begin
   --  Initialize GtkAda.
   Gtk.Main.Init;

   -- create a top level window
   Gtk_New (Win);
   Win.Set_Title ("Window");

   -- connect the "destroy" signal
   Win.On_Destroy (main_quit'Access);

   --  Show the window
   Win.Show_All;

   --  Start the Gtk+ main loop
   Gtk.Main.Main;
end Hello
src/main_quit.adb:
with Gtk.Widget;      use Gtk.Widget;
with Gtk.Main;

procedure main_quit (Self : access Gtk_Widget_Record'Class) is
begin
   Gtk.Main.Main_Quit;
end main_quit;
The project file hello.gpr:
with "gtkada";

project Hello is

   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Main use ("hello.adb");

   --  Enable Ada 2005.
   package Compiler is
      for Default_Switches ("ada") use ("-gnat05");
   end Compiler;

end Hello;
You then could compile the program as
gprbuild -P hello

All GtkAda application will, of course, with the Gtk.Main and Gtk.Window packages. We'll need to add a call back to quit the program, so with the Main_quit procedure.

In the main procedure Hello, we declared a Win variable as of type Gtk_Window. Since we have use Gtk.Window, we does not need to give the full path as Gtk.Window.Gtk_Window.

The following line will call Gtk.Main.Init, which is the initialization function for GtkAda; it calls the underline gtk_init() function and will setup GtkAda, the type system, the the connection to the windowing environment, etc. The Gtk.Main.Init take care of passing command line arguments counter and string array to the C gtk_init() function; this allows GTK+ to parse specific command line arguments that control the behavior of GTK+ itself. The parsed arguments will be removed from the array, leaving the unrecognized ones for your application to parse.

For more information on which command line arguments GtkAda recognizes, please refer to the Running GTK+ Applications section.

A call to Gtk_New (Win) will create a new Gtk_Window and store it inside the Win variable. Gtk_New has Gtk.Enums.WindowToplevel as its default The_Type argument, so we don't need to specify it with the call. Which means the the Gtk_Window will be managed by the windowing system: it will have a frame, a title bar and window controls, depending on the platform.

In order to terminate the application when the Win is destroyed we connect the "destroy" signal to the Main_quit function by calling Win.On_Destroy (main_quit'Access);

This function will terminate the GtkAda main loop started by calling Gtk.Main.Main later. The "destroy" signal is emitted when a widget is destroyed, either by explicitly calling Gtk.Widget.Destroy or when the widget is unparented. Top-level Gtk_Windows are also destroyed when the Close window control button is clicked.

Gtk_Window are hidden by default. By calling Win.Show_All, we are asking GtkAda to set the visibility attribute so that it can be displayed. Al this work is done after the main loop has been started.

The last line of interest is the call to Gtk.Main.Main. This function will start the GtkAda main loop and will block the control flow of Hello until Gtk.Main.Main_Quit is called.

While the program is running, GtkAda is receiving events. These are typically input events caused by the user interacting with your program, but also things like messages from the window manager or other applications. GtkAda processes these and as a result, signals may by emitted on your widgets. Connecting handlers for these signals is how you normally make your program do somthing in response to user input.

In the main_quit procedure, Gtk.Main.Main_Quit is called to quit the program.

The following example is slightly more complex, and tries to showcase some of the capabilities of GtkAda.

In the long tradition of programming languages and libraries, it is called Hello, World.

Using the same project file of the first example, here are the sources:

src/hello.adb:
with Gtk.Main;
with Gtk.Window;      use Gtk.Window;
with Gtk.Button;      use Gtk.Button;
with Gtkada.Handlers; use Gtkada.Handlers;

with hello_cb; use hello_cb;

procedure Hello is
   Win    : Gtk_Window;
   Button : Gtk_Button;
begin
   --  Initialize GtkAda.
   Gtk.Main.Init;

   -- create a top level window
   Gtk_New (Win);
   Win.Set_Title ("Window");

   -- When the window emits the "delete-event" signal (which is emitted
   -- by GTK+ in response to an event coming from the window manager,
   -- usually as a result of clicking the "close" window control), we
   -- ask it to call the on_delete_event() function as defined above.
   Win.On_Delete_Event (main_del'Access);

   -- connect the "destroy" signal
   Win.On_Destroy (main_quit'Access);

   -- set the border width of the window
   Win.Set_Border_Width (10);

   -- create a button with label
   Gtk_New (Button, "Hello World");

   -- connect the click signal
   Button.On_Clicked (button_clicked'Access);

   -- connect the "clicked" signal of the button to destroy function,
   -- it will destroy the Win when button is clicked.
   Widget_Callback.Object_Connect
     (Button,
      "clicked",
      Widget_Callback.To_Marshaller (button_quit'Access),
      Win);

   -- This packs the button into the window. A Gtk_Window inherits from Gtk_Bin
   -- which is a special container that can only have one child.
   Win.Add (Button);

   --  Show all the window
   Win.Show_All;

   -- All GTK applications must have a Gtk.Main.Main. Control ends here
   -- and waits for an event to occur (like a key press or a mouse event),
   -- until Gtk.Main.Main_Quit is called.
   Gtk.Main.Main;
end Hello;
src/hello_cb.ads:
with Gtk.Widget;  use Gtk.Widget;
with Gtk.Button;  use Gtk.Button;
with Glib.Object;

with Gdk.Event;

package hello_cb is
   function main_del
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event)
      return  Boolean;
   procedure main_quit (Self : access Gtk_Widget_Record'Class);
   procedure button_clicked (Self : access Gtk_Button_Record'Class);
   procedure button_quit (Self : access Gtk_Widget_Record'Class);
end hello_cb;
src/hello_cb.adb:
with Ada.Text_IO; use Ada.Text_IO;
with Gtk.Main;

package body hello_cb is
   -- If you return false in the "delete_event" signal handler,
   -- GTK will emit the "destroy" signal. Returning true means
   -- you don't want the window to be destroyed.
   --
   -- This is useful for popping up 'are you sure you want to quit?'
   -- type dialogs.
   function main_del
     (Self  : access Gtk_Widget_Record'Class;
      Event : Gdk.Event.Gdk_Event)
      return  Boolean
   is
   begin
      Put_Line ("Delete event encounter.");
      return True;
   end main_del;

   procedure main_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Gtk.Main.Main_Quit;
   end main_quit;

   procedure button_clicked (Self : access Gtk_Button_Record'Class) is
   begin
      Put_Line ("Hello clicked");
   end button_clicked;

   procedure button_quit (Self : access Gtk_Widget_Record'Class) is
   begin
      Put_Line ("buttion_quit is called");
      Destroy (Self);
   end button_quit;

end hello_cb;
You compile the program with the same command:
gprbuild -P hello

This time, we defined a hello_cb package to host all the call back functions.

In GtkAda, there is no need for g_signal_connect_swapped(), we use the Widget_Callback.Object_Connect method.

P.S. All source code from these series of Getting Started with GtkAda are now at GitHub.

Google wearables

If this Google Watch could function independently as a phone without a pairing smart phone, I will immediately get one when its out. If it needs a pairing smart phone then forget it!