Repository: kojix2/LibUI
Branch: main
Commit: 1442dcb96d4c
Files: 60
Total size: 170.3 KB
Directory structure:
gitextract_3lw4y_4b/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── doc.yml
│ ├── release-rubygems.yml
│ └── test.yml
├── .gitignore
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── examples/
│ ├── basic_area.rb
│ ├── basic_button.rb
│ ├── basic_draw_text.rb
│ ├── basic_entry.rb
│ ├── basic_table.rb
│ ├── basic_table_image.rb
│ ├── basic_window.rb
│ ├── control_gallery.rb
│ ├── date_time_picker.rb
│ ├── draw_text.rb
│ ├── font_button.rb
│ ├── gpt2_notepad.rb
│ ├── histogram.rb
│ ├── midi_player.rb
│ ├── simple_notepad.rb
│ ├── spectrum.rb
│ └── turing_pattern.rb
├── examples2/
│ ├── README.md
│ ├── button.rb
│ ├── checkbox.rb
│ ├── color_button.rb
│ ├── combobox.rb
│ ├── date_picker.rb
│ ├── editable_combobox.rb
│ ├── entry.rb
│ ├── font_button.rb
│ ├── grid.rb
│ ├── multiline_entry.rb
│ ├── password_entry.rb
│ ├── progress_bar.rb
│ ├── search_entry.rb
│ ├── slider.rb
│ ├── todo/
│ │ └── todo.md
│ └── window.rb
├── lib/
│ ├── libui/
│ │ ├── error.rb
│ │ ├── ffi.rb
│ │ ├── fiddle_patch.rb
│ │ ├── libui_base.rb
│ │ ├── utils.rb
│ │ └── version.rb
│ └── libui.rb
├── libui.gemspec
├── scripts/
│ ├── README.md
│ ├── ui_diff.sh
│ ├── ui_ffi.rb
│ └── ui_h.rb
├── test/
│ ├── libui_test.rb
│ ├── test_helper.rb
│ └── utils_test.rb
└── vendor/
├── LICENSE.md
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/doc.yml
================================================
name: doc
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: ruby
- name: Generate document
run: gem install -N yard && yard doc
- name: Publish Documentation on GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./doc
================================================
FILE: .github/workflows/release-rubygems.yml
================================================
name: Release libui to RubyGems
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
jobs:
push:
environment: release-rubygems
strategy:
fail-fast: false
matrix:
# Default Ruby version: 3.4 (used for all platforms except x64-mingw32)
include:
- os: ubuntu-latest
ruby-version: "3.4"
gem_platform: x86_64-linux
- os: ubuntu-24.04-arm
ruby-version: "3.4"
gem_platform: aarch64-linux
- os: macos-13 # Intel
ruby-version: "3.4"
gem_platform: x86_64-darwin
- os: macos-14 # Apple Silicon
ruby-version: "3.4"
gem_platform: arm64-darwin
- os: windows-latest
ruby-version: "2.7" # Ruby 2.7 is used for x64-mingw32 because newer Ruby versions default to x64-mingw-ucrt platform, causing bundler platform resolution conflicts
gem_platform: x64-mingw32
- os: windows-latest
ruby-version: "3.4"
gem_platform: x64-mingw-ucrt
runs-on: ${{ matrix.os }}
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
env:
GEM_PLATFORM: ${{ matrix.gem_platform }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
- name: Show versions
run: |
ruby -v
gem -v
bundle -v
shell: bash
- name: Update RubyGems for Windows Ruby 2.7 (enable 'gem exec')
if: ${{ matrix.gem_platform == 'x64-mingw32' }}
run: |
gem update --system 3.4.22
gem --version
shell: bash
- name: Prepare vendored binary (bundle exec rake vendor:auto)
run: |
bundle exec rake vendor:auto
shell: bash
- name: Release gem to RubyGems.org
uses: rubygems/release-gem@v1
================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
- push
- pull_request
- workflow_dispatch
jobs:
build:
name: ${{ matrix.runner }} Ruby ${{ matrix.ruby }}
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-latest
- ubuntu-24.04-arm
- macos-15-intel
- macos-latest
- windows-latest
ruby: ["2.7", "3.2", "3.3", "3.4", "4.0"]
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Install dependencies
run: bundle install
- name: Download libui shared libraries
run: bundle exec rake vendor:auto
- name: Rake test (XVFB)
uses: coactions/setup-xvfb@v1
with:
run: bundle exec rake test
================================================
FILE: .gitignore
================================================
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.lock
/vendor/bundle
/vendor/*libui*
build.log
# gpt2 exmaple
*.onnx
gpt2.bin
gpt2.i2w
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
# Specify your gem's dependencies in libui.gemspec
gemspec
gem 'minitest'
gem 'rake'
gem 'rubyzip'
# group :development do
# gem 'chunky_png'
# gem 'numo-narray'
# end
================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)
Copyright (c) 2020-present kojix2
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# LibUI
[](https://github.com/kojix2/LibUI/actions/workflows/test.yml)
[](https://badge.fury.io/rb/libui)
<a href="https://github.com/AndyObtiva/glimmer-dsl-libui"><img alt="glimmer-dsl-libui" src="https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-logo-hi-res.svg" width="50" height="50" align="right"></a>
[](https://github.com/kojix2/libui-ng/actions/workflows/pre-build.yml)
[](https://tokei.kojix2.net/github/kojix2/LibUI)
LibUI is a Ruby wrapper for libui family.
:rocket: [libui-ng](https://github.com/libui-ng/libui-ng) - A cross-platform portable GUI library
:wrench: [libui-dev](https://github.com/petabyt/libui-dev) - Native UI library for C - with some extras
:radio_button: [libui](https://github.com/andlabs/libui) - Original version by andlabs.
## Installation
```sh
gem install libui # --pre
```
- The gem package includes the libui-ng shared library for Windows, Mac, and Linux.
- Namely `libui.x64.dll`/`libui.x86.dll`, `libui.x86_64.dylib`/`libui.arm64.dylib`, or `libui.x86_64.so`/`libui.aarch64.so`.
- No dependencies required.
- The libui gem uses the standard Ruby library [Fiddle](https://github.com/ruby/fiddle) to call C functions.
| Windows | Mac | Linux |
| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| <img src="https://user-images.githubusercontent.com/5798442/103118046-900ea780-46b0-11eb-81fc-32626762e4df.png"> | <img src="https://user-images.githubusercontent.com/5798442/103118059-99980f80-46b0-11eb-9d12-324ec4d297c9.png"> | <img src="https://user-images.githubusercontent.com/5798442/103118068-a0bf1d80-46b0-11eb-8c5c-3bdcc3dcfb26.png"> |
Notes:
- If you are using the 32-bit (x86) version of Ruby, you need to download the 32-bit (x86) native dll. See the [Development](#development) section.
- On Windows, libui may not work due to missing DLLs. In that case, you need to install [Visual C++ Redistributable](https://docs.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist). See ([#48](https://github.com/kojix2/LibUI/issues/48))
- Users with [Raspberry Pi](https://www.raspberrypi.com/) or other platforms will need to compile the C libui library. See the [Development](#development) section.
## Usage
```ruby
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('hello world', 200, 100, 1)
button = UI.new_button('Button')
UI.button_on_clicked(button) do
UI.msg_box(main_window, 'Information', 'You clicked the button')
end
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.window_set_child(main_window, button)
UI.control_show(main_window)
UI.main
UI.quit
```
For more examples, see the [examples](https://github.com/kojix2/libui/tree/main/examples) directory.
### General Rules
Compared to the original libui library written in C:
- Method names use snake_case.
- The last argument can be omitted if it's nil.
- A block can be passed as a callback.
- The block will be converted to a Proc object and added as the last argument.
- The last argument can still be omitted when nil.
You can use [the libui-ng API documentation](https://libui-ng.github.io/libui-ng/) as a reference.
### DSLs for LibUI
LibUI is not object-oriented because it is a thin Ruby wrapper (binding) for the procedural C libui library, mirroring its API structure.
To build actual applications, it is recommended to use a DSL for LibUI, as they enable writing object-oriented code the Ruby way (instead of procedural code the C way):
- [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui)
- [libui_paradise](https://rubygems.org/gems/libui_paradise)
### Working with fiddle pointers
```ruby
require 'libui'
UI = LibUI
UI.init
```
To convert a pointer to a string:
```ruby
label = UI.new_label("Ruby")
p pointer = UI.label_text(label) # #<Fiddle::Pointer>
p pointer.to_s # Ruby
```
If you need to use C structs, you can do the following:
```ruby
font_button = UI.new_font_button
# Allocate memory
font_descriptor = UI::FFI::FontDescriptor.malloc
font_descriptor.to_ptr.free = Fiddle::RUBY_FREE
# font_descriptor = UI::FFI::FontDescriptor.malloc(Fiddle::RUBY_FREE) # fiddle 1.0.1 or higher
UI.font_button_on_changed(font_button) do
UI.font_button_font(font_button, font_descriptor)
p family: font_descriptor.Family.to_s,
size: font_descriptor.Size,
weight: font_descriptor.Weight,
italic: font_descriptor.Italic,
stretch: font_descriptor.Stretch
end
```
- Callbacks
- In Ruby/Fiddle, a C callback function is written as an object of
`Fiddle::Closure::BlockCaller` or `Fiddle::Closure`.
Be careful about Ruby's garbage collection - if the function object is collected, memory will be freed resulting in a segmentation violation when the callback is invoked.
```ruby
# Assign to a local variable to prevent it from being collected by GC.
handler.MouseEvent = (c1 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
handler.MouseCrossed = (c2 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
handler.DragBroken = (c3 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
```
### Creating a Windows executable (.exe) with OCRA
OCRA (One-Click Ruby Application) builds Windows executables from Ruby source code.
- https://github.com/larsch/ocra/
To build an exe with Ocra, include 3 DLLs from the ruby_builtin_dlls folder:
```sh
ocra examples/control_gallery.rb ^
--dll ruby_builtin_dlls/libssp-0.dll ^
--dll ruby_builtin_dlls/libgmp-10.dll ^
--dll ruby_builtin_dlls/libffi-7.dll ^
--gem-all=fiddle ^
```
Add additional options below if necessary:
```sh
--window ^
--add-all-core ^
--chdir-first ^
--icon assets\app.ico ^
--verbose ^
--output out\gallery.exe
```
## Development
```sh
git clone https://github.com/kojix2/libui
cd libui
bundle install
bundle exec rake vendor:auto
bundle exec rake test
```
### Pre-built shared libraries for libui-ng
Download pre-built libui-ng shared libraries (per your current platform):
```sh
bundle exec rake vendor:auto
```
Clean downloaded vendor files (keeps `vendor/{LICENSE,README}.md`):
```sh
bundle exec rake vendor:clean
```
### Using your own libui build
If you build libui-ng yourself, set `LIBUIDIR` to the directory containing the compiled `.so/.dylib/.dll` so LibUI can load it. You may also replace files under `vendor/` with your build if preferred. See [#46](https://github.com/kojix2/LibUI/issues/46#issuecomment-1041575792).
### Publishing gems
#### Automated Publishing
Push a version tag to automatically publish platform-specific gems:
```sh
git tag v0.2.0
git push origin v0.2.0
```
Requires `RUBYGEMS_API_KEY` repository secret with scoped API key.
#### Manual Publishing
```sh
ls vendor # check the vendor directory
rm -rf pkg # remove previously built gems
rake build_platform # build gems
# Check the contents of the gem
find pkg -name *.gem -exec sh -c "echo; echo \# {}; tar -O -f {} -x data.tar.gz | tar zt" \;
rake release_platform # publish gems
```
Windows Ruby (x64-mingw32 or x64-mingw-ucrt)
```sh
gem install rake rubyzip
GEM_PLATFORM=x64-mingw32 rake vendor:clean
GEM_PLATFORM=x64-mingw32 rake vendor:auto
GEM_PLATFORM=x64-mingw32 gem build libui.gemspec
gem push libui-0.2.0-x64-mingw32.gem
```
### libui or libui-ng
- From version 0.1.X, LibUI supports only libui-ng.
- Version 0.0.X only supports andlabs/libui.
## Contributing
Would you like to contribute to LibUI?
- Please feel free to send us your [pull requests](https://github.com/kojix2/libui/pulls).
- Small corrections, such as typo fixes, are appreciated.
- Did you find any bugs? Submit them in the [issues](https://github.com/kojix2/LibUI/issues) section!
Do you need commit rights?
- If you need commit rights to my repository or want to get admin rights and take over the project, please feel free to contact @kojix2.
- Many OSS projects become abandoned because only the founder has commit rights to the original repository.
Support libui-ng development
- Contributing to the development of libui-ng is a contribution to the entire libui community, including Ruby's LibUI.
- For example, it would be easier to release LibUI in Ruby if libui-ng could be built easily and official shared libraries could be distributed.
## Acknowledgements
This project is inspired by libui-ruby.
- https://github.com/jamescook/libui-ruby
While libui-ruby uses [Ruby-FFI](https://github.com/ffi/ffi), this gem uses [Fiddle](https://github.com/ruby/fiddle).
## License
[MIT License](https://opensource.org/licenses/MIT).
================================================
FILE: Rakefile
================================================
# frozen_string_literal: true
require 'rake/testtask'
require 'rbconfig'
require 'fileutils'
require 'zip'
require 'bundler/gem_tasks'
require_relative 'lib/libui/version'
# Configuration
COMMIT_HASH = ENV['LIBUI_NG_COMMIT_HASH'] || 'd37a4d8'
# Path constants
BUILD_DIR = 'builddir'
MESON_OUT_DIR = "#{BUILD_DIR}/meson-out"
DEBUG_DIR = 'libui/debug'
# Platform-specific configuration for shared libraries
PLATFORM_CONFIG = {
'arm64-darwin' => [
{ zip: 'macOS-arm64-shared-release.zip', src: 'builddir/meson-out/libui.dylib', dest: 'vendor/libui.arm64.dylib' }
],
'x86_64-darwin' => [
{ zip: 'macOS-x64-shared-release.zip', src: 'builddir/meson-out/libui.dylib', dest: 'vendor/libui.x86_64.dylib' }
],
'x86_64-linux' => [
{ zip: 'Ubuntu-x64-shared-release.zip', src: 'builddir/meson-out/libui.so', dest: 'vendor/libui.x86_64.so' }
],
'aarch64-linux' => [
{ zip: 'Ubuntu-arm64-shared-release.zip', src: 'builddir/meson-out/libui.so', dest: 'vendor/libui.aarch64.so' }
],
'x64-mingw32' => [
{ zip: 'Windows-x64-msvc-shared-release.zip', src: 'builddir/meson-out/libui.dll', dest: 'vendor/libui.x64.dll' }
],
'x86-mingw32' => [
{ zip: 'Windows-x86-msvc-shared-release.zip', src: 'builddir/meson-out/libui.dll', dest: 'vendor/libui.x86.dll' }
]
}.freeze
# Test configuration
Rake::TestTask.new(:test) do |t|
t.libs << 'test'
t.libs << 'lib'
t.test_files = FileList['test/**/*_test.rb']
end
task default: :test
# Utility functions
def log_message(message)
puts "[Rake] #{message}"
end
def detect_platform_config_key
# Environment variable override
if ENV['GEM_PLATFORM']
platform_str = ENV['GEM_PLATFORM']
return platform_str if PLATFORM_CONFIG.key?(platform_str)
end
current_platform = Gem::Platform.local
# Try exact match first
return current_platform.to_s if PLATFORM_CONFIG.key?(current_platform.to_s)
# Use RubyGems matching with custom Windows mingw support
PLATFORM_CONFIG.keys.find do |config_key|
config_platform = Gem::Platform.new(config_key)
# Standard RubyGems matching
if Gem::Platform.send(:match_platforms?, current_platform, [config_platform])
true
# Custom Windows mingw matching for x64 variants
elsif config_key == 'x64-mingw32' &&
current_platform.os.start_with?('mingw') &&
%w[x64 x86_64].include?(current_platform.cpu)
true
# Custom Windows mingw matching for x86 variants
elsif config_key == 'x86-mingw32' &&
current_platform.os.start_with?('mingw') &&
%w[x86 i386 i686].include?(current_platform.cpu)
true
else
false
end
end
end
def url_for_libui_ng_commit(file_name)
"https://github.com/kojix2/libui-ng/releases/download/commit-#{COMMIT_HASH}/#{file_name}"
end
def download_file(file_name, url)
log_message "Running: curl -L -o #{file_name} #{url}"
curl_status = system("curl -L -o #{file_name} #{url}")
return true if curl_status && File.exist?(file_name)
warn "Warning: Failed to download #{file_name} from #{url}"
false
end
def extract_zip_files(file_name, lib_paths)
return unless file_name.end_with?('.zip')
Zip::File.open(file_name) do |zip_file|
zip_file.each do |entry|
# Extract only exact matches for shared libraries
next unless lib_paths.include?(entry.name)
print "Extracting #{entry.name} from #{file_name}..."
# Preserve complete directory structure
target_path = entry.name
FileUtils.mkdir_p(File.dirname(target_path)) unless entry.directory?
unless entry.directory?
entry.extract(target_path) { true } # Overwrite if exists
end
puts 'done'
end
end
end
def download_from_url(lib_paths, file_name, url)
log_message "Downloading #{lib_paths} from #{url}"
download_file(file_name, url)
extract_zip_files(file_name, lib_paths)
ensure
File.delete(file_name) if File.exist?(file_name)
end
# Mid-level functions
def download_libui_ng_nightly(lib_paths, file_name)
url = url_for_libui_ng_commit(file_name)
download_from_url(lib_paths, file_name, url)
end
def download_and_place(zip_name, src, dest)
url = url_for_libui_ng_commit(zip_name)
success = download_file(zip_name, url)
unless success
warn "Error: Failed to download #{zip_name}"
exit 1
end
extract_zip_files(zip_name, [src])
FileUtils.mkdir_p File.dirname(dest)
FileUtils.cp src, dest
ensure
File.delete(zip_name) if File.exist?(zip_name)
end
# High-level processing functions
def process_config_entry(entry)
# Standard download and place for shared libraries only
download_and_place(entry[:zip], entry[:src], entry[:dest])
end
def process_platform(platform_entries)
platform_entries.each do |entry|
process_config_entry(entry)
end
end
# Platform gem building
platforms = %w[
x86_64-linux
aarch64-linux
x86_64-darwin
arm64-darwin
x64-mingw32
x86-mingw32
]
task :build_platform do
platforms.each do |platform|
sh 'gem', 'build', '--platform', platform
end
FileUtils.mkdir_p('pkg')
Dir['*.gem'].each do |file|
FileUtils.move(file, 'pkg')
end
end
task :release_platform do
Dir["pkg/libui-#{LibUI::VERSION}-*.gem"].each do |file|
sh 'gem', 'push', file
end
end
# Vendor tasks
namespace 'vendor' do
desc 'Download pre-built libraries for current platform'
task :auto do
platform_key = detect_platform_config_key
if platform_key && PLATFORM_CONFIG[platform_key]
log_message "Processing platform: #{platform_key}"
process_platform(PLATFORM_CONFIG[platform_key])
else
current_platform = Gem::Platform.local
log_message "No configuration found for current platform: #{current_platform}"
log_message "Available platforms: #{PLATFORM_CONFIG.keys.join(', ')}"
exit 1
end
ensure
# Clean up temporary directory after processing
Rake::Task['vendor:cleanup'].invoke
end
desc 'Clean vendor directory'
task :clean do
vendor_files = Dir['vendor/*'] - Dir['vendor/{LICENSE,README}.md']
vendor_files.each do |f|
FileUtils.rm_rf(f)
log_message "Removed #{f}"
end
end
# Clean up temporary directory
task :cleanup do
if Dir.exist?(BUILD_DIR)
FileUtils.rm_rf BUILD_DIR
log_message "Cleaned up #{BUILD_DIR}"
end
end
end
================================================
FILE: examples/basic_area.rb
================================================
require 'libui'
UI = LibUI
UI.init
handler = UI::FFI::AreaHandler.malloc
handler.to_ptr.free = Fiddle::RUBY_FREE
area = UI.new_area(handler)
brush = UI::FFI::DrawBrush.malloc
brush.to_ptr.free = Fiddle::RUBY_FREE
handler_draw_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _, area_draw_params|
path = UI.draw_new_path(0)
UI.draw_path_add_rectangle(path, 0, 0, 400, 400)
UI.draw_path_end(path)
brush.Type = 0
brush.R = 0.4
brush.G = 0.4
brush.B = 0.8
brush.A = 1.0
area_draw_params = UI::FFI::AreaDrawParams.new(area_draw_params)
UI.draw_fill(area_draw_params.Context, path, brush.to_ptr)
UI.draw_free_path(path)
end
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler.Draw = handler_draw_event
handler.MouseEvent = do_nothing
handler.MouseCrossed = do_nothing
handler.DragBroken = do_nothing
handler.KeyEvent = key_event
box = UI.new_vertical_box
UI.box_set_padded(box, 1)
UI.box_append(box, area, 1)
main_window = UI.new_window('Basic Area', 400, 400, 1)
UI.window_set_margined(main_window, 1)
UI.window_set_child(main_window, box)
UI.window_on_closing(main_window) do
UI.quit
1
end
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/basic_button.rb
================================================
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('hello world', 300, 200, 1)
button = UI.new_button('Button')
UI.button_on_clicked(button) do
UI.msg_box(main_window, 'Information', 'You clicked the button')
end
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.window_set_child(main_window, button)
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/basic_draw_text.rb
================================================
require 'libui'
UI = LibUI
UI.init
handler = UI::FFI::AreaHandler.malloc
handler.to_ptr.free = Fiddle::RUBY_FREE
area = UI.new_area(handler)
# Michael Ende (1929-1995)
# The Neverending Story is a fantasy novel by German writer Michael Ende,
# The English version, translated by Ralph Manheim, was published in 1983.
TITLE = 'Michael Ende (1929-1995) The Neverending Story'
str1 = \
' At last Ygramul sensed that something was coming toward ' \
'her. With the speed of lightning, she turned about, confronting ' \
'Atreyu with an enormous steel-blue face. Her single eye had a ' \
'vertical pupil, which stared at Atreyu with inconceivable malignancy. '
str2 = \
' A cry of fear escaped Bastian. '
str3 = \
' A cry of terror passed through the ravine and echoed from ' \
'side to side. Ygramul turned her eye to left and right, to see if ' \
'someone else had arrived, for that sound could not have been ' \
'made by the boy who stood there as though paralyzed with ' \
'horror. '
str4 = \
' Could she have heard my cry? Bastion wondered in alarm. ' \
"But that's not possible. "
str5 = \
' And then Atreyu heard Ygramuls voice. It was very high ' \
'and slightly hoarse, not at all the right kind of voice for that ' \
'enormous face. Her lips did not move as she spoke. It was the ' \
'buzzing of a great swarm of hornets that shaped itself into ' \
'words. '
str = ''
attr_str = UI.new_attributed_string(str)
def attr_str.append(what, color)
c = case color
when :red
[0.0, 0.5, 0.0, 0.7]
when :green
[0.5, 0.0, 0.25, 0.7]
end
color_attribute = UI.new_color_attribute(*c)
start = UI.attributed_string_len(self)
UI.attributed_string_append_unattributed(self, what)
UI.attributed_string_set_attribute(self, color_attribute, start, start + what.size)
UI.attributed_string_append_unattributed(self, "\n\n")
end
attr_str.append(str1, :green)
attr_str.append(str2, :red)
attr_str.append(str3, :green)
attr_str.append(str4, :red)
attr_str.append(str5, :green)
Georgia = 'Georgia'
handler_draw_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _, adp|
area_draw_params = UI::FFI::AreaDrawParams.new(adp)
default_font = UI::FFI::FontDescriptor.malloc
default_font.to_ptr.free = Fiddle::RUBY_FREE
default_font.Family = Georgia
default_font.Size = 13
default_font.Weight = 500
default_font.Italic = 0
default_font.Stretch = 4
params = UI::FFI::DrawTextLayoutParams.malloc
params.to_ptr.free = Fiddle::RUBY_FREE
# UI.font_button_font(font_button, default_font)
params.String = attr_str
params.DefaultFont = default_font
params.Width = area_draw_params.AreaWidth
params.Align = 0
text_layout = UI.draw_new_text_layout(params)
UI.draw_text(area_draw_params.Context, text_layout, 0, 0)
UI.draw_free_text_layout(text_layout)
end
handler.Draw = handler_draw_event
# Assigning to local variables
# This is intended to protect Fiddle::Closure from garbage collection.
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler.MouseEvent = do_nothing
handler.MouseCrossed = do_nothing
handler.DragBroken = do_nothing
handler.KeyEvent = key_event
box = UI.new_vertical_box
UI.box_set_padded(box, 1)
UI.box_append(box, area, 1)
main_window = UI.new_window(TITLE, 600, 400, 1)
UI.window_set_margined(main_window, 1)
UI.window_set_child(main_window, box)
UI.window_on_closing(main_window) do
UI.quit
1
end
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/basic_entry.rb
================================================
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('Basic Entry', 300, 50, 1)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
hbox = UI.new_horizontal_box
UI.window_set_child(main_window, hbox)
entry = UI.new_entry
UI.entry_on_changed(entry) do
puts UI.entry_text(entry).to_s
$stdout.flush # For Windows
end
UI.box_append(hbox, entry, 1)
button = UI.new_button('Button')
UI.button_on_clicked(button) do
text = UI.entry_text(entry).to_s
UI.msg_box(main_window, 'You entered', text)
0
end
UI.box_append(hbox, button, 0)
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/basic_table.rb
================================================
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 omitted
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.box_append(hbox, table, 1)
UI.control_show(main_window)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.main
UI.free_table_model(model)
UI.uninit
================================================
FILE: examples/basic_table_image.rb
================================================
# NOTE:
# This example displays images that can be freely downloaded from the Studio Ghibli website.
# https://www.ghibli.jp/works/red-turtle/
# "Please feel free to use them within the scope of common sense." Toshio Suzuki (producer)
require 'libui'
require 'chunky_png'
require 'open-uri'
UI = LibUI
UI.init
main_window = UI.new_window('The Red Turtle (2016)', 310, 350, 0)
hbox = UI.new_horizontal_box
UI.window_set_child(main_window, hbox)
IMAGES = Array.new(50) do |i|
url = format('https://www.ghibli.jp/gallery/thumb-redturtle%03d.png', i + 1)
f = URI.open(url)
canvas = ChunkyPNG::Canvas.from_io(f)
f.close
data = canvas.to_rgba_stream
width = canvas.width
height = canvas.height
image = UI.new_image(width, height)
UI.image_append(image, data, width, height, width * 4)
image
rescue StandardError => e
warn url, e.message
end
# Protects BlockCaller objects from garbage collection.
@block_callers = []
def rbcallback(*args, &block)
args << [0] if args.size == 1 # Argument types are omitted
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) { 1 }
model_handler.ColumnType = rbcallback(4) { 1 } # Image
model_handler.NumRows = rbcallback(4) { IMAGES.size }
model_handler.CellValue = rbcallback(1, [1, 1, 4, 4]) do |_, _, row, _column|
UI.new_table_value_image(IMAGES[row])
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_image_column(table, 'Directed by Michaël Dudok de Wit', -1)
UI.box_append(hbox, table, 1)
UI.control_show(main_window)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.main
UI.free_table_model(model)
IMAGES.each { |i| UI.free_image(i) }
UI.uninit
================================================
FILE: examples/basic_window.rb
================================================
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('hello world', 300, 200, 1)
UI.control_show(main_window)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.main
UI.uninit
================================================
FILE: examples/control_gallery.rb
================================================
require 'libui'
UI = LibUI
UI.init
# File menu
menu = UI.new_menu('File')
open_menu_item = UI.menu_append_item(menu, 'Open')
UI.menu_item_on_clicked(open_menu_item) do
pt = UI.open_file(MAIN_WINDOW)
puts pt unless pt.null?
end
save_menu_item = UI.menu_append_item(menu, 'Save')
UI.menu_item_on_clicked(save_menu_item) do
pt = UI.save_file(MAIN_WINDOW)
puts pt unless pt.null?
end
UI.menu_append_separator(menu)
should_quit_item = UI.menu_append_check_item(menu, 'Should Quit_')
UI.menu_item_set_checked(should_quit_item, 1)
UI.menu_append_quit_item(menu)
# onShouldQuit callback is called when the user presses the quit menu item.
UI.on_should_quit do
if UI.menu_item_checked(should_quit_item) == 1
puts 'Bye Bye (on_should_quit)'
UI.control_destroy(MAIN_WINDOW) # You have to destroy the window manually.
1 # UI.quit is automatically called in the C function onQuitClicked().
else
UI.msg_box(MAIN_WINDOW, 'Warning', 'Please check "Should Quit"')
0 # Don't quit
end
end
# Edit menu
edit_menu = UI.new_menu('Edit')
UI.menu_append_check_item(edit_menu, 'Checkable Item_')
UI.menu_append_separator(edit_menu)
disabled_item = UI.menu_append_item(edit_menu, 'Disabled Item_')
UI.menu_item_disable(disabled_item)
UI.menu_append_preferences_item(menu)
# Help menu
help_menu = UI.new_menu('Help')
UI.menu_append_item(help_menu, 'Help')
UI.menu_append_about_item(help_menu)
# Main Window
MAIN_WINDOW = UI.new_window('Control Gallery', 600, 500, 1)
UI.window_set_margined(MAIN_WINDOW, 1)
UI.window_on_closing(MAIN_WINDOW) do
puts 'Bye Bye'
UI.quit
# return 1 to destroys the window automatically.
# return 0 to keep the window. (You can destroy it manually.)
1
end
vbox = UI.new_vertical_box
UI.window_set_child(MAIN_WINDOW, vbox)
hbox = UI.new_horizontal_box
UI.box_set_padded(vbox, 1)
UI.box_set_padded(hbox, 1)
UI.box_append(vbox, hbox, 1)
# Group - Basic Controls
group = UI.new_group('Basic Controls')
UI.group_set_margined(group, 1)
UI.box_append(hbox, group, 1) # OSX bug?
inner = UI.new_vertical_box
UI.box_set_padded(inner, 1)
UI.group_set_child(group, inner)
# Button
button = UI.new_button('Button')
UI.button_on_clicked(button) do
UI.msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
end
UI.box_append(inner, button, 0)
# Checkbox
checkbox = UI.new_checkbox('Checkbox')
UI.checkbox_on_toggled(checkbox) do |ptr|
checked = UI.checkbox_checked(ptr) == 1
UI.window_set_title(MAIN_WINDOW, "Checkbox is #{checked}")
UI.checkbox_set_text(ptr, "I am the checkbox (#{checked})")
end
UI.box_append(inner, checkbox, 0)
# Label
UI.box_append(inner, UI.new_label('Label'), 0)
# Separator
UI.box_append(inner, UI.new_horizontal_separator, 0)
# Date Picker
UI.box_append(inner, UI.new_date_picker, 0)
# Time Picker
UI.box_append(inner, UI.new_time_picker, 0)
# Date Time Picker
UI.box_append(inner, UI.new_date_time_picker, 0)
# Font Button
UI.box_append(inner, UI.new_font_button, 0)
# Color Button
UI.box_append(inner, UI.new_color_button, 0)
inner2 = UI.new_vertical_box
UI.box_set_padded(inner2, 1)
UI.box_append(hbox, inner2, 1)
# Group - Numbers
group = UI.new_group('Numbers')
UI.group_set_margined(group, 1)
UI.box_append(inner2, group, 0)
inner = UI.new_vertical_box
UI.box_set_padded(inner, 1)
UI.group_set_child(group, inner)
# Spinbox
spinbox = UI.new_spinbox(0, 100)
UI.spinbox_set_value(spinbox, 42)
UI.spinbox_on_changed(spinbox) do |ptr|
puts "New Spinbox value: #{UI.spinbox_value(ptr)}"
end
UI.box_append(inner, spinbox, 0)
# Slider
slider = UI.new_slider(0, 100)
UI.box_append(inner, slider, 0)
# Progressbar
progressbar = UI.new_progress_bar
UI.box_append(inner, progressbar, 0)
UI.slider_on_changed(slider) do |ptr|
v = UI.slider_value(ptr)
puts "New Slider value: #{v}"
UI.progress_bar_set_value(progressbar, v)
end
# Group - Lists
group = UI.new_group('Lists')
UI.group_set_margined(group, 1)
UI.box_append(inner2, group, 0)
inner = UI.new_vertical_box
UI.box_set_padded(inner, 1)
UI.group_set_child(group, inner)
# Combobox
cbox = UI.new_combobox
UI.combobox_append(cbox, 'combobox Item 1')
UI.combobox_append(cbox, 'combobox Item 2')
UI.combobox_append(cbox, 'combobox Item 3')
UI.box_append(inner, cbox, 0)
UI.combobox_on_selected(cbox) do |ptr|
puts "New combobox selection: #{UI.combobox_selected(ptr)}"
end
# Editable Combobox
ebox = UI.new_editable_combobox
UI.editable_combobox_append(ebox, 'Editable Item 1')
UI.editable_combobox_append(ebox, 'Editable Item 2')
UI.editable_combobox_append(ebox, 'Editable Item 3')
UI.box_append(inner, ebox, 0)
# Radio Buttons
rb = UI.new_radio_buttons
UI.radio_buttons_append(rb, 'Radio Button 1')
UI.radio_buttons_append(rb, 'Radio Button 2')
UI.radio_buttons_append(rb, 'Radio Button 3')
UI.box_append(inner, rb, 1)
# Tab
tab = UI.new_tab
hbox1 = UI.new_horizontal_box
hbox2 = UI.new_horizontal_box
UI.tab_append(tab, 'Page 1', hbox1)
UI.tab_append(tab, 'Page 2', hbox2)
UI.tab_append(tab, 'Page 3', UI.new_horizontal_box)
UI.box_append(inner2, tab, 1)
# Text Entry
text_entry = UI.new_entry
UI.entry_set_text text_entry, 'Please enter your feelings'
UI.entry_on_changed(text_entry) do |ptr|
puts "Current textbox data: '#{UI.entry_text(ptr)}'"
end
UI.box_append(hbox1, text_entry, 1)
UI.control_show(MAIN_WINDOW)
UI.main
UI.uninit
================================================
FILE: examples/date_time_picker.rb
================================================
require 'libui'
UI = LibUI
UI.init
vbox = UI.new_vertical_box
date_time_picker = UI.new_date_time_picker
time = UI::FFI::TM.malloc
time.to_ptr.free = Fiddle::RUBY_FREE
UI.date_time_picker_on_changed(date_time_picker) do
UI.date_time_picker_time(date_time_picker, time)
p sec: time.tm_sec,
min: time.tm_min,
hour: time.tm_hour,
mday: time.tm_mday,
mon: time.tm_mon,
year: time.tm_year,
wday: time.tm_wday,
yday: time.tm_yday,
isdst: time.tm_isdst
end
UI.box_append(vbox, date_time_picker, 1)
main_window = UI.new_window('Date Time Pickers', 300, 200, 1)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.window_set_child(main_window, vbox)
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/draw_text.rb
================================================
require 'libui'
UI = LibUI
def append_with_attribute(attr_str, what, attr1, attr2)
start_pos = UI.attributed_string_len(attr_str)
end_pos = start_pos + what.length
UI.attributed_string_append_unattributed(attr_str, what)
UI.attributed_string_set_attribute(attr_str, attr1, start_pos, end_pos)
UI.attributed_string_set_attribute(attr_str, attr2, start_pos, end_pos) if attr2
end
def make_attribute_string
attr_str = UI.new_attributed_string(
"Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout objects.\n" \
'uiAttributedString lets you have a variety of attributes: '
)
attr1 = UI.new_family_attribute('Courier New')
append_with_attribute(attr_str, 'font family', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_size_attribute(18)
append_with_attribute(attr_str, 'font size', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_weight_attribute(UI::TextWeightBold)
append_with_attribute(attr_str, 'font weight', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_italic_attribute(UI::TextItalicItalic)
append_with_attribute(attr_str, 'font italicness', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_stretch_attribute(UI::TextStretchCondensed)
append_with_attribute(attr_str, 'font stretch', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_color_attribute(0.75, 0.25, 0.5, 0.75)
append_with_attribute(attr_str, 'text color', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_background_attribute(0.5, 0.5, 0.25, 0.5)
append_with_attribute(attr_str, 'text background color', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
attr1 = UI.new_underline_attribute(UI::UnderlineSingle)
append_with_attribute(attr_str, 'underline style', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ', ')
UI.attributed_string_append_unattributed(attr_str, 'and ')
attr1 = UI.new_underline_attribute(UI::UnderlineDouble)
attr2 = UI.new_underline_color_attribute(UI::UnderlineColorCustom, 1.0, 0.0, 0.5, 1.0)
append_with_attribute(attr_str, 'underline color', attr1, attr2)
UI.attributed_string_append_unattributed(attr_str, '. ')
UI.attributed_string_append_unattributed(attr_str, 'Furthermore, there are attributes allowing for ')
attr1 = UI.new_underline_attribute(UI::UnderlineSuggestion)
attr2 = UI.new_underline_color_attribute(UI::UnderlineColorSpelling, 0, 0, 0, 0)
append_with_attribute(attr_str, 'special underlines for indicating spelling errors', attr1, attr2)
UI.attributed_string_append_unattributed(attr_str, ' (and other types of errors) ')
UI.attributed_string_append_unattributed(attr_str,
'and control over OpenType features such as ligatures (for instance, ')
otf = UI.new_open_type_features
UI.open_type_features_add(otf, 'l', 'i', 'g', 'a', 0)
attr1 = UI.new_features_attribute(otf)
append_with_attribute(attr_str, 'afford', attr1, nil)
UI.attributed_string_append_unattributed(attr_str, ' vs. ')
UI.open_type_features_add(otf, 'l', 'i', 'g', 'a', 1)
attr1 = UI.new_features_attribute(otf)
append_with_attribute(attr_str, 'afford', attr1, nil)
UI.free_open_type_features(otf)
UI.attributed_string_append_unattributed(attr_str, ").\n")
UI.attributed_string_append_unattributed(attr_str,
'Use the controls opposite to the text to control properties of the text.')
attr_str
end
def on_font_changed(area)
UI.area_queue_redraw_all(area)
end
def on_combobox_selected(area)
UI.area_queue_redraw_all(area)
end
def draw_event(adp, attr_str, font_button, alignment)
area_draw_params = UI::FFI::AreaDrawParams.new(adp)
default_font = UI::FFI::FontDescriptor.malloc
default_font.to_ptr.free = Fiddle::RUBY_FREE
params = UI::FFI::DrawTextLayoutParams.malloc
params.to_ptr.free = Fiddle::RUBY_FREE
params.String = attr_str
UI.font_button_font(font_button, default_font)
params.DefaultFont = default_font
params.Width = area_draw_params.AreaWidth
params.Align = UI.combobox_selected(alignment)
text_layout = UI.draw_new_text_layout(params)
UI.draw_text(area_draw_params.Context, text_layout, 0, 0)
UI.draw_free_text_layout(text_layout)
UI.free_font_button_font(default_font)
end
UI.init
handler = UI::FFI::AreaHandler.malloc
handler.to_ptr.free = Fiddle::RUBY_FREE
handler_draw_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _area, adp|
draw_event(adp, @attr_str, @font_button, @alignment)
end
handler.Draw = handler_draw_event
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler.MouseEvent = do_nothing
handler.MouseCrossed = do_nothing
handler.DragBroken = do_nothing
handler.KeyEvent = key_event
@attr_str = make_attribute_string
main_window = UI.new_window('Text-Drawing Example', 640, 480, 1)
UI.window_set_margined(main_window, 1)
UI.window_on_closing(main_window) do
UI.quit
1
end
hbox = UI.new_horizontal_box
UI.box_set_padded(hbox, 1)
UI.window_set_child(main_window, hbox)
vbox = UI.new_vertical_box
UI.box_set_padded(vbox, 1)
UI.box_append(hbox, vbox, 0)
@font_button = UI.new_font_button
UI.font_button_on_changed(@font_button) { on_font_changed(@area) }
UI.box_append(vbox, @font_button, 0)
form = UI.new_form
UI.form_set_padded(form, 1)
UI.box_append(vbox, form, 0)
@alignment = UI.new_combobox
UI.combobox_append(@alignment, 'Left')
UI.combobox_append(@alignment, 'Center')
UI.combobox_append(@alignment, 'Right')
UI.combobox_set_selected(@alignment, 0)
UI.combobox_on_selected(@alignment) { on_combobox_selected(@area) }
UI.form_append(form, 'Alignment', @alignment, 0)
@area = UI.new_area(handler)
UI.box_append(hbox, @area, 1)
UI.control_show(main_window)
UI.main
UI.free_attributed_string(@attr_str)
UI.uninit
================================================
FILE: examples/font_button.rb
================================================
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('hello world', 300, 200, 1)
font_button = UI.new_font_button
font_descriptor = UI::FFI::FontDescriptor.malloc
font_descriptor.to_ptr.free = Fiddle::RUBY_FREE
UI.font_button_on_changed(font_button) do
UI.font_button_font(font_button, font_descriptor)
p family: font_descriptor.Family.to_s,
size: font_descriptor.Size,
weight: font_descriptor.Weight,
italic: font_descriptor.Italic,
stretch: font_descriptor.Stretch
end
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
UI.window_set_child(main_window, font_button)
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/gpt2_notepad.rb
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'libui'
require 'onnxruntime'
require 'blingfire'
require 'numo/narray'
# GPT-2 model
# Transformer-based language model for text generation.
# https://github.com/onnx/models/tree/main/text/machine_comprehension/gpt-2
Dir.chdir(__dir__) do
%w[
https://github.com/microsoft/BlingFire/raw/master/dist-pypi/blingfire/gpt2.bin
https://github.com/microsoft/BlingFire/raw/master/dist-pypi/blingfire/gpt2.i2w
https://github.com/onnx/models/raw/main/text/machine_comprehension/gpt-2/model/gpt2-lm-head-10.onnx
].each do |url|
fname = File.basename(url)
next if File.exist?(fname)
print "Downloading #{fname}..."
require 'open-uri'
File.binwrite(fname, URI.open(url).read)
puts 'done'
end
@encoder = BlingFire.load_model('gpt2.bin')
@decoder = BlingFire.load_model('gpt2.i2w')
@model = OnnxRuntime::Model.new('gpt2-lm-head-10.onnx')
end
def softmax(y)
Numo::NMath.exp(y) / Numo::NMath.exp(y).sum(1, keepdims: true)
end
def predict(a, prob: true)
outputs = @model.predict({ input1: [[a]] })
logits = Numo::DFloat.cast(outputs['output1'][0])
logits = logits[true, -1, true]
return logits.argmax unless prob
log_probs = softmax(logits)
cum_probs = log_probs.cumsum(1)
r = rand(0..cum_probs[-1]) # 0..1
(cum_probs < r).count
end
def predict_text(s, max = 30)
a = @encoder.text_to_ids(s)
max.times do
id = predict(a)
a << id
break if id == 13 # .
end
@decoder.ids_to_text(a)
end
# GUI
UI = LibUI
UI.init
main_window = UI.new_window('GPT-2 Notepad', 500, 300, 1)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
hbox = UI.new_vertical_box
UI.box_set_padded(hbox, 1)
UI.window_set_child(main_window, hbox)
bbox = UI.new_horizontal_box
UI.box_append(hbox, bbox, 0)
clear_button = UI.new_button('Clear')
write_button = UI.new_button('Continue the sentence(s)')
UI.box_append(bbox, clear_button, 1)
UI.box_append(bbox, write_button, 1)
entry = UI.new_multiline_entry
UI.box_append(hbox, entry, 1)
UI.button_on_clicked(clear_button) do
UI.multiline_entry_set_text(entry, '')
end
UI.button_on_clicked(write_button) do
s = UI.multiline_entry_text(entry).to_s
if s.empty?
UI.msg_box(main_window, 'Empty!', 'Please enter some text first.')
else
s2 = predict_text(s)
UI.multiline_entry_set_text(entry, s2)
end
end
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/histogram.rb
================================================
# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
require 'libui'
UI = LibUI
X_OFF_LEFT = 20
Y_OFF_TOP = 20
X_OFF_RIGHT = 20
Y_OFF_BOTTOM = 20
POINT_RADIUS = 5
init = UI.init
handler = UI::FFI::AreaHandler.malloc
handler.to_ptr.free = Fiddle::RUBY_FREE
histogram = UI.new_area(handler)
brush = UI::FFI::DrawBrush.malloc
brush.to_ptr.free = Fiddle::RUBY_FREE
color_button = UI.new_color_button
blue = 0x1E90FF
datapoints = []
def graph_size(area_width, area_height)
graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
[graph_width, graph_height]
end
matrix = UI::FFI::DrawMatrix.malloc
matrix.to_ptr.free = Fiddle::RUBY_FREE
def point_locations(datapoints, width, height)
xincr = width / 9.0 # 10 - 1 to make the last point be at the end
yincr = height / 100.0
data = []
datapoints.each_with_index do |dp, i|
val = 100 - UI.spinbox_value(dp)
data << [xincr * i, yincr * val]
end
data
end
def construct_graph(datapoints, width, height, should_extend)
locations = point_locations(datapoints, width, height)
path = UI.draw_new_path(0) # winding
first_location = locations[0] # x and y
UI.draw_path_new_figure(path, first_location[0], first_location[1])
locations.each do |loc|
UI.draw_path_line_to(path, loc[0], loc[1])
end
if should_extend
UI.draw_path_line_to(path, width, height)
UI.draw_path_line_to(path, 0, height)
UI.draw_path_close_figure(path)
end
UI.draw_path_end(path)
path
end
handler_draw_event = Fiddle::Closure::BlockCaller.new(
0, [1, 1, 1]
) do |_area_handler, _area, area_draw_params|
area_draw_params = UI::FFI::AreaDrawParams.new(area_draw_params)
path = UI.draw_new_path(0) # winding
UI.draw_path_add_rectangle(path, 0, 0, area_draw_params.AreaWidth, area_draw_params.AreaHeight)
UI.draw_path_end(path)
set_solid_brush(brush, 0xFFFFFF, 1.0) # white
UI.draw_fill(area_draw_params.Context, path, brush.to_ptr)
UI.draw_free_path(path)
dsp = UI::FFI::DrawStrokeParams.malloc
dsp.to_ptr.free = Fiddle::RUBY_FREE
dsp.Cap = 0 # flat
dsp.Join = 0 # miter
dsp.Thickness = 2
dsp.MiterLimit = 10 # DEFAULT_MITER_LIMIT
dashes = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE)
dsp.Dashes = dashes
dsp.NumDashes = 0
dsp.DashPhase = 0
# draw axes
set_solid_brush(brush, 0x000000, 1.0) # black
graph_width, graph_height = *graph_size(area_draw_params.AreaWidth, area_draw_params.AreaHeight)
path = UI.draw_new_path(0) # winding
UI.draw_path_new_figure(path, X_OFF_LEFT, Y_OFF_TOP)
UI.draw_path_line_to(path, X_OFF_LEFT, Y_OFF_TOP + graph_height)
UI.draw_path_line_to(path, X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
UI.draw_path_end(path)
UI.draw_stroke(area_draw_params.Context, path, brush, dsp)
UI.draw_free_path(path)
# now transform the coordinate space so (0, 0) is the top-left corner of the graph
UI.draw_matrix_set_identity(matrix)
UI.draw_matrix_translate(matrix, X_OFF_LEFT, Y_OFF_TOP)
UI.draw_transform(area_draw_params.Context, matrix)
# now get the color for the graph itself and set up the brush
# uiColorButtonColor(colorButton, &graphR, &graphG, &graphB, &graphA)
graph_r = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_g = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_b = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_a = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
UI.color_button_color(color_button, graph_r, graph_g, graph_b, graph_a)
brush.Type = 0 # solid
brush.R = graph_r[0, 8].unpack1('d')
brush.G = graph_g[0, 8].unpack1('d')
brush.B = graph_b[0, 8].unpack1('d')
# now create the fill for the graph below the graph line
path = construct_graph(datapoints, graph_width, graph_height, true)
brush.A = graph_a[0, 8].unpack1('d') / 2.0
UI.draw_fill(area_draw_params.Context, path, brush)
UI.draw_free_path(path)
# now draw the histogram line
path = construct_graph(datapoints, graph_width, graph_height, false)
brush.A = graph_a[0, 8].unpack1('d')
UI.draw_stroke(area_draw_params.Context, path, brush, dsp)
UI.draw_free_path(path)
end
# Assigning to local variables
# This is intended to protect Fiddle::Closure from garbage collection.
# See https://github.com/kojix2/LibUI/issues/8
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler.Draw = handler_draw_event
handler.MouseEvent = do_nothing
handler.MouseCrossed = do_nothing
handler.DragBroken = do_nothing
handler.KeyEvent = key_event
UI.freeInitError(init) unless init.nil?
hbox = UI.new_horizontal_box
UI.box_set_padded(hbox, 1)
vbox = UI.new_vertical_box
UI.box_set_padded(vbox, 1)
UI.box_append(hbox, vbox, 0)
UI.box_append(hbox, histogram, 1)
datapoints = Array.new(10) do
UI.new_spinbox(0, 100).tap do |datapoint|
UI.spinbox_set_value(datapoint, Random.new.rand(90))
UI.spinbox_on_changed(datapoint) do
UI.area_queue_redraw_all(histogram)
end
UI.box_append(vbox, datapoint, 0)
end
end
def set_solid_brush(brush, color, alpha)
brush.Type = 0 # solid
brush.R = ((color >> 16) & 0xFF) / 255.0
brush.G = ((color >> 8) & 0xFF) / 255.0
brush.B = (color & 0xFF) / 255.0
brush.A = alpha
brush
end
set_solid_brush(brush, blue, 1.0)
UI.color_button_set_color(color_button, brush.R, brush.G, brush.B, brush.A)
UI.color_button_on_changed(color_button) do
UI.area_queue_redraw_all(histogram)
end
UI.box_append(vbox, color_button, 0)
MAIN_WINDOW = UI.new_window('histogram example', 640, 480, 1)
UI.window_set_margined(MAIN_WINDOW, 1)
UI.window_set_child(MAIN_WINDOW, hbox)
UI.window_on_closing(MAIN_WINDOW) do
puts 'Bye Bye'
UI.quit
1
end
UI.control_show(MAIN_WINDOW)
UI.main
UI.uninit
================================================
FILE: examples/midi_player.rb
================================================
#!/usr/bin/env ruby
require 'libui'
UI = LibUI
class TinyMidiPlayer
VERSION = '0.0.1'
def initialize
UI.init
@pid = nil
@music_directory = File.expand_path(ARGV[0] || '~/Music/')
@midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
.sort_by { |path| File.basename(path) }
at_exit { stop_midi }
create_gui
end
def stop_midi
if @pid
Process.kill(:SIGKILL, @pid) if @th.alive?
@pid = nil
end
end
def play_midi
stop_midi
if @pid.nil? && @selected_file
begin
@pid = spawn "timidity #{@selected_file}"
@th = Process.detach @pid
rescue Errno::ENOENT
warn 'Timidty++ not found. Please install Timidity++.'
warn 'https://sourceforge.net/projects/timidity/'
end
end
end
def show_version(main_window)
UI.msg_box(main_window,
'Tiny Midi Player',
"Written in Ruby\n" \
"https://github.com/kojix2/libui\n" \
"Version #{VERSION}")
end
def create_gui
# loop_menu = UI.new_menu('Repeat')
# items = %w[Off One].map do |item_name|
# item = UI.menu_append_check_item(loop_menu, item_name)
# end
# items.each_with_index do |item, idx|
# UI.menu_item_on_clicked(item) do
# @repeat = idx
# (items - [item]).each do |i|
# UI.menu_item_set_checked(i, 0)
# end
# 0
# end
# end
help_menu = UI.new_menu('Help')
version_item = UI.menu_append_item(help_menu, 'Version')
UI.new_window('Tiny Midi Player', 200, 50, 1).tap do |main_window|
UI.menu_item_on_clicked(version_item) { show_version(main_window) }
UI.window_on_closing(main_window) do
UI.quit
1
end
UI.new_horizontal_box.tap do |hbox|
UI.new_vertical_box.tap do |vbox|
UI.new_button('▶').tap do |button1|
UI.button_on_clicked(button1) { play_midi }
UI.box_append(vbox, button1, 1)
end
UI.new_button('■').tap do |button2|
UI.button_on_clicked(button2) { stop_midi }
UI.box_append(vbox, button2, 1)
end
UI.box_append(hbox, vbox, 0)
end
UI.window_set_child(main_window, hbox)
UI.new_combobox.tap do |cbox|
@midi_files.each do |path|
name = File.basename(path)
UI.combobox_append(cbox, name)
end
UI.combobox_on_selected(cbox) do |ptr|
@selected_file = @midi_files[UI.combobox_selected(ptr)]
play_midi if @th&.alive?
0
end
UI.box_append(hbox, cbox, 1)
end
end
UI.control_show(main_window)
end
UI.main
UI.uninit
end
end
TinyMidiPlayer.new
================================================
FILE: examples/simple_notepad.rb
================================================
#!/usr/bin/env ruby
require 'libui'
UI = LibUI
UI.init
main_window = UI.new_window('Notepad', 500, 300, 1)
UI.window_on_closing(main_window) do
puts 'Bye Bye'
UI.quit
1
end
vbox = UI.new_vertical_box
UI.window_set_child(main_window, vbox)
entry = UI.new_non_wrapping_multiline_entry
UI.box_append(vbox, entry, 1)
UI.control_show(main_window)
UI.main
UI.uninit
================================================
FILE: examples/spectrum.rb
================================================
#!/usr/bin/env ruby
# Please play your favorite music or video on your computer
# when running this spectrum example.
require 'libui'
require 'ffi-portaudio' # https://github.com/nanki/ffi-portaudio
require 'numo/pocketfft' # https://github.com/yoshoku/numo-pocketfft
# ---------------------------------------------------------------------------- #
class FFTStream < FFI::PortAudio::Stream
def process(input, _output, frame_count, _time_info, _status_flags, _user_data)
i = Numo::Int16.cast(input.read_array_of_int16(frame_count))
@spec = (Numo::Pocketfft.rfft(i)[0..511].abs / 1000.0).to_a
:paContinue
end
def spec
@spec || [0] * 512
end
end
FFI::PortAudio::API.Pa_Initialize
input = FFI::PortAudio::API::PaStreamParameters.new
input[:device] = FFI::PortAudio::API.Pa_GetDefaultInputDevice
input[:channelCount] = 1
input[:sampleFormat] = FFI::PortAudio::API::Int16
input[:suggestedLatency] = 0
input[:hostApiSpecificStreamInfo] = nil
stream = FFTStream.new
stream.open(input, nil, 44_100, 1024)
stream.start
# ---------------------------------------------------------------------------- #
UI = LibUI
UI.init
handler = UI::FFI::AreaHandler.malloc
area = UI.new_area(handler)
brush = UI::FFI::DrawBrush.malloc.tap do |b|
b.Type = 0
b.R = 0.9
b.G = 0.2
b.B = 0.6
b.A = 1.0
end
dashes = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE)
stroke_params = UI::FFI::DrawStrokeParams.malloc.tap do |sp|
sp.Cap = UI::DrawLineCapFlat
sp.Join = UI::DrawLineJoinMiter
sp.MiterLimit = 10
sp.Dashes = dashes
sp.NumDashes = 0
sp.DashPhase = 0
sp.Thickness = 1.0
end
handler_draw_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _, area_draw_params|
UI.draw_new_path(UI::DrawFillModeWinding).then do |path|
stream.spec.each.with_index do |i, j|
UI.draw_path_new_figure(path, 10 + j, 121)
UI.draw_path_line_to(path, 10 + j, 120 - [i, 120].min)
end
UI.draw_path_end(path)
area_draw_params = UI::FFI::AreaDrawParams.new(area_draw_params)
UI.draw_stroke(area_draw_params.Context, path, brush.to_ptr, stroke_params)
UI.draw_free_path(path)
end
end
handler.Draw = handler_draw_event
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler.MouseEvent = do_nothing
handler.MouseCrossed = do_nothing
handler.DragBroken = do_nothing
handler.KeyEvent = key_event
box = UI.new_vertical_box
UI.box_set_padded(box, 1)
UI.box_append(box, area, 1)
main_window = UI.new_window('SPECTRUM', 560, 150, 1)
UI.window_set_margined(main_window, 1)
UI.window_set_child(main_window, box)
UI.window_on_closing(main_window) do
UI.quit
stream.close
::FFI::PortAudio::API.Pa_Terminate
1
end
UI.control_show(main_window)
UI.queue_main do
UI.timer(100) do
UI.area_queue_redraw_all(area)
1
end
end
UI.main
UI.uninit
================================================
FILE: examples/turing_pattern.rb
================================================
#!/usr/bin/env ruby
# https://en.wikipedia.org/wiki/Turing_pattern
#
# > The Turing pattern is a concept introduced by English mathematician Alan Turing
# in a 1952 paper titled "The Chemical Basis of Morphogenesis" which describes
# how patterns in nature, such as stripes and spots, can arise naturally and
# autonomously from a homogeneous, uniform state.
#
# > In his classic paper, Turing examined the behaviour of a system in which two
# diffusible substances interact with each other, and found that such a system
# is able to generate a spatially periodic pattern even from a random or almost
# uniform initial condition. Turing hypothesized that the resulting wavelike
# patterns are the chemical basis of morphogenesis.
# Studies of Turing pattern formation in zebrafish skin (2021)
# https://doi.org/10.1098/rsta.2020.0274
#
# > An unexpected discovery was made regarding the mechanism responsible for
# cell–cell interactions. When isolated melanophores and xanthophores were
# co-cultured in vitro, the cells were found to repel each other, but the signal
# was transmitted by short protrusion extending from the xanthophores. Regarding
# distant interactions, Hamada et al. found that long cell protrusions extending
# from the melanophores were involved (figure 3a–c). Interestingly, neither
# short- nor long-range interactions are mediated by ‘diffusion’. Therefore,
# strictly speaking, pattern formation does not occur by a reaction–diffusion
# system. However, mathematically, it can be considered as a homologous
# phenomenon, because the protrusions with two different lengths mimic the role
# of two different molecules with different diffusion coefficients in a
# reaction–diffusion system.
#
# > What established the pattern is not the shading of the chemicals, but the
# distribution of cells that behave autonomously. Another major difference is
# that long-distance signalling is conveyed directly by cell protrusions rather
# than by molecular diffusion.
# Perhaps Turing and his contemporaries were not right about the precise
# mechanism of pattern formation. It may be that the propagation of waves of
# cell signaling through direct contact, rather than the diffusion of morphogens,
# forms the pattern of the organism.
# However, the patterns generated by the diffusion reaction system are very
# impressive...
WAIT_TIME = 200 # Increase this number if you cannot redraw in time.
NUM_STEPS = 50 # Number of steps before being redrawn.
require 'libui'
# A matrix calculation library for Ruby like NumPy.
require 'numo/narray'
# generate png image from narray(faster)
# require 'magro'
require 'chunky_png'
module GrayScott
# shorthand
SFloat = Numo::SFloat
UInt8 = Numo::UInt8
class SFloat
alias _ inplace
end
module Utils
# To avoid mistakes
A = (1..-1).freeze
B = (0..-2).freeze
T = true
def self.laplacian2d(uv, dx)
l_uv = uv.new_zeros
l_uv[A, T]._ + uv[B, T]
l_uv[T, A]._ + uv[T, B]
l_uv[0, T]._ + uv[-1, T]
l_uv[T, 0]._ + uv[T, -1]
l_uv[B, T]._ + uv[A, T]
l_uv[T, B]._ + uv[T, A]
l_uv[-1, T]._ + uv[0, T]
l_uv[T, -1]._ + uv[T, 0]
l_uv._ - (uv * 4)
l_uv._ / (dx * dx)
l_uv
end
end
# Gray-Scott model
class Model
Dx = 0.01
# Delta t is the change in time for each iteration
Dt = 1
# diffusion rate for U
Du = 2e-5
# diffusion rate for V
Dv = 1e-5
attr_accessor :f, :k, :u, :v
attr_reader :width, :height
def initialize(width: 256, height: 256)
@width = width
@height = height
# Feed rate
@f = 0.04
# Kill rate
@k = 0.06
# concentration of U
@u = SFloat.ones height, width
# concentration of V
@v = SFloat.zeros height, width
end
def clear
u.fill 1.0
v.fill 0.0
end
def step
l_u = Utils.laplacian2d u, Dx
l_v = Utils.laplacian2d v, Dx
uvv = u * v * v
dudt = Du * l_u - uvv + f * (1.0 - u)
dvdt = Dv * l_v + uvv - (f + k) * v
u._ + (Dt * dudt)
v._ + (Dt * dvdt)
# clip is better.
@u[@u.lt 0.00001] = 0.00001
@u[@u.gt 1] = 1
@v[@v.lt 0.00001] = 0.00001
@v[@v.gt 1] = 1
end
end
module Color
module_function
def colorize(ar, color_type)
case color_type
when 'colorful'
hsv2rgb(ar)
when 'reverse-colorful'
hsv2rgb(1.0 - ar)
when 'red'
red(ar)
when 'green'
green(ar)
when 'blue'
blue(ar)
when 'reverse-red'
reverse_red(ar)
when 'reverse-green'
reverse_green(ar)
when 'reverse-blue'
reverse_blue(ar)
when 'grayscale'
grayscale(ar)
end
end
# speed
def uInt8_dstack(ar)
x = UInt8.zeros(*ar[0].shape, 3)
x[true, true, 0] = ar[0]
x[true, true, 1] = ar[1]
x[true, true, 2] = ar[2]
x
end
def hsv2rgb(h)
i = UInt8.cast(h * 6)
f = (h * 6.0) - i
p = UInt8.zeros(*h.shape)
v = UInt8.new(*h.shape).fill 255
q = (1.0 - f) * 256
t = f * 256
rgb = UInt8.zeros(*h.shape, 3)
t = UInt8.cast(t)
i = uInt8_dstack([i, i, i])
rgb[i.eq 0] = uInt8_dstack([v, t, p])[i.eq 0]
rgb[i.eq 1] = uInt8_dstack([q, v, p])[i.eq 1]
rgb[i.eq 2] = uInt8_dstack([p, v, t])[i.eq 2]
rgb[i.eq 3] = uInt8_dstack([p, q, v])[i.eq 3]
rgb[i.eq 4] = uInt8_dstack([t, p, v])[i.eq 4]
rgb[i.eq 5] = uInt8_dstack([v, p, q])[i.eq 5]
rgb
end
def red(ar)
uint8_zeros_256(0, ar)
end
def green(ar)
uint8_zeros_256(1, ar)
end
def blue(ar)
uint8_zeros_256(2, ar)
end
def reverse_red(ar)
uint8_zeros_256(0, (1.0 - ar))
end
def reverse_green(ar)
uint8_zeros_256(1, (1.0 - ar))
end
def reverse_blue(ar)
uint8_zeros_256(2, (1.0 - ar))
end
def grayscale(ar)
d = ar * 255
uInt8_dstack([d, d, d])
end
def uint8_zeros_256(ch, ar)
d = UInt8.zeros(*ar.shape, 3)
d[true, true, ch] = UInt8.cast(ar * 256)
d
end
end
end
UI = LibUI
UI.init
width = 100
height = 100
ratio = 2
pix_size = 4
pointer_size = 5
model_width = width * ratio
model_height = height * ratio
@model = GrayScott::Model.new(width: model_width, height: model_height)
@model.clear
@color_type = 'colorful'
@uv = 'v'
@running = false
# menu File
menu_file = UI.new_menu('File')
menu_file_new = UI.menu_append_item(menu_file, 'New')
UI.menu_item_on_clicked(menu_file_new) do
@model.clear
UI.area_queue_redraw_all(@area)
end
# menu File Open
menu_file_open = UI.menu_append_item(menu_file, 'Open Model')
UI.menu_item_on_clicked(menu_file_open) do
pt = UI.open_file(@main_window)
unless pt.null?
file_path = pt.to_s
begin
model = Marshal.load(File.binread(file_path))
rescue StandardError => e
UI.msg_box_error(
@main_window, '⚠️ Error',
"Failed to open file.\n" \
"#{file_path}\n" \
"#{e.message}"
)
next
end
if model.width == @model.width &&
model.height == @model.height
@model = model
UI.area_queue_redraw_all(@area)
else
UI.msg_box_error(
@main_window, '⚠️ Error',
"File shape is different.\n" \
"file: width #{model.width} height #{model.height}\n" \
"model: width #{@model.width} height #{@model.height}"
)
end
end
end
@save_file_path = nil
save_model_as_proc = proc do
pt = UI.save_file(@main_window)
unless pt.null?
@save_file_path = pt.to_s
Marshal.dump(@model, File.open(@save_file_path, 'wb'))
end
end
# menu File Save
menu_file_save = UI.menu_append_item(menu_file, 'Save Model')
UI.menu_item_on_clicked(menu_file_save) do
if @save_file_path
Marshal.dump(@model, File.open(@save_file_path, 'wb'))
else
save_model_as_proc.call
end
end
# menu File Save As
menu_file_save_as = UI.menu_append_item(menu_file, 'Save Model As')
UI.menu_item_on_clicked(menu_file_save_as, save_model_as_proc)
# menu File Quit
menu_file_quit = UI.menu_append_quit_item(menu_file)
UI.on_should_quit do
UI.control_destroy(@main_window)
1
end
# menu Help
menu_help = UI.new_menu('Help')
menu_help_about = UI.menu_append_item(menu_help, 'About')
UI.menu_item_on_clicked(menu_help_about) do
UI.msg_box(
@main_window,
'🦓 Turing Pattern 🐠',
<<~HELP_MESSAGE
How to use
(1) Click on the red area several times. Blue dots will show up.
(2) Press the "▶ START" button.
(3) Try out different parameters.
Written in Ruby
https://github.com/kojix2/LibUI
HELP_MESSAGE
)
end
# area
area_handler = UI::FFI::AreaHandler.malloc
area_handler.to_ptr.free = Fiddle::RUBY_FREE
@area = UI.new_area(area_handler)
brush = UI::FFI::DrawBrush.malloc
brush.to_ptr.free = Fiddle::RUBY_FREE
handler_draw_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _, area_draw_params|
area_draw_params = UI::FFI::AreaDrawParams.new(area_draw_params)
rgb = (GrayScott::Color.colorize(@model.public_send(@uv.to_sym), @color_type)
.cast_to(Numo::SFloat)
.inplace / 255.0)
.reshape!(height, ratio, width, ratio, 3).sum(1, 3) # Resize
.inplace / (ratio**2)
# 200 x 200 => 100 x 100 because LibUI is slow...
height.times do |y|
width.times do |x|
path = UI.draw_new_path(UI::DrawFillModeWinding)
UI.draw_path_add_rectangle(path,
pix_size * (x + 1), pix_size * (y + 1),
pix_size, pix_size)
UI.draw_path_end(path)
brush.Type = 0
brush.R = rgb[y, x, 0]
brush.G = rgb[y, x, 1]
brush.B = rgb[y, x, 2]
brush.A = 1.0
UI.draw_fill(area_draw_params.Context, path, brush.to_ptr)
UI.draw_free_path(path)
end
end
end
do_nothing = Fiddle::Closure::BlockCaller.new(0, [0]) {}
key_event = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 }
handler_mouse_event = Fiddle::Closure::BlockCaller.new(0, [1, 1, 1]) do |_, _, e|
e = UI::FFI::AreaMouseEvent.new(e)
if e.Down == 1
x = e.X * (ratio / pix_size.to_f)
y = e.Y * (ratio / pix_size.to_f)
next if x >= model_width + pointer_size ||
y >= model_height + pointer_size
yrange = ([(y - pointer_size), 0].max)..([y, (model_height - 1)].min)
xrange = ([(x - pointer_size), 0].max)..([x, (model_width - 1)].min)
@model.u[yrange, xrange] = 0.5
@model.v[yrange, xrange] = 0.5
UI.area_queue_redraw_all(@area)
end
end
area_handler.Draw = handler_draw_event
area_handler.MouseEvent = handler_mouse_event
area_handler.MouseCrossed = do_nothing
area_handler.DragBroken = do_nothing
area_handler.KeyEvent = key_event
# slide_feed
label_f = UI.new_label('f')
slider_feed = UI.new_slider(0, 100)
UI.slider_set_value(slider_feed, @model.f * 1000)
UI.slider_on_changed(slider_feed) do |ptr|
@model.f = UI.slider_value(ptr) / 1000.0
end
# slider_kill
label_k = UI.new_label('k')
slider_kill = UI.new_slider(0, 100)
UI.slider_set_value(slider_kill, @model.k * 1000)
UI.slider_on_changed(slider_kill) do |ptr|
@model.k = UI.slider_value(ptr) / 1000.0
end
# combobox preset
# The presets are taken from the following implementations:
# https://github.com/pmneila/jsexp
presets = [
{
name: 'Default',
feed: 0.037,
kill: 0.06
}, {
name: 'Solitons',
feed: 0.03,
kill: 0.062
}, {
name: 'Pulsating Solitons',
feed: 0.025,
kill: 0.06
}, {
name: 'Worms',
feed: 0.078,
kill: 0.061
}, {
name: 'Mazes',
feed: 0.029,
kill: 0.057
}, {
name: 'Holes',
feed: 0.039,
kill: 0.058
}, {
name: 'Chaos',
feed: 0.026,
kill: 0.051
}, {
name: 'Chaos and holes',
feed: 0.034,
kill: 0.056
}, {
name: 'Moving spots',
feed: 0.014,
kill: 0.054
}, {
name: 'Spots and loops',
feed: 0.018,
kill: 0.051
}, {
name: 'Waves',
feed: 0.014,
kill: 0.045
}, {
name: 'The U-Skate World',
feed: 0.062,
kill: 0.06093
}
]
cbox_presets = UI.new_combobox
presets.each do |preset|
UI.combobox_append(cbox_presets, preset[:name])
end
UI.combobox_set_selected(cbox_presets, 0)
UI.combobox_on_selected(cbox_presets) do |ptr|
preset = presets[UI.combobox_selected(ptr)]
@model.f = preset[:feed]
@model.k = preset[:kill]
UI.slider_set_value(slider_feed, preset[:feed] * 1000)
UI.slider_set_value(slider_kill, preset[:kill] * 1000)
end
# combobox u/v
uv = %w[u v]
cbox_uv = UI.new_combobox
uv.each do |s|
UI.combobox_append(cbox_uv, s)
end
UI.combobox_set_selected(cbox_uv, 1)
UI.combobox_on_selected(cbox_uv) do |ptr|
@uv = uv[UI.combobox_selected(ptr)]
UI.area_queue_redraw_all(@area) unless @running
end
# combobox color
color_type_list = %w[
colorful
reverse-colorful
red
green
blue
reverse-red
reverse-green
reverse-blue
grayscale
]
cbox_color = UI.new_combobox
color_type_list.each do |s|
UI.combobox_append(cbox_color, s)
end
UI.combobox_set_selected(cbox_color, 0)
UI.combobox_on_selected(cbox_color) do |ptr|
@color_type = color_type_list[UI.combobox_selected(ptr)]
UI.area_queue_redraw_all(@area) unless @running
end
# button start/stop
button_start = UI.new_button('▶️ START')
UI.button_on_clicked(button_start) do
@running = !@running
UI.button_set_text(button_start,
@running ? '🛑 STOP' : '▶️ START')
end
# button capture
button_capture = UI.new_button('📷')
UI.button_on_clicked(button_capture) do
image = GrayScott::Color.colorize(@model.public_send(@uv.to_sym), @color_type)
pt = UI.save_file(@main_window)
unless pt.null?
file_path = pt.to_s
if defined?(Magro::IO)
Magro::IO.imsave(file_path, image)
else
img = ChunkyPNG::Image.from_rgb_stream(model_width, model_height, image.to_string)
img.save(file_path)
end
end
end
# hbox
hbox1 = UI.new_horizontal_box
UI.box_set_padded(hbox1, 1)
UI.box_append(hbox1, cbox_presets, 0)
UI.box_append(hbox1, label_f, 0)
UI.box_append(hbox1, slider_feed, 1)
UI.box_append(hbox1, label_k, 0)
UI.box_append(hbox1, slider_kill, 1)
hbox2 = UI.new_horizontal_box
UI.box_set_padded(hbox2, 1)
UI.box_append(hbox2, cbox_uv, 0)
UI.box_append(hbox2, cbox_color, 0)
UI.box_append(hbox2, button_start, 1)
UI.box_append(hbox2, button_capture, 0)
# vbox
vbox = UI.new_vertical_box
UI.box_set_padded(vbox, 1)
UI.box_append(vbox, hbox1, 0)
UI.box_append(vbox, hbox2, 0)
UI.box_append(vbox, @area, 1)
# main window
@main_window = UI.new_window('Turing Pattern', 440, 560, 1)
UI.window_set_margined(@main_window, 1)
UI.window_set_child(@main_window, vbox)
UI.window_on_closing(@main_window) do
UI.quit
1
end
UI.control_show(@main_window)
# queue
UI.queue_main do
UI.timer(WAIT_TIME) do
next 1 unless @running # do nothing
NUM_STEPS.times do
@model.step
end
UI.area_queue_redraw_all(@area)
1 # continue
end
end
UI.main
UI.uninit
================================================
FILE: examples2/README.md
================================================
This directory (examples2/) contains code that refers to widgets and functions made available via the official libui-ng (see https://github.com/libui-ng/libui-ng) bindings (and perhaps eventually libui-dev as well).
The rationale (and objective) for this directory here serves at the least the following purposes:
- Provide standalone (working) .rb files that test individual components of
the ruby-libui suite, such as the various widgets that are part of (and supported by)
libui. For instance, button.rb should contain all code that relates to buttons,
including functionality to test the on-clicked event. Same for combobox.rb, which
should include all code specific to the combobox-widget, and so forth. Code used
for this purpose should be contained within a single .rb file only, as well as the
libui-bindings necessary to demonstrate its functionality (e. g. toplevel LibUI or
UI, the main window, usually a box to contain the widget at hand, and so forth). This
way a new user can look at the included functionality, learn from it, and quickly
adapt it to his or her use case.
- Provide explanations to any other methods and functions that are offered
by this project, even if it may not be directly related to a specific widget.
For instance, querying the current libui-version, if that is made available
by upstream code, and similar functionality that new users may find useful,
or old users may have forgotten - thus, this also serves as a quick refresher
for remembering how to use something, with a focus on specific widgets.
- Documentation and explanations within those individual .rb files. That way
new users of this project may learn the bindings made available by kojix2
more rapidly so.
Stay tuned for more updates in this regard in the long run. Right now nine
widgets have been added; expect more code in this regard over the next weeks
or months. \o/
I also invite other people to contribute changes, including documentation. Let's improve
the default experience of ruby + libui for new users, as well as provide working reference
implementations for all functionality made available in ruby-libui.
So far (this update) almost 82 "components" are verified by examples in the subdirectory examples2/ here.
I think we are close to 50% in total now or almost at 50%.
Widgets that have been added to this subdirectory include, as standalone files, in alphabetical
order:
button.rb
checkbox.rb
color_button.rb
combobox.rb
date_picker.rb
editable_combobox.rb
entry.rb
grid.rb
multiline_entry.rb
password_entry.rb
progress_bar.rb
search_entry.rb
slider.rb
window.rb
Note that this subdirectory here (examples2/) is different to examples/. The examples/ subdirectory has been created by kojix2 to test various parts of ruby-libui, including more complex use cases (see the histogram example for dynamic elements).
Available new-entries in regards to LibUI include:
- new_area
- new_attributed_string
- new_background_attribute
- new_button
- new_checkbox
- new_color_attribute
- new_color_button
- new_combobox
- new_date_picker
- new_date_time_picker
- new_editable_combobox
- new_entry
- new_family_attribute
- new_features_attribute
- new_font_button
- new_form
- new_grid
- new_group
- new_horizontal_box
- new_horizontal_separator
- new_image
- new_italic_attribute
- new_label
- new_menu
- new_multiline_entry
- new_non_wrapping_multiline_entry
- new_open_type_features
- new_password_entry
- new_progress_bar
- new_radio_buttons
- new_scrolling_area
- new_search_entry
- new_size_attribute
- new_slider
- new_spinbox
- new_stretch_attribute
- new_tab
- new_table
- new_table_model
- new_table_value_color
- new_table_value_image
- new_table_value_int
- new_table_value_string
- new_time_picker
- new_underline_attribute
- new_underline_color_attribute
- new_vertical_box
- new_vertical_separator
- new_weight_attribute
- new_window
================================================
FILE: examples2/button.rb
================================================
# ============================================================================ #
# This example (button.rb) shall demonstrate the following functionality
# (4 components), as well as their implementation-status in regards to
# this file:
#
# :new_button # [DONE]
# :button_on_clicked # [DONE]
# :button_set_text # [DONE]
# :button_text # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('button.rb', 400, 240, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_button # Create a new button here.
LibUI.box_append(hbox, _, 1) # Add the button here.
LibUI.button_set_text(_, 'This is a generic text for the button.')
puts 'The text for our button is as follows (obtained via LibUI.button_text():'
puts
puts " #{LibUI.button_text(_)}"
puts
callback_for_the_button = proc {
puts 'I was clicked.'
0 # This return value does not seem to be necessary, but we use it still, to show that one could use a return value here.
}
LibUI.button_on_clicked(_, callback_for_the_button)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/checkbox.rb
================================================
# ============================================================================ #
# This example (checkbox.rb) shall demonstrate the following functionality
# (6 components), as well as their implementation-status in regards to
# this file:
#
# :new_checkbox # [DONE]
# :checkbox_checked # [DONE]
# :checkbox_on_toggled # [DONE]
# :checkbox_set_checked # [DONE]
# :checkbox_set_text # [DONE]
# :checkbox_text # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('checkbox.rb', 400, 240, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_checkbox # Create a new checkbox here.
LibUI.box_append(hbox, _, 1) # Add it to a box.
LibUI.checkbox_set_text(_, 'This is a generic text for the checkbox.')
puts 'The text for our checkbox follows (obtained via LibUI.checkbox_text():'
puts
puts " #{LibUI.checkbox_text(_)}"
puts
callback_for_the_checkbox = proc {
puts 'I was toggled. My state is now:'
case LibUI.checkbox_checked(_)
when 1
puts ' checked (active)'
when 0
puts ' unchecked (inactive)'
end
0 # This return value does not seem to be necessary, but we use it still, to show that one could use a return value here.
}
LibUI.checkbox_on_toggled(_, callback_for_the_checkbox)
puts 'Setting the checkbox to checked (is-selected) next.'
LibUI.checkbox_set_checked(_, 1)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/color_button.rb
================================================
# ============================================================================ #
# This example (color_button.rb) shall demonstrate the following
# functionality (4 components), as well as their implementation-status
# in regards to this file:
#
# :new_color_button # [DONE]
# :color_button_color # [DONE]
# :color_button_on_changed # [DONE]
# :color_button_set_color # [DONE]
#
# See an API reference here:
#
# https://libui-ng.github.io/libui-ng/structui_editable_combobox.html
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('color_button.rb', 640, 240, 1)
# ============================================================================ #
# Get the colours and set up the brush
# uiColorButtonColor(colorButton, &graphR, &graphG, &graphB, &graphA)
# ============================================================================ #
graph_r = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_g = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_b = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
graph_a = Fiddle::Pointer.malloc(8, Fiddle::RUBY_FREE) # double
vbox = LibUI.new_vertical_box
LibUI.box_set_padded(vbox, 1)
_ = LibUI.new_color_button # Create a new color-button here.
LibUI.box_append(vbox, _, 0) # Add the combobox here. Right now the combobox is empty.
LibUI.color_button_on_changed(_) {
puts 'The colour button was changed.'
}
BRUSH = LibUI::FFI::DrawBrush.malloc
BRUSH.to_ptr.free = Fiddle::RUBY_FREE
# === set_solid_brush
def set_solid_brush(
brush = BRUSH,
color = 0x1E90FF,
alpha
)
BRUSH.Type = 0 # solid
BRUSH.R = ((color >> 16) & 0xFF) / 255.0
BRUSH.G = ((color >> 8) & 0xFF) / 255.0
BRUSH.B = (color & 0xFF) / 255.0
BRUSH.A = alpha
BRUSH
end
puts 'Set to a blue colour next.'
set_solid_brush(BRUSH, 0x1E90FF, 1.0)
LibUI.color_button_set_color(_, BRUSH.R, BRUSH.G, BRUSH.B, BRUSH.A)
LibUI.color_button_color(_, graph_r, graph_g, graph_b, graph_a) # Use LibUI.color_button_color() here.
LibUI.window_set_child(main_window, vbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/combobox.rb
================================================
# ============================================================================ #
# This example (combobox.rb) shall demonstrate the following functionality
# (9 components), as well as their implementation-status in regards to
# this file:
#
# :new_combobox [DONE]
# :combobox_append [DONE]
# :combobox_clear [DONE]
# :combobox_delete [DONE]
# :combobox_insert_at [DONE]
# :combobox_num_items [DONE]
# :combobox_on_selected [DONE]
# :combobox_selected [DONE]
# :combobox_set_selected [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
# ============================================================================ #
# === populate_the_combobox_with_this_array
#
# This method is used as a helper-method, to populate the combobox we use
# here with data (an Array).
# ============================================================================ #
def populate_the_combobox_with_this_array(
the_combobox,
this_array
)
this_array.each {|this_entry|
LibUI.combobox_append(the_combobox, this_entry) # Here we add elements to the combobox.
}
LibUI.combobox_set_selected(the_combobox, 0) # The first element is now the default selected entry.
end
main_window = LibUI.new_window('combobox.rb', 400, 240, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_combobox # Create a new combobox here.
LibUI.box_append(hbox, _, 1) # Add the combobox here. Right now the combobox is empty.
# ============================================================================ #
# Let's add data to the combobox, as an Array:
# ============================================================================ #
array = %w( matz created ruby as efficient alternative to perl )
populate_the_combobox_with_this_array(_, array)
# ============================================================================ #
# Next showing how to clear it:
# ============================================================================ #
LibUI.combobox_clear(_)
populate_the_combobox_with_this_array(_, array) # And re-populate it here.
# ============================================================================ #
# Next we delete the third entry; and then insert two new elements
# at that former position, to showcase the functionality
# combobox_delete, as well as combobox_insert_at. Note that combobox_delete
# takes two arguments, whereas combobox_insert_at takes three arguments.
# ============================================================================ #
LibUI.combobox_delete(_, 2)
LibUI.combobox_insert_at(_, 2, 'ruby')
LibUI.combobox_insert_at(_, 2, 'awesome')
# ============================================================================ #
# Show how many elements are in that combobox:
# ============================================================================ #
puts "The combobox we are using here has a total "\
"of #{LibUI.combobox_num_items(_)} elements."
LibUI.combobox_set_selected(_, 3) # Change the selected element next, to item 4.
# ============================================================================ #
# Show the selected entry next:
# ============================================================================ #
puts "The presently selected entry in our combobox is element number "\
"#{LibUI.combobox_selected(_)}."
puts 'Last but not least, try to change the combobox to a new value.'
puts 'This will trigger LibUI.combobox_on_selected().'
LibUI.combobox_on_selected(_) { |pointer|
selected = LibUI.combobox_selected(pointer)
puts "The new selection is element number `#{selected}`."
}
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/date_picker.rb
================================================
# ============================================================================ #
# === DatePicker - a widget to allow the user to enter a date
#
# This example (date_picker.rb) shall demonstrate the following functionality
# (3 components), as well as their implementation-status in regards to
# this file:
#
# :new_date_picker # [DONE]
# :date_time_picker_on_changed # [DONE]
# :date_time_picker_set_time # [NOT YET IMPLEMENTED]
#
# Documentation for the perl-API, for libui, can be seen here:
#
# https://metacpan.org/pod/LibUI::DatePicker
#
# While this is not necessarily 1:1 the ruby-API, for the most part it
# is quite similar.
#
# Note that not all functionality related to date_picker is tested for
# yet in this file. Patches to enhance functionality as well as the
# documentation are welcome.
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('date_picker.rb', 640, 240, 1)
vbox = LibUI.new_vertical_box
LibUI.box_set_padded(vbox, 1)
_ = LibUI.new_date_picker # Create a date-picker widget here.
LibUI.box_append(vbox, _, 0) # Add the font-button here.
callback_on_changed = proc { |pointer|
puts 'The time was changed.'
#puts LibUI.date_time_picker_time(pointer)
}
LibUI.date_time_picker_on_changed(_, callback_on_changed)
# uiDateTimePickerSetTime(d : UI::DateTimePicker*, tm : LibC::Tm*)
# LibUI.date_time_picker_set_time(_, Time.now)
LibUI.window_set_child(main_window, vbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/editable_combobox.rb
================================================
# ============================================================================ #
# This example (editable_combobox.rb) shall demonstrate the following
# functionality (5 components), as well as their implementation-status in
# regards to this file:
#
# :new_editable_combobox # [DONE]
# :editable_combobox_append # [DONE]
# :editable_combobox_on_changed # [DONE]
# :editable_combobox_set_text # [DONE]
# :editable_combobox_text # [DONE]
#
# See an API reference here:
#
# https://libui-ng.github.io/libui-ng/structui_editable_combobox.html
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
# ============================================================================ #
# === populate_the_combobox_with_this_array
#
# This method is used as a helper-method, to populate the combobox we use
# here with data (an Array).
# ============================================================================ #
def populate_the_combobox_with_this_array(
the_combobox,
this_array
)
this_array.each {|this_entry|
LibUI.editable_combobox_append(the_combobox, this_entry) # Here we add elements to the combobox.
}
LibUI.combobox_set_selected(the_combobox, 0) # The first element is now the default selected entry.
end
main_window = LibUI.new_window('combobox.rb', 640, 480, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_editable_combobox # Create a new combobox here.
LibUI.box_append(hbox, _, 1) # Add the combobox here. Right now the combobox is empty.
# ============================================================================ #
# Let's add data to the combobox, as an Array:
# ============================================================================ #
array = %w( matz created ruby as efficient alternative to perl )
populate_the_combobox_with_this_array(_, array)
# ============================================================================ #
# Next showing how to clear it:
# ============================================================================ #
LibUI.combobox_clear(_)
populate_the_combobox_with_this_array(_, array) # And re-populate it here.
# ============================================================================ #
# Next we delete the third entry; and then insert two new elements
# at that former position, to showcase the functionality
# combobox_delete, as well as combobox_insert_at. Note that combobox_delete
# takes two arguments, whereas combobox_insert_at takes three arguments.
# ============================================================================ #
LibUI.combobox_delete(_, 2)
LibUI.combobox_insert_at(_, 2, 'ruby')
# ============================================================================ #
# Show how many elements are in that combobox:
# ============================================================================ #
puts "The combobox we are using here has a total "\
"of #{LibUI.combobox_num_items(_)} elements."
LibUI.combobox_set_selected(_, 3) # Change the selected element next, to item 4.
# ============================================================================ #
# Show the selected entry next:
# ============================================================================ #
puts "The presently selected entry in our combobox is element number "\
"#{LibUI.combobox_selected(_)}."
puts 'Last but not least, try to change the combobox to a new value.'
puts 'This will trigger LibUI.combobox_on_selected().'
# ============================================================================ #
# Testing support for :editable_combobox_on_changed next.
# ============================================================================ #
LibUI.editable_combobox_on_changed(_) { |pointer|
selected = LibUI.combobox_selected(pointer)
puts "The new selection is element number `#{selected}`."
puts "The currently selected text is: `#{LibUI.editable_combobox_text(pointer)}`"
}
# ============================================================================ #
# As explained by kojix2, libui-ng intentionally limits editable comboboxes
# to simple text get/set functionality.
#
# Since users can freely input text in an editable combobox, the concept
# of "which item is selected" becomes ambiguous. This is why rather
# than using LibUI.combobox_set_selected(), LibUI.editable_combobox_set_text()
# is used next.
# ============================================================================ #
LibUI.editable_combobox_set_text(_, 'Testing a new default text. This will appear first.')
LibUI.editable_combobox_append(_, 'This is a black cat.') # Here we add elements to the combobox.
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/entry.rb
================================================
# ============================================================================ #
# This example (entry.rb) shall demonstrate the following functionality
# (6 components), as well as their implementation-status in regards to
# this file:
#
# :new_entry # [DONE]
# :entry_on_changed # [DONE]
# :entry_read_only # [DONE]
# :entry_set_read_only # [DONE]
# :entry_set_text # [DONE]
# :entry_text # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('entry.rb', 800, 440, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_entry # Create a new entry here.
LibUI.box_append(hbox, _, 1) # Add the entry here.
@old_entry_text = 'This is a generic text for the entry.'
LibUI.entry_set_text(_, @old_entry_text)
puts 'The entry will be set to read-only next, via '\
'LibUI.entry_set_read_only().'
LibUI.entry_set_read_only(_, 1) # We have to use 1 rather than true here, unfortunately.
puts
puts "Is this entry read-only? #{LibUI.entry_read_only(_)}"\
" # note that a 1 here means yes/true"
puts
puts 'The text for the current entry in use is as follows:'
puts
puts " → #{LibUI.entry_text(_)}"
puts
puts 'Making the entry no longer read-only next:'
puts
LibUI.entry_set_read_only(_, 0) # We have to use 1 rather than true here, unfortunately.
callback_proc = proc { |pointer|
new_text = LibUI.entry_text(pointer).to_s
puts
puts "The old entry-text was: '#{@old_entry_text}'"
puts "The new entry-text is: '#{new_text}'"
@old_entry_text = new_text
}
LibUI.entry_on_changed(_, callback_proc)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/font_button.rb
================================================
# ============================================================================ #
# This example (font_button.rb) shall demonstrate the following
# functionality (5 components), as well as their implementation-status
# in regards to this file:
#
# :new_font_button # [DONE]
# :font_button_font # [NOT YET ADDED]
# :font_button_on_changed # [DONE]
# :free_font_button_font # [NOT YET ADDED]
# :free_font_descriptor #[NOT YET ADDED]
#
# Unsure what ":load_control_ font" is.
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('font_button.rb', 640, 240, 1)
vbox = LibUI.new_vertical_box
LibUI.box_set_padded(vbox, 1)
_ = LibUI.new_font_button # Create a new font-button here.
LibUI.box_append(vbox, _, 0) # Add the font-button here.
LibUI.font_button_on_changed(_) {|entry|
puts 'The font was changed. (class '+entry.class.to_s+')'
}
LibUI.window_set_child(main_window, vbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/grid.rb
================================================
# ============================================================================ #
# This example (grid.rb) shall demonstrate the following functionality
# (8 components), as well as their implementation-status in regards to
# this file:
#
# :new_grid # [DONE]
# :grid_append # [DONE]
# :grid_insert_at # Unsure how to do this.
# :grid_padded # [DONE]
# :grid_set_padded # [DONE]
#
# API documentation can be seen here:
#
# https://libui.dev/structui_grid.html
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('grid.rb', 800, 250, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_grid # Create a new grid here.
LibUI.box_append(hbox, _, 1) # Add the grid here.
LibUI.grid_set_padded(_, 0) # Here we could toggle the padded-status.
puts 'Is the grid padded? '+LibUI.grid_padded(_).to_s+
' (1 means yes)'
button1 = LibUI.new_button('Test-Button #1')
button2 = LibUI.new_button('Test-Button #2')
button3 = LibUI.new_button('Test-Button #3')
button4 = LibUI.new_button('Test-Button #4')
left = 0
top = 0
xspan = 1
yspan = 1
hexpand = 1
halign = 1
vexpand = 1
valign = 1
# ======================================================================== #
# left, top, xspan, yspan, hexpand, halign, vexpand, valign
# 0, 0, 2, 1, 0, 1, 1, 1
# ======================================================================== #
LibUI.grid_append(
_,
button1, # This is the widget that will be added (appended) onto the grid-widget.
left,
top,
xspan,
yspan,
hexpand,
halign,
vexpand,
valign
)
left = 1
top = 0
xspan = 1
yspan = 1
hexpand = 1
halign = 1
vexpand = 1
valign = 1
# ======================================================================== #
# left, top, xspan, yspan, hexpand, halign, vexpand, valign
# 0, 0, 2, 1, 0, 1, 1, 1
# ======================================================================== #
LibUI.grid_append(
_,
button2, # This is the widget that will be added (appended) onto the grid-widget.
left,
top,
xspan,
yspan,
hexpand,
halign,
vexpand,
valign
)
left = 0
top = 1
xspan = 1
yspan = 1
hexpand = 1
halign = 1
vexpand = 1
valign = 1
# ======================================================================== #
# left, top, xspan, yspan, hexpand, halign, vexpand, valign
# 0, 1, 2, 1, 0, 1, 1, 1
# ======================================================================== #
LibUI.grid_append(
_,
button3, # This is the widget that will be added (appended) onto the grid-widget.
left,
top,
xspan,
yspan,
hexpand,
halign,
vexpand,
valign
)
left = 1
top = 1
xspan = 1
yspan = 1
hexpand = 1
halign = 1
vexpand = 1
valign = 1
# ======================================================================== #
# left, top, xspan, yspan, hexpand, halign, vexpand, valign
# 1, 1, 2, 1, 0, 1, 1, 1
# ======================================================================== #
LibUI.grid_append(
_,
button4, # This is the widget that will be added (appended) onto the grid-widget.
left,
top,
xspan,
yspan,
hexpand,
halign,
vexpand,
valign
)
if false # The next clause does not work correctly yet.
entry1 = LibUI.new_entry
# ============================================================================ #
# See: https://libui.dev/structui_grid.html#ad282fc62adbaed067699f949d619899c
#
# Arguments to LibUI.grid_insert_at() are:
#
# void uiGridInsertAt ( uiGrid * g,
# uiControl * c,
# uiControl * existing,
# uiAt at,
# int xspan,
# int yspan,
# int hexpand,
# uiAlign halign,
# int vexpand,
# uiAlign valign )
#
# ============================================================================ #
LibUI.grid_insert_at(
_,
entry1, # The widget to insert.
button3, # Our relative widget.
LibUI::AtTrailing, # at: Placement specifier in relation to existing control.
0, # xspan
1, # yspan
1,
1,
1,
1
)
end
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/multiline_entry.rb
================================================
# ============================================================================ #
# This example (multiline_entry.rb) shall demonstrate the following functionality
# (6 components), as well as their implementation-status in regards to
# this file:
#
# :multiline_entry_append # [DONE]
# :multiline_entry_on_changed
# :multiline_entry_read_only # [DONE]
# :multiline_entry_set_read_only # [DONE]
# :multiline_entry_set_text # [DONE]
# :multiline_entry_text
# :new_multiline_entry # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('entry.rb', 800, 440, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_multiline_entry # Create a new entry here.
LibUI.box_append(hbox, _, 1) # Add the entry here.
@old_entry_text = 'This is a generic text for the entry.'
LibUI.multiline_entry_set_text(_, @old_entry_text)
puts 'Appending something next.'
LibUI.multiline_entry_append(_, ' More content.')
callback_proc = proc { |pointer|
new_text = LibUI.multiline_entry_text(pointer).to_s
puts
puts "The old entry-text was: '#{@old_entry_text}'"
puts "The new entry-text is: '#{new_text}'"
@old_entry_text = new_text
}
LibUI.multiline_entry_on_changed(_, callback_proc)
# LibUI.multiline_entry_set_read_only(_, 1) # Set it read-only here.
# LibUI.multiline_entry_read_only(_) # Query the read-only way here.
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/password_entry.rb
================================================
# ============================================================================ #
# This example (password_entry.rb) shall demonstrate the following functionality
# (6 components), as well as their implementation-status in regards to
# this file:
#
# :new_entry # [DONE]
# :entry_on_changed # [DONE]
# :entry_read_only # [DONE]
# :entry_set_read_only # [DONE]
# :entry_set_text # [DONE]
# :entry_text # [DONE]
#
# Note that password-entry is a subclass of uiEntry.
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('password_entry.rb', 800, 440, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_password_entry # Create a new password-entry here.
LibUI.box_append(hbox, _, 1) # Add the password-entry here.
@old_entry_text = 'foobar' # Use a very short password here.
LibUI.entry_set_text(_, @old_entry_text)
puts 'The password-entry will be set to read-onlyn ext, via '\
'LibUI.entry_set_read_only().'
LibUI.entry_set_read_only(_, 1) # We have to use 1 rather than true here, unfortunately.
puts
puts "Is this entry read-only? #{LibUI.entry_read_only(_)}"\
" # note that a 1 here means yes/true"
puts
puts 'The text for the current entry in use is as follows:'
puts
puts " → #{LibUI.entry_text(_)}"
puts
puts 'Making the entry no longer read-only next:'
puts
LibUI.entry_set_read_only(_, 0) # We have to use 1 rather than true here, unfortunately.
callback_proc = proc { |pointer|
new_text = LibUI.entry_text(pointer).to_s
puts
puts "The old entry-text was: '#{@old_entry_text}'"
puts "The new entry-text is: '#{new_text}'"
@old_entry_text = new_text
puts
puts 'Note that this callback can be modified to'
puts 'allow for the search functionality'
puts
puts 'Interestingly the output shows the real password,'
puts 'so be careful with this.'
}
LibUI.entry_on_changed(_, callback_proc)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/progress_bar.rb
================================================
# ============================================================================ #
# This example (progress_bar.rb) shall demonstrate the following
# functionality (2 components), as well as their implementation-status
# in regards to this file:
#
# :progress_bar_set_value # [DONE]
# :progress_bar_value # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('ProgressBar', 640, 240, 1)
vbox = LibUI.new_vertical_box
LibUI.box_set_padded(vbox, 1)
_ = LibUI.new_progress_bar # Create a new progressbar here.
LibUI.progress_bar_set_value(_, 42) # Show how to set a value to a progress bar.
LibUI.box_append(vbox, _, 0.5) # Add the progressbar here.
puts 'The current value of the progress bar is: '+
LibUI.progress_bar_value(_).to_s
LibUI.window_set_child(main_window, vbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/search_entry.rb
================================================
# ============================================================================ #
# This example (search_entry.rb) shall demonstrate the following functionality
# (6 components), as well as their implementation-status in regards to
# this file:
#
# :new_entry # [DONE]
# :entry_on_changed # [DONE]
# :entry_read_only # [DONE]
# :entry_set_read_only # [DONE]
# :entry_set_text # [DONE]
# :entry_text # [DONE]
#
# Note that search-entry is a subclass of uiEntry.
#
# See also rust-documentation here:
#
# https://docs.rs/libui/latest/libui/controls/struct.SearchEntry.html
#
# Or Go documentation here:
#
# https://pkg.go.dev/github.com/andlabs/ui#NewSearchEntry
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('search_entry.rb', 800, 440, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
_ = LibUI.new_search_entry # Create a new search-entry here.
LibUI.box_append(hbox, _, 1) # Add the search-entry here.
@old_entry_text = 'This is a generic text for the search-entry.'
LibUI.entry_set_text(_, @old_entry_text)
puts 'The entry will be set to read-onlyn ext, via '\
'LibUI.entry_set_read_only().'
LibUI.entry_set_read_only(_, 1) # We have to use 1 rather than true here, unfortunately.
puts
puts "Is this entry read-only? #{LibUI.entry_read_only(_)}"\
" # note that a 1 here means yes/true"
puts
puts 'The text for the current entry in use is as follows:'
puts
puts " → #{LibUI.entry_text(_)}"
puts
puts 'Making the entry no longer read-only next:'
puts
LibUI.entry_set_read_only(_, 0) # We have to use 1 rather than true here, unfortunately.
callback_proc = proc { |pointer|
new_text = LibUI.entry_text(pointer).to_s
puts
puts "The old entry-text was: '#{@old_entry_text}'"
puts "The new entry-text is: '#{new_text}'"
@old_entry_text = new_text
puts
puts 'Note that this callback can be modified to'
puts 'allow for the search functionality'
}
LibUI.entry_on_changed(_, callback_proc)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/slider.rb
================================================
# ============================================================================ #
# This example (slider.rb) shall demonstrate the following functionality
# (8 components), as well as their implementation-status in regards to
# this file:
#
# :new_slider # [DONE]
# :slider_has_tool_tip # [DONE]
# :slider_on_changed # [DONE]
# :slider_on_released # [DONE]
# :slider_set_has_tool_tip # [DONE]
# :slider_set_range # [DONE]
# :slider_set_value # [DONE]
# :slider_value # [DONE]
#
# API documentation can be seen here:
#
# https://libui.dev/structui_slider.html
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('slider.rb', 800, 440, 1)
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
# new_slider() wants: uiNewSlider (int min, int max)
_ = LibUI.new_slider(1, 100) # Create a new slider here.
LibUI.box_append(hbox, _, 1) # Add the slider here.
# ============================================================================ #
# The default "tooltip" is the current value of the slider at hand.
# ============================================================================ #
puts 'Does this slider haver a tooltip? '+
LibUI.slider_has_tool_tip(_).to_s
callback_proc_on_changed = proc {|entry| # entry is a Fiddle::Pointer
new_value = LibUI.slider_value(entry) # Obtain the current value of the slider here.
puts 'The slider was changed. The new value is: '+new_value.to_s
}
LibUI.slider_set_has_tool_tip(_, 1) # 1 means true here
puts 'Modifying the range now from -200 to +200.'
LibUI.slider_set_range(_, -200, 200)
puts 'Setting the value of the slider to 42 now, as a new default.'
LibUI.slider_set_value(_, 42)
LibUI.slider_on_changed(_, callback_proc_on_changed)
callback_proc_on_released = proc {|entry| # entry is a Fiddle::Pointer
puts 'The slider was released.'
}
LibUI.slider_on_released(_, callback_proc_on_released)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
LibUI.quit
1
}
LibUI.main
LibUI.uninit
================================================
FILE: examples2/todo/todo.md
================================================
This file is no longer necessary - the parent directory keeps track of what is finished and what is yet-to-be-done.
================================================
FILE: examples2/window.rb
================================================
# ============================================================================ #
# This example (window.rb) shall demonstrate the following functionality
# (18 components), as well as their implementation-status in regards to
# this file:
#
# :new_window # [DONE]
# :window_borderless # [DONE]
# :window_content_size # Unsure how to use this
# :window_focused # [DONE] - returns whether or not the window is focused.
# :window_fullscreen # [DONE]
# :window_margined # [DONE]
# :window_on_closing # [DONE]
# :window_on_content_size_changed # [DONE]
# :window_on_focus_changed # [DONE]
# :window_resizeable # [DONE]
# :window_set_borderless # [DONE]
# :window_set_child # [DONE]
# :window_set_content_size # [DONE]
# :window_set_fullscreen # [DONE]
# :window_set_margined # [DONE]
# :window_set_resizeable # [DONE]
# :window_set_title # [DONE]
# :window_title # [DONE]
#
# ============================================================================ #
require 'libui'
LibUI.init # Initialize LibUI.
main_window = LibUI.new_window('window.rb', 880, 640, 1)
LibUI.window_set_title(main_window, 'TEST TITLE')
puts 'The temporary title of this window is: '+
LibUI.window_title(main_window)
LibUI.window_set_title(main_window, 'window.rb') # And restore the title here again.
LibUI.window_set_resizeable(main_window, 1) # Making it resizeable - is better, in my opinion.
LibUI.window_set_margined(main_window, 1)
puts 'Is the window margined? '+
LibUI.window_margined(main_window).to_s
puts 'The main-window will have a margin, thanks to LibUI.window_set_margined()'
puts 'Can this window be resized? '+
LibUI.window_resizeable(main_window).to_s
puts 'Setting this window to fullscreen next, by default.'
puts '(Actually, no, because this is annoying; the code for this'
puts 'is LibUI.window_set_fullscreen(main_window, 1), though.'
# LibUI.window_set_fullscreen(main_window, 1)
puts 'You can also query it via LibUI.window_fullscreen(main_window)'
hbox = LibUI.new_horizontal_box
LibUI.box_set_padded(hbox, 1)
# ============================================================================ #
# LibUI.window_set_content_size() is actually
# LibUI::FFI.uiWindowSetContentSize
#
# The code for this is:
#
# try_extern 'void uiWindowSetContentSize(uiWindow *w, int width, int height)'
#
# So it needs two arguments. Note that this can be ignored by the system
# though.
#
# More documentation for this method can be seen here:
#
# https://libui.dev/structui_window.html#a1f33b8462a999bdaf276bcdca07dfe28
#
# ============================================================================ #
_ = LibUI.new_label(
"Just testing the window-widget here.\n\n"\
"For testing-purposes this window can not be resized, "\
"which is\nNOT recommended. See LibUI.window_set_resizeable()"
) # Create a new help-text. here.
LibUI.box_append(hbox, _, 1) # Add it to a box.
puts
puts 'The window will be borderless, thanks to LibUI.window_set_borderless()'
puts 'Actually, that is not extremely useful, so we do not use it.'
LibUI.window_set_borderless(main_window, 0)
puts 'You can test whether it is borderless via LibUI.borderless()'
callback_proc = proc { |pointer|
puts '_'*80
puts 'The focus changed. This happens when the main-window'
puts 'is dragged to a new position, for instance, as well as'
puts 'on startup.'
# === window_content_size
#
# try_extern 'void uiWindowContentSize(uiWindow *w, int *width, int *height)'
#
# puts 'The window-content-size is '+
# LibUI.window_content_size(main_window, 15,15).to_s
puts '_'*80
}
LibUI.window_on_focus_changed(main_window, callback_proc)
puts 'Is the window focused? '+LibUI.window_focused(main_window).to_s
callback_on_content_size_changed = proc { |pointer|
puts 'The content-size of the main window was changed.'
}
LibUI.window_on_content_size_changed(main_window, callback_on_content_size_changed)
# ============================================================================ #
# Moves the window to the specified position.
# ============================================================================ #
# puts 'Set to a position:'
# LibUI.window_set_position(main_window, 2, 2)
LibUI.window_set_child(main_window, hbox)
LibUI.control_show(main_window)
LibUI.window_on_closing(main_window) {
# Do on-closing actions here.
LibUI.quit
1 # An Integer must be returned by this block.
}
puts 'Setting the content-size of the main window to a value of 2200, 500 next,'
puts 'to test the method called LibUI.window_set_content_size():'
LibUI.window_set_content_size(main_window, 2200, 500) # This actually works, I tested it recently.
LibUI.main
LibUI.uninit
================================================
FILE: lib/libui/error.rb
================================================
module LibUI
# base error class
class Error < StandardError; end
# LibUI shared library not found error
class LibraryNotFoundError < Error; end
# LibUI shared library load error
class LibraryLoadError < Error; end
end
================================================
FILE: lib/libui/ffi.rb
================================================
require 'fiddle/import'
require_relative 'fiddle_patch'
require_relative 'error'
module LibUI
module FFI
extend Fiddle::Importer
extend FiddlePatch
if LibUI.ffi_lib.nil?
raise LibraryNotFoundError, 'Could not find libui shared library. LibUI.ffi_lib is nil.'
elsif !File.exist?(LibUI.ffi_lib)
raise LibraryNotFoundError, "Could not find libui shared library: #{LibUI.ffi_lib}"
else
begin
dlload LibUI.ffi_lib
rescue LoadError
raise LibraryLoadError, "Could not load libui shared library: #{LibUI.ffi_lib}"
end
end
class << self
attr_reader :func_map
def try_extern(signature, *opts)
extern(signature, *opts)
rescue StandardError => e
# Do not raise error when the function is not found
# because some functions may not be available on older versions of libui.
warn "#{e.class.name}: #{e.message}"
end
def ffi_methods
@ffi_methods ||= func_map.each_key.to_a
end
end
typealias('uint32_t', 'unsigned int')
typealias('uint64_t', 'unsigned long long')
InitOptions = struct [
'size_t Size'
]
# https://github.com/andlabs/libui/blob/master/ui.h
# keep same order
try_extern 'const char *uiInit(uiInitOptions *options)'
try_extern 'void uiUninit(void)'
try_extern 'void uiFreeInitError(const char *err)'
try_extern 'void uiMain(void)'
try_extern 'void uiMainSteps(void)'
try_extern 'int uiMainStep(int wait)'
try_extern 'void uiQuit(void)'
try_extern 'void uiQueueMain(void (*f)(void *data), void *data)'
try_extern 'void uiTimer(int milliseconds, int (*f)(void *data), void *data)'
try_extern 'void uiOnShouldQuit(int (*f)(void *data), void *data)'
try_extern 'void uiFreeText(char *text)'
Control = struct [
'uint32_t Signature',
'uint32_t OSSignature',
'uint32_t TypeSignature',
'void (*Destroy)(uiControl *)',
'uintptr_t (*Handle)(uiControl *)',
'uiControl *(*Parent)(uiControl *)',
'void (*SetParent)(uiControl *, uiControl *)',
'int (*Toplevel)(uiControl *)',
'int (*Visible)(uiControl *)',
'void (*Show)(uiControl *)',
'void (*Hide)(uiControl *)',
'int (*Enabled)(uiControl *)',
'void (*Enable)(uiControl *)',
'void (*Disable)(uiControl *)'
]
try_extern 'void uiControlDestroy(uiControl *c)'
try_extern 'uintptr_t uiControlHandle(uiControl *c)'
try_extern 'uiControl *uiControlParent(uiControl *c)'
try_extern 'void uiControlSetParent(uiControl *c, uiControl *parent)'
try_extern 'int uiControlToplevel(uiControl *c)'
try_extern 'int uiControlVisible(uiControl *c)'
try_extern 'void uiControlShow(uiControl *c)'
try_extern 'void uiControlHide(uiControl *c)'
try_extern 'int uiControlEnabled(uiControl *c)'
try_extern 'void uiControlEnable(uiControl *c)'
try_extern 'void uiControlDisable(uiControl *c)'
try_extern 'uiControl *uiAllocControl(size_t n, uint32_t OSsig, uint32_t typesig, const char *typenamestr)'
try_extern 'void uiFreeControl(uiControl *c)'
try_extern 'void uiControlVerifySetParent(uiControl *c, uiControl *parent)'
try_extern 'int uiControlEnabledToUser(uiControl *c)'
try_extern 'void uiUserBugCannotSetParentOnToplevel(const char *type)'
# uiWindow
try_extern 'char *uiWindowTitle(uiWindow *w)'
try_extern 'void uiWindowSetTitle(uiWindow *w, const char *title)'
try_extern 'void uiWindowPosition(uiWindow *w, int *x, int *y)'
try_extern 'void uiWindowSetPosition(uiWindow *w, int x, int y)'
try_extern 'void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *sender, void *senderData), void *data)'
try_extern 'void uiWindowContentSize(uiWindow *w, int *width, int *height)'
try_extern 'void uiWindowSetContentSize(uiWindow *w, int width, int height)'
try_extern 'int uiWindowFullscreen(uiWindow *w)'
try_extern 'void uiWindowSetFullscreen(uiWindow *w, int fullscreen)'
try_extern 'void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *sender, void *senderData), void *data)'
try_extern 'void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *sender, void *senderData), void *data)'
try_extern 'void uiWindowOnFocusChanged(uiWindow *w, void (*f)(uiWindow *sender, void *senderData), void *data)'
try_extern 'int uiWindowFocused(uiWindow *w)'
try_extern 'int uiWindowBorderless(uiWindow *w)'
try_extern 'void uiWindowSetBorderless(uiWindow *w, int borderless)'
try_extern 'void uiWindowSetChild(uiWindow *w, uiControl *child)'
try_extern 'int uiWindowMargined(uiWindow *w)'
try_extern 'void uiWindowSetMargined(uiWindow *w, int margined)'
try_extern 'int uiWindowResizeable(uiWindow *w)'
try_extern 'void uiWindowSetResizeable(uiWindow *w, int resizeable)'
try_extern 'uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)'
# uiButton
try_extern 'char *uiButtonText(uiButton *b)'
try_extern 'void uiButtonSetText(uiButton *b, const char *text)'
try_extern 'void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *sender, void *senderData), void *data)'
try_extern 'uiButton *uiNewButton(const char *text)'
# uiBox
try_extern 'void uiBoxAppend(uiBox *b, uiControl *child, int stretchy)'
try_extern 'int uiBoxNumChildren(uiBox *b)'
try_extern 'void uiBoxDelete(uiBox *b, int index)'
try_extern 'int uiBoxPadded(uiBox *b)'
try_extern 'void uiBoxSetPadded(uiBox *b, int padded)'
try_extern 'uiBox *uiNewHorizontalBox(void)'
try_extern 'uiBox *uiNewVerticalBox(void)'
# uiCheckbox
try_extern 'char *uiCheckboxText(uiCheckbox *c)'
try_extern 'void uiCheckboxSetText(uiCheckbox *c, const char *text)'
try_extern 'void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *sender, void *senderData), void *data)'
try_extern 'int uiCheckboxChecked(uiCheckbox *c)'
try_extern 'void uiCheckboxSetChecked(uiCheckbox *c, int checked)'
try_extern 'uiCheckbox *uiNewCheckbox(const char *text)'
# uiEntry
try_extern 'char *uiEntryText(uiEntry *e)'
try_extern 'void uiEntrySetText(uiEntry *e, const char *text)'
try_extern 'void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *sender, void *senderData), void *data)'
try_extern 'int uiEntryReadOnly(uiEntry *e)'
try_extern 'void uiEntrySetReadOnly(uiEntry *e, int readonly)'
try_extern 'uiEntry *uiNewEntry(void)'
try_extern 'uiEntry *uiNewPasswordEntry(void)'
try_extern 'uiEntry *uiNewSearchEntry(void)'
# uiLabel
try_extern 'char *uiLabelText(uiLabel *l)'
try_extern 'void uiLabelSetText(uiLabel *l, const char *text)'
try_extern 'uiLabel *uiNewLabel(const char *text)'
# uiTab
try_extern 'void uiTabAppend(uiTab *t, const char *name, uiControl *c)'
try_extern 'void uiTabInsertAt(uiTab *t, const char *name, int index, uiControl *c)'
try_extern 'void uiTabDelete(uiTab *t, int index)'
try_extern 'int uiTabNumPages(uiTab *t)'
try_extern 'int uiTabMargined(uiTab *t, int index)'
try_extern 'void uiTabSetMargined(uiTab *t, int index, int margined)'
try_extern 'uiTab *uiNewTab(void)'
# uiGroup
try_extern 'char *uiGroupTitle(uiGroup *g)'
try_extern 'void uiGroupSetTitle(uiGroup *g, const char *title)'
try_extern 'void uiGroupSetChild(uiGroup *g, uiControl *c)'
try_extern 'int uiGroupMargined(uiGroup *g)'
try_extern 'void uiGroupSetMargined(uiGroup *g, int margined)'
try_extern 'uiGroup *uiNewGroup(const char *title)'
# uiSpinbox
try_extern 'int uiSpinboxValue(uiSpinbox *s)'
try_extern 'void uiSpinboxSetValue(uiSpinbox *s, int value)'
try_extern 'void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *sender, void *senderData), void *data)'
try_extern 'uiSpinbox *uiNewSpinbox(int min, int max)'
# uiSlider
try_extern 'int uiSliderValue(uiSlider *s)'
try_extern 'void uiSliderSetValue(uiSlider *s, int value)'
try_extern 'int uiSliderHasToolTip(uiSlider *s)'
try_extern 'void uiSliderSetHasToolTip(uiSlider *s, int hasToolTip)'
try_extern 'void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *sender, void *senderData), void *data)'
try_extern 'void uiSliderOnReleased(uiSlider *s, void (*f)(uiSlider *sender, void *senderData), void *data)'
try_extern 'void uiSliderSetRange(uiSlider *s, int min, int max)'
try_extern 'uiSlider *uiNewSlider(int min, int max)'
# uiProgressBar
try_extern 'int uiProgressBarValue(uiProgressBar *p)'
try_extern 'void uiProgressBarSetValue(uiProgressBar *p, int n)'
try_extern 'uiProgressBar *uiNewProgressBar(void)'
# uiSeparator
try_extern 'uiSeparator *uiNewHorizontalSeparator(void)'
try_extern 'uiSeparator *uiNewVerticalSeparator(void)'
# uiCombobox
try_extern 'void uiComboboxAppend(uiCombobox *c, const char *text)'
try_extern 'void uiComboboxInsertAt(uiCombobox *c, int index, const char *text)'
try_extern 'void uiComboboxDelete(uiCombobox *c, int index)'
try_extern 'void uiComboboxClear(uiCombobox *c)'
try_extern 'int uiComboboxNumItems(uiCombobox *c)'
try_extern 'int uiComboboxSelected(uiCombobox *c)'
try_extern 'void uiComboboxSetSelected(uiCombobox *c, int index)'
try_extern 'void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *sender, void *senderData), void *data)'
try_extern 'uiCombobox *uiNewCombobox(void)'
# uiEditableCombobox
try_extern 'void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)'
try_extern 'char *uiEditableComboboxText(uiEditableCombobox *c)'
try_extern 'void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)'
try_extern 'void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *sender, void *senderData), void *data)'
try_extern 'uiEditableCombobox *uiNewEditableCombobox(void)'
# uiRadioButtons
try_extern 'void uiRadioButtonsAppend(uiRadioButtons *r, const char *text)'
try_extern 'int uiRadioButtonsSelected(uiRadioButtons *r)'
try_extern 'void uiRadioButtonsSetSelected(uiRadioButtons *r, int index)'
try_extern 'void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *sender, void *senderData), void *data)'
try_extern 'uiRadioButtons *uiNewRadioButtons(void)'
# uiDateTimePicker
# time.h
TM = if Fiddle::WINDOWS
struct [
'int tm_sec',
'int tm_min',
'int tm_hour',
'int tm_mday',
'int tm_mon',
'int tm_year',
'int tm_wday',
'int tm_yday',
'int tm_isdst'
]
else # The GNU C Library (glibc)
struct [
'int tm_sec',
'int tm_min',
'int tm_hour',
'int tm_mday',
'int tm_mon',
'int tm_year',
'int tm_wday',
'int tm_yday',
'int tm_isdst',
'long tm_gmtoff',
'const char *tm_zone'
]
end
try_extern 'void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time)'
try_extern 'void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time)'
try_extern 'void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *sender, void *senderData), void *data)'
try_extern 'uiDateTimePicker *uiNewDateTimePicker(void)'
try_extern 'uiDateTimePicker *uiNewDatePicker(void)'
try_extern 'uiDateTimePicker *uiNewTimePicker(void)'
# uiMultilineEntry
try_extern 'char *uiMultilineEntryText(uiMultilineEntry *e)'
try_extern 'void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)'
try_extern 'void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)'
try_extern 'void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *sender, void *senderData), void *data)'
try_extern 'int uiMultilineEntryReadOnly(uiMultilineEntry *e)'
try_extern 'void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)'
try_extern 'uiMultilineEntry *uiNewMultilineEntry(void)'
try_extern 'uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)'
# uiMenuItem
try_extern 'void uiMenuItemEnable(uiMenuItem *m)'
try_extern 'void uiMenuItemDisable(uiMenuItem *m)'
try_extern 'void uiMenuItemOnClicked(uiMenuItem *m, void (*f)(uiMenuItem *sender, uiWindow *window, void *senderData), void *data)'
try_extern 'int uiMenuItemChecked(uiMenuItem *m)'
try_extern 'void uiMenuItemSetChecked(uiMenuItem *m, int checked)'
# uiMenu
try_extern 'uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)'
try_extern 'uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name)'
try_extern 'uiMenuItem *uiMenuAppendQuitItem(uiMenu *m)'
try_extern 'uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m)'
try_extern 'uiMenuItem *uiMenuAppendAboutItem(uiMenu *m)'
try_extern 'void uiMenuAppendSeparator(uiMenu *m)'
try_extern 'uiMenu *uiNewMenu(const char *name)'
try_extern 'char *uiOpenFile(uiWindow *parent)'
try_extern 'char *uiOpenFolder(uiWindow *parent)'
try_extern 'char *uiSaveFile(uiWindow *parent)'
try_extern 'void uiMsgBox(uiWindow *parent, const char *title, const char *description)'
try_extern 'void uiMsgBoxError(uiWindow *parent, const char *title, const char *description)'
# uiArea
AreaHandler = struct [
'void (*Draw)(uiAreaHandler *, uiArea *, uiAreaDrawParams *)',
'void (*MouseEvent)(uiAreaHandler *, uiArea *, uiAreaMouseEvent *)',
'void (*MouseCrossed)(uiAreaHandler *, uiArea *, int left)',
'void (*DragBroken)(uiAreaHandler *, uiArea *)',
'int (*KeyEvent)(uiAreaHandler *, uiArea *, uiAreaKeyEvent *)'
]
typealias 'uiWindowResizeEdge', 'int'
try_extern 'void uiAreaSetSize(uiArea *a, int width, int height)'
try_extern 'void uiAreaQueueRedrawAll(uiArea *a)'
try_extern 'void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)'
try_extern 'void uiAreaBeginUserWindowMove(uiArea *a)'
try_extern 'void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)'
try_extern 'uiArea *uiNewArea(uiAreaHandler *ah)'
try_extern 'uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)'
AreaDrawParams = struct [
'uiDrawContext *Context',
'double AreaWidth',
'double AreaHeight',
'double ClipX',
'double ClipY',
'double ClipWidth',
'double ClipHeight'
]
typealias 'uiDrawBrushType', 'int'
typealias 'uiDrawLineCap', 'int'
typealias 'uiDrawLineJoin', 'int'
typealias 'uiDrawFillMode', 'int'
DrawMatrix = struct [
'double M11',
'double M12',
'double M21',
'double M22',
'double M31',
'double M32'
]
DrawBrush = struct [
'uiDrawBrushType Type',
'double R',
'double G',
'double B',
'double A',
'double X0',
'double Y0',
'double X1',
'double Y1',
'double OuterRadius',
'uiDrawBrushGradientStop *Stops',
'size_t NumStops'
]
DrawBrushGradientStop = struct [
'double Pos',
'double R',
'double G',
'double B',
'double A'
]
DrawStrokeParams = struct [
'uiDrawLineCap Cap',
'uiDrawLineJoin Join',
'double Thickness',
'double MiterLimit',
'double *Dashes',
'size_t NumDashes',
'double DashPhase'
]
# uiDrawPath
try_extern 'uiDrawPath *uiDrawNewPath(uiDrawFillMode fillMode)'
try_extern 'void uiDrawFreePath(uiDrawPath *p)'
try_extern 'void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)'
try_extern 'void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)'
try_extern 'void uiDrawPathLineTo(uiDrawPath *p, double x, double y)'
try_extern 'void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)'
try_extern 'void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)'
try_extern 'void uiDrawPathCloseFigure(uiDrawPath *p)'
try_extern 'void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)'
try_extern 'int uiDrawPathEnded(uiDrawPath *p)'
try_extern 'void uiDrawPathEnd(uiDrawPath *p)'
try_extern 'void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)'
try_extern 'void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)'
# uiDrawMatrix
try_extern 'void uiDrawMatrixSetIdentity(uiDrawMatrix *m)'
try_extern 'void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)'
try_extern 'void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)'
try_extern 'void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)'
try_extern 'void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)'
try_extern 'void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)'
try_extern 'int uiDrawMatrixInvertible(uiDrawMatrix *m)'
try_extern 'int uiDrawMatrixInvert(uiDrawMatrix *m)'
try_extern 'void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)'
try_extern 'void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)'
try_extern 'void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)'
try_extern 'void uiDrawClip(uiDrawContext *c, uiDrawPath *path)'
try_extern 'void uiDrawSave(uiDrawContext *c)'
try_extern 'void uiDrawRestore(uiDrawContext *c)'
# uiAttribute
try_extern 'void uiFreeAttribute(uiAttribute *a)'
typealias 'uiAttributeType', 'int'
try_extern 'uiAttributeType uiAttributeGetType(const uiAttribute *a)'
try_extern 'uiAttribute *uiNewFamilyAttribute(const char *family)'
try_extern 'const char *uiAttributeFamily(const uiAttribute *a)'
try_extern 'uiAttribute *uiNewSizeAttribute(double size)'
try_extern 'double uiAttributeSize(const uiAttribute *a)'
typealias 'uiTextWeight', 'int'
try_extern 'uiAttribute *uiNewWeightAttribute(uiTextWeight weight)'
try_extern 'uiTextWeight uiAttributeWeight(const uiAttribute *a)'
typealias 'uiTextItalic', 'int'
try_extern 'uiAttribute *uiNewItalicAttribute(uiTextItalic italic)'
try_extern 'uiTextItalic uiAttributeItalic(const uiAttribute *a)'
typealias 'uiTextStretch', 'int'
try_extern 'uiAttribute *uiNewStretchAttribute(uiTextStretch stretch)'
try_extern 'uiTextStretch uiAttributeStretch(const uiAttribute *a)'
try_extern 'uiAttribute *uiNewColorAttribute(double r, double g, double b, double a)'
try_extern 'void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha)'
try_extern 'uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a)'
typealias 'uiUnderline', 'int'
try_extern 'uiAttribute *uiNewUnderlineAttribute(uiUnderline u)'
try_extern 'uiUnderline uiAttributeUnderline(const uiAttribute *a)'
typealias 'uiUnderlineColor', 'int'
try_extern 'uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a)'
try_extern 'void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha)'
# uiOpenTypeFeatures
typealias 'uiOpenTypeFeaturesForEachFunc', 'void*'
try_extern 'uiOpenTypeFeatures *uiNewOpenTypeFeatures(void)'
try_extern 'void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf)'
try_extern 'uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf)'
try_extern 'void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value)'
try_extern 'void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d)'
try_extern 'int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value)'
try_extern 'void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data)'
try_extern 'uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf)'
try_extern 'const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a)'
# uiAttributedString
typealias 'uiAttributedStringForEachAttributeFunc', 'void*'
try_extern 'uiAttributedString *uiNewAttributedString(const char *initialString)'
try_extern 'void uiFreeAttributedString(uiAttributedString *s)'
try_extern 'const char *uiAttributedStringString(const uiAttributedString *s)'
try_extern 'size_t uiAttributedStringLen(const uiAttributedString *s)'
try_extern 'void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str)'
try_extern 'void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at)'
try_extern 'void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end)'
try_extern 'void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end)'
try_extern 'void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data)'
try_extern 'size_t uiAttributedStringNumGraphemes(uiAttributedString *s)'
try_extern 'size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos)'
try_extern 'size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos)'
# uiFont
FontDescriptor = struct [
'char *Family',
'double Size',
'uiTextWeight Weight',
'uiTextItalic Italic',
'uiTextStretch Stretch'
]
try_extern 'void uiLoadControlFont(uiFontDescriptor *f)'
try_extern 'void uiFreeFontDescriptor(uiFontDescriptor *desc)'
typealias 'uiDrawTextAlign', 'int'
DrawTextLayoutParams = struct [
'uiAttributedString *String',
'uiFontDescriptor *DefaultFont',
'double Width',
'uiDrawTextAlign Align'
]
try_extern 'uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params)'
try_extern 'void uiDrawFreeTextLayout(uiDrawTextLayout *tl)'
try_extern 'void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)'
try_extern 'void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)'
# uiFontButton
try_extern 'void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc)'
try_extern 'void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *sender, void *senderData), void *data)'
try_extern 'uiFontButton *uiNewFontButton(void)'
try_extern 'void uiFreeFontButtonFont(uiFontDescriptor *desc)'
typealias 'uiModifiers', 'int'
AreaMouseEvent = struct [
'double X',
'double Y',
'double AreaWidth',
'double AreaHeight',
'int Down',
'int Up',
'int Count',
'uiModifiers Modifiers',
'uint64_t Held1To64'
]
typealias 'uiExtKey', 'int'
AreaKeyEvent = struct [
'char Key',
'uiExtKey ExtKey',
'uiModifiers Modifier',
'uiModifiers Modifiers',
'int Up'
]
# uiColorButton
try_extern 'void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a)'
try_extern 'void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a)'
try_extern 'void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *sender, void *senderData), void *data)'
try_extern 'uiColorButton *uiNewColorButton(void)'
# uiForm
try_extern 'void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy)'
try_extern 'int uiFormNumChildren(uiForm *f)'
try_extern 'void uiFormDelete(uiForm *f, int index)'
try_extern 'int uiFormPadded(uiForm *f)'
try_extern 'void uiFormSetPadded(uiForm *f, int padded)'
try_extern 'uiForm *uiNewForm(void)'
typealias 'uiAlign', 'int'
typealias 'uiAt', 'int'
# uiGrid
try_extern 'void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)'
try_extern 'void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)'
try_extern 'int uiGridPadded(uiGrid *g)'
try_extern 'void uiGridSetPadded(uiGrid *g, int padded)'
try_extern 'uiGrid *uiNewGrid(void)'
# uiImage
try_extern 'uiImage *uiNewImage(double width, double height)'
try_extern 'void uiFreeImage(uiImage *i)'
try_extern 'void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride)'
# uiTable
try_extern 'void uiFreeTableValue(uiTableValue *v)'
typealias 'uiTableValueType', 'int'
try_extern 'uiTableValueType uiTableValueGetType(const uiTableValue *v)'
try_extern 'uiTableValue *uiNewTableValueString(const char *str)'
try_extern 'const char *uiTableValueString(const uiTableValue *v)'
try_extern 'uiTableValue *uiNewTableValueImage(uiImage *img)'
try_extern 'uiImage *uiTableValueImage(const uiTableValue *v)'
try_extern 'uiTableValue *uiNewTableValueInt(int i)'
try_extern 'int uiTableValueInt(const uiTableValue *v)'
try_extern 'uiTableValue *uiNewTableValueColor(double r, double g, double b, double a)'
try_extern 'void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a)'
TableModelHandler = struct [
'int (*NumColumns)(uiTableModelHandler *, uiTableModel *)',
'uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int)',
'int (*NumRows)(uiTableModelHandler *, uiTableModel *)',
'uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column)',
'void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *)'
]
try_extern 'uiTableModel *uiNewTableModel(uiTableModelHandler *mh)'
try_extern 'void uiFreeTableModel(uiTableModel *m)'
try_extern 'void uiTableModelRowInserted(uiTableModel *m, int newIndex)'
try_extern 'void uiTableModelRowChanged(uiTableModel *m, int index)'
try_extern 'void uiTableModelRowDeleted(uiTableModel *m, int oldIndex)'
TableTextColumnOptionalParams = struct [
'int ColorModelColumn'
]
TableParams = struct [
'uiTableModel *Model',
'int RowBackgroundColorModelColumn'
]
try_extern 'void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)'
try_extern 'void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn)'
try_extern 'void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)'
try_extern 'void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn)'
try_extern 'void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams)'
try_extern 'void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn)'
try_extern 'void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn)'
try_extern 'int uiTableHeaderVisible(uiTable *t)'
try_extern 'void uiTableHeaderSetVisible(uiTable *t, int visible)'
try_extern 'uiTable *uiNewTable(uiTableParams *params)'
try_extern 'void uiTableOnRowClicked(uiTable *t, void (*f)(uiTable *t, int row, void *data), void *data)'
try_extern 'void uiTableOnRowDoubleClicked(uiTable *t, void (*f)(uiTable *t, int row, void *data), void *data)'
typealias 'uiSortIndicator', 'int'
try_extern 'void uiTableHeaderSetSortIndicator(uiTable *t, int column, uiSortIndicator indicator)'
try_extern 'uiSortIndicator uiTableHeaderSortIndicator(uiTable *t, int column)'
try_extern 'void uiTableHeaderOnClicked(uiTable *t, void (*f)(uiTable *sender, int column, void *senderData), void *data)'
try_extern 'int uiTableColumnWidth(uiTable *t, int column)'
try_extern 'void uiTableColumnSetWidth(uiTable *t, int column, int width)'
typealias 'uiTableSelectionMode', 'int'
try_extern 'uiTableSelectionMode uiTableGetSelectionMode(uiTable *t)'
try_extern 'void uiTableSetSelectionMode(uiTable *t, uiTableSelectionMode mode)'
try_extern 'void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *t, void *data), void *data)'
try_extern 'uiTableSelection* uiTableGetSelection(uiTable *t)'
try_extern 'void uiTableSetSelection(uiTable *t, uiTableSelection *sel)'
try_extern 'void uiFreeTableSelection(uiTableSelection* s)'
TableSelection = struct [
'int NumRows',
'int *Rows'
]
end
end
================================================
FILE: lib/libui/fiddle_patch.rb
================================================
module LibUI
# This module overrides Fiddle's mtehods
# - Fiddle::Importer#extern
# - Fiddle::CParser#parse_signature
# Original methods are in
# - https://github.com/ruby/fiddle/blob/master/lib/fiddle/import.rb
# - https://github.com/ruby/fiddle/blob/master/lib/fiddle/cparser.rb
# These changes add the ability to parse the signatures of functions given as arguments.
module FiddlePatch
def parse_signature(signature, tymap = nil)
tymap ||= {}
ctype, func, args = case compact(signature)
when /^(?:[\w\*\s]+)\(\*(\w+)\((.*?)\)\)(?:\[\w*\]|\(.*?\));?$/
[TYPE_VOIDP, Regexp.last_match(1), Regexp.last_match(2)]
when /^([\w\*\s]+[*\s])(\w+)\((.*?)\);?$/
[parse_ctype(Regexp.last_match(1).strip, tymap), Regexp.last_match(2), Regexp.last_match(3)]
else
raise("can't parse the function prototype: #{signature}")
end
symname = func
callback_argument_types = {} # Added
argtype = split_arguments(args).collect.with_index do |arg, idx| # Added with_index
# Check if it is a function pointer or not
if arg =~ /\(\*.*\)\(.*\)/ # Added
# From the arguments, create a notation that looks like a function declaration
# int(*f)(int *, void *) -> int f(int *, void *)
func_arg = arg.sub('(*', ' ').sub(')', '') # Added
# Use Fiddle's parse_signature method again.
callback_argument_types[idx] = parse_signature(func_arg) # Added
end
parse_ctype(arg, tymap)
end
# Added callback_argument_types. Original method return only 3 values.
[symname, ctype, argtype, callback_argument_types]
end
def extern(signature, *opts)
symname, ctype, argtype, callback_argument_types = parse_signature(signature, type_alias)
opt = parse_bind_options(opts)
func = import_function(symname, ctype, argtype, opt[:call_type])
# callback_argument_types
func.instance_variable_set(:@callback_argument_types,
callback_argument_types) # Added
# attr_reader
def func.callback_argument_types
@callback_argument_types
end
# argument_types
# Ruby 2.7 Fiddle::Function dose not have @argument_types
# Ruby 3.0 Fiddle::Function has @argument_types
if func.instance_variable_defined?(:@argument_types)
# check if @argument_types are the same
if func.instance_variable_get(:@argument_types) != argtype
warn "#{symname} func.argument_types:#{func.argument_types} != argtype #{argtype}"
end
else
func.instance_variable_set(:@argument_types, argtype)
end
# attr_reader
def func.argument_types
@argument_types
end
name = symname.gsub(/@.+/, '')
@func_map[name] = func
# define_method(name){|*args,&block| f.call(*args,&block)}
begin
/^(.+?):(\d+)/ =~ caller.first
file = Regexp.last_match(1)
line = Regexp.last_match(2).to_i
rescue StandardError
file, line = __FILE__, __LINE__ + 3
end
module_eval(<<-EOS, file, line)
def #{name}(*args, &block)
@func_map['#{name}'].call(*args,&block)
end
EOS
module_function(name)
func
end
end
private_constant :FiddlePatch
end
================================================
FILE: lib/libui/libui_base.rb
================================================
module LibUI
module LibUIBase
FFI.func_map.each_key do |original_method_name|
name = Utils.convert_to_ruby_method(original_method_name)
func = FFI.func_map[original_method_name]
define_method(name) do |*args, &blk|
# Assume that block is the last argument.
args << blk if blk
# The proc object is converted to a Closure::BlockCaller object.
args.map!.with_index do |arg, idx|
next arg unless arg.is_a?(Proc)
# now arg must be Proc
# The types of the function arguments are stored in advance.
# See the monkey patch in ffi.rb.
_f, ret_type, arg_types = func.callback_argument_types[idx]
# TODO: raise some nice error if _f is nil.
callback = Fiddle::Closure::BlockCaller.new(
ret_type, arg_types, &arg
)
# Protect from GC
# by giving the owner object a reference to the callback.
# See https://github.com/kojix2/LibUI/issues/8
receiver = args.first
owner = if idx == 0 || # UI.queue_main{}
receiver.nil? ||
(receiver.respond_to?(:frozen?) && receiver.frozen?) # UI.timer(100) {}
LibUIBase # keep a reference on an internal module to avoid GC
else
receiver # receiver object holds the callback
end
if owner.instance_variable_defined?(:@callbacks)
owner.instance_variable_get(:@callbacks) << callback
else
owner.instance_variable_set(:@callbacks, [callback])
end
callback
end
# Make it possible to omit the last nil. This may be an over-optimization.
siz = func.argument_types.size - 1
args[siz] = nil if args.size == siz
FFI.public_send(original_method_name, *args)
end
end
end
private_constant :LibUIBase
end
================================================
FILE: lib/libui/utils.rb
================================================
module LibUI
module Utils
class << self
def convert_to_ruby_method(original_method_name)
underscore(original_method_name.delete_prefix('ui'))
end
# Converting camel case to underscore case in ruby
# https://stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby#1509939
def underscore(str)
str.gsub(/::/, '/') # Maybe we don't need it.
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.tr('-', '_')
.downcase
end
end
end
end
================================================
FILE: lib/libui/version.rb
================================================
module LibUI
VERSION = '0.2.0'
end
================================================
FILE: lib/libui.rb
================================================
require_relative 'libui/version'
require_relative 'libui/utils'
require_relative 'libui/error'
require 'rbconfig'
module LibUI
class << self
attr_accessor :ffi_lib
end
host_cpu = case RbConfig::CONFIG['host_cpu']
when /i\d86/
'x86'
else
RbConfig::CONFIG['host_cpu']
end
lib_name = [
# For libui-ng shared libraries compiled with (rake vendor:build)
"libui.#{RbConfig::CONFIG['host_cpu']}.#{RbConfig::CONFIG['SOEXT']}",
# For libui-ng shared library downloaded from RubyGems.org
"libui.#{host_cpu}.#{RbConfig::CONFIG['SOEXT']}",
# For backward compatibility or manual compilation of libui-ng
"libui.#{RbConfig::CONFIG['SOEXT']}"
]
self.ffi_lib = \
if ENV['LIBUIDIR'] && !ENV['LIBUIDIR'].empty?
lib_name.lazy
.map { |name| File.expand_path(name, ENV['LIBUIDIR']) }
.find { |path| File.exist?(path) }
else
lib_name.lazy
.map { |name| File.expand_path("../vendor/#{name}", __dir__) }
.find { |path| File.exist?(path) }
end
require_relative 'libui/ffi'
require_relative 'libui/libui_base'
extend LibUIBase
class << self
def init(opt = nil)
# Allocate uiInitOptions if not provided
unless opt
opt = FFI::InitOptions.malloc
opt.to_ptr.free = Fiddle::RUBY_FREE
opt.Size = FFI::InitOptions.size
end
err_ptr = super(opt) # uiInit returns const char* error or NULL on success
return nil if err_ptr.null?
# Convert C string to Ruby string and free the error string per API contract
err_msg = err_ptr.to_s
free_init_error(err_ptr)
warn err_msg
nil
end
# Gets the window position.
# Coordinates are measured from the top left corner of the screen.
# @param w [Fiddle::Pointer] Pointer of uiWindow instance.
# @return [Array] position of the window. [x, y]
# @note This method may return inaccurate or dummy values on Unix platforms.
def window_position(w)
x_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
y_ptr = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT, Fiddle::RUBY_FREE)
super(w, x_ptr, y_ptr)
x = x_ptr[0, Fiddle::SIZEOF_INT].unpack1('i*')
y = y_ptr[0, Fiddle::SIZEOF_INT].unpack1('i*')
[x, y]
end
# FIXME: This is a workaround for a old version of Fiddle.
# Fiddle 1.1.2 and above should be able to handle one character string.
# See https://github.com/ruby/fiddle/issues/96
def open_type_features_add(otf, a, b, c, d, value)
a, b, c, d = [a, b, c, d].map { |s| s.is_a?(String) ? s.ord : s }
super(otf, a, b, c, d, value)
end
def open_type_features_remove(otf, a, b, c, d)
a, b, c, d = [a, b, c, d].map { |s| s.is_a?(String) ? s.ord : s }
super(otf, a, b, c, d)
end
def open_type_features_get(otf, a, b, c, d, value)
a, b, c, d = [a, b, c, d].map { |s| s.is_a?(String) ? s.ord : s }
super(otf, a, b, c, d, value)
end
end
## UI_ENUM https://github.com/libui-ng/libui-ng/blob/master/ui.h
# ForEach
ForEachContinue = 0
ForEachStop = 1
# WindowResizeEdge
WindowResizeEdgeLeft = 0
WindowResizeEdgeTop = 1
WindowResizeEdgeRight = 2
WindowResizeEdgeBottom = 3
WindowResizeEdgeTopLeft = 4
WindowResizeEdgeTopRight = 5
WindowResizeEdgeBottomLeft = 6
WindowResizeEdgeBottomRight = 7
# DrawBrushType
DrawBrushTypeSolid = 0
DrawBrushTypeLinearGradient = 1
DrawBrushTypeRadialGradient = 2
DrawBrushTypeImage = 3
# DrawLineCap
DrawLineCapFlat = 0
DrawLineCapRound = 1
DrawLineCapSquare = 2
# DrawLineJoin
DrawLineJoinMiter = 0
DrawLineJoinRound = 1
DrawLineJoinBevel = 2
DrawDefaultMiterLimit = 10.0
# DrawFillMode
DrawFillModeWinding = 0
DrawFillModeAlternate = 1
# AttributeType
AttributeTypeFamily = 0
AttributeTypeSize = 1
AttributeTypeWeight = 2
AttributeTypeItalic = 3
AttributeTypeStretch = 4
AttributeTypeColor = 5
AttributeTypeBackground = 6
AttributeTypeUnderline = 7
AttributeTypeUnderlineColor = 8
AttributeTypeFeatures = 9
# TextWeight
TextWeightMinimum = 0
TextWeightThin = 100
TextWeightUltraLight = 200
TextWeightLight = 300
TextWeightBook = 350
TextWeightNormal = 400
TextWeightMedium = 500
TextWeightSemiBold = 600
TextWeightBold = 700
TextWeightUltraBold = 800
TextWeightHeavy = 900
TextWeightUltraHeavy = 950
TextWeightMaximum = 1000
# TextItalic
TextItalicNormal = 0
TextItalicOblique = 1
TextItalicItalic = 2
# TextStretch
TextStretchUltraCondensed = 0
TextStretchExtraCondensed = 1
TextStretchCondensed = 2
TextStretchSemiCondensed = 3
TextStretchNormal = 4
TextStretchSemiExpanded = 5
TextStretchExpanded = 6
TextStretchExtraExpanded = 7
TextStretchUltraExpanded = 8
# Underline
UnderlineNone = 0
UnderlineSingle = 1
UnderlineDouble = 2
UnderlineSuggestion = 3
# UnderlineColor
UnderlineColorCustom = 0
UnderlineColorSpelling = 1
UnderlineColorGrammar = 2
UnderlineColorAuxiliary = 3
# DrawTextAlign
DrawTextAlignLeft = 0
DrawTextAlignCenter = 1
DrawTextAlignRight = 2
# Modifiers
ModifierCtrl = (1 << 0)
ModifierAlt = (1 << 1)
ModifierShift = (1 << 2)
ModifierSuper = (1 << 3)
# ExtKey
ExtKeyEscape = 1
ExtKeyInsert = 2
ExtKeyDelete = 3
ExtKeyHome = 4
ExtKeyEnd = 5
ExtKeyPageUp = 6
ExtKeyPageDown = 7
ExtKeyUp = 8
ExtKeyDown = 9
ExtKeyLeft = 10
ExtKeyRight = 11
ExtKeyF1 = 12
ExtKeyF2 = 13
ExtKeyF3 = 14
ExtKeyF4 = 15
ExtKeyF5 = 16
ExtKeyF6 = 17
ExtKeyF7 = 18
ExtKeyF8 = 19
ExtKeyF9 = 20
ExtKeyF10 = 21
ExtKeyF11 = 22
ExtKeyF12 = 23
ExtKeyN0 = 24
ExtKeyN1 = 25
ExtKeyN2 = 26
ExtKeyN3 = 27
ExtKeyN4 = 28
ExtKeyN5 = 29
ExtKeyN6 = 30
ExtKeyN7 = 31
ExtKeyN8 = 32
ExtKeyN9 = 33
ExtKeyNDot = 34
ExtKeyNEnter = 35
ExtKeyNAdd = 36
ExtKeyNSubtract = 37
ExtKeyNMultiply = 38
ExtKeyNDivide = 39
# Align
AlignFill = 0
AlignStart = 1
AlignCenter = 2
AlignEnd = 3
# At
AtLeading = 0
AtTop = 1
AtTrailing = 2
AtBottom = 3
# TableValueType
TableValueTypeString = 0
TableValueTypeImage = 1
TableValueTypeInt = 2
TableValueTypeColor = 3
# SortIndicator
SortIndicatorNone = 0
SortIndicatorAscending = 1
SortIndicatorDescending = 2
# editable
TableModelColumnNeverEditable = -1
TableModelColumnAlwaysEditable = -2
# TableSelectionMode
TableSelectionModeNone = 0
TableSelectionModeZeroOrOne = 1
TableSelectionModeOne = 2
TableSelectionModeZeroOrMany = 3
end
================================================
FILE: libui.gemspec
================================================
require_relative 'lib/libui/version'
Gem::Specification.new do |spec|
spec.name = 'libui'
spec.version = LibUI::VERSION
spec.summary = 'Ruby bindings to libui'
spec.homepage = 'https://github.com/kojix2/libui'
spec.license = 'MIT'
spec.authors = ['kojix2']
spec.email = ['2xijok@gmail.com']
spec.files = Dir['*.{md,txt}', '{lib}/**/*', 'vendor/{LICENSE,README}.md']
spec.require_paths = 'lib'
spec.required_ruby_version = '>= 2.6'
# Use the GEM_PLATFORM environment variable if specified
gem_platform = ENV['GEM_PLATFORM']
spec.platform = gem_platform if gem_platform && !gem_platform.empty? && gem_platform != 'ruby'
# See `gem help platform` for information on platform matching.
case spec.platform.to_s
when 'x86_64-linux'
spec.files << 'vendor/libui.x86_64.so'
when 'aarch64-linux'
spec.files << 'vendor/libui.aarch64.so'
when 'x86_64-darwin'
spec.files << 'vendor/libui.x86_64.dylib'
when 'arm64-darwin'
spec.files << 'vendor/libui.arm64.dylib'
when 'x64-mingw32', 'x64-mingw-ucrt'
spec.files << 'vendor/libui.x64.dll'
when 'x86-mingw32'
spec.files << 'vendor/libui.x86.dll'
else
spec.files.concat(Dir['vendor/*.{dll,dylib,so}']) # all
end
spec.add_dependency 'fiddle'
end
================================================
FILE: scripts/README.md
================================================
# Scripts to check for changes in ui.h
Helper scripts for the developer.
## Usage
```sh
bash ui_diff.sh
```
## Requirement
* [delta](https://github.com/dandavison/delta)
* gcc - [remove comments from C/C++ code](https://stackoverflow.com/questions/2394017/remove-comments-from-c-c-code)
================================================
FILE: scripts/ui_diff.sh
================================================
#!/usr/bin/env bash
cd "$(dirname "$0")"
delta <(./ui_ffi.rb) <(./ui_h.rb)
# diff <(./ui_ffi.rb) <(./ui_h.rb)
================================================
FILE: scripts/ui_ffi.rb
================================================
#!/usr/bin/env ruby
libui_ffi_path = File.expand_path('../lib/libui/ffi.rb', __dir__)
libui_path = File.expand_path('../lib/libui.rb', __dir__)
# Read the files into arrays by each line
ffi_lines = File.readlines(libui_ffi_path).map(&:strip)
libui_lines = File.readlines(libui_path)
# Count try_extern
matches = ffi_lines.select { _1.start_with?('try_extern') }
puts 'count try_extern'
puts matches.count
# Print try_extern calls
puts(matches.map { _1.delete_prefix("try_extern '").delete_suffix("'") })
# Print enum names
puts(libui_lines.select { _1.start_with?(' # ') }
.map { _1.delete_prefix(' # ').strip })
================================================
FILE: scripts/ui_h.rb
================================================
#!/usr/bin/env ruby
require 'open-uri'
require 'tempfile'
UI_H_URL = 'https://raw.githubusercontent.com/libui-ng/libui-ng/master/ui.h'.freeze
ui_h = []
Tempfile.open(['ui', '.h']) do |tf|
tf.write URI.open(UI_H_URL).read
tf.close
cmd_gcc = 'gcc -fpreprocessed -P -dD -E'
ui_h = `#{cmd_gcc} #{tf.path}` # Remove comments
.split("\n").select { !_1.strip.start_with?('#') }.join("\n") # Remove macros
.split(';').map do
_1.split("\n").map(&:strip).join(' ')
.strip.squeeze(' ')
end
end
# Count
# - Count the occurrences of '_UI_EXTERN'
puts 'count _UI_EXTERN'
puts(ui_h.count { _1.start_with?('_UI_EXTERN') })
# Functions
# - Print the definitions of functions marked with '_UI_EXTERN'
puts(ui_h.select { _1.start_with?('_UI_EXTERN') }
.map { _1.delete_prefix('_UI_EXTERN ').squeeze(' ') })
# Enums
# - Print the names of enums marked with '_UI_ENUM'
puts(ui_h.select { _1.include?('_UI_ENUM') }
.map { _1.match(/_UI_ENUM\(ui(\w+)/)[1] })
================================================
FILE: test/libui_test.rb
================================================
require 'test_helper'
class LibUITest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::LibUI::VERSION
end
def test_ffi_method_call
pt = LibUI::FFI::InitOptions.malloc
pt.to_ptr.free = Fiddle::RUBY_FREE
assert_kind_of Fiddle::Pointer, LibUI::FFI.uiInit(pt)
assert_nil LibUI::FFI.uiQuit
end
def test_method_call
assert_nil LibUI.init
assert_nil LibUI.quit
end
def test_basic_window
assert_nil LibUI.init
assert_kind_of Fiddle::Pointer, (
main_window = LibUI.new_window('hello world', 300, 200, 1)
)
assert_nil LibUI.control_show(main_window)
assert_nil(
LibUI.window_on_closing(main_window) do
LibUI.control_destroy(main_window)
LibUI.quit
0
end
)
# LibUI.main
assert_nil LibUI.quit
end
end
================================================
FILE: test/test_helper.rb
================================================
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
require 'libui'
require 'minitest/autorun'
require 'minitest/pride'
================================================
FILE: test/utils_test.rb
================================================
require 'test_helper'
class LibUIUtilsTest < Minitest::Test
def test_convert_to_ruby_method
rbmethod1 = LibUI::Utils.convert_to_ruby_method('uiNewMatz')
rbmethod2 = LibUI::Utils.convert_to_ruby_method('AINewMatz')
assert_equal 'new_matz', rbmethod1
assert_equal 'ai_new_matz', rbmethod2
end
def test_underscore
assert_equal '3v3_v_ap_f2_er7s@_f_d/d_sc', LibUI::Utils.underscore('3v3VApF2Er7s@-fD::DSc')
end
end
================================================
FILE: vendor/LICENSE.md
================================================
Copyright (c) 2022 libui-ng authors
Copyright (c) 2014 Pietro Gagliardi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: vendor/README.md
================================================
# libui-ng: a portable GUI library for C
Fork of [andlabs/libui](https://github.com/andlabs/libui). This README is being written.<br>
[](https://github.com/libui-ng/libui-ng/actions/workflows/build.yml)
[](https://gitlab.com/libui-ng/libui-ng)
[](https://github.com/libui-ng/libui-ng)
## About
Simple and portable (but not inflexible) GUI library in C that uses the native GUI technologies of each platform it supports.
## Status
libui-ng is currently **mid-alpha** software.
See [CHANGELOG.md](CHANGELOG.md)
*Old announcements can be found in the [news.md](old/news.md) file.*
## Runtime Requirements
* Windows: Windows Vista SP2 with Platform Update or newer
* Unix: GTK+ 3.10 or newer
* Mac OS X: OS X 10.8 or newer
## Build Requirements
* All platforms:
* [Meson](https://mesonbuild.com/) 0.58.0 or newer
* Any of Meson's backends; this section assumes you are using [Ninja](https://ninja-build.org/), but there is no reason the other backends shouldn't work.
* Windows: either
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
* [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest
* Unix: nothing else specific
* Mac OS X: nothing else specific, so long as you can build Cocoa programs
## Building
libui-ng mainly uses [the standard Meson build options](https://mesonbuild.com/Builtin-options.html).
```
$ # in the top-level libui-ng directory run:
$ meson setup build [options]
$ ninja -C build
```
Once this completes, everything will be under `build/meson-out/`.
libui-ng specific options:
- `-Dtests=(true|false)` controls whether tests are built; defaults to `true`
- `-Dexamples=(true|false)` controls whether examples are built; defaults to `true`
Most important Meson options:
* `--buildtype=(debug|release|...)` controls the type of build made; the default is `debug`. For a full list of valid values, consult [the Meson documentation](https://mesonbuild.com/Running-Meson.html).
* `--default-library=(shared|static)` controls whether libui is built as a shared library or a static library; the default is `shared`. You currently cannot specify `both`, as the build process changes depending on the target type (though I am willing to look into changing things if at all possible).
* `-Db_sanitize=which` allows enabling the chosen [sanitizer](https://github.com/google/sanitizers) on a system that supports sanitizers. The list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#base-options).
* `--backend=backend` allows using the specified `backend` for builds instead of `ninja` (the default). A list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options).
* `--wrap-mode=(forcefallback|nofallback|nodownload|...)` controls which cmocka library version to use in test enabled builds. The default is `forcefallback` to pull and build a local copy. Package maintainers may wish to choose `nofallback` to use the system's library and declare `cmocka` a build time dependency or `nodownload`, see [the Meson documentation](https://mesonbuild.com/Subprojects.html#commandline-options) for more details.
Most other built-in options will work, though keep in mind there are a handful of options that cannot be overridden because libui depends on them holding a specific value; if you do override these, though, libui will warn you when you run `meson`.
The Meson website and documentation has more in-depth usage instructions.
For the sake of completeness, I should note that the default value of `--layout` is `flat`, not the usual `mirror`. This is done both to make creating the release archives easier as well as to reduce the chance that shared library builds will fail to start on Windows because the DLL is in another directory. You can always specify this manually if you want.
Backends other than `ninja` should work, but are untested by me.
## Testing
### Automated Unit Tests
Run the included unit tests via `meson test -C build`. Alternatively you can also run the `unit` executable manually.
### Manual Testing Suite
Run the manual quality assurance test suite via `qa` and follow the instructions laid out within.
## Installation
Meson also supports installing from source; if you use Ninja, just do
```
$ ninja -C build install
```
When running `meson`, the `--prefix` option will set the installation prefix. [The Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options) has more information, and even lists more fine-grained options that you can use to control the installation.
#### Arch Linux
Can be built from AUR: https://aur.archlinux.org/packages/libui-ng-git/
## Documentation [WIP]
[API](https://libui-ng.github.io/libui-ng/), check the [modules](https://libui-ng.github.io/libui-ng/modules.html) section for an overview of (nearly all) uiControls.
Consult the `ui.h` comments for the uiControls missing in the docs.
Check the `examples` directory for fully fledged examples. Check out the `tests` directory and subdirectories for more real world usage.
## Language Bindings
libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding.
Other people have made bindings to other languages:
Language | Bindings
--- | ---
C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike)
C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding)
C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/)
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui)
Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron), [iu](https://github.com/grkek/iu)
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
Harbour | [hbui](https://github.com/rjopek/hbui)
Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui)
Janet | [JanetUI](https://github.com/janet-lang/janetui)
JavaScript/Node.js | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native), [vuido](https://github.com/mimecorg/vuido)
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui)
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui)
Nim | [ui](https://github.com/nim-lang/ui), [uing](https://github.com/neroist/uing)
Perl6 | [perl6-libui](https://github.com/Garland-g/perl6-libui)
PHP | [ui](https://github.com/krakjoe/ui), [Ardillo](https://github.com/ardillo-php/ext)
Python | [pylibui](https://github.com/joaoventura/pylibui)
Ring | [RingLibui](https://github.com/ring-lang/ring/tree/master/extensions/ringlibui)
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby), [LibUI](https://github.com/kojix2/libui), [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui)
Rust | [libui-ng-sys](https://github.com/norepimorphism/libui-ng-sys), [boing](https://github.com/norepimorphism/boing), [libui-rs](https://github.com/rust-native-ui/libui-rs), [libui](https://github.com/libui-rs/libui)
Scala | [scalaui](https://github.com/lolgab/scalaui)
Swift | [libui-swift](https://github.com/sclukey/libui-swift)
## Frequently Asked Questions
### Why does my program start in the background on OS X if I run from the command line?
OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation".
When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line.
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## Screenshots
From examples/controlgallery:



gitextract_3lw4y_4b/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── doc.yml
│ ├── release-rubygems.yml
│ └── test.yml
├── .gitignore
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── examples/
│ ├── basic_area.rb
│ ├── basic_button.rb
│ ├── basic_draw_text.rb
│ ├── basic_entry.rb
│ ├── basic_table.rb
│ ├── basic_table_image.rb
│ ├── basic_window.rb
│ ├── control_gallery.rb
│ ├── date_time_picker.rb
│ ├── draw_text.rb
│ ├── font_button.rb
│ ├── gpt2_notepad.rb
│ ├── histogram.rb
│ ├── midi_player.rb
│ ├── simple_notepad.rb
│ ├── spectrum.rb
│ └── turing_pattern.rb
├── examples2/
│ ├── README.md
│ ├── button.rb
│ ├── checkbox.rb
│ ├── color_button.rb
│ ├── combobox.rb
│ ├── date_picker.rb
│ ├── editable_combobox.rb
│ ├── entry.rb
│ ├── font_button.rb
│ ├── grid.rb
│ ├── multiline_entry.rb
│ ├── password_entry.rb
│ ├── progress_bar.rb
│ ├── search_entry.rb
│ ├── slider.rb
│ ├── todo/
│ │ └── todo.md
│ └── window.rb
├── lib/
│ ├── libui/
│ │ ├── error.rb
│ │ ├── ffi.rb
│ │ ├── fiddle_patch.rb
│ │ ├── libui_base.rb
│ │ ├── utils.rb
│ │ └── version.rb
│ └── libui.rb
├── libui.gemspec
├── scripts/
│ ├── README.md
│ ├── ui_diff.sh
│ ├── ui_ffi.rb
│ └── ui_h.rb
├── test/
│ ├── libui_test.rb
│ ├── test_helper.rb
│ └── utils_test.rb
└── vendor/
├── LICENSE.md
└── README.md
SYMBOL INDEX (80 symbols across 21 files)
FILE: examples/basic_draw_text.rb
function append (line 47) | def attr_str.append(what, color)
FILE: examples/basic_table.rb
function rbcallback (line 22) | def rbcallback(*args, &block)
FILE: examples/basic_table_image.rb
function rbcallback (line 36) | def rbcallback(*args, &block)
FILE: examples/draw_text.rb
function append_with_attribute (line 5) | def append_with_attribute(attr_str, what, attr1, attr2)
function make_attribute_string (line 13) | def make_attribute_string
function on_font_changed (line 81) | def on_font_changed(area)
function on_combobox_selected (line 85) | def on_combobox_selected(area)
function draw_event (line 89) | def draw_event(adp, attr_str, font_button, alignment)
FILE: examples/gpt2_notepad.rb
function softmax (line 32) | def softmax(y)
function predict (line 36) | def predict(a, prob: true)
function predict_text (line 48) | def predict_text(s, max = 30)
FILE: examples/histogram.rb
function graph_size (line 23) | def graph_size(area_width, area_height)
function point_locations (line 32) | def point_locations(datapoints, width, height)
function construct_graph (line 45) | def construct_graph(datapoints, width, height, should_extend)
function set_solid_brush (line 160) | def set_solid_brush(brush, color, alpha)
FILE: examples/midi_player.rb
class TinyMidiPlayer (line 6) | class TinyMidiPlayer
method initialize (line 9) | def initialize
method stop_midi (line 19) | def stop_midi
method play_midi (line 26) | def play_midi
method show_version (line 39) | def show_version(main_window)
method create_gui (line 47) | def create_gui
FILE: examples/spectrum.rb
class FFTStream (line 12) | class FFTStream < FFI::PortAudio::Stream
method process (line 13) | def process(input, _output, frame_count, _time_info, _status_flags, _u...
method spec (line 19) | def spec
FILE: examples/turing_pattern.rb
type GrayScott (line 55) | module GrayScott
class SFloat (line 60) | class SFloat
type Utils (line 64) | module Utils
function laplacian2d (line 70) | def self.laplacian2d(uv, dx)
class Model (line 91) | class Model
method initialize (line 106) | def initialize(width: 256, height: 256)
method clear (line 123) | def clear
method step (line 128) | def step
type Color (line 146) | module Color
function colorize (line 149) | def colorize(ar, color_type)
function uInt8_dstack (line 173) | def uInt8_dstack(ar)
function hsv2rgb (line 181) | def hsv2rgb(h)
function red (line 200) | def red(ar)
function green (line 204) | def green(ar)
function blue (line 208) | def blue(ar)
function reverse_red (line 212) | def reverse_red(ar)
function reverse_green (line 216) | def reverse_green(ar)
function reverse_blue (line 220) | def reverse_blue(ar)
function grayscale (line 224) | def grayscale(ar)
function uint8_zeros_256 (line 229) | def uint8_zeros_256(ch, ar)
FILE: examples2/color_button.rb
function set_solid_brush (line 43) | def set_solid_brush(
FILE: examples2/combobox.rb
function populate_the_combobox_with_this_array (line 26) | def populate_the_combobox_with_this_array(
FILE: examples2/editable_combobox.rb
function populate_the_combobox_with_this_array (line 26) | def populate_the_combobox_with_this_array(
FILE: lib/libui.rb
type LibUI (line 6) | module LibUI
function init (line 44) | def init(opt = nil)
function window_position (line 68) | def window_position(w)
function open_type_features_add (line 81) | def open_type_features_add(otf, a, b, c, d, value)
function open_type_features_remove (line 86) | def open_type_features_remove(otf, a, b, c, d)
function open_type_features_get (line 91) | def open_type_features_get(otf, a, b, c, d, value)
FILE: lib/libui/error.rb
type LibUI (line 1) | module LibUI
class Error (line 3) | class Error < StandardError; end
class LibraryNotFoundError (line 6) | class LibraryNotFoundError < Error; end
class LibraryLoadError (line 9) | class LibraryLoadError < Error; end
FILE: lib/libui/ffi.rb
type LibUI (line 5) | module LibUI
type FFI (line 7) | module FFI
function try_extern (line 26) | def try_extern(signature, *opts)
function ffi_methods (line 34) | def ffi_methods
FILE: lib/libui/fiddle_patch.rb
type LibUI (line 1) | module LibUI
type FiddlePatch (line 10) | module FiddlePatch
function parse_signature (line 11) | def parse_signature(signature, tymap = nil)
function extern (line 38) | def extern(signature, *opts)
FILE: lib/libui/libui_base.rb
type LibUI (line 1) | module LibUI
type LibUIBase (line 2) | module LibUIBase
FILE: lib/libui/utils.rb
type LibUI (line 1) | module LibUI
type Utils (line 2) | module Utils
function convert_to_ruby_method (line 4) | def convert_to_ruby_method(original_method_name)
function underscore (line 10) | def underscore(str)
FILE: lib/libui/version.rb
type LibUI (line 1) | module LibUI
FILE: test/libui_test.rb
class LibUITest (line 3) | class LibUITest < Minitest::Test
method test_that_it_has_a_version_number (line 4) | def test_that_it_has_a_version_number
method test_ffi_method_call (line 8) | def test_ffi_method_call
method test_method_call (line 15) | def test_method_call
method test_basic_window (line 20) | def test_basic_window
FILE: test/utils_test.rb
class LibUIUtilsTest (line 3) | class LibUIUtilsTest < Minitest::Test
method test_convert_to_ruby_method (line 4) | def test_convert_to_ruby_method
method test_underscore (line 11) | def test_underscore
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (183K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 110,
"preview": "version: 2\nupdates:\n- package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n"
},
{
"path": ".github/workflows/doc.yml",
"chars": 482,
"preview": "name: doc\n\non:\n push:\n branches:\n - main\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: ac"
},
{
"path": ".github/workflows/release-rubygems.yml",
"chars": 2204,
"preview": "name: Release libui to RubyGems\n\non:\n push:\n tags:\n - \"v*.*.*\"\n workflow_dispatch:\n\njobs:\n push:\n environm"
},
{
"path": ".github/workflows/test.yml",
"chars": 824,
"preview": "name: test\non:\n - push\n - pull_request\n - workflow_dispatch\njobs:\n build:\n name: ${{ matrix.runner }} Ruby ${{ ma"
},
{
"path": ".gitignore",
"chars": 161,
"preview": "/.bundle/\n/.yardoc\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\n*.lock\n/vendor/bundle\n/vendor/*libui*\nbuild.log"
},
{
"path": "Gemfile",
"chars": 205,
"preview": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in libui.gemspec\ngemspec\n\ngem 'minitest'\ngem 'rake'\ngem"
},
{
"path": "LICENSE.txt",
"chars": 1081,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2020-present kojix2\n\nPermission is hereby granted, free of charge, to any person ob"
},
{
"path": "README.md",
"chars": 9623,
"preview": "# LibUI\n\n[](https://github.com/kojix2/LibUI"
},
{
"path": "Rakefile",
"chars": 6298,
"preview": "# frozen_string_literal: true\n\nrequire 'rake/testtask'\nrequire 'rbconfig'\nrequire 'fileutils'\nrequire 'zip'\nrequire 'bun"
},
{
"path": "examples/basic_area.rb",
"chars": 1276,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nhandler = UI::FFI::AreaHandler.malloc\nhandler.to_ptr.free = Fiddle::RUBY_FREE\narea"
},
{
"path": "examples/basic_button.rb",
"chars": 395,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('hello world', 300, 200, 1)\n\nbutton = UI.new_button('B"
},
{
"path": "examples/basic_draw_text.rb",
"chars": 3568,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nhandler = UI::FFI::AreaHandler.malloc\nhandler.to_ptr.free = Fiddle::RUBY_FREE\narea"
},
{
"path": "examples/basic_entry.rb",
"chars": 619,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('Basic Entry', 300, 50, 1)\nUI.window_on_closing(main_w"
},
{
"path": "examples/basic_table.rb",
"chars": 1517,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('Animal sounds', 300, 200, 1)\n\nhbox = UI.new_horizonta"
},
{
"path": "examples/basic_table_image.rb",
"chars": 2121,
"preview": "# NOTE:\n# This example displays images that can be freely downloaded from the Studio Ghibli website.\n# https://www.ghibl"
},
{
"path": "examples/basic_window.rb",
"chars": 216,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('hello world', 300, 200, 1)\n\nUI.control_show(main_wind"
},
{
"path": "examples/control_gallery.rb",
"chars": 5300,
"preview": "require 'libui'\nUI = LibUI\n\nUI.init\n\n# File menu\nmenu = UI.new_menu('File')\nopen_menu_item = UI.menu_append_item(menu, '"
},
{
"path": "examples/date_time_picker.rb",
"chars": 755,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nvbox = UI.new_vertical_box\n\ndate_time_picker = UI.new_date_time_picker\n\ntime = UI:"
},
{
"path": "examples/draw_text.rb",
"chars": 6060,
"preview": "require 'libui'\n\nUI = LibUI\n\ndef append_with_attribute(attr_str, what, attr1, attr2)\n start_pos = UI.attributed_string_"
},
{
"path": "examples/font_button.rb",
"chars": 673,
"preview": "require 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('hello world', 300, 200, 1)\n\nfont_button = UI.new_font"
},
{
"path": "examples/gpt2_notepad.rb",
"chars": 2442,
"preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire 'libui'\nrequire 'onnxruntime'\nrequire 'blingfire'\nrequire 'nu"
},
{
"path": "examples/histogram.rb",
"chars": 5870,
"preview": "# https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb\n\nrequire 'libui'\n\nUI = LibUI\n\nX_OFF_LEFT = "
},
{
"path": "examples/midi_player.rb",
"chars": 2811,
"preview": "#!/usr/bin/env ruby\n\nrequire 'libui'\nUI = LibUI\n\nclass TinyMidiPlayer\n VERSION = '0.0.1'\n\n def initialize\n UI.init\n"
},
{
"path": "examples/simple_notepad.rb",
"chars": 373,
"preview": "#!/usr/bin/env ruby\n\nrequire 'libui'\n\nUI = LibUI\n\nUI.init\n\nmain_window = UI.new_window('Notepad', 500, 300, 1)\nUI.window"
},
{
"path": "examples/spectrum.rb",
"chars": 2884,
"preview": "#!/usr/bin/env ruby\n\n# Please play your favorite music or video on your computer\n# when running this spectrum example.\n\n"
},
{
"path": "examples/turing_pattern.rb",
"chars": 15116,
"preview": "#!/usr/bin/env ruby\n\n# https://en.wikipedia.org/wiki/Turing_pattern\n#\n# > The Turing pattern is a concept introduced by "
},
{
"path": "examples2/README.md",
"chars": 4035,
"preview": "This directory (examples2/) contains code that refers to widgets and functions made available via the official libui-ng "
},
{
"path": "examples2/button.rb",
"chars": 1376,
"preview": "# ============================================================================ #\n# This example (button.rb) shall demons"
},
{
"path": "examples2/checkbox.rb",
"chars": 1665,
"preview": "# ============================================================================ #\n# This example (checkbox.rb) shall demo"
},
{
"path": "examples2/color_button.rb",
"chars": 2304,
"preview": "# ============================================================================ #\n# This example (color_button.rb) shall "
},
{
"path": "examples2/combobox.rb",
"chars": 3994,
"preview": "# ============================================================================ #\n# This example (combobox.rb) shall demo"
},
{
"path": "examples2/date_picker.rb",
"chars": 1649,
"preview": "# ============================================================================ #\n# === DatePicker - a widget to allow th"
},
{
"path": "examples2/editable_combobox.rb",
"chars": 4931,
"preview": "# ============================================================================ #\n# This example (editable_combobox.rb) s"
},
{
"path": "examples2/entry.rb",
"chars": 1901,
"preview": "# ============================================================================ #\n# This example (entry.rb) shall demonst"
},
{
"path": "examples2/font_button.rb",
"chars": 1159,
"preview": "# ============================================================================ #\n# This example (font_button.rb) shall d"
},
{
"path": "examples2/grid.rb",
"chars": 4468,
"preview": "# ============================================================================ #\n# This example (grid.rb) shall demonstr"
},
{
"path": "examples2/multiline_entry.rb",
"chars": 1655,
"preview": "# ============================================================================ #\n# This example (multiline_entry.rb) sha"
},
{
"path": "examples2/password_entry.rb",
"chars": 2213,
"preview": "# ============================================================================ #\n# This example (password_entry.rb) shal"
},
{
"path": "examples2/progress_bar.rb",
"chars": 1033,
"preview": "# ============================================================================ #\n# This example (progress_bar.rb) shall "
},
{
"path": "examples2/search_entry.rb",
"chars": 2303,
"preview": "# ============================================================================ #\n# This example (search_entry.rb) shall "
},
{
"path": "examples2/slider.rb",
"chars": 2355,
"preview": "# ============================================================================ #\n# This example (slider.rb) shall demons"
},
{
"path": "examples2/todo/todo.md",
"chars": 116,
"preview": "This file is no longer necessary - the parent directory keeps track of what is finished and what is yet-to-be-done.\n"
},
{
"path": "examples2/window.rb",
"chars": 5146,
"preview": "# ============================================================================ #\n# This example (window.rb) shall demons"
},
{
"path": "lib/libui/error.rb",
"chars": 232,
"preview": "module LibUI\n # base error class\n class Error < StandardError; end\n\n # LibUI shared library not found error\n class L"
},
{
"path": "lib/libui/ffi.rb",
"chars": 29529,
"preview": "require 'fiddle/import'\nrequire_relative 'fiddle_patch'\nrequire_relative 'error'\n\nmodule LibUI\n\n module FFI\n extend "
},
{
"path": "lib/libui/fiddle_patch.rb",
"chars": 3621,
"preview": "module LibUI\n # This module overrides Fiddle's mtehods\n # - Fiddle::Importer#extern\n # - Fiddle::CParser#parse_signat"
},
{
"path": "lib/libui/libui_base.rb",
"chars": 1950,
"preview": "module LibUI\n module LibUIBase\n FFI.func_map.each_key do |original_method_name|\n name = Utils.convert_to_ruby_m"
},
{
"path": "lib/libui/utils.rb",
"chars": 589,
"preview": "module LibUI\n module Utils\n class << self\n def convert_to_ruby_method(original_method_name)\n underscore("
},
{
"path": "lib/libui/version.rb",
"chars": 37,
"preview": "module LibUI\n VERSION = '0.2.0'\nend\n"
},
{
"path": "lib/libui.rb",
"chars": 7124,
"preview": "require_relative 'libui/version'\nrequire_relative 'libui/utils'\nrequire_relative 'libui/error'\nrequire 'rbconfig'\n\nmodul"
},
{
"path": "libui.gemspec",
"chars": 1307,
"preview": "require_relative 'lib/libui/version'\n\nGem::Specification.new do |spec|\n spec.name = 'libui'\n spec.version "
},
{
"path": "scripts/README.md",
"chars": 291,
"preview": "# Scripts to check for changes in ui.h\n\nHelper scripts for the developer.\n\n## Usage\n\n```sh\nbash ui_diff.sh\n```\n## Requir"
},
{
"path": "scripts/ui_diff.sh",
"chars": 112,
"preview": "#!/usr/bin/env bash\n\ncd \"$(dirname \"$0\")\"\n\ndelta <(./ui_ffi.rb) <(./ui_h.rb)\n# diff <(./ui_ffi.rb) <(./ui_h.rb)\n"
},
{
"path": "scripts/ui_ffi.rb",
"chars": 636,
"preview": "#!/usr/bin/env ruby\n\nlibui_ffi_path = File.expand_path('../lib/libui/ffi.rb', __dir__)\nlibui_path = File.expand_path('.."
},
{
"path": "scripts/ui_h.rb",
"chars": 1023,
"preview": "#!/usr/bin/env ruby\n\nrequire 'open-uri'\nrequire 'tempfile'\n\nUI_H_URL = 'https://raw.githubusercontent.com/libui-ng/libui"
},
{
"path": "test/libui_test.rb",
"chars": 830,
"preview": "require 'test_helper'\n\nclass LibUITest < Minitest::Test\n def test_that_it_has_a_version_number\n refute_nil ::LibUI::"
},
{
"path": "test/test_helper.rb",
"chars": 124,
"preview": "$LOAD_PATH.unshift File.expand_path('../lib', __dir__)\nrequire 'libui'\n\nrequire 'minitest/autorun'\nrequire 'minitest/pri"
},
{
"path": "test/utils_test.rb",
"chars": 441,
"preview": "require 'test_helper'\n\nclass LibUIUtilsTest < Minitest::Test\n def test_convert_to_ruby_method\n rbmethod1 = LibUI::Ut"
},
{
"path": "vendor/LICENSE.md",
"chars": 1098,
"preview": "Copyright (c) 2022 libui-ng authors\n\nCopyright (c) 2014 Pietro Gagliardi\n\nPermission is hereby granted, free of charge, "
},
{
"path": "vendor/README.md",
"chars": 10158,
"preview": "# libui-ng: a portable GUI library for C\n\nFork of [andlabs/libui](https://github.com/andlabs/libui). This README is bein"
}
]
About this extraction
This page contains the full source code of the kojix2/LibUI GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (170.3 KB), approximately 49.0k tokens, and a symbol index with 80 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.