Kivy TextInput with DropDown widget does not allow scrolling

我与影子孤独终老i 提交于 2021-02-11 15:14:31

问题


I have several textinputs inside a ScrollView. When a 'normal' TextInput is on focus, I can easily scroll the view without losing the focus on the textinput. However, if the textinput opens a MeasureDropDown (custom DropDown) widget, scrolling is not possible anymore. The behavior is actually sligthly different on Windows and Android:

  • On Windows, I can scroll the view only if the mouse cursor is not above the TextInput;
  • On Android, there is no way to scroll the view as long as the textInput has focus (resulting in some of dropdown items not to be selectable if they are "hidden" from the current view).

Below is a minimal code to reproduce the issue (which benefited greatly from help received here):

from kivy.app import App
from kivy.factory import Factory
from kivy.lang import Builder
from kivy.properties import BooleanProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.textinput import TextInput


class DelIngButton(Button): # Button to delete IngredientRow
    pass


class DropListButton(Button): # Custom Button for DropDown widget
    def __init__(self, **kwargs):
        super(DropListButton, self).__init__(**kwargs)
        self.bind(on_release=lambda x: self.parent.parent.select(self.text))


class DropListTextInput(TextInput):
    # Provides a couple needed behaviors

    def on_focus(self, *args):
        if self.focus:
            self.dropDown.selection_is_DLTI = True
        else:
            self.dropDown.selection_is_DLTI = False

    def on_text_validate(self, *args):
        self.dropDown.selection_is_DLTI = False

        # put the text from this widget into the TextInput that the DropDown is attached to
        self.dropDown.attach_to.text = self.text

        # dismiss the DropDown
        self.dropDown.dismiss()


class IngredientRow(BoxLayout):
    def __init__(self, **kwargs):
        super(IngredientRow, self).__init__(**kwargs)
        self.dropdown = MeasureDropDown()

    def handle_focus(self, ti):
        # handle on_focus event for the measure TextInput
        if ti.focus:
            # open DropDown if the TextInput gets focus
            self.dropdown.open(ti)
        else:
            # ti has lost focus
            if self.dropdown.selection_is_DLTI:
                # do not dismiss if a DropListTextInput is the selection
                return

            # dismiss DropDown
            self.dropdown.dismiss(ti)
            self.dropdown.unbind_all()
            self.dropdown.fbind('on_select', lambda self, x: setattr(ti, 'text', x))


class MeasureDropDown(DropDown):
    # set to True if the selection is a DropListTextInput
    selection_is_DLTI = BooleanProperty(False)

    def unbind_all(self):
        for callBack in self.get_property_observers('on_select'):
            self.funbind('on_select', callBack)


####################################
class AddWindow(Screen):

    def addIngredient(self, instance): #adds a new IngredientRow
        row = instance.parent
        row.remove_widget(row.children[0])
        row.add_widget(Factory.DelIngButton(), index=0)
        self.ingsGrid.add_widget(Factory.IngredientRow(), index=0)


class WMan(ScreenManager):
    def __init__(self, **kwargs):
        super(WMan, self).__init__(**kwargs)

# kv = Builder.load_file("ui/layout.kv")
kv = Builder.load_string('''
#:set text_color 0,0,0,.8

#:set row_height '35sp'

#:set main_padding ['10sp', '10sp']
#:set small_padding ['5sp', '5sp']


<DropListButton>: # Button for custom DropDown
    color: text_color
    background_normal: ''

<DelIngButton>: # Button to delete row
    text: '-'
    size_hint: None, None
    height: row_height
    width: row_height
    on_release: self.parent.parent.remove_widget(self.parent)

<MeasureDropDown>:
    id: dropDown
    DropListButton:
        size_hint: 1, None
        height: row_height
        text: "g"
    DropListButton:
        size_hint: 1, None
        height: row_height
        text: "Kg"
    DropListButton:
        size_hint: 1, None
        height: row_height
        text: "L"
    DropListTextInput:  # CustomTextInput instead of standard TextInput
        dropDown: dropDown  # provide easy access to the DropDown
        size_hint: 1, None
        height: row_height
        hint_text: 'new'
        multiline: False  # needed to trigger on_text_validate

<IngredientRow>:
    orientation: 'horizontal'
    size_hint: 1, None
    height: row_height
    spacing: '5sp'
    TextInput:
        id: ing
        hint_text: 'Ingredient'
        multiline: False
        size_hint: .6, None
        height: row_height
    TextInput:
        id: quant
        hint_text: 'Quantity'
        multiline: False
        size_hint: .2, None
        height: row_height
    TextInput:
        id: measure
        hint_text: 'measure'
        size_hint: .2, None
        height: row_height
        on_focus:
            root.handle_focus(self)
    Button:
        id: addIng
        text: "+"
        size_hint: None, None
        height: row_height
        width: row_height
        on_release: app.root.ids.add.addIngredient(self)


<MainScrollView@ScrollView>:
    size_hint: 1, None
    scroll_type: ['bars', 'content']

##################
# Windows
##################

WMan:
    AddWindow:
        id: add

<AddWindow>:
    name: 'add'
    ingsGrid: ingsGrid
    ingredientRow: ingredientRow

    MainScrollView:
        height: self.parent.size[1]
        GridLayout:
            cols:1
            size_hint: 1, None
            pos_hint: {"top": 1}
            height: self.minimum_height
            padding: main_padding
            StackLayout:
                id: ingsGrid
                size_hint: 1, None
                height: self.minimum_height
                orientation: 'lr-tb'
                padding: small_padding
                IngredientRow:
                    id: ingredientRow
''')


class TryApp(App):
    def build(self):
        return kv


if __name__ == "__main__":
    TryApp().run()

来源:https://stackoverflow.com/questions/62510256/kivy-textinput-with-dropdown-widget-does-not-allow-scrolling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!