mittens_ui 0.0.7 → 0.0.11

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +3 -1
  5. data/Gemfile.lock +5 -2
  6. data/LICENSE +21 -0
  7. data/README.md +78 -21
  8. data/examples/app.rb +101 -8
  9. data/examples/assets/gnome_logo.png +0 -0
  10. data/examples/assets/mittens_ui_preview.gif +0 -0
  11. data/examples/contacts.rb +80 -0
  12. data/examples/file_menu_example.rb +52 -0
  13. data/examples/notify_example.rb +19 -0
  14. data/lib/mittens_ui.rb +40 -67
  15. data/lib/mittens_ui/alert.rb +32 -0
  16. data/lib/mittens_ui/assets/icon.png +0 -0
  17. data/lib/mittens_ui/assets/mittens_ui_preview.gif +0 -0
  18. data/lib/mittens_ui/button.rb +40 -0
  19. data/lib/mittens_ui/checkbox.rb +24 -0
  20. data/lib/mittens_ui/core.rb +34 -0
  21. data/lib/mittens_ui/file_menu.rb +83 -0
  22. data/lib/mittens_ui/file_picker.rb +29 -0
  23. data/lib/mittens_ui/grid.rb +29 -0
  24. data/lib/mittens_ui/hbox.rb +56 -0
  25. data/lib/mittens_ui/header_bar.rb +46 -0
  26. data/lib/mittens_ui/helpers.rb +41 -0
  27. data/lib/mittens_ui/image.rb +46 -0
  28. data/lib/mittens_ui/label.rb +19 -0
  29. data/lib/mittens_ui/listbox.rb +47 -0
  30. data/lib/mittens_ui/loader.rb +33 -0
  31. data/lib/mittens_ui/notify.rb +61 -0
  32. data/lib/mittens_ui/slider.rb +32 -0
  33. data/lib/mittens_ui/switch.rb +36 -0
  34. data/lib/mittens_ui/table_view.rb +179 -0
  35. data/lib/mittens_ui/textbox.rb +53 -0
  36. data/lib/mittens_ui/version.rb +1 -1
  37. data/lib/mittens_ui/web_link.rb +22 -0
  38. data/mittens_ui.gemspec +3 -3
  39. data/notes/dev_setup.txt +14 -0
  40. metadata +37 -16
  41. data/examples/brightness_controller.rb +0 -64
  42. data/lib/mittens_ui/layouts/box.rb +0 -38
  43. data/lib/mittens_ui/layouts/grid.rb +0 -31
  44. data/lib/mittens_ui/widgets/alert.rb +0 -33
  45. data/lib/mittens_ui/widgets/button.rb +0 -34
  46. data/lib/mittens_ui/widgets/label.rb +0 -41
  47. data/lib/mittens_ui/widgets/listbox.rb +0 -45
  48. data/lib/mittens_ui/widgets/slider.rb +0 -32
  49. data/lib/mittens_ui/widgets/switch.rb +0 -57
  50. data/lib/mittens_ui/widgets/textbox.rb +0 -25
@@ -0,0 +1,56 @@
1
+ require "mittens_ui/helpers"
2
+
3
+ module MittensUi
4
+ class HBox
5
+ include Helpers
6
+
7
+ def initialize(widgets, options={})
8
+ box_spacing = options[:spacing].nil? ? 6 : options[:spacing]
9
+
10
+ @box = Gtk::Box.new(:horizontal, box_spacing)
11
+
12
+ set_margin_from_opts_for(@box, options)
13
+
14
+ widgets.each do |w|
15
+ # We need to remove the widget from the global $vertical_box before hand
16
+ # otherwise it won't render the widgets properly since they are already in another container.
17
+ w.remove
18
+ self.attach(w.core_widget, { position: :start })
19
+ end
20
+ end
21
+
22
+ def remove
23
+ return if @box.nil?
24
+ @box.destroy
25
+ end
26
+
27
+ def render
28
+ $vertical_box.pack_start(@box)
29
+ return self
30
+ end
31
+
32
+ private
33
+
34
+ def attach(widget, options={})
35
+ expand = options[:expand].nil? ? true : options[:expand]
36
+ fill = options[:fill].nil? ? true : options[:fill]
37
+ padding = options[:padding].nil? ? 0 : options[:padding]
38
+
39
+ filterd_options = {
40
+ expaned: expand,
41
+ fill: fill,
42
+ padding: padding
43
+ }.freeze
44
+
45
+ case options.dig(:position)
46
+ when :start
47
+ @box.pack_start(widget, filterd_options)
48
+ when :end
49
+ @box.pack_end(widget, filterd_options)
50
+ when nil
51
+ @box.pack_start(widget, filterd_options)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class HeaderBar < Core
5
+ def initialize(widgets, options = {})
6
+ title = options[:title].nil? ? "" : options[:title]
7
+ position = options[:position].nil? ? :left : options[:position]
8
+
9
+ box = Gtk::Box.new(:horizontal, 0)
10
+ box.style_context.add_class("linked")
11
+
12
+ @header = Gtk::HeaderBar.new
13
+ @header.show_close_button = true
14
+ @header.title = title
15
+ @header.has_subtitle = false
16
+
17
+ widgets.each do |w|
18
+ w.remove
19
+ case position
20
+ when :left
21
+ box.pack_start(w.core_widget)
22
+ when :right
23
+ box.pack_end(w.core_widget)
24
+ else
25
+ box.pack_start(w.core_widget)
26
+ end
27
+ end
28
+
29
+ if position == :left
30
+ @header.pack_start(box)
31
+ end
32
+
33
+ if position == :right
34
+ @header.pack_end(box)
35
+ end
36
+
37
+ super(@header, options)
38
+ end
39
+
40
+ def render
41
+ $app_window.titlebar = @header
42
+ return self
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ module MittensUi
2
+ module Helpers
3
+ def icon_map
4
+ {
5
+ add: "list-add-symbolic",
6
+ remove: "list-remove-symbolic",
7
+ add_green: 'add',
8
+ remove_red: 'remove'
9
+ }.freeze
10
+ end
11
+
12
+ def list_system_icons
13
+ @theme = Gtk::IconTheme.default
14
+ puts @theme.icons
15
+ end
16
+
17
+ def set_margin_from_opts_for(widget, options={})
18
+ margin_top = options[:top].nil? ? nil : options[:top]
19
+ margin_bottom = options[:bottom].nil? ? nil : options[:bottom]
20
+ margin_right = options[:right].nil? ? nil : options[:right]
21
+ margin_left = options[:left].nil? ? nil : options[:left]
22
+
23
+ unless margin_top.nil?
24
+ widget.set_margin_top(margin_top)
25
+ end
26
+
27
+ unless margin_bottom.nil?
28
+ widget.set_margin_bottom(margin_bottom)
29
+ end
30
+
31
+ unless margin_left.nil?
32
+ widget.set_margin_left(margin_left)
33
+ end
34
+
35
+ unless margin_right.nil?
36
+ widget.set_margin_right(margin_right)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,46 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Image < Core
5
+ attr_reader :path
6
+
7
+ def initialize(path, options = {})
8
+ @path = File.join(path.strip)
9
+
10
+ tooltip_text = options[:tooltip_text].nil? ? "" : options[:tooltip_text]
11
+ width = options[:width].nil? ? 80 : options[:width]
12
+ height = options[:height].nil? ? 80 : options[:height]
13
+
14
+ pixbuf = nil
15
+
16
+ args_to_send = {}
17
+
18
+ if path.include?(".gif")
19
+ pixbuf = GdkPixbuf::PixbufAnimation.new(@path)
20
+ args_to_send[:animation] = pixbuf
21
+ else
22
+ pixbuf = GdkPixbuf::Pixbuf.new(file: @path, width: width, height: height)
23
+ args_to_send[:pixbuf] = pixbuf
24
+ end
25
+
26
+ @image = Gtk::Image.new(args_to_send)
27
+ @image.tooltip_text = tooltip_text
28
+
29
+ @event_box = Gtk::EventBox.new.add_child(@image)
30
+
31
+ super(@image, options)
32
+ end
33
+
34
+ def click
35
+ @event_box.signal_connect("button_press_event") do
36
+ yield
37
+ end
38
+ end
39
+
40
+ def render
41
+ $vertical_box.pack_start(@event_box)
42
+ return self
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Label < Core
5
+ def initialize(text, options)
6
+ if text.nil? || text == "" || text == " "
7
+ text = "Label"
8
+ end
9
+
10
+ @label = Gtk::Label.new(text)
11
+ super(@label, options)
12
+ end
13
+
14
+ def render
15
+ $vertical_box.pack_start(@label)
16
+ return self
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class ListBox < Core
5
+ attr_reader :items
6
+
7
+ def initialize(options={})
8
+ @items = options[:items]
9
+
10
+ list_store = Gtk::ListStore.new(String)
11
+
12
+ @items.each do |i|
13
+ iter = list_store.append
14
+ iter[0] = i
15
+ end
16
+
17
+ renderer = Gtk::CellRendererText.new
18
+
19
+ @gtk_combobox = Gtk::ComboBox.new(model: list_store)
20
+ @gtk_combobox.pack_start(renderer, true)
21
+ @gtk_combobox.set_attributes(renderer, "text" => 0)
22
+ @gtk_combobox.set_cell_data_func(renderer) do |_layout, _cell_renderer, _model, iter|
23
+ set_selected_value(iter[0])
24
+ end
25
+
26
+ @gtk_combobox.set_active(0)
27
+
28
+ super(@gtk_combobox)
29
+ end
30
+
31
+ def set_selected_value(value)
32
+ @selected_value = value
33
+ end
34
+ alias :set_value :set_selected_value
35
+
36
+ def get_selected_value
37
+ @selected_value
38
+ end
39
+ alias :selected_value :get_selected_value
40
+
41
+ def render
42
+ $vertical_box.pack_start(@gtk_combobox)
43
+ return self
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Loader < Core
5
+ def initialize(options={})
6
+ @spinner = Gtk::Spinner.new
7
+
8
+ @processing = false
9
+
10
+ super(@spinner, options)
11
+
12
+ self.hide
13
+ end
14
+
15
+ def start(&block)
16
+ return if @processing
17
+
18
+ return if @worker_thread && @worker_thread.alive?
19
+
20
+ self.show
21
+
22
+ @spinner.start
23
+
24
+ @worker_thread = Thread.new { yield; self.remove; @processing = true }
25
+ end
26
+
27
+ def render
28
+ $vertical_box.pack_end(@spinner)
29
+ return self
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,61 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Notify < Core
5
+ def initialize(msg, options={})
6
+ @activate_timer = options[:timer] || true
7
+
8
+ @notify_bar = Gtk::InfoBar.new
9
+
10
+ @notify_bar.set_show_close_button(true)
11
+
12
+ msg_label = Gtk::Label.new(msg)
13
+
14
+ @notify_bar.message_type = set_msg_type(options)
15
+
16
+ @notify_bar.content_area.pack_start(msg_label)
17
+
18
+ setup_close_notification
19
+
20
+ super(@notify_bar, options)
21
+ end
22
+
23
+ def render
24
+ $vertical_box.pack_start(@notify_bar) unless $vertical_box.children.include?(@notify_bar)
25
+ $vertical_box.reorder_child(@notify_bar, 0)
26
+
27
+ @notify_bar.show_all
28
+
29
+ trigger_notify_timer if @activate_timer
30
+
31
+ return self
32
+ end
33
+
34
+ private
35
+ def set_msg_type(options={})
36
+ case options[:type]
37
+ when :question then Gtk::MessageType::QUESTION
38
+ when :error then Gtk::MessageType::ERROR
39
+ when :info then Gtk::MessageType::INFO
40
+ else Gtk::MessageType::INFO
41
+ end
42
+ end
43
+
44
+ def trigger_notify_timer
45
+ Thread.new {
46
+ sleep 8
47
+
48
+ if @notify_bar.visible?
49
+ @notify_bar.hide
50
+ end
51
+ }
52
+ end
53
+
54
+ def setup_close_notification
55
+ @notify_bar.signal_connect("response") do |info_bar, response_id|
56
+ @notify_bar.hide if response_id == Gtk::ResponseType::CLOSE
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,32 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Slider < Core
5
+ def initialize(options={})
6
+ start_value = options[:start_value].nil? ? 1.0 : options[:start_value]
7
+ stop_value = options[:stop_value].nil? ? 10.0 : options[:stop_value]
8
+ step_value = options[:step_value].nil? ? 1.0 : options[:step_value]
9
+ init_value = options[:initial_value].nil? ? 1.0 : options[:initial_value]
10
+
11
+ @scale = Gtk::Scale.new(:horizontal, start_value, stop_value, step_value)
12
+ @scale.digits = 0
13
+ @scale.draw_value = true
14
+ @scale.value = init_value
15
+
16
+ super(@scale)
17
+ end
18
+
19
+ def move
20
+ @scale.signal_connect "value_changed" do |scale_widget|
21
+ yield(scale_widget)
22
+ end
23
+ end
24
+ alias :slide :move
25
+
26
+ def render
27
+ $vertical_box.pack_start(@scale)
28
+ return self
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class Switch < Core
5
+ def initialize(options = {})
6
+ @switch = Gtk::Switch.new
7
+ @switch.set_active(false)
8
+
9
+ # We need a Grid within our global $vertical_box layout
10
+ # in order to make the Widget look good (meaning not overly streched).
11
+ @grid = Gtk::Grid.new
12
+ @grid.set_column_spacing(1)
13
+ @grid.set_row_spacing(1)
14
+ @grid.attach(@switch, 0, 0, 1, 1)
15
+
16
+ super(@switch, options)
17
+ end
18
+
19
+ def activate
20
+ @switch.signal_connect('notify::active') do |switch_widget|
21
+ switch_widget.active? ? switch_widget.set_active(true) : switch_widget.set_active(false)
22
+ yield
23
+ end
24
+ end
25
+ alias :on :activate
26
+
27
+ def render
28
+ $vertical_box.pack_start(@grid)
29
+ return self
30
+ end
31
+
32
+ def status
33
+ @switch.active? ? :on : :off
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,179 @@
1
+ require_relative "./core"
2
+
3
+ module MittensUi
4
+ class TableView < Core
5
+
6
+ def initialize(options={})
7
+ headers = options[:headers] || []
8
+ data = options[:data] || []
9
+
10
+ unless is_data_valid?(headers, data)
11
+ exit 1
12
+ end
13
+
14
+ init_column_headers(headers)
15
+
16
+ init_list_store
17
+
18
+ @scrolled_window = Gtk::ScrolledWindow.new
19
+ calc_min_height = (data.size ** 2 ) * 10
20
+ @scrolled_window.min_content_height = calc_min_height
21
+
22
+ @tree_view = Gtk::TreeView.new(@list_store)
23
+ @tree_view.selection.set_mode(:single)
24
+
25
+ @columns.each { |col| @tree_view.append_column(col) }
26
+
27
+ init_sortable_columns
28
+
29
+ init_data_rows(data)
30
+
31
+ @scrolled_window.add(@tree_view)
32
+
33
+ super(@tree_view, options)
34
+ end
35
+
36
+ def add(data, direction=:append)
37
+ return if data.size.zero?
38
+
39
+ case direction
40
+ when :append
41
+ iter = @list_store.append
42
+ when :prepend
43
+ iter = @list_store.prepend
44
+ else
45
+ iter = @list_store.append
46
+ end
47
+
48
+ data.each_with_index do |item, idx|
49
+ iter[idx] = item
50
+ end
51
+ end
52
+
53
+ def clear
54
+ @list_store.clear
55
+ end
56
+
57
+ def render
58
+ $vertical_box.pack_start(@scrolled_window)
59
+ return self
60
+ end
61
+
62
+ def row_count
63
+ count = 0
64
+ @list_store.each { |item| count += 1 }
65
+
66
+ return count
67
+ end
68
+
69
+ def remove_selected
70
+ iter = @tree_view.selection.selected
71
+
72
+ values = []
73
+
74
+ if iter.nil?
75
+ return values
76
+ end
77
+
78
+ @list_store.n_columns.times do |x|
79
+ values << @list_store.get_value(iter, x)
80
+ end
81
+
82
+ @list_store.remove(iter)
83
+
84
+ return values
85
+ end
86
+
87
+ def row_clicked
88
+ @tree_view.signal_connect("row-activated") do |tv, path, column|
89
+ row = tv.selection.selected
90
+
91
+ values = []
92
+
93
+ @list_store.n_columns.times { |x| values << row.get_value(x) if row }
94
+
95
+ yield(values)
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def is_data_valid?(headers, data)
102
+ column_size = headers.size
103
+
104
+ data_is_array = data.class == Array
105
+ headers_is_array = headers.class == Array
106
+
107
+ valid = true
108
+
109
+ unless data_is_array
110
+ puts "=====[MittensUi: Critical Error]====="
111
+ puts "Incoming data must be an Array of Arrays: data = [ [el..], [el...] ]"
112
+ valid = false
113
+ return valid
114
+ end
115
+
116
+ unless headers_is_array
117
+ puts "=====[MittensUi: Critical Error]====="
118
+ puts "Incoming data must be an Array of Arrays: data = [ [el..], [el...] ]"
119
+ valid = false
120
+ return valid
121
+ end
122
+
123
+ data.each_with_index do |row, idx|
124
+ # Row data must be an Array.
125
+ # The size of the Row Array must match the size of the Header columns.
126
+ valid = (row.class == Array && column_size == row.size) ? true : false
127
+
128
+ unless valid
129
+ puts
130
+ puts "=====[MittensUi: Critical Error]====="
131
+ puts "The length of your data(Row) must match the length of the headers."
132
+ puts "Failed at Row: #{idx}"
133
+ puts "Row Length: #{row.size} elements"
134
+ puts "Header Length #{column_size} elements"
135
+ puts
136
+ return valid
137
+ end
138
+ end
139
+ end
140
+
141
+ def init_sortable_columns
142
+ @columns.each_with_index do |col, idx|
143
+ col.sort_indicator = true
144
+ col.sort_column_id = idx
145
+
146
+ col.signal_connect('clicked') do |w|
147
+ w.sort_order = w.sort_order == :ascending ? :descending : :ascending
148
+ end
149
+ end
150
+ end
151
+
152
+ def init_list_store
153
+ types = []
154
+ @columns.size.times { types << String }
155
+ @list_store = Gtk::ListStore.new(*types)
156
+ end
157
+
158
+ def init_data_rows(data)
159
+ data.each_with_index do |items_arr|
160
+ iter = @list_store.append
161
+ items_arr.each_with_index do |item, idx|
162
+ iter[idx] = item
163
+ end
164
+ end
165
+ end
166
+
167
+ def init_column_headers(headers_list)
168
+ renderer = Gtk::CellRendererText.new
169
+
170
+ @columns = []
171
+
172
+ headers_list.each_with_index do |h, i|
173
+ next unless h.class == String
174
+ @columns << Gtk::TreeViewColumn.new(h, renderer, text: i)
175
+ end
176
+ end
177
+
178
+ end
179
+ end