flet icon indicating copy to clipboard operation
flet copied to clipboard

SelectionArea fails for TextSpan elements

Open sgrefen opened this issue 10 months ago • 3 comments

Description When ft.Text is in a SelectionArea the on_change callback works as intended, unless either selectable is True or the Text is using TextSpans. In the later case the spans are not selectable (unless selectable is True which breaks the on_change callback). WebView and local Linux client behave identically.

Code example to reproduce the issue:

import flet as ft

name = "CupertinoSegmentedButton example"


def example(tag="plain",s=False):
    def pev(e):
        try:
            print("PEV(%s %s)"%(tag,s),vars(e.data))
        except Exception as ex:
            print(ex)
            print(e)

    def pe(e):
        try:
            print("PE(%s %s)"%(tag,s),vars(e))
        except Exception as ex:
            print(ex)
            pev(e)



    return ft.SelectionArea(
                  content=ft.Text(value="TEST [ %s select %s]\n 43240932840329\nsffsf"%(tag,s),
                  spans=[ ft.TextSpan("Test\n"), ft.TextSpan("sjflfff\n"), ft.TextSpan("sfsfdfd\n"), ft.TextSpan("sdfsdfsd\n")] if tag != "plain" else None,
                  selectable=s,
                  ),on_change=pe
    )


def main(page: ft.Page):
    page.add(example())
    page.add(example("spans"))
    page.add(example(s=True))
    page.add(example("spans",True))


#ft.app(target=main,view=ft.AppView.WEB_BROWSER)
ft.app(target=main)


Describe the results you received:

Only the first text works as expected, this are selectable areas image and only these callbacks are generated: $ python tcg.py package:media_kit_libs_linux registered. PE(plain False) {'target': '_2', 'name': 'change', 'data': '', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': 'T', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': 'TE', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': 'TEST [ plain select False]\n 432', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': 'TEST [ plain select False]\n 4324', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': 'TEST [ plain select False]\n 43240932840329\nsffsf', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')} PE(plain False) {'target': '_2', 'name': 'change', 'data': '', 'control': SelectionArea(), 'page': Page(id='page', route='/', pwa='false', web='false', debug='false', platform='linux', platformbrightness='light', media='{"padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_padding":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0},"view_insets":{"top":0.0,"right":0.0,"bottom":0.0,"left":0.0}}', width='1280.0', height='720.0', windowwidth='1280.0', windowheight='720.0', windowtop='0.0', windowleft='0.0', clientip='', clientuseragent='', windowminimized='false', windowmaximized='false', windowfocused='false', windowfullscreen='false')}

Describe the results you expected:

Expectation is receiving events also for text-spans, ideally as array with text_span-id + selected text in span

Additional information you deem important (e.g. issue happens only occasionally):

Flet version (pip show flet):

Name: flet
Version: 0.22.0
Summary: Flet for Python - easily build interactive multi-platform apps in Python
Home-page: 
Author: Appveyor Systems Inc.
Author-email: [email protected]
License: Apache-2.0
Location: /home/grefen/.local/lib/python3.12/site-packages
Requires: cookiecutter, fastapi, flet-runtime, packaging, qrcode, uvicorn, watchdog
Required-by: 
``


**Operating system**:

<!--
Linux
-->

**Additional environment details:**

sgrefen avatar Apr 16 '24 14:04 sgrefen

One hint is in https://github.com/flutter/flutter/issues/113530 , in the thread the recommendation is to use Text.rich instead of RichText. Text.dart:

return control.attrBool("selectable", false)!
          ? (spans.isNotEmpty)
              ? SelectableText.rich(
                  TextSpan(text: text, style: style, children: spans),
                  maxLines: maxLines,
                  textAlign: textAlign,
                )
              : SelectableText(
                  text,
                  semanticsLabel: semanticsLabel,
                  maxLines: maxLines,
                  style: style,
                  textAlign: textAlign,
                )
          : (spans.isNotEmpty)
              ? RichText(
                  text: TextSpan(text: text, style: style, children: spans),
                  maxLines: maxLines,
                  softWrap: !noWrap,
                  textAlign: textAlign,
                  overflow: overflow,
                )
              : Text(
                  text,
                  semanticsLabel: semanticsLabel,
                  maxLines: maxLines,
                  softWrap: !noWrap,
                  style: style,
                  textAlign: textAlign,
                  overflow: overflow,
                );

I'm going to try a build with the change

sgrefen avatar Apr 17 '24 13:04 sgrefen

The following patch seems to fix it:

git diff -u ../packages/flet/lib/src/controls/text.dart
diff --git a/packages/flet/lib/src/controls/text.dart b/packages/flet/lib/src/controls/text.dart
index 7d489e5..54d8217 100644
--- a/packages/flet/lib/src/controls/text.dart
+++ b/packages/flet/lib/src/controls/text.dart
@@ -116,8 +116,9 @@ class TextControl extends StatelessWidget with FletStoreMixin {
                   textAlign: textAlign,
                 )
           : (spans.isNotEmpty)
-              ? RichText(
-                  text: TextSpan(text: text, style: style, children: spans),
+              ? Text.rich(
+                  TextSpan(text: text, style: style, children: spans),
+                  semanticsLabel: semanticsLabel,
                   maxLines: maxLines,
                   softWrap: !noWrap,
                   textAlign: textAlign,

sgrefen avatar Apr 17 '24 14:04 sgrefen

Will you mind opening a PR with the suggestion?

ndonkoHenri avatar Apr 20 '24 09:04 ndonkoHenri