File: Frigcal/code/Frigcal--source/allpopups.kv

#===============================================================================
# Define GUI popups, included in main.kv, used in main.py and other .py
#
# Popups are temporary modal overlays on whatever screen is current.
# They prevent other actions from beign selected in the GUI but
# do not prevent the GUI from responding to events (non-blocking). 
#===============================================================================



# for shared LabelTextDisplay: get in main.kv only, else warning
##:include common.kv



#------------------------------------------------------------------------------
# INFO POPUP
#------------------------------------------------------------------------------


<InfoDialog>:    

    # Defined as a BoxLayout in main.py for properties.
    # General modal info popup: pass message, oncancel.
    # For all informational messages and interactions.
    # Toast alternative for PCs (all infos) and Android (long infos).
    # To see/augment this class in in .py, declare there or use Factory.name.

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    # legacy PPUS notes...
    # because the TextInput itself would not scroll vertically;
    # text normally fits, and this uses default [do_wrap: True],
    # but add scrollview for large text or fontsize - textinput
    # won't vscroll by itself; scrolling is also much more likely 
    # to be needed in landscape mode on phones; caveat: this code 
    # is repeated redundantly: root.message won't work otherwise?

    ScrollView:                          # scrolled info text
        size_hint: (1.0, 1.0)
        id: textscroll
        do_scroll_y: True                # auto opens at top of text
        effect_cls: 'ScrollEffect'       # don't overscroll/snapback (class or str)

        LabelTextDisplay:
            text: root.message
            #on_ref_press: app.open_web_page(args[1])    # but [ref] not working in KivyMD: TBD

    DialogButtonsRows1:

        DialogButton:
            text: 'Okay'
            on_release: root.oncancel(root)    # validate root=content instance



#------------------------------------------------------------------------------
# CONFIRM POPUP
#------------------------------------------------------------------------------


<ConfirmDialog>:    

    # Defined as a BoxLayout in main.py for properties.
    # General modal verify popup: pass message, onyes, onno.
    # For confirming app exits, many search results, event-dialog closes.

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    # see scrolling docs at InfoDialog above

    ScrollView:                            # scrolled confirm message
        size_hint: (1.0, 1.0)
        id: textscroll
        do_scroll_y: True                  # auto opens at top of text
        effect_cls: 'ScrollEffect'         # don't overscroll/snapback

        LabelTextDisplay:
            text: root.message

    DialogButtonsRows1:                    # cancel/proceed buttons

        DialogButton:
            text: 'Cancel'                 # "No" and "Yes" aren't firm enough
            on_release: root.onno(root)    # validate root=content instance

        DialogButton:
            text: 'Confirm'                # was "Yes" but exits are perilous
            on_release: root.onyes(root)



#------------------------------------------------------------------------------
# BUSY POPUP
#------------------------------------------------------------------------------


<BusyDialog>:    

    # Defined as a BoxLayout in main.py for properties.
    # General modal busy-wait popup: pass message (only).
    # For calendar-file loads and saves, threaded to avoid hangs:
    # - Starts with busy note, image animated, Okay disabled
    # - After op, caller changes note, stops animation, enables Okay
    # This both avoids a flash for fast actions and provides exit feedback.   

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    # see scrolling docs at InfoDialog above

    ScrollView:
        size_hint: (1.0, 1.0)            # scrolled message
        id: textscroll
        do_scroll_y: True                # auto opens at top of text
        effect_cls: 'ScrollEffect'       # don't overscroll/snapback

        LabelTextDisplay:
            text: root.message           # changed in .py on wait end

    Image:
        id: busyimage
        source: 'icons/Frigcal512-anim.gif'
        size: self.texture_size
        anim_loop: 0                  # reps till stop, 0=keep looping
        #anim_delay: -1               # frames/sec, -1=stop, 0.1428=go (7/sec)

    DialogButtonsRows1:

        DialogButton:
            id: busyokay
            text: 'Okay'
            disabled: True                   # enabled by .py on thread exit
            on_release: root.onokay(root)    # validate root=content instance



#------------------------------------------------------------------------------
# NEW CALENDAR POPUP
#------------------------------------------------------------------------------


<NewCalendarDialog>:    

    # Defined as a BoxLayout in main.py for properties.
    # User input of new calendar name: pass message, onadd, oncancel.
    # This is a main-menu action, uses this modal dialog (not a screen). 

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    # see scrolling docs at InfoDialog above

    ScrollView:
        size_hint: (1.0, 1.0)            # scrolled user message
        id: textscroll
        do_scroll_y: True                # auto opens at top of text
        effect_cls: 'ScrollEffect'       # don't overscroll/snapback (class or str)

        LabelTextDisplay:
            text: root.message
            
    LabelTextDisplay:    # spacer

    MDTextField:
        id: newcalname
        text: ''
        multiline: False
        mode: 'round'
        size_hint_x: 0.66
        pos_hint: {'center_x': .50}         # .padding fail: use spacers

    LabelTextDisplay:    # spacer

    DialogButtonsRows1:

        DialogButton:
            text: 'Add'
            on_release: root.onadd(root)    # validate root=content instance

        DialogButton:
            text: 'Cancel'
            on_release: root.oncancel(root)



#------------------------------------------------------------------------------
# GO-TO-DATE POPUP
#------------------------------------------------------------------------------


# UNUSED - replaced with a KivyMD 1.2.0 MDDatePicker, which has much 
# more functionality than pulldown m/d/y option menus... but also a
# rigid dd/mm/yyyy bias, a weird "Wrong date" label glitch in input
# mode, an arguably confusing year-choice UI, and a popup-only 
# packaging.  But it's pretty and follows the Android motif.

# KivyMD 2.x changed this radically and added an embeddable 'Docked' 
# variant that may be more consistent in this app, but it's far too 
# late to upgrade.  KivyMD 1.2.0 is the default pip 'kivy' installed
# today, 2.x is hugely backward incompatible, and the presence of both
# lines' docs on the web is confusing at best.  Churn breaks stuff.


# <GoToDateDialog>:    
#
#     # Defined as a BoxLayout in main.py for properties
#     # Jump to Month+day of a date picked by user: pass message, ongoto, oncancel
#     # This is a main-menu action, uses this modal dialog (not a screen) 
#
#     # ...cut...



#------------------------------------------------------------------------------
# EVENT-DIALOG POPUP: EDITS
#------------------------------------------------------------------------------


<EditEventDialog>:    

    # Defined as a BoxLayout in monthgui.py for callbacks.
    # User view/edit/copy/delete of event details and opened 
    # on event taps in both Month-screen days and events-list popup.

    id: dialogbox
    size: root.size
    pos: root.pos
    orientation: 'vertical'

    # Scroll entire inputs area, not just Note text, to both work around
    # a Kivy onscreen-keyboard overlay issue for the mutiline Note and 
    # make Note visible in landscape mode on smaller phones.  For more
    # on this scheme, see monthgui.py's now-defunct on_note_cursor_pos().

    MDScrollView:

        id: notescroll
        do_scroll_y: True
        do_scroll_x: False
        effect_cls: 'ScrollEffect'       # don't overscroll/snapback (class or str)

        #size_hint: (1, 1)
        #height: dialogbox.height - buttons.height

        MDBoxLayout:

            id: inputsbox
            orientation: 'vertical'

            size_hint_y: None
            height: self.minimum_height
            #height: max(self.minimum_height, notescroll.height)

            spacing: 16                              # TBD: use dp()?
            padding: [dp(8), dp(8), dp(8), dp(8)]    # [l, t, r, b]
                
            FormTextField:
                id: date
                hint_text: 'Date'
                readonly: True         # prevent mods and keyboard, show in normal font
                is_focusable: False    # prevent focus on taps (default=true on pcs ony)

                # but makes text color shaded light gray, unlike readonly
                # disabled: True

                # nope...
                # text_color_disabled: app.manual_theme_color_fg()
                # text_color_normal: app.manual_theme_color_fg()

            FormTextField:
                id: calendar
                hint_text: 'Calendar'
                readonly: True           # not changeable here: use copy+delete+paste           
                is_focusable: False      # prevent focus on taps (pcs only?)

            FormTextField:
                id: category
                hint_text: 'Category'                               # changeable, default empty ok
                readonly: True                                      # prevent on-screen keyboard
                on_touch_down: root.on_category_touched(args[1])    # popup menu, iff it's self

                # but not called on Android when readonly is True: bug!
                # on_focus: if self.focus: root.on_category_focus()

                # but focus stays on post close, and menu on_dismiss is uncalled: bug!
                # keyboard_mode: 'managed'

                # but this does not seems to have ever existed? (Google AI suggestion!)
                # use_softinput: False
  
            FormTextField:
                id: summary
                hint_text: 'Title'      # shortened nicelabels for space (not ics attrs)

                # see notes below
                use_handles: app.editbubbles == 'Enabled'    # select handles?
                use_bubble:  app.editbubbles == 'Enabled'    # select/longpress bubbles?

            FormTextField:
                id: description
                hint_text: 'Note'       # shortened labels for space
                multiline: True         # auto wraps on word boundaries
   
                # android only: selection widgets may be useful on touch 
                # screens, but can be annoying and persistent otherwise,
                # and there is no clear way to to test for touch screens;

                # UPDATE: enable on PCs too because it's useful and not 
                # all users will know about using control|command+A/C/V;
                # caveat: longpress selectall/paste bubble may post off-
                # screen for large notes, but select cut/copy/paste works;

                # UPDATE: selectall/paste bubble fixed with a workaround
                # subclass that forces it to appear near the touch cursor,
                # else it winds up posting off screen for larger notes;

                use_handles: app.editbubbles == 'Enabled'    # select handles?
                use_bubble:  app.editbubbles == 'Enabled'    # select/longpress bubbles?

                # avoiding onscreen-keyboard overlay of newly added
                # multiline text is weirdly hard in kivy, despite a
                # softinput_mode='below'target' in main.py; this burned
                # days of tedious trial-and-error and in the end required 
                # whole-dialog scrolling and a dummy under-keyboard widget;
                # else new text runs below widgets, and Android onscreen 
                # keyboard overlays focused Note in full; see monthgui.py;
                # scrolling is still manual and subpar, but fixed Note size
                # that accommodates on-screen keyboard proved elusive (TBD);

                on_focus: root.on_note_focus(*args)      # manage dummy widget

                # the trail of tears....
                #size_hint_y: None
                #height: '20dp'
                #height: max(self.minimum_height, notescroll.height)
                #height: notescroll.height
                #height: self.minimum_height

                # or on_keyboard key == 13 or key == 0: # Enter key or touch down (Android)
                # but also an issue on pcs sans touch
                #on_cursor_pos: root.on_note_cursor_pos(*args)

                #height: max((len(self._lines) + 2) * self.line_height, notescroll.height) 
                #height: max(self.minimum_height, notescroll.height)
                #max_height: max(self.minimum_height, notescroll.height)
                #height: self.minimum_height
                #max_height: root.height - (date.height + calendar.height + category.height + summary.height + buttons.height + (16 * 6))            
                #width: root.width

            # make this in the .py dynamically on focus/defocus
            # Label:
            #     # dummy widget to avoid onscreen keyboard overlay of Note: hack + kivy bug!
            #     size_hint_y: None
            #     height: (Window.height / 2) - buttons.height

    DialogButtonsRows1:

        id: buttons
        padding: [0, dp(0), 0, 0]    # dp(8) makes button bar shorter
             
        # TBD: or use Help/About button responsive sizing:
        # size_hint_y: dialogButtonRowHeight * 2
        # no: use dp() scaled abs height everywhere to responsive size on PCs 

        DialogButton:
            text: 'Update'
            on_release: root.on_event_update(root)    # validate root=content instance

        DialogButton:
            text: 'Copy'
            on_release: root.on_event_copy()          # copy for paste, don't close

        DialogButton:
            text: 'Delete'
            on_release: root.on_event_delete(root)    # delete and close, post warn 

        DialogButton:
            text: 'Cancel'
            on_release: root.on_event_cancel(root)    # close after warn for changes



#------------------------------------------------------------------------------
# EVENT-DIALOG POPUP: ADDS
#------------------------------------------------------------------------------


<AddEventDialog>:   

    # Defined as a BoxLayout in monthgui.py for callbacks.
    # Used for input of event details and opened on tap of daynum 
    # sans events, daynum-longpress paste, and event-list Add.
    # Same as edits, but Create+Cancel buttons and Calendar picker.
    # See EditEventDialog above for more notes on the UI structure here
    # NB: the .py coding assumes id names here are the same as in Edit.

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    MDScrollView:

        id: notescroll
        do_scroll_y: True
        do_scroll_x: False
        effect_cls: 'ScrollEffect'       # don't overscroll/snapback (class or str)

        MDBoxLayout:

            id: inputsbox
            orientation: 'vertical'

            size_hint_y: None
            height: self.minimum_height

            spacing: 16                              # TBD: use dp()?
            padding: [dp(8), dp(8), dp(8), dp(8)]    # [l, t, r, b]
                
            FormTextField:
                id: date
                hint_text: 'Date'
                readonly: True
                is_focusable: False                # normal font, no mod or focus

            FormTextField:
                id: calendar
                hint_text: 'Calendar'                               # changable here, verify text
                readonly: True                                      # prevent on-screen keyboard
                on_touch_down: root.on_calendar_touched(args[1])    # popup menu, iff it's self

            FormTextField:
                id: category
                hint_text: 'Category'                               # changeable, empty ok
                readonly: True                                      # prevent on-screen keyboard
                on_touch_down: root.on_category_touched(args[1])    # popup menu, iff it's self

            FormTextField:
                id: summary
                hint_text: 'Title'      # shortened labels for space

                # see notes at event-edit dialog
                use_handles: app.editbubbles == 'Enabled'    # select handles?
                use_bubble:  app.editbubbles == 'Enabled'    # select/longpress bubbles?

            FormTextField:
                id: description
                hint_text: 'Note'       # shortened labels for space
                multiline: True         # auto wraps on word boundaries, supports \n
   
                # see notes at event-edit dialog
                use_handles: app.editbubbles == 'Enabled'    # select handles?
                use_bubble:  app.editbubbles == 'Enabled'    # select/longpress bubbles?

                on_focus: root.on_note_focus(*args)          # manage dummy widget

            # make dummy Label in the .py dynamically on focus/defocus
 
    DialogButtonsRows1:

        id: buttons
        padding: [0, dp(0), 0, 0]
             
        # use dp() scaled abs height everywhere to responsively size on PCs 

        DialogButton:
            text: 'Create'
            on_release: root.on_event_create(root)    # validate root=content instance

        DialogButton:
            text: 'Cancel'
            on_release: root.on_event_cancel(root)    # close after warn for changes



#------------------------------------------------------------------------------
# DAY'S EVENT LIST
#------------------------------------------------------------------------------


<EventListDialog>:    

    # Defined as a BoxLayout in monthgui.py for callbacks.
    # Used for user selection of events (a zoomed version
    # of day's grid for easier access) and opened on taps
    # of daynums that already have events.  Includes an 
    # Add for creating new events on days with events.

    size: root.size
    pos: root.pos
    orientation: 'vertical'
    spacing: 16
    padding: [dp(8), dp(8), dp(8), 0]

    MDScrollView:
        id: eventsscroll
        size_hint: (1, 1)
        effect_cls: ScrollEffect          # avoid snap-back animation throb (class or str)
        always_overscroll: False          # Kivy scroll-defect workaround: monthgui.py

        MDGridLayout:
            id: eventsgrid
            cols: 1
            
            # rest like monthgui.make_month_screen_widgets

            spacing: [0, dp(24)]                 # between-event tap space: [hor, ver]
            padding: (dp(4), dp(20), dp(4), 0)   # (l, t, r, b), pad grid top else top event's line narrower
            size_hint_y: None                    # pad left/right to offset from dialog box border
            size_hint_x: None                    # enable scrolling: a kivy scourge
            height: self.minimum_height          # bind in .kv instead of .py: not dynamic
            width: self.minimum_width

            # monthgui.py adds overlined and tappable event cells

    DialogButtonsRows1:

        DialogButton:
            text: 'Add'
            on_release: root.on_event_list_add()    # validate .py self=content instance

        DialogButton:
            text: 'Cancel'
            on_release: root.on_event_list_cancel()



#------------------------------------------------------------------------------
# COLOR POPUP
#------------------------------------------------------------------------------


# TBD: is this, KivyMD's version, or a preset-colors list used?
# Use preset primary colors/categories, but customs may be nice too
# FINAL: this is unused in FC because only preset colors are allowed


<ColorPickDialog>:    

    # Defined as a BoxLayout in settingsgui.py for properties
    # Prebuilt content: pass oncancel, onpick
    # For Setting screen's calendar/category color selection

    size: root.size
    pos: root.pos
    orientation: 'vertical'

    ColorPicker:
        id: colorpicker

    DialogButtonsRows1:

        DialogButton:
            text: 'Cancel'
            on_release: root.oncancel(root)    # validate root=content instance

        DialogButton:
            text: 'Pick'
            on_release: root.onpick(colorpicker.color, colorpicker.hex_color, root)



#------------------------------------------------------------------------------
# (DEFUNCT) KIVY FILECHOOSER BUG WORKAROUND
#------------------------------------------------------------------------------


#------------------------------------------------------------------------------
# Redfine Kivy built-in widget's style to work arond an icon glitch.
#
# UNUSED IN FC: this is not required or used for KivyMD's MDFileManager.
# Frigcal 4.0 uses the SAF chooser on Android and MDFileManager on PCs.
# plyer and tkinter folder choosers work too, but plyer's is awful on 
# Windows (a blurry 1990s box), and tkinter runs independent of Kivy's 
# event loop (modal?) and has major issues on some macOS (see PyEdit).
# Kivy's chooser is really a toolbox for building one; overkill in FC.
#
# In Kivy 2.1.0, the filechooser's icon view clips text vertically when 
# fonts are set larger than usual by the app or user settings.  The fix 
# from PPUS tweaked code from PPUS's Kivy 2.1.0 that is identical in FC's
# Kivy 2.3.1, but it's unclear if the redef is ignored by Kivy: a redef 
# warning is issued on app start, but is for PPUS and Kivy 2.1.0 too.  In
# any event, it's unused in FC: cut code, but save this stub as a reminder.
#------------------------------------------------------------------------------


# [FileIconEntry@Widget]:             # this syntax is also deprecated: use <>...
#     cut: see PPUS's .kv