gtk_app 0.1.0a
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.
- data/lib/gtk_app.rb +35 -0
- data/lib/gtk_app/controller.rb +21 -0
- data/lib/gtk_app/dialog.rb +39 -0
- data/lib/gtk_app/dialog/ask.rb +11 -0
- data/lib/gtk_app/dialog/error.rb +11 -0
- data/lib/gtk_app/dialog/info.rb +11 -0
- data/lib/gtk_app/dialog/notify.rb +87 -0
- data/lib/gtk_app/dialog/progress.rb +20 -0
- data/lib/gtk_app/dialog/wait.png +0 -0
- data/lib/gtk_app/dialog/wait.rb +10 -0
- data/lib/gtk_app/dialog/warn.rb +11 -0
- data/lib/gtk_app/helpers.rb +12 -0
- data/lib/gtk_app/model.rb +12 -0
- data/lib/gtk_app/observer.rb +13 -0
- data/lib/gtk_app/partial.rb +5 -0
- data/lib/gtk_app/register_support.rb +17 -0
- data/lib/gtk_app/signal_support.rb +65 -0
- data/lib/gtk_app/text_buffer.rb +193 -0
- data/lib/gtk_app/version.rb +3 -0
- data/lib/gtk_app/view.rb +75 -0
- data/lib/gtk_app/view_helpers.rb +59 -0
- metadata +106 -0
data/lib/gtk_app.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'gtk2'
|
2
|
+
|
3
|
+
module GtkApp
|
4
|
+
lib = File.expand_path(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
autoload :Model, "#{lib}/gtk_app/model"
|
7
|
+
autoload :View, "#{lib}/gtk_app/view"
|
8
|
+
autoload :Controller, "#{lib}/gtk_app/controller"
|
9
|
+
|
10
|
+
autoload :Helpers, "#{lib}/gtk_app/helpers"
|
11
|
+
autoload :ViewHelpers, "#{lib}/gtk_app/view_helpers"
|
12
|
+
autoload :SignalSupport, "#{lib}/gtk_app/signal_support"
|
13
|
+
autoload :TextBuffer, "#{lib}/gtk_app/text_buffer"
|
14
|
+
autoload :Observer, "#{lib}/gtk_app/observer"
|
15
|
+
autoload :Version, "#{lib}/gtk_app/version"
|
16
|
+
|
17
|
+
def self.run
|
18
|
+
Gtk::main
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.quit
|
22
|
+
Gtk::main_quit
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.refresh
|
26
|
+
Gtk::main_iteration_do(false) while Gtk::events_pending?
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.add_timeout(time_in_milliseconds, controller, callback)
|
30
|
+
GLib::Timeout.add(time_in_milliseconds){ controller.method(:"#{callback}") }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'gtk_app/dialog'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GtkApp
|
2
|
+
class Controller
|
3
|
+
include GtkApp::Helpers
|
4
|
+
include GtkApp::SignalSupport
|
5
|
+
|
6
|
+
attr_accessor :model, :view
|
7
|
+
|
8
|
+
def initialize(&block)
|
9
|
+
instance_eval(&block) if block_given?
|
10
|
+
|
11
|
+
establish_signal_connections
|
12
|
+
end
|
13
|
+
|
14
|
+
def quit(with_validations=true)
|
15
|
+
# TODO: if with_validations
|
16
|
+
# end
|
17
|
+
GtkApp.quit
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module GtkApp
|
2
|
+
module Dialog
|
3
|
+
|
4
|
+
autoload(:Error, 'gtk_app/dialog/error')
|
5
|
+
autoload(:Info, 'gtk_app/dialog/info')
|
6
|
+
# autoload(:Wait, 'gtk_app/dialog/wait')
|
7
|
+
autoload(:Warn, 'gtk_app/dialog/warn')
|
8
|
+
autoload(:Ask, 'gtk_app/dialog/ask')
|
9
|
+
autoload(:Notify, 'gtk_app/dialog/notify')
|
10
|
+
# autoload(:Progress, 'gtk_app/dialog/progress')
|
11
|
+
|
12
|
+
module Support
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def show(parent, text, secondary_text=nil)
|
17
|
+
dialog = new(parent)
|
18
|
+
dialog.text = text
|
19
|
+
dialog.secondary_text = secondary_text if secondary_text
|
20
|
+
|
21
|
+
result = Gtk::Dialog::RESPONSE_NONE
|
22
|
+
dialog.run do |response|
|
23
|
+
result = response
|
24
|
+
end
|
25
|
+
dialog.destroy
|
26
|
+
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.included(base)
|
33
|
+
base.extend(ClassMethods)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module GtkApp::Dialog
|
2
|
+
class Ask < Gtk::MessageDialog
|
3
|
+
include GtkApp::Dialog::Support
|
4
|
+
|
5
|
+
def initialize(parent)
|
6
|
+
super(parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
|
7
|
+
Gtk::MessageDialog::QUESTION, Gtk::MessageDialog::BUTTONS_YES_NO)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module GtkApp::Dialog
|
2
|
+
class Error < Gtk::MessageDialog
|
3
|
+
include GtkApp::Dialog::Support
|
4
|
+
|
5
|
+
def initialize(parent)
|
6
|
+
super(parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
|
7
|
+
Gtk::MessageDialog::ERROR, Gtk::MessageDialog::BUTTONS_CLOSE)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module GtkApp::Dialog
|
2
|
+
class Info < Gtk::MessageDialog
|
3
|
+
include GtkApp::Dialog::Support
|
4
|
+
|
5
|
+
def initialize(parent)
|
6
|
+
super(parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
|
7
|
+
Gtk::MessageDialog::INFO, Gtk::MessageDialog::BUTTONS_OK)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module GtkApp
|
2
|
+
module Dialog
|
3
|
+
class Notify < Gtk::Window
|
4
|
+
DEFAULT_OPTIONS = {
|
5
|
+
:decorated => false,
|
6
|
+
:resizable => false,
|
7
|
+
:keep_above => true,
|
8
|
+
:opacity => 0.8,
|
9
|
+
:bg_color => 'lightgray',
|
10
|
+
:padding => 6,
|
11
|
+
:pixbuf => File.dirname(__FILE__) + '/notify.xpm',
|
12
|
+
:pixmap => File.dirname(__FILE__) + '/notify.xpm',
|
13
|
+
:label_fg => "#ffffff"
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :display
|
17
|
+
|
18
|
+
def initialize(title, text, options={})
|
19
|
+
@screen_info = {}
|
20
|
+
conf = DEFAULT_OPTIONS.merge(options)
|
21
|
+
|
22
|
+
screen = Gdk::Screen.default
|
23
|
+
# @screen_info[:monitor] = screen.monitor
|
24
|
+
@screen_info[:geometry] = screen.monitor_geometry(screen.number)
|
25
|
+
# @screen_info[:x] =
|
26
|
+
# @screen_info[:y] =
|
27
|
+
|
28
|
+
super(Gtk::Window::POPUP)
|
29
|
+
|
30
|
+
self.title = title
|
31
|
+
self.decorated = conf[:decorated]
|
32
|
+
self.resizable = conf[:resizable]
|
33
|
+
self.keep_above = conf[:keep_above]
|
34
|
+
self.stick
|
35
|
+
|
36
|
+
ebox = Gtk::EventBox.new
|
37
|
+
ebox.visible_window = false
|
38
|
+
self.add(ebox)
|
39
|
+
vbox = Gtk::VBox.new(false, conf[:padding])
|
40
|
+
vbox.border_width = 12
|
41
|
+
ebox.add(vbox)
|
42
|
+
hbox = Gtk::HBox.new(false, conf[:padding])
|
43
|
+
vbox.pack_start(hbox, false, true, 0)
|
44
|
+
|
45
|
+
image = Gtk::Image.new(Gdk::Pixbuf.new(conf[:pixbuf]))
|
46
|
+
hbox.pack_start(image, false, false, 0)
|
47
|
+
|
48
|
+
label = Gtk::Label.new(title)
|
49
|
+
label.modify_fg(Gtk::STATE_NORMAL, Gdk::Color.parse(conf[:label_fg]))
|
50
|
+
hbox.pack_start(label, false, false, 0)
|
51
|
+
|
52
|
+
label = Gtk::Label.new(text)
|
53
|
+
label.justify = Gtk::JUSTIFY_LEFT
|
54
|
+
label.modify_fg(Gtk::STATE_NORMAL, Gdk::Color.parse(conf[:label_fg]))
|
55
|
+
label.wrap = true
|
56
|
+
# label.wrap_mode = true
|
57
|
+
vbox.pack_start(label, true, false, 0)
|
58
|
+
|
59
|
+
ebox.signal_connect('button-press-event') do |widget|
|
60
|
+
puts "button-press-event"
|
61
|
+
end
|
62
|
+
|
63
|
+
ebox.signal_connect('enter-notify-event') do |widget|
|
64
|
+
puts "enter-notify-enter"
|
65
|
+
end
|
66
|
+
|
67
|
+
ebox.signal_connect('leave-notify-event') do |widget|
|
68
|
+
puts "leave-notify-event"
|
69
|
+
end
|
70
|
+
|
71
|
+
self.app_paintable = true
|
72
|
+
self.opacity = 0
|
73
|
+
self.show_all
|
74
|
+
#
|
75
|
+
# pixmap = Gdk::Pixmap.create_from_xpm(self, nil, conf[:pixmap])
|
76
|
+
# puts pixmap.size
|
77
|
+
# # Gdk::Drawable.new()
|
78
|
+
# self.shape_combine_mask(pixmap, 0, 0)
|
79
|
+
end
|
80
|
+
|
81
|
+
def display
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module GtkApp::Dialog
|
2
|
+
class Progress < Gtk::Dialog
|
3
|
+
|
4
|
+
def initialize(parent)
|
5
|
+
|
6
|
+
super("test", parent,
|
7
|
+
Gtk::Dialog::DESTROY_WITH_PARENT,
|
8
|
+
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_NONE])
|
9
|
+
pbar = Gtk::ProgressBar.new
|
10
|
+
hbox = Gtk::HBox.new(false, 6)
|
11
|
+
hbox.pack_start(pbar, true, false, 6)
|
12
|
+
self.vbox.add(hbox)
|
13
|
+
self.show_all
|
14
|
+
end
|
15
|
+
|
16
|
+
# def +(amt)
|
17
|
+
# end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module GtkApp::Dialog
|
2
|
+
class Warn < Gtk::MessageDialog
|
3
|
+
include GtkApp::Dialog::Support
|
4
|
+
|
5
|
+
def initialize(parent)
|
6
|
+
super(parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
|
7
|
+
Gtk::MessageDialog::WARNING, Gtk::MessageDialog::BUTTONS_OK_CANCEL)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module GtkApp
|
2
|
+
module SignalSupport
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
attr_reader :signal_connections
|
11
|
+
|
12
|
+
def on(widget_name, signal_name, receiver_method=nil, &block)
|
13
|
+
|
14
|
+
sc = SignalConnection.new do
|
15
|
+
@widget_name = widget_name
|
16
|
+
@signal_name = signal_name
|
17
|
+
@receiver_method = receiver_method
|
18
|
+
@receiver_block = block if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
@signal_connections ||= []
|
22
|
+
@signal_connections << sc
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module InstanceMethods
|
28
|
+
|
29
|
+
def establish_signal_connections
|
30
|
+
return unless self.class.signal_connections
|
31
|
+
|
32
|
+
self.class.signal_connections.each do |signal_connection|
|
33
|
+
signal_connection.with do |conn|
|
34
|
+
widget = @view.send conn.widget_name
|
35
|
+
if conn.receiver_block
|
36
|
+
widget.signal_connect conn.signal_name do |*args|
|
37
|
+
self.instance_exec(*args, &conn.receiver_block)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
widget.signal_connect conn.signal_name do |*args|
|
41
|
+
self.send conn.receiver_method_name, *args
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class SignalConnection
|
51
|
+
attr_accessor :widget_name, :signal_name
|
52
|
+
attr_accessor :receiver_method, :receiver_block
|
53
|
+
|
54
|
+
def initialize(&block)
|
55
|
+
instance_eval(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def with
|
59
|
+
yield(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'raspell'
|
2
|
+
|
3
|
+
module GtkApp
|
4
|
+
class TextBuffer < Gtk::TextBuffer
|
5
|
+
attr_reader :spell_check
|
6
|
+
attr_reader :undo_stack, :redo_stack
|
7
|
+
|
8
|
+
DEFAULT_LANG = "en_US"
|
9
|
+
DEFAULT_TAGS = %w[bold italic strikethrough underline error spell_error]
|
10
|
+
|
11
|
+
def initialize(tag_table=nil, options={})
|
12
|
+
super(tag_table)
|
13
|
+
@undo_stack, @redo_stack = [], []
|
14
|
+
@spell_check = Aspell.new(options[:lang] || DEFAULT_LANG)
|
15
|
+
setup_default_tags
|
16
|
+
setup_signals
|
17
|
+
end
|
18
|
+
|
19
|
+
# Pop the last action off the undo stack and rewind changes. If an action was
|
20
|
+
# performed, the cursor is placed at the actions starting
|
21
|
+
# Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter].
|
22
|
+
def undo
|
23
|
+
if @undo_stack.empty?
|
24
|
+
Gdk.beep
|
25
|
+
else
|
26
|
+
action = @undo_stack.pop
|
27
|
+
s_iter = get_iter_at_offset(action[1])
|
28
|
+
case action[0]
|
29
|
+
when :insert then delete(s_iter, get_iter_at_offset(action[2]))
|
30
|
+
when :delete then insert(s_iter, action[3])
|
31
|
+
end
|
32
|
+
@redo_stack.push(action)
|
33
|
+
place_cursor(s_iter)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Pop the last action off the redo stack and apply the changes. If and action
|
38
|
+
# was performed, the cursor is placed at the actions starting
|
39
|
+
# Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter].
|
40
|
+
def redo
|
41
|
+
if @redo_stack.empty?
|
42
|
+
Gdk.beep
|
43
|
+
else
|
44
|
+
action = @redo_stack.pop
|
45
|
+
s_iter = get_iter_at_offset(action[1])
|
46
|
+
e_iter = get_iter_at_offset(action[2])
|
47
|
+
case action[0]
|
48
|
+
when :insert then insert(s_iter, action[3])
|
49
|
+
when :delete then delete(s_iter, e_iter)
|
50
|
+
end
|
51
|
+
@undo_stack.push(action)
|
52
|
+
place_cursor(s_iter)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def word_at_cursor
|
57
|
+
get_text(*word_bounds).strip
|
58
|
+
end
|
59
|
+
|
60
|
+
def word_bounds
|
61
|
+
iter = get_iter_at_offset(cursor_position)
|
62
|
+
s_iter, e_iter = iter.clone, iter.clone
|
63
|
+
s_iter.backward_word_start unless s_iter.starts_word?
|
64
|
+
e_iter.forward_word_end unless e_iter.ends_word?
|
65
|
+
|
66
|
+
[s_iter, e_iter]
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_spelling(word=nil, s_iter=nil, e_iter=nil)
|
70
|
+
if word.nil?
|
71
|
+
text.gsub(/[\w\']+/) do |w| check_spelling(w); end
|
72
|
+
elsif !@spell_check.check(word)
|
73
|
+
s, e = start_iter.forward_search(word, Gtk::TextIter::SEARCH_TEXT_ONLY, nil)
|
74
|
+
format(:spell_error, s, e)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Does the
|
79
|
+
# Gtk::TextTagTable[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTagTable]
|
80
|
+
# contain any 'spell_error' tags?
|
81
|
+
def spelling_errors?
|
82
|
+
!tag_table.lookup('spell_error').nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Locate text in selection or the entire buffer. If found, a
|
86
|
+
# Gtk::TextMark[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextMark]
|
87
|
+
# is returned. Else, nil.
|
88
|
+
def find(string)
|
89
|
+
s_iter, e_iter, text_selected = selection_bounds
|
90
|
+
s_iter = start_iter unless text_selected
|
91
|
+
s_iter, e_iter = s_iter.forward_search(string, Gtk::TextIter::SEARCH_TEXT_ONLY, e_iter)
|
92
|
+
s_iter ? create_mark(nil, s_iter, false) : nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def replace(string, s_iter, e_iter)
|
96
|
+
begin_user_action
|
97
|
+
delete(s_iter, e_iter)
|
98
|
+
insert(s_iter, string)
|
99
|
+
end_user_action
|
100
|
+
place_cursor(s_iter)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Format text in the current selection range with a
|
104
|
+
# Gtk::TextTag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTag]
|
105
|
+
# identified by the given name.
|
106
|
+
def format_selection(tag_name)
|
107
|
+
s_iter, e_iter, text_selected = selection_bounds
|
108
|
+
format(tag_name, s_iter, e_iter) if text_selected
|
109
|
+
end
|
110
|
+
|
111
|
+
# This is a small wrapper around the
|
112
|
+
# Gtk::TextBuffer#apply_tag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextBuffer#apply_tag]
|
113
|
+
# method. It allows the Gtk::TextTag name to be passed as a symbol.
|
114
|
+
def format(tag_name, s_iter, e_iter)
|
115
|
+
apply_tag(tag_name.to_s, s_iter, e_iter)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Remove all occurrences of a
|
119
|
+
# Gtk::TextTag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTag]
|
120
|
+
# in the given selection range.
|
121
|
+
def clear_selection(*tag_names)
|
122
|
+
s_iter, e_iter, text_selected = selection_bounds
|
123
|
+
if text_selected
|
124
|
+
if tag_names.empty?
|
125
|
+
clear_all(s_iter, e_iter)
|
126
|
+
else
|
127
|
+
tag_names.each { |tag_name| clear(tag_name, s_iter, e_iter) }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Remove all tags of a given name from from one
|
133
|
+
# Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
|
134
|
+
# to another.
|
135
|
+
def clear(tag_name, s_iter, e_iter)
|
136
|
+
remove_tag(tag_name.to_s, s_iter, e_iter)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Remove all Gtk::TextTag's from one
|
140
|
+
# Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
|
141
|
+
# to another.
|
142
|
+
def clear_all(s_iter, e_iter)
|
143
|
+
remove_all_tags(s_iter, e_iter)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
# Establish default tag names for everyday text formatting.
|
149
|
+
def setup_default_tags
|
150
|
+
DEFAULT_TAGS.each do |name|
|
151
|
+
attibs = case name
|
152
|
+
when 'bold' then { weight: Pango::WEIGHT_BOLD }
|
153
|
+
when 'italic' then { style: Pango::STYLE_ITALIC }
|
154
|
+
when 'strikethrough' then { strikethrough: true }
|
155
|
+
when 'underline' then { underline: Pango::UNDERLINE_SINGLE }
|
156
|
+
when 'error' then { underline: Pango::UNDERLINE_ERROR }
|
157
|
+
when 'spell_error' then { underline: Pango::UNDERLINE_ERROR }
|
158
|
+
end
|
159
|
+
create_tag(name, attibs)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Establish base signal handlers. Here we track user actions and...
|
164
|
+
def setup_signals
|
165
|
+
signal_connect('begin-user-action') { |me| @user_action = true }
|
166
|
+
signal_connect('end-user-action') { |me| @user_action = false }
|
167
|
+
|
168
|
+
signal_connect('insert-text') do |me, iter, text, len|
|
169
|
+
if user_action?
|
170
|
+
@undo_stack << [:insert, iter.offset,
|
171
|
+
(iter.offset + text.scan(/./).size), text]
|
172
|
+
@redo_stack.clear
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
signal_connect('delete-range') do |me, s_iter, e_iter|
|
177
|
+
if user_action?
|
178
|
+
text = get_text(s_iter, e_iter)
|
179
|
+
@undo_stack << [:delete, s_iter.offset, e_iter.offset, text]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# TODO: Add suggestion popups for spelling erros.
|
184
|
+
# tag_table.lookup('spell_error').signal_connect('event') do |tag|
|
185
|
+
# end
|
186
|
+
end
|
187
|
+
|
188
|
+
def user_action?
|
189
|
+
@user_action
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
data/lib/gtk_app/view.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module GtkApp
|
2
|
+
class View < Gtk::Builder
|
3
|
+
include GtkApp::Helpers
|
4
|
+
include GtkApp::ViewHelpers
|
5
|
+
|
6
|
+
def initialize(controller, builder_file, *args)
|
7
|
+
super()
|
8
|
+
self.add_from_file(builder_file)
|
9
|
+
self.connect_signals { |handler| controller.method(handler) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(id, *args, &block)
|
13
|
+
method_name = id.to_s
|
14
|
+
|
15
|
+
widget_name = (method_name =~ /([!=]|<{2})$/ ? method_name.chop : method_name)
|
16
|
+
widget = self["#{widget_name}"]
|
17
|
+
super unless widget
|
18
|
+
|
19
|
+
bang_proc, equal_proc, append_proc = case widget
|
20
|
+
when Gtk::TextView then
|
21
|
+
[ lambda { widget.buffer.text },
|
22
|
+
lambda { |*argv| widget.buffer.text = argv[0].to_s },
|
23
|
+
lambda { |text| widget.buffer.text << text.to_s } ]
|
24
|
+
when Gtk::ComboBox then
|
25
|
+
[ lambda { widget.active_text },
|
26
|
+
lambda do |*argv|
|
27
|
+
if argv[0].is_a?(Fixnum)
|
28
|
+
widget.active = argv[0]
|
29
|
+
elsif widget.model
|
30
|
+
result = nil
|
31
|
+
widget.model.each do |m, p, i|
|
32
|
+
result = iter if iter[0] =~ /\A#{argv[0]}/i
|
33
|
+
end
|
34
|
+
widget.active_iter = result if result
|
35
|
+
end
|
36
|
+
end,
|
37
|
+
lambda { |text| widget.append_text(text.to_s) } ]
|
38
|
+
when Gtk::ToggleButton, Gtk::CheckButton then
|
39
|
+
[ lambda { widget.active? },
|
40
|
+
lambda { |*argv| widget.active = argv[0] },
|
41
|
+
nil ]
|
42
|
+
when Gtk::TreeView then
|
43
|
+
[ lambda { widget.selection.selected },
|
44
|
+
nil,
|
45
|
+
lambda do |row|
|
46
|
+
iter = widget.model.append
|
47
|
+
row.each_with_index { |v,i| iter[i] = v }
|
48
|
+
iter
|
49
|
+
end ]
|
50
|
+
else
|
51
|
+
if widget.respond_to?(:text)
|
52
|
+
[ lambda { widget.text },
|
53
|
+
lambda { |*argv| widget.text = argv[0].to_s },
|
54
|
+
lambda { |text| widget.text = ("#{widget.text}" << text) } ]
|
55
|
+
else [nil, nil, nil]; end
|
56
|
+
end
|
57
|
+
|
58
|
+
class_eval do
|
59
|
+
define_method(:"#{widget_name}", lambda { widget })
|
60
|
+
define_method(:"#{widget_name}!", bang_proc) if bang_proc
|
61
|
+
define_method(:"#{widget_name}=", equal_proc) if equal_proc
|
62
|
+
end
|
63
|
+
|
64
|
+
widget.class_eval do
|
65
|
+
define_method("<<", append_proc)
|
66
|
+
end if append_proc
|
67
|
+
|
68
|
+
send(:"#{method_name}", *args, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: def restore_geometry
|
72
|
+
# end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module GtkApp
|
2
|
+
module ViewHelpers
|
3
|
+
|
4
|
+
def build_listview(widget_name, columns, options={}, &block) # :yields: index, header, column, renderer
|
5
|
+
list = self.send(:"#{widget_name}")
|
6
|
+
list.model = Gtk::ListStore.new(*columns.values)
|
7
|
+
columns.each_with_index do |keyval, index|
|
8
|
+
header, data_type = keyval
|
9
|
+
renderer, attrs = case data_type
|
10
|
+
when String, Integer
|
11
|
+
[Gtk::CellRendererText.new, :text => index]
|
12
|
+
when TrueClass
|
13
|
+
toggle = Gtk::CellRendererToggle.new
|
14
|
+
toggle.signal_connect('toggled') do |widget, path|
|
15
|
+
iter = list.model.get_iter(path)
|
16
|
+
iter[index] = !iter[index]
|
17
|
+
end
|
18
|
+
[toggle, :active => index]
|
19
|
+
when Gtk::ListStore
|
20
|
+
_renderer = Gtk::CellRendererCombo.new
|
21
|
+
model = Gtk::ListStore.new(String)
|
22
|
+
_renderer.signal_connect("edited") do |cell, path, text|
|
23
|
+
model.get_iter(path)[index] = text
|
24
|
+
end
|
25
|
+
[_renderer, :text_column => 0, :model => model, :text => index,
|
26
|
+
:editable => index]
|
27
|
+
else
|
28
|
+
raise("GtkApp::View##{__method__} does not know how to handle " +
|
29
|
+
"'#{data_type}' data types.")
|
30
|
+
end
|
31
|
+
|
32
|
+
column = Gtk::TreeViewColumn.new("#{header}".titleize, renderer, attrs)
|
33
|
+
column.visible = false if index == 0
|
34
|
+
yield(index, header, column, renderer) if block_given?
|
35
|
+
|
36
|
+
if options.key?(:formatter) && options[:formatter].is_a?(Proc)
|
37
|
+
column.set_cell_data_func(renderer, &options[:formatter])
|
38
|
+
end
|
39
|
+
|
40
|
+
list.append_column(column)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set *widgets sensitive property to true
|
45
|
+
#== Example
|
46
|
+
# @view.sensitize(:txtFoo, :cmbBar)
|
47
|
+
def sensitize(*widgets)
|
48
|
+
widgets.each { |w| self["#{w}"].sensitive = true }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set *widgets sensitive property to false
|
52
|
+
#== Example
|
53
|
+
# @view.desensitize(:txtFoo, :cmbBar)
|
54
|
+
def desensitize(*widgets)
|
55
|
+
widgets.each { |w| self["#{w}"].sensitive = false }
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gtk_app
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: 5
|
5
|
+
version: 0.1.0a
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Daniel Johnston
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-08-24 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: pkg-config
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activemodel
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 3.0.7
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: dmarkow-raspell
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id003
|
48
|
+
description:
|
49
|
+
email: dan@dj-agiledev.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files: []
|
55
|
+
|
56
|
+
files:
|
57
|
+
- ./lib/gtk_app/controller.rb
|
58
|
+
- ./lib/gtk_app/dialog/ask.rb
|
59
|
+
- ./lib/gtk_app/dialog/error.rb
|
60
|
+
- ./lib/gtk_app/dialog/info.rb
|
61
|
+
- ./lib/gtk_app/dialog/notify.rb
|
62
|
+
- ./lib/gtk_app/dialog/progress.rb
|
63
|
+
- ./lib/gtk_app/dialog/wait.png
|
64
|
+
- ./lib/gtk_app/dialog/wait.rb
|
65
|
+
- ./lib/gtk_app/dialog/warn.rb
|
66
|
+
- ./lib/gtk_app/dialog.rb
|
67
|
+
- ./lib/gtk_app/helpers.rb
|
68
|
+
- ./lib/gtk_app/model.rb
|
69
|
+
- ./lib/gtk_app/observer.rb
|
70
|
+
- ./lib/gtk_app/partial.rb
|
71
|
+
- ./lib/gtk_app/register_support.rb
|
72
|
+
- ./lib/gtk_app/signal_support.rb
|
73
|
+
- ./lib/gtk_app/text_buffer.rb
|
74
|
+
- ./lib/gtk_app/version.rb
|
75
|
+
- ./lib/gtk_app/view.rb
|
76
|
+
- ./lib/gtk_app/view_helpers.rb
|
77
|
+
- ./lib/gtk_app.rb
|
78
|
+
homepage: https://github.com/drfeelngood/rb-gtk_app
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 1.9.2
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.3.1
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.8.9
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: A ruby-gtk framework
|
105
|
+
test_files: []
|
106
|
+
|