kojix2 / LibUI

A portable GUI library for Ruby
MIT License
208 stars 10 forks source link

Editable checkbox columns do not fire SetCellValue on checking/unchecking on the Mac #28

Closed AndyObtiva closed 3 years ago

AndyObtiva commented 3 years ago

Please try this example and let me know if you think this is a bug in the C libui or if there is a reason why SetCellValue does not fire:

# frozen_string_literal: true

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[0 cat 1 meow],
  %w[0 dog 1 woof],
  %w[1 checken 0 cock-a-doodle-doo],
  %w[1 hourse 1 neigh],
  %w[0 cow 0 moo]
]

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

model_handler = UI::FFI::TableModelHandler.malloc
model_handler.NumColumns   = rbcallback(4) { 2 }
model_handler.ColumnType = rbcallback(4, [1, 1, 4]) do | _mh, _m, column|
  column.odd? ? 0 : 2 # uiTableValueTypeInt : uiTableValueTypeString
end
model_handler.NumRows      = rbcallback(4) { 5 }
model_handler.CellValue    = rbcallback(1, [1, 1, 4, 4]) do |_, _, row, column|
  if column.odd?
    UI.new_table_value_string(data[row][column])
  else
    UI.new_table_value_int(data[row][column].to_i)
  end
end
model_handler.SetCellValue = rbcallback(0, [1, 1, 4, 4, 1]) do |_, _, row, column, val|
  puts [row, column, val].inspect
end

model = UI.new_table_model(model_handler)

table_params = UI::FFI::TableParams.malloc
table_params.Model = model
table_params.RowBackgroundColorModelColumn = -1

table = UI.new_table(table_params)
UI.table_append_checkbox_text_column(table, 'Animal', 0, -2, 1, -2)
UI.table_append_checkbox_text_column(table, 'Description', 2, -2, 3, -2)

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

Update: I added the ColumnType code you suggested, which was not needed on the Mac where I first ran the code, but is required on Linux. Update 2: I had the problem on the Mac (I reported before I got a chance to test on Linux, on which it worked when I later tested)

kojix2 commented 3 years ago

Hmm. I don't know if this is enough, but at least you need to specify the ColumnType.

model_handler.ColumnType = rbcallback(4, [1, 1, 4]) do | _mh, _m, column|
  column.odd? ? 0 : 2 # uiTableValueTypeString : uiTableValueTypeInt
end 
AndyObtiva commented 3 years ago

I added that code and it still does not fire SetCellValue upon editing checkboxes, but it does fire when editing text.

I will look into C libui to see if there is any useful information regarding table checkbox editing.

Thanks

AndyObtiva commented 3 years ago

The documentation of the Golang LibUI library clearly says: "AppendCheckboxColumn appends a column to t that contains a checkbox that the user can interact with (assuming the checkbox is editable). SetCellValue will be called with checkboxModelColumn as the column in this case."

I will keep digging.

AndyObtiva commented 3 years ago

I get the same issue with this example (using checkbox column instead of the dual-checkbox-text column):

# frozen_string_literal: true

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 = [
  [0, 'cat', 'meow'],
  [0, 'dog', 'woof'],
  [1, 'checken', 'cock-a-doodle-doo'],
  [1, 'hourse', 'neigh'],
  [0, 'cow', 'moo']
]

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

model_handler = UI::FFI::TableModelHandler.malloc
model_handler.NumColumns   = rbcallback(4) { 2 }
model_handler.ColumnType = rbcallback(4, [1, 1, 4]) do | _mh, _m, column|
  case column
  when 0
    2
  else
    0
  end
end
model_handler.NumRows      = rbcallback(4) { 5 }
model_handler.CellValue    = rbcallback(1, [1, 1, 4, 4]) do |_, _, row, column|
  case column
  when 0
    UI.new_table_value_int(data[row][column].to_i)
  else
    UI.new_table_value_string(data[row][column])
  end
end
model_handler.SetCellValue = rbcallback(0, [1, 1, 4, 4, 1]) do |_, _, row, column, val|
  puts [row, column].inspect
  case column
  when 0
    data[row][column] = UI.table_value_int(val).to_i
  else
    data[row][column] = UI.table_value_string(val).to_s
  end
end

model = UI.new_table_model(model_handler)

table_params = UI::FFI::TableParams.malloc
table_params.Model = model
table_params.RowBackgroundColorModelColumn = -1

table = UI.new_table(table_params)
UI.table_append_checkbox_column(table, 'Selected', 0, -2)
UI.table_append_text_column(table, 'Animal', 1, -2)
UI.table_append_text_column(table, 'Description', 2, -2)

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

Before reporting to the C libui project, I wondered if you perhaps knew how to replicate the same example in C to fully confirm the bug is not in Ruby and provide an example to report to C libui (I think you would be the better person to report it in that case)

kojix2 commented 3 years ago

I ran your code, and you're right, SetCellValue is not firing. That is strange. I don't have time today, so I'll look into it after tomorrow.

kojix2 commented 3 years ago

Looking at libui's unix/table.c, you can see signal_connect is here.

    g_signal_connect(r, "toggled", G_CALLBACK(checkboxColumnToggled), p);

So I compiled the tester with printf("FOO"); at the beginning of the checkboxColumnToggled function. checkboxColumnToggled calls SetCellValue via onEdit. So, if FOO is output, SetCellValue should be called.

However, when I click on the checkbox, FOO is not output. Interestingly, when I edit an Editable cell, the checkboxColumnToggled fires all at once. It seems that all the previous triggers have been flushed out at once.

I found the following problem reported in ui. (Parhaps you may already know this). https://github.com/andlabs/ui/issues/357

Yes, it might be a bug in C libui. Or. I've only tried it on unix, but if it's the same on macOS, it may be intentional behavior rather than a bug.

kojix2 commented 3 years ago

SetCellValue fires on Windows.

AndyObtiva commented 3 years ago

Closing this since it has been reported as a C libui issue (not a Ruby one).