Mercurial > libervia-desktop-kivy
comparison cagou/core/simple_xhtml.py @ 491:203755bbe0fe
massive refactoring from camelCase -> snake_case. See backend commit log for more details
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:44:32 +0200 |
parents | 3c9ba4a694ef |
children |
comparison
equal
deleted
inserted
replaced
490:962d17c4078c | 491:203755bbe0fe |
---|---|
41 | 41 |
42 def on_parent(self, instance, parent): | 42 def on_parent(self, instance, parent): |
43 if parent is not None: | 43 if parent is not None: |
44 self.font_size = parent.font_size | 44 self.font_size = parent.font_size |
45 | 45 |
46 def _addUrlMarkup(self, text): | 46 def _add_url_markup(self, text): |
47 text_elts = [] | 47 text_elts = [] |
48 idx = 0 | 48 idx = 0 |
49 links = 0 | 49 links = 0 |
50 while True: | 50 while True: |
51 m = sat_strings.RE_URL.search(text[idx:]) | 51 m = sat_strings.RE_URL.search(text[idx:]) |
72 def on_text(self, instance, text): | 72 def on_text(self, instance, text): |
73 # do NOT call the method if self.markup is set | 73 # do NOT call the method if self.markup is set |
74 # this would result in infinite loop (because self.text | 74 # this would result in infinite loop (because self.text |
75 # is changed if an URL is found, and in this case markup too) | 75 # is changed if an URL is found, and in this case markup too) |
76 if text and not self.markup: | 76 if text and not self.markup: |
77 self._addUrlMarkup(text) | 77 self._add_url_markup(text) |
78 | 78 |
79 def on_ref_press(self, ref): | 79 def on_ref_press(self, ref): |
80 url = self.ref_urls[ref] | 80 url = self.ref_urls[ref] |
81 G.local_platform.open_url(url, self) | 81 G.local_platform.open_url(url, self) |
82 | 82 |
113 self.add_widget(label) | 113 self.add_widget(label) |
114 else: | 114 else: |
115 xhtml = ET.fromstring(xhtml.encode()) | 115 xhtml = ET.fromstring(xhtml.encode()) |
116 self.current_wid = None | 116 self.current_wid = None |
117 self.styles = [] | 117 self.styles = [] |
118 self._callParseMethod(xhtml) | 118 self._call_parse_method(xhtml) |
119 if len(self.children) > 1: | 119 if len(self.children) > 1: |
120 self._do_split_labels() | 120 self._do_split_labels() |
121 | 121 |
122 def escape(self, text): | 122 def escape(self, text): |
123 """mark that a text need to be escaped (i.e. no markup)""" | 123 """mark that a text need to be escaped (i.e. no markup)""" |
135 styles = [] | 135 styles = [] |
136 tag = False | 136 tag = False |
137 new_text = [] | 137 new_text = [] |
138 current_tag = [] | 138 current_tag = [] |
139 current_value = [] | 139 current_value = [] |
140 current_wid = self._createText() | 140 current_wid = self._create_text() |
141 value = False | 141 value = False |
142 close = False | 142 close = False |
143 # we will parse the text and create a new widget | 143 # we will parse the text and create a new widget |
144 # on each new word (actually each space) | 144 # on each new word (actually each space) |
145 # FIXME: handle '\n' and other white chars | 145 # FIXME: handle '\n' and other white chars |
182 new_text.append('[/{}]'.format(t)) | 182 new_text.append('[/{}]'.format(t)) |
183 current_wid.text = ''.join(new_text) | 183 current_wid.text = ''.join(new_text) |
184 new_text = [] | 184 new_text = [] |
185 self.add_widget(current_wid) | 185 self.add_widget(current_wid) |
186 log.debug("new widget: {}".format(current_wid.text)) | 186 log.debug("new widget: {}".format(current_wid.text)) |
187 current_wid = self._createText() | 187 current_wid = self._create_text() |
188 for t, v in styles: | 188 for t, v in styles: |
189 new_text.append('[{tag}{value}]'.format( | 189 new_text.append('[{tag}{value}]'.format( |
190 tag = t, | 190 tag = t, |
191 value = '={}'.format(v) if v else '')) | 191 value = '={}'.format(v) if v else '')) |
192 else: | 192 else: |
205 self.splitted = True | 205 self.splitted = True |
206 log.debug("split OK") | 206 log.debug("split OK") |
207 | 207 |
208 # XHTML parsing methods | 208 # XHTML parsing methods |
209 | 209 |
210 def _callParseMethod(self, e): | 210 def _call_parse_method(self, e): |
211 """Call the suitable method to parse the element | 211 """Call the suitable method to parse the element |
212 | 212 |
213 self.xhtml_[tag] will be called if it exists, else | 213 self.xhtml_[tag] will be called if it exists, else |
214 self.xhtml_generic will be used | 214 self.xhtml_generic will be used |
215 @param e(ET.Element): element to parse | 215 @param e(ET.Element): element to parse |
219 except AttributeError: | 219 except AttributeError: |
220 log.warning(f"Unhandled XHTML tag: {e.tag}") | 220 log.warning(f"Unhandled XHTML tag: {e.tag}") |
221 method = self.xhtml_generic | 221 method = self.xhtml_generic |
222 method(e) | 222 method(e) |
223 | 223 |
224 def _addStyle(self, tag, value=None, append_to_list=True): | 224 def _add_style(self, tag, value=None, append_to_list=True): |
225 """add a markup style to label | 225 """add a markup style to label |
226 | 226 |
227 @param tag(unicode): markup tag | 227 @param tag(unicode): markup tag |
228 @param value(unicode): markup value if suitable | 228 @param value(unicode): markup value if suitable |
229 @param append_to_list(bool): if True style we be added to self.styles | 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 | 230 self.styles is needed to keep track of styles to remove |
231 should most probably be set to True | 231 should most probably be set to True |
232 """ | 232 """ |
233 label = self._getLabel() | 233 label = self._get_label() |
234 label.text += '[{tag}{value}]'.format( | 234 label.text += '[{tag}{value}]'.format( |
235 tag = tag, | 235 tag = tag, |
236 value = '={}'.format(value) if value else '' | 236 value = '={}'.format(value) if value else '' |
237 ) | 237 ) |
238 if append_to_list: | 238 if append_to_list: |
239 self.styles.append((tag, value)) | 239 self.styles.append((tag, value)) |
240 | 240 |
241 def _removeStyle(self, tag, remove_from_list=True): | 241 def _remove_style(self, tag, remove_from_list=True): |
242 """remove a markup style from the label | 242 """remove a markup style from the label |
243 | 243 |
244 @param tag(unicode): markup tag to remove | 244 @param tag(unicode): markup tag to remove |
245 @param remove_from_list(bool): if True, remove from self.styles too | 245 @param remove_from_list(bool): if True, remove from self.styles too |
246 should most probably be set to True | 246 should most probably be set to True |
247 """ | 247 """ |
248 label = self._getLabel() | 248 label = self._get_label() |
249 label.text += '[/{tag}]'.format( | 249 label.text += '[/{tag}]'.format( |
250 tag = tag | 250 tag = tag |
251 ) | 251 ) |
252 if remove_from_list: | 252 if remove_from_list: |
253 for rev_idx, style in enumerate(reversed(self.styles)): | 253 for rev_idx, style in enumerate(reversed(self.styles)): |
254 if style[0] == tag: | 254 if style[0] == tag: |
255 tag_idx = len(self.styles) - 1 - rev_idx | 255 tag_idx = len(self.styles) - 1 - rev_idx |
256 del self.styles[tag_idx] | 256 del self.styles[tag_idx] |
257 break | 257 break |
258 | 258 |
259 def _getLabel(self): | 259 def _get_label(self): |
260 """get current Label if it exists, or create a new one""" | 260 """get current Label if it exists, or create a new one""" |
261 if not isinstance(self.current_wid, Label): | 261 if not isinstance(self.current_wid, Label): |
262 self._addLabel() | 262 self._add_label() |
263 return self.current_wid | 263 return self.current_wid |
264 | 264 |
265 def _addLabel(self): | 265 def _add_label(self): |
266 """add a new Label | 266 """add a new Label |
267 | 267 |
268 current styles will be closed and reopened if needed | 268 current styles will be closed and reopened if needed |
269 """ | 269 """ |
270 self._closeLabel() | 270 self._close_label() |
271 self.current_wid = self._createText() | 271 self.current_wid = self._create_text() |
272 for tag, value in self.styles: | 272 for tag, value in self.styles: |
273 self._addStyle(tag, value, append_to_list=False) | 273 self._add_style(tag, value, append_to_list=False) |
274 self.add_widget(self.current_wid) | 274 self.add_widget(self.current_wid) |
275 | 275 |
276 def _createText(self): | 276 def _create_text(self): |
277 label = SimpleXHTMLWidgetText(color=self.color, markup=True) | 277 label = SimpleXHTMLWidgetText(color=self.color, markup=True) |
278 self.bind(color=label.setter('color')) | 278 self.bind(color=label.setter('color')) |
279 label.bind(texture_size=label.setter('size')) | 279 label.bind(texture_size=label.setter('size')) |
280 return label | 280 return label |
281 | 281 |
282 def _closeLabel(self): | 282 def _close_label(self): |
283 """close current style tags in current label | 283 """close current style tags in current label |
284 | 284 |
285 needed when you change label to keep style between | 285 needed when you change label to keep style between |
286 different widgets | 286 different widgets |
287 """ | 287 """ |
288 if isinstance(self.current_wid, Label): | 288 if isinstance(self.current_wid, Label): |
289 for tag, value in reversed(self.styles): | 289 for tag, value in reversed(self.styles): |
290 self._removeStyle(tag, remove_from_list=False) | 290 self._remove_style(tag, remove_from_list=False) |
291 | 291 |
292 def _parseCSS(self, e): | 292 def _parse_css(self, e): |
293 """parse CSS found in "style" attribute of element | 293 """parse CSS found in "style" attribute of element |
294 | 294 |
295 self._css_styles will be created and contained markup styles added by this method | 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 | 296 @param e(ET.Element): element which may have a "style" attribute |
297 """ | 297 """ |
311 log.warning(f"Unhandled CSS: {prop}") | 311 log.warning(f"Unhandled CSS: {prop}") |
312 else: | 312 else: |
313 method(e, value) | 313 method(e, value) |
314 self._css_styles = self.styles[styles_limit:] | 314 self._css_styles = self.styles[styles_limit:] |
315 | 315 |
316 def _closeCSS(self): | 316 def _close_css(self): |
317 """removed CSS styles | 317 """removed CSS styles |
318 | 318 |
319 styles in self._css_styles will be removed | 319 styles in self._css_styles will be removed |
320 and the attribute will be deleted | 320 and the attribute will be deleted |
321 """ | 321 """ |
322 for tag, __ in reversed(self._css_styles): | 322 for tag, __ in reversed(self._css_styles): |
323 self._removeStyle(tag) | 323 self._remove_style(tag) |
324 del self._css_styles | 324 del self._css_styles |
325 | 325 |
326 def xhtml_generic(self, elem, style=True, markup=None): | 326 def xhtml_generic(self, elem, style=True, markup=None): |
327 """Generic method for adding HTML elements | 327 """Generic method for adding HTML elements |
328 | 328 |
335 if markup is not None: | 335 if markup is not None: |
336 if isinstance(markup, str): | 336 if isinstance(markup, str): |
337 tag, value = markup, None | 337 tag, value = markup, None |
338 else: | 338 else: |
339 tag, value = markup | 339 tag, value = markup |
340 self._addStyle(tag, value) | 340 self._add_style(tag, value) |
341 style_ = 'style' in elem.attrib and style | 341 style_ = 'style' in elem.attrib and style |
342 if style_: | 342 if style_: |
343 self._parseCSS(elem) | 343 self._parse_css(elem) |
344 | 344 |
345 # then content | 345 # then content |
346 if elem.text: | 346 if elem.text: |
347 self._getLabel().text += escape_markup(elem.text) | 347 self._get_label().text += escape_markup(elem.text) |
348 | 348 |
349 # we parse the children | 349 # we parse the children |
350 for child in elem: | 350 for child in elem: |
351 self._callParseMethod(child) | 351 self._call_parse_method(child) |
352 | 352 |
353 # closing CSS style and markup | 353 # closing CSS style and markup |
354 if style_: | 354 if style_: |
355 self._closeCSS() | 355 self._close_css() |
356 if markup is not None: | 356 if markup is not None: |
357 self._removeStyle(tag) | 357 self._remove_style(tag) |
358 | 358 |
359 # and the tail, which is regular text | 359 # and the tail, which is regular text |
360 if elem.tail: | 360 if elem.tail: |
361 self._getLabel().text += escape_markup(elem.tail) | 361 self._get_label().text += escape_markup(elem.tail) |
362 | 362 |
363 # method handling XHTML elements | 363 # method handling XHTML elements |
364 | 364 |
365 def xhtml_br(self, elem): | 365 def xhtml_br(self, elem): |
366 label = self._getLabel() | 366 label = self._get_label() |
367 label.text+='\n' | 367 label.text+='\n' |
368 self.xhtml_generic(elem, style=False) | 368 self.xhtml_generic(elem, style=False) |
369 | 369 |
370 def xhtml_em(self, elem): | 370 def xhtml_em(self, elem): |
371 self.xhtml_generic(elem, markup='i') | 371 self.xhtml_generic(elem, markup='i') |
404 self.xhtml_generic(elem, markup='b') | 404 self.xhtml_generic(elem, markup='b') |
405 | 405 |
406 # methods handling CSS properties | 406 # methods handling CSS properties |
407 | 407 |
408 def css_color(self, elem, value): | 408 def css_color(self, elem, value): |
409 self._addStyle("color", css_color.parse(value)) | 409 self._add_style("color", css_color.parse(value)) |
410 | 410 |
411 def css_text_decoration(self, elem, value): | 411 def css_text_decoration(self, elem, value): |
412 if value == 'underline': | 412 if value == 'underline': |
413 self._addStyle('u') | 413 self._add_style('u') |
414 elif value == 'line-through': | 414 elif value == 'line-through': |
415 self._addStyle('s') | 415 self._add_style('s') |
416 else: | 416 else: |
417 log.warning("unhandled text decoration: {}".format(value)) | 417 log.warning("unhandled text decoration: {}".format(value)) |