add and use the concept of a "global overlay"

a problem with my previous merge was that the top overlay was always
removed when the install finished, but if the user had opened a help
topic in the mean time that was the wrong thing to do. also if the
view was switched under the install notification (which is a bit
unlikely but could happen) the dialog would disappear.

this finally pushed me to generically handle "global overlays" that are
not tied to the underlying, controller-controlled view. there are a few
of these now:

 * help dialogs
 * error reports
 * the "are you sure?" install confirmation
 * now the install notification

this adds an api for adding and removing them, and the api for removing
them includes which overlay to remove.
This commit is contained in:
Michael Hudson-Doyle 2020-05-01 12:38:52 +12:00
parent 2a53fce963
commit 2aa3bea55e
4 changed files with 65 additions and 58 deletions

View File

@ -35,6 +35,7 @@ from subiquitycore.async_helpers import (
)
from subiquitycore.controller import Skip
from subiquitycore.core import Application
from subiquitycore.view import BaseView
from subiquity.controllers.error import (
ErrorReportKind,
@ -128,7 +129,7 @@ class Subiquity(Application):
["subiquity"], self.subiquity_event, seek=True)
super().__init__(opts)
self.install_lock_file = Lockfile(self.state_path("installing"))
self.install_running = None
self.global_overlays = []
self.block_log_dir = block_log_dir
self.kernel_cmdline = shlex.split(opts.kernel_cmdline)
if opts.snaps_from_examples:
@ -168,16 +169,15 @@ class Subiquity(Application):
InstallRunning,
)
tty = self.install_lock_file.read_content()
self.install_running = InstallRunning(self.ui.body, self, tty)
self.ui.body.show_stretchy_overlay(self.install_running)
schedule_task(self._hide_install_running())
install_running = InstallRunning(self.ui.body, self, tty)
self.add_global_overlay(install_running)
schedule_task(self._hide_install_running(install_running))
async def _hide_install_running(self):
async def _hide_install_running(self, install_running):
# Wait until the install has completed...
async with self.install_lock_file.shared():
# And remove the overlay.
self.install_running = None
self.ui.body.remove_overlay()
self.remove_global_overlay(install_running)
def restart(self, remove_last_screen=True):
if remove_last_screen:
@ -343,7 +343,7 @@ class Subiquity(Application):
InstallConfirmation,
)
self._cancel_show_progress()
self.ui.body.show_stretchy_overlay(
self.add_global_overlay(
InstallConfirmation(self.ui.body, self))
else:
yes = _('yes')
@ -371,12 +371,22 @@ class Subiquity(Application):
return True
return bool(self.autoinstall_config.get('interactive-sections'))
def add_global_overlay(self, overlay):
self.global_overlays.append(overlay)
if isinstance(self.ui.body, BaseView):
self.ui.body.show_stretchy_overlay(overlay)
def remove_global_overlay(self, overlay):
self.global_overlays.remove(overlay)
if isinstance(self.ui.body, BaseView):
self.ui.body.remove_overlay(overlay)
def select_initial_screen(self, index):
super().select_initial_screen(index)
for report in self.controllers.Error.reports:
if report.kind == ErrorReportKind.UI and not report.seen:
self.report_to_show = report
return
self.show_error_report(report)
break
super().select_initial_screen(index)
def select_screen(self, new):
if new.interactive():
@ -390,12 +400,6 @@ class Subiquity(Application):
return
self.progress_showing = False
super().select_screen(new)
if self.report_to_show is not None:
log.debug("showing new error %r", self.report_to_show.base)
self.show_error_report(self.report_to_show)
self.report_to_show = None
if self.install_running is not None:
self.ui.body.show_stretchy_overlay(self.install_running)
elif self.autoinstall_config and not new.autoinstall_applied:
if self.interactive() and self.show_progress_handle is None:
self.ui.block_input = True
@ -520,12 +524,12 @@ class Subiquity(Application):
def show_error_report(self, report):
log.debug("show_error_report %r", report.base)
w = getattr(self.ui.body._w, 'top_w', None)
if isinstance(self.ui.body, BaseView):
w = getattr(self.ui.body._w, 'stretchy', None)
if isinstance(w, ErrorReportStretchy):
# Don't show an error if already looking at one.
return
self.ui.body.show_stretchy_overlay(
ErrorReportStretchy(self, self.ui.body, report))
self.add_global_overlay(ErrorReportStretchy(self, report))
def make_autoinstall(self):
config = {}

View File

@ -16,6 +16,7 @@
import logging
from subiquitycore.ui.frame import SubiquityCoreUI
from subiquitycore.view import BaseView
from subiquity.ui.views.help import HelpButton
@ -28,9 +29,16 @@ class SubiquityUI(SubiquityCoreUI):
block_input = False
def __init__(self, app):
self.app = app
self.right_icon = HelpButton(app)
super().__init__()
def keypress(self, size, key):
if not self.block_input:
return super().keypress(size, key)
def set_body(self, widget):
super().set_body(widget)
if isinstance(widget, BaseView):
for overlay in self.app.global_overlays:
widget.show_stretchy_overlay(overlay)

View File

@ -54,10 +54,12 @@ from subiquity.controllers.error import (
log = logging.getLogger('subiquity.ui.error')
def close_btn(parent, label=None):
def close_btn(stretchy, label=None):
if label is None:
label = _("Close")
return other_btn(label, on_press=lambda sender: parent.remove_overlay())
return other_btn(
label,
on_press=lambda sender: stretchy.app.remove_global_overlay(stretchy))
error_report_intros = {
@ -129,17 +131,16 @@ If you want to help improve the installer, you can send an error report.
class ErrorReportStretchy(Stretchy):
def __init__(self, app, parent, report, interrupting=True):
def __init__(self, app, report, interrupting=True):
self.app = app
self.report = report
self.parent = parent
self.interrupting = interrupting
self.btns = {
'cancel': other_btn(
_("Cancel upload"), on_press=self.cancel_upload),
'close': close_btn(parent, _("Close report")),
'continue': close_btn(parent, _("Continue")),
'close': close_btn(self, _("Close report")),
'continue': close_btn(self, _("Continue")),
'debug_shell': other_btn(
_("Switch to a shell"), on_press=self.debug_shell),
'restart': other_btn(
@ -247,7 +248,7 @@ class ErrorReportStretchy(Stretchy):
self.pile.focus_position += 1
def debug_shell(self, sender):
self.parent.remove_overlay()
self.app.debug_shell()
def restart(self, sender):
self.app.restart()
@ -273,9 +274,8 @@ class ErrorReportStretchy(Stretchy):
class ErrorReportListStretchy(Stretchy):
def __init__(self, app, parent):
def __init__(self, app):
self.app = app
self.parent = parent
rows = [
TableRow([
Text(""),
@ -295,13 +295,13 @@ class ErrorReportListStretchy(Stretchy):
Text(""),
self.table,
Text(""),
button_pile([close_btn(parent)]),
button_pile([close_btn(self)]),
]
super().__init__("", widgets, 2, 2)
def open_report(self, sender, report):
self.parent.show_stretchy_overlay(
ErrorReportStretchy(self.app, self.parent, report, False))
self.app.add_global_overlay(
ErrorReportStretchy(self.app, report, False))
def state_for_report(self, report):
if report.seen:

View File

@ -61,9 +61,10 @@ from subiquity.ui.views.error import ErrorReportListStretchy
log = logging.getLogger('subiquity.ui.help')
def close_btn(parent):
def close_btn(app, stretchy):
return other_btn(
_("Close"), on_press=lambda sender: parent.remove_overlay())
_("Close"),
on_press=lambda sender: app.remove_global_overlay(stretchy))
ABOUT_INSTALLER = _("""
@ -154,7 +155,7 @@ def ssh_help_texts(ips, password):
class SimpleTextStretchy(Stretchy):
def __init__(self, parent, title, *texts):
def __init__(self, app, title, *texts):
widgets = []
for text in texts:
@ -164,7 +165,7 @@ class SimpleTextStretchy(Stretchy):
widgets.extend([
Text(""),
button_pile([close_btn(parent)]),
button_pile([close_btn(app, self)]),
])
super().__init__(title, widgets, 0, len(widgets)-1)
@ -191,7 +192,7 @@ DRY_RUN_KEYS = (
class GlobalKeyStretchy(Stretchy):
def __init__(self, app, parent):
def __init__(self, app):
rows = []
for key, text in GLOBAL_KEYS:
rows.append(TableRow([Text(_(key)), Text(_(text))]))
@ -210,7 +211,7 @@ class GlobalKeyStretchy(Stretchy):
('pack', table),
]),
Text(""),
button_pile([close_btn(parent)]),
button_pile([close_btn(app, self)]),
]
super().__init__(_("Shortcut Keys"), widgets, 0, 2)
@ -339,18 +340,18 @@ class HelpMenu(WidgetWrap):
# We don't let help dialogs pile up: if one is already
# showing, remove it before showing the new one.
if self.parent.showing_something:
ui.body.remove_overlay()
self.parent.showing_something = True
if self.parent.current_help:
self.parent.app.remove_global_overlay(self.parent.current_help)
self.parent.current_help = stretchy
fp, ui.pile.focus_position = ui.pile.focus_position, 1
def on_close():
self.parent.showing_something = False
self.parent.current_help = None
ui.pile.focus_position = fp
connect_signal(stretchy, 'closed', on_close)
ui.body.show_stretchy_overlay(stretchy)
self.parent.app.add_global_overlay(stretchy)
def _about(self, sender=None):
info = lsb_release()
@ -364,7 +365,7 @@ class HelpMenu(WidgetWrap):
})
self._show_overlay(
SimpleTextStretchy(
self.parent.app.ui.body,
self.parent.app,
_("About the installer"),
template.format(**info)))
@ -382,7 +383,7 @@ class HelpMenu(WidgetWrap):
self._show_overlay(
SimpleTextStretchy(
self.parent.app.ui.body,
self.parent.app,
_("Help on SSH access"),
*texts,
))
@ -392,16 +393,13 @@ class HelpMenu(WidgetWrap):
def cb(sender=None):
self._show_overlay(
SimpleTextStretchy(
self.parent.app.ui.body,
self.parent.app,
local_title,
local_doc))
return cb
def _shortcuts(self, sender):
self._show_overlay(
GlobalKeyStretchy(
self.parent.app,
self.parent.app.ui.body))
self._show_overlay(GlobalKeyStretchy(self.parent.app))
def _debug_shell(self, sender):
self.parent.app.debug_shell()
@ -410,10 +408,7 @@ class HelpMenu(WidgetWrap):
self.parent.app.toggle_color()
def _show_errors(self, sender):
self._show_overlay(
ErrorReportListStretchy(
self.parent.app,
self.parent.app.ui.body))
self._show_overlay(ErrorReportListStretchy(self.parent.app))
def get_installer_password(dry_run=False):
@ -439,7 +434,7 @@ class HelpButton(PopUpLauncher):
self.app = app
self.btn = header_btn(_("Help"), on_press=self._open)
self.ssh_password = None
self.showing_something = False
self.current_help = None
super().__init__(self.btn)
def _open(self, sender):