gtk_app 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,24 +12,34 @@ module GtkApp
12
12
  autoload :SignalSupport, "#{lib}/gtk_app/signal_support"
13
13
  autoload :TextBuffer, "#{lib}/gtk_app/text_buffer"
14
14
  autoload :Observer, "#{lib}/gtk_app/observer"
15
+ autoload :Drawer, "#{lib}/gtk_app/drawer"
15
16
  autoload :Version, "#{lib}/gtk_app/version"
16
17
 
18
+ # Start the main Gtk loop.
17
19
  def self.run
18
20
  Gtk::main
19
21
  end
20
-
22
+
23
+ # Stop the main Gtk loop.
21
24
  def self.quit
22
25
  Gtk::main_quit
23
26
  end
24
-
27
+
28
+ # Run a single iteration of the main loop while there are pending events
29
+ # without blocking.
25
30
  def self.refresh
26
31
  Gtk::main_iteration_do(false) while Gtk::events_pending?
27
32
  end
28
33
 
34
+ # Establish a controller method to be invoked at regular intervals.
35
+ # @param [Fixnum] time_in_milliseconds Time between calls to the receiver method.
36
+ # @param [Object] controller The class in which the method exists.
37
+ # @param [String] callback Receiver method name.
29
38
  def self.add_timeout(time_in_milliseconds, controller, callback)
30
39
  GLib::Timeout.add(time_in_milliseconds){ controller.method(:"#{callback}") }
31
40
  end
32
41
 
33
42
  end
34
43
 
35
- require 'gtk_app/dialog'
44
+ require 'gtk_app/ext/text_view'
45
+ # require 'gtk_app/dialog'
@@ -0,0 +1,13 @@
1
+ module GtkApp
2
+ module Application
3
+
4
+ def run
5
+ Gtk.main
6
+ end
7
+
8
+ def quit
9
+ Gtk.main_quit
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module GtkApp
2
+ module CallbackSupport
3
+ include ActiveSupport::Callbacks
4
+ end
5
+ end
@@ -3,14 +3,18 @@ class Controller
3
3
  include GtkApp::Helpers
4
4
  include GtkApp::SignalSupport
5
5
 
6
- attr_accessor :model, :view
6
+ # @attribute [rw] model
7
+ attr_accessor :model
8
+ # @attr_accessor [rw] view
9
+ attr_accessor :view
7
10
 
8
11
  def initialize(&block)
9
12
  instance_eval(&block) if block_given?
10
-
13
+
11
14
  establish_signal_connections
12
15
  end
13
16
 
17
+ # @param [Boolean] with_validations
14
18
  def quit(with_validations=true)
15
19
  # TODO: if with_validations
16
20
  # end
@@ -0,0 +1,11 @@
1
+ module Gtk
2
+ class ComboBox
3
+
4
+ # @param [Object, #to_s] text
5
+ def <<(text)
6
+ append_text(text.to_s)
7
+ end
8
+
9
+ end
10
+ end
11
+
@@ -0,0 +1,272 @@
1
+ require 'raspell'
2
+
3
+ module Gtk
4
+ class TextBuffer
5
+
6
+ # @attr_accessor [Aspell] spell_check
7
+ attr_accessor:spell_check
8
+ # @attr_accessor [Array] undo_stack
9
+ attr_accessor:undo_stack
10
+ # @attr_accessor [Array] redo_stack
11
+ attr_accessor:redo_stack
12
+ # @attr_accessor [Gtk::TextMark] insert_start
13
+ attr_accessor:insert_start
14
+ # @attr_accessor [Gtk::TextMark] insert_end
15
+ attr_accessor:insert_end
16
+
17
+ DEFAULT_LANG = "en_US"
18
+ DEFAULT_TAGS = %w[bold italic strikethrough underline error spell_error]
19
+
20
+ def initialize(tag_table=nil, options={})
21
+ super(tag_table)
22
+ @undo_stack, @redo_stack = [], []
23
+ @spell_check = Aspell.new(options[:lang] || DEFAULT_LANG)
24
+ setup_default_tags
25
+ setup_spell_check_marks
26
+ setup_signals
27
+ end
28
+
29
+ # Pop the last action off the undo stack and rewind changes. If an action was
30
+ # performed, the cursor is placed at the actions starting
31
+ # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter].
32
+ def undo
33
+ if @undo_stack.empty?
34
+ Gdk.beep
35
+ else
36
+ action = @undo_stack.pop
37
+ s_iter = get_iter_at_offset(action[1])
38
+ case action[0]
39
+ when :insert then delete(s_iter, get_iter_at_offset(action[2]))
40
+ when :delete then insert(s_iter, action[3])
41
+ end
42
+ @redo_stack.push(action)
43
+ place_cursor(s_iter)
44
+ end
45
+ end
46
+
47
+ # Pop the last action off the redo stack and apply the changes. If and action
48
+ # was performed, the cursor is placed at the actions starting
49
+ # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter].
50
+ def redo
51
+ if @redo_stack.empty?
52
+ Gdk.beep
53
+ else
54
+ action = @redo_stack.pop
55
+ s_iter = get_iter_at_offset(action[1])
56
+ e_iter = get_iter_at_offset(action[2])
57
+ case action[0]
58
+ when :insert then insert(s_iter, action[3])
59
+ when :delete then delete(s_iter, e_iter)
60
+ end
61
+ @undo_stack.push(action)
62
+ place_cursor(s_iter)
63
+ end
64
+ end
65
+
66
+ # Retrieve the word at the current cursor position
67
+ # @return [String] The word.
68
+ def word_at_cursor
69
+ get_text(*word_bounds).strip
70
+ end
71
+
72
+ # Determine the boudaries of the word at the current cursor position.
73
+ # @return [Array] The start and end iter.
74
+ def word_bounds
75
+ iter = get_iter_at_offset(cursor_position)
76
+ s_iter, e_iter = iter.clone, iter.clone
77
+ s_iter.backward_word_start unless s_iter.starts_word?
78
+ e_iter.forward_word_end unless e_iter.ends_word?
79
+
80
+ [s_iter, e_iter]
81
+ end
82
+
83
+ def check_spelling(s_iter=nil, e_iter=nil)
84
+ s_iter = bounds.first if s_iter.nil?
85
+ e_iter = bounds.last if e_iter.nil?
86
+
87
+ e_iter.forward_word_end if e_iter.inside_word?
88
+
89
+ unless s_iter.starts_word?
90
+ if s_iter.inside_word? || s_iter.ends_word?
91
+ s_iter.backward_word_start
92
+ elsif s_iter.forward_word_end
93
+ s_iter.backward_word_start
94
+ end
95
+ end
96
+
97
+ cursor = get_iter_at_offset(cursor_position)
98
+ precursor = cursor.clone
99
+ precursor.backward_char
100
+ tag = tag_table.lookup('spell_error')
101
+ has_error = cursor.has_tag?(tag) || precursor.has_tag?(tag)
102
+
103
+ unformat(:spell_error, s_iter, e_iter)
104
+
105
+ word_start = s_iter.clone
106
+ while (word_start <=> e_iter) < 0 do
107
+ word_end = word_start.clone
108
+ word_end.forward_word_end
109
+
110
+ word = get_text(word_start, word_end)
111
+ if !has_error && word =~ /[A-Za-z]/ && !@spell_check.check(word)
112
+ # puts "[#{word}]"
113
+ format(:spell_error, word_start, word_end)
114
+ end
115
+
116
+ # => meow point to the beginning of the next word.
117
+ word_end.forward_word_end
118
+ word_end.backward_word_start
119
+
120
+ break if word_start == word_end
121
+
122
+ word_start = word_end.clone
123
+ end
124
+ end
125
+
126
+ # Does the
127
+ # Gtk::TextTagTable[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTagTable]
128
+ # contain any 'spell_error' tags?
129
+ def spelling_errors?
130
+ !tag_table.lookup('spell_error').nil?
131
+ end
132
+
133
+ # Locate text in selection or the entire buffer. If found, a
134
+ # Gtk::TextMark[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextMark]
135
+ # is returned. Else, nil.
136
+ # @param [String] string Text to search.
137
+ def find(string)
138
+ s_iter, e_iter, text_selected = selection_bounds
139
+ s_iter = start_iter unless text_selected
140
+ s_iter, e_iter = s_iter.forward_search(string, Gtk::TextIter::SEARCH_TEXT_ONLY, e_iter)
141
+ s_iter ? create_mark(nil, s_iter, false) : nil
142
+ end
143
+
144
+ # @param [String] string Text to replace the selection with.
145
+ # @param [Gtk::TextIter] s_iter
146
+ # @param [Gtk::TextIter] e_iter
147
+ def replace(string, s_iter, e_iter)
148
+ begin_user_action
149
+ delete(s_iter, e_iter)
150
+ insert(s_iter, string)
151
+ end_user_action
152
+ place_cursor(s_iter)
153
+ end
154
+
155
+ # Format text in the current selection range with a
156
+ # Gtk::TextTag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTag]
157
+ # identified by the given name.
158
+ # @param [String] tag_name
159
+ def format_selection(tag_name)
160
+ s_iter, e_iter, text_selected = selection_bounds
161
+ format(tag_name, s_iter, e_iter) if text_selected
162
+ end
163
+
164
+ # This is a small wrapper around the
165
+ # Gtk::TextBuffer#apply_tag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextBuffer#apply_tag]
166
+ # method. It allows the Gtk::TextTag name to be passed as a symbol.
167
+ # @param [] tag_name
168
+ # @param [Gtk::TextIter] s_iter
169
+ # @param [Gtk::TextIter] e_iter
170
+ def format(tag_name, s_iter, e_iter)
171
+ apply_tag(tag_name.to_s, s_iter, e_iter)
172
+ end
173
+
174
+ # Remove all tags of a given name from from one
175
+ # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
176
+ # to another.
177
+ # @param [] tag_name
178
+ # @param [Gtk::TextIter] s_iter
179
+ # @param [Gtk::TextIter] e_iter
180
+ def unformat(tag_name, s_iter, e_iter)
181
+ remove_tag(tag_name.to_s, s_iter, e_iter)
182
+ end
183
+
184
+ # Remove all occurrences of a
185
+ # Gtk::TextTag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTag]
186
+ # in the given selection range.
187
+ def unformat_selection(*tag_names)
188
+ s_iter, e_iter, text_selected = selection_bounds
189
+ if text_selected
190
+ if tag_names.empty?
191
+ clear_all(s_iter, e_iter)
192
+ else
193
+ tag_names.each { |tag_name| unformat(tag_name, s_iter, e_iter) }
194
+ end
195
+ end
196
+ end
197
+
198
+ # Remove all Gtk::TextTag's from one
199
+ # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
200
+ # to another.
201
+ # @param [Gtk::TextIter] s_iter
202
+ # @param [Gtk::TextIter] e_iter
203
+ def unformat_all(s_iter, e_iter)
204
+ remove_all_tags(s_iter, e_iter)
205
+ end
206
+
207
+ private
208
+
209
+ # Establish default tag names for everyday text formatting.
210
+ def setup_default_tags
211
+ DEFAULT_TAGS.each do |name|
212
+ attibs = case name
213
+ when 'bold' then { weight: Pango::WEIGHT_BOLD }
214
+ when 'italic' then { style: Pango::STYLE_ITALIC }
215
+ when 'strikethrough' then { strikethrough: true }
216
+ when 'underline' then { underline: Pango::UNDERLINE_SINGLE }
217
+ when 'error' then { underline: Pango::UNDERLINE_ERROR }
218
+ when 'spell_error' then { underline: Pango::UNDERLINE_ERROR }
219
+ end
220
+ create_tag(name, attibs)
221
+ end
222
+ end
223
+
224
+ def setup_spell_check_marks
225
+ s_iter, e_iter = bounds
226
+ @insert_start = create_mark('insert-start', s_iter, true)
227
+ @insert_end = create_mark('insert-end', s_iter, true)
228
+ end
229
+
230
+ # Establish base signal handlers. Here we track user actions and...
231
+ def setup_signals
232
+ signal_connect('begin-user-action') { |me| @user_action = true }
233
+ signal_connect('end-user-action') { |me| @user_action = false }
234
+
235
+ signal_connect('insert-text') do |me, iter, text, len|
236
+ if user_action?
237
+ me.undo_stack << [ :insert, iter.offset,
238
+ (iter.offset + text.scan(/./).size), text ]
239
+ me.redo_stack.clear
240
+ end
241
+ me.move_mark(me.insert_start, iter)
242
+ end
243
+
244
+ signal_connect_after('insert-text') do |me, iter, text, len|
245
+ s_iter = me.get_iter_at_mark(me.insert_start)
246
+ me.check_spelling(s_iter, iter)
247
+ me.insert_end = iter
248
+ end
249
+
250
+ signal_connect('delete-range') do |me, s_iter, e_iter|
251
+ if user_action?
252
+ me.undo_stack << [ :delete, s_iter.offset, e_iter.offset, text,
253
+ me.get_text(s_iter, e_iter) ]
254
+ end
255
+ end
256
+
257
+ signal_connect_after('delete-range') do |me, s_iter, e_iter|
258
+ me.check_spelling(s_iter, e_iter)
259
+ end
260
+
261
+ # TODO: Add suggestion popups for spelling erros.
262
+ # tag_table.lookup('spell_error').signal_connect('event') do |tag|
263
+ # p tag
264
+ # end
265
+ end
266
+
267
+ def user_action?
268
+ @user_action
269
+ end
270
+
271
+ end
272
+ end
@@ -0,0 +1,55 @@
1
+ module Gtk
2
+ class TextView
3
+
4
+ alias_method :gtk2_initialize, :initialize
5
+ alias_method :gtk2_buffer=, :buffer=
6
+
7
+ def initialize(buffer = nil)
8
+ self.buffer = buffer
9
+ self.gtk2_initialize(nil)
10
+ end
11
+
12
+ def buffer=(buffer)
13
+ buffer.view = self if buffer.is_a?(GtkApp::TextBuffer)
14
+ self.gtk2_buffer = buffer
15
+ end
16
+
17
+ # @api private
18
+ def __button_press_event(event)
19
+ if event.button == 3
20
+ x, y = window_to_buffer_coords(Gtk::TextView::WINDOW_TEXT, *event.coords)
21
+ iter, _ = get_iter_at_position(x, y)
22
+ buffer.move_mark('click', iter)
23
+ end
24
+ false
25
+ end
26
+
27
+ # @api private
28
+ def __populate_popup(menu)
29
+ iter = buffer.get_iter_at_mark(buffer.text_marks[:click])
30
+ s_iter, e_iter = iter.clone, iter.clone
31
+
32
+ s_iter.backward_word_start unless s_iter.starts_word?
33
+ e_iter.forward_word_end if e_iter.inside_word?
34
+
35
+ word = buffer.get_text(s_iter, e_iter)
36
+
37
+ menu_item = Gtk::MenuItem.new("Spelling Suggestions")
38
+ submenu = Gtk::Menu.new
39
+ buffer.spell_check.send(:suggestions, word).each_with_index do |wurd, ndx|
40
+ _menu_item = Gtk::MenuItem.new(wurd)
41
+ _menu_item.signal_connect('activate') do |mi|
42
+ buffer.begin_user_action
43
+ buffer.delete(s_iter, e_iter)
44
+ buffer.insert(s_iter, mi.label)
45
+ buffer.end_user_action
46
+ end
47
+ submenu.append(_menu_item)
48
+ end
49
+ menu_item.submenu = submenu
50
+ menu.prepend(menu_item)
51
+ menu.show_all
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ module Gtk
2
+ class TreeView
3
+
4
+ # @param [] row
5
+ # @returns [Gtk::TreeIter] Newly appended iter.
6
+ def <<(row)
7
+ iter = model.append
8
+ end
9
+
10
+ end
11
+ end
@@ -4,9 +4,14 @@ module Helpers
4
4
  def log
5
5
  GtkApp.logger
6
6
  end
7
-
8
- # def config
9
- # end
7
+
8
+ class Array
9
+
10
+ def extract_options!
11
+ last.is_a?(Hash) && last.extractable_options? ? pop : {}
12
+ end
13
+
14
+ end
10
15
 
11
16
  end
12
17
  end
@@ -0,0 +1,7 @@
1
+ module GtkApp
2
+ module ListControlSupport
3
+
4
+
5
+
6
+ end
7
+ end
@@ -9,6 +9,10 @@ module SignalSupport
9
9
  module ClassMethods
10
10
  attr_reader :signal_connections
11
11
 
12
+ # @param [Symbol] widget_name
13
+ # @param [String] signal_name
14
+ # @param [Symbol] receiver_method
15
+ # @yield [...]
12
16
  def on(widget_name, signal_name, receiver_method=nil, &block)
13
17
 
14
18
  sc = SignalConnection.new do
@@ -1,9 +1,27 @@
1
- require 'raspell'
1
+ require 'ffi/aspell'
2
2
 
3
3
  module GtkApp
4
4
  class TextBuffer < Gtk::TextBuffer
5
+
6
+ # !@attribute [r] spell_check
7
+ # Aspell object
8
+ # @return [FFI::Aspell::Speller]
5
9
  attr_reader :spell_check
6
- attr_reader :undo_stack, :redo_stack
10
+
11
+ # !@attribute [r] undo_stack
12
+ # Collection of actions performed
13
+ # @return [Array]
14
+ attr_reader :undo_stack
15
+
16
+ # !@attribute [r] redo_stack
17
+ # Collection of actions undone
18
+ # @return [Array]
19
+ attr_reader :redo_stack
20
+
21
+ # !@attribute [r] text_marks
22
+ # Collection of named text marks used to track user input
23
+ # @return [Hash]
24
+ attr_reader :text_marks
7
25
 
8
26
  DEFAULT_LANG = "en_US"
9
27
  DEFAULT_TAGS = %w[bold italic strikethrough underline error spell_error]
@@ -11,11 +29,23 @@ class TextBuffer < Gtk::TextBuffer
11
29
  def initialize(tag_table=nil, options={})
12
30
  super(tag_table)
13
31
  @undo_stack, @redo_stack = [], []
14
- @spell_check = Aspell.new(options[:lang] || DEFAULT_LANG)
32
+
33
+ options[:lang] ||= DEFAULT_LANG
34
+ @spell_check = FFI::Aspell::Speller.new(options[:lang])
35
+
15
36
  setup_default_tags
37
+ setup_text_marks
16
38
  setup_signals
17
39
  end
18
40
 
41
+ # @param [Gtk::TextView]
42
+ def view=(text_view)
43
+ text_view.instance_eval do
44
+ signal_connect('button-press-event', &:__button_press_event)
45
+ signal_connect('populate-popup', &:__populate_popup)
46
+ end
47
+ end
48
+
19
49
  # Pop the last action off the undo stack and rewind changes. If an action was
20
50
  # performed, the cursor is placed at the actions starting
21
51
  # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter].
@@ -52,11 +82,15 @@ class TextBuffer < Gtk::TextBuffer
52
82
  place_cursor(s_iter)
53
83
  end
54
84
  end
55
-
85
+
86
+ # Retrieve the word at the current cursor position
87
+ # @return [String] The word.
56
88
  def word_at_cursor
57
89
  get_text(*word_bounds).strip
58
90
  end
59
91
 
92
+ # Determine the boudaries of the word at the current cursor position.
93
+ # @return [Array] The start and end iter.
60
94
  def word_bounds
61
95
  iter = get_iter_at_offset(cursor_position)
62
96
  s_iter, e_iter = iter.clone, iter.clone
@@ -66,15 +100,69 @@ class TextBuffer < Gtk::TextBuffer
66
100
  [s_iter, e_iter]
67
101
  end
68
102
 
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)
103
+ # Helper method to check the spelling of every word in the buffer.
104
+ def check_spelling
105
+ check_range(*bounds)
106
+ end
107
+
108
+ # @param [Gtk::TextIter] s_iter
109
+ # @param [Gtk::TextIter] e_iter
110
+ def check_range(s_iter, e_iter)
111
+ e_iter.forward_word_end if e_iter.inside_word?
112
+ unless s_iter.starts_word?
113
+ if s_iter.inside_word? || s_iter.ends_word?
114
+ s_iter.backward_word_start
115
+ elsif s_iter.forward_word_end
116
+ s_iter.backward_word_start
117
+ end
118
+ end
119
+ # Get the iter at the current cursor position and the pre cursor.
120
+ c_iter = get_iter_at_offset(cursor_position)
121
+ pc_iter = c_iter.clone
122
+ pc_iter.backward_char
123
+
124
+ spell_error = tag_table.lookup('spell_error')
125
+ has_error = (c_iter.has_tag?(spell_error) || pc_iter.has_tag?(spell_error))
126
+ clear(:spell_error, s_iter, e_iter)
127
+
128
+ # NOTE: From GtkSpell, catch rare cases when the replacement occurs at the
129
+ # beginning of the buffer. An iter at offset 0 seems to always be inside a
130
+ # word even if it's not.
131
+ if get_iter_at_offset(0) == s_iter
132
+ s_iter.forward_word_end
133
+ s_iter.backward_word_start
134
+ end
135
+
136
+ ws_iter = s_iter.clone # => Word start iter
137
+ while (ws_iter <=> e_iter) < 0 do
138
+ we_iter = ws_iter.clone # => Word end iter
139
+ we_iter.forward_word_end
140
+
141
+ if ((ws_iter <=> c_iter) < 0) && ((c_iter <=> we_iter) <= 0)
142
+ # The current word is being actively edited. Only check it if it's
143
+ # already been identified as incorrect. Else, check later.
144
+ check_word(ws_iter, we_iter) if has_error
145
+ else
146
+ check_word(ws_iter, we_iter)
147
+ end
148
+ # Move the word end iter the the beginning of the next word.
149
+ we_iter.forward_word_end
150
+ we_iter.backward_word_start
151
+ break if we_iter == ws_iter
152
+
153
+ ws_iter = we_iter.clone
75
154
  end
76
155
  end
77
-
156
+
157
+ # @param [Gtk::TextIter] s_iter
158
+ # @param [Gtk::TextIter] e_iter
159
+ def check_word(s_iter, e_iter)
160
+ word = get_text(s_iter, e_iter)
161
+ unless @spell_check.correct?(word)
162
+ format(:spell_error, s_iter, e_iter)
163
+ end
164
+ end
165
+
78
166
  # Does the
79
167
  # Gtk::TextTagTable[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTagTable]
80
168
  # contain any 'spell_error' tags?
@@ -85,13 +173,17 @@ class TextBuffer < Gtk::TextBuffer
85
173
  # Locate text in selection or the entire buffer. If found, a
86
174
  # Gtk::TextMark[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextMark]
87
175
  # is returned. Else, nil.
176
+ # @param [String] string Text to search.
88
177
  def find(string)
89
178
  s_iter, e_iter, text_selected = selection_bounds
90
179
  s_iter = start_iter unless text_selected
91
180
  s_iter, e_iter = s_iter.forward_search(string, Gtk::TextIter::SEARCH_TEXT_ONLY, e_iter)
92
181
  s_iter ? create_mark(nil, s_iter, false) : nil
93
182
  end
94
-
183
+
184
+ # @param [String] string Text to replace the selection with.
185
+ # @param [Gtk::TextIter] s_iter
186
+ # @param [Gtk::TextIter] e_iter
95
187
  def replace(string, s_iter, e_iter)
96
188
  begin_user_action
97
189
  delete(s_iter, e_iter)
@@ -103,6 +195,7 @@ class TextBuffer < Gtk::TextBuffer
103
195
  # Format text in the current selection range with a
104
196
  # Gtk::TextTag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextTag]
105
197
  # identified by the given name.
198
+ # @param [String] tag_name
106
199
  def format_selection(tag_name)
107
200
  s_iter, e_iter, text_selected = selection_bounds
108
201
  format(tag_name, s_iter, e_iter) if text_selected
@@ -111,6 +204,9 @@ class TextBuffer < Gtk::TextBuffer
111
204
  # This is a small wrapper around the
112
205
  # Gtk::TextBuffer#apply_tag[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextBuffer#apply_tag]
113
206
  # method. It allows the Gtk::TextTag name to be passed as a symbol.
207
+ # @param [] tag_name
208
+ # @param [Gtk::TextIter] s_iter
209
+ # @param [Gtk::TextIter] e_iter
114
210
  def format(tag_name, s_iter, e_iter)
115
211
  apply_tag(tag_name.to_s, s_iter, e_iter)
116
212
  end
@@ -132,6 +228,9 @@ class TextBuffer < Gtk::TextBuffer
132
228
  # Remove all tags of a given name from from one
133
229
  # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
134
230
  # to another.
231
+ # @param [] tag_name
232
+ # @param [Gtk::TextIter] s_iter
233
+ # @param [Gtk::TextIter] e_iter
135
234
  def clear(tag_name, s_iter, e_iter)
136
235
  remove_tag(tag_name.to_s, s_iter, e_iter)
137
236
  end
@@ -139,6 +238,8 @@ class TextBuffer < Gtk::TextBuffer
139
238
  # Remove all Gtk::TextTag's from one
140
239
  # Gtk::TextIter[http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%3ATextIter]
141
240
  # to another.
241
+ # @param [Gtk::TextIter] s_iter
242
+ # @param [Gtk::TextIter] e_iter
142
243
  def clear_all(s_iter, e_iter)
143
244
  remove_all_tags(s_iter, e_iter)
144
245
  end
@@ -160,6 +261,14 @@ class TextBuffer < Gtk::TextBuffer
160
261
  end
161
262
  end
162
263
 
264
+ def setup_text_marks
265
+ @text_marks = {
266
+ insert_start: create_mark('insert_start', start_iter, true),
267
+ insert_end: create_mark('insert_end', start_iter, true),
268
+ click: create_mark('click', start_iter, true)
269
+ }
270
+ end
271
+
163
272
  # Establish base signal handlers. Here we track user actions and...
164
273
  def setup_signals
165
274
  signal_connect('begin-user-action') { |me| @user_action = true }
@@ -171,6 +280,13 @@ class TextBuffer < Gtk::TextBuffer
171
280
  (iter.offset + text.scan(/./).size), text]
172
281
  @redo_stack.clear
173
282
  end
283
+ move_mark(me.text_marks[:insert_start], iter)
284
+ end
285
+
286
+ signal_connect_after('insert-text') do |me, iter, text, len|
287
+ s_iter = get_iter_at_mark(me.text_marks[:insert_start])
288
+ check_range(s_iter, iter)
289
+ move_mark(me.text_marks[:insert_end], iter)
174
290
  end
175
291
 
176
292
  signal_connect('delete-range') do |me, s_iter, e_iter|
@@ -180,14 +296,14 @@ class TextBuffer < Gtk::TextBuffer
180
296
  end
181
297
  end
182
298
 
183
- # TODO: Add suggestion popups for spelling erros.
184
- # tag_table.lookup('spell_error').signal_connect('event') do |tag|
185
- # end
299
+ signal_connect_after('delete-range') do |me, s_iter, e_iter|
300
+ check_range(s_iter, e_iter)
301
+ end
186
302
  end
187
303
 
188
304
  def user_action?
189
305
  @user_action
190
306
  end
191
-
307
+
308
+ end
192
309
  end
193
- end
@@ -1,3 +1,3 @@
1
1
  module GtkApp
2
- Version = VERSION = '0.1.2'
3
- end
2
+ Version = VERSION = '0.2.0'
3
+ end
@@ -3,12 +3,17 @@ class View < Gtk::Builder
3
3
  include GtkApp::Helpers
4
4
  include GtkApp::ViewHelpers
5
5
 
6
+ # @param [GtkApp::Controller] controller
7
+ # @param [String] builder_file Path the Gtk builder file
6
8
  def initialize(controller, builder_file, *args)
7
9
  super()
10
+ # options = args.extract_options!
8
11
  self.add_from_file(builder_file)
9
12
  self.connect_signals { |handler| controller.method(handler) }
13
+
14
+ # self.title = options[:title] if options.key?(:title)
10
15
  end
11
-
16
+
12
17
  def method_missing(id, *args, &block)
13
18
  method_name = id.to_s
14
19
 
@@ -44,7 +49,11 @@ class View < Gtk::Builder
44
49
  nil,
45
50
  lambda do |row|
46
51
  iter = widget.model.append
47
- row.each_with_index { |v,i| iter[i] = v }
52
+ if row.is_a?(Gtk::TreeIter)
53
+ (0..model.n_columns).each { |i| iter[i] = row[i] }
54
+ else
55
+ row.each_with_index { |v,i| iter[i] = v }
56
+ end
48
57
  iter
49
58
  end ]
50
59
  else
@@ -54,13 +63,13 @@ class View < Gtk::Builder
54
63
  lambda { |text| widget.text = ("#{widget.text}" << text) } ]
55
64
  else [nil, nil, nil]; end
56
65
  end
57
-
58
- self.class.class_eval do
66
+
67
+ class_eval do
59
68
  define_method(:"#{widget_name}", lambda { widget })
60
69
  define_method(:"#{widget_name}!", bang_proc) if bang_proc
61
70
  define_method(:"#{widget_name}=", equal_proc) if equal_proc
62
71
  end
63
-
72
+
64
73
  widget.class_eval do
65
74
  define_method("<<", append_proc)
66
75
  end if append_proc
@@ -1,29 +1,43 @@
1
1
  module GtkApp
2
2
  module ViewHelpers
3
-
4
- def build_listview(widget_name, columns, options={}, &block) # :yields: index, header, column, renderer
3
+
4
+ # Simple way to setup a listview.
5
+ # @param [Symbol] Widget name
6
+ # @param [Hash]
7
+ # @param [Hash] options
8
+ # @option options [Proc] :formatter
9
+ # @option options [Array]
10
+ # @yield [iter, header, column, renderer] Core listview elements are yielded for each column.
11
+ # @yieldparam [Integer] iter Column index
12
+ # @yieldparam [String] header Column header text
13
+ # @yieldparam [Gtk::TreeViewColumn] column The current column
14
+ # @yieldparam [Gtk::CellRenderer] renderer Current column cell renderer
15
+ # @raise [ExceptionClass] If an unhandled column data type is provided, an
16
+ # exception is raised.
17
+ # @example Build a simple listview.
18
+ # @view.build_listview(:listThings, {id: Integer, name: String})
19
+ def build_listview(widget_name, columns, options={}, &block)
5
20
  list = self.send(:"#{widget_name}")
6
21
  list.model = Gtk::ListStore.new(*columns.values)
7
22
  columns.each_with_index do |keyval, index|
8
23
  header, data_type = keyval
9
- renderer, attrs = case data_type
10
- when String, Integer
11
- [Gtk::CellRendererText.new, :text => index]
12
- when TrueClass
24
+ renderer, attrs = case "#{data_type}".to_sym
25
+ when :String, :Integer
26
+ [Gtk::CellRendererText.new, text: index]
27
+ when :TrueClass
13
28
  toggle = Gtk::CellRendererToggle.new
14
29
  toggle.signal_connect('toggled') do |widget, path|
15
30
  iter = list.model.get_iter(path)
16
31
  iter[index] = !iter[index]
17
32
  end
18
- [toggle, :active => index]
19
- when Gtk::ListStore
33
+ [toggle, active: index]
34
+ when :'Gtk::ListStore'
20
35
  _renderer = Gtk::CellRendererCombo.new
21
36
  model = Gtk::ListStore.new(String)
22
37
  _renderer.signal_connect("edited") do |cell, path, text|
23
38
  model.get_iter(path)[index] = text
24
39
  end
25
- [_renderer, :text_column => 0, :model => model, :text => index,
26
- :editable => index]
40
+ [_renderer, text_column: 0, model: model, text: index, editable: index]
27
41
  else
28
42
  raise("GtkApp::View##{__method__} does not know how to handle " +
29
43
  "'#{data_type}' data types.")
@@ -40,20 +54,21 @@ module ViewHelpers
40
54
  list.append_column(column)
41
55
  end
42
56
  end
43
-
44
- # Set *widgets sensitive property to true
45
- #== Example
46
- # @view.sensitize(:txtFoo, :cmbBar)
57
+
58
+ # Set widget(s) sensitivity property to true
59
+ # @overload sensitize(:lblFoo, :txtFoo)
60
+ # @overload sensitize(:cmbBar)
47
61
  def sensitize(*widgets)
48
62
  widgets.each { |w| self["#{w}"].sensitive = true }
49
63
  end
50
-
51
- # Set *widgets sensitive property to false
52
- #== Example
53
- # @view.desensitize(:txtFoo, :cmbBar)
64
+
65
+ # Set widget(s) sensitivity property to false
66
+ # @overload desensitize(:lblFoo, :lblBar)
67
+ # @overload desensitize(:cmbBar)
54
68
  def desensitize(*widgets)
55
69
  widgets.each { |w| self["#{w}"].sensitive = false }
56
70
  end
71
+
57
72
 
58
73
  end
59
74
  end
@@ -0,0 +1,6 @@
1
+ module GtkApp
2
+ class WidgetObserver
3
+
4
+
5
+ end
6
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gtk_app
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-16 00:00:00.000000000Z
12
+ date: 2013-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pkg-config
16
- requirement: &70335886800000 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,31 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70335886800000
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: gtk2
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
25
46
  - !ruby/object:Gem::Dependency
26
47
  name: activemodel
27
- requirement: &70335886798960 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
28
49
  none: false
29
50
  requirements:
30
51
  - - ! '>='
@@ -32,10 +53,15 @@ dependencies:
32
53
  version: 3.0.7
33
54
  type: :runtime
34
55
  prerelease: false
35
- version_requirements: *70335886798960
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.7
36
62
  - !ruby/object:Gem::Dependency
37
- name: raspell
38
- requirement: &70335886798200 !ruby/object:Gem::Requirement
63
+ name: ffi-aspell
64
+ requirement: !ruby/object:Gem::Requirement
39
65
  none: false
40
66
  requirements:
41
67
  - - ! '>='
@@ -43,13 +69,36 @@ dependencies:
43
69
  version: '0'
44
70
  type: :runtime
45
71
  prerelease: false
46
- version_requirements: *70335886798200
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: yard
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
47
94
  description:
48
95
  email: dan@dj-agiledev.com
49
96
  executables: []
50
97
  extensions: []
51
98
  extra_rdoc_files: []
52
99
  files:
100
+ - ./lib/gtk_app/application.rb
101
+ - ./lib/gtk_app/callback_support.rb
53
102
  - ./lib/gtk_app/config.rb
54
103
  - ./lib/gtk_app/controller.rb
55
104
  - ./lib/gtk_app/dialog/ask.rb
@@ -62,7 +111,12 @@ files:
62
111
  - ./lib/gtk_app/dialog/warn.rb
63
112
  - ./lib/gtk_app/dialog.rb
64
113
  - ./lib/gtk_app/drawer.rb
114
+ - ./lib/gtk_app/ext/combo_box.rb
115
+ - ./lib/gtk_app/ext/text_buffer.rb
116
+ - ./lib/gtk_app/ext/text_view.rb
117
+ - ./lib/gtk_app/ext/tree_view.rb
65
118
  - ./lib/gtk_app/helpers.rb
119
+ - ./lib/gtk_app/list_control.rb
66
120
  - ./lib/gtk_app/model.rb
67
121
  - ./lib/gtk_app/observer.rb
68
122
  - ./lib/gtk_app/partial.rb
@@ -72,6 +126,7 @@ files:
72
126
  - ./lib/gtk_app/version.rb
73
127
  - ./lib/gtk_app/view.rb
74
128
  - ./lib/gtk_app/view_helpers.rb
129
+ - ./lib/gtk_app/widget_observer.rb
75
130
  - ./lib/gtk_app.rb
76
131
  homepage: https://github.com/drfeelngood/rb-gtk_app
77
132
  licenses:
@@ -94,8 +149,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
149
  version: '0'
95
150
  requirements: []
96
151
  rubyforge_project:
97
- rubygems_version: 1.8.10
152
+ rubygems_version: 1.8.24
98
153
  signing_key:
99
154
  specification_version: 3
100
155
  summary: A ruby-gtk framework
101
156
  test_files: []
157
+ has_rdoc: true