Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table Selection API #69

Open
AndyObtiva opened this issue Mar 4, 2023 · 12 comments
Open

Table Selection API #69

AndyObtiva opened this issue Mar 4, 2023 · 12 comments

Comments

@AndyObtiva
Copy link
Collaborator

AndyObtiva commented Mar 4, 2023

libui-ng recently merged a table selection API:
libui-ng/libui-ng#73

Could you please provide FFI bindings for that API in Ruby so that I could expose in Glimmer DSL for LibUI's GUI DSL?

I got a request about it from a user over here:
AndyObtiva/glimmer-dsl-libui#41

You could make a small release with a newer version of libui-ng and the table selection API only. Afterwards, you could continue to make small releases that add more and more FFI bindings in Ruby for other APIs that got added to libui-ng recently. But, to start, the table selection API should be enough for me to be able to expose it and make a release in Glimmer DSL for LibUI

@kojix2
Copy link
Owner

kojix2 commented Mar 5, 2023

Hello
I have added recent libui functions and structures.
I have not checked that it works. However, very few people seem to be using the pre-release version, so I have released it. Before I forget this issue exists. Please let me know if you have any problems.
Thank you very much.

921157e

@kojix2 kojix2 closed this as completed Mar 5, 2023
@AndyObtiva
Copy link
Collaborator Author

Thank you! I missed those new table methods the last time I supported a pre version of your bindings.

But, my code crashes whenever I try to use any of the new table listeners:

  • table_on_row_clicked
  • table_on_row_double_clicked
  • table_on_selection_changed

Perhaps their new API differs from the older common API for listeners?

Could you please share code using the listeners above that works using LibUI alone so that I could translate it to Glimmer DSL for LibUI code (please include table_header_on_clicked too in your example)?

@kojix2 kojix2 reopened this Mar 6, 2023
@kojix2
Copy link
Owner

kojix2 commented Mar 6, 2023

Sure.
If you use a block to receive arguments, it works like this.

require 'libui'

UI = LibUI

UI.init

main_window = UI.new_window('Animal sounds', 300, 200, 1)

hbox = UI.new_horizontal_box
UI.window_set_child(main_window, hbox)

data = [
  %w[cat meow],
  %w[dog woof],
  %w[chicken cock-a-doodle-doo],
  %w[horse neigh],
  %w[cow moo]
]

# Protects BlockCaller objects from garbage collection.
@block_callers = []
def rbcallback(*args, &block)
  args << [0] if args.size == 1 # Argument types are ommited
  block_caller = Fiddle::Closure::BlockCaller.new(*args, &block)
  @block_callers << block_caller
  block_caller
end

model_handler = UI::FFI::TableModelHandler.malloc
model_handler.to_ptr.free = Fiddle::RUBY_FREE
model_handler.NumColumns   = rbcallback(4) { 2 }
model_handler.ColumnType   = rbcallback(4) { 0 }
model_handler.NumRows      = rbcallback(4) { 5 }
model_handler.CellValue    = rbcallback(1, [1, 1, 4, 4]) do |_, _, row, column|
  UI.new_table_value_string(data[row][column])
end
model_handler.SetCellValue = rbcallback(0, [0]) {}

model = UI.new_table_model(model_handler)

table_params = UI::FFI::TableParams.malloc
table_params.to_ptr.free = Fiddle::RUBY_FREE
table_params.Model = model
table_params.RowBackgroundColorModelColumn = -1

table = UI.new_table(table_params)
UI.table_append_text_column(table, 'Animal', 0, -1)
UI.table_append_text_column(table, 'Description', 1, -1)
UI.table_set_selection_mode(table, UI::TableSelectionModeZeroOrMany)

UI.table_on_row_clicked(table) do |_, row_idx| 
  puts "#{data[row_idx][0]} \"#{data[row_idx][1]}\""
end

UI.table_on_row_double_clicked(table) do |_, row_idx|
  puts "#{data[row_idx][0]} \"#{data[row_idx][1]}!!\"".upcase
end


UI.table_on_selection_changed(table) do |ptr|
  tsp = UI.table_get_selection(ptr)
  ts = UI::FFI::TableSelection.new(tsp)
  if ts.NumRows > 0 # 
    p selected: ts.Rows[0, Fiddle::SIZEOF_INT * ts.NumRows].unpack("i*")
  end
  UI.free_table_selection(tsp)
end

UI.table_header_on_clicked(table) do |_, col_idx|
  puts col_idx
end


UI.box_append(hbox, table, 1)
UI.control_show(main_window)

UI.window_on_closing(main_window) do
  puts 'Bye Bye'
  UI.control_destroy(main_window)
  UI.free_table_model(model)
  UI.quit
  0
end

UI.main
UI.quit

It's not like Ruby, it's terrible code...

@rubyFeedback
Copy link
Collaborator

rubyFeedback commented Mar 6, 2023

very few people seem to be using the pre-release version

This may be because it can be a hassle to test things that are unstable or semi-stable.

Personally I actually never (or rather almost never) use pre-release versions. I much prefer using
"stable" releases but keep on updating within that tag, if possible. So for instance:

Version: 10.1
Version: 10.2
Version: 10.3

and so forth.

t's not like Ruby, it's terrible code...

Yeah. C. :)

Ruby is like a prettier syntactic sugar over C at the end of the day.

I should have learned C first - after having learned Ruby, C seems so messy in comparison ...

@AndyObtiva
Copy link
Collaborator Author

Thank you very much for the quick response @kojix2 . I will try to run the code on my machine to support it in Glimmer DSL for LibUI.

It's not like Ruby, it's terrible code...

It is great code for the middle architectural layer (binding) between high-level Ruby code and low-level C code. Thanks again. I will test the code when I get a chance and close this issue again if it ends up working for me.

@AndyObtiva
Copy link
Collaborator Author

OK, the code works, and I got it working in Glimmer DSL for LibUI locally too.

Thanks again.

I am closing this.

@AndyObtiva
Copy link
Collaborator Author

AndyObtiva commented Mar 9, 2023

One more thing. @kojix2 could you please provide code examples of setting selection on a table (LibUI.table_set_selection), including both:

  • an example of programmatically setting a single-row selection in a zero or one selection-mode table
  • an example of programmatically setting a multiple-row selection in a zero or many selection-mode table

And, if you could cover the following with an example (just in case), it would be helpful too:

  • LibUI.table_header_set_sort_indicator
  • LibUI.table_header_set_visible
  • LibUI.table_header_sort_indicator
  • LibUI.table_header_visible

@AndyObtiva AndyObtiva reopened this Mar 9, 2023
@AndyObtiva
Copy link
Collaborator Author

OK, I figured out how to set selection by first building a selection struct:

          ts = ::LibUI::FFI::TableSelection.malloc
          ts.NumRows = value.is_a?(Array) ? value.size : 1
          ts.Rows = [value].flatten.pack('i*')

So, I'm good for now as far as selection.

I already made a release for Glimmer DSL for LibUI that supports Table Selection API in version 0.0.7:
https://rubygems.org/gems/glimmer-dsl-libui/versions/0.7.0

There is a new example too:
https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/examples/basic_table_selection.rb

Screen Shot 2023-03-11 at 5 51 47 PM

I need to add support for the table column listener next:
LibUI.table_header_on_clicked

@kojix2
Copy link
Owner

kojix2 commented Mar 12, 2023

I tried a new example.
It's amazing!


ChatGPT's solution appears to be incorrect and has been removed!

@kojix2
Copy link
Owner

kojix2 commented Mar 12, 2023

I think the following is one solution. It is not that good, though.

59f757c

In the new version of Fiddle

font_descriptor = UI::FFI::FontDescriptor.malloc(Fiddle::RUBY_FREE)

is supported. However, this may not work with the old Fiddle that is included with older Ruby.

@kojix2
Copy link
Owner

kojix2 commented Mar 13, 2023

I see. uiTableSelection

typedef struct uiTableSelection uiTableSelection;
struct uiTableSelection
{
	int NumRows; //! < Number of selected rows.
	int *Rows; //! < Array containing selected row indices, NULL on empty selection.
};

is only a part of the allocated memory.
Metadata is allocated before Rows. uiFreeTableSelection frees this metadata.

If you try to free a FFI::TableSelection allocated by Ruby using uiFreeTableSelection, you will get an error, which seems to be caused by the metadata not existing.

@AndyObtiva
Copy link
Collaborator Author

AndyObtiva commented Mar 13, 2023

I definitely got an error every time I tried to free the table selection struct memory. If you could figure that out too, that would be great.

Could it be that the LibUI table automatically frees the memory of the table selection struct as soon as we set the selection on the table? If that is true, then perhaps that is the reason why we can’t free the memory afterwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants