tk_component 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -0
- data/Gemfile.lock +16 -5
- data/bin/browser_demo.rb +49 -0
- data/bin/table_view_demo.rb +66 -0
- data/bin/tiles_demo.rb +48 -0
- data/lib/tk_component.rb +5 -0
- data/lib/tk_component/base.rb +49 -3
- data/lib/tk_component/basic_component.rb +8 -0
- data/lib/tk_component/browser_column_component.rb +70 -0
- data/lib/tk_component/browser_component.rb +66 -0
- data/lib/tk_component/builder/grid_map.rb +4 -4
- data/lib/tk_component/builder/node.rb +35 -11
- data/lib/tk_component/builder/tk_item.rb +354 -18
- data/lib/tk_component/menu.rb +4 -0
- data/lib/tk_component/r_browser_component.rb +33 -0
- data/lib/tk_component/table_view_component.rb +90 -0
- data/lib/tk_component/turtle.rb +100 -0
- data/lib/tk_component/version.rb +1 -1
- data/lib/tk_component/window.rb +4 -4
- metadata +16 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69cfe9e4f6243ca3e4e2c3fa10824a56b078450777e06d971e22c558ea84db94
|
4
|
+
data.tar.gz: 6a74e65f6d965dfc540bfc85090195d0857a4a9c0879e3774948629857d27cdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 753418e59a8e68c868b6c221c21dd31c904b749b6403a4788313e6bdbc345a4f3e2cf3b3f5c2d306a87ec2278ddb9cc60863c9e263032ba7a1e94c341617d5ee
|
7
|
+
data.tar.gz: f741f628f87a923828d57cb9f3b1834f91f4d0ee36174d138059148c93d1570931b6549882dff8c3870176be3401bbc4d51e4811b8196778b37ff817b6ad55ff
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tk_component (0.1.
|
4
|
+
tk_component (0.1.1)
|
5
5
|
activesupport (~> 6.0.3)
|
6
6
|
tk (~> 0.3.0)
|
7
7
|
|
@@ -14,11 +14,20 @@ GEM
|
|
14
14
|
minitest (~> 5.1)
|
15
15
|
tzinfo (~> 1.1)
|
16
16
|
zeitwerk (~> 2.2, >= 2.2.2)
|
17
|
-
|
17
|
+
byebug (11.1.3)
|
18
|
+
coderay (1.1.3)
|
19
|
+
concurrent-ruby (1.1.8)
|
18
20
|
diff-lcs (1.4.4)
|
19
|
-
i18n (1.8.
|
21
|
+
i18n (1.8.7)
|
20
22
|
concurrent-ruby (~> 1.0)
|
21
|
-
|
23
|
+
method_source (1.0.0)
|
24
|
+
minitest (5.14.3)
|
25
|
+
pry (0.13.1)
|
26
|
+
coderay (~> 1.1)
|
27
|
+
method_source (~> 1.0)
|
28
|
+
pry-byebug (3.9.0)
|
29
|
+
byebug (~> 11.0)
|
30
|
+
pry (~> 0.13.0)
|
22
31
|
rake (10.5.0)
|
23
32
|
rspec (3.10.0)
|
24
33
|
rspec-core (~> 3.10.0)
|
@@ -35,7 +44,7 @@ GEM
|
|
35
44
|
rspec-support (3.10.0)
|
36
45
|
thread_safe (0.3.6)
|
37
46
|
tk (0.3.0)
|
38
|
-
tzinfo (1.2.
|
47
|
+
tzinfo (1.2.9)
|
39
48
|
thread_safe (~> 0.1)
|
40
49
|
zeitwerk (2.4.2)
|
41
50
|
|
@@ -44,6 +53,8 @@ PLATFORMS
|
|
44
53
|
|
45
54
|
DEPENDENCIES
|
46
55
|
bundler (~> 1.16)
|
56
|
+
pry
|
57
|
+
pry-byebug
|
47
58
|
rake (~> 10.0)
|
48
59
|
rspec (~> 3.0)
|
49
60
|
tk_component!
|
data/bin/browser_demo.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tk_component"
|
5
|
+
require "pry"
|
6
|
+
|
7
|
+
class DataSource
|
8
|
+
@@shared_data_source = nil
|
9
|
+
|
10
|
+
def self.shared
|
11
|
+
@@shared_data_source ||= self.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def items_for_path(path)
|
15
|
+
path = [] if path.blank?
|
16
|
+
path_str = File.join(ENV['HOME'], *path)
|
17
|
+
return nil unless Dir.exist?(path_str)
|
18
|
+
Dir.children(path_str)
|
19
|
+
end
|
20
|
+
|
21
|
+
def title_for_path(path, items)
|
22
|
+
path.blank? ? ENV['HOME'] : path.last
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DemoRoot < TkComponent::Base
|
27
|
+
def generate(parent_component, options = {})
|
28
|
+
parse_component(parent_component, options) do |p|
|
29
|
+
p.vframe(sticky: 'wens', x_flex: 1, y_flex: 1) do |f|
|
30
|
+
f.label(text: "Directory of #{DataSource.shared.title_for_path(nil, [])}")
|
31
|
+
f.insert_component(TkComponent::BrowserComponent, self,
|
32
|
+
data_source: DataSource.shared,
|
33
|
+
paned: true,
|
34
|
+
sticky: 'nsew', x_flex: 1, y_flex: 1) do |bc|
|
35
|
+
bc.on_event'PathChanged', ->(e) do
|
36
|
+
puts "PathChanged: " + e.data_object.selected_path.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@tk_root = TkComponent::Window.new(title: "BrowserComponent Demo", root: true)
|
45
|
+
@main_component = DemoRoot.new
|
46
|
+
@tk_root.place_root_component(@main_component)
|
47
|
+
|
48
|
+
Tk.mainloop
|
49
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tk_component"
|
5
|
+
require "pry"
|
6
|
+
|
7
|
+
TABLE_COLS = %w|name size|
|
8
|
+
|
9
|
+
class DataSource
|
10
|
+
@@shared_data_source = nil
|
11
|
+
|
12
|
+
def self.shared
|
13
|
+
@@shared_data_source ||= self.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def items_for_path(path)
|
17
|
+
path = [] if path.blank?
|
18
|
+
path_str = File.join(ENV['HOME'], *file_segments_for(path))
|
19
|
+
return nil unless Dir.exist?(path_str)
|
20
|
+
Dir.children(path_str).map do |f|
|
21
|
+
name = File.join(path_str, f)
|
22
|
+
{
|
23
|
+
name: f,
|
24
|
+
size: File.exist?(name) && !File.directory?(name) && File.size(name) || ''
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_sub_items?(path)
|
30
|
+
path = [] if path.blank?
|
31
|
+
path_str = File.join(ENV['HOME'], *file_segments_for(path))
|
32
|
+
Dir.exist?(path_str)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def file_segments_for(path)
|
38
|
+
path.map { |p| p[:name] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class DemoRoot < TkComponent::Base
|
43
|
+
def generate(parent_component, options = {})
|
44
|
+
parse_component(parent_component, options) do |p|
|
45
|
+
p.vframe(sticky: 'wens', x_flex: 1, y_flex: 1) do |f|
|
46
|
+
f.label(text: "Directory of #{ENV['HOME']}")
|
47
|
+
f.insert_component(TkComponent::TableViewComponent, self,
|
48
|
+
data_source: DataSource.shared,
|
49
|
+
columns: [
|
50
|
+
{ key: :name, text: 'Name' },
|
51
|
+
{ key: :size, text: 'Size' }
|
52
|
+
],
|
53
|
+
nested: true,
|
54
|
+
lazy: true,
|
55
|
+
sticky: 'nsew', x_flex: 1, y_flex: 1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@tk_root = TkComponent::Window.new(title: "TableView Demo", root: true)
|
62
|
+
@main_component = DemoRoot.new
|
63
|
+
@tk_root.place_root_component(@main_component)
|
64
|
+
|
65
|
+
Tk.mainloop
|
66
|
+
|
data/bin/tiles_demo.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tk_component"
|
5
|
+
require "pry"
|
6
|
+
|
7
|
+
class ColorBlock < TkComponent::Base
|
8
|
+
def generate(parent_component, options = {})
|
9
|
+
parse_component(parent_component, options) do |p|
|
10
|
+
p.canvas(background: options[:color], sticky: 'wens', x_flex: 1, y_flex: 1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class DemoRoot < TkComponent::Base
|
16
|
+
COLORS = %w|red yellow green brown blue orange|
|
17
|
+
ROWS = 10
|
18
|
+
COLUMNS = 10
|
19
|
+
def generate(parent_component, options = {})
|
20
|
+
parse_component(parent_component, options) do |p|
|
21
|
+
p.frame(sticky: 'wens', x_flex: 1, y_flex: 1) do |f|
|
22
|
+
f.row(sticky: 'wens', x_flex: 1, y_flex: 1) do |r|
|
23
|
+
r.button(text: "Refresh", columnspan: COLUMNS, sticky: 'e') do |b|
|
24
|
+
b.on_click ->(e) { regenerate }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
ROWS.times do
|
28
|
+
f.row(sticky: 'wens', x_flex: 1, y_flex: 1) do |r|
|
29
|
+
COLUMNS.times do
|
30
|
+
r.insert_component(ColorBlock, self, color: random_color, sticky: 'nsew', x_flex: 1, y_flex: 1)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def random_color
|
39
|
+
COLORS.sample
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@tk_root = TkComponent::Window.new(title: "Demo")
|
44
|
+
@main_component = DemoRoot.new
|
45
|
+
@tk_root.place_root_component(@main_component)
|
46
|
+
|
47
|
+
Tk.mainloop
|
48
|
+
|
data/lib/tk_component.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require "tk_component/version"
|
2
2
|
require "active_support/all"
|
3
3
|
|
4
|
+
require_relative 'tk_component/basic_component'
|
4
5
|
require_relative 'tk_component/base'
|
6
|
+
require_relative 'tk_component/browser_component'
|
7
|
+
require_relative 'tk_component/r_browser_component'
|
8
|
+
require_relative 'tk_component/table_view_component'
|
5
9
|
require_relative 'tk_component/window'
|
6
10
|
require_relative 'tk_component/builder'
|
11
|
+
require_relative 'tk_component/turtle'
|
data/lib/tk_component/base.rb
CHANGED
@@ -10,6 +10,8 @@ module TkComponent
|
|
10
10
|
attr_accessor :children
|
11
11
|
attr_accessor :node
|
12
12
|
|
13
|
+
include BasicComponent
|
14
|
+
|
13
15
|
def initialize(options = {})
|
14
16
|
@parent = options[:parent]
|
15
17
|
@parent_node = options[:parent_node]
|
@@ -27,21 +29,65 @@ module TkComponent
|
|
27
29
|
@node = @node.sub_nodes.first # Get rid of the dummy top node
|
28
30
|
end
|
29
31
|
|
32
|
+
def parse_nodes(parent_node, options = {})
|
33
|
+
old_sub_nodes = parent_node.sub_nodes.dup
|
34
|
+
yield(parent_node)
|
35
|
+
new_sub_nodes = parent_node.sub_nodes - old_sub_nodes
|
36
|
+
new_sub_nodes.each { |n| n.prepare_option_events(self) }
|
37
|
+
parent_node.prepare_grid
|
38
|
+
new_sub_nodes
|
39
|
+
end
|
40
|
+
|
30
41
|
def build(parent_component)
|
31
42
|
@node.build(@parent_node, parent_component)
|
32
43
|
component_did_build
|
33
44
|
children.each do |c|
|
34
45
|
c.build(self)
|
46
|
+
TkGrid.columnconfigure c.parent_node.tk_item.native_item, 0, weight: 1
|
47
|
+
TkGrid.rowconfigure c.parent_node.tk_item.native_item, 0, weight: 1
|
48
|
+
TkGrid.columnconfigure c.node.tk_item.native_item, 0, weight: 1
|
49
|
+
TkGrid.rowconfigure c.node.tk_item.native_item, 0, weight: 1
|
35
50
|
end
|
36
51
|
end
|
37
52
|
|
38
53
|
def regenerate
|
39
54
|
old_node = @node
|
55
|
+
old_children = @children
|
56
|
+
@children = []
|
40
57
|
generate(parent)
|
41
58
|
rebuild(old_node)
|
42
|
-
children.each do |c|
|
43
|
-
c.
|
59
|
+
@children.each do |c|
|
60
|
+
c.generate(self)
|
61
|
+
c.build(self)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def regenerate_from_node(node, parent_node, options = {}, &block)
|
66
|
+
regenerate_from_index(parent_node, parent_node.sub_nodes.index(node), options, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def regenerate_after_node(node, parent_node, options = {}, &block)
|
70
|
+
return if parent_node.sub_nodes.index(node).nil?
|
71
|
+
regenerate_from_index(parent_node, parent_node.sub_nodes.index(node) + 1, options, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def regenerate_from_index(parent_node, index, options = {}, &block)
|
75
|
+
old_children = @children.dup
|
76
|
+
to_remove = parent_node.sub_nodes.slice!(index..-1)
|
77
|
+
to_remove.each do |n|
|
78
|
+
n.remove
|
79
|
+
end
|
80
|
+
new_sub_nodes = parse_nodes(parent_node, options, &block)
|
81
|
+
new_children = @children - old_children
|
82
|
+
new_sub_nodes.each do |n|
|
83
|
+
n.build(parent_node, self)
|
84
|
+
end
|
85
|
+
new_children.each do |c|
|
86
|
+
c.generate(self)
|
87
|
+
c.build(self)
|
44
88
|
end
|
89
|
+
parent_node.apply_grid
|
90
|
+
parent_node.built
|
45
91
|
end
|
46
92
|
|
47
93
|
def rebuild(old_node)
|
@@ -53,7 +99,7 @@ module TkComponent
|
|
53
99
|
end
|
54
100
|
|
55
101
|
def emit(event_name)
|
56
|
-
TkComponent::Builder::Event.emit(
|
102
|
+
TkComponent::Builder::Event.emit(event_name, parent_node.native_item, self.object_id)
|
57
103
|
end
|
58
104
|
|
59
105
|
def component_did_build
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module TkComponent
|
2
|
+
class BrowserColumnComponent < TkComponent::Base
|
3
|
+
|
4
|
+
attr_accessor :browser
|
5
|
+
attr_accessor :column_index
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
super
|
9
|
+
@browser = options[:browser]
|
10
|
+
@column_index = options[:column_index] || 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate(parent_component, options = {})
|
14
|
+
parse_component(parent_component, options) do |p|
|
15
|
+
if @column_index <= @browser.selected_path.size
|
16
|
+
current_item = @browser.selected_path[@column_index]
|
17
|
+
path_so_far = @browser.selected_path.slice(0, @column_index)
|
18
|
+
items = @browser.data_source.items_for_path(path_so_far)
|
19
|
+
items ||= []
|
20
|
+
else
|
21
|
+
items = []
|
22
|
+
current_item = nil
|
23
|
+
end
|
24
|
+
command = @browser.paned ? :hpaned : :hframe
|
25
|
+
p.send(command, sticky: 'nsew', x_flex: 1, y_flex: 1) do |f|
|
26
|
+
@tree = f.tree(sticky: 'nsew', x_flex: 1, y_flex: 1,
|
27
|
+
on_select: :select_item,
|
28
|
+
scrollers: 'y', heading: @browser.data_source.title_for_path(path_so_far, items)) do |t|
|
29
|
+
items.each do |item|
|
30
|
+
t.tree_node(at: 'end',
|
31
|
+
text: item,
|
32
|
+
selected: item == current_item)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if (@browser.max_columns.blank? || @browser.max_columns > @column_index + 1) &&
|
36
|
+
(@column_index < @browser.selected_path.size || items.present?)
|
37
|
+
f.hframe(sticky: 'nsew', x_flex: 1, y_flex: 1) do |hf|
|
38
|
+
@next_column = hf.insert_component(TkComponent::BrowserColumnComponent, self,
|
39
|
+
browser: @browser,
|
40
|
+
column_index: @column_index + 1,
|
41
|
+
sticky: 'nsew', x_flex: 1, y_flex: 1) do |bc|
|
42
|
+
bc.on_event 'ItemSelected', ->(e) do
|
43
|
+
emit('ItemSelected')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def component_did_build
|
53
|
+
show_current_selection
|
54
|
+
end
|
55
|
+
|
56
|
+
def show_current_selection
|
57
|
+
@tree.tk_item.scroll_to_selection
|
58
|
+
end
|
59
|
+
|
60
|
+
def select_item(e)
|
61
|
+
item = e.sender.native_item.selection&.first.text.to_s
|
62
|
+
return if @browser.selected_path[@column_index] == item
|
63
|
+
@browser.selected_path[@column_index] = item
|
64
|
+
@browser.selected_path.slice!(@column_index + 1..-1) if @column_index < @browser.selected_path.size - 1
|
65
|
+
puts "New selected path: #{@browser.selected_path}"
|
66
|
+
@next_column&.regenerate
|
67
|
+
emit('ItemSelected')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module TkComponent
|
2
|
+
class BrowserComponent < TkComponent::Base
|
3
|
+
|
4
|
+
attr_accessor :data_source
|
5
|
+
attr_accessor :selected_path
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
super
|
9
|
+
@data_source = options[:data_source]
|
10
|
+
@selected_path = options[:selected_path] || []
|
11
|
+
@paned = !!options[:paned]
|
12
|
+
@trees_container = nil
|
13
|
+
@trees = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate(parent_component, options = {})
|
17
|
+
parse_component(parent_component, options) do |p|
|
18
|
+
partial_path = []
|
19
|
+
@trees = []
|
20
|
+
p.vframe(sticky: 'nsew', x_flex: 1, y_flex: 1) do |vf|
|
21
|
+
command = @paned ? :hpaned : :hframe
|
22
|
+
@trees_container = vf.send(command, sticky: 'nsew', x_flex: 1, y_flex: 1) do |f|
|
23
|
+
generate_from_level(f, 0)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def show_current_selection
|
30
|
+
@trees.each { |t| t.tk_item.scroll_to_selection }
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_from_level(container, start_index)
|
34
|
+
(start_index..selected_path.size).each do |idx|
|
35
|
+
next_in_path = selected_path[idx]
|
36
|
+
partial_path = selected_path.slice(0, idx)
|
37
|
+
items = data_source.items_for_path(partial_path)
|
38
|
+
title = items.nil? ? '' : data_source.title_for_path(partial_path, items)
|
39
|
+
@trees << container.tree(sticky: 'nsew', x_flex: 1, y_flex: 1,
|
40
|
+
scrollers: 'y', heading: title) do |t|
|
41
|
+
items&.each do |item|
|
42
|
+
t.tree_node(at: 'end', text: item, selected: item == next_in_path)
|
43
|
+
end
|
44
|
+
t.on_select ->(e) { select_item(e.sender, idx) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def select_item(sender, index)
|
50
|
+
# If we don't do this update sometimes the selection is not
|
51
|
+
# updated until some later event, like a mouse drag
|
52
|
+
Tk.update
|
53
|
+
item = sender.native_item.selection&.first.text.to_s
|
54
|
+
return if selected_path[index] == item
|
55
|
+
selected_path[index] = item
|
56
|
+
selected_path.slice!(index + 1..-1) if index < selected_path.size - 1
|
57
|
+
@trees.slice!(index + 1..-1)
|
58
|
+
regenerate_after_node(@trees[index], @trees_container) do |container|
|
59
|
+
generate_from_level(container, index + 1)
|
60
|
+
end
|
61
|
+
Tk.update
|
62
|
+
emit('PathChanged')
|
63
|
+
Tk.update
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|