Mercurial > libervia-desktop-kivy
annotate cagou/core/simple_xhtml.py @ 488:beedff600d2b
blog: blog widget implementation:
this patch implements a basic blog widget. The search bare can be used to change node
(only node for now, will be improved to do search and all).
Publication on current node can be done by pressing the pencil icon. A checkbox can be
activated to use end-to-end encryption.
No pagination or comments are supported for now.
Due to lack of HTML rendering in Kivy, only simple formatting is supported.
If item is end-2-end encrypted, a green closed locker is shown next to publication date.
rel 380
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 15 Oct 2022 20:20:10 +0200 |
parents | 3c9ba4a694ef |
children | 203755bbe0fe |
rev | line source |
---|---|
379 | 1 #!/usr/bin/env python3 |
2 | |
22 | 3 |
4 # Cagou: desktop/mobile frontend for Salut à Toi XMPP client | |
461 | 5 # Copyright (C) 2016-2021 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 | |
422
efee0e0afb78
core (common): moved simple_xhtml's image code to a generic "SizedImage" widget
Goffi <goffi@goffi.org>
parents:
379
diff
changeset
|
25 from kivy.metrics import sp |
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 |
422
efee0e0afb78
core (common): moved simple_xhtml's image code to a generic "SizedImage" widget
Goffi <goffi@goffi.org>
parents:
379
diff
changeset
|
30 from cagou.core.common import SizedImage |
325 | 31 |
32 | |
33 log = logging.getLogger(__name__) | |
22 | 34 |
35 | |
312 | 36 class Escape(str): |
57 | 37 """Class used to mark that a message need to be escaped""" |
38 | |
39 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
40 class SimpleXHTMLWidgetEscapedText(Label): |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
41 |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
42 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
|
43 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
|
44 self.font_size = parent.font_size |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
45 |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
46 def _addUrlMarkup(self, text): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
47 text_elts = [] |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
48 idx = 0 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
49 links = 0 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
50 while True: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
51 m = sat_strings.RE_URL.search(text[idx:]) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
52 if m is not None: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
53 text_elts.append(escape_markup(m.string[0:m.start()])) |
312 | 54 link_key = 'link_' + str(links) |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
55 url = m.group() |
325 | 56 escaped_url = escape_markup(url) |
57 text_elts.append( | |
58 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
|
59 if not links: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
60 self.ref_urls = {link_key: url} |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
61 else: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
62 self.ref_urls[link_key] = url |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
63 links += 1 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
64 idx += m.end() |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
65 else: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
66 if links: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
67 text_elts.append(escape_markup(text[idx:])) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
68 self.markup = True |
312 | 69 self.text = ''.join(text_elts) |
100
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
70 break |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
71 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
72 def on_text(self, instance, text): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
73 # 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
|
74 # 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
|
75 # 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
|
76 if text and not self.markup: |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
77 self._addUrlMarkup(text) |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
78 |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
79 def on_ref_press(self, ref): |
d7447c585603
chat: added url detection on text messages
Goffi <goffi@goffi.org>
parents:
98
diff
changeset
|
80 url = self.ref_urls[ref] |
344
83697218b9b2
core: handle URLs opening in a per-platform way:
Goffi <goffi@goffi.org>
parents:
335
diff
changeset
|
81 G.local_platform.open_url(url, self) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
82 |
59 | 83 |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
84 class SimpleXHTMLWidgetText(Label): |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
85 |
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
86 def on_parent(self, instance, parent): |
325 | 87 if parent is not None: |
88 self.font_size = parent.font_size | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
89 |
59 | 90 |
57 | 91 class SimpleXHTMLWidget(StackLayout): |
92 """widget handling simple XHTML parsing""" | |
93 xhtml = properties.StringProperty() | |
94 color = properties.ListProperty([1, 1, 1, 1]) | |
95 # XXX: bold is only used for escaped text | |
96 bold = properties.BooleanProperty(False) | |
185
ab3f5173ef5c
chat, simple XHTML: font size adjustement
Goffi <goffi@goffi.org>
parents:
126
diff
changeset
|
97 font_size = properties.NumericProperty(sp(14)) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
98 |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
99 # text/XHTML input |
57 | 100 |
101 def on_xhtml(self, instance, xhtml): | |
102 """parse xhtml and set content accordingly | |
103 | |
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
|
104 if xhtml is an instance of Escape, a Label with no markup will be used |
57 | 105 """ |
106 self.clear_widgets() | |
107 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
|
108 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
|
109 text=xhtml, color=self.color, bold=self.bold) |
325 | 110 self.bind(font_size=label.setter('font_size')) |
57 | 111 self.bind(color=label.setter('color')) |
112 self.bind(bold=label.setter('bold')) | |
113 self.add_widget(label) | |
114 else: | |
325 | 115 xhtml = ET.fromstring(xhtml.encode()) |
57 | 116 self.current_wid = None |
117 self.styles = [] | |
118 self._callParseMethod(xhtml) | |
325 | 119 if len(self.children) > 1: |
120 self._do_split_labels() | |
57 | 121 |
122 def escape(self, text): | |
123 """mark that a text need to be escaped (i.e. no markup)""" | |
124 return Escape(text) | |
125 | |
325 | 126 def _do_split_labels(self): |
127 """Split labels so their content can flow with images""" | |
128 # XXX: to make things easier, we split labels in words | |
129 log.debug("labels splitting start") | |
130 children = self.children[::-1] | |
131 self.clear_widgets() | |
132 for child in children: | |
133 if isinstance(child, Label): | |
134 log.debug("label before split: {}".format(child.text)) | |
135 styles = [] | |
136 tag = False | |
137 new_text = [] | |
138 current_tag = [] | |
139 current_value = [] | |
140 current_wid = self._createText() | |
141 value = False | |
142 close = False | |
143 # we will parse the text and create a new widget | |
144 # on each new word (actually each space) | |
145 # FIXME: handle '\n' and other white chars | |
146 for c in child.text: | |
147 if tag: | |
148 # we are parsing a markup tag | |
149 if c == ']': | |
150 current_tag_s = ''.join(current_tag) | |
151 current_style = (current_tag_s, ''.join(current_value)) | |
152 if close: | |
153 for idx, s in enumerate(reversed(styles)): | |
154 if s[0] == current_tag_s: | |
155 del styles[len(styles) - idx - 1] | |
156 break | |
157 else: | |
158 styles.append(current_style) | |
159 current_tag = [] | |
160 current_value = [] | |
161 tag = False | |
162 value = False | |
163 close = False | |
164 elif c == '/': | |
165 close = True | |
166 elif c == '=': | |
167 value = True | |
168 elif value: | |
169 current_value.append(c) | |
170 else: | |
171 current_tag.append(c) | |
172 new_text.append(c) | |
173 else: | |
174 # we are parsing regular text | |
175 if c == '[': | |
176 new_text.append(c) | |
177 tag = True | |
178 elif c == ' ': | |
179 # new word, we do a new widget | |
180 new_text.append(' ') | |
181 for t, v in reversed(styles): | |
182 new_text.append('[/{}]'.format(t)) | |
183 current_wid.text = ''.join(new_text) | |
184 new_text = [] | |
185 self.add_widget(current_wid) | |
186 log.debug("new widget: {}".format(current_wid.text)) | |
187 current_wid = self._createText() | |
188 for t, v in styles: | |
189 new_text.append('[{tag}{value}]'.format( | |
190 tag = t, | |
191 value = '={}'.format(v) if v else '')) | |
192 else: | |
193 new_text.append(c) | |
194 if current_wid.text: | |
195 # we may have a remaining widget after the parsing | |
196 close_styles = [] | |
197 for t, v in reversed(styles): | |
198 close_styles.append('[/{}]'.format(t)) | |
199 current_wid.text = ''.join(close_styles) | |
200 self.add_widget(current_wid) | |
201 log.debug("new widget: {}".format(current_wid.text)) | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
202 else: |
325 | 203 # non Label widgets, we just add them |
204 self.add_widget(child) | |
205 self.splitted = True | |
206 log.debug("split OK") | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
207 |
57 | 208 # XHTML parsing methods |
209 | |
210 def _callParseMethod(self, e): | |
325 | 211 """Call the suitable method to parse the element |
57 | 212 |
213 self.xhtml_[tag] will be called if it exists, else | |
214 self.xhtml_generic will be used | |
215 @param e(ET.Element): element to parse | |
216 """ | |
217 try: | |
325 | 218 method = getattr(self, f"xhtml_{e.tag}") |
57 | 219 except AttributeError: |
325 | 220 log.warning(f"Unhandled XHTML tag: {e.tag}") |
57 | 221 method = self.xhtml_generic |
222 method(e) | |
223 | |
224 def _addStyle(self, tag, value=None, append_to_list=True): | |
225 """add a markup style to label | |
226 | |
227 @param tag(unicode): markup tag | |
228 @param value(unicode): markup value if suitable | |
229 @param append_to_list(bool): if True style we be added to self.styles | |
230 self.styles is needed to keep track of styles to remove | |
231 should most probably be set to True | |
232 """ | |
233 label = self._getLabel() | |
312 | 234 label.text += '[{tag}{value}]'.format( |
57 | 235 tag = tag, |
312 | 236 value = '={}'.format(value) if value else '' |
57 | 237 ) |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
238 if append_to_list: |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
239 self.styles.append((tag, value)) |
57 | 240 |
241 def _removeStyle(self, tag, remove_from_list=True): | |
242 """remove a markup style from the label | |
243 | |
244 @param tag(unicode): markup tag to remove | |
245 @param remove_from_list(bool): if True, remove from self.styles too | |
246 should most probably be set to True | |
247 """ | |
248 label = self._getLabel() | |
312 | 249 label.text += '[/{tag}]'.format( |
57 | 250 tag = tag |
251 ) | |
252 if remove_from_list: | |
253 for rev_idx, style in enumerate(reversed(self.styles)): | |
254 if style[0] == tag: | |
255 tag_idx = len(self.styles) - 1 - rev_idx | |
256 del self.styles[tag_idx] | |
257 break | |
258 | |
259 def _getLabel(self): | |
260 """get current Label if it exists, or create a new one""" | |
261 if not isinstance(self.current_wid, Label): | |
262 self._addLabel() | |
263 return self.current_wid | |
264 | |
265 def _addLabel(self): | |
266 """add a new Label | |
267 | |
268 current styles will be closed and reopened if needed | |
269 """ | |
270 self._closeLabel() | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
271 self.current_wid = self._createText() |
57 | 272 for tag, value in self.styles: |
273 self._addStyle(tag, value, append_to_list=False) | |
274 self.add_widget(self.current_wid) | |
275 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
276 def _createText(self): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
277 label = SimpleXHTMLWidgetText(color=self.color, markup=True) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
278 self.bind(color=label.setter('color')) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
279 label.bind(texture_size=label.setter('size')) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
280 return label |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
281 |
57 | 282 def _closeLabel(self): |
283 """close current style tags in current label | |
284 | |
285 needed when you change label to keep style between | |
286 different widgets | |
287 """ | |
288 if isinstance(self.current_wid, Label): | |
289 for tag, value in reversed(self.styles): | |
290 self._removeStyle(tag, remove_from_list=False) | |
291 | |
292 def _parseCSS(self, e): | |
293 """parse CSS found in "style" attribute of element | |
294 | |
295 self._css_styles will be created and contained markup styles added by this method | |
296 @param e(ET.Element): element which may have a "style" attribute | |
297 """ | |
298 styles_limit = len(self.styles) | |
312 | 299 styles = e.attrib['style'].split(';') |
57 | 300 for style in styles: |
301 try: | |
312 | 302 prop, value = style.split(':') |
57 | 303 except ValueError: |
325 | 304 log.warning(f"can't parse style: {style}") |
57 | 305 continue |
312 | 306 prop = prop.strip().replace('-', '_') |
57 | 307 value = value.strip() |
308 try: | |
325 | 309 method = getattr(self, f"css_{prop}") |
57 | 310 except AttributeError: |
325 | 311 log.warning(f"Unhandled CSS: {prop}") |
57 | 312 else: |
313 method(e, value) | |
314 self._css_styles = self.styles[styles_limit:] | |
315 | |
316 def _closeCSS(self): | |
317 """removed CSS styles | |
318 | |
319 styles in self._css_styles will be removed | |
320 and the attribute will be deleted | |
321 """ | |
284 | 322 for tag, __ in reversed(self._css_styles): |
57 | 323 self._removeStyle(tag) |
324 del self._css_styles | |
325 | |
326 def xhtml_generic(self, elem, style=True, markup=None): | |
325 | 327 """Generic method for adding HTML elements |
57 | 328 |
329 this method handle content, style and children parsing | |
330 @param elem(ET.Element): element to add | |
331 @param style(bool): if True handle style attribute (CSS) | |
332 @param markup(tuple[unicode, (unicode, None)], None): kivy markup to use | |
333 """ | |
334 # we first add markup and CSS style | |
335 if markup is not None: | |
312 | 336 if isinstance(markup, str): |
57 | 337 tag, value = markup, None |
338 else: | |
339 tag, value = markup | |
340 self._addStyle(tag, value) | |
341 style_ = 'style' in elem.attrib and style | |
342 if style_: | |
343 self._parseCSS(elem) | |
344 | |
345 # then content | |
346 if elem.text: | |
347 self._getLabel().text += escape_markup(elem.text) | |
348 | |
349 # we parse the children | |
350 for child in elem: | |
351 self._callParseMethod(child) | |
352 | |
353 # closing CSS style and markup | |
354 if style_: | |
355 self._closeCSS() | |
356 if markup is not None: | |
357 self._removeStyle(tag) | |
358 | |
359 # and the tail, which is regular text | |
360 if elem.tail: | |
361 self._getLabel().text += escape_markup(elem.tail) | |
362 | |
363 # method handling XHTML elements | |
364 | |
365 def xhtml_br(self, elem): | |
366 label = self._getLabel() | |
367 label.text+='\n' | |
453
2d277a3d9cec
core (simple_xhtml): fixed xhtml_generic call in xhtml_br
Goffi <goffi@goffi.org>
parents:
422
diff
changeset
|
368 self.xhtml_generic(elem, style=False) |
57 | 369 |
370 def xhtml_em(self, elem): | |
371 self.xhtml_generic(elem, markup='i') | |
372 | |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
373 def xhtml_img(self, elem): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
374 try: |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
375 src = elem.attrib['src'] |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
376 except KeyError: |
312 | 377 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
|
378 return |
59 | 379 try: |
312 | 380 target_height = int(elem.get('height', 0)) |
59 | 381 except ValueError: |
325 | 382 log.warning(f"Can't parse image height: {elem.get('height')}") |
383 target_height = None | |
59 | 384 try: |
312 | 385 target_width = int(elem.get('width', 0)) |
59 | 386 except ValueError: |
325 | 387 log.warning(f"Can't parse image width: {elem.get('width')}") |
388 target_width = None | |
59 | 389 |
422
efee0e0afb78
core (common): moved simple_xhtml's image code to a generic "SizedImage" widget
Goffi <goffi@goffi.org>
parents:
379
diff
changeset
|
390 img = SizedImage( |
efee0e0afb78
core (common): moved simple_xhtml's image code to a generic "SizedImage" widget
Goffi <goffi@goffi.org>
parents:
379
diff
changeset
|
391 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
|
392 self.current_wid = img |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
393 self.add_widget(img) |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
394 |
57 | 395 def xhtml_p(self, elem): |
58
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
396 if isinstance(self.current_wid, Label): |
7aa2ffff9067
chat: <img/> tag handling first draft:
Goffi <goffi@goffi.org>
parents:
57
diff
changeset
|
397 self.current_wid.text+="\n\n" |
57 | 398 self.xhtml_generic(elem) |
399 | |
400 def xhtml_span(self, elem): | |
401 self.xhtml_generic(elem) | |
402 | |
403 def xhtml_strong(self, elem): | |
404 self.xhtml_generic(elem, markup='b') | |
405 | |
406 # methods handling CSS properties | |
407 | |
408 def css_color(self, elem, value): | |
312 | 409 self._addStyle("color", css_color.parse(value)) |
57 | 410 |
411 def css_text_decoration(self, elem, value): | |
312 | 412 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
|
413 self._addStyle('u') |
312 | 414 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
|
415 self._addStyle('s') |
57 | 416 else: |
312 | 417 log.warning("unhandled text decoration: {}".format(value)) |