gtk3 3.0.9 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/gtk3/rb-gtk3-private.h +1 -0
- data/ext/gtk3/rb-gtk3-spin-button.c +85 -0
- data/ext/gtk3/rb-gtk3.c +3 -0
- data/lib/gtk3/deprecated.rb +0 -8
- data/lib/gtk3/loader.rb +1 -7
- data/lib/gtk3/tree-model.rb +2 -0
- data/sample/gtk-demo/TODO +10 -10
- data/sample/gtk-demo/assistant.rb +44 -39
- data/sample/gtk-demo/builder.rb +71 -50
- data/sample/gtk-demo/button_box.rb +39 -28
- data/sample/gtk-demo/clipboard.rb +139 -46
- data/sample/gtk-demo/colorsel.rb +50 -36
- data/sample/gtk-demo/css_accordion.rb +18 -17
- data/sample/gtk-demo/css_basics.rb +60 -47
- data/sample/gtk-demo/css_multiplebgs.rb +92 -71
- data/sample/gtk-demo/css_pixbufs.rb +61 -48
- data/sample/gtk-demo/css_shadows.rb +63 -50
- data/sample/gtk-demo/cursors.rb +95 -64
- data/sample/gtk-demo/dialog.rb +95 -78
- data/sample/gtk-demo/drawingarea.rb +138 -171
- data/sample/gtk-demo/editable_cells.rb +169 -130
- data/sample/gtk-demo/entry_buffer.rb +15 -13
- data/sample/gtk-demo/entry_completion.rb +22 -17
- data/sample/gtk-demo/expander.rb +39 -31
- data/sample/gtk-demo/filtermodel.rb +67 -63
- data/sample/gtk-demo/font_features.rb +91 -60
- data/sample/gtk-demo/glarea.rb +277 -0
- data/sample/gtk-demo/headerbar.rb +17 -15
- data/sample/gtk-demo/hypertext.rb +146 -167
- data/sample/gtk-demo/iconview.rb +132 -91
- data/sample/gtk-demo/iconview_edit.rb +49 -38
- data/sample/gtk-demo/infobar.rb +81 -62
- data/sample/gtk-demo/links.rb +35 -30
- data/sample/gtk-demo/list_store.rb +169 -114
- data/sample/gtk-demo/listbox.rb +183 -0
- data/sample/gtk-demo/main.rb +32 -21
- data/sample/gtk-demo/markup.rb +65 -52
- data/sample/gtk-demo/menus.rb +57 -58
- data/sample/gtk-demo/modelbutton.rb +11 -9
- data/sample/gtk-demo/modelbutton.ui +3 -0
- data/sample/gtk-demo/overlay.rb +39 -32
- data/sample/gtk-demo/overlay2.rb +68 -54
- data/sample/gtk-demo/panes.rb +56 -68
- data/sample/gtk-demo/pickers.rb +46 -45
- data/sample/gtk-demo/pixbufs.rb +27 -25
- data/sample/gtk-demo/popover.rb +70 -63
- data/sample/gtk-demo/printing.rb +94 -69
- data/sample/gtk-demo/revealer.rb +46 -38
- data/sample/gtk-demo/rotated_text.rb +75 -54
- data/sample/gtk-demo/scale.rb +10 -8
- data/sample/gtk-demo/search_entry.rb +195 -0
- data/sample/gtk-demo/search_entry2.rb +71 -59
- data/sample/gtk-demo/sidebar.rb +20 -19
- data/sample/gtk-demo/sizegroup.rb +36 -35
- data/sample/gtk-demo/spinbutton.rb +128 -0
- data/sample/gtk-demo/spinner.rb +55 -40
- data/sample/gtk-demo/stack.rb +11 -8
- data/sample/gtk-demo/textmask.rb +14 -13
- data/sample/gtk-demo/textscroll.rb +16 -12
- data/sample/gtk-demo/theming_style_classes.rb +14 -12
- data/sample/gtk-demo/transparent.rb +17 -13
- data/sample/misc/treemodelfilter.rb +1 -1
- 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
|
-
|
16
|
-
def
|
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.
|
23
|
-
header.
|
24
|
-
header.
|
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.
|
49
|
-
window.add(Gtk::TextView.new
|
48
|
+
@window.titlebar = header
|
49
|
+
@window.add(Gtk::TextView.new)
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
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)
|
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
|
-
=
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
151
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
172
|
-
|
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
|
-
|
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
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
window
|
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
|