tk_component 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0588f1482fd807ba8c250d1e3098f2a0271bda795a0264fba9ccdced638cbdb5'
4
- data.tar.gz: 2360a3b2cb6bb52aa9ae17815d3f345782da42f1f563c357d35685f13153d468
3
+ metadata.gz: 69cfe9e4f6243ca3e4e2c3fa10824a56b078450777e06d971e22c558ea84db94
4
+ data.tar.gz: 6a74e65f6d965dfc540bfc85090195d0857a4a9c0879e3774948629857d27cdd
5
5
  SHA512:
6
- metadata.gz: 89b625e7097f5109bc4fbf903c098284c857ec6aece46fadb9f2d598004191ee9a5825010e05bd86342a515cea97f6b342d9d6f619f61f33d24ff93b192b5620
7
- data.tar.gz: a647f65cc892c511cc5d6bd693b4d6865a3355b5839add9cd7c8fd1acde6e7a30ba0a5e73f2b456f32e3005b45ca025cf589a568a401d3607db94eaa3437b1bf
6
+ metadata.gz: 753418e59a8e68c868b6c221c21dd31c904b749b6403a4788313e6bdbc345a4f3e2cf3b3f5c2d306a87ec2278ddb9cc60863c9e263032ba7a1e94c341617d5ee
7
+ data.tar.gz: f741f628f87a923828d57cb9f3b1834f91f4d0ee36174d138059148c93d1570931b6549882dff8c3870176be3401bbc4d51e4811b8196778b37ff817b6ad55ff
data/Gemfile CHANGED
@@ -4,3 +4,9 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in tk_component.gemspec
6
6
  gemspec
7
+
8
+ group :development, :test do
9
+ gem 'pry'
10
+ gem 'pry-byebug'
11
+ end
12
+
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tk_component (0.1.0)
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
- concurrent-ruby (1.1.7)
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.5)
21
+ i18n (1.8.7)
20
22
  concurrent-ruby (~> 1.0)
21
- minitest (5.14.2)
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.8)
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!
@@ -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
+
@@ -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
+
@@ -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'
@@ -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.regenerate
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('ParamChanged', parent_node.native_item, self.object_id)
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,8 @@
1
+ module TkComponent
2
+ module BasicComponent
3
+ def focus
4
+ self.tk_item.focus
5
+ end
6
+ end
7
+ end
8
+
@@ -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