yoga_layout 0.0.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/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/README.md +35 -0
- data/Rakefile +176 -0
- data/bin/console +10 -0
- data/bin/demo +89 -0
- data/bin/setup +8 -0
- data/ext/yoga_layout/extconf.rb +3 -0
- data/lib/yoga_layout.rb +33 -0
- data/lib/yoga_layout/bindings.rb +39 -0
- data/lib/yoga_layout/bindings/enums.rb +131 -0
- data/lib/yoga_layout/bindings/misc.rb +23 -0
- data/lib/yoga_layout/bindings/typedefs.rb +51 -0
- data/lib/yoga_layout/bindings/ygconfig.rb +37 -0
- data/lib/yoga_layout/bindings/ygnode.rb +59 -0
- data/lib/yoga_layout/bindings/ygnode_properties.rb +93 -0
- data/lib/yoga_layout/config.rb +56 -0
- data/lib/yoga_layout/node.rb +333 -0
- data/lib/yoga_layout/version.rb +3 -0
- data/lib/yoga_layout/wrapper.rb +103 -0
- data/yoga_layout.gemspec +45 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 26a638d33929146562ee6e51da0f722c69e6396b
|
4
|
+
data.tar.gz: 599d1368d1dbc2a113ecbd12492fdcb0bac63c10
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce2a19b0ec43b55d2c541352c6e61f1f1db096b60b106f35f477e08697d1f1d4c294f2a9b2ff49ce86bd067d870e4ffe240885afde25a48fa60a909cb9531e82
|
7
|
+
data.tar.gz: 25f13f5bcf4d48277ecfe898795a54f208ef5ef8a6708c2b018b1f484a11c3e243ef41f084334cde58503b55763871c98463cb4e3bde250ef71c724dd3c3818a
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
*.bundle
|
11
|
+
*.so
|
12
|
+
*.o
|
13
|
+
*.a
|
14
|
+
mkmf.log
|
15
|
+
|
16
|
+
# rspec failure tracking
|
17
|
+
.rspec_status
|
18
|
+
|
19
|
+
# we don't care about the Yoga source files right now; they should be copied in
|
20
|
+
# during gem build from the repo root
|
21
|
+
ext/yoga_layout/*.c
|
22
|
+
ext/yoga_layout/*.h
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# YogaLayout
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/yoga_layout`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'yoga_layout'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install yoga_layout
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/justjake/yoga_layout.
|
data/Rakefile
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
require "rake/extensiontask"
|
7
|
+
|
8
|
+
task :build => :compile
|
9
|
+
|
10
|
+
Rake::ExtensionTask.new("yoga_layout") do |ext|
|
11
|
+
ext.lib_dir = "lib/yoga_layout"
|
12
|
+
end
|
13
|
+
|
14
|
+
task :copy_src do
|
15
|
+
sh 'cp ../yoga/* ext/yoga_layout'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Parses Yoga.h into ruby function information
|
19
|
+
class YogaHeaderParser
|
20
|
+
# partial regexes for the parts of a macro call
|
21
|
+
MACRO_NAME = 'YG_[A-Z_]+'
|
22
|
+
ARGUMENT = '[*\w ]+'
|
23
|
+
COMMA = ',\s?'
|
24
|
+
|
25
|
+
# a regex to parse a single macro call
|
26
|
+
MACRO_REGEX = /
|
27
|
+
^ # start of line
|
28
|
+
(?<macro>#{MACRO_NAME}) # eg, 'YG_NODE_STYLE_PROPERTY'
|
29
|
+
\( # '('
|
30
|
+
(?<type>#{ARGUMENT}) # the "type" argument, eg 'YGValue' or 'void *'
|
31
|
+
#{COMMA} #
|
32
|
+
(?<name>#{ARGUMENT}) # the name of the property, eg 'MinWidth'
|
33
|
+
(?: # optional group, because not all calls have a paramName
|
34
|
+
#{COMMA}
|
35
|
+
(?<paramName>#{ARGUMENT}) # the paramName, eg 'minWidth'
|
36
|
+
)? # close the optional group, and mark it optional
|
37
|
+
\) # ')'
|
38
|
+
;? # usually, but not always, we need a ; at the end
|
39
|
+
$ # end of line
|
40
|
+
/x
|
41
|
+
|
42
|
+
def initialize(header_source)
|
43
|
+
@header = header_source
|
44
|
+
@functions = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def call
|
48
|
+
matches.each do |match|
|
49
|
+
handle_match(match)
|
50
|
+
end
|
51
|
+
@functions
|
52
|
+
end
|
53
|
+
|
54
|
+
# crazy ruby nonsense to get all the matches of the regex on the string
|
55
|
+
def matches
|
56
|
+
@matches ||= @header.to_enum(:scan, MACRO_REGEX).map { Regexp.last_match }
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_match(match)
|
60
|
+
case match['macro']
|
61
|
+
when 'YG_NODE_PROPERTY'
|
62
|
+
yg_node_property(match)
|
63
|
+
when 'YG_NODE_STYLE_PROPERTY'
|
64
|
+
yg_node_style_property(match)
|
65
|
+
when 'YG_NODE_STYLE_PROPERTY_UNIT'
|
66
|
+
yg_node_style_property_unit(match)
|
67
|
+
when 'YG_NODE_STYLE_PROPERTY_UNIT_AUTO'
|
68
|
+
yg_node_style_property_unit_auto(match)
|
69
|
+
when 'YG_NODE_STYLE_EDGE_PROPERTY'
|
70
|
+
yg_node_style_edge_property(match)
|
71
|
+
when 'YG_NODE_STYLE_EDGE_PROPERTY_UNIT'
|
72
|
+
yg_node_style_edge_property_unit(match)
|
73
|
+
when 'YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO'
|
74
|
+
yg_node_style_edge_property_unit_auto(match)
|
75
|
+
when 'YG_NODE_LAYOUT_PROPERTY'
|
76
|
+
yg_node_layout_property(match)
|
77
|
+
when 'YG_NODE_LAYOUT_EDGE_PROPERTY'
|
78
|
+
yg_node_layout_edge_property(match)
|
79
|
+
else
|
80
|
+
raise "Unparsable: #{match}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def declare(fn_info)
|
85
|
+
name, args, result = fn_info
|
86
|
+
name = name.to_sym
|
87
|
+
args = args.map { |t| normalize_type(t) }
|
88
|
+
result = normalize_type(result)
|
89
|
+
@functions << [name, args, result]
|
90
|
+
end
|
91
|
+
|
92
|
+
def normalize_type(type)
|
93
|
+
case type
|
94
|
+
when 'void *'
|
95
|
+
:pointer
|
96
|
+
when Symbol
|
97
|
+
type
|
98
|
+
when String
|
99
|
+
type.to_sym
|
100
|
+
else
|
101
|
+
raise "cannot normalize type #{type.inpect}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def yg_node_property(match)
|
106
|
+
declare ["YGNodeSet#{match['name']}", [:YGNodeRef, match['type']], :void]
|
107
|
+
declare ["YGNodeGet#{match['name']}", [:YGNodeRef], match['type']]
|
108
|
+
end
|
109
|
+
|
110
|
+
def yg_node_style_property(match)
|
111
|
+
declare ["YGNodeStyleSet#{match['name']}", [:YGNodeRef, match['type']], :void]
|
112
|
+
declare ["YGNodeStyleGet#{match['name']}", [:YGNodeRef], match['type']]
|
113
|
+
end
|
114
|
+
|
115
|
+
def yg_node_style_property_unit(match)
|
116
|
+
declare ["YGNodeStyleSet#{match['name']}", [:YGNodeRef, :float], :void]
|
117
|
+
declare ["YGNodeStyleSet#{match['name']}Percent", [:YGNodeRef, :float], :void]
|
118
|
+
declare ["YGNodeStyleGet#{match['name']}", [:YGNodeRef], match['type']]
|
119
|
+
end
|
120
|
+
|
121
|
+
def yg_node_style_property_unit_auto(match)
|
122
|
+
puts "unit_auto: #{match.inspect}"
|
123
|
+
yg_node_style_property_unit(match)
|
124
|
+
declare ["YGNodeStyleSet#{match['name']}Auto", [:YGNodeRef], :void]
|
125
|
+
end
|
126
|
+
|
127
|
+
def yg_node_style_edge_property(match)
|
128
|
+
declare ["YGNodeStyleSet#{match['name']}", [:YGNodeRef, :YGEdge, match['type']], :void]
|
129
|
+
declare ["YGNodeStyleGet#{match['name']}", [:YGNodeRef, :YGEdge], match['type']]
|
130
|
+
end
|
131
|
+
|
132
|
+
def yg_node_style_edge_property_unit(match)
|
133
|
+
declare ["YGNodeStyleSet#{match['name']}", [:YGNodeRef, :YGEdge, :float], :void]
|
134
|
+
declare ["YGNodeStyleSet#{match['name']}Percent", [:YGNodeRef, :YGEdge, :float], :void]
|
135
|
+
# According to YGMacros.h, the return type here should be
|
136
|
+
# pointerOf(match['type']) on Windows ARM.
|
137
|
+
# This is Ruby, we don't care.
|
138
|
+
declare ["YGNodeStyleGet#{match['name']}", [:YGNodeRef, :YGEdge], match['type']]
|
139
|
+
end
|
140
|
+
|
141
|
+
def yg_node_style_edge_property_unit_auto(match)
|
142
|
+
declare ["YGNodeStyleSet#{match['name']}Auto", [:YGNodeRef, :YGEdge], :void]
|
143
|
+
end
|
144
|
+
|
145
|
+
def yg_node_layout_property(match)
|
146
|
+
declare ["YGNodeLayoutGet#{match['name']}", [:YGNodeRef], match['type']]
|
147
|
+
end
|
148
|
+
|
149
|
+
def yg_node_layout_edge_property(match)
|
150
|
+
declare ["YGNodeLayoutGet#{match['name']}", [:YGNodeRef, :YGEdge], match['type']]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
desc 'Auto-generate lib/yoga_layout/native/functions.rb from Yoga.h by parsing macros'
|
155
|
+
task :ygnode_properties do
|
156
|
+
header = File.read('../yoga/Yoga.h')
|
157
|
+
|
158
|
+
parser = YogaHeaderParser.new(header)
|
159
|
+
functions = parser.call
|
160
|
+
|
161
|
+
File.open('lib/yoga_layout/bindings/ygnode_properties.rb', 'w') do |file|
|
162
|
+
file.puts('# Auto-generated by Rake from Yoga.h')
|
163
|
+
file.puts('# Do not edit by hand')
|
164
|
+
file.puts("module YogaLayout")
|
165
|
+
file.puts(" module Bindings")
|
166
|
+
functions.each do |fn_info|
|
167
|
+
attrs = fn_info.inspect[1..-2]
|
168
|
+
puts "writing function #{attrs}"
|
169
|
+
file.puts(" remember_function #{attrs}")
|
170
|
+
end
|
171
|
+
file.puts(" end")
|
172
|
+
file.puts("end")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
task :default => [:copy_src, :ygnode_properties, :clobber, :compile, :spec]
|
data/bin/console
ADDED
data/bin/demo
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require 'pry'
|
5
|
+
require "yoga_layout"
|
6
|
+
require "drawille"
|
7
|
+
|
8
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
9
|
+
# with your gem easier. You can also use a different console, if you like.
|
10
|
+
|
11
|
+
def node_enum(node)
|
12
|
+
Enumerator.new do |y|
|
13
|
+
queue = []
|
14
|
+
queue << node
|
15
|
+
|
16
|
+
while queue.size > 0
|
17
|
+
node = queue.pop
|
18
|
+
y << node
|
19
|
+
node.get_child_count.times do |i|
|
20
|
+
child = node.get_child(i)
|
21
|
+
queue << child
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rect(brush, top, left, width, height)
|
28
|
+
top_left = [left, top]
|
29
|
+
top_right = [left + width, top]
|
30
|
+
bottom_right = [left + width, top + height]
|
31
|
+
bottom_left = [left, top + height]
|
32
|
+
|
33
|
+
brush.line(from: top_left, to: top_right)
|
34
|
+
brush.line(from: top_right, to: bottom_right)
|
35
|
+
brush.line(from: bottom_right, to: bottom_left)
|
36
|
+
brush.line(from: bottom_left, to: top_left)
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_layout(root)
|
40
|
+
root.calculate_layout
|
41
|
+
canvas = Drawille::Canvas.new
|
42
|
+
brush = Drawille::Brush.new(canvas)
|
43
|
+
node_enum(root).each do |node|
|
44
|
+
rect(
|
45
|
+
brush,
|
46
|
+
node.layout[:top],
|
47
|
+
node.layout[:left],
|
48
|
+
node.layout[:width],
|
49
|
+
node.layout[:height],
|
50
|
+
)
|
51
|
+
end
|
52
|
+
puts canvas.frame
|
53
|
+
end
|
54
|
+
|
55
|
+
class Draw
|
56
|
+
def initialize
|
57
|
+
@canvas = Drawille::Canvas.new
|
58
|
+
@brush = Drawille::Brush.new(@canvas)
|
59
|
+
end
|
60
|
+
|
61
|
+
def draw_node(node)
|
62
|
+
end
|
63
|
+
|
64
|
+
def show
|
65
|
+
puts @canvas.frame
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
layout = YogaLayout::Node[
|
70
|
+
width: 500,
|
71
|
+
height: 100,
|
72
|
+
flex_direction: :row,
|
73
|
+
padding: 20,
|
74
|
+
children: [
|
75
|
+
YogaLayout::Node[
|
76
|
+
width: 80,
|
77
|
+
margin_end: 20,
|
78
|
+
],
|
79
|
+
YogaLayout::Node[
|
80
|
+
height: 25,
|
81
|
+
align_self: :center,
|
82
|
+
flex_grow: 1,
|
83
|
+
]
|
84
|
+
],
|
85
|
+
]
|
86
|
+
|
87
|
+
binding.pry
|
88
|
+
|
89
|
+
show_layout(layout)
|
data/bin/setup
ADDED
data/lib/yoga_layout.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# YogaLayout provides a thin FFI-based wrapper around the core Yoga library.
|
2
|
+
#
|
3
|
+
# Yoga is a cross-platform layout engine enabling maximum collaboration within
|
4
|
+
# your team by implementing an API familiar to many designers and opening it up
|
5
|
+
# to developers across different platforms.
|
6
|
+
#
|
7
|
+
# @see https://facebook.github.io/yoga
|
8
|
+
module YogaLayout
|
9
|
+
# Root error class for the YogaLayout module
|
10
|
+
class Error < ::StandardError; end
|
11
|
+
|
12
|
+
# Turn a CamelCase string into a snake_case string.
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
def self.underscore(string)
|
16
|
+
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
17
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'yoga_layout/version'
|
22
|
+
require 'yoga_layout/bindings'
|
23
|
+
require 'yoga_layout/node'
|
24
|
+
require 'yoga_layout/config'
|
25
|
+
|
26
|
+
def self.undefined
|
27
|
+
::Float::NAN
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.float_is_undefined?(float)
|
31
|
+
::YogaLayout::Bindings.YGFloatIsUndefined(float)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
# Our C extension, which loads the Yoga C code.
|
4
|
+
require "yoga_layout/yoga_layout"
|
5
|
+
|
6
|
+
module YogaLayout
|
7
|
+
# Native is the libffi wrapper around Yoga.
|
8
|
+
# It does not implement anything novel like memory management or nice ruby types.
|
9
|
+
# It's just a compatibility layer.
|
10
|
+
module Bindings
|
11
|
+
extend ::FFI::Library
|
12
|
+
# ref https://github.com/ffi/ffi/wiki/Loading-Libraries#in-memory-libraries
|
13
|
+
ffi_lib ::FFI::USE_THIS_PROCESS_AS_LIBRARY
|
14
|
+
|
15
|
+
def self.functions
|
16
|
+
@functions ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wrapper around FFI::Library.attach_function that also remembers the function
|
20
|
+
# name and arguments for later static analysis
|
21
|
+
def self.remember_function(*args)
|
22
|
+
result = attach_function(*args)
|
23
|
+
functions[args.first] = args
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
# Auto-genrated by ../../enums.py
|
28
|
+
require 'yoga_layout/bindings/enums'
|
29
|
+
|
30
|
+
# Written by hand
|
31
|
+
require 'yoga_layout/bindings/typedefs'
|
32
|
+
require 'yoga_layout/bindings/ygnode'
|
33
|
+
require 'yoga_layout/bindings/ygconfig'
|
34
|
+
require 'yoga_layout/bindings/misc'
|
35
|
+
|
36
|
+
# Auto-generated by `rake ygnode_properties`
|
37
|
+
require 'yoga_layout/bindings/ygnode_properties'
|
38
|
+
end
|
39
|
+
end
|