gtk3 3.0.9 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/ext/gtk3/rb-gtk3-private.h +1 -0
  3. data/ext/gtk3/rb-gtk3-spin-button.c +85 -0
  4. data/ext/gtk3/rb-gtk3.c +3 -0
  5. data/lib/gtk3/deprecated.rb +0 -8
  6. data/lib/gtk3/loader.rb +1 -7
  7. data/lib/gtk3/tree-model.rb +2 -0
  8. data/sample/gtk-demo/TODO +10 -10
  9. data/sample/gtk-demo/assistant.rb +44 -39
  10. data/sample/gtk-demo/builder.rb +71 -50
  11. data/sample/gtk-demo/button_box.rb +39 -28
  12. data/sample/gtk-demo/clipboard.rb +139 -46
  13. data/sample/gtk-demo/colorsel.rb +50 -36
  14. data/sample/gtk-demo/css_accordion.rb +18 -17
  15. data/sample/gtk-demo/css_basics.rb +60 -47
  16. data/sample/gtk-demo/css_multiplebgs.rb +92 -71
  17. data/sample/gtk-demo/css_pixbufs.rb +61 -48
  18. data/sample/gtk-demo/css_shadows.rb +63 -50
  19. data/sample/gtk-demo/cursors.rb +95 -64
  20. data/sample/gtk-demo/dialog.rb +95 -78
  21. data/sample/gtk-demo/drawingarea.rb +138 -171
  22. data/sample/gtk-demo/editable_cells.rb +169 -130
  23. data/sample/gtk-demo/entry_buffer.rb +15 -13
  24. data/sample/gtk-demo/entry_completion.rb +22 -17
  25. data/sample/gtk-demo/expander.rb +39 -31
  26. data/sample/gtk-demo/filtermodel.rb +67 -63
  27. data/sample/gtk-demo/font_features.rb +91 -60
  28. data/sample/gtk-demo/glarea.rb +277 -0
  29. data/sample/gtk-demo/headerbar.rb +17 -15
  30. data/sample/gtk-demo/hypertext.rb +146 -167
  31. data/sample/gtk-demo/iconview.rb +132 -91
  32. data/sample/gtk-demo/iconview_edit.rb +49 -38
  33. data/sample/gtk-demo/infobar.rb +81 -62
  34. data/sample/gtk-demo/links.rb +35 -30
  35. data/sample/gtk-demo/list_store.rb +169 -114
  36. data/sample/gtk-demo/listbox.rb +183 -0
  37. data/sample/gtk-demo/main.rb +32 -21
  38. data/sample/gtk-demo/markup.rb +65 -52
  39. data/sample/gtk-demo/menus.rb +57 -58
  40. data/sample/gtk-demo/modelbutton.rb +11 -9
  41. data/sample/gtk-demo/modelbutton.ui +3 -0
  42. data/sample/gtk-demo/overlay.rb +39 -32
  43. data/sample/gtk-demo/overlay2.rb +68 -54
  44. data/sample/gtk-demo/panes.rb +56 -68
  45. data/sample/gtk-demo/pickers.rb +46 -45
  46. data/sample/gtk-demo/pixbufs.rb +27 -25
  47. data/sample/gtk-demo/popover.rb +70 -63
  48. data/sample/gtk-demo/printing.rb +94 -69
  49. data/sample/gtk-demo/revealer.rb +46 -38
  50. data/sample/gtk-demo/rotated_text.rb +75 -54
  51. data/sample/gtk-demo/scale.rb +10 -8
  52. data/sample/gtk-demo/search_entry.rb +195 -0
  53. data/sample/gtk-demo/search_entry2.rb +71 -59
  54. data/sample/gtk-demo/sidebar.rb +20 -19
  55. data/sample/gtk-demo/sizegroup.rb +36 -35
  56. data/sample/gtk-demo/spinbutton.rb +128 -0
  57. data/sample/gtk-demo/spinner.rb +55 -40
  58. data/sample/gtk-demo/stack.rb +11 -8
  59. data/sample/gtk-demo/textmask.rb +14 -13
  60. data/sample/gtk-demo/textscroll.rb +16 -12
  61. data/sample/gtk-demo/theming_style_classes.rb +14 -12
  62. data/sample/gtk-demo/transparent.rb +17 -13
  63. data/sample/misc/treemodelfilter.rb +1 -1
  64. metadata +21 -16
@@ -0,0 +1,277 @@
1
+ # Copyright (c) 2016 Ruby-GNOME2 Project Team
2
+ # This program is licenced under the same licence as Ruby-GNOME2.
3
+ #
4
+ =begin
5
+ = OpenGL Area
6
+
7
+ GtkGLArea is a widget that allows custom drawing using OpenGL calls.
8
+ =end
9
+ require "opengl"
10
+ OpenGL.load_lib
11
+ include OpenGL
12
+
13
+ class GlareaDemo
14
+ X_AXIS, Y_AXIS, Z_AXIS = (0..2).to_a
15
+ # The object we are drawing
16
+ VERTEX_DATA = [0.0, 0.5, 0.0, 1.0,
17
+ 0.5, -0.366, 0.0, 1.0,
18
+ -0.5, -0.366, 0.0, 1.0].freeze
19
+
20
+ def initialize(main_window)
21
+ # Rotation angles on each axis
22
+ @rotation_angles = [0.0, 0.0, 0.0]
23
+ @window = Gtk::Window.new(:toplevel)
24
+ @window.screen = main_window.screen
25
+ @window.title = "OpenGL Area"
26
+ @window.set_default_size(400, 600)
27
+ @window.border_width = 12
28
+
29
+ box = Gtk::Box.new(:vertical, false)
30
+ box.spacing = 6
31
+ @window.add(box)
32
+
33
+ @gl_area = Gtk::GLArea.new
34
+ @gl_area.hexpand = true
35
+ @gl_area.vexpand = true
36
+ box.add(@gl_area)
37
+
38
+ # We need to initialize and free GL resources, so we use
39
+ # the realize and unrealize signals on the widget
40
+ @gl_area.signal_connect "realize" do |widget|
41
+ # We need to set up our state when we realize the GtkGLArea widget
42
+ widget.make_current
43
+ unless widget.error
44
+ init_buffers
45
+ @program, @mvp_location = init_shaders
46
+ end
47
+ end
48
+
49
+ @gl_area.signal_connect "unrealize" do |widget|
50
+ widget.make_current
51
+ unless widget.error
52
+ # We should tear down the state when unrealizing
53
+ glDeleteProgram(@program)
54
+ end
55
+ end
56
+
57
+ # The main "draw" call for GtkGLArea
58
+ @gl_area.signal_connect "render" do |area, _context|
59
+ return false if area.error
60
+
61
+ # Clear the viewport
62
+ glClearColor(0.5, 0.5, 0.5, 1.0)
63
+ glClear(GL_COLOR_BUFFER_BIT)
64
+ draw_triangle
65
+ glFlush
66
+ true
67
+ end
68
+
69
+ controls = Gtk::Box.new(:vertical, false)
70
+ box.add(controls)
71
+ controls.hexpand = true
72
+ (0..2).each do |i|
73
+ controls.add(create_axis_slider(i))
74
+ end
75
+
76
+ button = Gtk::Button.new(:label => "Quit")
77
+ button.hexpand = true
78
+ box.add(button)
79
+ button.signal_connect "clicked" do
80
+ @window.destroy
81
+ end
82
+ end
83
+
84
+ def run
85
+ if !@window.visible?
86
+ @window.show_all
87
+ else
88
+ @window.destroy
89
+ end
90
+ @window
91
+ end
92
+
93
+ private
94
+
95
+ def init_buffers
96
+ vao = " "
97
+ glGenVertexArrays(1, vao)
98
+ gl_vao = vao.unpack("L")[0]
99
+ @position_buffer = gl_vao
100
+ glBindVertexArray(gl_vao)
101
+
102
+ # This is the buffer that holds the vertices
103
+ buffer = " "
104
+ glGenBuffers(1, buffer)
105
+ gl_vbo = buffer.unpack("L")[0]
106
+ glBindBuffer(GL_ARRAY_BUFFER, gl_vbo)
107
+ glBufferData(GL_ARRAY_BUFFER, 3 * 4 * Fiddle::SIZEOF_FLOAT,
108
+ VERTEX_DATA.pack("F*"), GL_STATIC_DRAW)
109
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
110
+ end
111
+
112
+ # Initialize shaders and link them into a programm
113
+ def init_shaders
114
+ src_vertex = Gio::Resources.lookup_data("/glarea/glarea-vertex.glsl", 0)
115
+ src_fragment = Gio::Resources.lookup_data("/glarea/glarea-fragment.glsl", 0)
116
+ vertex = create_shader(GL_VERTEX_SHADER, src_vertex)
117
+ return [0, nil] if vertex.zero?
118
+ fragment = create_shader(GL_FRAGMENT_SHADER, src_fragment)
119
+ return [0, nil] if fragment.zero?
120
+
121
+ program = glCreateProgram
122
+ glAttachShader(program, vertex)
123
+ glAttachShader(program, fragment)
124
+ glLinkProgram(program)
125
+ # Check if the program is valid
126
+ linked_buf = " "
127
+ glGetProgramiv(program, GL_LINK_STATUS, linked_buf)
128
+ status = linked_buf.unpack("L")[0]
129
+ if status.zero?
130
+ log_len = " "
131
+ glGetShaderiv(program, GL_INFO_LOG_LENGTH, log_len)
132
+ len = log_len.unpack("L")[0]
133
+ info_log = " " * (len + 1)
134
+ glGetProgramInfoLog(program, len, nil, info_log)
135
+ STDERR.puts "Linking failure:\n#{info_log}\n"
136
+ glDeleteProgram(program)
137
+ glDeleteShader(vertex)
138
+ glDeleteShader(fragment)
139
+ program = 0
140
+ end
141
+ mvp = glGetUniformLocation(program, "mvp")
142
+ [program, mvp]
143
+ end
144
+
145
+ def create_shader(type, source)
146
+ # Load the shaders sources
147
+ shader = glCreateShader(type)
148
+
149
+ glShaderSource(shader, 1, [source].pack("p"), [source.bytesize].pack("I"))
150
+ # Compile the vertex shader
151
+ glCompileShader(shader)
152
+ # Check the vertex shader compilation
153
+ compiled_buf = " "
154
+ glGetShaderiv(shader, GL_COMPILE_STATUS, compiled_buf)
155
+ status = compiled_buf.unpack("L")[0]
156
+ if status.zero?
157
+ log_len = " "
158
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, log_len)
159
+ len = log_len.unpack("L")[0]
160
+ info_log = " " * (len + 1)
161
+ glGetShaderInfoLog(shader, len, nil, info_log)
162
+ type_name = type == GL_VERTEX_SHADER ? "vertex" : "fragment"
163
+ STDERR.puts "Compile failure in #{type_name}:\n#{info_log}\n"
164
+ return 0
165
+ end
166
+ shader
167
+ end
168
+
169
+ def draw_triangle
170
+ # Compute the model view projection matrix using the rotation angles
171
+ # specified through the GtkRange widgets
172
+ mvp = compute_mvp
173
+ # Use our program
174
+ glUseProgram(@program)
175
+ # Update the "mvp" matrix we use in the shader
176
+ glUniformMatrix4fv(@mvp_location, 1, GL_FALSE, mvp.pack("F16"))
177
+ # Use the vertices in our buffer
178
+ glBindBuffer(GL_ARRAY_BUFFER, @position_buffer)
179
+ glEnableVertexAttribArray(0)
180
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0)
181
+
182
+ # Draw the three vertices as a triangle
183
+ glDrawArrays(GL_TRIANGLES, 0, 3)
184
+
185
+ # We finished using the buffers and program
186
+ glDisableVertexAttribArray(0)
187
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
188
+ glUseProgram(0)
189
+ end
190
+
191
+ def compute_mvp
192
+ phi = @rotation_angles[X_AXIS]
193
+ theta = @rotation_angles[Y_AXIS]
194
+ psi = @rotation_angles[Z_AXIS]
195
+ res = Array.new(16, 0.0)
196
+ x = phi * (Math::PI / 180.0)
197
+ y = theta * (Math::PI / 180.0)
198
+ z = psi * (Math::PI / 180.0)
199
+ c1 = Math.cos(x)
200
+ c2 = Math.cos(y)
201
+ c3 = Math.cos(z)
202
+ s1 = Math.sin(x)
203
+ s2 = Math.sin(y)
204
+ s3 = Math.sin(z)
205
+ c3c2 = c3 * c2
206
+ s3c1 = s3 * c1
207
+ c3s2s1 = c3 * s2 * s1
208
+ s3s1 = s3 * s1
209
+ c3s2c1 = c3 * s2 * c1
210
+ s3c2 = s3 * c2
211
+ c3c1 = c3 * c1
212
+ s3s2s1 = s3 * s2 * s1
213
+ c3s1 = c3 * s1
214
+ s3s2c1 = s3 * s2 * c1
215
+ c2s1 = c2 * s1
216
+ c2c1 = c2 * c1
217
+
218
+ # initialize to the identity matrix
219
+ res[0] = 1.0
220
+ res[5] = 1.0
221
+ res[10] = 1.0
222
+ res[15] = 1.0
223
+
224
+ # apply all three rotations using the three matrices:
225
+ #
226
+ # ⎡ c3 s3 0 ⎤ ⎡ c2 0 -s2 ⎤ ⎡ 1 0 0 ⎤
227
+ # ⎢ -s3 c3 0 ⎥ ⎢ 0 1 0 ⎥ ⎢ 0 c1 s1 ⎥
228
+ # ⎣ 0 0 1 ⎦ ⎣ s2 0 c2 ⎦ ⎣ 0 -s1 c1 ⎦
229
+
230
+ res[0] = c3c2
231
+ res[1] = -s3c2
232
+ res[2] = s2
233
+ res[3] = 0.0
234
+ res[4] = s3c1 + c3s2s1
235
+ res[5] = c3c1 - s3s2s1
236
+ res[6] = -c2s1
237
+ res[7] = 0.0
238
+ res[8] = s3s1 - c3s2c1
239
+ res[9] = c3s1 + s3s2c1
240
+ res[10] = c2c1
241
+ res[11] = 0.0
242
+ res[12] = 0.0
243
+ res[13] = 0.0
244
+ res[14] = 0.0
245
+ res[15] = 1.0
246
+ res
247
+ end
248
+
249
+ def create_axis_slider(axis)
250
+ box = Gtk::Box.new(:horizontal, 0)
251
+ text = case axis
252
+ when X_AXIS
253
+ "X axis"
254
+ when Y_AXIS
255
+ "Y axis"
256
+ when Z_AXIS
257
+ "Z axis"
258
+ end
259
+ label = Gtk::Label.new(text)
260
+ box.add(label)
261
+ label.show
262
+
263
+ adj = Gtk::Adjustment.new(0.0, 0.0, 360.0, 1.0, 12.0, 0.0)
264
+ adj.signal_connect "value-changed" do |widget|
265
+ # Updte the rotation angle
266
+ @rotation_angles[axis] = widget.value
267
+ # Update the contents of the GL drawing area
268
+ @gl_area.queue_draw
269
+ end
270
+ slider = Gtk::Scale.new(:horizontal, adj)
271
+ box.add(slider)
272
+ slider.hexpand = true
273
+ slider.show
274
+ box.show
275
+ box
276
+ end
277
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2015 Ruby-GNOME2 Project Team
1
+ # Copyright (c) 2015-2016 Ruby-GNOME2 Project Team
2
2
  # This program is licenced under the same licence as Ruby-GNOME2.
3
3
  #
4
4
  =begin
@@ -12,18 +12,18 @@ or right.
12
12
 
13
13
  It is commonly used with gtk_window_set_titlebar()
14
14
  =end
15
- module HeaderbarDemo
16
- def self.run_demo(main_window)
17
- window = Gtk::Window.new(:toplevel)
18
- window.screen = main_window.screen
19
- window.set_default_size(600, 400)
15
+ class HeaderbarDemo
16
+ def initialize(main_window)
17
+ @window = Gtk::Window.new(:toplevel)
18
+ @window.screen = main_window.screen
19
+ @window.set_default_size(600, 400)
20
20
 
21
21
  header = Gtk::HeaderBar.new
22
- header.set_show_close_button(true)
23
- header.set_title("Welcome to Facebook - Log in, sign up or learn more")
24
- header.set_has_subtitle(false)
22
+ header.show_close_button = true
23
+ header.title = "Welcome to Facebook - Log in, sign up or learn more"
24
+ header.has_subtitle = false
25
25
 
26
- button = Gtk::Button.new()
26
+ button = Gtk::Button.new
27
27
 
28
28
  icon = Gio::ThemedIcon.new("mail-send-receive-symbolic")
29
29
  image = Gtk::Image.new(:icon => icon, :size => :button)
@@ -45,13 +45,15 @@ module HeaderbarDemo
45
45
  box.add(button)
46
46
 
47
47
  header.pack_start(box)
48
- window.set_titlebar(header)
49
- window.add(Gtk::TextView.new())
48
+ @window.titlebar = header
49
+ @window.add(Gtk::TextView.new)
50
+ end
50
51
 
51
- if !window.visible?
52
- window.show_all
52
+ def run
53
+ if !@window.visible?
54
+ @window.show_all
53
55
  else
54
- window.destroy
56
+ @window.destroy
55
57
  end
56
58
  end
57
59
  end
@@ -1,205 +1,184 @@
1
- # Copyright (c) 2003-2005 Ruby-GNOME2 Project Team
1
+ # Copyright (c) 2016 Ruby-GNOME2 Project Team
2
2
  # This program is licenced under the same licence as Ruby-GNOME2.
3
3
  #
4
- # $Id: hypertext.rb,v 1.2 2005/12/01 09:27:01 ktou Exp $
5
4
  =begin
6
- = Text Widget/Hypertext
5
+ = Text View/Hypertext
7
6
 
8
- Usually, tags modify the appearance of text in the view, e.g. making it
9
- bold or colored or underlined. But tags are not restricted to appearance.
10
- They can also affect the behavior of mouse and key presses, as this demo
11
- shows.
7
+ Usually, tags modify the appearance of text in the view, e.g. making it
8
+ bold or colored or underlined. But tags are not restricted to appearance.
9
+ They can also affect the behavior of mouse and key presses, as this demo
10
+ shows.
12
11
  =end
12
+ class HypertextDemo
13
+ def initialize(main_window)
14
+ initialize_window(main_window)
15
+ initialize_cursors(main_window)
16
+ initialize_view
13
17
 
14
- require 'common'
15
-
16
- module Demo
17
- class Hypertext < BasicWindow
18
- def initialize
19
- super('Hypertext')
20
-
21
- @hand_cursor = Gdk::Cursor.new(Gdk::Cursor::HAND2)
22
- @regular_cursor = Gdk::Cursor.new(Gdk::Cursor::XTERM)
23
- @hovering = false
24
-
25
- set_default_size(450, 450)
26
- self.border_width = 0
27
-
28
- view = Gtk::TextView.new
29
-
30
- view.wrap_mode = :word
31
-
32
- view.signal_connect('key-press-event') do |*args|
33
- key_press_event(*args)
34
- end
35
- view.signal_connect('event-after') do |*args|
36
- event_after(*args)
37
- end
38
- view.signal_connect('motion-notify-event') do |*args|
39
- motion_notify_event(*args)
40
- end
41
- view.signal_connect('visibility-notify-event') do |*args|
42
- visibility_notify_event(*args)
18
+ @view.signal_connect "key-press-event" do |_widget, event|
19
+ case event.keyval
20
+ when Gdk::Keyval::KEY_Return, Gdk::Keyval::KEY_KP_Enter
21
+ iter = @buffer.get_iter_at_mark(@buffer.get_mark("insert"))
22
+ follow_if_link(iter) if iter
43
23
  end
44
24
 
45
- buffer = view.buffer
46
-
47
- sw = Gtk::ScrolledWindow.new
48
- sw.set_policy(:automatic,
49
- :automatic)
50
-
51
- add(sw)
52
- sw.add(view)
53
-
54
- show_page(buffer, 1)
25
+ false
55
26
  end
56
27
 
28
+ # Links can also be activated by clicking or tapping.
29
+ @view.signal_connect "event-after" do |widget, event|
30
+ if event.is_a?(Gdk::EventButton) && event.button == 1
31
+ buffer = widget.buffer
57
32
 
58
- # Inserts a piece of text into the buffer, giving it the usual
59
- # appearance of a hyperlink in a web browser: blue and underlined.
60
- # Additionally, attaches some data on the tag, to make it recognizable
61
- # as a link.
62
- def insert_link(buffer, iter, text, page)
63
- tag = buffer.create_tag(nil,
64
- {
65
- 'foreground' => 'blue',
66
- 'underline' => Pango::AttrUnderline::SINGLE,
67
- })
68
- tag.page = page
69
-
70
- buffer.insert(iter, text, tag)
71
- print("Insert #{tag}:#{page}\n")
72
- end
33
+ # we shouldn't follow a link if the user has selected something
34
+ range = buffer.selection_bounds
35
+ return false if range && range[0].offset != range[1].offset
73
36
 
74
- # Fills the buffer with text and interspersed links. In any real
75
- # hypertext app, this method would parse a file to identify the links.
76
- def show_page(buffer, page)
77
- puts
78
-
79
- buffer.text = ''
80
- iter = buffer.get_iter_at(:offset => 0)
81
-
82
- case page
83
- when 1
84
- buffer.insert(iter, 'Some text ot show that simple ')
85
- insert_link(buffer, iter, 'hypertext', 3)
86
- buffer.insert(iter, ' can easily be realized with ')
87
- insert_link(buffer, iter, 'tags', 2)
88
- buffer.insert(iter, '.')
89
- when 2
90
- buffer.insert(iter,
91
- %Q[A tag is an attribute that can be applied to some range of text. For example, a tag might be called "bold" and make the text insid the tag bold. However, the tag concept is more general than that; tags don't have to affect appearance. They can instead affect the behavior of mouse and key presses, "lock" a range of text so the user can't edit it, or countless other things.])
92
- buffer.insert(iter, "\n")
93
- insert_link(buffer, iter, 'Go back', 1)
94
- when 3
95
- tag = buffer.create_tag(nil,
96
- {
97
- 'weight' =>
98
- Pango::FontDescription::WEIGHT_BOLD
99
- })
100
- buffer.insert(iter, "hypertext:\n", tag);
101
- buffer.insert(iter, %Q[machine-readable text that is not sequential but is organized so that related items of information are connected.\n])
102
- insert_link(buffer, iter, 'Go back', 1)
37
+ x, y = widget.window_to_buffer_coords(:widget, event.x, event.y)
38
+ iter = widget.get_iter_at_location(x, y)
39
+ follow_if_link(iter) if iter
40
+ else
41
+ false
103
42
  end
104
43
  end
105
44
 
106
- # Looks at all tags covering the position of iter in the text view,
107
- # and if one of them is a link, follow it by showing the page identified
108
- # by the data attached to it.
109
- def follow_if_link(text_view, iter)
110
- tags = iter.tags
111
- tags.each do |tag|
112
- print("Follow #{tag}:#{tag.page}\n")
113
- if tag.page
114
- show_page(text_view.buffer, tag.page)
115
- break
116
- end
117
- end
118
- end
119
-
120
- # Links can be activated by pressing Enter.
121
- def key_press_event(text_view, event)
122
- case event.keyval
123
- when Gdk::Keyval::KEY_Return, Gdk::Keyval::KEY_KP_Enter
124
- buffer = text_view.buffer
125
- iter = buffer.get_iter_at_mark(buffer.get_mark("insert"))
126
- follow_if_link(text_view, iter)
127
- end
45
+ @view.signal_connect "motion-notify-event" do |widget, event|
46
+ x, y = widget.window_to_buffer_coords(:widget, event.x, event.y)
47
+ set_cursor_if_appropriate(widget, x, y)
48
+ widget.window.pointer
128
49
 
129
50
  false
130
51
  end
131
52
 
132
- # Links can also be activated by clicking.
133
- def event_after(text_view, event)
134
- unless event.kind_of?(Gdk::EventButton) and event.button == 1
135
- return false
136
- end
53
+ sw = Gtk::ScrolledWindow.new
54
+ sw.set_policy(:automatic, :automatic)
55
+ @window.add(sw)
56
+ sw.add(@view)
57
+ show_page(1)
58
+ sw.show_all
59
+ end
137
60
 
138
- buffer = text_view.buffer
61
+ def run
62
+ if !@window.visible?
63
+ @window.show_all
64
+ else
65
+ @window.destroy
66
+ end
67
+ @window
68
+ end
139
69
 
140
- # we shouldn't follow a link if the user has selected something
141
- range = buffer.selection_bounds
142
- if range and range[0].offset != range[1].offset
143
- return false
144
- end
70
+ private
145
71
 
146
- x, y = text_view.window_to_buffer_coords(Gtk::TextView::WINDOW_WIDGET,
147
- event.x, event.y)
148
- iter = text_view.get_iter_at_location(x, y)
72
+ def initialize_window(main_window)
73
+ @window = Gtk::Window.new(:toplevel)
74
+ @window.screen = main_window.screen
75
+ @window.title = "Hypertext"
76
+ @window.set_default_size(450, 450)
77
+ @window.border_width = 0
78
+ end
149
79
 
150
- follow_if_link(text_view, iter)
151
- end
80
+ def initialize_cursors(main_window)
81
+ display = main_window.display
82
+ @hand_cursor = Gdk::Cursor.new("pointer", :display => display)
83
+ @regular_cursor = Gdk::Cursor.new("text", :display => display)
84
+ end
152
85
 
153
- # Looks at all tags covering the position (x, y) in the text view,
154
- # and if one of them is a link, change the cursor to the "hands" cursor
155
- # typically used by web browsers.
156
- def set_cursor_if_appropriate(text_view, x, y)
86
+ def initialize_view
87
+ @view = Gtk::TextView.new
88
+ @view.wrap_mode = :word
89
+ @view.left_margin = 20
90
+ @view.right_margin = 20
91
+ @buffer = @view.buffer
92
+ end
157
93
 
158
- buffer = text_view.buffer
159
- iter = text_view.get_iter_at_location(x, y)
94
+ # Fills the buffer with text and interspersed links. In any real
95
+ # hypertext app, this method would parse a file to identify the links.
96
+ def show_page(page)
97
+ @buffer.text = ""
98
+ case page
99
+ when 1
100
+ generate_page_1
101
+ when 2
102
+ generate_page_2
103
+ when 3
104
+ generate_page_3
105
+ end
106
+ end
160
107
 
161
- hovering = false
108
+ def generate_page_1
109
+ iter = @buffer.get_iter_at(:offset => 0)
110
+ @buffer.insert(iter, "Some text to show that simple")
111
+ insert_link(iter, "hyper text", 3)
112
+ @buffer.insert(iter, " can easily be realized with ")
113
+ insert_link(iter, "tags", 2)
114
+ @buffer.insert(iter, ".")
115
+ end
162
116
 
163
- tags = iter.tags
164
- tags.each do |t|
165
- if t.page
166
- hovering = true
167
- break
168
- end
169
- end
117
+ def generate_page_2
118
+ iter = @buffer.get_iter_at(:offset => 0)
119
+ @buffer.insert(iter, <<-EOF)
120
+ A tag is an attribute that can be applied to some range of text.
121
+ For example, a tag might be called "bold" and make the text inside
122
+ the tag bold. However, the tag concept is more general than that;
123
+ tags don't have to affect appearance. They can instead affect the
124
+ behavior of mouse and key presses, "lock" a range of text so the
125
+ user can't edit it, or countless other things.\n
126
+ EOF
127
+ insert_link(iter, "Goback", 1)
128
+ end
170
129
 
171
- if hovering != @hovering
172
- @hovering = hovering
130
+ def generate_page_3
131
+ iter = @buffer.get_iter_at(:offset => 0)
132
+ tag = @buffer.create_tag(nil,
133
+ "weight" => Pango::FontDescription::WEIGHT_BOLD)
134
+ @buffer.insert(iter, "hypertext:\n", :tags => [tag])
135
+ @buffer.insert(iter, <<-EOF)
136
+ machine-readable text that is not sequential but is organized
137
+ so that related items of information are connected
138
+ EOF
139
+ insert_link(iter, "Go back", 1)
140
+ end
173
141
 
174
- window = text_view.get_window(Gtk::TextView::WINDOW_TEXT)
142
+ def insert_link(iter, text, page)
143
+ tag = @buffer.create_tag(nil,
144
+ "foreground" => "blue",
145
+ "underline" => Pango::AttrUnderline::SINGLE)
146
+ tag.page = page
147
+ @buffer.insert(iter, text, :tags => [tag])
148
+ end
175
149
 
176
- window.cursor = if @hovering
177
- @hand_cursor
178
- else
179
- @regular_cursor
180
- end
150
+ # Looks at all tags covering the position of iter in the text view,
151
+ # and if one of them is a link, follow it by showing the page identified
152
+ # by the data attached to it.
153
+ def follow_if_link(iter)
154
+ tags = iter.tags
155
+ tags.each do |tag|
156
+ if tag.page
157
+ show_page(tag.page)
158
+ break
181
159
  end
182
160
  end
161
+ end
183
162
 
184
- # Update the cursor image if the pointer moved.
185
- def motion_notify_event(text_view, event)
186
- x, y = text_view.window_to_buffer_coords(Gtk::TextView::WINDOW_WIDGET,
187
- event.x, event.y)
188
- set_cursor_if_appropriate(text_view, x, y)
189
- text_view.window.pointer
190
-
191
- false
163
+ # Looks at all tags covering the position (x, y) in the text view,
164
+ # and if one of them is a link, change the cursor to the "hands" cursor
165
+ # typically used by web browsers.
166
+ def set_cursor_if_appropriate(text_view, x, y)
167
+ iter = text_view.get_iter_at_location(x, y)
168
+ return unless iter
169
+ hovering = false
170
+ tags = iter.tags
171
+ tags.each do |tag|
172
+ if tag.page
173
+ hovering = true
174
+ break
175
+ end
192
176
  end
193
177
 
194
- # Also update the cursor image if the window becomes visible
195
- # (e.g. when a window covering it got iconified).
196
- def visibility_notify_event (text_view, event)
197
- window, wx, wy = text_view.window.pointer
198
- bx, by = text_view.window_to_buffer_coords(Gtk::TextView::WINDOW_WIDGET,
199
- wx, wy)
200
- set_cursor_if_appropriate(text_view, bx, by)
201
-
202
- false
178
+ if hovering != @hovering
179
+ @hovering = hovering
180
+ window = text_view.get_window(:text)
181
+ window.cursor = @hovering ? @hand_cursor : @regular_cursor
203
182
  end
204
183
  end
205
184
  end