add a helper to wait and show a dialog if needed
use this in snaplist view. cancellation with asyncio is mindbending!
This commit is contained in:
parent
a7bcc7faf0
commit
9b414df0eb
|
@ -13,6 +13,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
|
@ -246,34 +247,6 @@ class SnapInfoView(WidgetWrap):
|
|||
return self.pile.render(size, focus)
|
||||
|
||||
|
||||
class FetchingInfo(WidgetWrap):
|
||||
|
||||
def __init__(self, parent, snap, aio_loop):
|
||||
self.parent = parent
|
||||
self.spinner = Spinner(aio_loop, style='dots')
|
||||
self.spinner.start()
|
||||
self.closed = False
|
||||
text = _("Fetching info for {snap}").format(snap=snap.name)
|
||||
# | text |
|
||||
# 12 34
|
||||
self.width = len(text) + 4
|
||||
cancel = cancel_btn(label=_("Cancel"), on_press=self.close)
|
||||
super().__init__(
|
||||
LineBox(
|
||||
Pile([
|
||||
('pack', Text(' ' + text)),
|
||||
('pack', self.spinner),
|
||||
('pack', button_pile([cancel])),
|
||||
])))
|
||||
|
||||
def close(self, sender=None):
|
||||
if self.closed:
|
||||
return
|
||||
self.closed = True
|
||||
self.spinner.stop()
|
||||
self.parent.remove_overlay()
|
||||
|
||||
|
||||
class FetchingFailed(WidgetWrap):
|
||||
|
||||
def __init__(self, row, snap):
|
||||
|
@ -294,7 +267,7 @@ class FetchingFailed(WidgetWrap):
|
|||
|
||||
def load(self, sender=None):
|
||||
self.close()
|
||||
self.row.load_info()
|
||||
schedule_task(self.row.load_info())
|
||||
|
||||
def close(self, sender=None):
|
||||
if self.closed:
|
||||
|
@ -331,25 +304,18 @@ class SnapCheckBox(CheckBox):
|
|||
on_press=self.parent.show_main_screen)],
|
||||
focus_buttons=False))
|
||||
|
||||
async def wait(self, t, fi):
|
||||
await t
|
||||
fi.close()
|
||||
async def load_info(self):
|
||||
app = self.parent.controller.app
|
||||
await app.wait_with_text_dialog(
|
||||
asyncio.shield(
|
||||
self.parent.controller.get_snap_info_task(self.snap)),
|
||||
_("Fetching info for {snap}").format(snap=self.snap.name),
|
||||
can_cancel=True)
|
||||
self.loaded()
|
||||
|
||||
def load_info(self):
|
||||
t = self.parent.controller.get_snap_info_task(self.snap)
|
||||
|
||||
if t.done():
|
||||
self.loaded()
|
||||
return
|
||||
fi = FetchingInfo(
|
||||
self.parent, self.snap, self.parent.controller.app.aio_loop)
|
||||
self.parent.show_overlay(fi, width=fi.width)
|
||||
schedule_task(self.wait(t, fi))
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key.startswith("enter"):
|
||||
self.load_info()
|
||||
schedule_task(self.load_info())
|
||||
else:
|
||||
return super().keypress(size, key)
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ from subiquitycore.palette import (
|
|||
)
|
||||
from subiquitycore.screen import make_screen
|
||||
from subiquitycore.tuicontroller import Skip
|
||||
from subiquitycore.ui.utils import LoadingDialog
|
||||
from subiquitycore.ui.frame import SubiquityCoreUI
|
||||
from subiquitycore.utils import arun_command
|
||||
|
||||
|
@ -161,6 +162,30 @@ class TuiApplication(Application):
|
|||
def show_progress(self):
|
||||
raise NotImplementedError
|
||||
|
||||
async def wait_with_text_dialog(self, awaitable, message,
|
||||
*, can_cancel=False):
|
||||
ld = None
|
||||
|
||||
task_to_cancel = None
|
||||
if can_cancel:
|
||||
if not isinstance(awaitable, asyncio.Task):
|
||||
async def w():
|
||||
return await awaitable
|
||||
task_to_cancel = self.aio_loop.create_task(w())
|
||||
else:
|
||||
task_to_cancel = awaitable
|
||||
|
||||
def show_load():
|
||||
nonlocal ld
|
||||
ld = LoadingDialog(
|
||||
self.ui.body, self.aio_loop, message, task_to_cancel)
|
||||
self.ui.body.show_overlay(ld, width=ld.width)
|
||||
|
||||
def hide_load():
|
||||
ld.close()
|
||||
|
||||
await self._wait_with_indication(awaitable, show_load, hide_load)
|
||||
|
||||
async def _move_screen(self, increment, coro):
|
||||
if coro is not None:
|
||||
await coro
|
||||
|
|
|
@ -23,6 +23,7 @@ from urwid import (
|
|||
AttrMap,
|
||||
CompositeCanvas,
|
||||
connect_signal,
|
||||
LineBox,
|
||||
Padding as _Padding,
|
||||
SelectableIcon,
|
||||
Text,
|
||||
|
@ -30,8 +31,16 @@ from urwid import (
|
|||
WidgetDisable,
|
||||
)
|
||||
|
||||
from subiquitycore.ui.buttons import other_btn
|
||||
from subiquitycore.ui.container import ListBox, Pile
|
||||
from subiquitycore.ui.buttons import (
|
||||
cancel_btn,
|
||||
other_btn,
|
||||
)
|
||||
from subiquitycore.ui.container import (
|
||||
ListBox,
|
||||
Pile,
|
||||
WidgetWrap,
|
||||
)
|
||||
from subiquitycore.ui.spinner import Spinner
|
||||
from subiquitycore.ui.stretchy import Stretchy
|
||||
from subiquitycore.ui.table import TableRow
|
||||
from subiquitycore.ui.width import widget_width
|
||||
|
@ -352,3 +361,33 @@ class SomethingFailed(Stretchy):
|
|||
|
||||
def close(self, sender):
|
||||
self.parent.remove_overlay()
|
||||
|
||||
|
||||
class LoadingDialog(WidgetWrap):
|
||||
|
||||
def __init__(self, parent, aio_loop, msg, task_to_cancel):
|
||||
self.parent = parent
|
||||
self.spinner = Spinner(aio_loop, style='dots')
|
||||
self.spinner.start()
|
||||
self.closed = False
|
||||
# | text |
|
||||
# 12 34
|
||||
self.width = len(msg) + 4
|
||||
widgets = [
|
||||
('pack', Text(' ' + msg)),
|
||||
('pack', self.spinner),
|
||||
]
|
||||
if task_to_cancel is not None:
|
||||
self.task_to_cancel = task_to_cancel
|
||||
cancel = cancel_btn(label=_("Cancel"), on_press=self.close)
|
||||
widgets.append(('pack', button_pile([cancel])))
|
||||
super().__init__(LineBox(Pile(widgets)))
|
||||
|
||||
def close(self, sender=None):
|
||||
if self.closed:
|
||||
return
|
||||
if sender is not None:
|
||||
self.task_to_cancel.cancel()
|
||||
self.closed = True
|
||||
self.spinner.stop()
|
||||
self.parent.remove_overlay()
|
||||
|
|
Loading…
Reference in New Issue