File: Frigcal/code/Frigcal--source/allscreens.kv
#===============================================================================
# Define GUI screens, included in main.kv, used in main.kv and main.py
#
# Screens are primary windows in the App. They are not modal, but only
# one screen can be shown at a time in Kivy apps (and mobile, generally).
# In FC, they are switched by main-menu options, unlike PPUS's tabs.
# See _dev-misc/kivy-dev-notes.txt for notes and temp code trimmed here.
#===============================================================================
#------------------------------------------------------------------------------
# LOCAL CLASSES
#------------------------------------------------------------------------------
# for shared LabelTextDisplay: get in main.kv only, else warning
##:include common.kv
# HAMBURGER-BUTTON PADDING
# To avoid overlaying the hamburger-button with screen content, define
# the screen label redundantly on each screen, and insert a small amount
# of padding to the top of of each screen's content layout (not to its
# label: that skews the label too much).
# Caveat: space is a premium in this app and the padding both heuristic
# and platform dependent. On Android, dp(8) nearly sufficed, but one of
# ~ten test devices required >= dp(10). On Windows, there is more wasted
# top space, but there is more room in general. Kivy dp() accounts for
# screen density and sp() also accounts for host-device font-size settings,
# but the latter seems no different in this context.
<ScreenLayout@MDBoxLayout>:
orientation: 'vertical'
pos_hint: {'top': 1} # required to place at top of window
padding: [0, dp(12), 0, 0] # [l, t, r, b]: over hb button overlays
#adaptive_height: True # kills Month, unneeded in Help/About
<ScreenLabel@MDLabel>:
markup: True # use [] for bold, italic, under, etc.
halign: 'center'
theme_text_color: 'Primary' # white or black to keep ('Secondary' is grey)
size_hint_y: None
#adaptive_height: True
#text: f'{root.height=}, {topbar.height=}, {navlayout.height=}'
# DEV NOTES
# Single tap on MonthScreen label == Today == double tap on screen
# Fail: cannot tap hamburger button, and it's not worth resolving
#
#<TappableScreenLabel@ButtonBehavior+ScreenLabel>:
# on_release: None
# DAY-CELL BORDERS (docs only)
# BorderedDayLayout is now defined in Python only (monthgui.py) because
# it is used to create widgets dynamically in Python code. It was formerly
# here for its canvas.after border code, but this can be done in .py too,
# and, unlike widgets, coding the class _both_ here an in .py generated a
# startup warning and ignored the version here. It may work in both sans
# the superclass or differening property values; unknown and undocumented.
# border_width could be 1 to minimize its space taken, but this rendered
# borders unevely on tested phones, with some borders slightly wider than
# others (like CSS). Using dp(1) was the same as abs 2 on tested phones.
# BoxLayout stacks fixed-size items from bottom up (sans a size_hint=(1, 1)
# dummy widget at bottom to fill the space that precludes later extension),
# and an AnchorLayout doesn't do top-down layouts; use a 1-col Grid in Grid;
#<BorderedDayLayout@MDGridLayout>:
# cols: 1
# border_width: 2 # Border width in pixels: 2px is a min
# border_color: app.theme_cls.primary_color # e.g., [1, 0, 0, 1] red border color (RGBA)
#
# canvas.after:
# Color:
# rgba: root.border_color
# Line:
# width: root.border_width
# rectangle: self.x, self.y, self.width, self.height
#
# # plus subwidgets added in .py
# unused/incomplete/punt...
#<ThemedBorderBox@MDBoxLayout+BackgroundColorBehavior>:
# orientation: 'vertical'
# # binds the line_color to the theme's primary color
# line_color: app.theme_cls.primary_color
# line_width: 2
#------------------------------------------------------------------------------
# MONTH SCREEN
#------------------------------------------------------------------------------
<MonthScreen>:
# Super is MDScreen in .py
# The app's main screen: month with days, days with events
name: 'month'
ScreenLayout:
ScreenLabel: # swipable: see main.py
id: screenlabel # to access in .py
text: '[i]Month Year[/i]' # set in .py
# fail: cannot tap hamburger button
# on_release: app.on_menu_today(app.menuMonthItem)
GridLayout:
id: monthgrid
cols: 7
# main.py adds bordered day cells
# day cells have tappable numbers and scrolled+overlined events
#------------------------------------------------------------------------------
# SEARCH SCREEN
#------------------------------------------------------------------------------
<Spacing@MDLabel>
size_hint_y: None
height: 20 # TBD: dp()?
<SearchScreen>:
# Super is MDScreen in .py
# Calendar search inputs and tappable results
name: 'search'
#spacing: 20 # doesn't work here
ScreenLayout:
ScreenLabel:
text: '[i]Search Calendars[/i]'
FormTextField: # allpopups.kv: MDTextField, adaptive
id: searchfor
hint_text: 'Search for'
# see notes at event-edit dialog
use_handles: app.editbubbles == 'Enabled' # select handles?
use_bubble: app.editbubbles == 'Enabled' # select/longpress bubbles?
Spacing:
FormTextField:
id: searchin
hint_text: 'Search in'
readonly: True # no on-screen keyboard
on_touch_down:
app.searchgui.on_search_field_touched(args[1], root) # popup options menu
#see allpopups.kv: k_m botches focus, o_f not run on android
#keyboard_mode: 'managed'
#on_focus: if self.focus: app.searchgui.on_search_field_focus(root)
Spacing:
MDLabel:
id: resultslabel
text: '' # set on search results in .py
adaptive_height: True
halign: 'center'
disabled: True
MDScrollView:
# caveat: List doesn't hscroll - and trying was a big pita;
# the builtin List.kv code does this to truncate instead:
#
# <BaseListItem> # OneLineListItem's super
# BoxLayout:
# MDLabel:
# ...etc...
# shorten_from: "right"
# shorten: True
#
# which rules out hscroll: a [shorten: False] here or in .py
# does not help given the nested structure, and hscroll likely
# requires kivy-code mods, which is a non-starter; RecycleView
# may or may not hscroll but it's obfuscated; web searches just
# yield bogus size-setting chides (don't trust AI gossip, kids...)
size_hint: (1, 1)
do_scroll_y: True
do_scroll_x: True # this doesn't work: see above
effect_cls: ScrollEffect # avoid snap-back animation throb: moot here?
always_overscroll: False # Kivy scroll-defect workaround: moot here?
MDList: # in its code: an MDGridLayout
id: searchresults
adaptive_height: True # enable scrolling, also set in MDList class
#adaptive_width: True # this doesn't work: see above
# searchgui.py clears+adds list items on Search
# adds OneLineListItem: a FloatLayout, with behaviors
DialogButtonsRows1:
DialogButton:
text: 'Search'
on_release: app.searchgui.on_search_button(root)
#------------------------------------------------------------------------------
# SETTINGS SCREEN
#------------------------------------------------------------------------------
<SettingsScreen>:
# Super is MDScreen in .py
# User-setting configuration and saves
name: 'settings'
ScreenLayout:
ScreenLabel:
text: '[i]Configure Frigcal[/i]'
ScrollView:
id: settingsscroll
do_scroll_y: True
do_scroll_x: False # vertical only
effect_cls: 'ScrollEffect' # don't overscroll/snapback (class or str)
BoxLayout:
id: settingsbox
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
spacing: 20 # works here; TBD: use dp()?
padding: [dp(8), dp(8), dp(8), dp(8)] # [l, t, r, b]
FormTextField:
id: colortheme
hint_text: 'Color theme'
readonly: True
on_touch_down: app.settingsgui.on_colortheme_touch(args[1])
#text: root.colortheme
FormTextField:
id: globalfontsize # too trivial for handles/bubbles
hint_text: 'Font size'
on_focus: if not self.focus: app.settingsgui.on_fontsize_defocus()
#text: root.globalfontsize
# font-name setting was cut because setting Widget's font_name
# globally breaks hamburger button and main-menu icons, and not
# setting it means already-displayed text is not updated; and
# font_name settings don't apply to all widgets, unlike font_size
#FormTextField:
# id: globalfontname
# hint_text: 'Font name (new text)'
# readonly: True
# on_touch_down: app.settingsgui.on_fontname_touch(args[1])
# #text: root.globalfontname
FormTextField:
id: maxbackups # too trivial for handles/bubbles
hint_text: 'Max Backups'
on_focus: if not self.focus: app.settingsgui.on_maxbackups_defocus()
#text: root.maxbackups
FormTextField:
id: editbubbles
hint_text: 'Edit bubbles'
readonly: True
on_touch_down: app.settingsgui.on_editbubbles_touch(args[1])
#text: root.editwidgets
# punt on a ScrollView here: could not get text to hscroll
# despite hours of trying - including the same just-awful
# private-member width calcs used in PPUS for its logs tab;
# this may have been because it used nested ScrollViews (?);
#
# instead, use multiline, which breaks at width and adds lines
# as needed and may be better than scrolling in hindsight;
# [shorten: True, shorten_from: 'right'] is another option,
# but phone users would not be able to see the truncated text;
#
# getting a ScollView to appear at all was a major ordeal:
# [size_hint_y: None] did the trick in the end, but Kivy
# widget layout settings can be frustrating and arduous;
FormTextField:
id: calendarsfolder
hint_text: 'Calendars folder'
readonly: True # ro=no android kbd, but no focus
multiline: True
on_touch_down: app.settingsgui.on_calendarsfolder_touch(args[1])
#on_focus: if self.focus: app.settingsgui.on_calendarsfolder_focus()
#text: root.calendarsfolder
# update: calendar colors dropped, categories/colors unchangeble;
# lifting either constraint requires settings syncs across devices
#FormTextField:
# id: calendarcolors
# hint_text: 'Calendar Colors'
# readonly: True
# on_touch_down: app.settingsgui.on_calendarcolors_touch(args[1])
# #text: root.calendarcolors
DialogButtonsRows1:
DialogButton:
text: 'Save'
on_release: app.settingsgui.on_settings_save(root)
DialogButton:
text: 'Restore'
on_release: app.settingsgui.on_settings_restore(root)
#------------------------------------------------------------------------------
# HELP SCREEN
#------------------------------------------------------------------------------
<HelpScreen>:
# Super is MDScreen in .py
# In-app helps as mark-up text and web links
name: 'help'
ScreenLayout:
ScreenLabel:
text: '[i]Frigcal Help[/i]'
MDScrollView:
id: helptextscroll
do_scroll_x: False
# Labels display as empty black for any trivially-large
# text, and Text fields don't do markup. To workaround,
# split text into parts and span across multiple Labels.
SplitTextBoxLayout:
id: helptextbox
# main.py adds >= 1 LabelTextDisplay for split text
DialogButtonsRows1:
DialogButton:
text: 'Docs'
on_release:
app.open_web_page('https://quixotely.com/Frigcal/User-Guide.html')
DialogButton:
text: 'Website'
on_release:
app.open_web_page('https://quixotely.com/Frigcal/index.html')
DialogButton:
text: 'Files'
on_release:
app.launch_file_explorer(folder=None)
#------------------------------------------------------------------------------
# ABOUT SCREEN
#------------------------------------------------------------------------------
<AboutScreen>:
# Super is MDScreen in .py
# In-app logistics info as mark-up text and web links
name: 'about'
ScreenLayout:
ScreenLabel:
text: '[i]About Frigcal[/i]'
MDScrollView:
id: abouttextscroll
do_scroll_x: False
# Labels display as empty black for any trivially-large
# text, and Text fields don't do markup. To workaround,
# split text into parts and span across multiple Labels.
SplitTextBoxLayout:
id: abouttextbox
# main.py adds >= 1 LabelTextDisplay for split text
DialogButtonsRows1:
DialogButton:
text: 'Play Store'
on_release:
app.open_web_page('https://play.google.com/store/apps/details?id=com.quixotely.frigcal')
DialogButton
text: 'Downloads'
on_release:
app.open_web_page('https://quixotely.com/Frigcal/App-Packages.html')
DialogButton:
text: 'Frigcal3'
on_release:
app.open_web_page('https://learning-python.com/frigcal.html')