gtk4 4.2.1 → 4.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/gtk4.gemspec +1 -1
- data/lib/gtk4/css-provider.rb +23 -4
- data/lib/gtk4/loader.rb +2 -0
- data/lib/gtk4/text-buffer.rb +11 -0
- data/sample/demos/gtk-demo/README.md +3 -0
- data/sample/demos/gtk-demo/data/scalable/apps/org.gtk.Demo4.svg +1 -0
- data/sample/demos/gtk-demo/data/symbolic/apps/org.gtk.Demo4-symbolic.svg +114 -0
- data/sample/demos/gtk-demo/demo.gresource +0 -0
- data/sample/demos/gtk-demo/demo.gresource.xml +474 -0
- data/sample/demos/gtk-demo/demo.rb +82 -0
- data/sample/demos/gtk-demo/glarea-gl.fs.glsl +9 -0
- data/sample/demos/gtk-demo/glarea-gl.vs.glsl +8 -0
- data/sample/demos/gtk-demo/glarea-gles.fs.glsl +7 -0
- data/sample/demos/gtk-demo/glarea-gles.vs.glsl +7 -0
- data/sample/demos/gtk-demo/glarea.rb +323 -0
- data/sample/demos/gtk-demo/main-listitem.ui +25 -0
- data/sample/demos/gtk-demo/main.rb +264 -0
- data/sample/demos/gtk-demo/main.ui +184 -0
- metadata +22 -11
@@ -0,0 +1,323 @@
|
|
1
|
+
# Copyright (C) 2024 Ruby-GNOME Project Team
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
# Original:
|
18
|
+
# https://gitlab.gnome.org/GNOME/gtk/-/blob/main/demos/gtk-demo/glarea.c
|
19
|
+
#
|
20
|
+
# See the following for license information:
|
21
|
+
# * https://gitlab.gnome.org/GNOME/gtk/-/blob/main/AUTHORS
|
22
|
+
# * https://gitlab.gnome.org/GNOME/gtk/-/blob/main/COPYING
|
23
|
+
|
24
|
+
require "opengl"
|
25
|
+
|
26
|
+
class GtkGLAreaDemo < GtkDemo
|
27
|
+
X_AXIS = 0
|
28
|
+
Y_AXIS = 1
|
29
|
+
Z_AXIS = 2
|
30
|
+
N_AXISES = 3
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
super(__FILE__,
|
34
|
+
"OpenGL/OpenGL Area",
|
35
|
+
"Gtk::GLArea is a widget that allows custom drawing using OpenGL calls.",
|
36
|
+
[])
|
37
|
+
end
|
38
|
+
|
39
|
+
def run(parent)
|
40
|
+
window = Gtk::Window.new
|
41
|
+
window.display = parent.display
|
42
|
+
window.title = "OpenGL Area"
|
43
|
+
window.set_default_size(400, 600)
|
44
|
+
|
45
|
+
box = Gtk::Box.new(:vertical)
|
46
|
+
box.margin_start = 12
|
47
|
+
box.margin_end = 12
|
48
|
+
box.margin_top = 12
|
49
|
+
box.margin_bottom = 12
|
50
|
+
box.spacing = 6
|
51
|
+
window.child = box
|
52
|
+
|
53
|
+
gl_area = Gtk::GLArea.new
|
54
|
+
gl_area.hexpand = true
|
55
|
+
gl_area.vexpand = true
|
56
|
+
gl_area.set_size_request(100, 200)
|
57
|
+
box.append(gl_area)
|
58
|
+
|
59
|
+
# We need to initialize and free GL resources, so we use the
|
60
|
+
# realize and unrealize signals on the widget
|
61
|
+
gl_area.signal_connect("realize") do
|
62
|
+
realize(gl_area)
|
63
|
+
end
|
64
|
+
gl_area.signal_connect("unrealize") do
|
65
|
+
unrealize(gl_area)
|
66
|
+
end
|
67
|
+
|
68
|
+
# The main "draw" call for GtkGLArea
|
69
|
+
gl_area.signal_connect("render") do
|
70
|
+
render(gl_area)
|
71
|
+
end
|
72
|
+
|
73
|
+
controls = Gtk::Box.new(:vertical)
|
74
|
+
controls.hexpand = true
|
75
|
+
box.append(controls)
|
76
|
+
|
77
|
+
N_AXISES.times do |i|
|
78
|
+
controls.append(create_axis_slider(gl_area, i))
|
79
|
+
end
|
80
|
+
|
81
|
+
button = Gtk::Button.new(label: "Quit")
|
82
|
+
button.hexpand = true
|
83
|
+
box.append(button)
|
84
|
+
|
85
|
+
button.signal_connect("clicked") do
|
86
|
+
window.destroy
|
87
|
+
end
|
88
|
+
|
89
|
+
window.present
|
90
|
+
|
91
|
+
window
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
# We need to set up our state when we realize the GtkGLArea widget
|
96
|
+
def realize(gl_area)
|
97
|
+
@rotation_angles = [0.0] * N_AXISES
|
98
|
+
|
99
|
+
GL.load_lib
|
100
|
+
|
101
|
+
gl_area.make_current
|
102
|
+
api_name = gl_area.api.nick
|
103
|
+
vertex_path = "/glarea/glarea-#{api_name}.vs.glsl"
|
104
|
+
fragment_path = "/glarea/glarea-#{api_name}.fs.glsl"
|
105
|
+
init_buffers
|
106
|
+
init_shaders(vertex_path, fragment_path)
|
107
|
+
end
|
108
|
+
|
109
|
+
# The object we are drawing
|
110
|
+
VERTEX_DATA = [
|
111
|
+
0.0, 0.5, 0.0, 1.0,
|
112
|
+
0.5, -0.366, 0.0, 1.0,
|
113
|
+
-0.5, -0.366, 0.0, 1.0,
|
114
|
+
]
|
115
|
+
|
116
|
+
def init_buffers
|
117
|
+
# We only use one VAO, so we always keep it bound
|
118
|
+
vao_buffer = " " * Fiddle::SIZEOF_UINT32_T
|
119
|
+
GL.GenVertexArrays(1, vao_buffer)
|
120
|
+
@vao = vao_buffer.unpack1("L")
|
121
|
+
GL.BindVertexArray(@vao)
|
122
|
+
|
123
|
+
# This is the buffer that holds the vertices
|
124
|
+
position_buffer = " " * Fiddle::SIZEOF_UINT32_T
|
125
|
+
GL.GenBuffers(1, position_buffer)
|
126
|
+
GL.GenBuffers(1, position_buffer)
|
127
|
+
@position = position_buffer.unpack1("L")
|
128
|
+
GL.BindBuffer(GL::ARRAY_BUFFER, @position)
|
129
|
+
GL.BufferData(GL::ARRAY_BUFFER,
|
130
|
+
Fiddle::SIZEOF_FLOAT * VERTEX_DATA.size,
|
131
|
+
VERTEX_DATA.pack("f*"),
|
132
|
+
GL::STATIC_DRAW)
|
133
|
+
GL.BindBuffer(GL::ARRAY_BUFFER, 0)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Initialize the shaders and link them into a program
|
137
|
+
def init_shaders(vertex_path, fragment_path)
|
138
|
+
@program = nil
|
139
|
+
vertex = 0
|
140
|
+
fragment = 0
|
141
|
+
|
142
|
+
begin
|
143
|
+
source = Gio::Resources.lookup_data(vertex_path)
|
144
|
+
vertex = create_shader(GL::VERTEX_SHADER, source.to_s)
|
145
|
+
return if vertex.zero?
|
146
|
+
|
147
|
+
source = Gio::Resources.lookup_data(fragment_path)
|
148
|
+
fragment = create_shader(GL::FRAGMENT_SHADER, source.to_s)
|
149
|
+
return if fragment.zero?
|
150
|
+
|
151
|
+
@program = GL.CreateProgram
|
152
|
+
return if @program.zero?
|
153
|
+
|
154
|
+
GL.AttachShader(@program, vertex)
|
155
|
+
GL.AttachShader(@program, fragment)
|
156
|
+
|
157
|
+
GL.LinkProgram(@program)
|
158
|
+
|
159
|
+
program_ok_buffer = " " * Fiddle::SIZEOF_UINT32_T
|
160
|
+
GL.GetProgramiv(@program, GL::LINK_STATUS, program_ok_buffer)
|
161
|
+
program_ok = (program_ok_buffer.unpack1("L") == GL::TRUE)
|
162
|
+
unless program_ok
|
163
|
+
log_length_buffer = " " * Fiddle::SIZEOF_UINT32_T
|
164
|
+
GL.GetProgramiv(@program, GL::INFO_LOG_LENGTH, log_length_buffer)
|
165
|
+
log_length = log_length_buffer.unpack1("L")
|
166
|
+
log = " " * log_length
|
167
|
+
GL.GetProgramInfoLog(@program, log_length, nil, log)
|
168
|
+
$stderr.puts("Linking failure:")
|
169
|
+
$stderr.puts(log)
|
170
|
+
GL.DeleteProgram(@program)
|
171
|
+
@program = nil
|
172
|
+
return
|
173
|
+
end
|
174
|
+
|
175
|
+
# Get the location of the "mvp" uniform
|
176
|
+
@mvp_location = GL.GetUniformLocation(@program, "mvp")
|
177
|
+
|
178
|
+
GL.DetachShader(@program, vertex)
|
179
|
+
GL.DetachShader(@program, fragment)
|
180
|
+
ensure
|
181
|
+
GL.DeleteShader(vertex) if vertex.zero?
|
182
|
+
GL.DeleteShader(fragment) if fragment.zero?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Create and compile a shader
|
187
|
+
def create_shader(type, source)
|
188
|
+
shader = GL.CreateShader(type)
|
189
|
+
GL.ShaderSource(shader, 1, [source].pack("p"), nil)
|
190
|
+
GL.CompileShader(shader)
|
191
|
+
|
192
|
+
shader_ok_buffer = " " * Fiddle::SIZEOF_UINT32_T
|
193
|
+
GL.GetShaderiv(shader, GL::COMPILE_STATUS, shader_ok_buffer)
|
194
|
+
shader_ok = (shader_ok_buffer.unpack1("L") == GL::TRUE)
|
195
|
+
unless shader_ok
|
196
|
+
log_length_buffer = " " * Fiddle::SIZEOF_UINT32_t
|
197
|
+
GL.GetShaderiv(shader, GL::INFO_LOG_LENGTH, log_length_buffer)
|
198
|
+
log_length = log_length_buffer.unpack1("L")
|
199
|
+
log = " " * log_length
|
200
|
+
GL.GetShaderInfoLog(shader, log_length, nil, buffer)
|
201
|
+
type_name = (type == GL::VERTEX_SHADER ? "vertex" : "fragment")
|
202
|
+
$stderr.puts("Compile failure in #{type_name} shader:")
|
203
|
+
$stderr.puts(log)
|
204
|
+
GL.DeleteShader(shader)
|
205
|
+
return 0
|
206
|
+
end
|
207
|
+
|
208
|
+
shader
|
209
|
+
end
|
210
|
+
|
211
|
+
# We should tear down the state when unrealizing
|
212
|
+
def unrealize(gl_area)
|
213
|
+
gl_area.make_current
|
214
|
+
GL.DeleteBuffers(1, [[@position].pack("L")].pack("p"))
|
215
|
+
GL.DeleteProgram(@program)
|
216
|
+
end
|
217
|
+
|
218
|
+
def compute_mvp(phi, theta, psi)
|
219
|
+
x = phi * (Math::PI / 180.0)
|
220
|
+
y = theta * (Math::PI / 180.0)
|
221
|
+
z = psi * (Math::PI / 180.0)
|
222
|
+
c1 = Math.cos(x)
|
223
|
+
s1 = Math.sin(x)
|
224
|
+
c2 = Math.cos(y)
|
225
|
+
s2 = Math.sin(y)
|
226
|
+
c3 = Math.cos(z)
|
227
|
+
s3 = Math.sin(z)
|
228
|
+
c3c2 = c3 * c2
|
229
|
+
s3c1 = s3 * c1
|
230
|
+
c3s2s1 = c3 * s2 * s1
|
231
|
+
s3s1 = s3 * s1
|
232
|
+
c3s2c1 = c3 * s2 * c1
|
233
|
+
s3c2 = s3 * c2
|
234
|
+
c3c1 = c3 * c1
|
235
|
+
s3s2s1 = s3 * s2 * s1
|
236
|
+
c3s1 = c3 * s1
|
237
|
+
s3s2c1 = s3 * s2 * c1
|
238
|
+
c2s1 = c2 * s1
|
239
|
+
c2c1 = c2 * c1
|
240
|
+
|
241
|
+
# apply all three rotations using the three matrices:
|
242
|
+
#
|
243
|
+
# ⎡ c3 s3 0 ⎤ ⎡ c2 0 -s2 ⎤ ⎡ 1 0 0 ⎤
|
244
|
+
# ⎢ -s3 c3 0 ⎥ ⎢ 0 1 0 ⎥ ⎢ 0 c1 s1 ⎥
|
245
|
+
# ⎣ 0 0 1 ⎦ ⎣ s2 0 c2 ⎦ ⎣ 0 -s1 c1 ⎦
|
246
|
+
mvp = [0.0] * 16
|
247
|
+
mvp[0] = c3c2; mvp[4] = s3c1 + c3s2s1; mvp[8] = s3s1 - c3s2c1; mvp[12] = 0.0;
|
248
|
+
mvp[1] = -s3c2; mvp[5] = c3c1 - s3s2s1; mvp[9] = c3s1 + s3s2c1; mvp[13] = 0.0;
|
249
|
+
mvp[2] = s2; mvp[6] = -c2s1; mvp[10] = c2c1; mvp[14] = 0.0;
|
250
|
+
mvp[3] = 0.0; mvp[7] = 0.0; mvp[11] = 0.0; mvp[15] = 1.0;
|
251
|
+
mvp
|
252
|
+
end
|
253
|
+
|
254
|
+
def draw_triangle
|
255
|
+
# Compute the model view projection matrix using the
|
256
|
+
# rotation angles specified through the GtkRange widgets
|
257
|
+
mvp = compute_mvp(@rotation_angles[X_AXIS],
|
258
|
+
@rotation_angles[Y_AXIS],
|
259
|
+
@rotation_angles[Z_AXIS])
|
260
|
+
|
261
|
+
# Use our shaders
|
262
|
+
GL.UseProgram(@program)
|
263
|
+
|
264
|
+
# Update the "mvp" matrix we use in the shader
|
265
|
+
GL.UniformMatrix4fv(@mvp_location, 1, GL::FALSE, mvp.pack("f*"))
|
266
|
+
|
267
|
+
# Use the vertices in our buffer
|
268
|
+
GL.BindBuffer(GL::ARRAY_BUFFER, @position)
|
269
|
+
GL.EnableVertexAttribArray(0)
|
270
|
+
GL.VertexAttribPointer(0, 4, GL::FLOAT, GL::FALSE, 0, 0)
|
271
|
+
|
272
|
+
# Draw the three vertices as a triangle
|
273
|
+
GL.DrawArrays(GL::TRIANGLES, 0, 3)
|
274
|
+
|
275
|
+
# We finished using the buffers and program
|
276
|
+
GL.DisableVertexAttribArray(0)
|
277
|
+
GL.BindBuffer(GL::ARRAY_BUFFER, 0)
|
278
|
+
GL.UseProgram(0)
|
279
|
+
end
|
280
|
+
|
281
|
+
def render(gl_area)
|
282
|
+
# Clear the viewport
|
283
|
+
GL.ClearColor(0.5, 0.5, 0.5, 1.0)
|
284
|
+
GL.Clear(GL::COLOR_BUFFER_BIT)
|
285
|
+
|
286
|
+
# Draw our object
|
287
|
+
draw_triangle
|
288
|
+
|
289
|
+
# Flush the contents of the pipeline
|
290
|
+
GL.Flush
|
291
|
+
|
292
|
+
true
|
293
|
+
end
|
294
|
+
|
295
|
+
def create_axis_slider(gl_area, axis)
|
296
|
+
box = Gtk::Box.new(:horizontal)
|
297
|
+
case axis
|
298
|
+
when X_AXIS
|
299
|
+
text = "X axis"
|
300
|
+
when Y_AXIS
|
301
|
+
text = "Y axis"
|
302
|
+
when Z_AXIS
|
303
|
+
text = "Z axis"
|
304
|
+
end
|
305
|
+
label = Gtk::Label.new(text)
|
306
|
+
box.append(label)
|
307
|
+
|
308
|
+
adjustment = Gtk::Adjustment.new(0.0, 0.0, 360.0, 1.0, 12.0, 0.0)
|
309
|
+
adjustment.signal_connect("value-changed") do
|
310
|
+
@rotation_angles[axis] = adjustment.value
|
311
|
+
# Update the contents of the GL drawing area
|
312
|
+
gl_area.queue_draw
|
313
|
+
end
|
314
|
+
|
315
|
+
slider = Gtk::Scale.new(:horizontal, adjustment)
|
316
|
+
box.append(slider)
|
317
|
+
slider.hexpand = true
|
318
|
+
|
319
|
+
box
|
320
|
+
end
|
321
|
+
|
322
|
+
GtkDemo.register(new)
|
323
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<interface>
|
3
|
+
<template class="GtkListItem">
|
4
|
+
<property name="focusable">0</property>
|
5
|
+
<property name="child">
|
6
|
+
<object class="GtkTreeExpander" id="expander">
|
7
|
+
<binding name="list-row">
|
8
|
+
<lookup name="item">GtkListItem</lookup>
|
9
|
+
</binding>
|
10
|
+
<property name="child">
|
11
|
+
<object class="GtkInscription">
|
12
|
+
<property name="hexpand">1</property>
|
13
|
+
<property name="nat-chars">25</property>
|
14
|
+
<property name="text-overflow">ellipsize-end</property>
|
15
|
+
<binding name="text">
|
16
|
+
<lookup name="title" type="GtkDemo">
|
17
|
+
<lookup name="item">expander</lookup>
|
18
|
+
</lookup>
|
19
|
+
</binding>
|
20
|
+
</object>
|
21
|
+
</property>
|
22
|
+
</object>
|
23
|
+
</property>
|
24
|
+
</template>
|
25
|
+
</interface>
|
@@ -0,0 +1,264 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (C) 2024 Ruby-GNOME Project Team
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
# Original:
|
20
|
+
# https://gitlab.gnome.org/GNOME/gtk/-/blob/main/demos/gtk-demo/main.c
|
21
|
+
#
|
22
|
+
# See the following for license information:
|
23
|
+
# * https://gitlab.gnome.org/GNOME/gtk/-/blob/main/AUTHORS
|
24
|
+
# * https://gitlab.gnome.org/GNOME/gtk/-/blob/main/COPYING
|
25
|
+
|
26
|
+
require "gtk4"
|
27
|
+
|
28
|
+
gresource = File.join(__dir__, "demo.gresource")
|
29
|
+
gresource_xml = "#{gresource}.xml"
|
30
|
+
need_generate = false
|
31
|
+
if File.exist?(gresource)
|
32
|
+
need_generate = File.mtime(gresource_xml) > File.mtime(gresource)
|
33
|
+
else
|
34
|
+
need_generate = true
|
35
|
+
end
|
36
|
+
if need_generate
|
37
|
+
unless system("glib-compile-resources",
|
38
|
+
"--target", gresource,
|
39
|
+
"--sourcedir", __dir__,
|
40
|
+
gresource_xml)
|
41
|
+
$stderr.puts("Failed to compile resource")
|
42
|
+
exit(false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
resource = Gio::Resource.load(gresource)
|
46
|
+
Gio::Resources.register(resource)
|
47
|
+
|
48
|
+
|
49
|
+
require_relative "demo"
|
50
|
+
Dir.glob("#{__dir__}/*.rb") do |rb|
|
51
|
+
base_name = File.basename(rb, ".rb")
|
52
|
+
next if base_name == "main"
|
53
|
+
require_relative base_name
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def run_demo(demo, window)
|
58
|
+
demo_window = demo.run(window)
|
59
|
+
demo_window.transient_for = window
|
60
|
+
demo_window.modal = true
|
61
|
+
demo_window
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_demo_model(window)
|
65
|
+
store = Gio::ListStore.new(GtkDemo)
|
66
|
+
GtkDemo.all.each do |demo|
|
67
|
+
store.append(demo)
|
68
|
+
end
|
69
|
+
store
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_file(demo, notebook, info_view)
|
73
|
+
notebook.n_pages.downto(1) do |i|
|
74
|
+
notebook.remove_page(i)
|
75
|
+
end
|
76
|
+
|
77
|
+
info_buffer = Gtk::TextBuffer.new
|
78
|
+
info_buffer.create_tag("title",
|
79
|
+
size: 18 * Pango::SCALE,
|
80
|
+
pixels_below_lines: 10)
|
81
|
+
info_buffer.begin_irreversible_action do
|
82
|
+
info_buffer_iter = info_buffer.get_iter_at(offset: 0)
|
83
|
+
info_buffer.insert(info_buffer_iter, demo.title, tags: ["title"])
|
84
|
+
info_buffer.insert(info_buffer_iter, "\n")
|
85
|
+
info_buffer.insert(info_buffer_iter, demo.description)
|
86
|
+
end
|
87
|
+
info_view.buffer = info_buffer
|
88
|
+
end
|
89
|
+
|
90
|
+
def select_item(selection, window, notebook, info_view)
|
91
|
+
row = selection.selected_item
|
92
|
+
notebook.sensitive = !!row
|
93
|
+
if row
|
94
|
+
demo = row.item
|
95
|
+
load_file(demo, notebook, info_view)
|
96
|
+
run_action = window.lookup_action("run")
|
97
|
+
run_action.enabled = true
|
98
|
+
window.title = demo.title
|
99
|
+
else
|
100
|
+
window.title = "No match"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
app = Gtk::Application.new("com.github.ruby-gnome.gtk4.Demo",
|
106
|
+
[:non_unique, :handles_command_line])
|
107
|
+
|
108
|
+
about_action = Gio::SimpleAction.new("about")
|
109
|
+
about_action.signal_connect("activate") do
|
110
|
+
dialog = Gtk::AboutDialog.new
|
111
|
+
dialog.modal = true
|
112
|
+
dialog.transient_for = app.active_window
|
113
|
+
dialog.destroy_with_parent = true
|
114
|
+
dialog.program_name = "Ruby/GTK4 Demo"
|
115
|
+
dialog.version = GLib::BINDING_VERSION.join(".")
|
116
|
+
dialog.copyright = "(C) 2024-#{Time.now.year} Ruby-GNOME Project Team"
|
117
|
+
dialog.license_type = Gtk::License::LGPL_2_1
|
118
|
+
dialog.website = "https://ruby-gnome.github.io/"
|
119
|
+
dialog.comments = "Program to demonstrate Ruby/GTK4 widgets"
|
120
|
+
dialog.authors = ["Ruby-GNOME Project Team"]
|
121
|
+
dialog.logo_icon_name = "org.gtk.Demo4"
|
122
|
+
dialog.title = "About Ruby/GTK4 Demo"
|
123
|
+
dialog.system_information = <<-INFO.chomp
|
124
|
+
OS\t#{GLib.get_os_info(:name)} #{GLib.get_os_info(:version)}
|
125
|
+
|
126
|
+
System Libraries
|
127
|
+
\tGLib\t#{GLib::Version::STRING}
|
128
|
+
\tPango\t#{Pango::Version::STRING}
|
129
|
+
\tGTK\t#{Gtk::Version::STRING}
|
130
|
+
|
131
|
+
A link can appear here: <https://ruby-gnome.github.io/>
|
132
|
+
INFO
|
133
|
+
dialog.present
|
134
|
+
end
|
135
|
+
app.add_action(about_action)
|
136
|
+
|
137
|
+
quit_action = Gio::SimpleAction.new("quit")
|
138
|
+
quit_action.signal_connect("activate") do
|
139
|
+
app.windows.each do |window|
|
140
|
+
window.destroy
|
141
|
+
end
|
142
|
+
end
|
143
|
+
app.add_action(quit_action)
|
144
|
+
|
145
|
+
inspector_action = Gio::SimpleAction.new("inspector")
|
146
|
+
inspector_action.signal_connect("activate") do
|
147
|
+
Gtk::Window.interactive_debugging = true
|
148
|
+
end
|
149
|
+
app.add_action(inspector_action)
|
150
|
+
app.set_accels_for_action("app.about", ["F1"])
|
151
|
+
app.set_accels_for_action("app.quit", ["<Control>q"])
|
152
|
+
|
153
|
+
app.add_main_option("version",
|
154
|
+
0,
|
155
|
+
GLib::OptionFlags::NONE,
|
156
|
+
GLib::OptionArg::NONE,
|
157
|
+
"Show program version")
|
158
|
+
app.add_main_option("run",
|
159
|
+
0,
|
160
|
+
GLib::OptionFlags::NONE,
|
161
|
+
GLib::OptionArg::STRING,
|
162
|
+
"Run an example",
|
163
|
+
"EXAMPLE")
|
164
|
+
app.add_main_option("list",
|
165
|
+
0,
|
166
|
+
GLib::OptionFlags::NONE,
|
167
|
+
GLib::OptionArg::NONE,
|
168
|
+
"List examples")
|
169
|
+
app.add_main_option("auto-quit",
|
170
|
+
0,
|
171
|
+
GLib::OptionFlags::NONE,
|
172
|
+
GLib::OptionArg::NONE,
|
173
|
+
"Quit after a delay")
|
174
|
+
run = nil
|
175
|
+
list = false
|
176
|
+
auto_quit = false
|
177
|
+
app.signal_connect("command-line") do |_app, command_line|
|
178
|
+
run = command_line.options_dict.lookup("run", "s")
|
179
|
+
list = command_line.options_dict.lookup("list", "b")
|
180
|
+
auto_quit = command_line.options_dict.lookup("auto-quit", "b")
|
181
|
+
app.activate
|
182
|
+
0
|
183
|
+
end
|
184
|
+
|
185
|
+
app.signal_connect("handle-local-options") do |_app, options|
|
186
|
+
if options.lookup("version", "b")
|
187
|
+
puts("ruby-gtk4-demo: #{GLib::BINDING_VERSION.join(".")}")
|
188
|
+
0
|
189
|
+
else
|
190
|
+
-1
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
app.signal_connect("activate") do |_app|
|
195
|
+
builder = Gtk::Builder.new(resource: "/ui/main.ui")
|
196
|
+
window = builder["window"]
|
197
|
+
app.add_window(window)
|
198
|
+
|
199
|
+
run_action = Gio::SimpleAction.new("run")
|
200
|
+
run_action.signal_connect("activate") do
|
201
|
+
row = selection.selected_item
|
202
|
+
demo = row.item
|
203
|
+
run_demo(demo, window)
|
204
|
+
end
|
205
|
+
window.add_action(run_action)
|
206
|
+
|
207
|
+
notebook = builder["notebook"]
|
208
|
+
|
209
|
+
info_view = builder["info-textview"]
|
210
|
+
source_view = builder["source-textview"]
|
211
|
+
list_view = builder["listview"]
|
212
|
+
list_view.signal_connect("activate") do |_, position|
|
213
|
+
model = list_view.model
|
214
|
+
row = model.get_item(position)
|
215
|
+
run_demo(row.item, window)
|
216
|
+
end
|
217
|
+
search_bar = builder["searchbar"]
|
218
|
+
search_bar.signal_connect("notify::search-mode-enabled") do
|
219
|
+
unless search_bar.search_mode?
|
220
|
+
bar.child.text = ""
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
list_model = create_demo_model(window)
|
225
|
+
tree_model = Gtk::TreeListModel.new(list_model, false, true) do |item|
|
226
|
+
item.children_model
|
227
|
+
end
|
228
|
+
filter_model = Gtk::FilterListModel.new(tree_model, nil)
|
229
|
+
filter = Gtk::CustomFilter.new do
|
230
|
+
# TODO
|
231
|
+
true
|
232
|
+
end
|
233
|
+
filter_model.filter = filter
|
234
|
+
|
235
|
+
search_entry = builder["search-entry"]
|
236
|
+
|
237
|
+
selection = Gtk::SingleSelection.new(filter_model)
|
238
|
+
selection.signal_connect("notify::selected-item") do
|
239
|
+
select_item(selection, window, notebook, info_view)
|
240
|
+
end
|
241
|
+
list_view.model = selection
|
242
|
+
select_item(selection, window, notebook, info_view)
|
243
|
+
|
244
|
+
target_demo = nil
|
245
|
+
if run
|
246
|
+
GtkDemo.each do |demo|
|
247
|
+
if demo.name == run
|
248
|
+
target_demo = demo
|
249
|
+
break
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
if target_demo
|
254
|
+
demo_window = run_demo(target_demo, window)
|
255
|
+
# TODO: This doesn't work
|
256
|
+
demo_window.signal_connect("destroy") do
|
257
|
+
app.quit
|
258
|
+
end
|
259
|
+
else
|
260
|
+
window.present
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
app.run([$0] + ARGV)
|