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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
# frozen_string_literal: true
require 'mui/gtk_postbox'
module Plugin::Gtk3
# 投稿ボックスとスクロール可能のリストビューを備えたウィジェット
class Timeline < Gtk::Grid
class << self
attr_accessor :current
def update_rows(model)
@instances.each do |instance|
instance.bulk_add([model]) if instance.include?(model)
end
end
def remove_rows(models)
@instances.each do |instance|
instance.bulk_remove(models)
end
end
def miracle_painters_of(message)
Enumerator.new do |yielder|
@instances.each do |instance|
detect = instance.find_miracle_painter_by_message(message)
yielder << detect if detect
end
end
end
def new(*)
instance = super
(@instances ||= []).push(instance)
@current ||= instance
instance
end
end
type_register
attr_reader :postbox, :order, :imaginary
def initialize(imaginary=nil)
super()
self.name = 'timeline'
self.orientation = :vertical
@uri_mp_dict = {} # { String (model uri) => MiraclePainter }
@imaginary = imaginary
@order = ->(m) { m.modified.to_i }
@postbox = Gtk::Grid.new.tap do |grid|
grid.orientation = :vertical
end
@listbox = Gtk::ListBox.new.tap do |listbox|
listbox.selection_mode = :multiple
listbox.activate_on_single_click = false
listbox.set_sort_func do |a, b|
@order.(b.model) <=> @order.(a.model)
end
end
ssc(:destroy) do
@imaginary.destroy
end
add @postbox
add(Gtk::ScrolledWindow.new.tap do |sw|
sw.set_policy :never, :automatic
sw.overlay_scrolling = false
sw.expand = true
sw.add @listbox
end)
end
def order=(order)
@order = order
@listbox.invalidate_sort
end
def include?(message)
@uri_mp_dict.key?(message.uri.to_s)
end
def active
@imaginary.active!(true, true)
if self.class.current && self.class.current != self && !self.class.current.destroyed?
self.class.current.unselect_all
end
self.class.current = self
end
def keypress(keyname)
Plugin::GUI.keypress keyname, @imaginary
end
def bulk_add(models)
update_ordinal = false
filtered_models = Plugin.filtering(:show_filter, [*models]).first
filtered_models.each do |message_or_share|
message = message_or_share.retweet_source || message_or_share
mp = find_miracle_painter_by_message(message)
if mp
update_ordinal |= @order.(mp.model) != @order.(message)
mp.message = message
else
row = MiraclePainter.new(message)
row.show_all
@listbox.add(row)
@uri_mp_dict[message.uri.to_s] = row
end
end
@listbox.invalidate_sort if update_ordinal
overflow = @listbox.children[1000..]
bulk_remove(overflow.map(&:message)) if overflow
end
def bulk_remove(messages)
messages.map { |message|
@uri_mp_dict.delete(message.uri.to_s)
}.to_a.compact.each(&@listbox.method(:remove))
end
def clear
raise NotImplementedError
end
def size
@uri_mp_dict.size
end
def select_row(row)
@listbox.select_row(row) unless selected_rows.include?(row)
end
def select_row_at_index(index)
unselect_all
@listbox.select_row @listbox.get_row_at_index index
end
def unselect_all
selected_rows.each do |row|
@listbox.unselect_row row
end
end
def jump_to(to)
case to
when :top
@listbox.adjustment.value = @listbox.adjustment.lower
select_row_at_index(0)
when :up
@listbox.adjustment.value -= @listbox.adjustment.page_increment
when :down
@listbox.adjustment.value += @listbox.adjustment.page_increment
end
end
def selected_rows
@listbox.selected_rows
end
def popup_menu(event)
menu = Gtk::Menu.new
menu.attach_to_widget self
ev, menus = Plugin::GUI::Command.get_menu_items @imaginary
Gtk::ContextMenu.new(*menus).build!(self, ev, menu = menu)
menu.show_all
menu.popup_at_pointer event
# TODO: gtk3 参照を保持しておかないとGCされてしまう
@menu = menu
end
def add_postbox(i_postbox)
# ずっと表示される(投稿しても消えない)PostBoxの処理
# 既にprocっぽいものが入っているときはそのままにしておく
options = i_postbox.options.dup
if options[:delegate_other] && !options[:delegate_other].respond_to?(:to_proc)
i_timeline = i_postbox.ancestor_of(Plugin::GUI::Timeline)
options[:delegate_other] = postbox_delegation_generator(i_timeline)
options[:postboxstorage] = postbox
end
create_postbox(options)
end
def find_miracle_painter_by_message(message)
@uri_mp_dict[message.uri.to_s]
end
private
def create_postbox(options, &block)
options = options.dup
options[:before_post_hook] = ->(_this) {
get_ancestor(Gtk::Window).set_focus(self) unless destroyed?
}
pb = Gtk::PostBox.new(**options).show_all
postbox << pb
pb.on_delete(&block) if block
get_ancestor(Gtk::Window).set_focus(pb.post)
pb
end
def postbox_delegation_generator(i_timeline)
->(params) { i_timeline.create_postbox(params) }
end
end
end
|