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 +73 -2
- package/CHANGELOG.md.backup +115 -0
- package/ejemplo_funcional.js +29 -0
- package/ejemplo_funcional_corregido.js +29 -0
- package/package.json +1 -1
- package/widgets/stringgrid/stringgrid.cpp +124 -156
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
# CHANGELOG - Extensión GTK3 para Node.js
|
|
2
2
|
|
|
3
|
-
## [Versión 1.
|
|
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,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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
364
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
}
|