konpeito 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/lib/konpeito/codegen/llvm_generator.rb +35 -0
- data/lib/konpeito/codegen/mruby_backend.rb +42 -10
- data/lib/konpeito/stdlib/clay/clay.rb +16 -0
- data/lib/konpeito/stdlib/clay/clay.rbs +44 -0
- data/lib/konpeito/stdlib/clay/clay_native.c +146 -0
- data/lib/konpeito/stdlib/clay_tui/clay_tui.rb +41 -0
- data/lib/konpeito/stdlib/clay_tui/clay_tui.rbs +111 -0
- data/lib/konpeito/stdlib/clay_tui/clay_tui_native.c +183 -0
- data/lib/konpeito/stdlib/game_framework/game_framework.rb +1552 -0
- data/lib/konpeito/stdlib/kui/kui.rb +533 -2
- data/lib/konpeito/stdlib/kui/kui.rbs +38 -0
- data/lib/konpeito/stdlib/kui/kui_events.rb +17 -0
- data/lib/konpeito/stdlib/kui/kui_gui.rb +243 -7
- data/lib/konpeito/stdlib/kui/kui_theme.rb +12 -4
- data/lib/konpeito/stdlib/kui/kui_theme.rbs +5 -0
- data/lib/konpeito/stdlib/kui/kui_tui.rb +189 -2
- data/lib/konpeito/stdlib/raylib/raylib.rb +27 -0
- data/lib/konpeito/stdlib/raylib/raylib.rbs +81 -0
- data/lib/konpeito/stdlib/raylib/raylib_native.c +29 -0
- data/lib/konpeito/version.rb +1 -1
- metadata +2 -2
- data/lib/konpeito/stdlib/rpg_framework/rpg_framework.rb +0 -668
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed1949148547cf4716d3405d71d9d79413d84201a699fefe98512ddb4202808e
|
|
4
|
+
data.tar.gz: cbcc7ecbea2a4c6edeab12fd5f20d5221e9dca2e379906c728d37e16b0168bea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ad8a635a74b09378cacf342e14a9fdba84434d29d3657071a72fb42d800dd8cfe84bc5a9f66ca81fd48852b880f6896c8a4aff5d83c12be03a8cbf8208b5b5d
|
|
7
|
+
data.tar.gz: f1635c6083bd97079fff23d07e3fe5232eadaffc21aeccdc6718689569da8afe50eba3252a188eb2aff42318317ed0ec18e5d2e1b190ac63679c0b354a27c11d
|
data/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-03-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **KUI widget expansion**: text_input, checkbox, radio, toggle, spinner, status_bar,
|
|
14
|
+
selectable_list, list_item, table (header/row/cell), modal, tab_bar, tab_button,
|
|
15
|
+
tab_content — all with keyboard focus support and click-to-focus
|
|
16
|
+
- **KUI focus system**: Tab/arrow key navigation across all interactive widgets with
|
|
17
|
+
focus highlight and Enter key activation
|
|
18
|
+
- **KUI extended key input**: `kui_char_pressed()`, `kui_mod_pressed()`,
|
|
19
|
+
Delete/Home/End/PgUp/PgDn/F1-F12 key constants
|
|
20
|
+
- **KUI text buffer API**: `kui_textbuf_copy`, `kui_textbuf_render` for dynamic content
|
|
21
|
+
- **KUI GUI backend**: Full GUI implementations for all new widgets (Clay+Raylib)
|
|
22
|
+
- **Game framework expansion**: tween/easing (linear, quad, cubic, bounce, elastic),
|
|
23
|
+
screen shake, scene transitions (fade), FSM, timer system, parallax scrolling,
|
|
24
|
+
simple physics (AABB, gravity, friction), particle system, object pool, grid/tile
|
|
25
|
+
utilities, debug overlay (FPS, collision rects), gamepad abstraction, save/load
|
|
26
|
+
- **Game showcase demo**: "Coin Dash" score-attack platformer demonstrating
|
|
27
|
+
game framework API (physics, particles, tween, FSM, parallax, Camera2D)
|
|
28
|
+
- **Physics demo**: Platformer demo showcasing NativeArray-based physics
|
|
29
|
+
- **KUI form demo**: Text input, checkbox, radio, toggle showcase
|
|
30
|
+
- **KUI tabs demo**: Tab bar, table, modal dialog, selectable list, status bar showcase
|
|
31
|
+
- **KUI showcase demo**: Rich GUI app demonstrating all v0.8.0 widgets in a 4-tab layout
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- **NativeArray SEGV as function argument**: `convert_value()` lacked
|
|
35
|
+
`[:native_array, :value]` case, causing raw LLVM pointers where Ruby VALUEs
|
|
36
|
+
were expected
|
|
37
|
+
- **NativeArray SEGV in compound expressions**: `get_effective_unboxed_type()`
|
|
38
|
+
only checked `@variable_types` for LoadLocal nodes, causing incorrect float path
|
|
39
|
+
for integer arithmetic in if conditions
|
|
40
|
+
- **KUI text input isolation**: Only focused text_input processes keyboard input
|
|
41
|
+
- **KUI toggle flip-back**: Filter out termbox2 mouse release events (key 65509)
|
|
42
|
+
- **KUI modal layout**: Reduced TUI padding from 16 to 1 char cells
|
|
43
|
+
- **mruby keyword argument handling**: Optional keyword hash with `MRB_ARGS_ARG`
|
|
44
|
+
- **KUI scroll containers not scrolling**: `Clay_GetScrollOffset()` was not called
|
|
45
|
+
in `konpeito_clay_scroll` / `konpeito_clay_tui_scroll`, so `childOffset` remained
|
|
46
|
+
zero and scroll position was never visually applied
|
|
47
|
+
|
|
48
|
+
## [0.7.1] - 2026-03-14
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
- **rescue handler callback crash**: `generate_rescue_handler_with_global_flag_callback` was restoring `@mruby_gc_arena_alloca` without saving it first, causing `NameError` in begin/rescue/else code generation.
|
|
52
|
+
|
|
10
53
|
## [0.7.0] - 2026-03-14
|
|
11
54
|
|
|
12
55
|
### Added
|
|
13
56
|
- **KUI declarative UI framework**: Pure Ruby DSL for building GUI/TUI apps with a single codebase. Wraps Clay+Raylib (GUI) or ClayTUI (TUI). Widgets: `vpanel`, `hpanel`, `fixed_panel`, `label`, `label_num`, `button`, `menu_item`, `spacer`, `divider`, `progress_bar`. Theme system and unified key event abstraction.
|
|
14
57
|
- **ClayTUI stdlib**: Terminal UI backend using Clay layout engine + termbox2 rendering. Auto-detected via `ClayTUI` module reference.
|
|
15
58
|
- **KonpeitoShell stdlib**: Shell execution (`exec`, `system`), environment variables (`getenv`, `setenv`), and file I/O (`read_file`, `write_file`, `append_file`, `file_exists`) for mruby backend.
|
|
16
|
-
- **
|
|
59
|
+
- **Game framework stdlib**: Reusable 2D game components (tilemap, sprites, camera, battle system) for raylib-based games.
|
|
17
60
|
- **Stdlib auto-detection expansion**: `KonpeitoJSON`, `KonpeitoHTTP`, `KonpeitoCrypto`, `KonpeitoCompression` added to `STDLIB_MODULE_MAP` for automatic RBS injection when referenced in source code.
|
|
18
61
|
- **KUI auto-path resolution**: `require "kui_gui"` / `require "kui_tui"` works without `-I` flags — KUI stdlib directory is automatically included in compiler search paths.
|
|
19
62
|
- **KUI example apps**: Counter (GUI + TUI), minimal hello, multi-page dashboard with sidebar navigation.
|
|
@@ -336,6 +379,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
336
379
|
- `%a{extern}` - external C struct wrappers
|
|
337
380
|
- `%a{simd}` - SIMD vectorization
|
|
338
381
|
|
|
382
|
+
[0.8.0]: https://github.com/i2y/konpeito/compare/v0.7.1...v0.8.0
|
|
383
|
+
[0.7.1]: https://github.com/i2y/konpeito/compare/v0.7.0...v0.7.1
|
|
384
|
+
[0.7.0]: https://github.com/i2y/konpeito/compare/v0.6.0...v0.7.0
|
|
385
|
+
[0.6.0]: https://github.com/i2y/konpeito/compare/v0.5.0...v0.6.0
|
|
386
|
+
[0.5.0]: https://github.com/i2y/konpeito/compare/v0.4.2...v0.5.0
|
|
387
|
+
[0.4.2]: https://github.com/i2y/konpeito/compare/v0.4.1...v0.4.2
|
|
339
388
|
[0.4.1]: https://github.com/i2y/konpeito/compare/v0.4.0...v0.4.1
|
|
340
389
|
[0.4.0]: https://github.com/i2y/konpeito/compare/v0.3.1...v0.4.0
|
|
341
390
|
[0.3.1]: https://github.com/i2y/konpeito/compare/v0.3.0...v0.3.1
|
|
@@ -1597,6 +1597,24 @@ module Konpeito
|
|
|
1597
1597
|
boxed_value = func.params[i + 1] # +1 to skip self
|
|
1598
1598
|
type_tag = @variable_types[param.name]
|
|
1599
1599
|
|
|
1600
|
+
if type_tag == :native_array
|
|
1601
|
+
# NativeArray parameter: the VALUE is actually a pointer address (via ptr2int).
|
|
1602
|
+
# Reconstruct the pointer and store directly in @variables (not alloca).
|
|
1603
|
+
# generate_load_local reads NativeArray values from @variables, not allocas.
|
|
1604
|
+
array_ptr = @builder.int2ptr(boxed_value, ptr_type, "#{param.name}_ptr")
|
|
1605
|
+
@variables[param.name] = array_ptr
|
|
1606
|
+
# Propagate element type and length from the parameter's HM type if available
|
|
1607
|
+
if param.type.is_a?(TypeChecker::Types::NativeArrayType)
|
|
1608
|
+
elem_type = param.type.element_type
|
|
1609
|
+
@variables["#{param.name}_len"] = LLVM::Int64.from_i(param.type.size || 0)
|
|
1610
|
+
if elem_type && elem_type != :Int64 && elem_type != :Float64
|
|
1611
|
+
@native_array_class_types ||= {}
|
|
1612
|
+
@native_array_class_types[param.name] = elem_type
|
|
1613
|
+
end
|
|
1614
|
+
end
|
|
1615
|
+
next
|
|
1616
|
+
end
|
|
1617
|
+
|
|
1600
1618
|
# Unbox parameter if it's a numeric type
|
|
1601
1619
|
value_to_store = case type_tag
|
|
1602
1620
|
when :i64
|
|
@@ -2004,6 +2022,10 @@ module Konpeito
|
|
|
2004
2022
|
[LLVM::Int64, :i64]
|
|
2005
2023
|
elsif float_type?(ruby_type)
|
|
2006
2024
|
[LLVM::Double, :double]
|
|
2025
|
+
elsif ruby_type.is_a?(TypeChecker::Types::NativeArrayType)
|
|
2026
|
+
# NativeArray parameters: alloca holds VALUE (i64) but type is :native_array
|
|
2027
|
+
# The actual pointer is reconstructed from the VALUE in parameter setup
|
|
2028
|
+
[value_type, :native_array]
|
|
2007
2029
|
else
|
|
2008
2030
|
[value_type, :value]
|
|
2009
2031
|
end
|
|
@@ -3029,6 +3051,9 @@ module Konpeito
|
|
|
3029
3051
|
when [:native_class, :value]
|
|
3030
3052
|
# NativeClass struct pointer to VALUE (i64)
|
|
3031
3053
|
@builder.ptr2int(value, LLVM::Int64)
|
|
3054
|
+
when [:native_array, :value]
|
|
3055
|
+
# NativeArray pointer to VALUE (i64) — encode pointer address as integer
|
|
3056
|
+
@builder.ptr2int(value, LLVM::Int64)
|
|
3032
3057
|
when [:i64, :double]
|
|
3033
3058
|
@builder.si2fp(value, LLVM::Double)
|
|
3034
3059
|
when [:double, :i64]
|
|
@@ -7372,6 +7397,15 @@ module Konpeito
|
|
|
7372
7397
|
return type_tag if type_tag == :i64 || type_tag == :double
|
|
7373
7398
|
end
|
|
7374
7399
|
|
|
7400
|
+
# Check @variable_types for any instruction with result_var.
|
|
7401
|
+
# This is critical for sub-expressions (e.g. NativeArrayGet, Call) whose
|
|
7402
|
+
# HIR type may be an unresolved TypeVar but whose actual generated type
|
|
7403
|
+
# is correctly tracked in @variable_types.
|
|
7404
|
+
if hir_value.is_a?(HIR::Instruction) && hir_value.result_var
|
|
7405
|
+
type_tag = @variable_types[hir_value.result_var]
|
|
7406
|
+
return type_tag if type_tag == :i64 || type_tag == :double
|
|
7407
|
+
end
|
|
7408
|
+
|
|
7375
7409
|
# Check HIR type
|
|
7376
7410
|
hir_type = get_type(hir_value)
|
|
7377
7411
|
if integer_type?(hir_type)
|
|
@@ -8469,6 +8503,7 @@ module Konpeito
|
|
|
8469
8503
|
saved_block_callback_self = @block_callback_self
|
|
8470
8504
|
saved_rescue_escape_array = @rescue_escape_array
|
|
8471
8505
|
saved_rescue_escape_indices = @rescue_escape_indices
|
|
8506
|
+
saved_gc_arena_alloca = @mruby_gc_arena_alloca
|
|
8472
8507
|
|
|
8473
8508
|
# In normal mode: params[0] = self. In escape mode, set @block_callback_self after unpacking.
|
|
8474
8509
|
@block_callback_self = callback_func.params[0] unless escape_var_names
|
|
@@ -78,13 +78,17 @@ module Konpeito
|
|
|
78
78
|
# Generate license file alongside the executable
|
|
79
79
|
generate_license_file
|
|
80
80
|
ensure
|
|
81
|
-
# Clean up intermediate files
|
|
81
|
+
# Clean up intermediate files (keep .ll for debugging if ENV['KONPEITO_KEEP_IR'] is set)
|
|
82
|
+
keep_ir = ENV['KONPEITO_KEEP_IR']
|
|
82
83
|
all_temps = [ir_file, obj_file, init_c_file, init_obj_file, helpers_obj_file] + extra_obj_files
|
|
83
84
|
all_temps.each do |f|
|
|
85
|
+
next if keep_ir && f&.end_with?('.ll')
|
|
84
86
|
FileUtils.rm_f(f) if f && File.exist?(f)
|
|
85
87
|
end
|
|
86
88
|
# Also clean optimized IR if it was generated
|
|
87
|
-
|
|
89
|
+
unless keep_ir
|
|
90
|
+
FileUtils.rm_f("#{ir_file}.opt.ll") if File.exist?("#{ir_file}.opt.ll")
|
|
91
|
+
end
|
|
88
92
|
end
|
|
89
93
|
end
|
|
90
94
|
|
|
@@ -302,7 +306,7 @@ module Konpeito
|
|
|
302
306
|
|
|
303
307
|
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
304
308
|
wrapper = "mrb_wrap_#{mangled_name}"
|
|
305
|
-
mrb_args =
|
|
309
|
+
mrb_args = effective_mrb_args(mangled_name, arity)
|
|
306
310
|
lines << " mrb_define_method(mrb, #{module_var}, \"#{method_name}\", #{wrapper}, #{mrb_args});"
|
|
307
311
|
end
|
|
308
312
|
|
|
@@ -316,7 +320,7 @@ module Konpeito
|
|
|
316
320
|
|
|
317
321
|
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
318
322
|
wrapper = "mrb_wrap_#{mangled_name}"
|
|
319
|
-
mrb_args =
|
|
323
|
+
mrb_args = effective_mrb_args(mangled_name, arity)
|
|
320
324
|
lines << " mrb_define_class_method(mrb, #{module_var}, \"#{method_name}\", #{wrapper}, #{mrb_args});"
|
|
321
325
|
end
|
|
322
326
|
|
|
@@ -372,7 +376,7 @@ module Konpeito
|
|
|
372
376
|
|
|
373
377
|
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
374
378
|
wrapper = "mrb_wrap_#{mangled_name}"
|
|
375
|
-
mrb_args =
|
|
379
|
+
mrb_args = effective_mrb_args(mangled_name, arity)
|
|
376
380
|
lines << " mrb_define_method(mrb, #{class_var}, \"#{method_name}\", #{wrapper}, #{mrb_args});"
|
|
377
381
|
end
|
|
378
382
|
|
|
@@ -386,7 +390,7 @@ module Konpeito
|
|
|
386
390
|
|
|
387
391
|
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
388
392
|
wrapper = "mrb_wrap_#{mangled_name}"
|
|
389
|
-
mrb_args =
|
|
393
|
+
mrb_args = effective_mrb_args(mangled_name, arity)
|
|
390
394
|
lines << " mrb_define_class_method(mrb, #{class_var}, \"#{method_name}\", #{wrapper}, #{mrb_args});"
|
|
391
395
|
end
|
|
392
396
|
|
|
@@ -408,7 +412,7 @@ module Konpeito
|
|
|
408
412
|
|
|
409
413
|
arity = llvm_generator.variadic_functions[mangled_name] ? -1 : func.params.size - 1
|
|
410
414
|
wrapper = "mrb_wrap_#{mangled_name}"
|
|
411
|
-
mrb_args =
|
|
415
|
+
mrb_args = effective_mrb_args(mangled_name, arity)
|
|
412
416
|
lines << " mrb_define_method(mrb, mrb->object_class, \"#{func_def.name}\", #{wrapper}, #{mrb_args});"
|
|
413
417
|
end
|
|
414
418
|
|
|
@@ -456,13 +460,14 @@ module Konpeito
|
|
|
456
460
|
else
|
|
457
461
|
arity = llvm_func.params.size - 1 # Subtract self
|
|
458
462
|
|
|
459
|
-
# Check if this function has
|
|
460
|
-
#
|
|
461
|
-
# when no keywords are specified
|
|
463
|
+
# Check if this function has keyword args.
|
|
464
|
+
# The kwargs hash arg should be optional — caller may pass only
|
|
465
|
+
# positional args when no keywords are specified.
|
|
462
466
|
func_base_name = mangled_name.sub(/\Arn_/, "")
|
|
463
467
|
kw_info = llvm_generator.keyword_param_functions[func_base_name] ||
|
|
464
468
|
llvm_generator.keyword_param_functions[func_base_name.to_sym]
|
|
465
469
|
kwargs_only = kw_info && kw_info[:regular_count] == 0 && arity == 1
|
|
470
|
+
has_mixed_kwargs = kw_info && kw_info[:regular_count] > 0 && arity > 1
|
|
466
471
|
|
|
467
472
|
lines << "static mrb_value #{wrapper_name}(mrb_state *mrb, mrb_value self) {"
|
|
468
473
|
if kwargs_only
|
|
@@ -470,6 +475,15 @@ module Konpeito
|
|
|
470
475
|
lines << " mrb_value a0 = mrb_nil_value(), _block = mrb_nil_value();"
|
|
471
476
|
lines << " mrb_get_args(mrb, \"|o&\", &a0, &_block);"
|
|
472
477
|
lines << " if (mrb_nil_p(a0)) a0 = mrb_hash_new(mrb);"
|
|
478
|
+
elsif has_mixed_kwargs
|
|
479
|
+
# Positional args + optional kwargs hash — last arg defaults to empty hash
|
|
480
|
+
kw_hash_idx = arity - 1
|
|
481
|
+
req_count = kw_info[:regular_count]
|
|
482
|
+
lines << " mrb_value #{(0...arity).map { |i| "a#{i} = mrb_nil_value()" }.join(', ')}, _block = mrb_nil_value();"
|
|
483
|
+
format_str = "o" * req_count + "|o&"
|
|
484
|
+
args_list = (0...arity).map { |i| "&a#{i}" }.join(", ") + ", &_block"
|
|
485
|
+
lines << " mrb_get_args(mrb, \"#{format_str}\", #{args_list});"
|
|
486
|
+
lines << " if (mrb_nil_p(a#{kw_hash_idx})) a#{kw_hash_idx} = mrb_hash_new(mrb);"
|
|
473
487
|
elsif arity > 0
|
|
474
488
|
lines << " mrb_value #{(0...arity).map { |i| "a#{i}" }.join(', ')}, _block;"
|
|
475
489
|
format_str = "o" * arity + "&"
|
|
@@ -804,6 +818,24 @@ module Konpeito
|
|
|
804
818
|
end
|
|
805
819
|
end
|
|
806
820
|
|
|
821
|
+
# Compute mrb_args for a function, accounting for optional keyword hash.
|
|
822
|
+
# Functions with keyword params can be called with or without the keyword
|
|
823
|
+
# hash argument, so the kwargs arg must be registered as optional.
|
|
824
|
+
def effective_mrb_args(mangled_name, arity)
|
|
825
|
+
return arity_to_mrb_args(arity) if arity <= 0
|
|
826
|
+
|
|
827
|
+
func_base_name = mangled_name.sub(/\Arn_/, "")
|
|
828
|
+
kw_info = llvm_generator.keyword_param_functions[func_base_name] ||
|
|
829
|
+
llvm_generator.keyword_param_functions[func_base_name.to_sym]
|
|
830
|
+
if kw_info
|
|
831
|
+
# Keyword hash is optional — use MRB_ARGS_ARG(required, optional)
|
|
832
|
+
req = kw_info[:regular_count]
|
|
833
|
+
"MRB_ARGS_ARG(#{req}, #{arity - req})"
|
|
834
|
+
else
|
|
835
|
+
arity_to_mrb_args(arity)
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
|
|
807
839
|
def resolve_superclass_mrb_expr(superclass, non_native_classes)
|
|
808
840
|
return "mrb->object_class" unless superclass
|
|
809
841
|
|
|
@@ -63,4 +63,20 @@ module Clay
|
|
|
63
63
|
def self.sizing_percent() end
|
|
64
64
|
def self.left_to_right() end
|
|
65
65
|
def self.top_to_bottom() end
|
|
66
|
+
|
|
67
|
+
# Text Buffer System
|
|
68
|
+
def self.textbuf_clear(id) end
|
|
69
|
+
def self.textbuf_copy(dst, src) end
|
|
70
|
+
def self.textbuf_putchar(id, ch) end
|
|
71
|
+
def self.textbuf_backspace(id) end
|
|
72
|
+
def self.textbuf_delete(id) end
|
|
73
|
+
def self.textbuf_cursor_left(id) end
|
|
74
|
+
def self.textbuf_cursor_right(id) end
|
|
75
|
+
def self.textbuf_cursor_home(id) end
|
|
76
|
+
def self.textbuf_cursor_end(id) end
|
|
77
|
+
def self.textbuf_len(id) end
|
|
78
|
+
def self.textbuf_cursor(id) end
|
|
79
|
+
def self.textbuf_render(id, fid, fsz, r, g, b) end
|
|
80
|
+
def self.textbuf_render_range(id, start_pos, end_pos, fid, fsz, r, g, b) end
|
|
81
|
+
def self.text_char(ch, fid, fsz, r, g, b) end
|
|
66
82
|
end
|
|
@@ -166,4 +166,48 @@ module Clay
|
|
|
166
166
|
|
|
167
167
|
%a{cfunc: "konpeito_clay_top_to_bottom"}
|
|
168
168
|
def self.top_to_bottom: () -> Integer
|
|
169
|
+
|
|
170
|
+
# ── Text Buffer System ──
|
|
171
|
+
|
|
172
|
+
%a{cfunc: "konpeito_clay_textbuf_clear"}
|
|
173
|
+
def self.textbuf_clear: (Integer id) -> void
|
|
174
|
+
|
|
175
|
+
%a{cfunc: "konpeito_clay_textbuf_copy"}
|
|
176
|
+
def self.textbuf_copy: (Integer dst, Integer src) -> void
|
|
177
|
+
|
|
178
|
+
%a{cfunc: "konpeito_clay_textbuf_putchar"}
|
|
179
|
+
def self.textbuf_putchar: (Integer id, Integer ch) -> void
|
|
180
|
+
|
|
181
|
+
%a{cfunc: "konpeito_clay_textbuf_backspace"}
|
|
182
|
+
def self.textbuf_backspace: (Integer id) -> void
|
|
183
|
+
|
|
184
|
+
%a{cfunc: "konpeito_clay_textbuf_delete"}
|
|
185
|
+
def self.textbuf_delete: (Integer id) -> void
|
|
186
|
+
|
|
187
|
+
%a{cfunc: "konpeito_clay_textbuf_cursor_left"}
|
|
188
|
+
def self.textbuf_cursor_left: (Integer id) -> void
|
|
189
|
+
|
|
190
|
+
%a{cfunc: "konpeito_clay_textbuf_cursor_right"}
|
|
191
|
+
def self.textbuf_cursor_right: (Integer id) -> void
|
|
192
|
+
|
|
193
|
+
%a{cfunc: "konpeito_clay_textbuf_cursor_home"}
|
|
194
|
+
def self.textbuf_cursor_home: (Integer id) -> void
|
|
195
|
+
|
|
196
|
+
%a{cfunc: "konpeito_clay_textbuf_cursor_end"}
|
|
197
|
+
def self.textbuf_cursor_end: (Integer id) -> void
|
|
198
|
+
|
|
199
|
+
%a{cfunc: "konpeito_clay_textbuf_len"}
|
|
200
|
+
def self.textbuf_len: (Integer id) -> Integer
|
|
201
|
+
|
|
202
|
+
%a{cfunc: "konpeito_clay_textbuf_cursor"}
|
|
203
|
+
def self.textbuf_cursor: (Integer id) -> Integer
|
|
204
|
+
|
|
205
|
+
%a{cfunc: "konpeito_clay_textbuf_render"}
|
|
206
|
+
def self.textbuf_render: (Integer id, Integer fid, Integer fsz, Float r, Float g, Float b) -> void
|
|
207
|
+
|
|
208
|
+
%a{cfunc: "konpeito_clay_textbuf_render_range"}
|
|
209
|
+
def self.textbuf_render_range: (Integer id, Integer start, Integer end_pos, Integer fid, Integer fsz, Float r, Float g, Float b) -> void
|
|
210
|
+
|
|
211
|
+
%a{cfunc: "konpeito_clay_text_char"}
|
|
212
|
+
def self.text_char: (Integer ch, Integer fid, Integer fsz, Float r, Float g, Float b) -> void
|
|
169
213
|
end
|
|
@@ -275,6 +275,7 @@ void konpeito_clay_border(double r, double g, double b, double a,
|
|
|
275
275
|
void konpeito_clay_scroll(int horizontal, int vertical) {
|
|
276
276
|
g_decl.clip.horizontal = horizontal != 0;
|
|
277
277
|
g_decl.clip.vertical = vertical != 0;
|
|
278
|
+
g_decl.clip.childOffset = Clay_GetScrollOffset();
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
/* Floating element */
|
|
@@ -491,3 +492,148 @@ int konpeito_clay_sizing_fixed(void) { return 2; }
|
|
|
491
492
|
int konpeito_clay_sizing_percent(void){ return 3; }
|
|
492
493
|
int konpeito_clay_left_to_right(void) { return 0; }
|
|
493
494
|
int konpeito_clay_top_to_bottom(void) { return 1; }
|
|
495
|
+
|
|
496
|
+
/* ═══════════════════════════════════════════
|
|
497
|
+
* Text Buffer System
|
|
498
|
+
* ═══════════════════════════════════════════
|
|
499
|
+
* 8 independent text buffers for text_input widgets.
|
|
500
|
+
* Each buffer holds up to 255 characters (256 bytes including null).
|
|
501
|
+
* Buffer operations are GC-free — no mruby String allocation.
|
|
502
|
+
*/
|
|
503
|
+
|
|
504
|
+
#define TEXTBUF_COUNT 8
|
|
505
|
+
#define TEXTBUF_SIZE 256
|
|
506
|
+
|
|
507
|
+
static char g_textbufs[TEXTBUF_COUNT][TEXTBUF_SIZE];
|
|
508
|
+
static int g_textbuf_lens[TEXTBUF_COUNT];
|
|
509
|
+
static int g_textbuf_cursors[TEXTBUF_COUNT];
|
|
510
|
+
|
|
511
|
+
void konpeito_clay_textbuf_clear(int id) {
|
|
512
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
513
|
+
g_textbufs[id][0] = '\0';
|
|
514
|
+
g_textbuf_lens[id] = 0;
|
|
515
|
+
g_textbuf_cursors[id] = 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
void konpeito_clay_textbuf_copy(int dst, int src) {
|
|
519
|
+
if (dst < 0 || dst >= TEXTBUF_COUNT) return;
|
|
520
|
+
if (src < 0 || src >= TEXTBUF_COUNT) return;
|
|
521
|
+
int len = g_textbuf_lens[src];
|
|
522
|
+
for (int i = 0; i <= len; i++) {
|
|
523
|
+
g_textbufs[dst][i] = g_textbufs[src][i];
|
|
524
|
+
}
|
|
525
|
+
g_textbuf_lens[dst] = len;
|
|
526
|
+
g_textbuf_cursors[dst] = len;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
void konpeito_clay_textbuf_putchar(int id, int ch) {
|
|
530
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
531
|
+
int len = g_textbuf_lens[id];
|
|
532
|
+
int cur = g_textbuf_cursors[id];
|
|
533
|
+
if (len >= TEXTBUF_SIZE - 1) return;
|
|
534
|
+
for (int i = len; i > cur; i--) {
|
|
535
|
+
g_textbufs[id][i] = g_textbufs[id][i - 1];
|
|
536
|
+
}
|
|
537
|
+
g_textbufs[id][cur] = (char)ch;
|
|
538
|
+
g_textbuf_lens[id] = len + 1;
|
|
539
|
+
g_textbuf_cursors[id] = cur + 1;
|
|
540
|
+
g_textbufs[id][len + 1] = '\0';
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
void konpeito_clay_textbuf_backspace(int id) {
|
|
544
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
545
|
+
int cur = g_textbuf_cursors[id];
|
|
546
|
+
int len = g_textbuf_lens[id];
|
|
547
|
+
if (cur <= 0) return;
|
|
548
|
+
for (int i = cur - 1; i < len - 1; i++) {
|
|
549
|
+
g_textbufs[id][i] = g_textbufs[id][i + 1];
|
|
550
|
+
}
|
|
551
|
+
g_textbuf_lens[id] = len - 1;
|
|
552
|
+
g_textbuf_cursors[id] = cur - 1;
|
|
553
|
+
g_textbufs[id][len - 1] = '\0';
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
void konpeito_clay_textbuf_delete(int id) {
|
|
557
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
558
|
+
int cur = g_textbuf_cursors[id];
|
|
559
|
+
int len = g_textbuf_lens[id];
|
|
560
|
+
if (cur >= len) return;
|
|
561
|
+
for (int i = cur; i < len - 1; i++) {
|
|
562
|
+
g_textbufs[id][i] = g_textbufs[id][i + 1];
|
|
563
|
+
}
|
|
564
|
+
g_textbuf_lens[id] = len - 1;
|
|
565
|
+
g_textbufs[id][len - 1] = '\0';
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
void konpeito_clay_textbuf_cursor_left(int id) {
|
|
569
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
570
|
+
if (g_textbuf_cursors[id] > 0) g_textbuf_cursors[id]--;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
void konpeito_clay_textbuf_cursor_right(int id) {
|
|
574
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
575
|
+
if (g_textbuf_cursors[id] < g_textbuf_lens[id]) g_textbuf_cursors[id]++;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
void konpeito_clay_textbuf_cursor_home(int id) {
|
|
579
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
580
|
+
g_textbuf_cursors[id] = 0;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
void konpeito_clay_textbuf_cursor_end(int id) {
|
|
584
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
585
|
+
g_textbuf_cursors[id] = g_textbuf_lens[id];
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
int konpeito_clay_textbuf_len(int id) {
|
|
589
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return 0;
|
|
590
|
+
return g_textbuf_lens[id];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
int konpeito_clay_textbuf_cursor(int id) {
|
|
594
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return 0;
|
|
595
|
+
return g_textbuf_cursors[id];
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
void konpeito_clay_textbuf_render(int id, int fid, int fsz, double r, double g, double b) {
|
|
599
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
600
|
+
if (g_textbuf_lens[id] == 0) return;
|
|
601
|
+
flush_config();
|
|
602
|
+
Clay_TextElementConfig cfg = {
|
|
603
|
+
.textColor = {(float)r, (float)g, (float)b, 255.0f},
|
|
604
|
+
.fontId = (uint16_t)fid, .fontSize = (uint16_t)fsz, .wrapMode = 0
|
|
605
|
+
};
|
|
606
|
+
Clay_String cs = make_string(g_textbufs[id]);
|
|
607
|
+
Clay__OpenTextElement(cs, Clay__StoreTextElementConfig(cfg));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
void konpeito_clay_textbuf_render_range(int id, int start, int end, int fid, int fsz,
|
|
611
|
+
double r, double g, double b) {
|
|
612
|
+
if (id < 0 || id >= TEXTBUF_COUNT) return;
|
|
613
|
+
int len = g_textbuf_lens[id];
|
|
614
|
+
if (start >= len || start >= end) return;
|
|
615
|
+
if (end > len) end = len;
|
|
616
|
+
int range_len = end - start;
|
|
617
|
+
flush_config();
|
|
618
|
+
const char *pooled = pool_string(g_textbufs[id] + start, range_len);
|
|
619
|
+
Clay_String cs = {.isStaticallyAllocated = false, .length = range_len, .chars = pooled};
|
|
620
|
+
Clay_TextElementConfig cfg = {
|
|
621
|
+
.textColor = {(float)r, (float)g, (float)b, 255.0f},
|
|
622
|
+
.fontId = (uint16_t)fid, .fontSize = (uint16_t)fsz, .wrapMode = 0
|
|
623
|
+
};
|
|
624
|
+
Clay__OpenTextElement(cs, Clay__StoreTextElementConfig(cfg));
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
void konpeito_clay_text_char(int ch, int fid, int fsz, double r, double g, double b) {
|
|
628
|
+
if (ch < 32 || ch > 126) return;
|
|
629
|
+
char buf[2];
|
|
630
|
+
buf[0] = (char)ch;
|
|
631
|
+
buf[1] = '\0';
|
|
632
|
+
flush_config();
|
|
633
|
+
Clay_TextElementConfig cfg = {
|
|
634
|
+
.textColor = {(float)r, (float)g, (float)b, 255.0f},
|
|
635
|
+
.fontId = (uint16_t)fid, .fontSize = (uint16_t)fsz, .wrapMode = 0
|
|
636
|
+
};
|
|
637
|
+
Clay_String cs = make_string(buf);
|
|
638
|
+
Clay__OpenTextElement(cs, Clay__StoreTextElementConfig(cfg));
|
|
639
|
+
}
|
|
@@ -109,4 +109,45 @@ module ClayTUI
|
|
|
109
109
|
|
|
110
110
|
# Color Helper
|
|
111
111
|
def self.rgb(r, g, b) end
|
|
112
|
+
|
|
113
|
+
# Extended Key Constants
|
|
114
|
+
def self.key_delete() end
|
|
115
|
+
def self.key_home() end
|
|
116
|
+
def self.key_end() end
|
|
117
|
+
def self.key_pgup() end
|
|
118
|
+
def self.key_pgdn() end
|
|
119
|
+
def self.key_f1() end
|
|
120
|
+
def self.key_f2() end
|
|
121
|
+
def self.key_f3() end
|
|
122
|
+
def self.key_f4() end
|
|
123
|
+
def self.key_f5() end
|
|
124
|
+
def self.key_f6() end
|
|
125
|
+
def self.key_f7() end
|
|
126
|
+
def self.key_f8() end
|
|
127
|
+
def self.key_f9() end
|
|
128
|
+
def self.key_f10() end
|
|
129
|
+
def self.key_f11() end
|
|
130
|
+
def self.key_f12() end
|
|
131
|
+
|
|
132
|
+
# Modifier Keys
|
|
133
|
+
def self.event_mod() end
|
|
134
|
+
def self.mod_alt() end
|
|
135
|
+
def self.mod_ctrl() end
|
|
136
|
+
def self.mod_shift() end
|
|
137
|
+
|
|
138
|
+
# Text Buffer System
|
|
139
|
+
def self.textbuf_clear(id) end
|
|
140
|
+
def self.textbuf_copy(dst, src) end
|
|
141
|
+
def self.textbuf_putchar(id, ch) end
|
|
142
|
+
def self.textbuf_backspace(id) end
|
|
143
|
+
def self.textbuf_delete(id) end
|
|
144
|
+
def self.textbuf_cursor_left(id) end
|
|
145
|
+
def self.textbuf_cursor_right(id) end
|
|
146
|
+
def self.textbuf_cursor_home(id) end
|
|
147
|
+
def self.textbuf_cursor_end(id) end
|
|
148
|
+
def self.textbuf_len(id) end
|
|
149
|
+
def self.textbuf_cursor(id) end
|
|
150
|
+
def self.textbuf_render(id, r, g, b) end
|
|
151
|
+
def self.textbuf_render_range(id, start_pos, end_pos, r, g, b) end
|
|
152
|
+
def self.text_char(ch, r, g, b) end
|
|
112
153
|
end
|
|
@@ -255,4 +255,115 @@ module ClayTUI
|
|
|
255
255
|
# Build a truecolor value from RGB components
|
|
256
256
|
%a{cfunc: "konpeito_clay_tui_rgb"}
|
|
257
257
|
def self.rgb: (Integer r, Integer g, Integer b) -> Integer
|
|
258
|
+
|
|
259
|
+
# ── Extended Key Constants ──
|
|
260
|
+
|
|
261
|
+
%a{cfunc: "konpeito_clay_tui_key_delete"}
|
|
262
|
+
def self.key_delete: () -> Integer
|
|
263
|
+
|
|
264
|
+
%a{cfunc: "konpeito_clay_tui_key_home"}
|
|
265
|
+
def self.key_home: () -> Integer
|
|
266
|
+
|
|
267
|
+
%a{cfunc: "konpeito_clay_tui_key_end"}
|
|
268
|
+
def self.key_end: () -> Integer
|
|
269
|
+
|
|
270
|
+
%a{cfunc: "konpeito_clay_tui_key_pgup"}
|
|
271
|
+
def self.key_pgup: () -> Integer
|
|
272
|
+
|
|
273
|
+
%a{cfunc: "konpeito_clay_tui_key_pgdn"}
|
|
274
|
+
def self.key_pgdn: () -> Integer
|
|
275
|
+
|
|
276
|
+
%a{cfunc: "konpeito_clay_tui_key_f1"}
|
|
277
|
+
def self.key_f1: () -> Integer
|
|
278
|
+
|
|
279
|
+
%a{cfunc: "konpeito_clay_tui_key_f2"}
|
|
280
|
+
def self.key_f2: () -> Integer
|
|
281
|
+
|
|
282
|
+
%a{cfunc: "konpeito_clay_tui_key_f3"}
|
|
283
|
+
def self.key_f3: () -> Integer
|
|
284
|
+
|
|
285
|
+
%a{cfunc: "konpeito_clay_tui_key_f4"}
|
|
286
|
+
def self.key_f4: () -> Integer
|
|
287
|
+
|
|
288
|
+
%a{cfunc: "konpeito_clay_tui_key_f5"}
|
|
289
|
+
def self.key_f5: () -> Integer
|
|
290
|
+
|
|
291
|
+
%a{cfunc: "konpeito_clay_tui_key_f6"}
|
|
292
|
+
def self.key_f6: () -> Integer
|
|
293
|
+
|
|
294
|
+
%a{cfunc: "konpeito_clay_tui_key_f7"}
|
|
295
|
+
def self.key_f7: () -> Integer
|
|
296
|
+
|
|
297
|
+
%a{cfunc: "konpeito_clay_tui_key_f8"}
|
|
298
|
+
def self.key_f8: () -> Integer
|
|
299
|
+
|
|
300
|
+
%a{cfunc: "konpeito_clay_tui_key_f9"}
|
|
301
|
+
def self.key_f9: () -> Integer
|
|
302
|
+
|
|
303
|
+
%a{cfunc: "konpeito_clay_tui_key_f10"}
|
|
304
|
+
def self.key_f10: () -> Integer
|
|
305
|
+
|
|
306
|
+
%a{cfunc: "konpeito_clay_tui_key_f11"}
|
|
307
|
+
def self.key_f11: () -> Integer
|
|
308
|
+
|
|
309
|
+
%a{cfunc: "konpeito_clay_tui_key_f12"}
|
|
310
|
+
def self.key_f12: () -> Integer
|
|
311
|
+
|
|
312
|
+
# ── Modifier Keys ──
|
|
313
|
+
|
|
314
|
+
%a{cfunc: "konpeito_clay_tui_event_mod"}
|
|
315
|
+
def self.event_mod: () -> Integer
|
|
316
|
+
|
|
317
|
+
%a{cfunc: "konpeito_clay_tui_mod_alt"}
|
|
318
|
+
def self.mod_alt: () -> Integer
|
|
319
|
+
|
|
320
|
+
%a{cfunc: "konpeito_clay_tui_mod_ctrl"}
|
|
321
|
+
def self.mod_ctrl: () -> Integer
|
|
322
|
+
|
|
323
|
+
%a{cfunc: "konpeito_clay_tui_mod_shift"}
|
|
324
|
+
def self.mod_shift: () -> Integer
|
|
325
|
+
|
|
326
|
+
# ── Text Buffer System ──
|
|
327
|
+
|
|
328
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_clear"}
|
|
329
|
+
def self.textbuf_clear: (Integer id) -> void
|
|
330
|
+
|
|
331
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_copy"}
|
|
332
|
+
def self.textbuf_copy: (Integer dst, Integer src) -> void
|
|
333
|
+
|
|
334
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_putchar"}
|
|
335
|
+
def self.textbuf_putchar: (Integer id, Integer ch) -> void
|
|
336
|
+
|
|
337
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_backspace"}
|
|
338
|
+
def self.textbuf_backspace: (Integer id) -> void
|
|
339
|
+
|
|
340
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_delete"}
|
|
341
|
+
def self.textbuf_delete: (Integer id) -> void
|
|
342
|
+
|
|
343
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_cursor_left"}
|
|
344
|
+
def self.textbuf_cursor_left: (Integer id) -> void
|
|
345
|
+
|
|
346
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_cursor_right"}
|
|
347
|
+
def self.textbuf_cursor_right: (Integer id) -> void
|
|
348
|
+
|
|
349
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_cursor_home"}
|
|
350
|
+
def self.textbuf_cursor_home: (Integer id) -> void
|
|
351
|
+
|
|
352
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_cursor_end"}
|
|
353
|
+
def self.textbuf_cursor_end: (Integer id) -> void
|
|
354
|
+
|
|
355
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_len"}
|
|
356
|
+
def self.textbuf_len: (Integer id) -> Integer
|
|
357
|
+
|
|
358
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_cursor"}
|
|
359
|
+
def self.textbuf_cursor: (Integer id) -> Integer
|
|
360
|
+
|
|
361
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_render"}
|
|
362
|
+
def self.textbuf_render: (Integer id, Integer r, Integer g, Integer b) -> void
|
|
363
|
+
|
|
364
|
+
%a{cfunc: "konpeito_clay_tui_textbuf_render_range"}
|
|
365
|
+
def self.textbuf_render_range: (Integer id, Integer start, Integer end_pos, Integer r, Integer g, Integer b) -> void
|
|
366
|
+
|
|
367
|
+
%a{cfunc: "konpeito_clay_tui_text_char"}
|
|
368
|
+
def self.text_char: (Integer ch, Integer r, Integer g, Integer b) -> void
|
|
258
369
|
end
|