ruflet 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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/ruflet/version.rb +5 -0
  4. data/lib/ruflet.rb +40 -0
  5. data/lib/ruflet_protocol/ruflet/protocol.rb +62 -0
  6. data/lib/ruflet_protocol.rb +4 -0
  7. data/lib/ruflet_ui/ruflet/app.rb +20 -0
  8. data/lib/ruflet_ui/ruflet/colors.rb +200 -0
  9. data/lib/ruflet_ui/ruflet/control.rb +154 -0
  10. data/lib/ruflet_ui/ruflet/dsl.rb +182 -0
  11. data/lib/ruflet_ui/ruflet/event.rb +28 -0
  12. data/lib/ruflet_ui/ruflet/icon_data.rb +57 -0
  13. data/lib/ruflet_ui/ruflet/icons/cupertino/cupertino_icons.rb +54 -0
  14. data/lib/ruflet_ui/ruflet/icons/cupertino_icon_lookup.rb +112 -0
  15. data/lib/ruflet_ui/ruflet/icons/material_icon_lookup.rb +112 -0
  16. data/lib/ruflet_ui/ruflet/icons/material_icons.rb +55 -0
  17. data/lib/ruflet_ui/ruflet/page.rb +550 -0
  18. data/lib/ruflet_ui/ruflet/ui/control_factory.rb +22 -0
  19. data/lib/ruflet_ui/ruflet/ui/control_methods.rb +16 -0
  20. data/lib/ruflet_ui/ruflet/ui/control_registry.rb +13 -0
  21. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_action_sheet_control.rb +13 -0
  22. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_alert_dialog_control.rb +13 -0
  23. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_button_control.rb +13 -0
  24. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_dialog_action_control.rb +13 -0
  25. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_filled_button_control.rb +13 -0
  26. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_navigation_bar_control.rb +13 -0
  27. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_slider_control.rb +13 -0
  28. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_switch_control.rb +13 -0
  29. data/lib/ruflet_ui/ruflet/ui/controls/cupertino/cupertino_text_field_control.rb +13 -0
  30. data/lib/ruflet_ui/ruflet/ui/controls/material/alert_dialog_control.rb +13 -0
  31. data/lib/ruflet_ui/ruflet/ui/controls/material/app_bar_control.rb +13 -0
  32. data/lib/ruflet_ui/ruflet/ui/controls/material/bottom_sheet_control.rb +13 -0
  33. data/lib/ruflet_ui/ruflet/ui/controls/material/button_control.rb +24 -0
  34. data/lib/ruflet_ui/ruflet/ui/controls/material/checkbox_control.rb +13 -0
  35. data/lib/ruflet_ui/ruflet/ui/controls/material/column_control.rb +13 -0
  36. data/lib/ruflet_ui/ruflet/ui/controls/material/container_control.rb +13 -0
  37. data/lib/ruflet_ui/ruflet/ui/controls/material/drag_target_control.rb +13 -0
  38. data/lib/ruflet_ui/ruflet/ui/controls/material/draggable_control.rb +13 -0
  39. data/lib/ruflet_ui/ruflet/ui/controls/material/elevated_button_control.rb +13 -0
  40. data/lib/ruflet_ui/ruflet/ui/controls/material/filled_button_control.rb +13 -0
  41. data/lib/ruflet_ui/ruflet/ui/controls/material/floating_action_button_control.rb +28 -0
  42. data/lib/ruflet_ui/ruflet/ui/controls/material/gesture_detector_control.rb +13 -0
  43. data/lib/ruflet_ui/ruflet/ui/controls/material/icon_button_control.rb +13 -0
  44. data/lib/ruflet_ui/ruflet/ui/controls/material/icon_control.rb +24 -0
  45. data/lib/ruflet_ui/ruflet/ui/controls/material/image_control.rb +13 -0
  46. data/lib/ruflet_ui/ruflet/ui/controls/material/markdown_control.rb +13 -0
  47. data/lib/ruflet_ui/ruflet/ui/controls/material/navigation_bar_control.rb +13 -0
  48. data/lib/ruflet_ui/ruflet/ui/controls/material/navigation_bar_destination_control.rb +13 -0
  49. data/lib/ruflet_ui/ruflet/ui/controls/material/radio_control.rb +13 -0
  50. data/lib/ruflet_ui/ruflet/ui/controls/material/radio_group_control.rb +13 -0
  51. data/lib/ruflet_ui/ruflet/ui/controls/material/row_control.rb +13 -0
  52. data/lib/ruflet_ui/ruflet/ui/controls/material/snack_bar_control.rb +13 -0
  53. data/lib/ruflet_ui/ruflet/ui/controls/material/stack_control.rb +13 -0
  54. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_bar_control.rb +13 -0
  55. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_bar_view_control.rb +13 -0
  56. data/lib/ruflet_ui/ruflet/ui/controls/material/tab_control.rb +13 -0
  57. data/lib/ruflet_ui/ruflet/ui/controls/material/tabs_control.rb +63 -0
  58. data/lib/ruflet_ui/ruflet/ui/controls/material/text_button_control.rb +13 -0
  59. data/lib/ruflet_ui/ruflet/ui/controls/material/text_control.rb +13 -0
  60. data/lib/ruflet_ui/ruflet/ui/controls/material/text_field_control.rb +13 -0
  61. data/lib/ruflet_ui/ruflet/ui/controls/material/view_control.rb +13 -0
  62. data/lib/ruflet_ui/ruflet/ui/cupertino_control_factory.rb +40 -0
  63. data/lib/ruflet_ui/ruflet/ui/cupertino_control_methods.rb +26 -0
  64. data/lib/ruflet_ui/ruflet/ui/cupertino_control_registry.rb +35 -0
  65. data/lib/ruflet_ui/ruflet/ui/material_control_factory.rb +94 -0
  66. data/lib/ruflet_ui/ruflet/ui/material_control_methods.rb +116 -0
  67. data/lib/ruflet_ui/ruflet/ui/material_control_registry.rb +88 -0
  68. data/lib/ruflet_ui/ruflet/ui/shared_control_forwarders.rb +85 -0
  69. data/lib/ruflet_ui/ruflet/ui/widget_builder.rb +48 -0
  70. data/lib/ruflet_ui.rb +112 -0
  71. metadata +109 -0
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Ruflet
6
+ class Event
7
+ attr_reader :name, :target, :raw_data, :data, :page, :control
8
+
9
+ def initialize(name:, target:, raw_data:, page:, control:)
10
+ @name = name
11
+ @target = target
12
+ @raw_data = raw_data
13
+ @data = parse_data(raw_data)
14
+ @page = page
15
+ @control = control
16
+ end
17
+
18
+ private
19
+
20
+ def parse_data(raw)
21
+ return raw unless raw.is_a?(String)
22
+
23
+ JSON.parse(raw)
24
+ rescue JSON::ParserError
25
+ raw
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "icons/material_icon_lookup"
4
+ require_relative "icons/cupertino_icon_lookup"
5
+
6
+ module Ruflet
7
+ class IconData
8
+ attr_reader :value
9
+
10
+ def initialize(value)
11
+ @value = normalize(value)
12
+ end
13
+
14
+ def ==(other)
15
+ other_value = other.is_a?(IconData) ? other.value : self.class.new(other).value
16
+ if value.is_a?(Integer) && other_value.is_a?(Integer)
17
+ value == other_value
18
+ elsif value.is_a?(String) && other_value.is_a?(String)
19
+ value.casecmp?(other_value)
20
+ else
21
+ value == other_value
22
+ end
23
+ end
24
+
25
+ def eql?(other)
26
+ self == other
27
+ end
28
+
29
+ def hash
30
+ value.is_a?(String) ? value.downcase.hash : value.hash
31
+ end
32
+
33
+ def to_s
34
+ value.to_s
35
+ end
36
+
37
+ private
38
+
39
+ def normalize(input)
40
+ return input.value if input.is_a?(IconData)
41
+ if input.is_a?(Integer)
42
+ codepoint = Ruflet::MaterialIconLookup.codepoint_for(input)
43
+ codepoint = Ruflet::CupertinoIconLookup.codepoint_for(input) if codepoint == input
44
+ return codepoint
45
+ end
46
+
47
+ raw = input.to_s.strip
48
+ return raw if raw.empty?
49
+
50
+ codepoint = Ruflet::MaterialIconLookup.codepoint_for(raw)
51
+ codepoint = Ruflet::CupertinoIconLookup.codepoint_for(raw) if codepoint.nil?
52
+ return codepoint unless codepoint.nil?
53
+
54
+ raw
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../icon_data"
4
+ require_relative "../cupertino_icon_lookup"
5
+
6
+ module Ruflet
7
+ module CupertinoIcons
8
+ module_function
9
+
10
+ ICONS = begin
11
+ source = Ruflet::CupertinoIconLookup.icon_map
12
+ if source.empty?
13
+ {
14
+ HOME: "house",
15
+ SEARCH: "search",
16
+ SETTINGS: "gear",
17
+ ADD: "add",
18
+ CLOSE: "clear"
19
+ }
20
+ else
21
+ source.keys.each_with_object({}) do |name, result|
22
+ text = name.to_s.gsub(/[^a-zA-Z0-9]/, "_").gsub(/_+/, "_").sub(/\A_/, "").sub(/_\z/, "")
23
+ text = "ICON_#{text}" if text.match?(/\A\d/)
24
+ result[text.upcase.to_sym] = name
25
+ end
26
+ end.freeze
27
+ end
28
+
29
+ ICONS.each do |const_name, icon_name|
30
+ next if const_defined?(const_name, false)
31
+
32
+ const_set(const_name, Ruflet::IconData.new(icon_name))
33
+ end
34
+
35
+ def [](name)
36
+ key = name.to_s.upcase.to_sym
37
+ return const_get(key) if const_defined?(key, false)
38
+
39
+ Ruflet::IconData.new(name.to_s)
40
+ end
41
+
42
+ def all
43
+ ICONS.keys.map { |k| const_get(k) }
44
+ end
45
+
46
+ def random
47
+ all.sample || Ruflet::IconData.new(Ruflet::CupertinoIconLookup.fallback_codepoint)
48
+ end
49
+
50
+ def names
51
+ ICONS.values
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruflet
4
+ module CupertinoIconLookup
5
+ module_function
6
+
7
+ def codepoint_for(value)
8
+ return value if value.is_a?(Integer)
9
+
10
+ text = value.to_s.strip
11
+ return nil if text.empty?
12
+
13
+ numeric = parse_numeric(text)
14
+ return numeric unless numeric.nil?
15
+
16
+ icons = icon_map
17
+ candidate_names(text).each do |name|
18
+ codepoint = icons[name]
19
+ return codepoint unless codepoint.nil?
20
+ end
21
+
22
+ nil
23
+ end
24
+
25
+ def fallback_codepoint
26
+ icons = icon_map
27
+ icons["QUESTION_CIRCLE"] || icons["QUESTION"] || 0
28
+ end
29
+
30
+ def icon_map
31
+ @icon_map ||= load_icon_map
32
+ end
33
+
34
+ def candidate_names(name)
35
+ raw = name.to_s.strip
36
+ return [] if raw.empty?
37
+
38
+ underscored = raw.gsub(/\s+|-/, "_").downcase
39
+ stripped = underscored.sub(/\Acupertinoicons\./i, "")
40
+ upper = stripped.upcase
41
+
42
+ [raw, raw.upcase, underscored, stripped, upper].uniq
43
+ end
44
+
45
+ def parse_numeric(text)
46
+ return text.to_i(16) if text.match?(/\A0x[0-9a-fA-F]+\z/)
47
+ return text.to_i if text.match?(/\A\d+\z/)
48
+
49
+ nil
50
+ end
51
+
52
+ def load_icon_map
53
+ path = flet_cupertino_icons_json_file
54
+ if path && File.file?(path)
55
+ return parse_icons_json(path)
56
+ end
57
+
58
+ dart_list = flet_cupertino_icons_dart_file
59
+ if dart_list && File.file?(dart_list)
60
+ return parse_flet_icons_dart(dart_list, icon_prefix: "CupertinoIcons.", set_id: 2)
61
+ end
62
+
63
+ {}
64
+ end
65
+
66
+ def flet_cupertino_icons_json_file
67
+ candidate_from_flet_checkout("sdk/python/packages/flet/src/flet/controls/cupertino/cupertino_icons.json")
68
+ end
69
+
70
+ def flet_cupertino_icons_dart_file
71
+ candidate_from_flet_checkout("packages/flet/lib/src/utils/cupertino_icons.dart")
72
+ end
73
+
74
+ def candidate_from_flet_checkout(relative_path)
75
+ flet_root = File.join(Dir.home, ".pub-cache", "git")
76
+ return nil unless Dir.exist?(flet_root)
77
+
78
+ entries = Dir.children(flet_root).select { |e| e.start_with?("flet-") }
79
+ return nil if entries.empty?
80
+
81
+ candidates = entries.map { |e| File.join(flet_root, e, relative_path) }.select { |p| File.file?(p) }
82
+ return nil if candidates.empty?
83
+
84
+ candidates.max_by { |p| File.mtime(p) rescue Time.at(0) }
85
+ end
86
+
87
+ def parse_icons_json(path)
88
+ require "json"
89
+ parsed = JSON.parse(File.read(path))
90
+ parsed.each_with_object({}) do |(k, v), out|
91
+ out[k.to_s.upcase] = v.to_i
92
+ end
93
+ end
94
+
95
+ def parse_flet_icons_dart(path, icon_prefix:, set_id:)
96
+ mapping = {}
97
+ index = 0
98
+ pattern = /^\s*#{Regexp.escape(icon_prefix)}([a-zA-Z0-9_]+),/
99
+
100
+ File.foreach(path) do |line|
101
+ next unless (match = line.match(pattern))
102
+
103
+ encoded = (set_id << 16) | index
104
+ mapping[match[1].upcase] = encoded
105
+ index += 1
106
+ end
107
+
108
+ mapping
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruflet
4
+ module MaterialIconLookup
5
+ module_function
6
+
7
+ def codepoint_for(value)
8
+ return value if value.is_a?(Integer)
9
+
10
+ text = value.to_s.strip
11
+ return nil if text.empty?
12
+
13
+ numeric = parse_numeric(text)
14
+ return numeric unless numeric.nil?
15
+
16
+ icons = icon_map
17
+ candidate_names(text).each do |name|
18
+ codepoint = icons[name]
19
+ return codepoint unless codepoint.nil?
20
+ end
21
+
22
+ nil
23
+ end
24
+
25
+ def fallback_codepoint
26
+ icons = icon_map
27
+ icons["HELP_OUTLINE"] || icons["HELP"] || icons["QUESTION_MARK"] || 0
28
+ end
29
+
30
+ def icon_map
31
+ @icon_map ||= load_icon_map
32
+ end
33
+
34
+ def candidate_names(name)
35
+ raw = name.to_s.strip
36
+ return [] if raw.empty?
37
+
38
+ underscored = raw.gsub(/\s+|-/, "_").downcase
39
+ stripped = underscored.sub(/\Aicons\./, "")
40
+ upper = stripped.upcase
41
+
42
+ [raw, raw.upcase, underscored, stripped, upper].uniq
43
+ end
44
+
45
+ def parse_numeric(text)
46
+ return text.to_i(16) if text.match?(/\A0x[0-9a-fA-F]+\z/)
47
+ return text.to_i if text.match?(/\A\d+\z/)
48
+
49
+ nil
50
+ end
51
+
52
+ def load_icon_map
53
+ path = flet_material_icons_json_file
54
+ if path && File.file?(path)
55
+ return parse_icons_json(path)
56
+ end
57
+
58
+ dart_list = flet_material_icons_dart_file
59
+ if dart_list && File.file?(dart_list)
60
+ return parse_flet_icons_dart(dart_list, icon_prefix: "Icons.", set_id: 1)
61
+ end
62
+
63
+ {}
64
+ end
65
+
66
+ def flet_material_icons_json_file
67
+ candidate_from_flet_checkout("sdk/python/packages/flet/src/flet/controls/material/icons.json")
68
+ end
69
+
70
+ def flet_material_icons_dart_file
71
+ candidate_from_flet_checkout("packages/flet/lib/src/utils/material_icons.dart")
72
+ end
73
+
74
+ def candidate_from_flet_checkout(relative_path)
75
+ flet_root = File.join(Dir.home, ".pub-cache", "git")
76
+ return nil unless Dir.exist?(flet_root)
77
+
78
+ entries = Dir.children(flet_root).select { |e| e.start_with?("flet-") }
79
+ return nil if entries.empty?
80
+
81
+ candidates = entries.map { |e| File.join(flet_root, e, relative_path) }.select { |p| File.file?(p) }
82
+ return nil if candidates.empty?
83
+
84
+ candidates.max_by { |p| File.mtime(p) rescue Time.at(0) }
85
+ end
86
+
87
+ def parse_icons_json(path)
88
+ require "json"
89
+ parsed = JSON.parse(File.read(path))
90
+ parsed.each_with_object({}) do |(k, v), out|
91
+ out[k.to_s.upcase] = v.to_i
92
+ end
93
+ end
94
+
95
+ def parse_flet_icons_dart(path, icon_prefix:, set_id:)
96
+ mapping = {}
97
+ index = 0
98
+ pattern = /^\s*#{Regexp.escape(icon_prefix)}([a-zA-Z0-9_]+),/
99
+
100
+ File.foreach(path) do |line|
101
+ next unless (match = line.match(pattern))
102
+
103
+ encoded = (set_id << 16) | index
104
+ mapping[match[1].upcase] = encoded
105
+ index += 1
106
+ end
107
+
108
+ mapping
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../icon_data"
4
+ require_relative "material_icon_lookup"
5
+
6
+ module Ruflet
7
+ module MaterialIcons
8
+ module_function
9
+
10
+ ICONS = begin
11
+ source = Ruflet::MaterialIconLookup.icon_map
12
+ if source.empty?
13
+ {
14
+ HOME: "home",
15
+ SETTINGS: "settings",
16
+ SEARCH: "search",
17
+ ADD: "add",
18
+ CLOSE: "close"
19
+ }
20
+ else
21
+ source.keys.each_with_object({}) do |name, result|
22
+ text = name.to_s.gsub(/[^a-zA-Z0-9]/, "_").gsub(/_+/, "_").sub(/\A_/, "").sub(/_\z/, "")
23
+ text = "ICON_#{text}" if text.match?(/\A\d/)
24
+ result[text.upcase.to_sym] = name
25
+ end
26
+ end.freeze
27
+ end
28
+
29
+ ICONS.each do |const_name, icon_name|
30
+ next if const_defined?(const_name, false)
31
+
32
+ const_set(const_name, Ruflet::IconData.new(icon_name))
33
+ end
34
+
35
+ def [](name)
36
+ key = name.to_s.upcase.to_sym
37
+ return const_get(key) if const_defined?(key, false)
38
+
39
+ Ruflet::IconData.new(name.to_s)
40
+ end
41
+
42
+ def all
43
+ ICONS.keys.map { |k| const_get(k) }
44
+ end
45
+
46
+ def random
47
+ all.sample || Ruflet::IconData.new(Ruflet::MaterialIconLookup.fallback_codepoint)
48
+ end
49
+
50
+ def names
51
+ ICONS.values
52
+ end
53
+
54
+ end
55
+ end