Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tables unable to scroll manually or using table.scroll_to_bottom #2281

Open
saviorofdeholocron opened this issue Dec 14, 2023 · 3 comments
Open
Labels
android The issue relates to Android mobile support. bug A crash or error in behavior.

Comments

@saviorofdeholocron
Copy link

saviorofdeholocron commented Dec 14, 2023

Describe the bug

here are the simplified codes of my application, which only the number inputs for 'total attacker' and 'total defender', and the button 'allout attack' is working and there is no logical calculations:

use for syntax, logic check, complete code down below

from os import device_encoding
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
import random

class RISKdicer(toga.App):

    def startup(self):
        main_box = toga.Box(style=Pack(direction=COLUMN,padding=5))
        main_UI = toga.ScrollContainer(content = main_box)
        
        numbers_box = toga.Box(style=Pack(direction=ROW))
        buttons_box = toga.Box(style=Pack(direction=ROW))
        display_box = toga.Box(style=Pack(direction=COLUMN))
        
        atker_box = toga.Box(style=Pack(direction=COLUMN))
        defer_box = toga.Box(style=Pack(direction=COLUMN,padding_left=5))
        atkernum_container = toga.Box(style=Pack(direction=ROW))
        defernum_container = toga.Box(style=Pack(direction=ROW))
        
        atkernums_box = toga.Box(style=Pack(direction=COLUMN))
        defernums_box = toga.Box(style=Pack(direction=COLUMN))
        atkerinput_box = toga.Box(style=Pack(direction=COLUMN))
        deferinput_box = toga.Box(style=Pack(direction=COLUMN))
        
        atkernum_label = toga.Label('Total number of Attacker (exclude artillery): ',style=Pack(padding_top=3))
        self.atkernum_input = toga.NumberInput(step=1,min=2,value=2,style=Pack(height=30))
        defernum_label = toga.Label('Total number of Defender (include artillery): ',style=Pack(padding_top=3))
        self.defernum_input = toga.NumberInput(step=1,min=1,value=1,style=Pack(height=30))
        
        chaos_button = toga.Button('Allout attack',on_press=self.confirm_auto,style=Pack(height=30))
       
        self.nums_table = toga.Table(headings=[
            'round','attacker','atk Cavalry','atk Artillery','Commander','defender','def Artillery','landing'],style=Pack(flex=1,height=70,width=720))
        self.dice_table = toga.Table(headings=[
            'round','dice A1','dice A2','dice A3','dice D1','dice D2','dice D3'],style=Pack(flex=1,height=100,width=720))
        clear_table_button = toga.Button('Clear table',on_press=self.clear_table)

        atkernums_box.add(atkernum_label)
        defernums_box.add(defernum_label)
        atkerinput_box.add(self.atkernum_input)
        deferinput_box.add(self.defernum_input)
        
        atkernum_container.add(atkernums_box)
        atkernum_container.add(atkerinput_box)
        defernum_container.add(defernums_box)
        defernum_container.add(deferinput_box)
        atker_box.add(atkernum_container)
        defer_box.add(defernum_container)
        boolean_box.add(self.atkerhasC_switch)
        boolean_box.add(self.landing_switch)
        CA_box.add(CA_label_button_box)
        CA_box.add(CA_atker_input_box)
        CA_box.add(CA_defer_input_box)
        
        numbers_box.add(atker_box)
        numbers_box.add(defer_box)
        buttons_box.add(chaos_button)
        buttons_box.add(throw_dice_button)
        buttons_box.add(CA_box)
        display_box.add(self.nums_table)
        display_box.add(self.dice_table)
        display_box.add(clear_table_button)
        
        main_box.add(numbers_box)
        main_box.add(atkerCnum_box)
        main_box.add(boolean_box)
        main_box.add(buttons_box)
        main_box.add(display_box)
        
        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_UI
        self.main_window.show()
        if toga.platform.current_platform == 'android':
            self._impl.native.getSupportActionBar().hide()
        
    def clear_table(self,widget):
        self.nums_table.data = 0
        self.dice_table.data = 0
        
    def confirm_auto(self,widget):
        atkernum_display = self.atkernum_input.value
        atkerC_display = self.atkerCnum_input.value
        atkerA_display = self.atkerAnum_input.value
        defernum_display = self.defernum_input.value
        deferA_display = self.deferAnum_input.value
        self.CA_atker_input.value
        self.CA_defer_input.value
        boolean_atkerhasC_display = self.atkerhasC_switch.value
        boolean_landing_display = self.landing_switch.value
        self.nums_table.data.append(('',atkernum_display,atkerC_display,atkerA_display,boolean_atkerhasC_display,defernum_display,deferA_display,boolean_landing_display))
        self.nums_table.scroll_to_bottom()
        self.main_window.confirm_dialog('Confirmation','Do you really want to do this?',on_result=self.allout_attack)

     def allout_attack(self,widget,result):
        if result == True:
            roundnum = 0
            atkernum = self.atkernum_input.value
            defernum = self.defernum_input.value
            while atkernum > 1 and defernum > 0:
                roundnum += 1
                if atkernum > 3:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                elif atkernum > 2:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                else:
                        diceA1 = random.randint(1,6)
                        diceA2 = 0
                        diceA3 = 0
                if defernum > 1:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                else:
                        diceD1 = random.randint(1,6)
                        diceD2 = 0
                        diceD3 = 0    
                if diceA1 > diceD1:
                    defernum -= 1
                else:
                    atkernum -= 1
                if diceA2 > 0 and diceD2 > 0:
                    if diceA2 > diceD2:
                        defernum -= 1
                    else:
                        atkernum -= 1
                if diceA3 > 0 and diceD3 > 0:
                    if diceA3 > diceD3:
                        defernum -= 1
                    else:
                        atkernum -= 1          
                self.dice_table.data.append((roundnum,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                self.nums_table.data.append((roundnum,atkernum,atkerCnum,'atkerA','',defernum,'deferA'))
            self.nums_table.scroll_to_bottom()
            self.nums_table.data.append('')
            self.dice_table.scroll_to_bottom()
            self.dice_table.data.append('')
        else:
            None

here are the complete codes of my application:

use this to compile into apk and test

from os import device_encoding
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
import random

class RISKdicer(toga.App):

    def startup(self):
        main_box = toga.Box(style=Pack(direction=COLUMN,padding=5))
        main_UI = toga.ScrollContainer(content = main_box)
        
        numbers_box = toga.Box(style=Pack(direction=ROW))
        boolean_box = toga.Box(style=Pack(direction=ROW))
        buttons_box = toga.Box(style=Pack(direction=ROW))
        display_box = toga.Box(style=Pack(direction=COLUMN))
        
        atker_box = toga.Box(style=Pack(direction=COLUMN))
        atker_label = toga.Label('Attacker')
        defer_box = toga.Box(style=Pack(direction=COLUMN,padding_left=5))
        defer_label = toga.Label('Defender')
        atkernum_container = toga.Box(style=Pack(direction=ROW))
        defernum_container = toga.Box(style=Pack(direction=ROW))
        
        atkernums_box = toga.Box(style=Pack(direction=COLUMN))
        defernums_box = toga.Box(style=Pack(direction=COLUMN))
        atkerinput_box = toga.Box(style=Pack(direction=COLUMN))
        deferinput_box = toga.Box(style=Pack(direction=COLUMN))
        atkerCnum_box = toga.Box(style=Pack(direction=ROW))
        CA_box = toga.Box(style=Pack(direction=COLUMN))
        CA_label_button_box = toga.Box(style=Pack(direction=ROW))
        CA_atker_input_box = toga.Box(style=Pack(direction=ROW))
        CA_defer_input_box = toga.Box(style=Pack(direction=ROW))
        
        atkernum_label = toga.Label('Total number of Attacker (exclude artillery): ',style=Pack(padding_top=3))
        self.atkernum_input = toga.NumberInput(step=1,min=2,value=2,style=Pack(height=30))
        defernum_label = toga.Label('Total number of Defender (include artillery): ',style=Pack(padding_top=3))
        self.defernum_input = toga.NumberInput(step=1,min=1,value=1,style=Pack(height=30))
        self.atkerhasC_switch = toga.Switch('Attacker has Commander',value=False)
        atkerAnum_label = toga.Label('Number of Attacker Artillery: ',style=Pack(padding_top=9))
        self.atkerAnum_input = toga.NumberInput(step=1,min=0,value=0,style=Pack(height=30))
        deferAnum_label = toga.Label('Number of Defender Artillery: ',style=Pack(padding_top=9))
        self.deferAnum_input = toga.NumberInput(step=1,min=0,value=0,style=Pack(height=30))
        atkerCnum_label = toga.Label('Number of Attacker Cavalry in adjacent territories to target: ',style=Pack(padding_top=3))
        self.atkerCnum_input = toga.NumberInput(step=1,min=0,value=0,style=Pack(height=30))
        self.landing_switch = toga.Switch('Amphibious attack',value=False)
        
        chaos_button = toga.Button('Allout attack',on_press=self.confirm_auto,style=Pack(height=30))
        throw_dice_button = toga.Button('Throw dice once',on_press=self.confirm_manual,style=Pack(height=30))
        CA_label = toga.Label('Conditional skirmish',style=Pack(padding_top=5))
        CA_atker_label = toga.Label('Halt attack when less than or X attackers (including cavalry) left: ',style=Pack(padding_top=3))
        self.CA_atker_input = toga.NumberInput(step=1,min=1,value=1)
        CA_defer_label = toga.Label('Retreat when less than or X defenders left: ',style=Pack(padding_top=3))
        self.CA_defer_input = toga.NumberInput(step=1,min=0,value=0)
        CA_button = toga.Button('Quick attack',on_press=self.confirm_conditional,style=Pack(height=30))
        
        self.nums_table = toga.Table(headings=[
            'round','attacker','atk Cavalry','atk Artillery','Commander','defender','def Artillery','landing'],style=Pack(flex=1,height=70,width=720))
        self.dice_table = toga.Table(headings=[
            'round','dice A1','dice A2','dice A3','dice D1','dice D2','dice D3'],style=Pack(flex=1,height=100,width=720))
        clear_table_button = toga.Button('Clear table',on_press=self.clear_table)

        atkernums_box.add(atkernum_label)
        atkernums_box.add(atkerAnum_label)
        defernums_box.add(defernum_label)
        defernums_box.add(deferAnum_label)
        atkerinput_box.add(self.atkernum_input)
        atkerinput_box.add(self.atkerAnum_input)
        deferinput_box.add(self.defernum_input)
        deferinput_box.add(self.deferAnum_input)
        atkerCnum_box.add(atkerCnum_label)
        atkerCnum_box.add(self.atkerCnum_input)
        CA_label_button_box.add(CA_label)
        CA_label_button_box.add(CA_button)
        CA_atker_input_box.add(CA_atker_label)
        CA_atker_input_box.add(self.CA_atker_input)
        CA_defer_input_box.add(CA_defer_label)
        CA_defer_input_box.add(self.CA_defer_input)
        
        atkernum_container.add(atkernums_box)
        atkernum_container.add(atkerinput_box)
        defernum_container.add(defernums_box)
        defernum_container.add(deferinput_box)
        atker_box.add(atker_label)
        atker_box.add(atkernum_container)
        defer_box.add(defer_label)
        defer_box.add(defernum_container)
        boolean_box.add(self.atkerhasC_switch)
        boolean_box.add(self.landing_switch)
        CA_box.add(CA_label_button_box)
        CA_box.add(CA_atker_input_box)
        CA_box.add(CA_defer_input_box)
        
        numbers_box.add(atker_box)
        numbers_box.add(defer_box)
        buttons_box.add(chaos_button)
        buttons_box.add(throw_dice_button)
        buttons_box.add(CA_box)
        display_box.add(self.nums_table)
        display_box.add(self.dice_table)
        display_box.add(clear_table_button)
        
        main_box.add(numbers_box)
        main_box.add(atkerCnum_box)
        main_box.add(boolean_box)
        main_box.add(buttons_box)
        main_box.add(display_box)
        
        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_UI
        self.main_window.show()
        if toga.platform.current_platform == 'android':
            self._impl.native.getSupportActionBar().hide()
        
    def clear_table(self,widget):
        self.nums_table.data = 0
        self.dice_table.data = 0
        
    def confirm_auto(self,widget):
        atkernum_display = self.atkernum_input.value
        atkerC_display = self.atkerCnum_input.value
        atkerA_display = self.atkerAnum_input.value
        defernum_display = self.defernum_input.value
        deferA_display = self.deferAnum_input.value
        self.CA_atker_input.value
        self.CA_defer_input.value
        boolean_atkerhasC_display = self.atkerhasC_switch.value
        boolean_landing_display = self.landing_switch.value
        self.nums_table.data.append(('',atkernum_display,atkerC_display,atkerA_display,boolean_atkerhasC_display,defernum_display,deferA_display,boolean_landing_display))
        self.nums_table.scroll_to_bottom()
        self.main_window.confirm_dialog('Confirmation','Do you really want to do this?',on_result=self.allout_attack)
        
    def confirm_manual(self,widget):
        self.atkernum_store = self.atkernum_input.value
        self.atkerC_store = self.atkerCnum_input.value
        self.atkerA_store = self.atkerAnum_input.value
        self.defernum_store = self.defernum_input.value
        self.deferA_store = self.deferAnum_input.value
        boolean_atkerhasC_display = self.atkerhasC_switch.value
        boolean_landing_display = self.landing_switch.value
        self.roundnum_store = 0
        self.nums_table.data.append(('',self.atkernum_store,self.atkerC_store,self.atkerA_store,boolean_atkerhasC_display,self.defernum_store,self.deferA_store,boolean_landing_display))
        self.nums_table.scroll_to_bottom()
        self.main_window.confirm_dialog('Confirmation','Do you really want to do this?',on_result=self.dice_once)
        
    def confirm_conditional(self,widget):
        atkernum_display = self.atkernum_input.value
        atkerC_display = self.atkerCnum_input.value
        atkerA_display = self.atkerAnum_input.value
        defernum_display = self.defernum_input.value
        deferA_display = self.deferAnum_input.value
        self.CA_atker_input.value
        self.CA_defer_input.value
        boolean_atkerhasC_display = self.atkerhasC_switch.value
        boolean_landing_display = self.landing_switch.value
        self.nums_table.data.append(('',atkernum_display,atkerC_display,atkerA_display,boolean_atkerhasC_display,defernum_display,deferA_display,boolean_landing_display))
        self.nums_table.scroll_to_bottom()
        self.main_window.confirm_dialog('Confirmation','Do you really want to do this?',on_result=self.conditional_attack)
        
    def allout_attack(self,widget,result):
        if result == True:
            roundnum = 0
            atkernum = self.atkernum_input.value
            atkerCnum = self.atkerCnum_input.value
            atkerAnum = self.atkerAnum_input.value
            defernum = self.defernum_input.value
            deferAnum = self.deferAnum_input.value
            boolean_atkerhasC = self.atkerhasC_switch.value
            boolean_landing = self.landing_switch.value
            while atkernum + atkerCnum > 1 and defernum > 0:
                roundnum += 1
                if atkernum + atkerCnum > 3:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                elif atkernum + atkerCnum > 2:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                else:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = 0
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = 0
                        diceA3 = 0
                if defernum > 1:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = random.randint(1,6)
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                else:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = 0
                        diceD3 = 0
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceA2 < diceA3:
                    m = diceA2
                    diceA2 = diceA3
                    diceA3 = m
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if diceD2 < diceD3:
                    m = diceD2
                    diceD2 = diceD3
                    diceD3 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if atkerAnum > 0 or deferAnum > 0:
                    self.dice_table.data.append((roundnum,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                    self.dice_table.scroll_to_bottom()
                if atkerAnum > 2:
                    diceD1 -= 1
                    if diceD1 == 0:
                        diceD1 = 1
                    if diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                elif atkerAnum > 1:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                elif atkerAnum > 0:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                if deferAnum > 2:
                    diceA1 -= 1
                    if diceA1 == 0:
                        diceA1 = 1
                    if diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                elif deferAnum > 1:
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                        if diceA2 > 0:
                            diceA2 -= 1
                            if diceA2 == 0:
                                diceA2 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                elif deferAnum > 0:
                    if diceA3 >0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1        
                if diceA1 > diceD1:
                    defernum -= 1
                else:
                    atkernum -= 1
                    if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if diceA2 > 0 and diceD2 > 0:
                    if diceA2 > diceD2:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if diceA3 > 0 and diceD3 > 0:
                    if diceA3 > diceD3:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if deferAnum > defernum:
                    deferAnum = defernum            
                self.dice_table.data.append((roundnum,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                self.nums_table.data.append((roundnum,atkernum,atkerCnum,'atkerA','',defernum,'deferA'))
            self.nums_table.scroll_to_bottom()
            self.nums_table.data.append('')
            self.dice_table.scroll_to_bottom()
            self.dice_table.data.append('')
            if defernum < 1:
                self.main_window.info_dialog('Result',f'Attack success, remaining attacker: {atkernum}, remaining cavalry: {atkerCnum}')
            elif atkernum < 2:
                self.main_window.info_dialog('Result',f'Attack failed, remaining defender: {defernum}')
        else:
            None
        
    def conditional_attack(self,widget,result):
        if result == True:
            roundnum = 0
            atkernum = self.atkernum_input.value
            atkerCnum = self.atkerCnum_input.value
            atkerAnum = self.atkerAnum_input.value
            defernum = self.defernum_input.value
            deferAnum = self.deferAnum_input.value
            boolean_atkerhasC = self.atkerhasC_switch.value
            boolean_landing = self.landing_switch.value
            while atkernum + atkerCnum > self.CA_atker_input.value and defernum > self.CA_defer_input.value:
                roundnum += 1
                if atkernum + atkerCnum > 3:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                elif atkernum + atkerCnum > 2:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                else:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = 0
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = 0
                        diceA3 = 0
                if defernum > 1:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = random.randint(1,6)
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                else:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = 0
                        diceD3 = 0
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceA2 < diceA3:
                    m = diceA2
                    diceA2 = diceA3
                    diceA3 = m
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if diceD2 < diceD3:
                    m = diceD2
                    diceD2 = diceD3
                    diceD3 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if atkerAnum > 0 or deferAnum > 0:
                    self.dice_table.data.append((roundnum,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                    self.dice_table.scroll_to_bottom()
                if atkerAnum > 2:
                    diceD1 -= 1
                    if diceD1 == 0:
                        diceD1 = 1
                    if diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                elif atkerAnum > 1:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                elif atkerAnum > 0:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                if deferAnum > 2:
                    diceA1 -= 1
                    if diceA1 == 0:
                        diceA1 = 1
                    if diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                elif deferAnum > 1:
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                        if diceA2 > 0:
                            diceA2 -= 1
                            if diceA2 == 0:
                                diceA2 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                elif deferAnum > 0:
                    if diceA3 >0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1 
                if diceA1 > diceD1:
                    defernum -= 1
                else:
                    atkernum -= 1
                    if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if diceA2 > 0 and diceD2 > 0:
                    if diceA2 > diceD2:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if diceA3 > 0 and diceD3 > 0:
                    if diceA3 > diceD3:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if deferAnum > defernum:
                    deferAnum = defernum
                self.dice_table.data.append((roundnum,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                self.nums_table.data.append((roundnum,atkernum,atkerCnum,'atkerA','',defernum,'deferA'))
            self.nums_table.scroll_to_bottom()
            self.nums_table.data.append('')
            self.dice_table.scroll_to_bottom()
            self.dice_table.data.append('')
            if defernum <= self.CA_defer_input.value:
                self.main_window.info_dialog('Result',f'Attack success, remaining attacker: {atkernum}, remaining cavalry: {atkerCnum}, retreated defender: {defernum}')
            elif atkernum + atkerCnum <= self.CA_atker_input.value:
                self.main_window.info_dialog('Result',f'Attack failed, remaining attacker: {atkernum}, remaining cavalry: {atkerCnum}, remaining defender: {defernum}')
        else:
            None
            
    def dice_once(self, widget, result):
        if result == True:
            self.roundnum_store += 1
            atkernum = self.atkernum_store
            defernum = self.defernum_store
            atkerCnum = self.atkerC_store
            atkerAnum = self.atkerA_store
            deferAnum = self.deferA_store
            boolean_atkerhasC = self.atkerhasC_switch.value
            boolean_landing = self.landing_switch.value
            if atkernum + atkerCnum > 1 and defernum > 0:
                if atkernum + atkerCnum > 3:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = random.randint(1,6)
                elif atkernum + atkerCnum > 2:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = random.randint(1,6)
                        diceA3 = 0
                else:
                    if boolean_atkerhasC == True:
                        diceA1 = 6
                        diceA2 = 0
                        diceA3 = 0
                    else:
                        diceA1 = random.randint(1,6)
                        diceA2 = 0
                        diceA3 = 0
                if defernum > 1:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = random.randint(1,6)
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                else:
                    if boolean_landing == True:
                        diceD1 = random.randint(1,6)
                        diceD2 = random.randint(1,6)
                        diceD3 = 0
                    else:
                        diceD1 = random.randint(1,6)
                        diceD2 = 0
                        diceD3 = 0
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceA2 < diceA3:
                    m = diceA2
                    diceA2 = diceA3
                    diceA3 = m
                if diceA1 < diceA2:
                    m = diceA1
                    diceA1 = diceA2
                    diceA2 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if diceD2 < diceD3:
                    m = diceD2
                    diceD2 = diceD3
                    diceD3 = m
                if diceD1 < diceD2:
                    m = diceD1
                    diceD1 = diceD2
                    diceD2 = m
                if atkerAnum > 0 or deferAnum > 0:
                    self.dice_table.data.append((self.roundnum_store,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                    self.dice_table.scroll_to_bottom()
                if atkerAnum > 2:
                    diceD1 -= 1
                    if diceD1 == 0:
                        diceD1 = 1
                    if diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                elif atkerAnum > 1:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                elif atkerAnum > 0:
                    if diceD3 > 0:
                        diceD3 -= 1
                        if diceD3 == 0:
                            diceD3 = 1
                    elif diceD2 > 0:
                        diceD2 -= 1
                        if diceD2 == 0:
                            diceD2 = 1
                    else:
                        diceD1 -= 1
                        if diceD1 == 0:
                            diceD1 = 1
                if deferAnum > 2:
                    diceA1 -= 1
                    if diceA1 == 0:
                        diceA1 = 1
                    if diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                elif deferAnum > 1:
                    if diceA3 > 0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                        if diceA2 > 0:
                            diceA2 -= 1
                            if diceA2 == 0:
                                diceA2 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1
                elif deferAnum > 0:
                    if diceA3 >0:
                        diceA3 -= 1
                        if diceA3 == 0:
                            diceA3 = 1
                    elif diceA2 > 0:
                        diceA2 -= 1
                        if diceA2 == 0:
                            diceA2 = 1
                    else:
                        diceA1 -= 1
                        if diceA1 == 0:
                            diceA1 = 1 
                if diceA1 > diceD1:
                    defernum -= 1
                else:
                    atkernum -= 1
                    if atkernum < 1:
                        atkernum = 1
                        atkerCnum -= 1
                if diceA2 > 0 and diceD2 > 0:
                    if diceA2 > diceD2:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if diceA3 > 0 and diceD3 > 0:
                    if diceA3 > diceD3:
                        defernum -= 1
                        if defernum < 0:
                            defernum = 0
                    else:
                        atkernum -= 1
                        if atkernum < 1:
                            atkernum = 1
                            atkerCnum -= 1
                if deferAnum > defernum:
                    deferAnum = defernum
                self.dice_table.data.append((self.roundnum_store,diceA1,diceA2,diceA3,diceD1,diceD2,diceD3))
                self.nums_table.data.append((self.roundnum_store,atkernum,atkerCnum,atkerAnum,'',defernum,deferAnum))
                self.nums_table.scroll_to_bottom()
                self.dice_table.scroll_to_bottom()
                self.atkernum_store = atkernum
                self.atkerC_store = atkerCnum
                self.defernum_store = defernum
                self.deferA_store = deferAnum
                self.main_window.info_dialog('Result',f'Remaining attacker: {atkernum}, remaining cavalry: {atkerCnum}, remaining defender: {defernum}',on_result=self.confirm_continue)             
            else:
                self.nums_table.data.append('')
                self.dice_table.data.append('')
                self.main_window.info_dialog('Error','Cannot start attack, check attacker/ defender numbers.')            
        else:
            None
    
    def confirm_continue(self,widget,result):
        if self.atkernum_store + self.atkerC_store < 2:
            self.main_window.info_dialog('Result',f'Attack failed, remaining defender: {self.defernum_store}, remaining D.artillery: {self.deferA_store}')
        elif self.defernum_store < 1:
            self.main_window.info_dialog('Result',f'Attack success, remaining attacker: {self.atkernum_store}, remaining cavalry: {self.atkerC_store}')
        else:    
            self.main_window.confirm_dialog('Continue?','Do you wish to continue?',on_result=self.dice_once)

def main():
    return RISKdicer()

Steps to reproduce

  1. briefcase create android, briefcase build android
  2. download and run app on an android device
  3. input numbers larger than 10 for both 'attacker' and 'defender'
  4. click 'allout attack' button
  5. confirm the dialog
  6. the table should automatically generate lines and scroll to bottom after the while loops
  7. try to scroll the table
  8. See error

Expected behavior

expectation:
the tables should automatically scroll to bottom when the function is complete

observation:
in briefcase dev - table can and does scroll to bottom
on android phone - table does not scroll at all

Screenshots

image_2023-12-14_234849434
^screenshot of briefcase dev using complete code, there is a slider for the table and it did automatically scroll to bottom

bug report android screenshot
^screenshot of android phone running the app, the table is un-scrollable and does not automatically scroll, both screenshot shows the same code running

Environment

  • Operating System: android
  • Python version: 3.11
  • Software versions:
    • Briefcase:
    • Toga: 0.4.0
    • ...

Logs


Additional context

No response

@saviorofdeholocron saviorofdeholocron added the bug A crash or error in behavior. label Dec 14, 2023
@freakboy3742 freakboy3742 added android The issue relates to Android mobile support. awaiting details More details are needed before the issue can be triaged. labels Dec 15, 2023
@freakboy3742
Copy link
Member

I mentioned this on Discord where the issue arose - but when providing example code, it's important that the example is minimal. There is a big difference between debugging an issue with a widget, and debugging an 800 line program that, coincidentally, uses a widget.

@saviorofdeholocron
Copy link
Author

here is a simple example for the bug:

using this code,

import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW


class togatest(toga.App):

    def startup(self):

        main_box = toga.Box()

        table = toga.Table(headings=['a','b','c'],style=Pack(height=200,width=700))
        
        main_box.add(table)
        
        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()
        
        for i in range(1,20):
            table.data.append(('1','2','3'))
        table.data.append(('this is the end of table'))
        table.scroll_to_bottom()
        


def main():
    return togatest()

it generates a table that would not show all content in its height limit, and should auto scroll to bottom when done

OBSERVATIONS

BRIEFCASE DEV

can auto scroll
^in windows it can both auto scroll to bottom and use slider to manually scroll

Android

no auto scroll
^when putting it to android i discovered that it would not automatically scroll as it should
HOWEVER,

does scroll
^it was possible to manually scroll the table, it also works while in landscape orientation

when comparing to my other program i found out that the table is actually scrollable when the main window is not put into a vertical scroll_container, and the program seems to prioritize scroll_container control over scrolling table when they both exists

@freakboy3742
Copy link
Member

Ok - now we have an example that can be reproduced - and, as a result, the problem becomes clear.

The issue is that at the point you're invoking scroll_to_bottom(), the table doesn't actually have any data yet. It is technically scrolling to the bottom of the table - but the "bottom" of the table isn't the row you think it is. Although you've called append(), the data that you've appended hasn't been actually added to the table yet, because Android hasn't had a chance to redraw the widget.

I agree that it's counterintuitive, and it would be desirable to fix it; but there are limitations to what we can do - this is always going to be a problem with any GUI-related operation on platforms rendering of a change doesn't happen as a direct consequence (and synchronously) of making the change with an API. Android is particularly susceptible to this (see #2274 for another example), but other platforms can have analogous issues.

The immediate workaround is to defer the "scroll to bottom" call until there's been a chance to redraw the GUI. Something like the following will work:

    def startup(self):
        ...
        self.add_background_task(self.later)

    async def later(self, app, **kwargs):
        self.table.scroll_to_bottom()

That is - create a background task, and make the GUI change there. This will happen "later" - it will be added to the queue of operations to be performed, but will be scheduled after the GUI update caused by adding rows to the table.

@freakboy3742 freakboy3742 removed the awaiting details More details are needed before the issue can be triaged. label Dec 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
android The issue relates to Android mobile support. bug A crash or error in behavior.
Projects
None yet
Development

No branches or pull requests

2 participants