mattn / go-gtk

Go binding for GTK
http://mattn.github.com/go-gtk
BSD 3-Clause "New" or "Revised" License
2.11k stars 247 forks source link

gtk: CreateTag not working for numeric values #381

Open pebbe opened 6 years ago

pebbe commented 6 years ago

The function g_value_transform doesn't seem to work for numeric values. This effects the function *TextBuffer.CreateTag in gtk.

I fixed this for integer values not equal to zero like this. In gtk/gtk.go:

 func (v *TextBuffer) CreateTag(tag_name string, props map[string]string) *TextTag {
     ptr := C.CString(tag_name)
     defer cfree(ptr)
     tag := C._gtk_text_buffer_create_tag(v.GTextBuffer, gstring(ptr))
     for prop, val := range props {
         pprop := C.CString(prop)
         pval := C.CString(val)
-        C._apply_property(unsafe.Pointer(tag), gstring(pprop), gstring(pval))
+        intval, err := strconv.Atoi(val)
+        if err != nil {
+            intval = 0
+        }
+        C._apply_property(unsafe.Pointer(tag), gstring(pprop), gstring(pval), gint(intval))
         cfree(pprop)
         cfree(pval)
     }
     return newTextTag(tag)
 }

And in gtk/gtk.go.h:

-static void _apply_property(void* obj, const gchar* prop, const gchar* val) {
+static void _apply_property(void* obj, const gchar* prop, const gchar* val, const gint intval) {
        GParamSpec *pspec;
        GValue fromvalue = { 0, };
        GValue tovalue = { 0, };
        pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), prop);
        if (!pspec) return;
        g_value_init(&fromvalue, G_TYPE_STRING);
        g_value_set_string(&fromvalue, val);
+       if (intval) {
+               g_value_init(&tovalue, G_TYPE_INT);
+               g_value_set_int(&tovalue, intval);
+       } else {                     
                g_value_init(&tovalue, G_PARAM_SPEC_VALUE_TYPE(pspec));
                g_value_transform(&fromvalue, &tovalue);
+       }
        g_object_set_property((GObject *)obj, prop, &tovalue);
        g_value_unset(&fromvalue);
        g_value_unset(&tovalue);
 }

Of course, this is not a real solution. It just demonstrates that where integer values were not working in CreateTag, now they are. So there is a problem with g_value_transform.

mattn commented 6 years ago

Hmm, This is my bug. So how about to change type of argument to interface{}. And handle the props as map using reflect?

pebbe commented 6 years ago

That would make more sense. How many type distinctions are there for text properties? Here is my implementation differentiating between boolean, int, float, and string:

In gtk/gtk.go:

func (v *TextBuffer) CreateTag(tag_name string, props map[string]interface{}) *TextTag {
    toInt := func(val interface{}) int {
        switch v := val.(type) {
        case uint:
            return int(v)
        case uintptr:
            return int(v)
        case uint8:
            return int(v)
        case uint16:
            return int(v)
        case uint32:
            return int(v)
        case uint64:
            return int(v)
        case int:
            return int(v)
        case int8:
            return int(v)
        case int16:
            return int(v)
        case int32:
            return int(v)
        case int64:
            return int(v)
        }
        return 0
    }
    toFloat := func(val interface{}) float64 {
        switch v := val.(type) {
        case float32:
            return float64(v)
        case float64:
            return float64(v)
        }
        return 0
    }
    ptr := C.CString(tag_name)
    defer cfree(ptr)
    tag := C._gtk_text_buffer_create_tag(v.GTextBuffer, gstring(ptr))
    for prop, val := range props {
        pprop := C.CString(prop)
        switch v := val.(type) {
        case bool:
            C._apply_property_bool(unsafe.Pointer(tag), gstring(pprop), gbool(v))
        case uint, uintptr, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
            C._apply_property_int(unsafe.Pointer(tag), gstring(pprop), gint(toInt(v)))
        case float32, float64:
            C._apply_property_float(unsafe.Pointer(tag), gstring(pprop), gdouble(toFloat(v)))
        case string:
            pval := C.CString(v)
            C._apply_property_string(unsafe.Pointer(tag), gstring(pprop), gstring(pval))
            cfree(pval)
        }
        cfree(pprop)
    }
    return newTextTag(tag)
}

In gtk/gtk.go.h:

static void _apply_property_bool(void* obj, const gchar* prop, const gboolean val) {
    GValue value = { 0, };
    g_value_init(&value, G_TYPE_BOOLEAN);
    g_value_set_boolean(&value, val);
    g_object_set_property((GObject *)obj, prop, &value);
    g_value_unset(&value);
}

static void _apply_property_int(void* obj, const gchar* prop, const gint val) {
    GValue value = { 0, };
    g_value_init(&value, G_TYPE_INT);
    g_value_set_int(&value, val);
    g_object_set_property((GObject *)obj, prop, &value);
    g_value_unset(&value);
}

static void _apply_property_float(void* obj, const gchar* prop, const gdouble val) {
    GValue value = { 0, };
    g_value_init(&value, G_TYPE_DOUBLE);
    g_value_set_double(&value, val);
    g_object_set_property((GObject *)obj, prop, &value);
    g_value_unset(&value);
}

static void _apply_property_string(void* obj, const gchar* prop, const gchar* val) {
    GValue value = { 0, };
    g_value_init(&value, G_TYPE_STRING);
    g_value_set_string(&value, val);
    g_object_set_property((GObject *)obj, prop, &value);
    g_value_unset(&value);
}
mattn commented 6 years ago

Thanks. Could you please send me PR?

pebbe commented 6 years ago

My current top also includes implementations for these functions:

gtk_container_child_get
gtk_calendar_new
gtk_calendar_get_date

Is it OK to make a single PR with all these changes?

mattn commented 6 years ago

Okay, I don't mind.

pebbe commented 6 years ago

I submitted the PR.