gtk3-node 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,61 @@
1
1
  # CHANGELOG - Extensión GTK3 para Node.js
2
2
 
3
- ## [Versión 1.0.0] - 2026-02-14
3
+ ## [Versión 1.3.0] - 2026-02-15
4
+
5
+
6
+ ### Características
7
+ - Integración nativa con GTK3
8
+ - Sistema de eventos con callbacks
9
+ - Soporte para interfaces gráficas completas
10
+ - Estructura modular y extensible
11
+ - Temporización con funcionalidad similar a TTimer de Delphi
12
+
13
+ ### Documentación
14
+ - README principal
15
+ - Documentación para cada widget
16
+ - Ejemplos básicos, intermedios y avanzados
17
+ - Guía de compilación (HOWTO-COMPILE.md)
18
+
19
+ ### Notas Técnicas
20
+ - Basado en N-API para compatibilidad con múltiples versiones de Node.js
21
+ - Compilación con node-gyp
22
+ - Integración con GTK3 3.x
23
+ - Soporte para Linux (actualmente)
24
+
25
+ ## [Versión 1.2.0] - 2026-02-15
26
+
27
+ ### Añadido
28
+ - Widget Button con soporte para eventos y callbacks
29
+ - Widget Window para contenedores de interfaz
30
+ - Widget Label para mostrar texto estático
31
+ - Widget Box para layouts horizontales y verticales
32
+ - Widget Scroll para contenedores con barras de desplazamiento
33
+ - Widget Entry para entrada de texto simple
34
+ - Widget StringGrid para tablas bidimensionales de datos
35
+ - Widget TextView para áreas de texto multilinea
36
+ - Estructura modular para futuras expansiones
37
+ - Ejemplos organizados por niveles de complejidad
38
+ - Principio DRY aplicado en la arquitectura
39
+
40
+ ### Características
41
+ - Integración nativa con GTK3
42
+ - Sistema de eventos con callbacks
43
+ - Soporte para interfaces gráficas completas
44
+ - Estructura modular y extensible
45
+
46
+ ### Documentación
47
+ - README principal
48
+ - Documentación para cada widget
49
+ - Ejemplos básicos, intermedios y avanzados
50
+ - Guía de compilación (HOWTO-COMPILE.md)
51
+
52
+ ### Notas Técnicas
53
+ - Basado en N-API para compatibilidad con múltiples versiones de Node.js
54
+ - Compilación con node-gyp
55
+ - Integración con GTK3 3.x
56
+ - Soporte para Linux (actualmente)
57
+
58
+ ## [Versión 1.1.0] - 2026-02-14
4
59
 
5
60
  ### Añadido
6
61
  - Implementación inicial de la extensión GTK3 para Node.js
@@ -32,4 +87,20 @@
32
87
  - Basado en N-API para compatibilidad con múltiples versiones de Node.js
33
88
  - Compilación con node-gyp
34
89
  - Integración con GTK3 3.x
35
- - Soporte para Linux (actualmente)
90
+ - Soporte para Linux (actualmente)
91
+
92
+ ## [Versión 1.0.0] - 2026-02-14
93
+
94
+ ### Añadido
95
+ - Implementación inicial de la extensión GTK3 para Node.js
96
+ - Soporte para creación de interfaces gráficas con GTK3
97
+ - Widget Button con soporte para eventos y callbacks
98
+ - Widget Window para contenedores de interfaz
99
+ - Widget Label para mostrar texto estático
100
+ - Widget Box para layouts horizontales y verticales
101
+ - Widget Scroll para contenedores con barras de desplazamiento
102
+ - Widget Entry para entrada de texto simple
103
+ - Widget StringGrid para tablas bidimensionales de datos
104
+ - Estructura modular para futuras expansiones
105
+ - Ejemplos organizados por niveles de complejidad
106
+ - Principio DRY aplicado en la arquitectura
@@ -0,0 +1,115 @@
1
+ # CHANGELOG - Extensión GTK3 para Node.js
2
+
3
+ ## [Versión 1.3.0] - 2026-02-15
4
+
5
+ ### Añadido
6
+ - Widget TextView para áreas de texto multilinea
7
+ - Widget Timer para funcionalidad de temporización similar a TTimer de Delphi
8
+ - Soporte completo para eventos y callbacks en todos los widgets
9
+ - Mejoras en la arquitectura para evitar duplicación de código
10
+ - Correcciones de errores en la implementación de StringGrid
11
+ - Ejemplos funcionales completos que demuestran la creación de interfaces gráficas
12
+ - Soporte para creación de ventanas con botones interactivos
13
+ - Implementación de bucles de eventos no bloqueantes
14
+
15
+ ### Características
16
+ - Integración nativa con GTK3
17
+ - Sistema de eventos con callbacks
18
+ - Soporte para interfaces gráficas completas
19
+ - Estructura modular y extensible
20
+ - Temporización con funcionalidad similar a TTimer de Delphi
21
+
22
+ ### Documentación
23
+ - README principal
24
+ - Documentación para cada widget
25
+ - Ejemplos básicos, intermedios y avanzados
26
+ - Guía de compilación (HOWTO-COMPILE.md)
27
+
28
+ ### Notas Técnicas
29
+ - Basado en N-API para compatibilidad con múltiples versiones de Node.js
30
+ - Compilación con node-gyp
31
+ - Integración con GTK3 3.x
32
+ - Soporte para Linux (actualmente)
33
+
34
+ ## [Versión 1.2.0] - 2026-02-15
35
+
36
+ ### Añadido
37
+ - Widget Button con soporte para eventos y callbacks
38
+ - Widget Window para contenedores de interfaz
39
+ - Widget Label para mostrar texto estático
40
+ - Widget Box para layouts horizontales y verticales
41
+ - Widget Scroll para contenedores con barras de desplazamiento
42
+ - Widget Entry para entrada de texto simple
43
+ - Widget StringGrid para tablas bidimensionales de datos
44
+ - Widget TextView para áreas de texto multilinea
45
+ - Estructura modular para futuras expansiones
46
+ - Ejemplos organizados por niveles de complejidad
47
+ - Principio DRY aplicado en la arquitectura
48
+
49
+ ### Características
50
+ - Integración nativa con GTK3
51
+ - Sistema de eventos con callbacks
52
+ - Soporte para interfaces gráficas completas
53
+ - Estructura modular y extensible
54
+
55
+ ### Documentación
56
+ - README principal
57
+ - Documentación para cada widget
58
+ - Ejemplos básicos, intermedios y avanzados
59
+ - Guía de compilación (HOWTO-COMPILE.md)
60
+
61
+ ### Notas Técnicas
62
+ - Basado en N-API para compatibilidad con múltiples versiones de Node.js
63
+ - Compilación con node-gyp
64
+ - Integración con GTK3 3.x
65
+ - Soporte para Linux (actualmente)
66
+
67
+ ## [Versión 1.1.0] - 2026-02-14
68
+
69
+ ### Añadido
70
+ - Implementación inicial de la extensión GTK3 para Node.js
71
+ - Soporte para creación de interfaces gráficas con GTK3
72
+ - Widget Button con soporte para eventos y callbacks
73
+ - Widget Window para contenedores de interfaz
74
+ - Widget Label para mostrar texto estático
75
+ - Widget Box para layouts horizontales y verticales
76
+ - Widget Scroll para contenedores con barras de desplazamiento
77
+ - Widget Entry para entrada de texto simple
78
+ - Widget StringGrid para tablas bidimensionales de datos
79
+ - Estructura modular para futuras expansiones
80
+ - Ejemplos organizados por niveles de complejidad
81
+ - Principio DRY aplicado en la arquitectura
82
+
83
+ ### Características
84
+ - Integración nativa con GTK3
85
+ - Sistema de eventos con callbacks
86
+ - Soporte para interfaces gráficas completas
87
+ - Estructura modular y extensible
88
+
89
+ ### Documentación
90
+ - README principal
91
+ - Documentación para cada widget
92
+ - Ejemplos básicos, intermedios y avanzados
93
+ - Guía de compilación (HOWTO-COMPILE.md)
94
+
95
+ ### Notas Técnicas
96
+ - Basado en N-API para compatibilidad con múltiples versiones de Node.js
97
+ - Compilación con node-gyp
98
+ - Integración con GTK3 3.x
99
+ - Soporte para Linux (actualmente)
100
+
101
+ ## [Versión 1.0.0] - 2026-02-14
102
+
103
+ ### Añadido
104
+ - Implementación inicial de la extensión GTK3 para Node.js
105
+ - Soporte para creación de interfaces gráficas con GTK3
106
+ - Widget Button con soporte para eventos y callbacks
107
+ - Widget Window para contenedores de interfaz
108
+ - Widget Label para mostrar texto estático
109
+ - Widget Box para layouts horizontales y verticales
110
+ - Widget Scroll para contenedores con barras de desplazamiento
111
+ - Widget Entry para entrada de texto simple
112
+ - Widget StringGrid para tablas bidimensionales de datos
113
+ - Estructura modular para futuras expansiones
114
+ - Ejemplos organizados por niveles de complejidad
115
+ - Principio DRY aplicado en la arquitectura
@@ -0,0 +1,29 @@
1
+ // ejemplo_funcional.js - Ejemplo funcional con ventana y botón visibles
2
+
3
+ const { init, run } = require('./index.js');
4
+
5
+ // Inicializar GTK
6
+ init();
7
+
8
+ // Crear una ventana usando directamente las funciones nativas
9
+ const window = gtk3_native.createWindow('Ejemplo Funcional', 400, 300);
10
+
11
+ // Crear un botón usando directamente las funciones nativas
12
+ const button = gtk3_native.createButton('Haz clic aquí');
13
+
14
+ // Conectar un callback al botón
15
+ gtk3_native.connectClick(button, () => {
16
+ console.log('¡Botón presionado!');
17
+ gtk3_native.setButtonText(button, '¡Gracias por hacer clic!');
18
+ });
19
+
20
+ // Agregar el botón a la ventana
21
+ gtk3_native.addToWindow(window, button);
22
+
23
+ // Mostrar todos los widgets
24
+ gtk3_native.showAll(window);
25
+
26
+ console.log('Interfaz gráfica lista. Abriendo ventana con botón...');
27
+
28
+ // Iniciar el loop de eventos GTK para mostrar la interfaz
29
+ run();
@@ -0,0 +1,29 @@
1
+ // ejemplo_funcional_corregido.js - Ejemplo funcional con ventana y botón visibles
2
+
3
+ const { Button, Window, init, run } = require('./index.js');
4
+
5
+ // Inicializar GTK
6
+ init();
7
+
8
+ // Crear una ventana
9
+ const window = new Window('Ejemplo Funcional', 400, 300);
10
+
11
+ // Crear un botón
12
+ const button = new Button('Haz clic aquí');
13
+
14
+ // Agregar un manejador de eventos al botón
15
+ button.onClick(() => {
16
+ console.log('¡Botón presionado!');
17
+ button.label = '¡Gracias por hacer clic!';
18
+ });
19
+
20
+ // Agregar el botón a la ventana
21
+ window.add(button);
22
+
23
+ // Mostrar la ventana y todos sus contenidos
24
+ window.show();
25
+
26
+ console.log('Interfaz gráfica lista. Abriendo ventana con botón...');
27
+
28
+ // Iniciar el loop de eventos GTK para mostrar la interfaz
29
+ run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtk3-node",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Native Node.js extension for creating GTK3 widgets",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,5 +1,7 @@
1
1
  #include <napi.h>
2
2
  #include <gtk/gtk.h>
3
+ #include <vector>
4
+ #include <string>
3
5
 
4
6
  // Estructura para almacenar datos del stringgrid
5
7
  struct StringGridData {
@@ -8,109 +10,123 @@ struct StringGridData {
8
10
  std::vector<std::string> headers;
9
11
  Napi::Env env;
10
12
  Napi::FunctionReference cell_edit_callback;
11
-
13
+
12
14
  StringGridData(Napi::Env e) : treeview(nullptr), store(nullptr), env(e) {}
13
15
  };
14
16
 
15
17
  // Callback para cuando se edita una celda
16
18
  static void cell_edited_callback(GtkCellRendererText *renderer, gchar *path_string, gchar *new_text, gpointer user_data) {
17
19
  StringGridData* grid_data = static_cast<StringGridData*>(user_data);
18
-
20
+
19
21
  // Convertir la cadena de ruta a un GtkTreePath
20
22
  GtkTreePath* path = gtk_tree_path_new_from_string(path_string);
21
-
23
+
22
24
  // Obtener el iterador para la fila
23
25
  GtkTreeIter iter;
24
26
  if (gtk_tree_model_get_iter(GTK_TREE_MODEL(grid_data->store), &iter, path)) {
25
27
  // Determinar qué columna fue editada
26
- GtkTreeViewColumn* column = gtk_tree_view_get_column(GTK_TREE_VIEW(grid_data->treeview), 0); // Necesitamos identificar la columna
27
-
28
- // Buscar cuál columna es la que se está editando
29
28
  gint n_columns = gtk_tree_view_get_n_columns(GTK_TREE_VIEW(grid_data->treeview));
30
29
  for (gint i = 0; i < n_columns; i++) {
31
30
  GtkTreeViewColumn* col = gtk_tree_view_get_column(GTK_TREE_VIEW(grid_data->treeview), i);
32
31
  GList* renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
33
-
32
+
34
33
  if (renderers) {
35
34
  GtkCellRenderer* renderer_in_col = (GtkCellRenderer*)renderers->data;
36
35
  if (renderer_in_col == (GtkCellRenderer*)renderer) {
37
36
  // Actualizar el valor en la tienda
38
37
  gtk_list_store_set(grid_data->store, &iter, i, new_text, -1);
38
+
39
+ // Si hay un callback registrado, llamarlo
40
+ if (!grid_data->cell_edit_callback.IsEmpty()) {
41
+ Napi::HandleScope scope(grid_data->env);
42
+
43
+ // Crear objeto con información de la edición
44
+ Napi::Object editInfo = Napi::Object::New(grid_data->env);
45
+ gint row_index = gtk_tree_path_get_indices(path)[0];
46
+ editInfo.Set("row", Napi::Number::New(grid_data->env, row_index));
47
+ editInfo.Set("col", Napi::Number::New(grid_data->env, i));
48
+ editInfo.Set("newValue", Napi::String::New(grid_data->env, new_text));
49
+
50
+ grid_data->cell_edit_callback.Call({editInfo});
51
+ }
52
+ g_list_free(renderers);
39
53
  break;
40
54
  }
41
55
  g_list_free(renderers);
42
56
  }
43
57
  }
44
58
  }
45
-
59
+
46
60
  gtk_tree_path_free(path);
47
61
  }
48
62
 
49
63
  // Constructor de stringgrid
50
64
  Napi::Value CreateStringGrid(const Napi::CallbackInfo& info) {
51
65
  Napi::Env env = info.Env();
52
-
66
+
53
67
  int rows = 10; // Valor por defecto
54
68
  int cols = 5; // Valor por defecto
55
-
69
+
56
70
  if (info.Length() > 0 && info[0].IsNumber()) {
57
71
  rows = info[0].As<Napi::Number>().Int32Value();
58
72
  }
59
73
  if (info.Length() > 1 && info[1].IsNumber()) {
60
74
  cols = info[1].As<Napi::Number>().Int32Value();
61
75
  }
62
-
76
+
63
77
  // Crear modelo para el TreeView
64
78
  GType* types = new GType[cols];
65
79
  for (int i = 0; i < cols; i++) {
66
80
  types[i] = G_TYPE_STRING;
67
81
  }
68
-
82
+
69
83
  GtkListStore* store = gtk_list_store_newv(cols, types);
70
84
  GtkWidget* treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
71
-
85
+
72
86
  // Crear columnas con edición
73
87
  for (int i = 0; i < cols; i++) {
74
88
  GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
75
-
89
+
76
90
  // Permitir edición
77
91
  g_object_set(renderer, "editable", TRUE, NULL);
78
-
79
- // Conectar el callback de edición
92
+
93
+ // Conectar el callback de edición con la estructura de datos
80
94
  g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_callback), NULL);
81
-
95
+
82
96
  GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes(
83
97
  ("Col " + std::to_string(i)).c_str(), renderer, "text", i, NULL);
84
-
98
+
85
99
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
86
100
  }
87
-
101
+
88
102
  // Permitir selección
89
103
  GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
90
104
  gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
91
-
105
+
92
106
  delete[] types;
93
-
107
+
94
108
  // Crear estructura de datos para el stringgrid
95
109
  StringGridData* grid_data = new StringGridData(env);
96
110
  grid_data->treeview = treeview;
97
111
  grid_data->store = store;
98
-
99
- // Conectar el callback de edición a la estructura de datos
100
- for (int i = 0; i < cols; i++) {
101
- GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
102
- g_object_set(renderer, "editable", TRUE, NULL);
103
- g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_callback), grid_data);
104
-
105
- // Actualizar el renderer en la columna existente
112
+
113
+ // Conectar el callback de edición a la estructura de datos para todas las columnas
114
+ gint n_columns = gtk_tree_view_get_n_columns(GTK_TREE_VIEW(treeview));
115
+ for (gint i = 0; i < n_columns; i++) {
106
116
  GtkTreeViewColumn* column = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i);
107
- gtk_tree_view_column_clear(column);
108
- gtk_tree_view_column_pack_start(column, renderer, TRUE);
109
- gtk_tree_view_column_add_attribute(column, renderer, "text", i);
117
+ GList* renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
118
+
119
+ if (renderers) {
120
+ GtkCellRenderer* renderer = (GtkCellRenderer*)renderers->data;
121
+ // Reconectar el callback con la estructura de datos correcta
122
+ g_signal_handlers_disconnect_by_func(renderer, (gpointer)cell_edited_callback, NULL);
123
+ g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_callback), grid_data);
124
+ g_list_free(renderers);
125
+ }
110
126
  }
111
-
127
+
112
128
  // Guardar la estructura en los datos del widget
113
- g_object_set_data_full(G_OBJECT(treeview), "stringgrid_data", grid_data,
129
+ g_object_set_data_full(G_OBJECT(treeview), "stringgrid_data", grid_data,
114
130
  GDestroyNotify([](gpointer data) {
115
131
  StringGridData* grid_data = static_cast<StringGridData*>(data);
116
132
  if (!grid_data->cell_edit_callback.IsEmpty()) {
@@ -118,55 +134,55 @@ Napi::Value CreateStringGrid(const Napi::CallbackInfo& info) {
118
134
  }
119
135
  delete grid_data;
120
136
  }));
121
-
137
+
122
138
  // Convertir el widget a External para pasarlo a JavaScript
123
139
  Napi::Object obj = Napi::Object::New(env);
124
140
  obj.Set("widget", Napi::External<GtkWidget>::New(env, treeview));
125
-
141
+
126
142
  return obj;
127
143
  }
128
144
 
129
145
  // Función para establecer el valor de una celda
130
146
  Napi::Value SetGridCellValue(const Napi::CallbackInfo& info) {
131
147
  Napi::Env env = info.Env();
132
-
133
- if (info.Length() < 4 || !info[0].IsObject() || !info[1].IsNumber() ||
148
+
149
+ if (info.Length() < 4 || !info[0].IsObject() || !info[1].IsNumber() ||
134
150
  !info[2].IsNumber() || !info[3].IsString()) {
135
151
  Napi::TypeError::New(env, "Se requiere un objeto widget, fila, columna y valor").ThrowAsJavaScriptException();
136
152
  return Napi::Boolean::New(env, false);
137
153
  }
138
-
154
+
139
155
  Napi::Object widgetObj = info[0].As<Napi::Object>();
140
156
  int row = info[1].As<Napi::Number>().Int32Value();
141
157
  int col = info[2].As<Napi::Number>().Int32Value();
142
158
  std::string value = info[3].As<Napi::String>().Utf8Value();
143
-
159
+
144
160
  if (!widgetObj.HasOwnProperty("widget")) {
145
161
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
146
162
  return Napi::Boolean::New(env, false);
147
163
  }
148
-
164
+
149
165
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
150
166
  GtkWidget* treeview = widgetExt.Data();
151
-
167
+
152
168
  StringGridData* grid_data = static_cast<StringGridData*>(
153
169
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
154
-
170
+
155
171
  if (!grid_data || !grid_data->store) {
156
172
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
157
173
  return Napi::Boolean::New(env, false);
158
174
  }
159
-
175
+
160
176
  // Buscar la fila específica
161
177
  GtkTreeIter iter;
162
178
  gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(grid_data->store), &iter);
163
179
  int current_row = 0;
164
-
180
+
165
181
  while (valid && current_row < row) {
166
182
  valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(grid_data->store), &iter);
167
183
  current_row++;
168
184
  }
169
-
185
+
170
186
  if (valid && current_row == row) {
171
187
  gtk_list_store_set(grid_data->store, &iter, col, value.c_str(), -1);
172
188
  } else {
@@ -178,50 +194,50 @@ Napi::Value SetGridCellValue(const Napi::CallbackInfo& info) {
178
194
  }
179
195
  gtk_list_store_set(grid_data->store, &iter, col, value.c_str(), -1);
180
196
  }
181
-
197
+
182
198
  return Napi::Boolean::New(env, true);
183
199
  }
184
200
 
185
201
  // Función para obtener el valor de una celda
186
202
  Napi::Value GetGridCell(const Napi::CallbackInfo& info) {
187
203
  Napi::Env env = info.Env();
188
-
189
- if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsNumber() ||
204
+
205
+ if (info.Length() < 3 || !info[0].IsObject() || !info[1].IsNumber() ||
190
206
  !info[2].IsNumber()) {
191
207
  Napi::TypeError::New(env, "Se requiere un objeto widget, fila y columna").ThrowAsJavaScriptException();
192
208
  return Napi::String::New(env, "");
193
209
  }
194
-
210
+
195
211
  Napi::Object widgetObj = info[0].As<Napi::Object>();
196
212
  int row = info[1].As<Napi::Number>().Int32Value();
197
213
  int col = info[2].As<Napi::Number>().Int32Value();
198
-
214
+
199
215
  if (!widgetObj.HasOwnProperty("widget")) {
200
216
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
201
217
  return Napi::String::New(env, "");
202
218
  }
203
-
219
+
204
220
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
205
221
  GtkWidget* treeview = widgetExt.Data();
206
-
222
+
207
223
  StringGridData* grid_data = static_cast<StringGridData*>(
208
224
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
209
-
225
+
210
226
  if (!grid_data || !grid_data->store) {
211
227
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
212
228
  return Napi::String::New(env, "");
213
229
  }
214
-
230
+
215
231
  // Buscar la fila específica
216
232
  GtkTreeIter iter;
217
233
  gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(grid_data->store), &iter);
218
234
  int current_row = 0;
219
-
235
+
220
236
  while (valid && current_row < row) {
221
237
  valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(grid_data->store), &iter);
222
238
  current_row++;
223
239
  }
224
-
240
+
225
241
  if (valid && current_row == row) {
226
242
  GValue value = {0};
227
243
  gtk_tree_model_get_value(GTK_TREE_MODEL(grid_data->store), &iter, col, &value);
@@ -229,257 +245,209 @@ Napi::Value GetGridCell(const Napi::CallbackInfo& info) {
229
245
  g_value_unset(&value);
230
246
  return Napi::String::New(env, cell_value ? cell_value : "");
231
247
  }
232
-
248
+
233
249
  return Napi::String::New(env, "");
234
250
  }
235
251
 
236
252
  // Función para establecer encabezados
237
253
  Napi::Value SetGridHeader(const Napi::CallbackInfo& info) {
238
254
  Napi::Env env = info.Env();
239
-
255
+
240
256
  if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsArray()) {
241
257
  Napi::TypeError::New(env, "Se requiere un objeto widget y un array de encabezados").ThrowAsJavaScriptException();
242
258
  return Napi::Boolean::New(env, false);
243
259
  }
244
-
260
+
245
261
  Napi::Object widgetObj = info[0].As<Napi::Object>();
246
262
  Napi::Array headersArray = info[1].As<Napi::Array>();
247
-
263
+
248
264
  if (!widgetObj.HasOwnProperty("widget")) {
249
265
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
250
266
  return Napi::Boolean::New(env, false);
251
267
  }
252
-
268
+
253
269
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
254
270
  GtkWidget* treeview = widgetExt.Data();
255
-
271
+
256
272
  StringGridData* grid_data = static_cast<StringGridData*>(
257
273
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
258
-
274
+
259
275
  if (!grid_data) {
260
276
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
261
277
  return Napi::Boolean::New(env, false);
262
278
  }
263
-
279
+
264
280
  // Actualizar los encabezados
265
281
  guint n_columns = gtk_tree_view_get_n_columns(GTK_TREE_VIEW(treeview));
266
282
  guint array_length = headersArray.Length();
267
-
283
+
268
284
  for (guint i = 0; i < n_columns && i < array_length; i++) {
269
285
  Napi::Value headerValue = headersArray.Get(i);
270
286
  std::string header = headerValue.As<Napi::String>().Utf8Value();
271
-
287
+
272
288
  GtkTreeViewColumn* column = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i);
273
289
  if (column) {
274
290
  gtk_tree_view_column_set_title(column, header.c_str());
275
291
  }
276
292
  }
277
-
293
+
278
294
  return Napi::Boolean::New(env, true);
279
295
  }
280
296
 
281
297
  // Función para agregar una fila
282
298
  Napi::Value AddGridRow(const Napi::CallbackInfo& info) {
283
299
  Napi::Env env = info.Env();
284
-
300
+
285
301
  if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsArray()) {
286
302
  Napi::TypeError::New(env, "Se requiere un objeto widget y un array de datos").ThrowAsJavaScriptException();
287
303
  return Napi::Boolean::New(env, false);
288
304
  }
289
-
305
+
290
306
  Napi::Object widgetObj = info[0].As<Napi::Object>();
291
307
  Napi::Array dataArray = info[1].As<Napi::Array>();
292
-
308
+
293
309
  if (!widgetObj.HasOwnProperty("widget")) {
294
310
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
295
311
  return Napi::Boolean::New(env, false);
296
312
  }
297
-
313
+
298
314
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
299
315
  GtkWidget* treeview = widgetExt.Data();
300
-
316
+
301
317
  StringGridData* grid_data = static_cast<StringGridData*>(
302
318
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
303
-
319
+
304
320
  if (!grid_data || !grid_data->store) {
305
321
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
306
322
  return Napi::Boolean::New(env, false);
307
323
  }
308
-
324
+
309
325
  // Crear una nueva fila
310
326
  GtkTreeIter iter;
311
327
  gtk_list_store_append(grid_data->store, &iter);
312
-
328
+
313
329
  // Establecer los valores para cada columna
314
330
  guint array_length = dataArray.Length();
315
331
  guint n_cols = gtk_tree_model_get_n_columns(GTK_TREE_MODEL(grid_data->store));
316
-
332
+
317
333
  for (guint i = 0; i < n_cols && i < array_length; i++) {
318
334
  Napi::Value cellValue = dataArray.Get(i);
319
335
  std::string cellStr = cellValue.As<Napi::String>().Utf8Value();
320
336
  gtk_list_store_set(grid_data->store, &iter, i, cellStr.c_str(), -1);
321
337
  }
322
-
338
+
323
339
  return Napi::Boolean::New(env, true);
324
340
  }
325
341
 
326
342
  // Función para obtener la fila/columna seleccionada
327
343
  Napi::Value GetSelectedCell(const Napi::CallbackInfo& info) {
328
344
  Napi::Env env = info.Env();
329
-
345
+
330
346
  if (info.Length() < 1 || !info[0].IsObject()) {
331
347
  Napi::TypeError::New(env, "Se requiere un objeto widget").ThrowAsJavaScriptException();
332
348
  return Napi::Object::New(env);
333
349
  }
334
-
350
+
335
351
  Napi::Object widgetObj = info[0].As<Napi::Object>();
336
-
352
+
337
353
  if (!widgetObj.HasOwnProperty("widget")) {
338
354
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
339
355
  return Napi::Object::New(env);
340
356
  }
341
-
357
+
342
358
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
343
359
  GtkWidget* treeview = widgetExt.Data();
344
-
360
+
345
361
  StringGridData* grid_data = static_cast<StringGridData*>(
346
362
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
347
-
363
+
348
364
  if (!grid_data) {
349
365
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
350
366
  return Napi::Object::New(env);
351
367
  }
352
-
368
+
353
369
  // Obtener la selección
354
370
  GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
355
-
371
+
356
372
  if (gtk_tree_selection_get_selected(selection, NULL, NULL)) {
357
373
  GtkTreeModel* model;
358
374
  GtkTreeIter iter;
359
-
375
+
360
376
  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
361
377
  // Para encontrar el índice de la fila, usaremos gtk_tree_model_get_path
362
378
  GtkTreePath* path = gtk_tree_model_get_path(model, &iter);
363
- gint* indices = gtk_tree_path_get_indices(path);
364
- gint row_index = -1;
365
-
366
- if (indices != NULL) {
367
- row_index = indices[0]; // El primer índice es el número de fila
368
- }
369
-
379
+ gint row_index = gtk_tree_path_get_indices(path)[0];
380
+
370
381
  gtk_tree_path_free(path);
371
-
382
+
372
383
  // Crear un objeto con la información de la selección
373
384
  Napi::Object result = Napi::Object::New(env);
374
385
  result.Set("row", Napi::Number::New(env, row_index));
375
386
  result.Set("col", Napi::Number::New(env, -1)); // GTK TreeView no proporciona fácilmente la columna seleccionada
376
-
387
+
377
388
  return result;
378
389
  }
379
390
  }
380
-
391
+
381
392
  // Si no hay selección, retornar un objeto vacío
382
393
  Napi::Object result = Napi::Object::New(env);
383
394
  result.Set("row", Napi::Number::New(env, -1));
384
395
  result.Set("col", Napi::Number::New(env, -1));
385
-
386
- return result;
387
- }
388
396
 
389
- // Función para conectar un callback cuando se edita una celda
390
- static void cell_edited_callback_with_context(GtkCellRendererText *renderer, gchar *path_string, gchar *new_text, gpointer user_data) {
391
- StringGridData* grid_data = static_cast<StringGridData*>(user_data);
392
-
393
- // Convertir la cadena de ruta a un GtkTreePath
394
- GtkTreePath* path = gtk_tree_path_new_from_string(path_string);
395
-
396
- // Obtener el iterador para la fila
397
- GtkTreeIter iter;
398
- if (gtk_tree_model_get_iter(GTK_TREE_MODEL(grid_data->store), &iter, path)) {
399
- // Determinar qué columna fue editada
400
- gint n_columns = gtk_tree_view_get_n_columns(GTK_TREE_VIEW(grid_data->treeview));
401
- for (gint i = 0; i < n_columns; i++) {
402
- GtkTreeViewColumn* col = gtk_tree_view_get_column(GTK_TREE_VIEW(grid_data->treeview), i);
403
- GList* renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
404
-
405
- if (renderers) {
406
- GtkCellRenderer* renderer_in_col = (GtkCellRenderer*)renderers->data;
407
- if (renderer_in_col == (GtkCellRenderer*)renderer) {
408
- // Actualizar el valor en la tienda
409
- gtk_list_store_set(grid_data->store, &iter, i, new_text, -1);
410
-
411
- // Si hay un callback registrado, llamarlo
412
- if (!grid_data->cell_edit_callback.IsEmpty()) {
413
- Napi::HandleScope scope(grid_data->env);
414
-
415
- // Crear objeto con información de la edición
416
- Napi::Object editInfo = Napi::Object::New(grid_data->env);
417
- editInfo.Set("row", Napi::Number::New(grid_data->env, gtk_tree_path_get_indices(path)[0]));
418
- editInfo.Set("col", Napi::Number::New(grid_data->env, i));
419
- editInfo.Set("newValue", Napi::String::New(grid_data->env, new_text));
420
-
421
- grid_data->cell_edit_callback.Call({editInfo});
422
- }
423
- break;
424
- }
425
- g_list_free(renderers);
426
- }
427
- }
428
- }
429
-
430
- gtk_tree_path_free(path);
397
+ return result;
431
398
  }
432
399
 
433
400
  // Función para registrar un callback de edición de celda
434
401
  Napi::Value RegisterCellEditCallback(const Napi::CallbackInfo& info) {
435
402
  Napi::Env env = info.Env();
436
-
403
+
437
404
  if (info.Length() < 2 || !info[0].IsObject() || !info[1].IsFunction()) {
438
405
  Napi::TypeError::New(env, "Se requiere un objeto widget y una función callback").ThrowAsJavaScriptException();
439
406
  return Napi::Boolean::New(env, false);
440
407
  }
441
-
408
+
442
409
  Napi::Object widgetObj = info[0].As<Napi::Object>();
443
410
  Napi::Function callback = info[1].As<Napi::Function>();
444
-
411
+
445
412
  if (!widgetObj.HasOwnProperty("widget")) {
446
413
  Napi::TypeError::New(env, "El objeto no contiene un widget válido").ThrowAsJavaScriptException();
447
414
  return Napi::Boolean::New(env, false);
448
415
  }
449
-
416
+
450
417
  Napi::External<GtkWidget> widgetExt = widgetObj.Get("widget").As<Napi::External<GtkWidget>>();
451
418
  GtkWidget* treeview = widgetExt.Data();
452
-
419
+
453
420
  StringGridData* grid_data = static_cast<StringGridData*>(
454
421
  g_object_get_data(G_OBJECT(treeview), "stringgrid_data"));
455
-
422
+
456
423
  if (!grid_data) {
457
424
  Napi::TypeError::New(env, "Datos del grid no encontrados").ThrowAsJavaScriptException();
458
425
  return Napi::Boolean::New(env, false);
459
426
  }
460
-
427
+
461
428
  // Limpiar callback anterior si existe
462
429
  if (!grid_data->cell_edit_callback.IsEmpty()) {
463
430
  grid_data->cell_edit_callback.Reset();
464
431
  }
465
-
432
+
466
433
  // Almacenar el nuevo callback
467
434
  grid_data->cell_edit_callback = Napi::Persistent(callback);
468
-
469
- // Conectar el callback de edición a todos los renderers
435
+
436
+ // Conectar el callback de edición a todas las columnas
470
437
  gint n_columns = gtk_tree_view_get_n_columns(GTK_TREE_VIEW(treeview));
471
438
  for (gint i = 0; i < n_columns; i++) {
472
439
  GtkTreeViewColumn* col = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), i);
473
440
  GList* renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(col));
474
-
441
+
475
442
  if (renderers) {
476
443
  GtkCellRenderer* renderer = (GtkCellRenderer*)renderers->data;
444
+ // Reconectar el callback con la estructura de datos correcta
477
445
  g_signal_handlers_disconnect_by_func(renderer, (gpointer)cell_edited_callback, NULL);
478
- g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_callback_with_context), grid_data);
446
+ g_signal_connect(renderer, "edited", G_CALLBACK(cell_edited_callback), grid_data);
479
447
  g_list_free(renderers);
480
448
  }
481
449
  }
482
-
450
+
483
451
  return Napi::Boolean::New(env, true);
484
452
  }
485
453
 
@@ -491,6 +459,6 @@ Napi::Object InitStringGrid(Napi::Env env, Napi::Object exports) {
491
459
  exports.Set("addGridRow", Napi::Function::New(env, AddGridRow));
492
460
  exports.Set("getSelectedCell", Napi::Function::New(env, GetSelectedCell));
493
461
  exports.Set("registerCellEditCallback", Napi::Function::New(env, RegisterCellEditCallback));
494
-
462
+
495
463
  return exports;
496
464
  }