Sunday, February 5, 2017

GtkAda Button Clicks and Containers

GtkAda Button Clicks and Containers

Since I reported that I was beginning to understand GtkAda I've found that I had more to learn.

I've gone further to display a group of buttons at different positions within the display window and to field button clicks to hide and show a button as well as to display text on the button in different fonts, sizes, colors and font styles.

The examples to hide a button all seemed to be to hide the button that was clicked.  So it took a little bit to figure out how to reference a different button instead.  (How can you click on a hidden button to redisplay it?)  And even more time to find out how to select the font, font size, etc.  The examples just didn't seem to compile.  Maybe I've developed old age disease but it took me a while to come up with what would compile.  (The GtkAda code makes use of some esoteric Ada constructs.)  But when I got the solution it became easy.

I then wanted to put the button widgets into a parent container widget since this is standard practice in aircraft ARINC 661 standard.  Thus being able to hide the parent widget to hide all its child widgets.  However, looking at the GtkAda types of containers (vertical boxes, horizontal boxes, grids) they all wanted to place the widget themselves whereas A661 has widgets placed within their container by their x, y position from the container boundary.

So I wrote my own container widget generic package so as to be able to hide or show the container and have its widgets hidden or visible.  After my attempts to use GtkAda this was a snap.  Although it doesn't yet place the widgets relative to the containers position or even specify a position for the container.  So the widgets are still placed relative to the window.

This resulted in the following set of views.

First I have a view with only one button (the one not in the container) visible.  To do this I had to first do a Show All on the window and all its contents and then do a hide of the container.  This doesn't flash the buttons since GtkAda doesn't act on the Show All until the signal handling loop is activated.  View 1 shows the result.

Next I clicked on the Sixth button which is tied to an event handler that toggles the display of the container from hidden to displayed or vice versa.  See View 2 where buttons First thru Fifth are also displayed in their different sizes, fonts and positions.

Next I clicked the First button which is tied to an event handler that toggles the display of the second button.  The results are shown in View 3 where the Second button is no longer displayed.

To illustrate how the Second button will remain hidden when the container is hidden and then redisplayed, the Sixth button was again clicked resulting in View 5.  Then the First button was clicked to redisplay the Second button as in the final view.


View 1

View 2


View 3


View 4



View 5


View 6

For those interested, the applicable code follows.
with Gtk;
with Gtk.Box;
with Gtk.Button;
with Gtk.Button_Box;
with Gtk.Enums;
with Gtk.Handlers;
with Gtk.Fixed;
with Gtk.Font_Button;
with Gtk.Frame;
with Gtk.Hbutton_Box;
with Gtk.Widget;
with Gtk.Window;

package body Layer1 is

  Win
  -- Main Window of layer 1
  : Gtk.Window.Gtk_Window;

  Frame   : Gtk.Frame.Gtk_Frame;
  -- where Gtk_Frame is access all Gtk_Frame_Record'Class;

  Fix     : Gtk.Fixed.Gtk_Fixed;

  FButtonFirst  : Gtk.Button.Gtk_Button;
  FButtonSecond : Gtk.Button.Gtk_Button;
  FButtonThird  : Gtk.Button.Gtk_Button;
  FButtonFourth : Gtk.Button.Gtk_Button;
  FButtonFifth  : Gtk.Button.Gtk_Button;
  FButtonSixth  : Gtk.Button.Gtk_Button;


  function Delete_Event
  -- Treat Delete button of upper right of Window
  ( Object : access Gtk.Widget.Gtk_Widget_Record'Class
  ) return Boolean;
  -- Callback when the main window is killed

  package Container
  --| Instantiate Container for buttons First thru Fifth
  is new Gtk_Container( Size => 10 );

  -----------------------------------------------------------------------------

  package Events is
  -- Treat widget events

    procedure Treat_First_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    -- Callback when the FButtonFirst button is clicked

    procedure Treat_Second_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    -- Callback when the FButtonSecond button is clicked

    procedure Treat_Third_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    --  Callback when the FButtonThird button is clicked

    procedure Treat_Fourth_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    --  Callback when the FButtonFourth button is clicked

    procedure Treat_Fifth_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    --  Callback when the FButtonFifth button is clicked

    procedure Treat_Sixth_Click_Event
    ( Object : access Gtk.Widget.Gtk_Widget_Record'Class );
    --  Callback when the FButtonSixth button is clicked

  end Events;

  -----------------------------------------------------------------------------
  -- Window events

  function Delete_Event
  ( Object : access Gtk.Widget.Gtk_Widget_Record'Class) return Boolean
  is
    pragma Unreferenced (Object);
  begin
    return True; -- to avoid killing the Layer1 window when
                 –- the X button is clicked
  end Delete_Event;


  package body Events is separate;

end Layer1;


with Glib;
with Gtk.Font_Chooser;
with Gtk.Font_Chooser_Widget;
with Gtk.Handlers;
with Gtk.Label;
with Pango.Font;

separate( A661.Layer1 )

procedure Initialize is

begin -- Initialize

  -- Install the configuration.
  Configuration.Initialize;

  -- Build the main window.
  Configuration.Build_Window( Window => 100 );

  -- Initializes GtkAda
  Gtk.Main.Init;

  -- Create the basic window
  Gtk.Window.Gtk_New
  ( Window   => Win,
    The_Type => Gtk.Enums.Window_Toplevel );

  -- Position the window in the center of the screen   
  Gtk.Window.Set_Position( Win, Gtk.Enums.Win_Pos_Center );

  -- Set the window size
  Gtk.Window.Set_Default_Size( Win, 800, 500 );

  --  Set a window title
  Gtk.Window.Set_Title
  ( Window => Win,
    Title  => "Layer 1" );

  -- Connect the Delete_Event procedure to the Window Close button
  Return_Window_CB.Connect
  ( Win,
    "delete_event", -- Close button event
    Return_Window_CB.To_Marshaller( Delete_Event'Access) );
 
  --------------------------
  
  -- create a frame
  Gtk.Frame.Gtk_New( Frame );
  Frame.Set_Shadow_Type( Gtk.Enums.Shadow_In );
  Win.Add( Frame );

  Gtk.Fixed.Gtk_New( Fix );
  Gtk.Frame.Add( Frame, Fix );

  Gtk.Button.Gtk_New( FButtonFirst, "First" );
  Gtk.Fixed.Put( Fix, FButtonFirst, 10, 10 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.GTk_Label(Gtk.Button.Get_Child(FButtonFirst)),
    "<span font=""Calibri"" style=""oblique"" weight=""100"" color=""green"" size=""small"">First</span>");  
  Window_CB.Object_Connect
  ( FButtonFirst, "clicked",
    Window_CB.To_Marshaller(Events.Treat_First_Click_Event'Access), Win );
  declare
    Added     : Boolean;
    ButtonRec : Container.Widget_Record;
  begin
    ButtonRec.Widget  := FButtonFirst;
    ButtonRec.Id      := 1001;
    ButtonRec.Visible := True;
    Added := Container.Add( Widget => ButtonRec );
  end;

  Gtk.Button.Gtk_New( FButtonSecond, "Second" );
  Gtk.Fixed.Put( Fix, FButtonSecond, 20, 80 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.GTk_Label(Gtk.Button.Get_Child(FButtonSecond)),
    "<span font=""Arial"" style=""italic"" weight=""900"" color=""blue"" size=""xx-large"">Second</span>"); 
  Window_CB.Object_Connect
  ( FButtonSecond, "clicked",
    Window_CB.To_Marshaller(Events.Treat_Second_Click_Event'Access), Win );
  declare
    Added     : Boolean;
    ButtonRec : Container.Widget_Record;
  begin
    ButtonRec.Widget  := FButtonSecond;
    ButtonRec.Id      := 1002;
    ButtonRec.Visible := True;
    Added := Container.Add( Widget => ButtonRec );
  end;

  Gtk.Button.Gtk_New( FButtonThird, "Third" );
  Gtk.Fixed.Put( Fix, FButtonThird, 80, 30 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.Gtk_Label(Gtk.Button.Get_Child(FButtonThird)),
   "<span font=""Sans"" style=""normal"" weight=""800"" color=""Orange"" size=""medium"">Third</span>"); 
  Window_CB.Object_Connect
  ( FButtonThird, "clicked",
    Window_CB.To_Marshaller(Events.Treat_Third_Click_Event'Access), Win );
  declare
    Added     : Boolean;
    ButtonRec : Container.Widget_Record; --My_Button_Record;
  begin
    ButtonRec.Widget  := FButtonThird;
    ButtonRec.Id      := 1003;
    ButtonRec.Visible := True;
    Added := Container.Add( Widget => ButtonRec );
  end;

  Gtk.Button.Gtk_New( FButtonFourth, "Fourth" );
  Gtk.Button.Set_Size_Request( FButtonFourth, 40, 50 );
  Gtk.Fixed.Put( Fix, FButtonFourth, 100, 150 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.GTk_Label(Gtk.Button.Get_Child(FButtonFourth)),
    "<span font=""Book Antiqua"" style=""normal"" weight=""300"" color=""Red"" size=""xx-small"">Fourth</span>"); 
  Window_CB.Object_Connect
  ( FButtonFourth, "clicked",
    Window_CB.To_Marshaller(Events.Treat_Fourth_Click_Event'Access), Win );
  declare
    Added     : Boolean;
    ButtonRec : Container.Widget_Record;
  begin
    ButtonRec.Widget  := FButtonFourth;
    ButtonRec.Id      := 1004;
    ButtonRec.Visible := True;
    Added := Container.Add( Widget => ButtonRec );
  end;

  Gtk.Button.Gtk_New( FButtonFifth, "Fifth" );
  Gtk.Button.Set_Size_Request( FButtonFifth, 150, 50 );
  Gtk.Fixed.Put( Fix, FButtonFifth, 10, 280 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.GTk_Label(Gtk.Button.Get_Child(FButtonFifth)),
    "<span font=""Courier New"" style=""normal"" weight=""900"" color=""Violet"" size=""medium"">Fifth</span>"); 
  Window_CB.Object_Connect
  ( FButtonFifth, "clicked",
    Window_CB.To_Marshaller(Events.Treat_Fifth_Click_Event'Access), Win );
  declare
    Added     : Boolean;
    ButtonRec : Container.Widget_Record; --My_Button_Record;
  begin
    ButtonRec.Widget  := FButtonFifth;
    ButtonRec.Id      := 1005;
    ButtonRec.Visible := True;
    Added := Container.Add( Widget => ButtonRec );
  end;

  Gtk.Button.Gtk_New( FButtonSixth, "Sixth" );
  Gtk.Button.Set_Size_Request( FButtonSixth, 150, 50 );
  Gtk.Fixed.Put( Fix, FButtonSixth, 200, 280 );
  Gtk.Label.Set_Markup
  ( Gtk.Label.GTk_Label(Gtk.Button.Get_Child(FButtonSixth)),
    "<span font=""Tahoma"" style=""normal"" weight=""900"" color=""Purple"" size=""large"">Sixth</span>"); 
  Window_CB.Object_Connect
  ( FButtonSixth, "clicked",
    Window_CB.To_Marshaller(Events.Treat_Sixth_Click_Event'Access), Win );
  -- Note: Not part of the Container since it must stay visible for alternate
  --       clicks to show the container again.

–-> code from Parse that displays the window when App2
–-  sends the command to do so
        -- Show the window.
        Gtk.Window.Show_All( Win );

        -- Hide the container and its widgets to start
        Container.Hide;

        -- Activate the signal handling loop to allow use of click events
        Gtk.Main.Main;

end Initialize;

package body Events is

  -----------------------------------------------------------------------------
  -- Button click event handlers

  procedure Treat_First_Click_Event
  ( Object : access Gtk.Widget.Gtk_Widget_Record'Class
  ) is
    --  Callback when the FButtonFirst button is clicked
    pragma Unreferenced( Object );
  begin -- Treat_First_Click_Event

    if Container.Is_Visible( Widget => FButtonSecond ) then
      Container.Hide( Widget => FButtonSecond );
    else
      Container.Show( Widget => FButtonSecond );
    end if;
  end Treat_First_Click_Event;

  procedure Treat_Sixth_Click_Event
  ( Object : access Gtk.Widget.Gtk_Widget_Record'Class
  ) is
    --  Callback when the FButtonSixth button is clicked
    pragma Unreferenced( Object );
  begin -- Treat_Sixth_Click_Event
    Console.Write( "Button Sixth clicked" );
    if Container.Is_Visible then
      Container.Hide;
    else -- supposed to be visible, refresh it
      Container.Show;
    end if;
  end Treat_Sixth_Click_Event;

end Events;


with Gtk.Button; -- change to Gtk.Widget

generic

-- Overview:
--   Generic package to group widgets as their parent.
--
--   Widgets can be added to the container and then the widgets
--   can be hidden or made visible as a group by invoking the
--   Hide or Show procedure of the container.
--
--   The container is different from a GtkAda container package
--   since there is no restriction as to how the widgets can be
--   placed in relation to each other.

-- Notes:
--   Parameter to supply when instantiate an instance of a topic.

  Size
  -- Maximum number of widgets allowed to be added to the container.
  : Integer;

package Gtk_Container is
-- Treat a group/array of widgets as being in a container as far as hiding
-- or making visible.

  type Widget_Id_Type
  --| Define 16-bit widget identifier
  is new Integer range 0..32767;
  for Widget_Id_Type'size use 16; -- bits
  -- Note: Needs its own declaration since circularity results if use A661.Types

  type Widget_Record is new Gtk.Button.Gtk_Button_Record with record
    Widget  : Gtk.Button.Gtk_Button;
--<<< will need to generalize to a Gtk Widget >>>
    Id      : Widget_Id_Type;
    Visible : Boolean;
  end record;
  type Widget is access all Widget_Record'Class;

  function Add
  ( Widget : in Widget_Record
    --will want a general type (from Configuration) rather than just for buttons
  ) return Boolean;
  -- Add a widget to the container.
  -- Return True if the Add was successful

  procedure Hide;
  -- Hide all the widgets of the container

  procedure Hide
  ( Widget : in Gtk.Button.Gtk_Button
    -- Widget being hidden
  );
  -- Hide the named widget of the container

  procedure Show;
  -- Show all the widgets of the container

  procedure Show
  ( Widget : in Gtk.Button.Gtk_Button
    -- Widget being shown
  );
  -- Show the named widget of the container

  function Is_Visible
  return Boolean;
  -- Returns whether the container is set to be visible

  function Is_Visible
  ( Widget : in Gtk.Button.Gtk_Button
    -- Widget being checked
  ) return Boolean;
  -- Returns whether the widget is set to be visible

end Gtk_Container;


with Gtk.Widget;

package body Gtk_Container is

  type Widget_Count_Type
  is new Integer range 0..Size; --Max;

  type Widget_Array_Type
  is array (1..Widget_Count_Type'last) of Widget_Record;

  type Widget_List_Type
  is record
    Count   : Widget_Count_Type;
    Visible : Boolean;
    List    : Widget_Array_Type;
  end record;

  type Container_Type
  is record
    Visible : Boolean;
    -- True if container has been requested to be visible
    Data    : Widget_List_Type;
    -- Data concerning the widgets of the container
  end record;

  Container
  -- Container attributes including its widgets
  : Container_Type;

  -----------------------------------------------------------------------------

  function Add
  ( Widget : in Widget_Record
  --will want a general type
  ) return Boolean is

  -- Note: The initialized Visible condition of the Widget is contained in
  --       the record.
  begin -- Add

    if Container.Data.Count < Widget_Count_Type'last then
      Container.Data.Count := Container.Data.Count + 1;
      Container.Data.List(Container.Data.Count) := Widget;
      return True;
    else
      return False; -- widget not added
    end if;

  end Add;

  procedure Hide is
  -- Hide all the widgets of the container but leave the status of the
  -- individual widgets unchanged so when the container is made visible
  -- the widgets that were hidden prior to hiding the container will
  -- remain hidden.
  begin -- Hide
    for I in 1..Container.Data.Count loop
      Gtk.Widget.Hide( Gtk.Widget.Gtk_Widget(Container.Data.List(I).Widget) );
    end loop;
    Container.Visible := False;
  end Hide;

  procedure Hide
  ( Widget : in Gtk.Button.Gtk_Button
  ) is
  -- Hide the Widget
    use type Gtk.Button.Gtk_Button;
  begin -- Hide
    for I in 1..Container.Data.Count loop
      if Widget = Container.Data.List(I).Widget then
        if Container.Data.List(I).Visible then
          Gtk.Widget.Hide( Gtk.Widget.Gtk_Widget(Widget) );
          Container.Data.List(I).Visible := False;
          exit; -- loop
      --else already hidden
        end if;
      end if;
    end loop;
  end Hide;

  procedure Show is
  -- Show all the widgets of the container that weren't hidden before the
  -- Hide of the container was done
  begin -- Show
    if not Container.Visible then
      for I in 1..Container.Data.Count loop
        if Container.Data.List(I).Visible then -- reshow the widget
          Gtk.Widget.Show( Gtk.Widget.Gtk_Widget(Container.Data.List(I).Widget) );
      --else keep hidden
        end if;
      end loop;
    end if;
    Container.Visible := True;
  end Show;

  procedure Show
  ( Widget : in Gtk.Button.Gtk_Button
  ) is
  -- Show the Widget.  Refresh it as supposed to be visible already.
    use type Gtk.Button.Gtk_Button;
  begin -- Show
    for I in 1..Container.Data.Count loop
      if Widget = Container.Data.List(I).Widget then
        Gtk.Widget.Show( Gtk.Widget.Gtk_Widget(Widget) );
        Container.Data.List(I).Visible := True;
        exit; -- loop
      end if;
    end loop;
  end Show;

  function Is_Visible
  return Boolean is
  -- Returns whether the container is set to be visible
  begin -- Is_Visible
    return Container.Visible;
  end Is_Visible;

  function Is_Visible
  ( Widget : in Gtk.Button.Gtk_Button
    -- Widget being checked
  ) return Boolean is
  -- Returns whether the widget is set to be visible
    use type Gtk.Button.Gtk_Button;
  begin -- Is_Visible
    for I in 1..Container.Data.Count loop
      if Widget = Container.Data.List(I).Widget then
        return Container.Data.List(I).Visible;
      end if;
    end loop;
    return False; -- widget isn't in container
  end Is_Visible;

begin -- at instantiation

  Container.Visible := False; -- assume container hidden
  Container.Data.Count := 0;


end Gtk_Container;

No comments: