hokusai-zero 0.1.1
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 +7 -0
- data/Dockerfile +26 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +91 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/ast/genheader +3 -0
- data/ast/include/hashmap.c +1151 -0
- data/ast/include/hashmap.c.license +20 -0
- data/ast/include/hashmap.h +54 -0
- data/ast/src/core/ast.c +448 -0
- data/ast/src/core/ast.h +259 -0
- data/ast/src/core/common.h +24 -0
- data/ast/src/core/component.c +85 -0
- data/ast/src/core/component.h +35 -0
- data/ast/src/core/hml.c +665 -0
- data/ast/src/core/hml.h +11 -0
- data/ast/src/core/input.c +458 -0
- data/ast/src/core/input.h +118 -0
- data/ast/src/core/style.c +101 -0
- data/ast/src/core/style.h +41 -0
- data/ast/src/core/text.c +784 -0
- data/ast/src/core/text.h +93 -0
- data/ast/src/core/util.c +140 -0
- data/ast/src/core/util.h +48 -0
- data/ast/src/hokusai.c +6 -0
- data/ast/src/hokusai.h +6 -0
- data/ast/test/fixtures/test.ui +13 -0
- data/ast/test/greatest.h +1266 -0
- data/ast/test/hokusai.c +14 -0
- data/ast/test/parser.c +234 -0
- data/ast/test/text.c +116 -0
- data/ext/extconf.rb +27 -0
- data/grammar/Cargo.lock +80 -0
- data/grammar/Cargo.toml +26 -0
- data/grammar/binding.gyp +20 -0
- data/grammar/bindings/node/binding.cc +28 -0
- data/grammar/bindings/node/index.js +19 -0
- data/grammar/bindings/rust/build.rs +40 -0
- data/grammar/bindings/rust/lib.rs +52 -0
- data/grammar/corpus/1_document.txt +131 -0
- data/grammar/corpus/2_selectors.txt +58 -0
- data/grammar/corpus/3_spaces.txt +69 -0
- data/grammar/corpus/4_errors.txt +10 -0
- data/grammar/corpus/5_macros.txt +175 -0
- data/grammar/corpus/6_styles.txt +81 -0
- data/grammar/grammar.js +275 -0
- data/grammar/package-lock.json +34 -0
- data/grammar/package.json +33 -0
- data/grammar/src/grammar.json +1269 -0
- data/grammar/src/node-types.json +474 -0
- data/grammar/src/parser.c +5772 -0
- data/grammar/src/scanner.c +258 -0
- data/grammar/src/tree_sitter/parser.h +230 -0
- data/grammar/src/tree_sitter/scanner.h +12 -0
- data/grammar/test.nml +10 -0
- data/hokusai.gemspec +19 -0
- data/ui/examples/assets/DigitalDisplay.ttf +0 -0
- data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
- data/ui/examples/assets/addy.png +0 -0
- data/ui/examples/assets/baby_sean.png +0 -0
- data/ui/examples/assets/football-troll.png +0 -0
- data/ui/examples/assets/gear.png +0 -0
- data/ui/examples/assets/icecold.ttf +0 -0
- data/ui/examples/assets/science-troll.png +0 -0
- data/ui/examples/buddy.rb +31 -0
- data/ui/examples/clock.rb +58 -0
- data/ui/examples/counter.rb +123 -0
- data/ui/examples/dynamic.rb +147 -0
- data/ui/examples/foobar.rb +236 -0
- data/ui/examples/stock.rb +115 -0
- data/ui/examples/stock_decider/option.rb +74 -0
- data/ui/examples/tic_tac_toe.rb +246 -0
- data/ui/lib/lib_hokusai.rb +425 -0
- data/ui/spec/hokusai/ast_spec.rb +88 -0
- data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
- data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
- data/ui/spec/hokusai/block_spec.rb +126 -0
- data/ui/spec/hokusai/directives_spec.rb +327 -0
- data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
- data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
- data/ui/spec/hokusai/publisher_spec.rb +38 -0
- data/ui/spec/hokusai/slots_spec.rb +150 -0
- data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
- data/ui/spec/hokusai_spec.rb +0 -0
- data/ui/spec/spec_helper.rb +30 -0
- data/ui/src/hokusai/ast.rb +446 -0
- data/ui/src/hokusai/automation/client.rb +167 -0
- data/ui/src/hokusai/automation/constants.rb +98 -0
- data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
- data/ui/src/hokusai/automation/driver.rb +54 -0
- data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
- data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
- data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
- data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
- data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
- data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
- data/ui/src/hokusai/automation/selector.rb +39 -0
- data/ui/src/hokusai/automation/server.rb +114 -0
- data/ui/src/hokusai/automation.rb +3 -0
- data/ui/src/hokusai/backends/raylib/config.rb +47 -0
- data/ui/src/hokusai/backends/raylib/font.rb +113 -0
- data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
- data/ui/src/hokusai/backends/raylib.rb +449 -0
- data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
- data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
- data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
- data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
- data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
- data/ui/src/hokusai/backends/sdl2.rb +529 -0
- data/ui/src/hokusai/block.rb +237 -0
- data/ui/src/hokusai/blocks/button.rb +100 -0
- data/ui/src/hokusai/blocks/checkbox.rb +51 -0
- data/ui/src/hokusai/blocks/circle.rb +28 -0
- data/ui/src/hokusai/blocks/clipped.rb +23 -0
- data/ui/src/hokusai/blocks/cursor.rb +49 -0
- data/ui/src/hokusai/blocks/dynamic.rb +37 -0
- data/ui/src/hokusai/blocks/empty.rb +10 -0
- data/ui/src/hokusai/blocks/hblock.rb +35 -0
- data/ui/src/hokusai/blocks/image.rb +18 -0
- data/ui/src/hokusai/blocks/input.rb +200 -0
- data/ui/src/hokusai/blocks/label.rb +39 -0
- data/ui/src/hokusai/blocks/panel.rb +126 -0
- data/ui/src/hokusai/blocks/rect.rb +24 -0
- data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
- data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
- data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
- data/ui/src/hokusai/blocks/selectable.rb +77 -0
- data/ui/src/hokusai/blocks/svg.rb +20 -0
- data/ui/src/hokusai/blocks/text.rb +214 -0
- data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
- data/ui/src/hokusai/blocks/toggle.rb +55 -0
- data/ui/src/hokusai/blocks/vblock.rb +35 -0
- data/ui/src/hokusai/commands/base.rb +22 -0
- data/ui/src/hokusai/commands/circle.rb +47 -0
- data/ui/src/hokusai/commands/image.rb +45 -0
- data/ui/src/hokusai/commands/rect.rb +158 -0
- data/ui/src/hokusai/commands/scissor.rb +22 -0
- data/ui/src/hokusai/commands/text.rb +92 -0
- data/ui/src/hokusai/commands.rb +87 -0
- data/ui/src/hokusai/diff.rb +124 -0
- data/ui/src/hokusai/error.rb +3 -0
- data/ui/src/hokusai/event.rb +54 -0
- data/ui/src/hokusai/events/keyboard.rb +84 -0
- data/ui/src/hokusai/events/mouse.rb +172 -0
- data/ui/src/hokusai/font.rb +280 -0
- data/ui/src/hokusai/meta.rb +152 -0
- data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
- data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
- data/ui/src/hokusai/mounting/update_entry.rb +101 -0
- data/ui/src/hokusai/node.rb +98 -0
- data/ui/src/hokusai/node_mounter.rb +102 -0
- data/ui/src/hokusai/painter.rb +214 -0
- data/ui/src/hokusai/publisher.rb +32 -0
- data/ui/src/hokusai/style.rb +72 -0
- data/ui/src/hokusai/types.rb +266 -0
- data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
- data/ui/src/hokusai/util/piece_table.rb +111 -0
- data/ui/src/hokusai/util/selection.rb +145 -0
- data/ui/src/hokusai.rb +120 -0
- data/ui/vendor/.gitkeep +0 -0
- data/vendor/.gitkeep +0 -0
- data/xmake.lua +192 -0
- metadata +222 -0
@@ -0,0 +1,449 @@
|
|
1
|
+
require "raylib"
|
2
|
+
require 'memory_profiler'
|
3
|
+
|
4
|
+
# frozen_string_literal: true
|
5
|
+
|
6
|
+
module Hokusai::Backends
|
7
|
+
class RaylibBackend
|
8
|
+
RAYLIB_PATH = ENV["RAYLIB_PATH"] || "#{__dir__}/../../../../vendor/lib"
|
9
|
+
|
10
|
+
attr_reader :config
|
11
|
+
|
12
|
+
def self.icons
|
13
|
+
@icons ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.images
|
17
|
+
@images ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.stopped?
|
21
|
+
@stop = false if @stop.nil?
|
22
|
+
|
23
|
+
@stop
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.stop!
|
27
|
+
@stop = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.reset
|
31
|
+
@stop = false
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@config = Config.new
|
36
|
+
|
37
|
+
yield(config)
|
38
|
+
end
|
39
|
+
|
40
|
+
def vec2(x, y)
|
41
|
+
if @raylib_vec2.nil?
|
42
|
+
@raylib_vec2 = Raylib::Vector2.new
|
43
|
+
end
|
44
|
+
@raylib_vec2[:x] = x
|
45
|
+
@raylib_vec2[:y] = y
|
46
|
+
|
47
|
+
@raylib_vec2
|
48
|
+
end
|
49
|
+
|
50
|
+
def hml_vec2(x, y)
|
51
|
+
if @hml_vec2.nil?
|
52
|
+
@hml_vec2 = LibHokusai::HmlVec2.create(x, y)
|
53
|
+
else
|
54
|
+
@hml_vec2[:x] = x
|
55
|
+
@hml_vec2[:y] = y
|
56
|
+
end
|
57
|
+
|
58
|
+
@hml_vec2
|
59
|
+
end
|
60
|
+
|
61
|
+
def color(hc)
|
62
|
+
if @raylib_color.nil?
|
63
|
+
@raylib_color = Raylib::Color.from_u8(hc.red, hc.green, hc.blue, hc.alpha)
|
64
|
+
else
|
65
|
+
@raylib_color[:r] = hc.red
|
66
|
+
@raylib_color[:g] = hc.green
|
67
|
+
@raylib_color[:b] = hc.blue
|
68
|
+
@raylib_color[:a] = hc.alpha
|
69
|
+
end
|
70
|
+
|
71
|
+
@raylib_color
|
72
|
+
end
|
73
|
+
|
74
|
+
def rect(arr)
|
75
|
+
if @raylib_rect.nil?
|
76
|
+
@raylib_rect = Raylib::Rectangle.create(arr[0], arr[1], arr[2], arr[3])
|
77
|
+
else
|
78
|
+
@raylib_rect[:x] = arr[0]
|
79
|
+
@raylib_rect[:y] = arr[1]
|
80
|
+
@raylib_rect[:width] = arr[2]
|
81
|
+
@raylib_rect[:height] = arr[3]
|
82
|
+
end
|
83
|
+
|
84
|
+
@raylib_rect
|
85
|
+
end
|
86
|
+
|
87
|
+
def mouse_button(clicked: false, down: false, released: false, up: false)
|
88
|
+
if @mouse_button.nil?
|
89
|
+
@mouse_button = LibHokusai::HmlInputMouseButton.create(clicked: clicked, down: down, released: released, up: up)
|
90
|
+
else
|
91
|
+
@mouse_button[:clicked] = clicked
|
92
|
+
@mouse_button[:down] = down
|
93
|
+
@mouse_button[:released] = released
|
94
|
+
@mouse_button[:up] = up
|
95
|
+
end
|
96
|
+
|
97
|
+
@mouse_button
|
98
|
+
end
|
99
|
+
|
100
|
+
def process_input(input)
|
101
|
+
raylib_mouse_pos = Raylib.GetMousePosition
|
102
|
+
raylib_mouse_delta = Raylib.GetMouseDelta
|
103
|
+
LibHokusai.hoku_input_mouse_set_scroll(input.raw, Raylib.GetMouseWheelMove)
|
104
|
+
LibHokusai.hoku_input_set_mouse_position(input.raw, hml_vec2(raylib_mouse_pos.x, raylib_mouse_pos.y))
|
105
|
+
|
106
|
+
input.raw[:mouse][:delta][:x] = raylib_mouse_delta.x
|
107
|
+
input.raw[:mouse][:delta][:y] = raylib_mouse_delta.y
|
108
|
+
|
109
|
+
[0,1,2].each do |button_id|
|
110
|
+
clicked = Raylib.IsMouseButtonPressed(button_id)
|
111
|
+
down = Raylib.IsMouseButtonDown(button_id)
|
112
|
+
released = Raylib.IsMouseButtonReleased(button_id)
|
113
|
+
up = Raylib.IsMouseButtonUp(button_id)
|
114
|
+
|
115
|
+
button = mouse_button(clicked: clicked, down: down, released: released, up: up)
|
116
|
+
LibHokusai.hoku_input_mouse_set_button(input.raw, button, button_id)
|
117
|
+
end
|
118
|
+
|
119
|
+
LibHokusai.hoku_input_keyboard_start(input.raw)
|
120
|
+
|
121
|
+
Keys.each do |(hoku_key, raylib_key)|
|
122
|
+
LibHokusai.hoku_input_keyboard_set_key(input.raw, hoku_key, Raylib.IsKeyDown(raylib_key))
|
123
|
+
end
|
124
|
+
|
125
|
+
LibHokusai.hoku_input_keyboard_stop(input.raw)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.run(app)
|
129
|
+
backend = new do |config|
|
130
|
+
yield config
|
131
|
+
end
|
132
|
+
|
133
|
+
block = app.mount
|
134
|
+
backend.run(block)
|
135
|
+
end
|
136
|
+
|
137
|
+
def run(block)
|
138
|
+
self.class.reset
|
139
|
+
|
140
|
+
case RbConfig::CONFIG['host_os']
|
141
|
+
when /darwin/
|
142
|
+
Raylib.load_lib("#{RAYLIB_PATH}/libraylib.dylib")
|
143
|
+
when /mswin|msys|mingw/
|
144
|
+
Raylib.load_lib("#{RAYLIB_PATH}/libraylib.dll")
|
145
|
+
when /linux/
|
146
|
+
Raylib.load_lib("#{RAYLIB_PATH}/libraylib.so")
|
147
|
+
end
|
148
|
+
|
149
|
+
resize = false
|
150
|
+
initial = true
|
151
|
+
width = config.width
|
152
|
+
height = config.height
|
153
|
+
|
154
|
+
register_command_handlers
|
155
|
+
|
156
|
+
ptr = FFI::MemoryPointer.new :pointer
|
157
|
+
LibHokusai.hoku_input_init(ptr)
|
158
|
+
raw = LibHokusai::HmlInput.new(ptr.get_pointer(0))
|
159
|
+
input = Hokusai::Input.new(raw)
|
160
|
+
ptr.free
|
161
|
+
|
162
|
+
Raylib.SetConfigFlags(config.config_flags)
|
163
|
+
Raylib.InitWindow(config.width, config.height, config.title)
|
164
|
+
Raylib.SetTargetFPS(config.fps)
|
165
|
+
Raylib.SetWindowState(config.window_state_flags)
|
166
|
+
|
167
|
+
unless Hokusai.fonts.get("default")
|
168
|
+
Hokusai.fonts.register "default", Hokusai::Backends::RaylibBackend::Font.default
|
169
|
+
Hokusai.fonts.activate "default"
|
170
|
+
end
|
171
|
+
|
172
|
+
config.after_load_cb&.call
|
173
|
+
|
174
|
+
if config.automated
|
175
|
+
config.start_automation_driver
|
176
|
+
end
|
177
|
+
|
178
|
+
canvas = Hokusai::Canvas.new(width.to_f, height.to_f, 0.0, 0.0)
|
179
|
+
@texture = Raylib.LoadRenderTexture(width, height)
|
180
|
+
Raylib.SetTextureFilter(@texture.texture, Raylib::TEXTURE_FILTER_POINT)
|
181
|
+
|
182
|
+
width = nil
|
183
|
+
height = nil
|
184
|
+
|
185
|
+
MemoryProfiler.start if ENV["PROFILE"]
|
186
|
+
|
187
|
+
Raylib.EnableEventWaiting if config.event_waiting
|
188
|
+
last_input_hash = nil
|
189
|
+
|
190
|
+
until Raylib.WindowShouldClose
|
191
|
+
last_width = Raylib.GetScreenWidth
|
192
|
+
last_height = Raylib.GetScreenHeight
|
193
|
+
|
194
|
+
if last_width != width || last_height != height
|
195
|
+
resize = true
|
196
|
+
else
|
197
|
+
resize = false
|
198
|
+
end
|
199
|
+
|
200
|
+
width = last_width
|
201
|
+
height = last_height
|
202
|
+
|
203
|
+
process_input(input)
|
204
|
+
block.update
|
205
|
+
|
206
|
+
canvas.reset(nil, nil, width.to_f, height.to_f)
|
207
|
+
|
208
|
+
Raylib.BeginDrawing
|
209
|
+
Raylib.ClearBackground(config.background)
|
210
|
+
|
211
|
+
painter = Hokusai::Painter.new(block, input)
|
212
|
+
|
213
|
+
if config.automation_driver
|
214
|
+
painter.on_before_render do |blocks, canvas, input|
|
215
|
+
config.automation_driver.process(blocks, canvas, input)
|
216
|
+
end
|
217
|
+
|
218
|
+
painter.on_after_render do
|
219
|
+
config.automation_driver.complete
|
220
|
+
end
|
221
|
+
|
222
|
+
sleep 0.004
|
223
|
+
end
|
224
|
+
|
225
|
+
painter.render(canvas, resize)
|
226
|
+
|
227
|
+
Raylib.DrawFPS(10, 10) if ENV["PROFILE"] || ENV["FPS"]
|
228
|
+
Raylib.EndDrawing
|
229
|
+
|
230
|
+
|
231
|
+
break if self.class.stopped?
|
232
|
+
end
|
233
|
+
|
234
|
+
Hokusai.fonts.fonts.each do |key, font|
|
235
|
+
Raylib.UnloadFont(font.raw)
|
236
|
+
end
|
237
|
+
|
238
|
+
LibHokusai.hoku_input_free(input.raw)
|
239
|
+
|
240
|
+
if ENV["PROFILE"]
|
241
|
+
report = MemoryProfiler.stop
|
242
|
+
report.pretty_print(scale_bytes: true)
|
243
|
+
end
|
244
|
+
|
245
|
+
Raylib.CloseWindow
|
246
|
+
|
247
|
+
config.automation_driver&.stop
|
248
|
+
end
|
249
|
+
|
250
|
+
def inside_scissor(x, y, h = 0)
|
251
|
+
return true if @scissor.nil?
|
252
|
+
|
253
|
+
val = y + h >= @scissor[1] && y <= @scissor[1] + @scissor[3]
|
254
|
+
val
|
255
|
+
end
|
256
|
+
|
257
|
+
def register_command_handlers
|
258
|
+
Hokusai.on_close_window do
|
259
|
+
self.class.stop!
|
260
|
+
end
|
261
|
+
|
262
|
+
Hokusai.on_set_mouse_cursor do |type|
|
263
|
+
raylib_type = {
|
264
|
+
default: Raylib::MOUSE_CURSOR_DEFAULT,
|
265
|
+
arrow: Raylib::MOUSE_CURSOR_ARROW,
|
266
|
+
ibeam: Raylib::MOUSE_CURSOR_IBEAM,
|
267
|
+
crosshair: Raylib::MOUSE_CURSOR_CROSSHAIR,
|
268
|
+
pointer: Raylib::MOUSE_CURSOR_POINTING_HAND,
|
269
|
+
}[type]
|
270
|
+
|
271
|
+
raise Hokusai::Error.new("Cursor #{type} not recognized") if raylib_type.nil?
|
272
|
+
|
273
|
+
Raylib::SetMouseCursor(raylib_type)
|
274
|
+
end
|
275
|
+
|
276
|
+
Hokusai.on_set_mouse_position do |mouse|
|
277
|
+
Raylib.SetMousePosition(mouse.pos.x, mouse.pos.y)
|
278
|
+
end
|
279
|
+
|
280
|
+
Hokusai.on_set_window_position do |(dx, dy)|
|
281
|
+
wpos = Raylib.GetWindowPosition
|
282
|
+
|
283
|
+
Raylib.SetWindowPosition(wpos.x + dx, wpos.y + dy)
|
284
|
+
end
|
285
|
+
|
286
|
+
Hokusai.on_restore_window do
|
287
|
+
Raylib.RestoreWindow
|
288
|
+
end
|
289
|
+
|
290
|
+
Hokusai.on_maximize_window do
|
291
|
+
if Raylib.IsWindowMaximized
|
292
|
+
Raylib.RestoreWindow
|
293
|
+
else
|
294
|
+
Raylib.MaximizeWindow
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
Hokusai.on_minimize_window do
|
299
|
+
Raylib.MinimizeWindow
|
300
|
+
end
|
301
|
+
|
302
|
+
Hokusai.on_renderable do |canvas|
|
303
|
+
inside_scissor(canvas.x, canvas.y, canvas.height)
|
304
|
+
end
|
305
|
+
|
306
|
+
Hokusai::Commands::ScissorBegin.on_draw do |command|
|
307
|
+
Raylib.BeginScissorMode(command.x, command.y, command.width, command.height)
|
308
|
+
@scissor = [command.x, command.y, command.width, command.height]
|
309
|
+
end
|
310
|
+
|
311
|
+
Hokusai::Commands::ScissorEnd.on_draw do |_|
|
312
|
+
Raylib.EndScissorMode
|
313
|
+
@scissor = nil
|
314
|
+
end
|
315
|
+
|
316
|
+
Hokusai::Commands::Circle.on_draw do |command|
|
317
|
+
next unless inside_scissor(command.x, command.y)
|
318
|
+
|
319
|
+
radius = command.radius - command.outline
|
320
|
+
rc = command.color
|
321
|
+
color = Raylib::Color.from_u8(rc.r, rc.g, rc.b, rc.a)
|
322
|
+
Raylib.DrawCircleV(vec2(command.x, command.y), radius, color)
|
323
|
+
|
324
|
+
if command.outline > 0
|
325
|
+
Raylib.DrawCircleLines(command.x.ceil.to_i, command.y.ceil.to_i, command.radius, color(command.color))
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
Hokusai::Commands::SVG.on_draw do |command|
|
330
|
+
command = command.as(Hokusai::Commands::SVG)
|
331
|
+
texture = begin
|
332
|
+
if self.class.images[command.source]
|
333
|
+
self.class.images[command.source]
|
334
|
+
else
|
335
|
+
img = Raylib.LoadImageSVG(command.source, command.width, command.height)
|
336
|
+
texture = Raylib.load_texture_from_image(img)
|
337
|
+
Raylib.UnloadImage(img)
|
338
|
+
self.class.images[command.source] = texture
|
339
|
+
Raylib.GenTextureMipmaps(texture)
|
340
|
+
texture
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
Raylib.DrawTexture(texture, command.x, command.y, color(command.color))
|
345
|
+
end
|
346
|
+
|
347
|
+
Hokusai::Commands::Image.on_draw do |command|
|
348
|
+
next unless inside_scissor(command.x, command.y, command.height)
|
349
|
+
|
350
|
+
texture = begin
|
351
|
+
if self.class.images[command.cache]
|
352
|
+
self.class.images[command.cache]
|
353
|
+
else
|
354
|
+
img = Raylib.LoadImage(command.source)
|
355
|
+
Raylib.ImageResize(img.to_ptr, command.width, command.height)
|
356
|
+
|
357
|
+
texture = Raylib.LoadTextureFromImage(img)
|
358
|
+
Raylib.UnloadImage(img)
|
359
|
+
self.class.images[command.cache] = texture
|
360
|
+
Raylib.GenTextureMipmaps(texture)
|
361
|
+
texture
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
Raylib.DrawTexture(texture, command.x, command.y, Raylib::WHITE)
|
366
|
+
end
|
367
|
+
|
368
|
+
Hokusai::Commands::Text.on_draw do |command|
|
369
|
+
next unless inside_scissor(command.x, command.y, command.size)
|
370
|
+
|
371
|
+
font = Hokusai.fonts.active
|
372
|
+
c = color(command.color)
|
373
|
+
x = command.x + command.padding.l
|
374
|
+
y = command.y + command.padding.t
|
375
|
+
|
376
|
+
if fnt = font
|
377
|
+
# content = FFI::MemoryPointer.from_string(command.content)
|
378
|
+
Raylib.DrawTextEx(fnt.raw, command.content.to_s, vec2(x, y), command.size, fnt.spacing, c)
|
379
|
+
# content.free
|
380
|
+
else
|
381
|
+
Raylib.DrawText(command.content, x, y, command.size, c)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
Hokusai::Commands::Rect.on_draw do |command|
|
386
|
+
next unless inside_scissor(command.x, command.y, command.height)
|
387
|
+
|
388
|
+
background_color = color(command.color)
|
389
|
+
# the rect has an outline
|
390
|
+
# so we want to render twice
|
391
|
+
if command.outline?
|
392
|
+
# for Raylib, the boundaries for background and outline are the same
|
393
|
+
# rectangles render in from the boundary
|
394
|
+
# and outlines render out from the boundary
|
395
|
+
# background_rect = outline_rect
|
396
|
+
outline_rect = rect(command.background_boundary)
|
397
|
+
background_rect = outline_rect
|
398
|
+
# rect is rounded so we need a rounded outline and
|
399
|
+
# a regular one
|
400
|
+
if command.rounding > 0
|
401
|
+
Raylib.DrawRectangleRounded(background_rect, command.rounding, 50, background_color)
|
402
|
+
else
|
403
|
+
Raylib.DrawRectangleRec(background_rect, background_color)
|
404
|
+
end
|
405
|
+
|
406
|
+
outline_color = color(command.outline_color)
|
407
|
+
|
408
|
+
# now draw the outlines
|
409
|
+
if command.outline_uniform? && command.rounding > 0.0 && outline_color.a > 0
|
410
|
+
Raylib.DrawRectangleRoundedLines(outline_rect, command.rounding, 50, command.outline.top, outline_color)
|
411
|
+
elsif command.outline_uniform? && !(command.rounding <= 0.0) && outline_color.a > 0
|
412
|
+
Raylib.DrawRectangleLinesEx(outline_rect, command.outline.top, outline_color)
|
413
|
+
elsif !command.outline_uniform?
|
414
|
+
ox, oy, ow, oh = command.background_boundary
|
415
|
+
|
416
|
+
if command.outline.top > 0.0
|
417
|
+
Raylib.DrawLineEx(vec2(ox, oy), vec2(ox + ow, oy), command.outline.top, outline_color)
|
418
|
+
end
|
419
|
+
|
420
|
+
if command.outline.left > 0.0
|
421
|
+
Raylib.DrawLineEx(vec2(ox, oy), vec2(ox, oy + oh), command.outline.left, outline_color)
|
422
|
+
end
|
423
|
+
|
424
|
+
if command.outline.right > 0.0
|
425
|
+
Raylib.DrawLineEx(vec2(ox + ow, oy), vec2(ox + ow, oy + oh), command.outline.right, outline_color)
|
426
|
+
end
|
427
|
+
|
428
|
+
if command.outline.bottom > 0.0
|
429
|
+
Raylib.DrawLineEx(vec2(ox, oy + oh), vec2(ox + ow, oy + oh), command.outline.bottom, outline_color)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
else
|
433
|
+
rect = rect(command.background_boundary)
|
434
|
+
|
435
|
+
if command.rounding > 0
|
436
|
+
Raylib.DrawRectangleRounded(rect, command.rounding, 50, background_color)
|
437
|
+
else
|
438
|
+
Raylib.DrawRectangleRec(rect, background_color)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
require_relative "../../hokusai/font"
|
447
|
+
require_relative "./raylib/config"
|
448
|
+
require_relative "./raylib/font"
|
449
|
+
require_relative "./raylib/keys"
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hokusai::Backends
|
2
|
+
class SDLBackend
|
3
|
+
class Config
|
4
|
+
attr_accessor :width, :height, :fps, :init_flags,
|
5
|
+
:title, :background, :automation_driver,
|
6
|
+
:after_load_cb, :host, :port, :automated,
|
7
|
+
:window_config_flags
|
8
|
+
|
9
|
+
def after_load(&block)
|
10
|
+
self.after_load_cb = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@width = 500
|
15
|
+
@height = 500
|
16
|
+
@fps = 60
|
17
|
+
|
18
|
+
@init_flags = ::SDL::INIT_VIDEO | ::SDL::INIT_EVENTS
|
19
|
+
@window_config_flags = SDL::WINDOW_RESIZABLE
|
20
|
+
@title = "(Unknown Title)"
|
21
|
+
@background = SDLBackend.color(255,255,255, 0)
|
22
|
+
|
23
|
+
after_load do
|
24
|
+
font = Hokusai::Backends::SDLBackend::Font.from "#{__dir__}/Monaco.ttf"
|
25
|
+
Hokusai.fonts.register "default", font
|
26
|
+
Hokusai.fonts.activate "default"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Hokusai::Backends
|
2
|
+
class SDLBackend
|
3
|
+
OnWidthCb = Proc.new do |char, raw|
|
4
|
+
minxp = FFI::MemoryPointer.new :int
|
5
|
+
maxxp = FFI::MemoryPointer.new :int
|
6
|
+
minyp = FFI::MemoryPointer.new :int
|
7
|
+
maxyp = FFI::MemoryPointer.new :int
|
8
|
+
advancep = FFI::MemoryPointer.new :int
|
9
|
+
|
10
|
+
SDL.TTF_GlyphMetrics32(raw, char.ord, minxp, maxxp, minyp, maxyp, advancep)
|
11
|
+
|
12
|
+
metrics = [minxp.read_int, maxxp.read_int, minyp.read_int, maxyp.read_int, advancep.read_int]
|
13
|
+
|
14
|
+
minxp.free
|
15
|
+
maxxp.free
|
16
|
+
minyp.free
|
17
|
+
maxyp.free
|
18
|
+
advancep.free
|
19
|
+
|
20
|
+
metrics[4].to_f
|
21
|
+
end
|
22
|
+
|
23
|
+
class Font < Hokusai::Font
|
24
|
+
def self.from(file = "#{__dir__}/Monaco.ttf", size=50)
|
25
|
+
raw = SDL.TTF_OpenFont(file, size)
|
26
|
+
|
27
|
+
raise Hokusai::Error.new("Cannot open font from #{file}: #{ttf_error}") if raw.null?
|
28
|
+
|
29
|
+
new(raw)
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :raw
|
33
|
+
|
34
|
+
def initialize(raw)
|
35
|
+
@raw = raw
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ttf_error
|
39
|
+
SDL.GetError.read_string
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_style(bold: false, italic: false)
|
43
|
+
style = SDL::TTF_STYLE_NORMAL
|
44
|
+
style = style | SDL::TTF_STYLE_BOLD if bold
|
45
|
+
style = style | SDL::TTF_STYLE_ITALIC if italic
|
46
|
+
|
47
|
+
SDL.TTF_SetFontStyle(raw, style)
|
48
|
+
end
|
49
|
+
|
50
|
+
def render(text, color = Color.new(0, 0, 0, 255), **args)
|
51
|
+
set_style(**args)
|
52
|
+
|
53
|
+
render_color = SDLBackend.color(color.r, color.g, color.b, color.a)
|
54
|
+
SDL.TTF_RenderUTF8_Blended(raw, text, render_color)
|
55
|
+
end
|
56
|
+
|
57
|
+
def ascent
|
58
|
+
SDL.TTF_FontAscent(raw)
|
59
|
+
end
|
60
|
+
|
61
|
+
def descent
|
62
|
+
SDL.TTF_FontDescent(raw)
|
63
|
+
end
|
64
|
+
|
65
|
+
def height
|
66
|
+
SDL.TTF_FontHeight(raw)
|
67
|
+
end
|
68
|
+
|
69
|
+
def clamp(text, size, width, initial_offset = 0.0)
|
70
|
+
self.size = size
|
71
|
+
|
72
|
+
clamping_pointer = FFI::MemoryPointer.new :pointer
|
73
|
+
|
74
|
+
ret = LibHokusai.hoku_text_clamp(clamping_pointer, text, width, initial_offset, raw, SDLBackend::OnWidthCb)
|
75
|
+
raise Hokusai::Error.new("Clamping failed") unless ret.zero?
|
76
|
+
|
77
|
+
clamping = LibHokusai::HokuClamping.new(clamping_pointer.get_pointer(0))
|
78
|
+
Hokusai::Clamping.new(clamping)
|
79
|
+
end
|
80
|
+
|
81
|
+
def clamp_markdown(text, size, width, initial_offset = 0.0)
|
82
|
+
self.size = size
|
83
|
+
|
84
|
+
clamping_pointer = FFI::MemoryPointer.new :pointer
|
85
|
+
|
86
|
+
ret = LibHokusai.hoku_text_md_clamp(clamping_pointer, text, width, initial_offset, raw, SDLBackend::OnWidthCb)
|
87
|
+
raise Hokusai::Error.new("Clamping failed") unless ret.zero?
|
88
|
+
|
89
|
+
clamping = LibHokusai::HokuClamping.new(clamping_pointer.get_pointer(0))
|
90
|
+
Hokusai::Clamping.new(clamping, markdown: true)
|
91
|
+
end
|
92
|
+
|
93
|
+
def fits(text, width)
|
94
|
+
extent = FFI::MemoryPointer.new :int
|
95
|
+
count = FFI::MemoryPointer.new :int
|
96
|
+
SDL.TTF_Measure(raw, text, width.to_i, extent, count)
|
97
|
+
|
98
|
+
e = extent.read_int
|
99
|
+
c = count.read_int
|
100
|
+
|
101
|
+
extent.free
|
102
|
+
count.free
|
103
|
+
|
104
|
+
[e, c]
|
105
|
+
end
|
106
|
+
|
107
|
+
def measure(text, size = 15)
|
108
|
+
w = FFI::MemoryPointer.new :int
|
109
|
+
h = FFI::MemoryPointer.new :int
|
110
|
+
|
111
|
+
SDL.TTF_SizeText(raw, text, w, h)
|
112
|
+
|
113
|
+
width = w.read_int
|
114
|
+
height = h.read_int
|
115
|
+
|
116
|
+
w.free
|
117
|
+
h.free
|
118
|
+
|
119
|
+
[width, height]
|
120
|
+
end
|
121
|
+
|
122
|
+
def size=(val)
|
123
|
+
SDL.TTF_SetFontSize(raw, val.to_i)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|