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 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