1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
# -*- coding: utf-8 -*-
module Plugin::Openimg
class Window < Gtk::Window
attr_reader :photo
def initialize(photo, next_opener)
super()
@photo = photo
@image_surface = loading_surface
@next_opener = next_opener
window_settings
ssc(:destroy, &:destroy)
end
def start_loading
Thread.new {
Plugin.filtering(:openimg_pixbuf_from_display_url, photo, nil, nil)
}.next { |_, pixbufloader, complete_promise|
if pixbufloader.is_a? Gdk::PixbufLoader
rect = nil
pixbufloader.ssc(:area_updated, self) do |_, x, y, width, height|
if rect
rect[:left] = [rect[:left], x].min
rect[:top] = [rect[:top], y].min
rect[:right] = [rect[:right], x+width].max
rect[:bottom] = [rect[:bottom], y+height].max
else
rect = {left: x, top: y, right: x+width, bottom: y+height}
Delayer.new do
progress(pixbufloader.pixbuf,
x: rect[:left],
y: rect[:top],
width: rect[:right] - rect[:left],
height: rect[:bottom] - rect[:top])
rect = nil
end
end
true end
complete_promise.next{
progress(pixbufloader.pixbuf, paint: true)
pixbufloader.close
}.trap { |exception|
error exception
@image_surface = error_surface
redraw(repaint: true)
}
else
warn "cant open: #{photo}"
@image_surface = error_surface
redraw(repaint: true) end
}.trap{ |exception|
error exception
@image_surface = error_surface
redraw(repaint: true)
}
self
end
private
def window_settings
set_title(photo.perma_link.to_s)
set_role('mikutter_image_preview'.freeze)
set_type_hint(Gdk::Window::TYPE_HINT_DIALOG)
set_default_size(*default_size)
add(Gtk::VBox.new.closeup(w_toolbar).add(w_wrap))
end
def redraw(repaint: true)
return if w_wrap.destroyed?
gdk_window = w_wrap.window
return unless gdk_window
ew, eh = gdk_window.geometry[2,2]
return if(ew == 0 or eh == 0)
context = gdk_window.create_cairo_context
context.save do
if repaint
context.set_source_color(Cairo::Color::BLACK)
context.paint end
if (ew * @image_surface.height) > (eh * @image_surface.width)
rate = eh.to_f / @image_surface.height
context.translate((ew - @image_surface.width*rate)/2, 0)
else
rate = ew.to_f / @image_surface.width
context.translate(0, (eh - @image_surface.height*rate)/2) end
context.scale(rate, rate)
context.set_source(Cairo::SurfacePattern.new(@image_surface))
context.paint end
rescue => _
error _ end
def progress(pixbuf, x: 0, y: 0, width: 0, height: 0, paint: false)
return unless pixbuf
context = nil
size_changed = false
unless @image_surface.width == pixbuf.width and @image_surface.height == pixbuf.height
size_changed = true
@image_surface = Cairo::ImageSurface.new(pixbuf.width, pixbuf.height)
context = Cairo::Context.new(@image_surface)
context.save do
context.set_source_color(Cairo::Color::BLACK)
context.paint end end
context ||= Cairo::Context.new(@image_surface)
context.save do
context.set_source_pixbuf(pixbuf)
if paint
context.paint
else
context.rectangle(x, y, width, height)
context.fill end end
redraw(repaint: paint || size_changed)
end
#
# === Widgetたち
#
def w_wrap
@w_wrap ||= ::Gtk::DrawingArea.new.tap{|w|
w.ssc(:size_allocate, &gen_wrap_size_allocate)
w.ssc(:expose_event, &gen_wrap_expose_event)
}
end
def w_toolbar
@w_toolbar ||= ::Gtk::Toolbar.new.tap{|w| w.insert(0, w_browser) }
end
def w_browser
@w_browser ||= ::Gtk::ToolButton.new(
Gtk::Image.new(GdkPixbuf::Pixbuf.new(file: Skin.get('forward.png'), width: 24, height: 24))
).tap{|w|
w.ssc(:clicked, &gen_browser_clicked)
}
end
#
# === イベントハンドラ
#
def gen_browser_clicked
proc do
@next_opener.forward
false
end
end
def gen_wrap_expose_event
proc do |widget|
redraw(repaint: true)
true
end
end
def gen_wrap_size_allocate
last_size = nil
proc do |widget|
if widget.window && last_size != widget.window.geometry[2,2]
last_size = widget.window.geometry[2,2]
redraw(repaint: true)
end
false
end
end
#
# === その他
#
def default_size
@size || [640, 480]
end
def loading_surface
surface = Cairo::ImageSurface.from_png(Skin.get('loading.png'))
surface
end
def error_surface
surface = Cairo::ImageSurface.from_png(Skin.get('notfound.png'))
surface
end
end
end
|