Glimmer DSL for LibUI 0.6.1 has been released with support for table lazy loading via Enumerator (or Enumerator::Lazy). As a result, the table control can now handle millions of rows and renders instantly without waiting for all data to be loaded given that it is loaded lazily as the user scrolls through the table. That enables applications with a lot of data (or with data that needs to be loaded/generated) to start instantly. A new example, Lazy Table (4 versions), has been included to demonstrate table lazy loading (read on to learn more about it). Of course, from a usability and user experience point of view, it might still not be a good idea to display millions of rows, yet to display only a few via pagination, like by using the Refined Table custom control that was released and blogged about a while ago.
Glimmer DSL for LibUI 0.6.0 was a final release that included many changes implemented as pre versions previously, including a new C libui version (libui-ng Nov 13, 2022), which includes some low-level fixes and new features for the libui GUI toolkit.
Change Log (both 0.6.1 & 0.6.0):
- examples/lazy_table.rb (4 versions) table lazy loading with a million rows via Enumerator or Enumerator::Lazy to enable instant app startup time
- Support table cell_rows implicit data-binding to a collection of models (only supported an array of arrays before in implicit data-binding)
- Upgrade to libui Ruby gem version 0.1.0.pre.0, which includes a newer C libui alpha release (libui-ng Nov 13, 2022)
- Support table cell_rows Enumerator or Enumerator::Lazy value to do lazy loading of data upon display of rows instead of immediate loading of all table data, thus improving performance of table initial render for very large datasets
- Fix issue with table progress_bar_column not getting updated successfully on Windows if there were dual or triple columns before it.
- Fix issue with table progress_bar_column not getting updated successfully on Windows if data-binding table to an array of models instead of an array of arrays
- Fix issue with table checkbox_column checkbox editing not working in Mac by including a new C libui-ng release
- Fix issue with table checkbox_text_column checkbox editing not working in Mac or Windows by including a new C libui-ng release
- Update examples/basic_table_checkbox.rb to enable editing checkbox values
- Update examples/basic_table_checkbox_text.rb to enable editing checkbox/text values
- [final] Optimize table scrolling performance when having many rows and columns (prevent recalculation of expanded_cell_rows on every cell evaluation). Resolve: #38
- [final] refined_table pagination: false option to disable pagination, but keep filtering.
- [final] Fix issue with Glimmer::LibUI::interpret_color support for [r, g, b, a] Array-based colors, returning [r, g, b] only without alpha value
- [final] Fix issue "Cannot add rows to a table that started empty": #36
- [final] window #open method as alias to #show
- [final] window #focused? read-only property
- [final] Document window on_focus_changed listener
- [final] Update examples/basic_child_window.rb to demo on_focus_changed and focused?
- [final] open_folder support
- [final] examples/control_gallery.rb now includes an "Open Folder" File menu item
Lazy Table example:
# From: https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#lazy-table | |
require 'glimmer-dsl-libui' | |
class LazyTable | |
Contact = Struct.new(:name, :email, :phone, :city, :state) | |
# Extending Enumerator enables building a collection generator in an encapsulated maintainable fashion | |
class ContactGenerator < Enumerator | |
NAMES_FIRST = %w[ | |
Liam Noah William James Oliver Benjamin Elijah Lucas Mason Logan Alexander Ethan Jacob Michael Daniel Henry Jackson Sebastian | |
Aiden Matthew Samuel David Joseph Carter Owen Wyatt John Jack Luke Jayden Dylan Grayson Levi Isaac Gabriel Julian Mateo | |
Anthony Jaxon Lincoln Joshua Christopher Andrew Theodore Caleb Ryan Asher Nathan Thomas Leo Isaiah Charles Josiah Hudson | |
Christian Hunter Connor Eli Ezra Aaron Landon Adrian Jonathan Nolan Jeremiah Easton Elias Colton Cameron Carson Robert Angel | |
Maverick Nicholas Dominic Jaxson Greyson Adam Ian Austin Santiago Jordan Cooper Brayden Roman Evan Ezekiel Xaviar Jose Jace | |
Jameson Leonardo Axel Everett Kayden Miles Sawyer Jason Emma Olivia Bartholomew Corey Danielle Eva Felicity | |
] | |
NAMES_LAST = %w[ | |
Smith Johnson Williams Brown Jones Miller Davis Wilson Anderson Taylor George Harrington Iverson Jackson Korby Levinson | |
] | |
CITIES = [ | |
'Bellesville', 'Lombardia', 'Steepleton', 'Deerenstein', 'Schwartz', 'Hollandia', 'Saint Pete', 'Grandville', 'London', | |
'Berlin', 'Elktown', 'Paris', 'Garrison', 'Muncy', 'St Louis', | |
] | |
STATES = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA', | |
'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', | |
'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', | |
'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', | |
'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY'] | |
def initialize(contact_count) | |
# Make sure to pass super constructor size argument as it gets used by Glimmer DSL for LibUI | |
# to determine the number of rows in the table before generating all its data | |
super(contact_count) do |yielder| | |
contact_count.times do |index| | |
# Data will get lazy loaded into the table as the user scrolls through. | |
# After data is built, it is cached long-term, till updating table `cell_rows`. | |
yielder << contact_for(index) | |
end | |
end | |
end | |
def contact_for(index) | |
number = index + 1 | |
first_name = NAMES_FIRST.sample | |
last_name = NAMES_LAST.sample | |
phone = 10.times.map { rand(10) }.yield_self { |numbers| [numbers[0..2], numbers[3..5], numbers[6..9]].map(&:join).join('-') } | |
city = CITIES.sample | |
state = STATES.sample | |
Contact.new("#{first_name} #{last_name}", "#{first_name.downcase}#{number}@#{last_name.downcase}.com", phone, city, state) | |
end | |
end | |
include Glimmer::LibUI::Application | |
body { | |
window("1,000,000 Lazy Loaded Contacts", 600, 700) { | |
margined true | |
vertical_box { | |
table { | |
text_column('Name') | |
text_column('Email') | |
text_column('Phone') | |
text_column('City') | |
text_column('State') | |
cell_rows ContactGenerator.new(1_000_000) | |
} | |
} | |
} | |
} | |
end | |
LazyTable.launch |
# From: https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#lazy-table | |
require 'glimmer-dsl-libui' | |
class LazyTable | |
Contact = Struct.new(:name, :email, :phone, :city, :state) | |
# Extending Enumerator::Lazy enables building a collection generator in an encapsulated maintainable fashion | |
class ContactGenerator < Enumerator::Lazy | |
NAMES_FIRST = %w[ | |
Liam Noah William James Oliver Benjamin Elijah Lucas Mason Logan Alexander Ethan Jacob Michael Daniel Henry Jackson Sebastian | |
Aiden Matthew Samuel David Joseph Carter Owen Wyatt John Jack Luke Jayden Dylan Grayson Levi Isaac Gabriel Julian Mateo | |
Anthony Jaxon Lincoln Joshua Christopher Andrew Theodore Caleb Ryan Asher Nathan Thomas Leo Isaiah Charles Josiah Hudson | |
Christian Hunter Connor Eli Ezra Aaron Landon Adrian Jonathan Nolan Jeremiah Easton Elias Colton Cameron Carson Robert Angel | |
Maverick Nicholas Dominic Jaxson Greyson Adam Ian Austin Santiago Jordan Cooper Brayden Roman Evan Ezekiel Xaviar Jose Jace | |
Jameson Leonardo Axel Everett Kayden Miles Sawyer Jason Emma Olivia Bartholomew Corey Danielle Eva Felicity | |
] | |
NAMES_LAST = %w[ | |
Smith Johnson Williams Brown Jones Miller Davis Wilson Anderson Taylor George Harrington Iverson Jackson Korby Levinson | |
] | |
CITIES = [ | |
'Bellesville', 'Lombardia', 'Steepleton', 'Deerenstein', 'Schwartz', 'Hollandia', 'Saint Pete', 'Grandville', 'London', | |
'Berlin', 'Elktown', 'Paris', 'Garrison', 'Muncy', 'St Louis', | |
] | |
STATES = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA', | |
'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', | |
'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', | |
'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', | |
'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY'] | |
def initialize(contact_count) | |
# Make sure to pass super constructor size (2nd) argument as it gets used by Glimmer DSL for LibUI | |
# to determine the number of rows in the table before generating all its data | |
super(contact_count.times, contact_count) do |yielder, index| | |
# Data will get lazy loaded into the table as the user scrolls through. | |
# After data is built, it is cached long-term, till updating table `cell_rows`. | |
yielder << contact_for(index) | |
end | |
end | |
def contact_for(index) | |
number = index + 1 | |
first_name = NAMES_FIRST.sample | |
last_name = NAMES_LAST.sample | |
phone = 10.times.map { rand(10) }.yield_self { |numbers| [numbers[0..2], numbers[3..5], numbers[6..9]].map(&:join).join('-') } | |
city = CITIES.sample | |
state = STATES.sample | |
Contact.new("#{first_name} #{last_name}", "#{first_name.downcase}#{number}@#{last_name.downcase}.com", phone, city, state) | |
end | |
end | |
include Glimmer::LibUI::Application | |
body { | |
window("1,000,000 Lazy Loaded Contacts", 600, 700) { | |
margined true | |
vertical_box { | |
table { | |
text_column('Name') | |
text_column('Email') | |
text_column('Phone') | |
text_column('City') | |
text_column('State') | |
cell_rows ContactGenerator.new(1_000_000) | |
} | |
} | |
} | |
} | |
end | |
LazyTable.launch |
3 comments:
I tried the sample code on both Linux and Windows, on Linux no problem but on Windows I get C:/Ruby/lib/ruby/gems/3.1.0/gems/glimmer-dsl-libui-0.6.1/lib/glimmer/libui/control_proxy/table_proxy.rb:515:in `block in apply_windows_fix': undefined method `<<' for #:each> (NoMethodError)
@cell_rows << new_row
^^
from C:/Ruby/lib/ruby/gems/3.1.0/gems/glimmer-dsl-libui-0.6.1/lib/glimmer/libui.rb:161:in `block in queue_main'
from C:/Ruby/lib/ruby/3.1.0/fiddle/closure.rb:45:in `call'
from C:/Ruby/lib/ruby/gems/3.1.0/gems/libui-0.1.0.pre.0-x64-mingw/lib/libui/ffi.rb:20:in `call'
from C:/Ruby/lib/ruby/gems/3.1.0/gems/libui-0.1.0.pre.0-x64-mingw/lib/libui/ffi.rb:20:in `uiMain'
from C:/Ruby/lib/ruby/gems/3.1.0/gems/libui-0.1.0.pre.0-x64-mingw/lib/libui/libui_base.rb:46:in `public_send'
from C:/Ruby/lib/ruby/gems/3.1.0/gems/libui-0.1.0.pre.0-x64-mingw/lib/libui/libui_base.rb:46:in `block (2 levels) in '
from C:/Ruby/lib/ruby/gems/3.1.0/gems/glimmer-dsl-libui-0.6.1/lib/glimmer/libui/control_proxy/window_proxy.rb:69:in `show'
from C:/Ruby/lib/ruby/gems/3.1.0/gems/glimmer-dsl-libui-0.6.1/lib/glimmer/libui/custom_window.rb:59:in `show'
from (eval):7:in `launch'
from C:/Box Sync/ruby_werk/glimmer/lazy_loading1.rb:76:in `'
Thank you for reporting.
I just confirmed the issue and fixed it in version 0.6.2:
https://rubygems.org/gems/glimmer-dsl-libui/versions/0.6.2
And now it works ! Thanks and a very nice job ! Finally a Ruby GUI that works without dependencies ! Can't wait to see the next version and features.
Post a Comment