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