| ... |
... |
@@ -21,6 +21,7 @@ from collections import OrderedDict |
|
21
|
21
|
from gi.repository import Gtk
|
|
22
|
22
|
from gi.repository import GLib
|
|
23
|
23
|
from gi.repository import GdkPixbuf
|
|
|
24
|
+from gi.repository import Pango
|
|
24
|
25
|
|
|
25
|
26
|
from gajim.common import app
|
|
26
|
27
|
from gajim.common import helpers
|
| ... |
... |
@@ -38,8 +39,9 @@ log = logging.getLogger('gajim.emoji') |
|
38
|
39
|
|
|
39
|
40
|
|
|
40
|
41
|
class Section(Gtk.Box):
|
|
41
|
|
- def __init__(self, name, search_entry, press_cb):
|
|
|
42
|
+ def __init__(self, name, search_entry, press_cb, chooser):
|
|
42
|
43
|
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
|
|
|
44
|
+ self._chooser = chooser
|
|
43
|
45
|
self._press_cb = press_cb
|
|
44
|
46
|
self.pixbuf_generator = None
|
|
45
|
47
|
self.heading = Gtk.Label(label=name)
|
| ... |
... |
@@ -83,22 +85,44 @@ class Section(Gtk.Box): |
|
83
|
85
|
emoji_pixbufs[codepoint] = pixbuf
|
|
84
|
86
|
else:
|
|
85
|
87
|
if pixbuf is not None:
|
|
86
|
|
- chooser = self._get_emoji_modifier(
|
|
87
|
|
- codepoint, pixbuf, attrs)
|
|
88
|
|
- chooser.flowbox.connect(
|
|
89
|
|
- 'child-activated', self._press_cb)
|
|
|
88
|
+ chooser = ModifierChooser()
|
|
|
89
|
+
|
|
|
90
|
+ # Iterate over the variations and add the codepoints
|
|
|
91
|
+ for codepoint_ in variations.keys():
|
|
|
92
|
+ pixbuf_ = self._get_next_pixbuf()
|
|
|
93
|
+ if pixbuf_ is None:
|
|
|
94
|
+ continue
|
|
|
95
|
+
|
|
|
96
|
+ if pixbuf_ == 'font':
|
|
|
97
|
+ if not self._chooser._font_supports_codepoint(
|
|
|
98
|
+ codepoint_):
|
|
|
99
|
+ continue
|
|
|
100
|
+ else:
|
|
|
101
|
+ emoji_pixbufs[codepoint_] = pixbuf_
|
|
|
102
|
+
|
|
|
103
|
+ # Only codepoints are added which the
|
|
|
104
|
+ # font or theme supports
|
|
|
105
|
+ chooser.add_emoji(codepoint_, pixbuf_)
|
|
|
106
|
+
|
|
|
107
|
+ # Check if we successfully added codepoints with modifiers
|
|
|
108
|
+ if chooser.has_child:
|
|
|
109
|
+ # If we have children then add a button
|
|
|
110
|
+ # and set the popover
|
|
|
111
|
+ child = EmojiModifierChild(
|
|
|
112
|
+ codepoint, pixbuf, attrs['desc'])
|
|
|
113
|
+ child.button.set_popover(chooser)
|
|
|
114
|
+ chooser.flowbox.connect(
|
|
|
115
|
+ 'child-activated', self._press_cb)
|
|
|
116
|
+ else:
|
|
|
117
|
+ # If no children were added, destroy the chooser
|
|
|
118
|
+ # and add a EmojiChild instead of a EmojiModifierChild
|
|
|
119
|
+ chooser.destroy()
|
|
|
120
|
+ child = EmojiChild(codepoint, pixbuf, attrs['desc'])
|
|
90
|
121
|
|
|
91
|
122
|
if pixbuf != 'font':
|
|
92
|
123
|
emoji_pixbufs[codepoint] = pixbuf
|
|
93
|
124
|
|
|
94
|
|
- for codepoint, attrs in variations.items():
|
|
95
|
|
- pixbuf = self._get_next_pixbuf()
|
|
96
|
|
- if pixbuf is None:
|
|
97
|
|
- continue
|
|
98
|
|
- chooser.add_emoji(codepoint, pixbuf)
|
|
99
|
|
-
|
|
100
|
|
- if pixbuf != 'font':
|
|
101
|
|
- emoji_pixbufs[codepoint] = pixbuf
|
|
|
125
|
+ self.flowbox.add(child)
|
|
102
|
126
|
else:
|
|
103
|
127
|
# We dont have a image for the base codepoint
|
|
104
|
128
|
# so skip all modifiers of it
|
| ... |
... |
@@ -112,13 +136,6 @@ class Section(Gtk.Box): |
|
112
|
136
|
emoji.destroy()
|
|
113
|
137
|
self.flowbox.foreach(_remove_emoji)
|
|
114
|
138
|
|
|
115
|
|
- def _get_emoji_modifier(self, codepoint, pixbuf, attrs):
|
|
116
|
|
- chooser = ModifierChooser()
|
|
117
|
|
- modifier_button = EmojiModifierChild(codepoint, pixbuf, attrs['desc'])
|
|
118
|
|
- modifier_button.button.set_popover(chooser)
|
|
119
|
|
- self.flowbox.add(modifier_button)
|
|
120
|
|
- return chooser
|
|
121
|
|
-
|
|
122
|
139
|
def _get_next_pixbuf(self):
|
|
123
|
140
|
if self.pixbuf_generator is None:
|
|
124
|
141
|
return 'font'
|
| ... |
... |
@@ -193,6 +210,7 @@ class ModifierChooser(Gtk.Popover): |
|
193
|
210
|
def __init__(self):
|
|
194
|
211
|
Gtk.Popover.__init__(self)
|
|
195
|
212
|
self.set_name('EmoticonPopover')
|
|
|
213
|
+ self._has_child = False
|
|
196
|
214
|
|
|
197
|
215
|
self.flowbox = Gtk.FlowBox()
|
|
198
|
216
|
self.flowbox.get_style_context().add_class(
|
| ... |
... |
@@ -202,8 +220,13 @@ class ModifierChooser(Gtk.Popover): |
|
202
|
220
|
self.flowbox.show()
|
|
203
|
221
|
self.add(self.flowbox)
|
|
204
|
222
|
|
|
|
223
|
+ @property
|
|
|
224
|
+ def has_child(self):
|
|
|
225
|
+ return self._has_child
|
|
|
226
|
+
|
|
205
|
227
|
def add_emoji(self, codepoint, pixbuf):
|
|
206
|
228
|
self.flowbox.add(EmojiChild(codepoint, pixbuf, None))
|
|
|
229
|
+ self._has_child = True
|
|
207
|
230
|
|
|
208
|
231
|
|
|
209
|
232
|
class EmojiChooser(Gtk.Popover):
|
| ... |
... |
@@ -224,6 +247,7 @@ class EmojiChooser(Gtk.Popover): |
|
224
|
247
|
self.set_name('EmoticonPopover')
|
|
225
|
248
|
self._text_widget = None
|
|
226
|
249
|
self._load_source_id = None
|
|
|
250
|
+ self._pango_layout = Pango.Layout(self.get_pango_context())
|
|
227
|
251
|
|
|
228
|
252
|
self._builder = get_builder('emoji_chooser.ui')
|
|
229
|
253
|
self._search = self._builder.get_object('search')
|
| ... |
... |
@@ -232,7 +256,7 @@ class EmojiChooser(Gtk.Popover): |
|
232
|
256
|
self._sections = OrderedDict()
|
|
233
|
257
|
for name in self._section_names:
|
|
234
|
258
|
self._sections[name] = Section(
|
|
235
|
|
- name, self._search, self._on_emoticon_press)
|
|
|
259
|
+ name, self._search, self._on_emoticon_press, self)
|
|
236
|
260
|
|
|
237
|
261
|
section_box = self._builder.get_object('section_box')
|
|
238
|
262
|
for section in self._sections.values():
|
| ... |
... |
@@ -326,21 +350,35 @@ class EmojiChooser(Gtk.Popover): |
|
326
|
350
|
self._clear_sections()
|
|
327
|
351
|
emoji_pixbufs.clear()
|
|
328
|
352
|
|
|
329
|
|
- factory = self._emoji_factory()
|
|
|
353
|
+ factory = self._emoji_factory(theme == 'font')
|
|
330
|
354
|
self._load_source_id = GLib.idle_add(lambda: next(factory, False),
|
|
331
|
355
|
priority=GLib.PRIORITY_LOW)
|
|
332
|
356
|
|
|
333
|
|
- def _emoji_factory(self):
|
|
|
357
|
+ def _emoji_factory(self, font):
|
|
334
|
358
|
for codepoint, attrs in emoji_data.items():
|
|
335
|
359
|
if not attrs['fully-qualified']:
|
|
336
|
360
|
# We dont add these to the UI
|
|
337
|
361
|
continue
|
|
338
|
362
|
|
|
|
363
|
+ if font and not self._font_supports_codepoint(codepoint):
|
|
|
364
|
+ continue
|
|
|
365
|
+
|
|
339
|
366
|
section = self._sections[attrs['group']]
|
|
340
|
367
|
yield section.add_emoji(codepoint, attrs)
|
|
341
|
368
|
self._load_source_id = None
|
|
342
|
369
|
emoji_pixbufs.complete = True
|
|
343
|
370
|
|
|
|
371
|
+ def _font_supports_codepoint(self, codepoint):
|
|
|
372
|
+ self._pango_layout.set_text(codepoint, -1)
|
|
|
373
|
+ if self._pango_layout.get_unknown_glyphs_count():
|
|
|
374
|
+ return False
|
|
|
375
|
+ if len(codepoint) > 1:
|
|
|
376
|
+ # The font supports each of the codepoints
|
|
|
377
|
+ # Check if the rendered glyph is more than one char
|
|
|
378
|
+ if self._pango_layout.get_size()[0] > 19000:
|
|
|
379
|
+ return False
|
|
|
380
|
+ return True
|
|
|
381
|
+
|
|
344
|
382
|
def _get_next_pixbuf(self, path):
|
|
345
|
383
|
src_x = src_y = cur_column = 0
|
|
346
|
384
|
atlas = GdkPixbuf.Pixbuf.new_from_file(path)
|