Mercurial > libervia-desktop-kivy
annotate cagou/core/simple_xhtml.py @ 354:aa860c10acfc
chat: new chat selector:
Using the new ScreenManager feature, a widget to select a chat to display is shown when a
user opens the chat (except if an entity jid is specified, in which case it opens directly
the Chat widget), or when user presses ESC.
When on ChatSelector, pressing ESC brings to the root widget (i.e. default widget).
The ChatSelect is a first draft, it is planned to show opened chats, rooms, and a way to
create new chats.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 17 Jan 2020 18:44:35 +0100 |
parents | 83697218b9b2 |
children | 4d660b252487 |
rev | line source |
---|---|
22 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Cagou: desktop/mobile frontend for Salut à Toi XMPP client | |
282 | 5 # Copyright (C) 2016-2019 Jérôme Poisson (goffi@goffi.org) |
22 | 6 |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
325 | 21 from xml.etree import ElementTree as ET |
57 | 22 from kivy.uix.stacklayout import StackLayout |
23 from kivy.uix.label import Label | |
24 from kivy.utils import escape_markup | |
325 | 25 from kivy.metrics import sp, dp |
22 | 26 from kivy import properties |
325 | 27 from sat.core import log as logging |
106
9909ed7a7a20
moved SimpleXHTMLWidget to a dedicated module
Goffi <goffi@goffi.org>
parents:
105
diff
changeset
|
28 from sat_frontends.tools import css_color, strings as sat_strings |
335
597cc207c8e7
core (simple_xhtml): handle `aesgcm` schemes:
Goffi <goffi@goffi.org>
parents:
325
diff
changeset
|
29 from cagou import G |
106
9909ed7a7a20
moved SimpleXHTMLWidget to a dedicated module
Goffi <goffi@goffi.org>
parents:
105
diff
changeset
|
30 from cagou.core.image import AsyncImage |
325 | 31 from cagou.core.constants import Const as C |
32 | |
33 | |
34 log = logging.getLogger(__name__) | |
22 | 35 |
36 | |
312 | 37 class Escape(str): |
57 | 38 """Class used to mark that a message need to be escaped""" |
39 | |
40 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
41 class SimpleXHTMLWidgetEscapedText(Label): |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
42 |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
43 def on_parent(self, instance, parent): |
288
44752e8031f8
simple XHTML: fixed crash when parent is set to None + fixed bold restoration when escaped message is modified
Goffi <goffi@goffi.org>
parents:
284
diff
changeset
|
44 if parent is not None: |
44752e8031f8
simple XHTML: fixed crash when parent is set to None + fixed bold restoration when escaped message is modified
Goffi <goffi@goffi.org>
parents:
284
diff
changeset
|
45 self.font_size = parent.font_size |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
46 |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
47 def _addUrlMarkup(self, text): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
48 text_elts = [] |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
49 idx = 0 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
50 links = 0 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
51 while True: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
52 m = sat_strings.RE_URL.search(text[idx:]) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
53 if m is not None: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
54 text_elts.append(escape_markup(m.string[0:m.start()])) |
312 | 55 link_key = 'link_' + str(links) |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
56 url = m.group() |
325 | 57 escaped_url = escape_markup(url) |
58 text_elts.append( | |
59 f'[color=5500ff][ref={link_key}]{escaped_url}[/ref][/color]') | |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
60 if not links: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
61 self.ref_urls = {link_key: url} |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
62 else: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
63 self.ref_urls[link_key] = url |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
64 links += 1 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
65 idx += m.end() |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
66 else: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
67 if links: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
68 text_elts.append(escape_markup(text[idx:])) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
69 self.markup = True |
312 | 70 self.text = ''.join(text_elts) |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
71 break |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
72 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
73 def on_text(self, instance, text): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
74 # do NOT call the method if self.markup is set |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
75 # this would result in infinite loop (because self.text |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
76 # is changed if an URL is found, and in this case markup too) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
77 if text and not self.markup: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
78 self._addUrlMarkup(text) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
79 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
80 def on_ref_press(self, ref): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
81 url = self.ref_urls[ref] |
344
83697218b9b2
core: handle URLs opening in a per-platform way:
Goffi <goffi@goffi.org>
parents:
335
diff
changeset
|
82 G.local_platform.open_url(url, self) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
83 |
59 | 84 |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
85 class SimpleXHTMLWidgetText(Label): |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
86 |
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
87 def on_parent(self, instance, parent): |
325 | 88 if parent is not None: |
89 self.font_size = parent.font_size | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
90 |
59 | 91 |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
92 class SimpleXHTMLWidgetImage(AsyncImage): |
59 | 93 # following properties are desired height/width |
94 # i.e. the ones specified in height/width attributes of <img> | |
95 # (or wanted for whatever reason) | |
325 | 96 # set to None to ignore them |
97 target_height = properties.NumericProperty(allownone=True) | |
98 target_width = properties.NumericProperty(allownone=True) | |
59 | 99 |
325 | 100 def __init__(self, **kwargs): |
101 # best calculated size | |
102 self._best_width = self._best_height = 100 | |
103 super().__init__(**kwargs) | |
59 | 104 |
325 | 105 def on_texture(self, instance, texture): |
106 """Adapt the size according to max size and target_*""" | |
107 if texture is None: | |
108 return | |
109 max_width, max_height = dp(C.IMG_MAX_WIDTH), dp(C.IMG_MAX_HEIGHT) | |
110 width, height = texture.size | |
111 if self.target_width: | |
112 width = min(width, self.target_width) | |
113 if width > max_width: | |
114 width = C.IMG_MAX_WIDTH | |
59 | 115 |
325 | 116 height = width / self.image_ratio |
117 | |
118 if self.target_height: | |
119 height = min(height, self.target_height) | |
120 | |
121 if height > max_height: | |
122 height = max_height | |
123 width = height * self.image_ratio | |
124 | |
125 self.width, self.height = self._best_width, self._best_height = width, height | |
59 | 126 |
127 def on_parent(self, instance, parent): | |
128 if parent is not None: | |
325 | 129 parent.bind(width=self.on_parent_width) |
130 | |
131 def on_parent_width(self, instance, width): | |
132 if self._best_width > width: | |
133 self.width = width | |
134 self.height = width / self.image_ratio | |
135 else: | |
136 self.width, self.height = self._best_width, self._best_height | |
59 | 137 |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
138 |
57 | 139 class SimpleXHTMLWidget(StackLayout): |
140 """widget handling simple XHTML parsing""" | |
141 xhtml = properties.StringProperty() | |
142 color = properties.ListProperty([1, 1, 1, 1]) | |
143 # XXX: bold is only used for escaped text | |
144 bold = properties.BooleanProperty(False) | |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
145 font_size = properties.NumericProperty(sp(14)) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
146 |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
147 # text/XHTML input |
57 | 148 |
149 def on_xhtml(self, instance, xhtml): | |
150 """parse xhtml and set content accordingly | |
151 | |
288
44752e8031f8
simple XHTML: fixed crash when parent is set to None + fixed bold restoration when escaped message is modified
Goffi <goffi@goffi.org>
parents:
284
diff
changeset
|
152 if xhtml is an instance of Escape, a Label with no markup will be used |
57 | 153 """ |
154 self.clear_widgets() | |
155 if isinstance(xhtml, Escape): | |
288
44752e8031f8
simple XHTML: fixed crash when parent is set to None + fixed bold restoration when escaped message is modified
Goffi <goffi@goffi.org>
parents:
284
diff
changeset
|
156 label = SimpleXHTMLWidgetEscapedText( |
44752e8031f8
simple XHTML: fixed crash when parent is set to None + fixed bold restoration when escaped message is modified
Goffi <goffi@goffi.org>
parents:
284
diff
changeset
|
157 text=xhtml, color=self.color, bold=self.bold) |
325 | 158 self.bind(font_size=label.setter('font_size')) |
57 | 159 self.bind(color=label.setter('color')) |
160 self.bind(bold=label.setter('bold')) | |
161 self.add_widget(label) | |
162 else: | |
325 | 163 xhtml = ET.fromstring(xhtml.encode()) |
57 | 164 self.current_wid = None |
165 self.styles = [] | |
166 self._callParseMethod(xhtml) | |
325 | 167 if len(self.children) > 1: |
168 self._do_split_labels() | |
57 | 169 |
170 def escape(self, text): | |
171 """mark that a text need to be escaped (i.e. no markup)""" | |
172 return Escape(text) | |
173 | |
325 | 174 def _do_split_labels(self): |
175 """Split labels so their content can flow with images""" | |
176 # XXX: to make things easier, we split labels in words | |
177 log.debug("labels splitting start") | |
178 children = self.children[::-1] | |
179 self.clear_widgets() | |
180 for child in children: | |
181 if isinstance(child, Label): | |
182 log.debug("label before split: {}".format(child.text)) | |
183 styles = [] | |
184 tag = False | |
185 new_text = [] | |
186 current_tag = [] | |
187 current_value = [] | |
188 current_wid = self._createText() | |
189 value = False | |
190 close = False | |
191 # we will parse the text and create a new widget | |
192 # on each new word (actually each space) | |
193 # FIXME: handle '\n' and other white chars | |
194 for c in child.text: | |
195 if tag: | |
196 # we are parsing a markup tag | |
197 if c == ']': | |
198 current_tag_s = ''.join(current_tag) | |
199 current_style = (current_tag_s, ''.join(current_value)) | |
200 if close: | |
201 for idx, s in enumerate(reversed(styles)): | |
202 if s[0] == current_tag_s: | |
203 del styles[len(styles) - idx - 1] | |
204 break | |
205 else: | |
206 styles.append(current_style) | |
207 current_tag = [] | |
208 current_value = [] | |
209 tag = False | |
210 value = False | |
211 close = False | |
212 elif c == '/': | |
213 close = True | |
214 elif c == '=': | |
215 value = True | |
216 elif value: | |
217 current_value.append(c) | |
218 else: | |
219 current_tag.append(c) | |
220 new_text.append(c) | |
221 else: | |
222 # we are parsing regular text | |
223 if c == '[': | |
224 new_text.append(c) | |
225 tag = True | |
226 elif c == ' ': | |
227 # new word, we do a new widget | |
228 new_text.append(' ') | |
229 for t, v in reversed(styles): | |
230 new_text.append('[/{}]'.format(t)) | |
231 current_wid.text = ''.join(new_text) | |
232 new_text = [] | |
233 self.add_widget(current_wid) | |
234 log.debug("new widget: {}".format(current_wid.text)) | |
235 current_wid = self._createText() | |
236 for t, v in styles: | |
237 new_text.append('[{tag}{value}]'.format( | |
238 tag = t, | |
239 value = '={}'.format(v) if v else '')) | |
240 else: | |
241 new_text.append(c) | |
242 if current_wid.text: | |
243 # we may have a remaining widget after the parsing | |
244 close_styles = [] | |
245 for t, v in reversed(styles): | |
246 close_styles.append('[/{}]'.format(t)) | |
247 current_wid.text = ''.join(close_styles) | |
248 self.add_widget(current_wid) | |
249 log.debug("new widget: {}".format(current_wid.text)) | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
250 else: |
325 | 251 # non Label widgets, we just add them |
252 self.add_widget(child) | |
253 self.splitted = True | |
254 log.debug("split OK") | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
255 |
57 | 256 # XHTML parsing methods |
257 | |
258 def _callParseMethod(self, e): | |
325 | 259 """Call the suitable method to parse the element |
57 | 260 |
261 self.xhtml_[tag] will be called if it exists, else | |
262 self.xhtml_generic will be used | |
263 @param e(ET.Element): element to parse | |
264 """ | |
265 try: | |
325 | 266 method = getattr(self, f"xhtml_{e.tag}") |
57 | 267 except AttributeError: |
325 | 268 log.warning(f"Unhandled XHTML tag: {e.tag}") |
57 | 269 method = self.xhtml_generic |
270 method(e) | |
271 | |
272 def _addStyle(self, tag, value=None, append_to_list=True): | |
273 """add a markup style to label | |
274 | |
275 @param tag(unicode): markup tag | |
276 @param value(unicode): markup value if suitable | |
277 @param append_to_list(bool): if True style we be added to self.styles | |
278 self.styles is needed to keep track of styles to remove | |
279 should most probably be set to True | |
280 """ | |
281 label = self._getLabel() | |
312 | 282 label.text += '[{tag}{value}]'.format( |
57 | 283 tag = tag, |
312 | 284 value = '={}'.format(value) if value else '' |
57 | 285 ) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
286 if append_to_list: |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
287 self.styles.append((tag, value)) |
57 | 288 |
289 def _removeStyle(self, tag, remove_from_list=True): | |
290 """remove a markup style from the label | |
291 | |
292 @param tag(unicode): markup tag to remove | |
293 @param remove_from_list(bool): if True, remove from self.styles too | |
294 should most probably be set to True | |
295 """ | |
296 label = self._getLabel() | |
312 | 297 label.text += '[/{tag}]'.format( |
57 | 298 tag = tag |
299 ) | |
300 if remove_from_list: | |
301 for rev_idx, style in enumerate(reversed(self.styles)): | |
302 if style[0] == tag: | |
303 tag_idx = len(self.styles) - 1 - rev_idx | |
304 del self.styles[tag_idx] | |
305 break | |
306 | |
307 def _getLabel(self): | |
308 """get current Label if it exists, or create a new one""" | |
309 if not isinstance(self.current_wid, Label): | |
310 self._addLabel() | |
311 return self.current_wid | |
312 | |
313 def _addLabel(self): | |
314 """add a new Label | |
315 | |
316 current styles will be closed and reopened if needed | |
317 """ | |
318 self._closeLabel() | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
319 self.current_wid = self._createText() |
57 | 320 for tag, value in self.styles: |
321 self._addStyle(tag, value, append_to_list=False) | |
322 self.add_widget(self.current_wid) | |
323 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
324 def _createText(self): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
325 label = SimpleXHTMLWidgetText(color=self.color, markup=True) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
326 self.bind(color=label.setter('color')) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
327 label.bind(texture_size=label.setter('size')) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
328 return label |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
329 |
57 | 330 def _closeLabel(self): |
331 """close current style tags in current label | |
332 | |
333 needed when you change label to keep style between | |
334 different widgets | |
335 """ | |
336 if isinstance(self.current_wid, Label): | |
337 for tag, value in reversed(self.styles): | |
338 self._removeStyle(tag, remove_from_list=False) | |
339 | |
340 def _parseCSS(self, e): | |
341 """parse CSS found in "style" attribute of element | |
342 | |
343 self._css_styles will be created and contained markup styles added by this method | |
344 @param e(ET.Element): element which may have a "style" attribute | |
345 """ | |
346 styles_limit = len(self.styles) | |
312 | 347 styles = e.attrib['style'].split(';') |
57 | 348 for style in styles: |
349 try: | |
312 | 350 prop, value = style.split(':') |
57 | 351 except ValueError: |
325 | 352 log.warning(f"can't parse style: {style}") |
57 | 353 continue |
312 | 354 prop = prop.strip().replace('-', '_') |
57 | 355 value = value.strip() |
356 try: | |
325 | 357 method = getattr(self, f"css_{prop}") |
57 | 358 except AttributeError: |
325 | 359 log.warning(f"Unhandled CSS: {prop}") |
57 | 360 else: |
361 method(e, value) | |
362 self._css_styles = self.styles[styles_limit:] | |
363 | |
364 def _closeCSS(self): | |
365 """removed CSS styles | |
366 | |
367 styles in self._css_styles will be removed | |
368 and the attribute will be deleted | |
369 """ | |
284 | 370 for tag, __ in reversed(self._css_styles): |
57 | 371 self._removeStyle(tag) |
372 del self._css_styles | |
373 | |
374 def xhtml_generic(self, elem, style=True, markup=None): | |
325 | 375 """Generic method for adding HTML elements |
57 | 376 |
377 this method handle content, style and children parsing | |
378 @param elem(ET.Element): element to add | |
379 @param style(bool): if True handle style attribute (CSS) | |
380 @param markup(tuple[unicode, (unicode, None)], None): kivy markup to use | |
381 """ | |
382 # we first add markup and CSS style | |
383 if markup is not None: | |
312 | 384 if isinstance(markup, str): |
57 | 385 tag, value = markup, None |
386 else: | |
387 tag, value = markup | |
388 self._addStyle(tag, value) | |
389 style_ = 'style' in elem.attrib and style | |
390 if style_: | |
391 self._parseCSS(elem) | |
392 | |
393 # then content | |
394 if elem.text: | |
395 self._getLabel().text += escape_markup(elem.text) | |
396 | |
397 # we parse the children | |
398 for child in elem: | |
399 self._callParseMethod(child) | |
400 | |
401 # closing CSS style and markup | |
402 if style_: | |
403 self._closeCSS() | |
404 if markup is not None: | |
405 self._removeStyle(tag) | |
406 | |
407 # and the tail, which is regular text | |
408 if elem.tail: | |
409 self._getLabel().text += escape_markup(elem.tail) | |
410 | |
411 # method handling XHTML elements | |
412 | |
413 def xhtml_br(self, elem): | |
414 label = self._getLabel() | |
415 label.text+='\n' | |
416 self.xhtml_generic(style=False) | |
417 | |
418 def xhtml_em(self, elem): | |
419 self.xhtml_generic(elem, markup='i') | |
420 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
421 def xhtml_img(self, elem): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
422 try: |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
423 src = elem.attrib['src'] |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
424 except KeyError: |
312 | 425 log.warning("<img> element without src: {}".format(ET.tostring(elem))) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
426 return |
59 | 427 try: |
312 | 428 target_height = int(elem.get('height', 0)) |
59 | 429 except ValueError: |
325 | 430 log.warning(f"Can't parse image height: {elem.get('height')}") |
431 target_height = None | |
59 | 432 try: |
312 | 433 target_width = int(elem.get('width', 0)) |
59 | 434 except ValueError: |
325 | 435 log.warning(f"Can't parse image width: {elem.get('width')}") |
436 target_width = None | |
59 | 437 |
438 img = SimpleXHTMLWidgetImage(source=src, target_height=target_height, target_width=target_width) | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
439 self.current_wid = img |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
440 self.add_widget(img) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
441 |
57 | 442 def xhtml_p(self, elem): |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
443 if isinstance(self.current_wid, Label): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
444 self.current_wid.text+="\n\n" |
57 | 445 self.xhtml_generic(elem) |
446 | |
447 def xhtml_span(self, elem): | |
448 self.xhtml_generic(elem) | |
449 | |
450 def xhtml_strong(self, elem): | |
451 self.xhtml_generic(elem, markup='b') | |
452 | |
453 # methods handling CSS properties | |
454 | |
455 def css_color(self, elem, value): | |
312 | 456 self._addStyle("color", css_color.parse(value)) |
57 | 457 |
458 def css_text_decoration(self, elem, value): | |
312 | 459 if value == 'underline': |
299
86b1cd8121dd
core (simple_xhtml): activated underline and line-through which are now available
Goffi <goffi@goffi.org>
parents:
288
diff
changeset
|
460 self._addStyle('u') |
312 | 461 elif value == 'line-through': |
299
86b1cd8121dd
core (simple_xhtml): activated underline and line-through which are now available
Goffi <goffi@goffi.org>
parents:
288
diff
changeset
|
462 self._addStyle('s') |
57 | 463 else: |
312 | 464 log.warning("unhandled text decoration: {}".format(value)) |