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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +5 -2
- data/LICENSE +21 -0
- data/README.md +78 -21
- data/examples/app.rb +101 -8
- data/examples/assets/gnome_logo.png +0 -0
- data/examples/assets/mittens_ui_preview.gif +0 -0
- data/examples/contacts.rb +80 -0
- data/examples/file_menu_example.rb +52 -0
- data/examples/notify_example.rb +19 -0
- data/lib/mittens_ui.rb +40 -67
- data/lib/mittens_ui/alert.rb +32 -0
- data/lib/mittens_ui/assets/icon.png +0 -0
- data/lib/mittens_ui/assets/mittens_ui_preview.gif +0 -0
- data/lib/mittens_ui/button.rb +40 -0
- data/lib/mittens_ui/checkbox.rb +24 -0
- data/lib/mittens_ui/core.rb +34 -0
- data/lib/mittens_ui/file_menu.rb +83 -0
- data/lib/mittens_ui/file_picker.rb +29 -0
- data/lib/mittens_ui/grid.rb +29 -0
- data/lib/mittens_ui/hbox.rb +56 -0
- data/lib/mittens_ui/header_bar.rb +46 -0
- data/lib/mittens_ui/helpers.rb +41 -0
- data/lib/mittens_ui/image.rb +46 -0
- data/lib/mittens_ui/label.rb +19 -0
- data/lib/mittens_ui/listbox.rb +47 -0
- data/lib/mittens_ui/loader.rb +33 -0
- data/lib/mittens_ui/notify.rb +61 -0
- data/lib/mittens_ui/slider.rb +32 -0
- data/lib/mittens_ui/switch.rb +36 -0
- data/lib/mittens_ui/table_view.rb +179 -0
- data/lib/mittens_ui/textbox.rb +53 -0
- data/lib/mittens_ui/version.rb +1 -1
- data/lib/mittens_ui/web_link.rb +22 -0
- data/mittens_ui.gemspec +3 -3
- data/notes/dev_setup.txt +14 -0
- metadata +37 -16
- data/examples/brightness_controller.rb +0 -64
- data/lib/mittens_ui/layouts/box.rb +0 -38
- data/lib/mittens_ui/layouts/grid.rb +0 -31
- data/lib/mittens_ui/widgets/alert.rb +0 -33
- data/lib/mittens_ui/widgets/button.rb +0 -34
- data/lib/mittens_ui/widgets/label.rb +0 -41
- data/lib/mittens_ui/widgets/listbox.rb +0 -45
- data/lib/mittens_ui/widgets/slider.rb +0 -32
- data/lib/mittens_ui/widgets/switch.rb +0 -57
- 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
|