gtk_app 0.1.2 → 0.2.0

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.
@@ -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