StefanSalewski / gintro

High level GObject-Introspection based GTK3/GTK4 bindings for Nim language
MIT License
296 stars 20 forks source link

macro cant choose what to use gtk4.Window00 or adw.Window00 when using connect signal with LibAdwaita #188

Closed gavr123456789 closed 2 years ago

gavr123456789 commented 2 years ago

Here minimal example:

import gintro/[gtk4, gobject, gio, pango, adw]
import std/with

proc windowOnClose(self: PreferencesWindow, mainWindow: adw.ApplicationWindow) = 
  mainWindow.close()

proc activate(app: gtk4.Application) =
  adw.init()
  let mainWindow = adw.newApplicationWindow(app)

  let
    window = adw.newPreferencesWindow()

  window.connect("close_request", windowOnClose, mainWindow)
  with window:
    # add createPage(window)
    title = "Main"
    defaultSize = (100, 400)
    show

proc main() =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  discard run(app)

main()

Here I want to main window close when preference window close.
Error:

> nimble run
  Verifying dependencies for TaskManager@0.1.0
      Info: Dependency on gintro@any version already satisfied
  Verifying dependencies for gintro@0.9.5
   Building TaskManager/TaskManager using c backend
/home/gavr/Projects/Nim/gtk-task-manager/src/TaskManager.nim(15, 9) template/generic instantiation of `connect` from here
/usr/lib/nim/core/macros.nim(557, 56) Error: ambiguous identifier: 'Window00' -- use one of the following:
  gtk4.Window00: Window00
  adw.Window00: Window00
       Tip: 3 messages have been suppressed, use --verbose to show them.
     Error: Build failed for package: TaskManager
        ... Execution failed with exit code 1
        ... Command: /usr/bin/nim c --colors:on --noNimblePath -d:NimblePkgVersion=0.1.0 --path:/home/gavr/.nimble/pkgs/gintro-0.9.5 --hints:off -o:/home/gavr/Projects/Nim/gtk-task-manager/TaskManager /home/gavr/Projects/Nim/gtk-task-manager/src/TaskManager.nim
StefanSalewski commented 2 years ago

Have you tried without

import std/with
StefanSalewski commented 2 years ago

Yes, I can reproduce it without the use of the with module.

I have to investigate the mconnect macro, will see if we can somehow prefix the low level types with the module name.

StefanSalewski commented 2 years ago

That seems to be a hard problem. We have not only to add module prefixes in the mconnect macro, but also for all the types in the gisup files. I already tried, but then it stops working. So this seems to be a task that takes more than one weekend...

A different approach would be to rename types from adwaita module, e.g. AdwWindow and such. I guess this will be additional necessary, to prevent name conflicts. We did such a renaming already in early days for one type, I think it was GApplication. Without that rename we had to always use "import gio except Application". But we should try to fix the mconnect macro too.

StefanSalewski commented 2 years ago

You may have seen https://discourse.gnome.org/t/is-someone-using-libadwaita-or-knows-what-it-is/7899/3

So the recommended fix is really to use full module prefixes for all data types used in the mconnect macro. Fixing gimplgob.nim seems to be not that hard. But we have to fix the generation of the gisup files too, which then should contain lines like

    "close_request!gtk4.Window!0!(self: gtk4.Window): bool!(self: ptr gtk4.Window00): gboolean",

with that manually edited line I get your program to compile and run, after fixing proc windowOnClose() to return a bool, and glib import.

import gintro/[gtk4, gdk4, gobject, glib, gio, pango, adw]
import std/with

proc windowOnClose(self: PreferencesWindow, mainWindow: adw.ApplicationWindow): bool =
  echo "windowOnClose"
  mainWindow.close()
  return gdk4.EVENT_PROPAGATE # window will not close with gdk4.EVENT_STOP

proc activate(app: gtk4.Application) =
  adw.init()
  let mainWindow = adw.newApplicationWindow(app)

  let
    window = adw.newPreferencesWindow()

  window.connect("close_request", windowOnClose, mainWindow)
  with window:
    # add createPage(window)
    title = "Main"
    defaultSize = (100, 400)
    show

proc main() =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  discard run(app)

main()

With return gdk4.EVENT_PROPAGATE window can be closed, but program continues to run. So it seems that you have to do some investigations how libadwaita is used correctly. I think two years ago I was not able to find examples at all.

I hope I can fix the gisup file generation this weekend. Unfortunately this in a larger and not really easy change. I guess it should work, as gobject-introspection can provide the module names. But we have to change the gen.nim script at many locations, so there is much room for bugs, and we may break other peoples code. At least after that change we will have to do a lot of testing.

gavr123456789 commented 2 years ago

Wanna add this doesnt block me anymore, If it's too hard to solve, it can be postponed for later, or use a simple solution using aliases. And yes, as was said there https://discourse.gnome.org/t/is-someone-using-libadwaita-or-knows-what-it-is/7899/3 adwaita will be used in any new GTK 4 project, its very important lib.

P.S. I recently started a new nim GTK Adwaita project https://github.com/gavr123456789/gtk-task-manager, a simple TODO with time tracking, and I really like how fast it turns out to develop an application on Nim GTK, the working version was done to be about 3 hours(a). I think we need to stop sending all newcomers to other frameworks.

StefanSalewski commented 2 years ago

If it's too hard to solve,

Currently I have the hope that it is not that hard. Have done some investigations this morning. Main task is to fix proc genPars(), which is a very large proc. So first I was disscouraged, but now I have the impression that we have to fix only a few locations. My fear is always that we may break code of the few other gintro users with such larger changes. But I think we should do it. Maybe renaming some ADW types too additional to avoid the except imports. Will try to continue work this evening.

StefanSalewski commented 2 years ago

No, had not worked this weekend, sorry. And the weather was fine here in Germany, so I had to do some sports. It was not that difficult to get all the module prefixes in the gisup files, but then we get the prefixes at many locations in all the modules as well. That does not only look very ugly, but also stops them from working, as a lot substitutions does not work any longer. And main problem, the modules changes so drastically that diff does not work any longer, so we have no clue what is happening and there is a gigantic room for regressions and bugs.

So my feeling is, that we have to fix the gen.nim script to really only add prefixes to the gisup files and leave the rest unchanged.

Have you already seriously considered using GTK with Rust instead of Nim? My feeling is that the Rust GTK bindings are better, and most importantly we get them for free, because the Rust people do the work for us. Considering all the trouble with Mr. Rumpf I should really look again on some other languages. There is not only Zig, VLang or Jai -- Kotlin is promising and a good choice for Android. But there are many more of course, Julia seems to be interesing and has a really large community already...

StefanSalewski commented 2 years ago

The good news is, that I think that I have successfully modified the gen.nim file so that it creates gisup files with all the module prefixes and leave the modules itself unchanged. I was just going to test that, when I discovered that I have overwritten the modified gimplgobj.nim which I created a week ago with some of the tests I did in the last days. So I have to recreate the new gimplgobj.nim which shall work with full module prefixes tomorrow. Should be not hard, I think I can remember the few fixes I did a week ago. So hopefully I can send you all the files for your testings tomorrow.

StefanSalewski commented 2 years ago

Sorry, we still have an issue with connectArgs.nim example.

In gimplgob.nim

ats = at.owner.strVal & '.' & at.toStrLit.strVal # v0.9.7

fails for that example. owner seems not to work for "ref O". If you have some good Nim macro understanding, I could ship new gimplgob.nim and gen.nim, and you can try debug yourself. I have not really an idea currently, so it may take some time. I think at least gen.nim works now. For gio.nim and two webkit modules diff shown differences for v0.9.6 and the new ones, but size of modules is identical, so I guess it is only a difference in the order of procs, and not in the content itself.

I will try a few hours more to fix gimplgob.nim, and when I don't get it working I will ship it with that bug.

StefanSalewski commented 2 years ago

Well, this seems to compile and work:

  var ats: string # v0.9.7
  if at.kind == nnkRefTy:
    ats = at.getType[0].strVal  & " " & at.getType[1].owner.strVal & '.' & at.getType[1].strVal
  else:
    ats = at.owner.strVal & '.' & at.toStrLit.strVal

Looks not very nice. Have you a better idea. And I wonder why we need this construct for the optional argument of connect() when argument is a ref type. Maybe we need it elsewhere too?

Have to watch Tagesschau in German TV now. Maybe I can really ship it this evening.

StefanSalewski commented 2 years ago

OK, I have decided to ship this fix. Please note that it is not yet the official version 0.9.7, so you have to install it with

nimble install gintro@#head

Maybe make a backup of the directory ~/.nimble/pkgs/gintro-0.9.6/gintro before.

The file changes of this fix are not that large, but still I have some fear that the fix may break some other peoples projects. At least our examples including the SDT tool and Nim-Chess seems to compile and work still. File gimplgobj.nim is changed so that it works with the new gisup files with the module name prefixes:

$ diff ~/.nimble/pkgs/gintro-0.9.6/gintro/gimplgobj.nim ~/gintro/gintro/gimplgobj.nim 
22,23c22
<     #return "notify!Object!1!(self: Object; paramSpec: ParamSpec)!(self: ptr Object00; paramSpec: ptr ParamSpec00)"
<     return "notify!gobject.Object!1!(self: gobject.Object; paramSpec: gobject.ParamSpec)!(self: ptr gobject.Object00; paramSpec: ptr gobject.ParamSpec00)" # v0.9.7
---
>     return "notify!Object!1!(self: Object; paramSpec: ParamSpec)!(self: ptr Object00; paramSpec: ptr ParamSpec00)"
41,44c40
<           #var h = $getType(n)[1].toStrLit
<           var h = n.getTypeInst.owner.strVal & '.' & $getType(n)[1].toStrLit # v0.9.7
<           #echo n.getTypeInst.owner.strVal
< 
---
>           var h = $getType(n)[1].toStrLit
52,53d47
< 
< # for v0.9.7 we use full qualified symbols -- in mconect() and in the gisup files
89,93c83,86
<   var ats: string # v0.9.7
<   if at.kind == nnkRefTy:
<     ats = at.getType[0].strVal  & " " & at.getType[1].owner.strVal & '.' & at.getType[1].strVal
<   else:
<     ats = at.owner.strVal & '.' & at.toStrLit.strVal
---
>   let ats = at.toStrLit.strVal
> 
>   #assert ats == $(at.toStrLit)
>   #assert ats == at.toStrLit.strVal
165,167c158
<           # h[0] = h[0].toLowerAscii # before v0.9.7
<           let xxx = h.find('.') + 1 # since v0.9.7
<           h[xxx] = h[xxx].toLowerAscii
---
>           h[0] = h[0].toLowerAscii

The changes in gen.nim are not that big, but make the file gen.nim again more ugly:

$ diff gintrotest/tests/gen.nim ~/gintro/tests/gen.nim 
3c3
< # v 0.9.7 2021-NOV-02
---
> # v 0.9.6 2021-OCT-20
406,420d405
< proc gBaseInfoGetQualifiedName(info: GIBaseInfo; modPrefix: bool = false): string =
<   if modPrefix:
<     result = ($gBaseInfoGetNamespace(info)).toLowerAscii.fixedModName & "." & $(gir.gBaseInfoGetName(info))
<     if result == "gio.Application":
<       result = "gio.GApplication"
<     if result == "gio.File":
<       result = "gio.GFile"
<   else:
<     result = $(gir.gBaseInfoGetName(info))
<     let h = $(gir.gBaseInfoGetNamespace(info))
<     if h == "Gio" and result == "Application":
<       result = "GApplication"
<     if h == "Gio" and result == "File":
<       result = "GFile"
< 
889d873
<     name00NS: string
908c892
< proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; genArrayMark = false; tryOut2Ret: bool = false; modPrefix: bool = false): GPars
---
> proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; genArrayMark = false; tryOut2Ret: bool = false): GPars
926,927c910
<     var rus = newGenRec(arrayType, genProxy = true)
<     var child = rus.namePlain # [0]
---
>     var child = newGenRec(arrayType, genProxy = true).namePlain # [0]
933,934c916
<     result.namePlainNS = rus.namePlainNS # does not work
<     result.namePlainNS = result[0] # this is the old one, which is not really correct but works
---
>     result.namePlainNS = result[0]
936d917
<     result.name00NS = rus.name00NS
945,947c926
<     var rus = newGenRec(arrayType, genProxy = true)
<     #var child = newGenRec(arrayType, genProxy = true).namePlain # [0]
<     var child = rus.namePlain
---
>     var child = newGenRec(arrayType, genProxy = true).namePlain # [0]
953d931
<     result.namePlainNS = rus.namePlainNS # does not work
956,957d933
<     #result.name00NS = result[0] # TODO as above!
<     result.name00NS = rus.name00NS
969,970c945
<       var rus = newGenRec(arrayType)
<       var child = rus.name00 # [0]
---
>       var child = newGenRec(arrayType).name00 # [0]
979,980d953
<         result.name00NS = "ptr " & rus.name00NS # "ptr " & fixedModName(ns) & '.' &
<         assert result.name00NS.find('.') > 0
1012d984
<       result.name00NS = result.namePlainNS
1021,1023d992
< 
<   result.name00NS = result.namePlainNS
< 
1034,1039d1002
< 
<   if p:
<     result.name00NS = "ptr " & result.name00NS
<   result.name00NS = mangleType(result.name00NS)
<   result.name00NS = result.name00NS & newrawmark
< 
1164c1127
< proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; genArrayMark = false; tryOut2Ret: bool = false; modPrefix: bool = false): GPars =
---
> proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; genArrayMark = false; tryOut2Ret: bool = false): GPars =
1191d1153
<       # self = "(self: " & manglename(gBaseInfoGetQualifiedName(binfo)) & "; "
1228,1229c1190,1191
<       result.arglist = "(cast[ptr " & manglename(gBaseInfoGetName(binfo)) & "00](" & arg0
<       #result.arglist = "(cast[ptr " & manglename(gBaseInfoGetQualifiedName(binfo)) & "00](" & arg0 # "self.impl)" # https://discourse.gnome.org/t/g-arg-info-may-be-null-for-the-instace-itself/3284/7
---
>       result.arglist = "(cast[ptr " & manglename(gBaseInfoGetName(binfo)) & "00](" &
>           arg0 # "self.impl)" # https://discourse.gnome.org/t/g-arg-info-may-be-null-for-the-instace-itself/3284/7
1232c1194
<         var h = manglename(gBaseInfoGetQualifiedName(binfo, modPrefix))
---
>         var h = manglename(gBaseInfoGetName(binfo))
1239,1240c1201
<         self = "(self: ptr " & manglename(gBaseInfoGetQualifiedName(binfo, modPrefix)) & "00" & "; "
<         #self = "(self: ptr " & manglename(gBaseInfoGetName(binfo)) & "00" & "; "
---
>         self = "(self: ptr " & manglename(gBaseInfoGetName(binfo)) & "00" & "; "
1300,1303c1261
<       if modPrefix:
<         str = ngr.namePlainNS
<       else:
<         str = ngr.namePlain#NS
---
>       str = ngr.namePlain
1305,1309c1263
<       if modPrefix:
<         str = ngr.name00NS
<       else:
<         str = ngr.name00#NS
< 
---
>       str = ngr.name00
1345,1348c1299
<       if modPrefix:
<         str = ngr.namePlainNS
<       else:
<         str = ngr.namePlain
---
>       str = ngr.namePlain
1530,1531c1481
<           resusres = ": " & mangleType(mangleName(gBaseInfoGetQualifiedName(h, modPrefix)))
<           #resusres = ": " & mangleType(mangleName(gBaseInfoGetName(h)))
---
>           resusres = ": " & mangleType(mangleName(gBaseInfoGetName(h)))
1533,1534c1483
<           resusres = ": ptr " & mangleType(mangleName(gBaseInfoGetQualifiedName(h, modPrefix))) & "00"
<           #resusres = ": ptr " & mangleType(mangleName(gBaseInfoGetName(h))) & "00"
---
>           resusres = ": ptr " & mangleType(mangleName(gBaseInfoGetName(h))) & "00"
2746c2695
<     (plist, arglist, replist) = genPars(signalInfo, true, info, genArrayMark = true, modPrefix = true)
---
>     (plist, arglist, replist) = genPars(signalInfo, true, info, genArrayMark = true)
2749c2698
<     (plist, arglist, replist) = genPars(signalInfo, false, info, genArrayMark = true, modPrefix = true)
---
>     (plist, arglist, replist) = genPars(signalInfo, false, info, genArrayMark = true)
2755c2704
<         supmod3.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetQualifiedName(info, true) &
---
>         supmod3.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetName(info) &
2758c2707
<         supmod4.writeLine("    \"" & (gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetQualifiedName(info, true) &
---
>         supmod4.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetName(info) &
2761c2710
<       supmod3.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetQualifiedName(info, true) & RecSep &
---
>       supmod3.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetName(info) & RecSep &
2763c2712
<       supmod4.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetQualifiedName(info, true) & RecSep &
---
>       supmod4.writeLine("    \"" & ($gBaseInfoGetName(signalInfo)).replace("-", "_") & RecSep & $gBaseInfoGetName(info) & RecSep &
4033c3982
<         let name = gBaseInfoGetQualifiedName(info)
---
>         let name = $gBaseInfoGetName(info)
4042c3991
<           interf.add(gBaseInfoGetQualifiedName(info.gObjectInfoGetInterface(i)))
---
>           interf.add($gBaseInfoGetName(info.gObjectInfoGetInterface(i)))
4050d3998
<           #echo "eeeeeeeeeee", provInt[name]
4066d4013
<         #echo "fff", obj
4530c4477
< # 4477 lines gisup4 genPars memo genRec QRS XXX seq List TODO seeq ttempResGL HHHHHH echo
---
> # 4477 lines

The diff tools shows still some differences for a few modules:

diff -q ~/gintrotest/tests//nim_gi/ ~/.nimble/pkgs/gintro-0.9.6/gintro/
Files /home/salewski/gintrotest/tests//nim_gi/gio.nim and /home/salewski/.nimble/pkgs/gintro-0.9.6/gintro/gio.nim differ
Files /home/salewski/gintrotest/tests//nim_gi/gtksource5.nim and /home/salewski/.nimble/pkgs/gintro-0.9.6/gintro/gtksource5.nim differ
Files /home/salewski/gintrotest/tests//nim_gi/webkit25.nim and /home/salewski/.nimble/pkgs/gintro-0.9.6/gintro/webkit25.nim differ
Files /home/salewski/gintrotest/tests//nim_gi/webkit2.nim and /home/salewski/.nimble/pkgs/gintro-0.9.6/gintro/webkit2.nim differ

I was not able to avoid that. Seems to be only the order of the content. Maybe you can do a more detailed content investigation. I have currently no good idea to do that, you may load the output of diff in an editor and then compare lines or blocks to verify that the content is equal.

gavr123456789 commented 2 years ago

thanks, will check tomorrow

gavr123456789 commented 2 years ago

Sorry for late. Have error during install gintro head:

Hint:  [Link]
Hint: 65710 lines; 34.060s; 117.668MiB peakmem; Debug build; proj: /tmp/gintrosalewski/gen.nim; out: /tmp/gintrosalewski/gen [SuccessX]
First we try generating bindings for GTK4, this may fail when GTK4 is not properly installed
on your computer. But don't worry, you can still use GTK3
/tmp/gintrosalewski/gen.nim(4515) gen
/tmp/gintrosalewski/gen.nim(4440) launch
/tmp/gintrosalewski/gen.nim(4119) main
/tmp/gintrosalewski/gen.nim(3211) processInfo
/tmp/gintrosalewski/gen.nim(3125) writeObj
/tmp/gintrosalewski/gen.nim(1986) writeMethod
/tmp/gintrosalewski/gen.nim(1544) genPars
/tmp/gintrosalewski/gen.nim(980) newGenRec
/usr/lib/nim/system/assertions.nim(30) failedAssertImpl
/usr/lib/nim/system/assertions.nim(23) raiseAssert
/usr/lib/nim/system/fatal.nim(49) sysFatal
Error: unhandled exception: /tmp/gintrosalewski/gen.nim(980, 16) `result.name00NS.find('.') > 0`  [AssertionDefect]
stack trace: (most recent call last)
/tmp/nimblecache-4098377420/nimscriptapi_2282383482.nim(199, 29)
/tmp/nimble_13824/githubcom_stefansalewskigintro_#head/gintro.nimble(77, 7) installBefore
/tmp/nimble_13824/githubcom_stefansalewskigintro_#head/gintro.nimble(64, 7) prep
/usr/lib/nim/system/nimscript.nim(260, 7) exec
/usr/lib/nim/system/nimscript.nim(260, 7) Error: unhandled exception: FAILED: /tmp/gintrosalewski/gen 1 [OSError]
       Tip: 3 messages have been suppressed, use --verbose to show them.
     Error: Exception raised during nimble script execution

Still can successfully install not head gintro

StefanSalewski commented 2 years ago

Thanks for testing, will investigate that soon.

StefanSalewski commented 2 years ago

Sorry, I can verify your issue easily. Seems that I did some late fixes and forget to do a final test, or maybe did the test with outdated data :-(

Error: unhandled exception: /home/salewski/gintrotest/tests/gen.nim(980, 16) result.name00NS.find('.') > 0 [AssertionDefect]

It is not really surprising that there are some new issues, as the latest changes for full namespace use in mconnect() macro are not tiny, but it is really bad that I have not tested more carefully. I guess I was too tired, as that latest fix took me 18 hours total, and the latest anger with the Nim management fully demotivated me. I guess you have seen it, e.g. at https://github.com/StefanSalewski/NimProgrammingBook/issues/10

StefanSalewski commented 2 years ago

I have removed that assert statement in line 980. I think I had un-commented it after doing the last test, and so the error occurred. I think that simple fix is good enough for now. With that fix we get exactly the files that I have tested some days ago.

The actual problem are types like cstring, int8 and some more from system module. That types are generated by proc mangleType(). I think it should be very unlikely that these types should generate problems in the mconnect() macro. Theoretical problems could occur, e.g. if another module defines these types too. So a better patch would be to modify proc mangleType() to use an additional boolean parameter that decides if it should return a type name with module prefix or not, and then we would have to check all the locations where the proc is called and had to decide if module prefix is needed or not. Not too much work, but than we have to compile and run all the examples again.

I think we can delay that work to a later time, when we have decided if we want to continue Nim and gintro at all.

Note you need nimble install gintro@#head still.

gavr123456789 commented 2 years ago

installing gintro@#head now works but my project https://github.com/gavr123456789/gtk-task-manager doesnt build anymore.

stack trace: (most recent call last)
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gimplgobj.nim(93, 13) mconnect
/home/gavr/Projects/gtk-task-manager/src/Utils.nim(105, 22) template/generic instantiation of `connect` from here
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gimplgobj.nim(93, 13) Error: node is not a symbol
       Tip: 5 messages have been suppressed, use --verbose to show them.
     Error: Build failed for package: TaskManager
        ... Execution failed with exit code 1
        ... Command: /usr/bin/nim c --colors:on --noNimblePath -d:NimblePkgVersion=0.1.0 --path:'/home/gavr/.nimble/pkgs/gintro-#head' --path:/home/gavr/.nimble/pkgs/print-1.0.1 --hints:off -o:/home/gavr/Projects/gtk-task-manager/TaskManager /home/gavr/Projects/gtk-task-manager/src/TaskManager.nim
gavr123456789 commented 2 years ago

Have you already seriously considered using GTK with Rust instead of Nim?

Yes and its terrible, Rust is too low level for GUI programming.
https://twitter.com/gavr123456789/status/1443339771779108868
https://twitter.com/gavr123456789/status/1443347658467430409
Im sure nim still the best language. I haven't talked too much with its authors, but the community in telegram is just great.

StefanSalewski commented 2 years ago

Error: node is not a symbol

Thanks for testing. I had the feeling that we may get problems, but the available test does not show it. I assume it is related to the issue which I initially had with the connect_args.nim example, fix was

var ats: string # v0.9.7
  if at.kind == nnkRefTy:
    ats = at.getType[0].strVal  & " " & at.getType[1].owner.strVal & '.' & at.getType[1].strVal
  else:
    ats = at.owner.strVal & '.' & at.toStrLit.strVal

I do not really understand the getType() stuff well, the above code was more a guessed fix.

I will try to compile your taskmanager and see where the issue is located.

Yes, Rust is really low level, I have seen some Rust GTK code and did not liked it. For passing strings to GTK libs they really do some("MyLabel"). But still I think the Rust GTK bindings are the best, Rust GTK may fully replace Vala, which gintro can not do currently, as it uses Proxy objects. And unfortunately the whole GTK community has not grown due to the GTK4 release. And Mr. Bassi has been fired! I have currently not much hope for GTK and for Nim, both seems to shrink instead to grow.

Have you seen https://forum.nim-lang.org/t/8590#55874

He is a long time Nim observer and a smart guy, and at least some of his observations are true.

StefanSalewski commented 2 years ago

OK, after installing the print package I can compile your taskmanager:

nim c TaskManager.nim

revealBtnSetTabName.connect("clicked", openFileEntry, (newTabNameReveal, tabNameEntry))

I was not aware that passing tuples have ever worked. So obviously we have no test for that.

Was it not possible to locate the source of the problem for you? Compiler show it as

Utils.nim(105, 22) template/generic instantiation of connect from here

So we have to fix the tuple case in gimplgob.nim somehow.

Have you an idea how the tuple optional argument have worked at all? How is it passed? As a ref? And what is its life time, have we to copy it. Well I wrote the mconnect macro in 2016, no one was willing to help, and I was glad that I got a first prototype to work. And basically we have still that first prototype. I really can not believe that tuples have worked, as I have no idea how it can work. But without module name prefixes it was all simpler.

I am still not happy with all the module name prefixes for the mconnect macro, as it made all more complicated. And gintro was initially designed to avoid namespaces in a way similar as Nim does it. Works well for ordinary code. But for macros the user seems to have no possibility to specify namespaces.

Is some of your Telegram people a real macro wizard? Is that a russian group only? I will not try, as I was told that Telegram is what russian mafia uses, maybe Telegram is not even legal in Germany.

Will investigate this issue tomorrow.

gavr123456789 commented 2 years ago

I should really look again on some other languages.

It doesn't really matter to me whether proxy objects are used or not. Of course, nim is an ideal candidate to replace vala because of the ability to directly control the generation of c code (generate gobject the same way vala does it), but this would require enormous effort. The most important thing for any bindings to GTK now is the ability to use GTK 4 and LibAdwaita, the rest is not so important.

https://forum.nim-lang.org/t/8590#55874

I agree with all this, but I do not consider critical shortcomings, as for me nim is clearly becoming more popular. For example, the recent kickstarter project Goodboy Galaxy is very impressive. Also judging by the talk from Mamy Ratsimbazafy, nim can be successfully used in production.

image Mamy Ratsimbazafy: Nim in production (NimConf 2021)

1) Community - I think it's great, but I only communicate in telegram chat. 2) Advertising / marketing - yes, but it's not too important for language using 3) Nimble - Before nimble, I used meson with vala, and as for me, Nimble is an order of magnitude simpler, although it completely contradicts the ideals of meson like declarativeness and not turing completeness. 4) Stdlib - yes, they are going to solve this problem with nim 2.0 by breaking std into smaller modules and reworking it.
5) Projects are dying. I haven't encountered this yet, I think gintro will be the first. 6) Leadership - yes, there is definitely a need for a fund like rust and greater participation of the community in decision-making. But this requires a lot of popularity, point 2.

All this is absolutely not important compared to what the language has achieved now. It really has three declared parameters Efficient Expressive Elegant. While remaining a high-level language similar to scripting, nim tries very hard to be on par with system languages like Rust and Zig in speed (arc, orc, view with some borrowing etc), unfortunately I do not know other similar languages except maybe Vala, but it has an even smaller community, and a strong lack of libraries. For me, the biggest problem with nim is the IDE support, autocompletion works terribly, renaming just doesn't work, type hints don't work on the variable declaration line, they say that incremental compilation will greatly improve this.

gavr123456789 commented 2 years ago

I will not try, as I was told that Telegram is what russian mafia uses, maybe Telegram is not even legal in Germany.

Ahah, I didn't think I'd meet a person who believes. This is propaganda, as well as the fact that all the hacks in the world are done by Russian hackers. The Russian mafia no longer exists, after the 90s it came to power and has been there for the last 20 years. The government tried to block telegram on the territory of Russia because telegram refused to transfer encryption keys (which do not exist) which resulted in a very funny situation https://meduza.io/en/cards/russia-is-trying-to-block-telegram-but-it-s-failing-why . As a result, they did not succeed, the ban was lifted. Sorry for some offtop here.

StefanSalewski commented 2 years ago

And in case it is not obvious for you: We generally use ref objects when we have to pass multiple values as optional arguments. I think in the past people tried tuples and failed, and I told them to use a ref object. But of course tuple support would be fine, unfortunately I do not know how tuples are passed.

It doesn't really matter to me whether proxy objects are used or not.

Proxy objects have overhead, and restrictions. I had a discussion with Mr. Droege, The Rust people tried really hard to avoid proxies. When I started gintro in 2016 I could not imagine that avoiding proxies can be possible, and I still have no idea how it may work. It is hard, as Mr. Droege told me. Some months ago a Vala guys, I think a medicinal doctor, tried to do real low level stuff with gintro. Which was not working nice due to the proxies, we should have an issue about that, maybe he closed it.

Mamy Ratsimbazafy

Unfortunately I have not seen him in the Forum any more since maybe one year. The other Status people are nearly never present in the Forum or IRC. So many people left unfortunately.

Of course, Nim is still the best universal languages for me. There are some others for special purpose, Rust for OS development, Julia for nummerics, Kotlin for web. Python for calling C libs. Zig as C replacement. Go as a very restricted something. Vlang and Jai are not really useful yet. Maybe Crystal is closest to Nim.

gavr123456789 commented 2 years ago

Is that a russian group only?

There are 2 groups, general (English) and russian, although mostly russians are sitting in the general, but you will never know about it.

Is some of your Telegram people a real macro wizard?

In fact, yes, I recently said that I want to study macros in order to do something similar to https://github.com/glurp/nimgtkdsl and I need to find out how to identify all the descendants of an object to find out if it is the heir of Gtk.Box in the computer time, and to my surprise haxscramper gave me a ready-made macro for this: https://t.me/ru_nim_talks/42808

gavr123456789 commented 2 years ago

Maybe Crystal is closest to Nim.

Yes, I was just about to write about it, as I mentioned in my tweet where I sorted out all the languages that have GTK bindings now https://twitter.com/gavr123456789/status/1443345004043411456 and https://twitter.com/gavr123456789/status/1443347658467430409

So
My top GTK languages is

Can be used right now:
Vala (full support)
Nim (GTK 3/4, LibAdwaita)

In dev:
Crystal (GTK 3 ready, 4 in dev)
Swift (GTK 3/4)
C# (GTK 4 in dev)
TypeScript (GIR for JS with Node in dev)

BAD but popular:
Rust
Python

But before that I only knew about the repository https://github.com/jhass/crystal-gobject, now I have found https://github.com/hugopl/gtk4.cr

PS I keep a record of all GTK bindings in this repository https://github.com/gavr123456789/GTK-info


I really don't like the concentration of the Crystal author on the OOP paradigm. Here 2 issues why I hate crystal design decisions: [RFC] Pipe Operator, Implementation of "lonely operator". I don't like modern Java like OOP at all, I think Nim has an almost perfect paradigm of something between all three procedural functionally and OOP. The only true OOP in my opinion was in Smalltalk. Here is a short article from another Smalltalk fan about Nim http://goran .krampe.se/2014/10/29/nim-and-oo/

StefanSalewski commented 2 years ago

Yes, I still know GoranKrampe, he was available when I started with Nim in 2014.

haxscramper. Yes, I think he is a really smart guy, when you get good contacts to him he may help you with macro stuff. I think there is one more, called ElegantBeef, and of course PMunch. And maybe Yardanico, also from Russia, he is smart, but I am not sure if he uses macros a lot.

Will continue investigation the tuple issue later this day. In case that you do not know, my approach to debug gimplgob.nim is to enable the two echo statements for r1 and r2. Then we see what is generated and what is wrong, at least for the case that the macro execution does not crash before. In that case we have to disable a few lines of the macro code before. Another approach is to compile with an option like "expandMacro", that should show the macro stuff too. My guess is, that for the tuple case, we can somehow split the return value from getType(), somehow insert the module namespace prefixes, and join it again. Similar like the code which I provided above, which was needed for the connect_args.nim test file. But of course all that is ugly and guess-work. Maybe some people from your Telegram groups have a better idea.

StefanSalewski commented 2 years ago

Yes, only un-commenting the echo statements does not help much in this case, as the processing terminates early.

Issue is the line

ats = at.owner.strVal & '.' & at.toStrLit.strVal

With

    #ats = at.owner.strVal & '.' & at.toStrLit.strVal
    ats = at.toStrLit.strVal # wrong of course
    echo ">>> ", at.toStrLit.strVal

We get as output when compiling

(Revealer, Entry)

So first question is, how we can best add module name prefixes to such tuples, where tuple can have many components of course, not only two as in your case. And other question is lifetime of the tuple.

That seems to be really not that easy.

StefanSalewski commented 2 years ago

And is (Revealer, Entry) a valid data type name at all? I really wonder why tuples may have worked before for you. For other people they seem to have never worked, and I myself never tested tuple at all, as I guessed that there may be a lot of problems. And a plain ref object is so easy :-)

Have to do some real work now, sorry.

gavr123456789 commented 2 years ago

And is (Revealer, Entry) a valid data type name at all?

Hmm, ofc

I really wonder why tuples may have worked before for you.

Idk, I started to using it from the very beginning.
Tuples are structurally typed, which seemed to me extremely logical to use as a container for signal parameters Will try to replace them with ref objects then.

StefanSalewski commented 2 years ago

Maybe you can make some suggestions (with code examples) how we can support tuples with full namespaces. If you have absolutely no idea, you may ask in the Telegram groups.

The only possible solution that I see is writing a lot of macro code -- for tuple constructor case, for real tuples, and then also for array and seq parameters. And all that for value types and ref types. That is really ugly and much room for errors. Maybe there is another way, but I do not see one currently.

StefanSalewski commented 2 years ago

Yesterday I was wondering if maybe only the use of parseStmt() in the mconnect macro is the problem, and if use of "quote do" instead or constructing the whole macro with AST functions only could avoid the namespace problems. But I don't think so.

A possible solution is of course that we ignore namespaces for your tuples, like

  var ats: string # v0.9.7
  if at.kind == nnkTupleConstr:
    ats = at.toStrLit.strVal
  elif at.kind == nnkRefTy:
    ats = at.getType[0].strVal  & " " & at.getType[1].owner.strVal & '.' & at.getType[1].strVal
  else:
    ats = at.owner.strVal & '.' & at.toStrLit.strVal

For most cases that should work, as long as the tuple does not contain a field with a type which generates name space conflicts. At least with that patch your taskmanager would compile, but only when I uncomment

row.add footerBox

I have no idea what that is and how that add() has ever worked. There are a lot of such procs in adw module, but all have longer names. Maybe it was an older libadw version?

For the tuple case, I would have to check what exactly its lifetime is. Maybe it works?

StefanSalewski commented 2 years ago

Have you already made some progress?

I just found some minimal motivation again to test "quote do:"

import macros
import gintro/[gtk4, adw]

macro genProc(arg: typed): untyped =
  let argType = arg.getType
  result = quote do:
    proc p(x: `argType`) =
      echo typeof(x)
      echo sizeof(x)

var s: string
var w: gtk4.Window

genProc(w)
p(w)

That compiles for me indeed. So replacing parseStmt() in gimplgob.nim with "quote do" may solve our issue with the namespace of the optional connect() argument. Well I am not really sure, but have some hope at least.

Will you be able to do that alone? Maybe it would be better to even avoid "quote do" as I read some years ago that that construct can have issues, so we may construct the full mconnect() directly with AST functions. But I have no idea how difficult that may be, maybe that would take us some weeks fulltime? I really do not know.

StefanSalewski commented 2 years ago

OK, as it is still not clear who is working on a fully rewrite of the mconnect() macro in gimplgobj.nim and when the rewritten macro will arrive, I have added the plain nnkTupleConstr fix. So tuples should work as before as long as the tuples do not contain conflicting types. From the macro generated code we can see that tuples are copied into a ref, so lifetime should be OK. At least your taskmanager compiles now again, when the add() call is commented out. I have done no other tests, but as the tests have not used tuples before, there should be no new problems.

nimble install gintro@#head

gavr123456789 commented 2 years ago

your taskmanager compiles now again

Thank you so much.
I just fixed 2 problems, 1) row.add, I have no idea where this method came from, it's not in the documentation, at work I still have a computer on which I compiled taskmanager every day, I'll look there. row.add relaced with row.addRow https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/method.ExpanderRow.add_row.html 2) Looks like something changed in adw again. After fresh adw compile its now sayig

(TaskManager:4896): Adwaita-ERROR **: 13:14:08.643: gtk_window_set_child() is not supported for AdwWindow

So child just must be replaced with content https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/property.Window.content.html

gavr123456789 commented 2 years ago

If you have absolutely no idea, you may ask in the Telegram groups

Unfortunately, I couldn't figure out how gintro works and I don't have the context of the name conflict problem.

StefanSalewski commented 2 years ago

Unfortunately, I couldn't figure out how gintro works and I don't have the context of the name conflict problem.

It is just a macro issue, at least it seems so. For fixing it it is not necessary to understand gen.nim. I used in 2016 parseStmt() for mconnect(), as it was that time the only solution that I could work with myself. I can remember that in 2017 or 2018, when I had a problem with that, and I asked on IRC or Forum, Araq just told me not to use string operations and parseStmt, but AST manipulation. Now I understand his advice better. So we can rewrite it to use "quote do" as a first solution, which should be not that difficult. And later rewrite it to use only AST manipulation, which is too hard for me currently.

For libadwaita, I recently asked in GTK forum about it. They told me that it is not really released yet. Gentoo Linux does not ship it yet. And I have to learn it still. Let me know if there are still issues like missing symbols. For me it compiles currently, my libadwaita install was 3 weeks ago from git sources.

gavr123456789 commented 2 years ago

Let me know if there are still issues like missing symbols

It looks like my other project(https://github.com/gavr123456789/Katana/blob/master/REWRITE/widgets/in_to_search_and_reveal.nim#L68 ) has the same problem,

> nim r --hints=off katana.nim
/home/gavr/Projects/Katana/REWRITE/gtk_utils/set_file_row_for_file.nim(23, 36) Warning: implicit conversion to 'cstring' from a non-const location: name(fileInfo); this will become a compile time error in the future [CStringConv]
/home/gavr/Projects/Katana/REWRITE/widgets/in_to_search_and_reveal.nim(44, 38) Warning: implicit conversion to 'cstring' from a non-const location: "*" & text(entry) & "*"; this will become a compile time error in the future [CStringConv]
/home/gavr/Projects/Katana/REWRITE/widgets/in_to_search_and_reveal.nim(46, 26) Warning: implicit conversion to 'cstring' from a non-const location: text(entry); this will become a compile time error in the future [CStringConv]
stack trace: (most recent call last)
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gimplgobj.nim(96, 13) mconnect
/home/gavr/Projects/Katana/REWRITE/widgets/in_to_search_and_reveal.nim(68, 8) template/generic instantiation of `connect` from here
/home/gavr/.nimble/pkgs/gintro-#head/gintro/gimplgobj.nim(96, 13) Error: node is not a symbol
gavr@arch ~/P/K/REWRITE (master) [1]> 

I will try just replace tuple with object now

StefanSalewski commented 2 years ago

That is strange. But maybe not fully unexpected. I said before, that a lot of containers like arrays or sequences are concerned when used for the optional arg, that is why it is so hard to fix. Maybe in that example you are not using a nnkTupleConstr for on the fly tuples, but a readymade tuple, or a ref tuple? I will investigate, maybe I can provide a similar plain patch like before. But we should really rewrite the macro.

gavr123456789 commented 2 years ago

After making 2 replacements of tuples for objects, I managed to compile everything.
I think you can add a mention in the readme not to use the tuples in the connect macro and close the issue.

StefanSalewski commented 2 years ago

You are using the with macro, that seems to be the issue here.

I told all people not to use tuples, but there seems to be a desire to use them. And now that I learned from you that they have worked before, I would like to keep that working, and maybe a few other people are using tuples too. Some people told me that tuples have not worked for them in the past, maybe they have done it different than you do it?

The example connect_args.nim was the initial example for passing arguments, and it contained four different cases, which did not contain tuples. That is why I was surprised that tuples have worked at all.

So, when we really should decide that we want to continue using Nim and gintro, then we should indeed rewrite the macro. Maybe we can also support arrays and sequences, beside tuples. Maybe even other containers like tables?

StefanSalewski commented 2 years ago

Your initial issue was

revealBtnSetTabName.connect("clicked", openFileEntry, (newTabNameReveal, tabNameEntry))

And now

entry.connect("search-changed", entryChanged, (fm: fm, searchBar: searchBar))

That is a different tuple construction, and needs a different fix unfortunately.

StefanSalewski commented 2 years ago

I really wonder where you learned that notation. I have never seen that before.

When we rewrite it to

entry.connect("search-changed", entryChanged, (fm, searchBar))

and do a similar rewrite in file list_view.nim at three locations than

nim c main_view.nim

compiles fine. I assume that is the main file?

Can you tell us where you learned that notation and what the purpose of it is? I may fix the mconnect macro for that case too.

gavr123456789 commented 2 years ago

Can you tell us where you learned that notation and what the purpose of it is?

https://nim-lang.org/docs/tut1.html#advanced-types-tuples
Here it is:

person = (name: "Peter", age: 30)
# Person and PersonX are equivalent
personX = person

# Create a tuple with anonymous fields:
personY = ("Peter", 30)
StefanSalewski commented 2 years ago

Ah yes I remember, I read that in 2014, and may have used that notation myself. But now I wonder if the field names have some actual purpose, or are fully redundant. I have to reread the manual, and then I will try to add one more plain fix to the mconnect macro.

gavr123456789 commented 2 years ago

offtop question, but is there a way to create gobjects with properties from gintro?

GObject is needed for ListView. ListView works in the Model -> View paradigm, for this it is necessary that all objects of the model support observable in order to re-render the corresponding elements of the list by tracking changes in the model. Each property of GObject is observable by default. There is a default implementation of ListModel - ListStore https://docs.gtk.org/gio/class.ListStore.html which works with GObject. So for creating any list with model of custom objects I need to create a GObject. Its hard to imagine an app that dont use lists

StefanSalewski commented 2 years ago

offtop question, but is there a way to create gobjects with properties from gintro?

I think declaring new GObjects with new custom properties is not supported by gobject-introspection directly. In C this is often done with C macros, which we would have to recreate. We did this for oldgtk3 for a few macros.

But first we need a small complete C code example so that we can test.

StefanSalewski commented 2 years ago

From manual:

Different tuple-types are equivalent if they specify the same fields of the same type in the same order. The names of the fields also have to be the same.

Actually I did not remember the second sentence.

I have patched that case also now:

$ diff gimplgobj.nim ~/.nimble/pkgs/gintro-0.9.6/gintro/gimplgobj.nim 
91c91,94
<   if at.kind == nnkTupleConstr:
---
>   #echo ">>>>>>>>", at.kind
>   #echo at.toStrLit.strVal
>   
>   if at.kind == nnkTupleConstr or at.kind == nnkTupleTy:

I hope that all your programs compile now. As we two seems to be currently the only gintro users, maybe then it is good enough for now.

nimble install gintro@#head

gavr123456789 commented 2 years ago

Now example from the first message here says error:

/home/gavr/Projects/gtk-task-manager/sas.nim(14, 9) template/generic instantiation of `connect` from here
/usr/lib/nim/core/macros.nim(540, 92) Error: undeclared identifier: 'gboolean'
candidates (edit distance, scope distance); see '--spellSuggest': 
 (1, 5): 'boolean' [proc declared in /home/gavr/.nimble/pkgs/gintro-#head/gintro/gobject.nim(1535, 6)]

Here new one:

 import gintro/[gtk4, gobject, gio, pango, adw, glib, gdk4]
import std/with

proc windowOnClose(self: adw.ApplicationWindow, x: string) = 
  echo x

proc activate(app: gtk4.Application) =
  adw.init()

  let
    window = adw.newApplicationWindow(app)
    header = adw.newHeaderBar()
    mainBox = newBox(Orientation.vertical, 0)
    tabBar = newTabBar()
    tabView = newTabView()

  tabBar.view = tabView
  window.connect("close_request", windowOnClose, "test")

  with mainBox: 
    append header
    append tabBar
    append tabView

  with window:
    content = mainBox
    title = ""
    defaultSize = (100, 300)
    show

proc main() =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  discard run(app)

main()

Error:

 /home/gavr/Projects/gtk-task-manager/sas.nim(19, 9) template/generic instantiation of `connect` from here
/usr/lib/nim/core/macros.nim(542, 16) Error: expression 'windowOnClose(cast[adw.ApplicationWindow](h),
              cast[ptr system.string](user_data)[])' has no type (or is ambiguous)
StefanSalewski commented 2 years ago

I told you that your first example was incorrect, and I provided working code:

after fixing proc windowOnClose() to return a bool, and glib import.

For your code above, fix is

proc windowOnClose(self: adw.ApplicationWindow, x: string): bool = 
 echo x
 return gdk4.EVENT_PROPAGATE

So it compiles for me. But indeed guessing the reason for errors is not easy always.

gavr123456789 commented 2 years ago

Oh, I completely forgot. All works for me too. And you're right, it's impossible to figure out how to fix it from such an error message. I think can be closed now, thank you, now its possible to save state of application on close event, to open from the last stage https://github.com/gavr123456789/gtk-task-manager/commit/ecb4454f4e78637b10f75f0dfcdf0c446a2bd3d0#diff-d6408b27e7a7e4a9d8fa5aba5b8ffaa82f513000aa85da4f172ff823a9fc2a32R10.