Recent

Author Topic: Creating own widget in GTK  (Read 2642 times)

yyttyy

  • New Member
  • *
  • Posts: 18
Creating own widget in GTK
« on: December 06, 2020, 07:47:15 am »
The question will be very big! I'm a newbie and I'm currently writing a mini-project. I needed a button widget for it, but the standard widget didn't suit me, because I needed a button with my own style. I was looking for tutorials on creating my own widget and creating my own button. I found something, but widgets are implemented differently everywhere. I also found something about creating a button, but it wasn't a separate widget, just a single button implementation that didn't allow me to make multiple buttons(and I needed several). But thanks to this tutorial, I realized that it's best to use the cairo library to draw the context (image) inside the button. In short, here's what I managed to write:
Something like a header for types, and so on:
Code: Pascal  [Select][+][-]
  1. unit ButtonWidgetHeader;
  2.  
  3. interface
  4.  
  5. uses glib2, gdk2pixbuf, gdk2, gtk2, Cairo;
  6.  
  7. type
  8.     button_widget_state =
  9.         (state_button_normal, state_button_hovered, state_button_pressed);
  10.  
  11. type
  12.     PGtkButtonWidget = ^TGtkButtonWidget;
  13.     TGtkButtonWidget = record
  14.         button_widget : TGtkFixed;
  15.         state : button_widget_state;
  16.         drawing_box : PGtkWidget;
  17.     end;
  18.  
  19. type
  20.     PGtkButtonWidgetClass = ^TGtkButtonWidgetClass;
  21.     TGtkButtonWidgetClass = record
  22.         parent_class : TGtkFixedClass;
  23.         press_button : procedure (button_widget : PGtkButtonWidget);cdecl;
  24.         release_button : procedure (button_widget : PGtkButtonWidget);cdecl;
  25.     end;
  26.  
  27. function GTK_TYPE_BUTTON_WIDGET() : GType;
  28. function GTK_BUTTON_WIDGET(obj : gpointer) : PGtkButtonWidget;
  29. function GTK_IS_BUTTON_WIDGET(obj : gpointer) : gboolean;
  30.  
  31. implementation
  32.  
  33. function GTK_TYPE_BUTTON_WIDGET() : GType;
  34. begin
  35.     GTK_TYPE_BUTTON_WIDGET := gtk_button_get_type();
  36. end;
  37.  
  38. function GTK_BUTTON_WIDGET(obj : gpointer) : PGtkButtonWidget;
  39. begin
  40.     GTK_BUTTON_WIDGET := PGtkButtonWidget(G_TYPE_CHECK_INSTANCE_CAST ((obj),
  41.                                           GTK_TYPE_BUTTON_WIDGET))
  42. end;
  43.  
  44. function GTK_IS_BUTTON_WIDGET(obj : gpointer) : gboolean;
  45. begin
  46.     GTK_IS_BUTTON_WIDGET := G_TYPE_CHECK_INSTANCE_TYPE(obj,
  47.                             gtk_button_get_type());
  48. end;
  49.  
  50.  
  51.  
  52.  
  53. end.
  54.  

And the widget itself:

Code: Pascal  [Select][+][-]
  1. unit ButtonWidget;
  2.  
  3. interface
  4.  
  5. uses glib2, gdk2pixbuf, gdk2, gtk2, Cairo, ButtonWidgetHeader in
  6.         '/home/yarik/Desktop/ButtonWidgetUnit(Incorrectly)/' +
  7.         'ButtonWidgetHeader.pp',
  8.         Header in
  9.         '/home/yarik/Desktop/Zhuzhculator/Prog/Units/Header/Header.pp';
  10.  
  11. function gtk_button_widget_new(pathnorm, pathhover,
  12.                                pathpress, pathmask : PathArr;
  13.                                width, height : integer) : PGtkWidget;
  14.  
  15. implementation
  16.  
  17. procedure gtk_button_widget_class_init(class : PGtkButtonWidgetClass);
  18.                                                  cdecl; forward;
  19. procedure gtk_button_widget_init(button_widget : PGtkButtonWidget);
  20.                                                  cdecl; forward;
  21. procedure move_cursor(widget : PGtkWidget; event : PGdkEventCrossing;
  22.                       user_data : gpointer);cdecl; forward;
  23. procedure press_cursor(widget : PGtkWidget; event : PGdkEventButton;
  24.                        user_data : gpointer);cdecl; forward;
  25.  
  26. procedure gtk_button_widget_unrealize(widget : PGtkWidget); cdecl; forward;
  27.  
  28. procedure gtk_button_widget_update(button_widget : PGtkButtonWidget);
  29.                                                         forward;
  30. procedure button_context_create(button_widget : PGtkButtonWidget;
  31.                                 width, height : integer); forward;
  32. procedure button_images_init(); forward;
  33.  
  34. type
  35.     button_signals = (PRESS_BUTTON_SIGNAL, RELEASE_BUTTON_SIGNAL);
  36.     button_images = (image_button_normal, image_button_hovered,
  37.                      image_button_pressed, image_button_mask);
  38.     button_pathes = (path_button_normal, path_button_hovered,
  39.                      path_button_pressed, path_button_mask);
  40. var
  41.     date_type : GType = 0;
  42.     button_widget_signals : array [button_signals] of guint = (0, 0);
  43.     button_widget_images : array [button_images] of Pcairo_surface_t;
  44.     button_widget_pathes : array [button_pathes] of PathArr;
  45.     parent_class : PGtkWidgetClass = nil;
  46.  
  47. function gtk_button_get_type() : GType;
  48. const
  49.     date_info : TGTypeInfo =
  50.         (class_size : SizeOf(TGtkButtonWidgetClass);
  51.          base_init : nil;
  52.          base_finalize : nil;
  53.          class_init : TGClassInitFunc (@gtk_button_widget_class_init);
  54.          class_finalize : nil;
  55.          class_data : nil;
  56.          instance_size : SizeOf(TGtkButtonWidget);
  57.          n_preallocs : 0;
  58.          instance_init : TGInstanceInitFunc (@gtk_button_widget_init););
  59. begin
  60.     if (date_type = 0) then
  61.         date_type := g_type_register_static(
  62.             GTK_TYPE_EVENT_BOX, 'GtkButtonWidget', @date_info, 0
  63.             );
  64.     gtk_button_get_type := date_type;
  65. end;
  66.  
  67.  
  68.  
  69. procedure gtk_button_widget_class_init(class : PGtkButtonWidgetClass);cdecl;
  70. var
  71.     widget_class : PGtkWidgetClass;
  72. begin
  73.     widget_class := GTK_WIDGET_CLASS(class);
  74.     widget_class^.unrealize := @gtk_button_widget_unrealize;
  75.     button_widget_signals[PRESS_BUTTON_SIGNAL] :=
  76.         g_signal_new(
  77.         'press_button',
  78.         G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(class)),
  79.         G_SIGNAL_RUN_FIRST,
  80.         @class^.press_button - pointer(class),
  81.         nil,
  82.         nil,
  83.         @g_cclosure_marshal_VOID__VOID,
  84.         G_TYPE_NONE,
  85.         0);
  86.     button_widget_signals[RELEASE_BUTTON_SIGNAL] :=
  87.         g_signal_new(
  88.         'release_button',
  89.         G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(class)),
  90.         G_SIGNAL_RUN_FIRST,
  91.         @class^.release_button - pointer(class),
  92.         nil,
  93.         nil,
  94.         @g_cclosure_marshal_VOID__VOID,
  95.         G_TYPE_NONE,
  96.         0);
  97. end;
  98.  
  99. procedure gtk_button_widget_init(button_widget : PGtkButtonWidget);cdecl;
  100. begin
  101.     button_widget^.state := state_button_normal;
  102.     button_widget^.drawing_box := gtk_drawing_area_new();
  103.     gtk_widget_realize(GTK_WIDGET(button_widget));
  104.     button_widget^.drawing_box^.window :=
  105.         gdk_window_new(GTK_WIDGET(button_widget)^.window, nil, 0);
  106.     gtk_widget_realize(button_widget^.drawing_box);
  107.     gtk_widget_show(button_widget^.drawing_box);
  108.     gtk_container_add(GTK_CONTAINER(button_widget),
  109.                       button_widget^.drawing_box);
  110.     gtk_widget_set_events (GTK_WIDGET(button_widget), GDK_ENTER_NOTIFY_MASK
  111.                            or GDK_LEAVE_NOTIFY_MASK);
  112.     g_signal_connect (G_OBJECT(button_widget),
  113.                       'button-press-event',
  114.                       G_CALLBACK(@press_cursor), nil);
  115.     g_signal_connect (G_OBJECT(button_widget),
  116.                       'button-release-event',
  117.                       G_CALLBACK(@press_cursor), nil);
  118.     g_signal_connect (G_OBJECT(button_widget),
  119.                       'enter-notify-event',
  120.                       G_CALLBACK(@move_cursor), nil);
  121.     g_signal_connect (G_OBJECT(button_widget),
  122.                       'leave-notify-event',
  123.                       G_CALLBACK(@move_cursor), nil);
  124. end;
  125.  
  126. procedure move_cursor(widget : PGtkWidget; event : PGdkEventCrossing;
  127.                       user_data : gpointer);cdecl;
  128. //const
  129.   //  hand_cursor : PGdkCursor = nil;
  130. begin
  131.     if (event^._type = GDK_ENTER_NOTIFY) then
  132.     begin
  133.     //    if (hand_cursor = nil) then
  134.      //   begin
  135.        //     hand_cursor := gdk_cursor_new(GDK_HAND2);
  136.          //   gdk_window_set_cursor(widget^.window, hand_cursor)
  137.       //  end;
  138.         GTK_BUTTON_WIDGET(widget)^.state := state_button_hovered;
  139.         gtk_widget_queue_draw(GTK_BUTTON_WIDGET(widget)^.drawing_box);
  140.     end;
  141.     if (event^._type = GDK_LEAVE_NOTIFY) then
  142.     begin
  143.         GTK_BUTTON_WIDGET(widget)^.state := state_button_normal;
  144.         gtk_widget_queue_draw(GTK_BUTTON_WIDGET(widget)^.drawing_box);
  145.     end;
  146.     gtk_button_widget_update(GTK_BUTTON_WIDGET(widget));
  147. end;
  148.  
  149. procedure press_cursor(widget : PGtkWidget; event : PGdkEventButton;
  150.                        user_data : gpointer);cdecl;
  151. begin
  152.     if (event^._type = GDK_BUTTON_PRESS) then
  153.     begin
  154.         g_signal_emit(GTK_BUTTON_WIDGET(widget),
  155.                       button_widget_signals[PRESS_BUTTON_SIGNAL], 0);
  156.         GTK_BUTTON_WIDGET(widget)^.state := state_button_pressed;
  157.         gtk_widget_queue_draw(GTK_BUTTON_WIDGET(widget)^.drawing_box);
  158.     end;
  159.     if (event^._type = GDK_BUTTON_RELEASE) then
  160.     begin
  161.         g_signal_emit(GTK_BUTTON_WIDGET(widget),
  162.                       button_widget_signals[RELEASE_BUTTON_SIGNAL], 0);
  163.         GTK_BUTTON_WIDGET(widget)^.state := state_button_hovered;
  164.     end;
  165.     gtk_button_widget_update(GTK_BUTTON_WIDGET(widget));
  166. end;
  167.  
  168. procedure gtk_button_widget_unrealize(widget : PGtkWidget);cdecl;
  169. var
  170.     button_widget : PGtkButtonWidget;
  171. begin
  172.     if(widget = nil) then;
  173.         exit;
  174.     if(GTK_IS_BUTTON_WIDGET(widget) = false) then
  175.         exit;
  176.     button_widget := GTK_BUTTON_WIDGET(widget);
  177.     if(button_widget^.drawing_box <> nil) then
  178.         g_free(button_widget^.drawing_box);
  179.     if(GTK_OBJECT_CLASS(parent_class)^.destroy <> nil) then
  180.         GTK_OBJECT_CLASS(parent_class)^.destroy(GTK_OBJECT(widget))
  181. end;
  182.  
  183. function gtk_button_widget_new(pathnorm, pathhover,
  184.                                pathpress, pathmask : PathArr;
  185.                                width, height : integer) : PGtkWidget;
  186. var
  187.     button_widget : PGtkButtonWidget;
  188. begin
  189.     button_widget :=
  190.         g_object_new(GTK_TYPE_BUTTON_WIDGET, nil);
  191.     button_widget_pathes[path_button_normal] := pathnorm;
  192.     button_widget_pathes[path_button_hovered] := pathhover;
  193.     button_widget_pathes[path_button_pressed] := pathpress;
  194.     button_widget_pathes[path_button_mask] := pathmask;
  195.     button_images_init();
  196.     button_context_create(button_widget, width, height);
  197.     gtk_button_widget_update(button_widget);
  198.     gtk_button_widget_new := GTK_WIDGET(button_widget);
  199. end;
  200.  
  201. procedure button_images_init();
  202. begin
  203.     button_widget_images[image_button_normal] :=
  204.         cairo_image_surface_create_from_png(
  205.             @button_widget_pathes[path_button_normal]);
  206.     button_widget_images[image_button_hovered] :=
  207.         cairo_image_surface_create_from_png(
  208.             @button_widget_pathes[path_button_hovered]);
  209.     button_widget_images[image_button_pressed] :=
  210.         cairo_image_surface_create_from_png(
  211.             @button_widget_pathes[path_button_pressed]);
  212.     button_widget_images[image_button_mask] :=
  213.         cairo_image_surface_create_from_png(
  214.             @button_widget_pathes[path_button_mask]);
  215. end;
  216.  
  217. procedure button_context_create(button_widget : PGtkButtonWidget;
  218.                                 width, height : integer);
  219. var
  220.     mask_pixmap : PGdkPixmap;
  221.     context : Pcairo_t;
  222. begin
  223.     mask_pixmap := gdk_pixmap_new(nil, width, height, 1);
  224.     context := gdk_cairo_create(mask_pixmap);
  225.     cairo_set_source_surface(context, button_widget_images[image_button_mask],
  226.                              0, 0);
  227.     cairo_paint(context);
  228.     cairo_destroy(context);
  229.     gtk_widget_shape_combine_mask(button_widget^.drawing_box,
  230.                                   mask_pixmap, 0, 0);
  231.     gtk_widget_set_size_request(button_widget^.drawing_box,
  232.                                 width, height);
  233. end;
  234.  
  235. procedure gtk_button_widget_update(button_widget : PGtkButtonWidget);
  236. var
  237.     cr : Pcairo_t;
  238. begin
  239.     cr := gdk_cairo_create(button_widget^.drawing_box^.window);
  240.     case button_widget^.state of
  241.         state_button_normal:
  242.             cairo_set_source_surface(cr,
  243.                 button_widget_images[image_button_normal], 0, 0);
  244.         state_button_hovered:
  245.             cairo_set_source_surface(cr,
  246.                 button_widget_images[image_button_hovered], 0, 0);
  247.         state_button_pressed:
  248.             cairo_set_source_surface(cr,
  249.                 button_widget_images[image_button_pressed], 0, 0);
  250.     end;
  251.     cairo_paint(cr);
  252.     cairo_destroy(cr);
  253. end;
  254.  
  255.  
  256. end.
  257.  

When creating a button with the button_widget_new (...) there are such errors:

(ZhuzhMain:11860): Gtk-CRITICAL **: 12:52:58.972: IA__gtk_widget_shape_combine_mask: assertion 'GTK_IS_WIDGET (widget)' failed

(ZhuzhMain:11860): Gtk-CRITICAL **: 12:52:58.972: IA__gtk_widget_set_size_request: assertion 'GTK_IS_WIDGET (widget)' failed
An unhandled exception occurred at $000000000043C7D8:
EAccessViolation: Access violation
  $000000000043C7D8
  $000000000043C67D
  $0000000000409A0B

I tried everything, but I don't understand what the problem is. Maybe I'm writing absolutely the wrong thing. Knowledgeable people please help!

 

TinyPortal © 2005-2018